STM32F4---单声道录音和播放问题

由于是刚开始学习这个东西,对很多地方都不是很了解,便记录一下自己遇到的问题。
这次是单声道的录音和播放问题。
由于给的例程(音乐播放器实验和录音实验)是双声道的,所以要修改一些东西。

原始问题:

在音乐播放器实验中,要是在SD卡中放单声道16khz的wav文件,电脑上放没有问题,但到了开发板上播放速度会加快一倍,听起来十分怪异。但如果放双声道的音频,就播放正常了。
在录音实验中,录音和播放的都是双声道的音频。

解决方法(这里直接对录音机实验的代码进行修改):

第一步:

首先在recorder.c中修改以下这个函数:
第一步
这个应该很多人都能解决,毕竟很容易就能找到。但是呢,仅仅修改这里还远远不够,只修改这里你会发现,录音在开发板上播放没有什么问题,但是到了电脑上,速度会变慢一倍,十分鬼畜。所以还要进行下面的修改。

第二步:

经过第一步发现,很明显数据是有问题的,很可能要修改wm8978,i2s的底层代码,想想都恐怖,于是便想着能不能在recorder.c中修改。另外我问了一下淘宝客服:
淘宝
很明显对于一个新入手的小白来说,还是啥都不知道,甚至我连MONO都不知道是啥,后来才知道是指单声道。
经过查资料,最后采用了他最后的建议。
意思是说,即使经过第一步修改,我们采集到的音频数据还是双声道的数据,如果我们不修改底层的什么wm8978和i2s的代码,我们采取的方法就是把双声道的数据变为单声道,即只取左声道的数据,丢弃右声道。
所以将rec_i2s_dma_rx_callback(void)函数修改为如下:
//¼Òô I2S_DMA½ÓÊÕÖжϷþÎñº¯Êý.ÔÚÖжÏÀïÃæÐ´ÈëÊý¾Ý
void rec_i2s_dma_rx_callback(void) 
{    
    u16 bw;
    u8 res;
    u16 i;
    if(rec_sta==0X80)//¼Òôģʽ
    {  
        if(DMA1_Stream3->CR&(1<<19))
        {
            for(i=2;i<I2S_RX_DMA_BUF_SIZE;i++)
   {
     i2srecbuf1[   i]=i2srecbuf1[i*2];//i2srecbuf1=i2srecbuf1[i*2];
     i2srecbuf1[i+1]=i2srecbuf1[i*2+1];
     i++;
   }
            res=f_write(f_rec,i2srecbuf1,I2S_RX_DMA_BUF_SIZE/2,(UINT*)&bw);//дÈëÎļþ
            if(res)
            {
                printf("write error:%d\r\n",res);
            }
             
        }else 
        {
            for(i=2;i<I2S_RX_DMA_BUF_SIZE;i++)
   {
     i2srecbuf2 [i  ]=i2srecbuf2[i*2];
     i2srecbuf2[i+1]=i2srecbuf2[i*2+1];
     i++;
   }
            res=f_write(f_rec,i2srecbuf2,I2S_RX_DMA_BUF_SIZE/2,(UINT*)&bw);//дÈëÎļþ
            if(res)
            {
                printf("write error:%d\r\n",res);
            }
        }
        wavsize+=I2S_RX_DMA_BUF_SIZE;
    } 
}  
汉字有乱码不影响,这个函数调用中断,从i2srecbuf1和i2srecbuf2中录音得到的数据保存到f_rec中,保存数据大小为I2S_RX_DMA_BUF_SIZE/2。
由于两个缓冲区的数据(也就是录音得到的数据)是双声道,形式是 : 【两个字节左声道 两个字节右声道 两个字节左声道 两个字节右声道。。。【
所以我们只需要左声道的数据,于是便有了我们增加的代码:
for(i=2;i<I2S_RX_DMA_BUF_SIZE;i++)
   {
     i2srecbuf1[   i]=i2srecbuf1[i*2];//i2srecbuf1=i2srecbuf1[i*2];
     i2srecbuf1[i+1]=i2srecbuf1[i*2+1];
     i++;
   }
这个循环就是将【左声道数据 右声道数据 左声道数据 右声道数据。。。】变为【左声道数据 左声道数据。。。】把右声道数据全部扔掉,所以我们执行f_write时,长度应该是I2S_RX_DMA_BUF_SIZE/2,即变为一半。
经过这样的修改,录音是没有问题了,录制的音频在电脑上播放时正常的。

第三步:

