/**
* @brief mp3_player 进行mp3文件解码、播放
* @param filename:要播放的文件路径
* @retval none
*/
static void mp3_player(const char *filename)
{
int err, i, outputSamps, current_sample_rate = 0;
int read_offset = 0; /* 读偏移指针 */
int bytes_left = 0; /* 剩余字节数 */
unsigned long Frames = 0; /* mP3帧计数 */
unsigned char *read_ptr = buffer; /* 缓冲区指针 */
HMP3Decoder Mp3Decoder; /* mp3解码器指针 */
//打开音频文件
fres = f_open (&file, filename, FA_READ );
//打开失败
if (fres!=FR_OK)
{
printf("read file %s error ! open another file\r\n",filename);
fres = f_close (&file);
if (++play_index>=file_num) //索引值加1
{
play_index=0; //归0,所以如果所有文件都打开失败会一直循环
}
return ; //文件无法打开,终止解码。进入下一次循环,读取下一个文件
}
//打开成功
//初始化MP3解码器
Mp3Decoder = MP3InitDecoder();
//获取输入数据流,调用helix库解码,输出PCM数据,约20ms完成一次循环
//开始进入播放状态,期间中断会修改touch_even状态
while(player_state != S_SWITCH) //循环1, 如果touch_even不是切歌状态则继续呆在循环体里
{
//有时出现解码错误,错误后继续在本循环体内,继续播放
//显示播放图标
Lcd_GramScan(1);
LCD_Clear(12,88,8,145,BACKGROUND);
Lcd_show_bmp(320-(103+((play_index-((current_page-1)*8))*18)),240-20,"/mp3player/ui_playing.bmp");
//读取mp3文件
fres = f_read(&file, buffer, sizeof(buffer), &rw_num);
if(fres != FR_OK)
{
printf("读取%s失败! %d\r\n",filename,fres);
break;
//return;
}
read_ptr = buffer; //指向mp3输入流
bytes_left = rw_num; //实际读到的输入流大小大小
//按帧处理
while(player_state != S_SWITCH) //循环2,循环本过程播放音频,直到按了下一首、上一首
{
if (player_state == S_STOP)
{
even_process(); //检查是否有事件需要处理
continue; //暂停的时候结束本次循环
}
player_state = S_PLAY; //状态更新为正在播放
read_offset = MP3FindSyncWord(read_ptr, bytes_left); //寻找帧同步,返回第一个同步字的位置
if(read_offset < 0) //没有找到同步字
{
break; //跳出循环2,回到循环1
}
read_ptr += read_offset; //偏移至同步字的位置
bytes_left -= read_offset; //同步字之后的数据大小
if(bytes_left < 1024) //补充数据
{
/* 注意这个地方因为采用的是DMA读取,所以一定要4字节对齐 */
i=(uint32_t)(bytes_left)&3; //判断多余的字节
if(i) i=4-i; //需要补充的字节
memcpy(buffer+i, read_ptr, bytes_left); //从对齐位置开始复制
read_ptr = buffer+i; //指向数据对齐位置
fres = f_read(&file, buffer+bytes_left+i, sizeof(buffer)-bytes_left-i, &rw_num);//补充数据
bytes_left += rw_num; //有效数据流大小
}
err = MP3Decode(Mp3Decoder, &read_ptr, &bytes_left, outBuf[bufflag], 0); //开始解码 参数:mp3解码结构体、输入流指针、输入流大小、输出流指针、数据格式
Frames++;
if (err != ERR_MP3_NONE) //错误处理
{
switch (err)
{
case ERR_MP3_INDATA_UNDERFLOW:
printf(&quot;ERR_MP3_INDATA_UNDERFLOW\r\n&quot;);
read_ptr = buffer;
fres = f_read(&file, read_ptr, sizeof(buffer), &rw_num);
bytes_left = rw_num;
break;
case ERR_MP3_MAINDATA_UNDERFLOW:
/* do nothing - next call to decode will provide more mainData */
printf(&quot;ERR_MP3_MAINDATA_UNDERFLOW\r\n&quot;);
break;
default:
printf(&quot;UNKNOWN ERROR:%d\r\n&quot;, err);
// 跳过此帧
if (bytes_left > 0)
{
bytes_left --;
read_ptr ++;
}
break;
}
}
else //解码无错误,准备把数据输出到PCM
{
MP3GetLastFrameInfo(Mp3Decoder, &Mp3FrameInfo); //获取解码信息
/* 根据解码信息设置采样率 */
if (Mp3FrameInfo.samprate != current_sample_rate) //采样率
{
current_sample_rate = Mp3FrameInfo.samprate;
printf(&quot; \r\n Bitrate %dKbps&quot;, Mp3FrameInfo.bitrate/1000);
printf(&quot; \r\n Samprate %dHz&quot;, current_sample_rate);
printf(&quot; \r\n BitsPerSample %db&quot;, Mp3FrameInfo.bitsPerSample);
printf(&quot; \r\n nChans %d&quot;, Mp3FrameInfo.nChans);
printf(&quot; \r\n Layer %d&quot;, Mp3FrameInfo.layer);
printf(&quot; \r\n Version %d&quot;, Mp3FrameInfo.version);
printf(&quot; \r\n OutputSamps %d&quot;, Mp3FrameInfo.outputSamps);
if(current_sample_rate >= I2S_AudioFreq_Default) //I2S_AudioFreq_Default = 2,正常的帧,每次都要改速率
{
I2S_Freq_Config(current_sample_rate); //根据采样率修改iis速率
}
}
/* 输出到DAC */
outputSamps = Mp3FrameInfo.outputSamps; //PCM数据个数
if (outputSamps > 0)
{
if (Mp3FrameInfo.nChans == 1) //单声道
{
//单声道数据需要复制一份到另一个声道
for (i = outputSamps - 1; i >= 0; i--)
{
outBuf[bufflag][i * 2] = outBuf[bufflag];
outBuf[bufflag][i * 2 + 1] = outBuf[bufflag];
}
outputSamps *= 2;
}
//非单声道数据可直接由DMA传输到IIS交给DAC
/* 等待DMA播放完,这段时间我们可以干其他的事,扫描事件进行处理 */
while((DMA1_Channel5->CCR&DMA_CCR1_EN) && !(DMA1->ISR&DMA1_IT_TC5))
{
static uint8_t i=0;
if(i==0)
{
PCM1770_VolumeSet(voice);
i++;
}
even_process();
}
/*DMA传输完毕*/
DMA_ClearFlag(DMA1_FLAG_TC5 | DMA1_FLAG_TE5);
DMA_I2S_Configuration((uint32_t)outBuf[bufflag], outputSamps);
bufflag = 1 -bufflag; //切换buffer
}//if (outputSamps > 0)
}//else 解码正常
if(file.fptr==file.fsize) //如果指针指向了文件尾,表示数据全部读完
{
printf(&quot;END\r\n&quot;);
if(play_index<file_num-1) //自动开始下一首歌曲
{
play_index++;
player_state = S_SWITCH; //进入切歌状态,跳出
}
else
{
play_index = 0;
player_state = S_SWITCH;
}
break; //跳出这首歌的播放状态 while break;
}
}//循环2 内 while(player_state != S_SWITCH)
}//循环1 外 while(player_state != S_SWITCH)
f_close(&file); //结束播放本歌曲,关闭文件
MP3FreeDecoder(Mp3Decoder);
I2S_Stop();
}
程序执行到红色代码区域,但是没有看到outBuf缓存区有数据,outBuf缓存区全部是00输出PCM个数是2304不知道为啥 |
|