本文介绍了基于waveX低级音频API采集音频,然后播放的技术,也支持实时的播放。对于将录音和播放分开的做法原因是为了保存pcm文件(未经压缩过的音频文件),

然后转码wav和mp3格式。

PCM(Pulse code modulation):脉冲编码调制 ,即对波形按照固定周期频率采样。为了保证采样后数据质量,采样频率必须是样本声音最高频率的两倍,这就是 Nyquist 频率 .



任务:C++开发---录音播放系统

实现的功能:

1,麦克风录入声音,保存为pcm(未压缩的音频文件格式)文件

2,播放pcm声音文件

3,将pcm文件转码为mp3,wav格式。

一:基础知识点

关于音频相关的知识点以及音频格式推荐篇文章给大家看:音频格式 

二:录入和播放流程

对于音频的录入和播放我们采用waveX的API接口,在介绍这些函数接口之前,我们必须明确声音在采集(录音)和播放的时需要有一些统一的格式,包括音频格式类型,声道,采样率等信息。下面的数据结构具体描述了该格式:

typedef struct tWAVEFORMATEX
{
    WORD        wFormatTag;         /* format type */
    WORD        nChannels;          /* number of channels (i.e. mono, stereo...) */
    DWORD       nSamplesPerSec;     /* sample rate */
    DWORD       nAvgBytesPerSec;    /* for buffer estimation */
    WORD        nBlockAlign;        /* block size of data */
    WORD        wBitsPerSample;     /* number of bits per sample of mono data */
    WORD        cbSize;             /* the count in bytes of the size of */
                                    /* extra information (after cbSize) */
} WAVEFORMATEX, *PWAVEFORMATEX, NEAR *NPWAVEFORMATEX, FAR *LPWAVEFORMATEX;

其中,wFormatTag是音频格式类型,一般默认为PCM_FORMAT_PCM,nChannels是声道数,nSamplesPerSec是采样频率,nAvgBytesPerSec是每秒钟的字节数,nBlockAlign是每个样本的字节数,wBitsPerSample是每个样本的量化位数,cbSize是附加信息的字节大小。


无论在录入和播放的时候,必须有存音频文件的结构,其内容如下

typedef struct { 
    LPSTR  lpData; //内存指针
    DWORD  dwBufferLength;//长度 
    DWORD  dwBytesRecorded; //已录音的字节长度
    DWORD  dwUser; 
    DWORD  dwFlags; 
    DWORD  dwLoops; //循环次数
    struct wavehdr_tag * lpNext; 
    DWORD  reserved; 
} WAVEHDR;

其中,lpData是指定的缓冲块地址,dwBufferLength是指定的缓冲块大小,dwBytesRecorded是已录音数据大小,dwUser是用户数据,dwFlags是控制标志,表明缓冲的使用状态,dwLoops是音频输出时缓冲数据块循环的次数,lpNext和reserved是系统保留数据。在程序实现时,通过设置或修改这个结构的相关参数来实现对音频输入和输出缓冲区的控制。


声卡输入和输出的音频属性可定义如下:

mywaveform.wFormatTag = WAVE_FORMAT_PCM;
mywaveform.nChannels = 1;
mywaveform.nSamplesPerSec = 8000;
mywaveform.wBitsPerSample = 16;
mywaveform.cbSize = 0;
mywaveform.nBlockAlign = (mywaveform.wBitsPerSample * mywaveform.nChannels) >> 3;
mywaveform.nAvgBytesPerSec = mywaveform.nBlockAlign * mywaveform.nSamplesPerSec;

设置的音频格式类型是PCM格式,单通道,8000HZ的采样率。

音频数据块头结构定义如下


pWaveHdr->lpData            = pDataBuf;     // 指定缓冲的地址;
pWaveHdr->dwBufferLength    = bufferLength; //指定缓冲的大小;
pWaveHdr->dwBytesRecorded   = 0 ;
pWaveHdr->dwUser            = 0 ;
pWaveHdr->dwFlags           = 0 ;
pWaveHdr->dwLoops           = 1 ;
pWaveHdr->lpNext            = NULL;
pWaveHdr->reserved          = 0;


 

调用WaveX低级音频函数API启动声卡录音的基本操作步骤如下图所示:

打开录音设备: waveInOpen
                            ↓
         为录音设备准备缓存: waveInPrepareHeader
                            ↓
         为输入设备增加缓存: waveInAddBuffer
                            ↓
                   启动录音: waveInStart
                            ↓
                   清除缓存: waveInUnprepareHeader
                            ↓
                   停止录音: waveInReset
                            ↓
               关闭录音设备: waveInClose


录音流程


        在这个过程中,会产生很多WM_WIM_***格式的WINDOWS消息。程序通过捕获这些消息对缓存,数据和设备进行处理,具体可见后面章节。录音设备打开时,可以指定消息的响应方式:回掉函数,线程ID,WINDOWS窗口句柄或事件句柄等。



MMRESULT waveInOpen(
    LPHWAVEIN phwi,             //输入设备句柄
    UINT uDeviceID,             //输入设备ID
    LPWAVEFORMATEX pwfx,        //录音格式指针
    DWORD dwCallback,           //处理消息的回调函数或窗口句柄,线程ID等
    DWORD dwCallbackInstance,   //通常为0
    DWORD fdwOpen               //处理消息方式的符号位
);



调用WaveX低级音频函数API启动声卡输出的基本操作步骤如下图所示:

打开输出设备: waveOutOpen
                            ↓
         为输出设备准备缓存: waveOutPrepareHeader
                            ↓
       写数据导输出设备缓存: waveOutWrite
                            ↓
               清除输出缓存: waveOutUnprepareHeader
                            ↓
                   停止输出: waveOutReset
                            ↓
               关闭输出设备: waveOutClose

WINDOWS下提供消息映射实现事件的处理。低级音频函数处理声音数据块也正要归功于WINDOWS的消息映射机制。

我们的代买中主要是运用所提供的回调函数,来处理一些消息。

分别描述了声卡录音和播放过程中产生的消息:

WIM_OPEN
            │     ↘
            │       音频输入设备打开消息
            ↓
        WIM_DATA
            │     ↘
            │       缓冲录满或停止录音消息
            ↓
        WIM_CLOSE
            │      ↘
            ↓       音频输入设备关闭消息
 




        WOM_OPEN
            │     ↘
            │       音频输出 设备打开消息
            ↓
        WOM_DONE
            │     ↘
            │       缓冲播放完或停止输出消息
            ↓
        WOM_CLOSE
            │      ↘
            ↓       音频输出设备关闭消息

三:运用lame库实现文件的转换pcm-->mp3

pcm-->wav只需要添加wav文件的一些头格式,然后把pcm文件内容全部写进去,就生成了wav文件。这是因为wav文件本来就是未经压缩的pcm文件构成,知识添加了用以标示wav文件的头格式而已。所以当你生成wav文件后你就会发现,其实它的大小没有多大改变。MP3格式则不同,它会小得多。而且必须注意的是,MP3文件在设置格式的时候好像必须是双声道,采样率是44100。

我们转MP3文件时,用到的就是libmp3lame这个库文件,所以你必须在网上下载这个库,并且配置好头文件lame.h,动态库,静态库路径,这样才会编译过去。具体的配置问问度娘就好了。或者你直接去下载我的源码,把文件中的库文件拿出来就好,不过路径还得你自己配置了。

关于lame库API的介绍详情请参考源码。

四:难点和重点

完成这个系统后,感觉最大的难点和重点就是对waveX函数簇的理解,包括各种API的参数,音频配置格式(这决定了你播放时听到的音频质量),还有回调函数里消息处理的理解,还有如何解决播放时的实时性和连续性(一般采用多缓冲技术)。对于后面的转码函数,你必须清楚地了解你的源文件pcm音频文件的格式,还有所需要转码成的MP3,wav音频文件的格式。还有系统开发完成后用户测试时的一些问题。比如,如何停止录音,实时播放录音。录音文件的非追加性等等。


双/多缓冲技术可以很好的实现声音的快速连续采集和实时顺畅播放。采集声音时,缓冲满了会有一个消息,程序在响应这个消息需要几毫秒~几十毫秒甚至更多的时间,假设为Xms,如果只使用一个缓冲,程序必须在响应完该消息才再次采集声音,那么在这Xms的时间里,没有采集到任何声音;声音的播放也是一样的道理,这样声音就会不连续。因此双缓冲或多缓冲技术是必要的,让输入和输出设备可以循环使用这些缓冲,当程序在响应某块缓冲数据已满或播放完毕消息时,声卡可以继续往下一块缓冲添加数据或播放下一块缓冲的数据,如此循环保障声音的连续性。



五:源码

具体操作界面:

java pcm文件转成wav pcm文件怎么转换mp3_api

必须注意的是每次2选项播放文件后,必须4停止播放。然后通过6,7,选项即可生成mp3,wav格式

已生成具体的文件:

java pcm文件转成wav pcm文件怎么转换mp3_mp3_02

测试已成功!

博主水平有限,系统还存在很多问题,还请各位读者不吝赐教!!!

以下是些参考资料,读者可能会看到很多内容都是抄袭,博主只是觉得人家总结的很清楚,对于你们的理解有帮助,所以还请各位嘴下留情!


http://www.programgo.com/article/86251552095/

源码地址:链接: http://pan.baidu.com/s/1pLHHY8j 密码: smzn