经过前两步,录制的音频没有什么问题了,但是在开发板上速度会变快,就像音频播放器实验一样,播放速度会加快一倍。这样我们就知道,音频播放也出现了问题!
所以我们修改wavplay.c代码,修改wav_buffill函数:
u32 wav_buffill(u8 *buf,u16 size,u8 bits)
{
    u16 readlen=0;
    u32 bread;
    u16 i;
    u8 *p;
    if(bits==24)//24bitÒôƵ,ÐèÒª´¦ÀíÒ»ÏÂ
    {
        readlen=(size/4)*3;                         //´Ë´ÎÒª¶ÁÈ¡µÄ×Ö½ÚÊý
        f_read(audiodev.file,audiodev.tbuf,readlen,(UINT*)&bread);  //¶ÁÈ¡Êý¾Ý
        p=audiodev.tbuf;
        for(i=0;i<size;)
        {
            buf[i++]=p[1];
            buf[i]=p[2]; 
            i+=2;
            buf[i++]=p[0];
            p+=3;
        } 
        bread=(bread*4)/3;      //Ìî³äºóµÄ´óС.
    }else 
    {
        readlen=size/2;
        f_read(audiodev.file,audiodev.tbuf,readlen,(UINT*)&bread);//16bitÒôƵ,Ö±½Ó¶ÁÈ¡Êý¾Ý  
        p=audiodev.tbuf;
        for(i=0;i<readlen;i++)
        {buf[4*i+0]=p[0];
            buf[4*i+1]=p[1];
            buf[4*i+2]=p[0];
            buf[4*i+3]=p[1];
            p+=2;
        }
        bread=bread*2;
        if(bread<size)//²»¹»Êý¾ÝÁË,²¹³ä0
        {
            for(i=bread;i<size-bread;i++)buf[i]=0; 
        }
    }
    return bread;
}  
这个函数是将audiodev.file指向的音频文件数据读取到一个buf里面(这里是audiodev.tbuf),数据大小为readlen,也就是输入参数中size的一半。
实际上我们只修改了else里面的代码,因为我们用的16bits数据:
else 
    {
        readlen=size/2;
        f_read(audiodev.file,audiodev.tbuf,readlen,(UINT*)&bread);//16bitÒôƵ,Ö±½Ó¶ÁÈ¡Êý¾Ý  
        p=audiodev.tbuf;
        for(i=0;i<readlen;i++)
        {buf[4*i+0]=p[0];
            buf[4*i+1]=p[1];
            buf[4*i+2]=p[0];
            buf[4*i+3]=p[1];
            p+=2;
        }
        bread=bread*2;
这个意思就是说,这个音频是单声道的,我们得把数据扩充到双声道,这就和第二步的操作相反了(因为wm8978和i2s设置的是双声道,如果不改wm8978.c和i2s.c这俩底层代码的话,只能这样改了,这也是淘宝技术客服最后的建议,把单声道扩展成双声道)。for就是执行的扩充操作。
另外由于这里buf(也就是i2sbuf1或者i2sbuf2)的长度是tbuf的二倍,所以我们要在u8 wav_play_song(u8* fname)函数里进行分配内存的修改,修改如下:
audiodev.i2sbuf1=mymalloc(SRAMIN,WAV_I2S_TX_DMA_BUFSIZE*2);
    audiodev.i2sbuf2=mymalloc(SRAMIN,WAV_I2S_TX_DMA_BUFSIZE*2);
    audiodev.tbuf=mymalloc(SRAMIN,WAV_I2S_TX_DMA_BUFSIZE);
这样的话就大功告成了,无论是录音实验的播放,还是音频播放器实验的播放,播放单声道的数据都没有问题了。

第四步

其实这时候发现LCD显示的录制时间,和播放时的总时间有问题,扩大了二倍,这也是我们前几步修改数据造成的,所以将时间除以二就行了。

在recorder.c中:

if(recsec!=(wavsize/wavhead->fmt.ByteRate)) //¼Òôʱ¼äÏÔʾ
            {      
                LED0=!LED0;//DS0ÉÁ˸ 
                recsec=wavsize/wavhead->fmt.ByteRate;   //¼Òôʱ¼ä
                recsec=recsec/2;
                recoder_msg_show(recsec,wavhead->fmt.SampleRate*wavhead->fmt.NumOfChannels*wavhead->fmt.BitsPerSample);//ÏÔʾÂëÂÊ
            }
在wavplay.c中:
wav_get_curtime(audiodev.file,&wavctrl);//µÃµ½×Üʱ¼äºÍµ±Ç°²¥·ÅµÄʱ¼ä 
                        audio_msg_show((wavctrl.totsec)/2,wavctrl.cursec,wavctrl.bitrate);

至此,问题就全部解决了。这里只是修改了recorder.c和wavplay.c文件,相比较而言是最高层的文件,当然有其他修改方法欢迎大家来分享。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容