STM32--vs1053 WAV录音实现(wav保存在flash)
发布时间:2020-12-15 07:19:18 所属栏目:百科 来源:网络整理
导读:上一篇文件保存在SD卡中http://www.voidcn.com/article/p-nrodfkzi-cr.html, 项目中要节约成本,只把录音数据放到flash w25x16中WiFi发送 下面是上层程序 #include "recorder.h"#include "delay.h"#include "usart.h"#include "key.h" #include "led.h" //#
上一篇文件保存在SD卡中http://www.voidcn.com/article/p-nrodfkzi-cr.html, 项目中要节约成本,只把录音数据放到flash w25x16中WiFi发送 下面是上层程序 #include "recorder.h" #include "delay.h" #include "usart.h" #include "key.h" #include "led.h" //#include "lcd.h" #include "vs10xx.h" #include "malloc.h" #include "ff.h" #include "exfuns.h" #include "text.h" #include "flash.h" #define debugmsg #define RECORDERADDR (4*1024) #define DATASIZE 2048 #define READSIZE 1024 //VS1053的WAV录音有bug,这个plugin可以修正这个问题 const u16 wav_plugin[40]=/* Compressed plugin */ { 0x0007,0x0001,0x8010,0x0006,0x001c,0x3e12,0xb817,0x3e14,/* 0 */ 0xf812,0x3e01,0xb811,0x0007,0x9717,0x0020,0xffd2,0x0030,/* 8 */ 0x11d1,0x3111,0x8024,0x3704,0xc024,0x3b81,0x3101,/* 10 */ 0x8024,0x3f04,0x2808,0x4800,0x36f1,/* 18 */ 0x9811,0x8028,0x0002,0x2a00,0x040e,}; //激活PCM 录音模式 //agc:0,自动增益.1024相当于1倍,512相当于0.5倍,最大值65535=64倍 void recoder_enter_rec_mode(u16 agc) { //如果是IMA ADPCM,采样率计算公式如下: //采样率=CLKI/256*d; //假设d=0,并2倍频,外部晶振为12.288M.那么Fc=(2*12288000)/256*6=16Khz //如果是线性PCM,采样率直接就写采样值 VS_WR_Cmd(SPI_BASS,0x0000); VS_WR_Cmd(SPI_AICTRL0,8000); //设置采样率,设置为8Khz VS_WR_Cmd(SPI_AICTRL1,agc); //设置增益,最大值65535=64倍 VS_WR_Cmd(SPI_AICTRL2,0); //设置增益最大值,代表最大值65536=64X VS_WR_Cmd(SPI_AICTRL3,6); //左通道(MIC单声道输入) VS_WR_Cmd(SPI_CLOCKF,0X2000); //设置VS10XX的时钟,MULT:2倍频;ADD:不允许;CLK:12.288Mhz VS_WR_Cmd(SPI_MODE,0x1804); //MIC,录音激活 delay_ms(5); //等待至少1.35ms VS_Load_Patch((u16*)wav_plugin,40);//VS1053的WAV录音需要patch } //初始化WAV头. void recoder_wav_init(__WaveHeader* wavhead) //初始化WAV头 { wavhead->riff.ChunkID=0X46464952; //"RIFF" wavhead->riff.ChunkSize=0; //还未确定,最后需要计算 wavhead->riff.Format=0X45564157; //"WAVE" wavhead->fmt.ChunkID=0X20746D66; //"fmt " wavhead->fmt.ChunkSize=16; //大小为16个字节 wavhead->fmt.AudioFormat=0X01; //0X01,表示PCM;0X01,表示IMA ADPCM wavhead->fmt.NumOfChannels=1; //单声道 wavhead->fmt.SampleRate=8000; //8Khz采样率 采样速率 wavhead->fmt.ByteRate=wavhead->fmt.SampleRate*2;//16位,即2个字节 wavhead->fmt.BlockAlign=2; //块大小,2个字节为一个块 wavhead->fmt.BitsPerSample=16; //16位PCM wavhead->data.ChunkID=0X61746164; //"data" wavhead->data.ChunkSize=0; //数据大小,还需要计算 } //显示录音时长 //x,y:地址 //tsec:秒钟数. void recoder_show_time(u32 tsec) { //显示录音时间 printf("TIME:"); printf("%d",tsec/60); //分钟 printf(":"); printf("%drn",tsec%60); //秒钟 } //通过时间获取文件名 //仅限在SD卡保存,不支持FLASH DISK保存 //组合成:形如"0:RECORDER/REC20120321210633.wav"的文件名 /* void recoder_new_pathname(u8 *pname) { u8 res; u16 index=0; while(index<0XFFFF) { sprintf((char*)pname,"0:RECORDER/REC%05d.wav",index); res=f_open(ftemp,(const TCHAR*)pname,FA_READ);//尝试打开这个文件 if(res==FR_NO_FILE)break; //该文件名不存在=正是我们需要的. index++; } }*/ //显示AGC大小 //x,y:坐标 //agc:增益值 1~15,表示1~15倍;0,表示自动增益 void recoder_show_agc(u8 agc) { printf("AGC: "); //显示名称,同时清楚上次的显示 if(agc==0)printf("AUTOrn"); //自动agc else printf("%drn",agc); //显示AGC值 } //播放pname这个wav文件(也可以是MP3等) u8 rec_play_wav(u8 *pname) { u8 rval=0; u8 *databuf; u16 i=0,j=0; u32 sectorsize=0,n=0; databuf=(u8*)mymalloc(512); //开辟512字节的内存区域 if(databuf==NULL)rval=0XFF ;//内存申请失败. if(rval==0) { VS_HD_Reset(); //硬复位 VS_Soft_Reset(); //软复位 VS_Set_Vol(220); //设置音量 VS_Reset_DecodeTime(); //复位解码时间 VS_SPI_SpeedHigh(); //高速 //读取音频数据总长度,文件总长度-8 SPI_Flash_Read(databuf,RECORDERADDR-44+4,4); sectorsize+=databuf[3]<<24; sectorsize+=databuf[2]<<16; sectorsize+=databuf[1]<<8; sectorsize+=databuf[0]; sectorsize+=8;//文件数据总长度 while(rval==0) { SPI_Flash_Read(databuf,RECORDERADDR-44+n*512,512); //printf("sectorsize=%drn",n); i=0; do//主播放循环 { if(VS_Send_MusicData(databuf+i)==0)i+=32;//给VS10XX发送音频数据 else //显示播放时间 { if(j!=VS_Get_DecodeTime()) { #ifdef debugmsg recoder_show_time(VS_Get_DecodeTime()); #endif j=VS_Get_DecodeTime(); LED0=!LED0;// } } }while(i<512);//循环发送1024个字节 n++; if(n==((sectorsize-44)/512)+1) { rval=0; #ifdef debugmsg printf("the file size=%dkBytern",sectorsize/1024); #endif break;//读完了. } } } myfree(databuf); return rval; } //录音机 //所有录音文件,均保存在SD卡RECORDER文件夹内. u8 recoder_play(void) { u8 key; u8 rval=0; __WaveHeader *wavhead=0; u16 sectorsize=0; u8 *recbuf; //数据内存 u16 w; u16 idx=0; u8 rec_sta=0; //录音状态 //[7]:0,没有录音;1,有录音; //[6:1]:保留 //[0]:0,正在录音;1,暂停录音; u8 *pname=0; u32 recsec=0; //录音时间 u8 recagc=4; //默认增益为4 u8 playFlag=0; //播放标志 wavhead=(__WaveHeader*)mymalloc(sizeof(__WaveHeader));//开辟__WaveHeader字节的内存区域 if(wavhead==NULL)rval=1; recbuf=mymalloc(DATASIZE); if(recbuf==NULL)rval=1; pname=mymalloc(30); //申请30个字节内存,类似"0:RECORDER/REC00001.wav" if(pname==NULL)rval=1; if(rval==0) //内存申请OK { #ifdef debugmsg recoder_show_time(recsec); //显示时间 recoder_show_agc(recagc); //显示agc #endif pname[0]=0; //pname没有任何文件名 while(rval==0) { key=KEY_Scan(0); switch(key) { case KEY0_PRES: //STOP&SAVE #ifdef debugmsg printf("key0:stop/save is downrn"); #endif if(rec_sta&0X80)//有录音 { wavhead->riff.ChunkSize=sectorsize*DATASIZE+36; //整个文件的大小-8; wavhead->data.ChunkSize=sectorsize*DATASIZE; //数据大小 SPI_Flash_Write((u8*)wavhead,RECORDERADDR-44,sizeof(__WaveHeader));//写入头数据 44Byte #ifdef debugmsg printf("save file in flash ok!all file size=%dkBytern",(sectorsize*DATASIZE+44)/1024); #endif sectorsize=0; } rec_sta=0; LED1=1; //关闭DS1 #ifdef debugmsg recoder_show_time(recsec); //显示时间 #endif recsec=0; break; case KEY1_PRES: //REC/PAUSE #ifdef debugmsg //printf("key1:rec is downrn"); #endif if(rec_sta&0X01)//原来是暂停,继续录音 { //rec_sta&=0XFE;//取消暂停 }else if(rec_sta&0X80)//已经在录音了,暂停 { //rec_sta|=0X01; //暂停 }else //还没开始录音 { recoder_enter_rec_mode(1024*recagc); while(VS_RD_Reg(SPI_HDAT1)>>8); //等到buf 较为空闲再开始 rec_sta|=0X80; //开始录音 recoder_wav_init(wavhead); //初始化wav数据 } break; case WKUP_PRES://播放录音(仅在非录音状态下有效) #ifdef debugmsg printf("wk_up:play is downrn"); #endif if(rec_sta==0)playFlag=1; LED1=1; break; } /////////////////////////////////////////////////////////// //读取数据 if(rec_sta==0X80)//已经在录音了 { w=VS_RD_Reg(SPI_HDAT1);//看缓冲区中有多少个16位数据 if((w>=768)&&(w<896))//SPI_HDAT0是一个1024个16位数据的缓冲区,也就是2048字节,由于下面读取字节要耗时,边读取的同时 { //vs1053还是会把数据放入缓冲区,所以当缓冲区中有768*2字节的数据时开始读取1024*2个字节,不准确读写 idx=0; //音频做到差不多就行,不需要那么的精确 while(idx<DATASIZE) //为什么是768?因为读完后还要存储Flash后再循环过来读取一次,存储Flash耗时较长,所以每次存储后容易丢失数据,因此尽量减少存储次数 { //所以说存储完要赶紧翻过头来读取数据,不然就会发生溢出数据清零,所以程序扫描过程中的printf对音频影响也很大 w=VS_RD_Reg(SPI_HDAT0); recbuf[idx++]=w&0XFF; recbuf[idx++]=w>>8; } if(sectorsize<=2048*2/DATASIZE*512)//flash未满 { SPI_Flash_Write(recbuf,RECORDERADDR+sectorsize*DATASIZE,DATASIZE); sectorsize++;//扇区数增加1,约为16ms //printf("sectorsize=%drn",sectorsize); } else { sectorsize=2048*2/DATASIZE*512; printf("err:flash is all,sectorsize=%drn",sectorsize); } } }else//没有开始录音,按KEY0播放音频 { if(playFlag) { #ifdef debugmsg printf("开始播放:rn"); #endif rec_play_wav(pname); //播放pname #ifdef debugmsg recoder_show_time(recsec); //显示时间 recoder_show_agc(recagc); //显示agc #endif playFlag = 0; } LED1=0;//DS1长亮 LED0=1; } ///////////////////////////////////////////////////////////// if(recsec!=(sectorsize*16/125))//录音时间显示 { LED1=!LED1;//DS1闪烁 recsec=sectorsize*16/125; #ifdef debugmsg recoder_show_time(recsec);//显示时间 #endif } } } myfree(wavhead); myfree(recbuf); myfree(pname); return rval; } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |