tag 标签: 录音机

相关博文
  • 热度 21
    2013-4-13 23:01
    2404 次阅读|
    1 个评论
      第五十章 录音机实验     上一章,我们实现了一个简单的音乐播放器,本章我们将在上一章的基础上,实现一个简单的录音机,实现WAV录音。本章分为如下几个部: 50.1 WAV简介 50.2 硬件设计 50.3 软件设计 50.4 下载验证   50.1 WAV简介 WAV即WAVE文件,WAV是计算机领域最常用的数字化声音文件格式之一,它是微软专门为Windows系统定义的波形文件格式(Waveform Audio),由于其扩展名为"*.wav"。它符合RIFF(Resource Interchange File Format)文件规范,用于保存Windows平台的音频信息资源,被Windows平台及其应用程序所广泛支持,该格式也支持MSADPCM,CCITT A LAW等多种压缩运算法,支持多种音频数字,取样频率和声道,标准格式化的WAV文件和CD格式一样,也是44.1K的取样频率,16位量化数字,因此在声音文件质量和CD相差无几! ALIENTEK战舰STM32开发板板载的VS1053支持2种格式的WAV录音:PCM格式或者IMA ADPCM格式,其中PCM(脉冲编码调制)是最基本的WAVE文件格式,这种文件直接存储采样的声音数据没有经过任何的压缩。而IAM ADPCM则是使用了压缩算法,压缩比率为4:1。 本章,我们主要讨论PCM,因为这个最简单。我们将利用VS1053实现16位,8Khz采样率的单声道WAV录音(PCM格式)。要想实现WAV录音得先了解一下WAV文件的格式,WAVE文件是由若干个Chunk组成的。按照在文件中的出现位置包括:RIFF WAVE Chunk、 Format Chunk、 Fact Chunk(可选)和 Data Chunk。每个Chunk由块标识符、数据大小和数据三部分组成,如图50.1.1所示:   图50.1.1 Chunk结构示意图        其中块标识符由4个ASCII码构成,数据大小则标出紧跟其后的数据的长度(单位为字节),注意这个长度不包含块标识符和数据大小的长度,即不包含最前面的8个字节。所以实际Chunk的大小为数据大小加8。 首先,我们来看看RIFF块(RIFF WAVE Chunk),该块以“RIFF”作为标示,紧跟wav文件大小(该大小是wav文件的总大小-8),然后数据段为“WAVE”,表示是wav文件。RIFF块的Chunk结构如下: //RIFF块 typedef __packed struct {     u32 ChunkID;             //chunk id;这里固定为"RIFF",即0X46464952     u32 ChunkSize ;            //集合大小;文件总大小-8     u32 Format;                //格式;WAVE,即0X45564157 }ChunkRIFF ; 接着,我们看看Format块(Format Chunk),该块以“fmt ”作为标示(注意有个空格!),一般情况下,该段的大小为16个字节,但是有些软件生成的wav格式,该部分可能有18个字节,含有2个字节的附加信息。Format块的Chunk结构如下: //fmt块 typedef __packed struct {     u32 ChunkID;             //chunk id;这里固定为"fmt ",即0X20746D66     u32 ChunkSize ;            //子集合大小(不包括ID和Size);这里为:20.     u16 AudioFormat;        //音频格式;0X10,表示线性PCM;0X11表示IMA ADPCM        u16 NumOfChannels;    //通道数量;1,表示单声道;2,表示双声道;        u32 SampleRate;           //采样率;0X1F40,表示8Khz        u32 ByteRate;               //字节速率;        u16 BlockAlign;            //块对齐(字节);        u16 BitsPerSample;              //单个采样数据大小;4位ADPCM,设置为4 }ChunkFMT;  接下来,我们再看看Fact块(Fact Chunk),该块为可选块,以“fact”作为标示,不是每个WAV文件都有,在非PCM格式的文件中,一般会在Format结构后面加入一个Fact块,该块Chunk结构如下: //fact块 typedef __packed struct {     u32 ChunkID;                    //chunk id;这里固定为"fact",即0X74636166;     u32 ChunkSize ;                 //子集合大小(不包括ID和Size);这里为:4.     u32 DataFactSize;               //数据转换为PCM格式后的大小 }ChunkFACT; DataFactSize是这个Chunk中最重要的数据,如果这是某种压缩格式的声音文件,那么从这里就可以知道他解压缩后的大小。对于解压时的计算会有很大的好处!不过本章我们使用的是PCM格式,所以不存在这个块。 最后,我们来看看数据块(Data Chunk),该块是真正保存wav数据的地方,以“data”'作为该Chunk的标示。然后是数据的大小。紧接着就是wav数据。根据Format Chunk中的声道数以及采样bit数,wav数据的bit位置可以分成如表50.1.1所示的几种形式: 单声道 取样1 取样2 取样3 取样4 8 位量化 声道0 声道0 声道0 声道0 双声道 取样1 取样2 8 位量化 声道0(左) 声道1(右) 声道0(左) 声道1(右) 单声道 取样1 取样2 16 位量化 声道0(低字节) 声道0(高字节) 声道0(低字节) 声道0(高字节) 双声道 取样1 16 位量化 声道0 (左,低字节) 声道0 (左,高字节) 声道1 (右,低字节) 声道1 (右,高字节) 表50.1.1 WAVE文件数据采样格式        本章,我们采用的是16位,单声道,所以每个取样为2个字节,低字节在前,高字节在后。数据块的Chunk结构如下: //data块 typedef __packed struct {     u32 ChunkID;             //chunk id;这里固定为"data",即0X61746164     u32 ChunkSize ;            //子集合大小(不包括ID和Size);文件大小-60. }ChunkDATA;        通过以上学习,我们对WAVE文件有了个大概了解。接下来,我们看看如何使用VS1053实现WAV(PCM格式)录音。        激活PCM 录音 VS1053激活PCM录音需要设置的寄存器和相关位如表50.1.2所示:   图50.1.2 VS1053激活PCM录音相关寄存器        通过设置SCI_MODE寄存器的2、12、14位,来激活PCM录音,SCI_MODE的各位描述见表49.1.4(也可以参考VS1053的数据手册)。SCI_AICTRL0寄存器用于设置采样率,我们本章用的是8K的采样率,所以设置这个值为8000即可。SCI_AICTRL1寄存器用于设置AGC,1024相当于数字增加1,这里建议大家设置AGC在4(4*1024)左右比较合适。SCI_AICTRL2用于设置自动AGC的时候的最大值,当设置为0的时候表示最大64(65536),这个大家按自己的需要设置即可。最后,SCI_AICTRL3,我们本章用到的是咪头线性PCM单声道录音,所以设置该寄存器值为6。 通过这几个寄存器的设置,我们就激活VS1053的PCM录音了。不过,VS1053的PCM录音有一个小BUG,必须通过加载patch才能解决,如果不加载patch,那么VS1053是不输出PCM数据的,VLSI提供了我们这个patch,只需要通过软件加载即可。        读取PCM 数据 在激活了PCM录音之后,SCI_HDAT0和SCI_HDAT1有了新的功能。VS1053的PCM采样缓冲区由1024个16位数据组成,如果SCI_HDAT1大于0,则说明可以从SCI_HDAT0读取至少SCI_HDAT1个16位数据,如果数据没有被及时读取,那么将溢出,并返回空的状态。 注意,如果SCI_HDAT1≥896,最好等待缓冲区溢出,以免数据混叠。所以,对我们来说,只需要判断SCI_HDAT1的值非零,然后从SCI_HDAT0读取对应长度的数据,即完成一次数据读取,以此循环,即可实现PCM数据的持续采集。 最后,我们看看本章实现WAV录音需要经过哪些步骤: 1) 设置VS1053 PCM 采样参数 这一步,我们要设置PCM的格式(线性PCM)、采样率(8K)、位数(16位)、通道数(单声道)等重要参数,同时还要选择采样通道(咪头),还包括AGC设置等。可以说这里的设置直接决定了我们wav文件的性质。 2) 激活VS1053 的PCM 模式,加载 patch 通过激活VS1053的PCM格式,让其开始PCM数据采集,同时,由于VS1053的BUG,我们需要加载patch,以实现正常的PCM数据接收。 3) 创建WAV 文件,并保存wav 头 在前两部设置成功之后,我们即可正常的从SCI_HDAT0读取我们需要的PCM数据了,不过在这之前,我们需要先在创建一个新的文件,并写入wav头,然后才能开始写入我们的PCM数据。 4) 读取PCM 数据 经过前面几步的处理,这一步就比较简单了,只需要不停的从SCI_HDAT0读取数据,然后存入wav文件即可,不过这里我们还需要做文件大小统计,在最后的时候写入wav头里面。 5) 计算整个文件大小,重新保存wav 头并关闭文件 在结束录音的时候,我们必须知道本次录音的大小(数据大小和整个文件大小),然后更新wav头,重新写入文件,最后因为FATFS,在文件创建之后,必须调用f_close,文件才会真正体现在文件系统里面,否则是不会写入的!所以最后还需要调用f_close,以保存文件。 50.2 硬件设计 本章实验功能简介:开机的时候先检测字库,然后初始化VS1053,进行RAM测试和正弦测试,之后,检测SD卡根目录是否存在RECORDER文件夹,如果不存在则创建,如果创建失败,则报错。在找到SD卡的RECORDER文件夹后,即设置VS1053进入录音模式,此时可以在耳机听到VS1053采集的音频。KEY0用于开始/暂停录音,KEY2用于保存并停止录音,WK_UP用于AGC增加、KEY1用于AGC减小,TPAD用于播放最近一次的录音。当我们按下KEY0的时候,可以在屏幕上看到录音文件的名字,以及录音时间,然后通过KEY2可以保存该文件,同时停止录音(文件名和时间也都将清零),在完成一个录音后,我们可以通过按TPAD按键,来试听刚刚的录音。DS0用于提示程序正在运行,DS1用于指示当前是否处于录音暂停状态。 本实验用到的资源如下: 1)  指示灯DS0和DS1 2)  五个按键(WK_UP/KEY0/KEY1/KEY2/TPAD) 3)  串口 4)  TFTLCD模块 5)  SD卡 6)  SPI FLASH 7)  VS1053 8)  74HC4052 9)  TDA1308 本章用到的硬件资源同上一章基本一样,就多了一个TPAD按键,用于播放最近一次录音。 本实验,大家需要准备1个SD卡和一个耳机,分别插入SD卡接口和耳机接口,然后下载本实验就可以实现一个简单的录音机了。 50.3 软件设计 打开上一章的工程,首先在APP文件夹下面新建recorder.c和recorder.h两个文件,然后将recorder.c加入到工程的APP组下。 因为recorder.c代码比较多,我们这里仅介绍其中的三个函数,首先是设置VS1053进入PCM模式的函数:recoder_enter_rec_mode,该函数代码如下: //进入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);             //设置增益,0,自动增益.1024相当于1倍,512相当于0.5倍,最大值65535=64倍       VS_WR_Cmd(SPI_AICTRL2,0);         //设置增益最大值,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 } 该函数就是用我们前面介绍的方法,激活VS1053的PCM模式,本章,我们使用的是8Khz采样率,16位单声道线性PCM模式,AGC通过函数参数设置。最后加载patch(用于修复VS1053录音BUG)。 第二个函数是初始化wav头的函数:recoder_wav_init,该函数代码如下: //初始化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;                     //数据大小,还需要计算 } 该函数初始化wav头的绝大部分数据,这里我们设置了该wav文件为8Khz采样率,16位线性PCM格式,另外由于录音还未真正开始,所以文件大小和数据大小都还是未知的,要等录音结束才能知道。该函数__WaveHeader结构体就是由前面介绍的三个Chunk组成,结构为: //wav头 typedef __packed struct {        ChunkRIFF riff;     //riff块        ChunkFMT fmt;  //fmt块        //ChunkFACT fact; //fact块 线性PCM,没有这个结构体        ChunkDATA data;   //data块         }__WaveHeader; 最后,我们介绍recoder_play函数,是录音机实现的主循环函数,该函数代码如下:    非常抱歉,由于编辑器篇幅所限,剩下内容,请看附件。
相关资源
  • 所需E币: 1
    时间: 2022-5-5 13:00
    大小: 39.26MB
    上传者: 西风瘦马
    盒式录音机机芯结构原理与维修_10832040_李....pdf
  • 所需E币: 0
    时间: 2021-3-17 17:17
    大小: 7.61MB
    上传者: Argent
    arm公司设计的内核在电子产品MCU中仍占据主流,其设计的armcortex内核有多个系列,根据产品设计需求选择相应的类型,而Cortex-M系列是面向具有确定性的微控制器应用的成本敏感型解决方案,分享关于Cortex-M3的综合性讲解资料,欢迎下载阅读。