最近在做毕设的过程中,需要用到录音播放方面的知识。所以,在借鉴了正点原子例程之后,成功修改出了16k录音+播放的代码。在录音之后马上接着播放音频是为了验证录制的音频是否正常。在修改的过程中也遇到了不少bug,记录于此,以表警示,希望今后不再重蹈覆辙。本文仅挑关键性代码进行分析:
keil5,stm32F103ZET6战舰开发板、蓝牙模块,ST-LINK烧录器
(开发板上的USB_232坏了,所以使用蓝牙模块仅仅是为了验证一些关键性数据是否正确,当然你也可以使用其他手段)
//初始化VS1053 ,使用SPI1进行通信,速度设置为最低,Fpclk之后256分频(Fpclk=72Mhz)
VS_Init();
while(1)
{
//在循环中检查到有按键按键就调用函数,开始录音->播放录音,返回音频数据地址
dstbuf=recoder_play();
}
SIZEWAVD, SIZEWAVH 在头文件中定义如下
#define SIZEWAVD 64 //wav音频数据大小
#define SIZEWAVH sizeof(__WaveHeader) //wav文件头大小
以下是recoder_play()函数分析:
/*函数名:
**功能:生成录音数据,并保存在外部SRAM的特定地址内.
**返回值:(成功)返回存放wav音频数据的位置 (失败)分配失败
*/
u8* recoder_play(void)
{
u32 sectorsize=0;
u8 *recbuf_old;
u8 *recbuf; //数据内存
u16 w;
u32 idx=0;
__WaveHeader *wavhead=0;
LCD_ShowString(30,210,200,16,16,"RAM...");
u3_printf("Ram Test:0X%04X\r\n",VS_Ram_Test()); //打印RAM测试结果,VS1053为0X83FF表明完好;
LCD_ShowString(30,210,200,20,16,"sintest");
VS_Sine_Test(); //可以听到“嘟”一声
LCD_ShowString(30,210,200,16,16,"<
//申请 SIZEWAVD k音频数据空间 + SIZEWAVH 字节wav文件头空间
//录音码率为16000*16*1=256000kbps 即1秒可以传32000byte
recbuf_old=mymalloc(SRAMEX,SIZEWAVD*1024+SIZEWAVH);
if(recbuf_old==NULL) return NULL;
u3_printf("recbuf_old=%x\n",recbuf_old); //通过蓝牙模块返回数据到手机
recbuf=recbuf_old+SIZEWAVH; //指向存放wav音频数据的位置
wavhead=(__WaveHeader*)recbuf_old;
//初始化wav文件头,修改:
//wavhead->riff.ChunkSize=SIZEWAVD*1024+36; (wavhead->data.ChunkSize+36)
//wavhead->fmt.SampleRate=16000; (16Khz采样率 采样速率)
//wavhead->data.ChunkSize=SIZEWAVD*1024; (数据大小byte)
recoder_wav_init(wavhead);
recoder_enter_rec_mode(); //进入录音模式,采样频率改为16k
while(VS_RD_Reg(SPI_HDAT1)>>8); //等到buf 较为空闲再开始录音
LCD_ShowString(30+30,250,200,16,16,"START."); //可以开始录音
while(sectorsize < SIZEWAVD*2)
{
w=VS_RD_Reg(SPI_HDAT1);
if((w>=256)&&(w<896))
{
while(idx
w=VS_RD_Reg(SPI_HDAT0);
recbuf[idx++]=w&0XFF;
recbuf[idx++]=w>>8;
if((idx&0x1FF)==0) break; //相当于idx%512
}
sectorsize++; //扇区数增加1
}
LED1=!LED1;//DS0闪烁
}
rec_play_wav(recbuf_old);
return recbuf;
}
注意!!在循环读取HDAT0 寄存器内容的时候,不要使用打印调试语句,或者其他比较耗时的语句,这样会造成寄存器中的音频数据漏读,这个bug调了好久,一直以为是spi的读取速度太慢了导致的,就跑去提高vs1053的内部时钟CLKI,从而提高spi的读取速度(不能超过CLKI/7)。来回折腾好久才发现问题所在,同时,即使你后面的播放函数是正确的,也会产生一些奇怪的问题:比如播放语速加快,播放不了等等。
以下是rec_play_wav()函数分析:
/*函数名:rec_play_wav
**功能:播放音频
**参数:u8 *类型的指针,指向wav音频文件头+音频数据
*/
void rec_play_wav(u8 *databuf)
{
u32 i=0;
if(databuf == NULL)
{
while(1)
{u3_printf("in recoder_play,mymalloc fail!\n"); delay_ms(1000);}
}
VS_HD_Reset(); //硬复位
VS_Soft_Reset(); //软复位
VS_Set_All(); //设置音量等参数
VS_Reset_DecodeTime(); //复位解码时间
VS_SPI_SpeedHigh(); //高速
do//主播放循环
{
if(VS_Send_MusicData(databuf+i)==0)
{
i+=32;//给VS10XX发送音频数据
}
else
{
recoder_show_time(VS_Get_DecodeTime());//显示播放时间
}
}while(i
VS_SPK_Set(0); //关闭板载喇叭
}
若需要播放成功,不能单单将音频数据发送给vs1053,还要包含wav文件头,这样vs1053才会帮你自动去解析播放音频。