本文转载自博客:http://blog.csdn.net/gongluck93/article/details/53096013
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
在WINDOWS下,音频函数有多种类型,如MCI、多媒体OLE控制、高级音频等,使用方法都比较简单。
但如果想编写一个功能较强大的音频处理程序,那就必须使用低级音频函数和多媒体文件I/O来控制音频设备的输入和输出。
因为低级音频函数可直接与音频驱动程序交互,通过窗口消息或回调(CALLBACK)函数来管理音频数据块的记录和播放,控制非常灵活。重要的一点是,低级音频函数为我们提供了一个设备无关的接口。
Header: Declared in Mmsystem.h; include Windows.h.
Library: Use Winmm.lib.
WAVEOUT
1.waveOutGetNumDevs函数
The waveOutGetNumDevs function retrieves the number of waveform-audio output devices present in the system.
获取系统中波形音频输出设备的数目,如果为0,呵呵。
2.waveOutGetDevCaps
The waveOutGetDevCaps function retrieves the capabilities of a given waveform-audio output device.
获取波形输出设备的性能、能力
-
- MMRESULT waveOutGetDevCaps(
- UINT_PTR uDeviceID,
- LPWAVEOUTCAPS pwoc,
- UINT cbwoc
- );
-
- MMSYSERR_BADDEVICEID = 2;
- MMSYSERR_NODRIVER = 6;
-
-
- typedef struct {
- WORD wMid;
- WORD wPid;
- MMVERSION vDriverVersion;
- TCHAR szPname[MAXPNAMELEN];
- DWORD dwFormats;
- WORD wChannels;
- WORD wReserved1;
- DWORD dwSupport;
- } WAVEOUTCAPS;
-
-
- WAVE_INVALIDFORMAT = $00000000;
- WAVE_FORMAT_1M08 = $00000001;
- WAVE_FORMAT_1S08 = $00000002;
- WAVE_FORMAT_1M16 = $00000004;
- WAVE_FORMAT_1S16 = $00000008;
- WAVE_FORMAT_2M08 = $00000010;
- WAVE_FORMAT_2S08 = $00000020;
- WAVE_FORMAT_2M16 = $00000040;
- WAVE_FORMAT_2S16 = $00000080;
- WAVE_FORMAT_4M08 = $00000100;
- WAVE_FORMAT_4S08 = $00000200;
- WAVE_FORMAT_4M16 = $00000400;
- WAVE_FORMAT_4S16 = $00000800;
-
-
- WAVECAPS_PITCH = $0001;
- WAVECAPS_PLAYBACKRATE = $0002;
- WAVECAPS_VOLUME = $0004;
- WAVECAPS_LRVOLUME = $0008;
- WAVECAPS_SYNC = $0010;
- WAVECAPS_SAMPLEACCURATE = $0020;
- WAVECAPS_DIRECTSOUND = $0040;
3.waveOutOpen--MM_WOM_OPEN
The waveOutOpen function opens the given waveform-audio output device for playback.
打开波形设备
-
- MMRESULT waveOutOpen(
- LPHWAVEOUT phwo,
- UINT_PTR uDeviceID,
-
-
- LPWAVEFORMATEX pwfx,
- DWORD_PTR dwCallback,
-
- DWORD_PTR dwCallbackInstance,
- DWORD fdwOpen
- );
-
-
- CALLBACK_EVENT
- CALLBACK_FUNCTION
- CALLBACK_NULL
- CALLBACK_THREAD
- CALLBACK_WINDOW
- WAVE_ALLOWSYNC
-
-
- WAVE_FORMAT_DIRECT
- WAVE_FORMAT_QUERY
-
- WAVE_MAPPED
-
4.waveOutPrepareHeader
The waveOutPrepareHeader function prepares a waveform-audio data block for playback.
准备工作
-
- MMRESULT waveOutPrepareHeader(
- HWAVEOUT hwo,
- LPWAVEHDR pwh,
- UINT cbwh
- );
-
-
- typedef struct {
- LPSTR lpData;
- DWORD dwBufferLength;
- DWORD dwBytesRecorded;
- DWORD_PTR dwUser;
- DWORD dwFlags;
- DWORD dwLoops;
- struct wavehdr_tag * lpNext;
- DWORD_PTR reserved;
- } WAVEHDR;
-
-
- WHDR_BEGINLOOP
- WHDR_DONE
- WHDR_ENDLOOP
- WHDR_INQUEUE
- WHDR_PREPARED
-
-
- MMRESULT waveOutUnprepareHeader(
- HWAVEOUT hwo,
- LPWAVEHDR pwh,
- UINT cbwh
- );
5.waveOutWrite--MM_WOM_DONE
The waveOutWrite function sends a data block to the given waveform-audio output device.
发送数据到音频输出设备
-
- MMRESULT waveOutWrite(
- HWAVEOUT hwo,
- LPWAVEHDR pwh,
- UINT cbwh
- );
通过使用waveOutPause、waveOutRestart、waveOutReset(MM_WOM_DONE)和waveOutClose(MM_WOM_CLOSE)来进行暂停、重新启动、停止播放和关闭设备。
WAVEIN
记录数字音频的方法基本同播放过程,不同在于记录期间是不提供诸如暂停和重新开始这样的控制的。
使用到的函数包括:waveInGetNumDevs、waveInGetDevCaps、waveInOpen(MM_WIM_OPEN)、waveInPrepareHeader、waveInUnPrepareHeader、waveInAddBuffer、waveInReset(MM_WIM_DATA)、waveInStart、waveInStop、waveInClose(MM_WIM_CLOSE)等。
音频的输入大体分三步:
1 打开设备 -----waveInOpen(打开一个音频输入设备)
2 开始录音------waveInStart开始录音
3 关闭设备------waveInClose关闭录音。之前调用一下waveInReset,这样可以清掉尚在等待录音的缓冲区
常用的相关API为:
waveInOpen(打开一个音频输入设备)
waveInPrepareHeader(为一个即将在waveInAddBuffer中调用的输入缓冲区准备头部)
waveInAddBuffer(添加一个输入用的数据缓冲区)
waveInStart(开始录音)
waveInClose(关闭音频输入设备)等几个,以及需要在waveInOpen中指定的一个回调函数或者线程,其作用是在一个数据缓冲区被录满后被调用,以对这些数据进行处理,和其他一些相关的操作。注意这里的一个数据缓冲区。
-
- MMRESULT waveInOpen(
- LPHWAVEIN phwi,
- UINT uDeviceID,
- LPWAVEFORMATEX pwfx,
- DWORD dwCallback,
- DWORD dwCallbackInstance,
- DWORD fdwOpen
- );
至于pwfx,则比较关键,它指定了要以什么音频格式打开音频输入设备,它是一个结构WAVEFORMATEX:
- typedef struct {
- WORD wFormatTag;
-
- WORD nChannels;
- DWORD nSamplesPerSec;
- DWORD nAvgBytesPerSec;
-
-
-
- WORD nBlockAlign;
-
- WORD wBitsPerSample;
- WORD cbSize;
-
- } WAVEFORMATEX;
下面要做的事情就是准备几个用做录音的缓冲区。常准备多个缓冲区,并在回调中循环使用。对于缓冲区,得使用waveInPerpareHeader准备一下头部,这个API比较简单,如果你是循环使用缓冲区,对每个缓冲区也只需要调用一次waveInPrepareHeader。为什么要使用一次就可以。参看waveInPerpareHeader说明就明白。此函数功能就是定位缓冲区的数据区地址,和数据大小。以便为系统所用。
A)首先得确定一下需要用什么回调方式,即在某个时间片的音频数据被录完后,Windows将通过这个回调来激活对这些数据的处理过程,一般用到的无非是FUNCTION、THREAD和EVENT这几类,而比较方便简单的就是FUNCTION和THREAD了。FUNCTION方式是指Windows会调用你这个函数,而THREAD则是由Windows来激活你所指定的线程。这些都在waveInOpen中指定。
B)一切准备好之后,就可以调用waveInAddBuffer和waveInStart开始录音了,只要你一调用这个waveInStart,录音就开始了,即使这个缓冲区录满之后你没有加入新的缓冲区进去,录音也不会停,只是这中间的语音数据全都丢了。当通过waveInAddBuffer送入的缓冲区被录满后,Windows就会通过你在waveInOpen中指定的方式进行回调,在回调中把录好的语音数据取出来,并且,如果还想继续录音的话,得将下一个缓冲区添加进去。考虑到这个处理是有时间延迟的,而且音频对时间很敏感,一般都要先预加入若干个缓冲区,有人提出:比如,一共定义了8个缓冲区,而为了保险起见,最好保证任一时刻至少有3个缓冲区可被录音使用,那么在开始录音时,则先加入4个缓冲区,然后在回调中,如果当前录好的缓冲区第n个,则对第(n+4)%8调用waveInAddBuffer,这时,还有第(n+1)%8,(n+2)%8, (n+3)%8这三个缓冲区可用,即基本上就可以保证所录得音频中不会有断开的间隔。比如0,1,2,3这些个先加入,当0好的时候对4,5 ,6 ,7调用waveInAddBuffer。
如此这样何不:开始的时候把8个全部放入缓冲区,当一个缓冲区满后调用回调,处理后立即把这个缓冲区重用,继续添加到缓冲区队列中。不更简单明了。如下
mmReturn = ::waveInPrepareHeader ( m_hRecord, pHdr, sizeof(WAVEHDR) ); //准备
mmReturn = ::waveInAddBuffer ( m_hRecord, pHdr, sizeof(WAVEHDR) );//添加
注意这两步都是在回调,或者线程中处理的。
C)想结束录音时,最好在waveInClose之前调用一下waveInReset,这样可以清掉尚在等待录音的缓冲区,这里常见的问题是等待的缓冲区清理了,可是正在用的缓冲区怎么办?如果这个时候就用waveInClose,那么系统会出错。解决方法一:在回调函数中注意,一个缓冲区满后,不要再用waveInAddBuffer增加缓存,当缓冲区用到1的时候调用waveInReset清掉尚在等待录音的缓冲区继续waveInClose。
总结上面的注意3点:回调的选取,注意缓冲区的原理,注意结束的处理
windows waveform方式实现录音要通过这么几步:(注意顺序!!)
一、打开录音设备
waveinopen()函数
注意,调用之前要填写好wav头信息(包含采样率、采样位数等);还要定义好回调函数等,回调函数的解释后面讲。
二、准备好录音缓存空间
waveinprepareheader()函数
这一步为了准备好将要送入录音设备的缓存,以便之后可以供之使用。
一般至少需要准备两块缓存。因为录音不能间断,当一块填满时没有时间等待你去送入下一块缓存,所以必须提前就准备好。
三、将缓存送入录音设备
waveinaddbuffer()函数
将缓存送入录音设备,供之存入已录下的音频。开始录音时,应至少送入两块不同的缓存,即调用两次这个函数。之后为了不致录音产生间断,应保证至少有一块缓存在录音时为空,以备衔接。
四、开始录音
waveinstart()函数
当以上的工作都准备好时,便可调用此函数开始录音了。一旦调用,录音设备便立即录音并存入已经送来的缓存块内,当被送来的有多个缓存块时,按照FIFO的原则向缓存块内存入录音数据。此函数执行之后可以执行一个while()循环,来等待录音设备录音。为了减少cpu使用率,可以在循环中加人sleepex(x,TRUE),x单位ms,TRUE必须要有。
每个缓冲块存满时,会产生一个回调信号,并调用回调函数(或回调窗口等,具体定义在waveinopen函数内,这里只讲回调函数的情况);回调信号自动被回调函数接收,回调函数根据回调信号来执行各种相应的操作。回调函数执行完后,程序跳回到原来执行位置继续执行。回调函数的具体如下:
回调信号一般有三个,对应着三种回调函数被调用的情况:
1、 WIM_OPEN
当执行waveinopen()函数时,会调用回调函数,并产生这个回调信号。代表录音设备已经打开。在这次回调函数的调用中,可以自己设定一些操作,也可以没有操作。
2、 WIM_DATA
当每块缓存块填满时,产生这个回调信号,并调用回调函数。在这次调用中,回调函数应当完成这样的工作,以便录音连续进行:
将存满的缓存块处理,例如存入文件,或送入其他设备;
向录音设备送入新的缓存块;录音设备任何时刻应当拥有不少于2个的缓存块,以保证录音不间断性。
3、 WIM_CLOSE
当调用waveinclose函数时,会产生这个回调信号,代表录音设备关闭成功。这次回调函数调用中,可以执行相应的一些关闭文件保存信息等等的操作,自定义。
五、停止录音,关闭设备
waveinstop() 停止录音
waveinreset() 复位
waveinclose()关闭设备
依次调用这些函数,来结束录音。
最后,注意在代码开头要包含windows.h和mmsystem.h两个头文件,还要加人库winmm.lib,用#pragma comment(lib,”winmm.lib”)即可。
主要顺序就是这些,注意每一步要完成的工作,一旦没有按顺序执行或者没有把每步应当完成的工作做完,录音是不能够启动的。
以下是各函数返回值的部分可能结果:
-
- #define MMSYSERR_NOERROR 0 /* no error */
- #define MMSYSERR_ERROR (MMSYSERR_BASE + 1) /* unspecified error */
- #define MMSYSERR_BADDEVICEID (MMSYSERR_BASE + 2) /* device ID out of range */
- #define MMSYSERR_NOTENABLED (MMSYSERR_BASE + 3) /* driver failed enable */
- #define MMSYSERR_ALLOCATED (MMSYSERR_BASE + 4) /* device already allocated */
- #define MMSYSERR_INVALHANDLE (MMSYSERR_BASE + 5) /* device handle is invalid */
- #define MMSYSERR_NODRIVER (MMSYSERR_BASE + 6) /* no device driver present */
- #define MMSYSERR_NOMEM (MMSYSERR_BASE + 7) /* memory allocation error */
- #define MMSYSERR_NOTSUPPORTED (MMSYSERR_BASE + 8) /* function isn't supported */
- #define MMSYSERR_BADERRNUM (MMSYSERR_BASE + 9) /* error value out of range */
- #define MMSYSERR_INVALFLAG (MMSYSERR_BASE + 10) /* invalid flag passed */
- #define MMSYSERR_INVALPARAM (MMSYSERR_BASE + 11) /* invalid parameter passed */
- #define MMSYSERR_HANDLEBUSY (MMSYSERR_BASE + 12) /* handle being used */
-
-
- #define MMSYSERR_INVALIDALIAS (MMSYSERR_BASE + 13) /* specified alias not found */
- #define MMSYSERR_BADDB (MMSYSERR_BASE + 14) /* bad registry database */
- #define MMSYSERR_KEYNOTFOUND (MMSYSERR_BASE + 15) /* registry key not found */
- #define MMSYSERR_READERROR (MMSYSERR_BASE + 16) /* registry read error */
- #define MMSYSERR_WRITEERROR (MMSYSERR_BASE + 17) /* registry write error */
- #define MMSYSERR_DELETEERRO (MMSYSERR_BASE + 18) /* registry delete error */
- #define MMSYSERR_VALNOTFOUND (MMSYSERR_BASE + 19) /* registry value not found */
- #define MMSYSERR_NODRIVERCB (MMSYSERR_BASE + 20) /* driver does not call DriverCallback */
- #define MMSYSERR_MOREDATA (MMSYSERR_BASE + 21) /* more data to be returned */
- #define MMSYSERR_LASTERROR (MMSYSERR_BASE + 21) /* last error in range */
-
-
- #define WAVERR_BADFORMAT (WAVERR_BASE + 0) /* unsupported wave format */
- #define WAVERR_STILLPLAYING (WAVERR_BASE + 1) /* still something playing */
- #define WAVERR_UNPREPARED (WAVERR_BASE + 2) /* header not prepared */
- #define WAVERR_SYNC (WAVERR_BASE + 3) /* device is synchronous */
- #define WAVERR_LASTERROR (WAVERR_BASE + 3) /* last error in range */
详解WAVE音频文件格式
WAVE声音文件格式是目前Windows最直接保存声音数据的文件格式.在涉及声音信号处理时大多是对WAV文件直接操作,有必要搞清楚所研究声音的文件格式.
RIFF文件与WAV文件
在Windows环境下,大部分多媒体文件都依循着一种结构来存放信息,称为资源互换文件格式(Resources Interchange File Format),简称RIFF。比如声音的WAV文件,视频的AVI文件,动画的MMM文件等均是由此结构衍生出来的.所以,要掌握多媒体文件格式,首先得认识RIFF的结构.
RIFF是一种树状结构,其基本组成单位是chunk(即块),每个chunk由辨识码,数据大小和数据组成,如下图。可以看出,一个chunk的长度,就是数据的大小加上8Byte.
一般而言,chunk本身不允许内部再包含chunk,但有两个例外:以"RIFF"和以"UST"为辨识码的chunk。针对这两种chunk,RIFF又从原先的"裸数据"中切出4Byte作为"格式辨别码",如下图所示.
对RIFF的树状结构有所了解之后,可以知道它相当于一个根目录,而格式辨识码则相当于具体的盘符如C:,D:等等.Windows下的各种多媒体文件格式就如同在磁盘机下规定只能存放怎样的目录,而在该目录下仅能存放何种数据.
WAV文件头
顾名思义,WAV就是波形音频文件(Wave Audio),是Windows中用来表示数字化声音的一种标准格式,其文件扩展名为.wav,是一种非常简单的RIFF文件,格式辨识码为"WAVE".整个WAV文件分成两部分:文件头和数据块.WAV格式文件主要有两种文件头.
标准的44字节文件头
这种WAV是最简单的一种RIFF格式,包含两个chunk:<fmt—chunk>,<wave—data>,这两个子块都是一个WAV文件必须包含的.
RIFF WAVE Chunk
以'RIFF'作为标示,然后紧跟着为size字段,该size是整个wav文件大小减去ID和Size所占用的字节数,即FileLen - 8 =Size.然后是Type字段,为'WAVE',表示是wav文件.结构定义如下:
- struct RIFF_HEADER
- {
- char szRiffID[4];
- DWORD dwRiffSize;
- char szRiffFormat[4];
- };
Format Chunk
以'fmt '作为标示.一般情况下Size为16,此时最后附加信息没有;如果为18则最后多了2个字节的附加信息.主要由一些软件制成的wav格式中含有该2个字节的
- struct WAVE_FORMAT
- {
- WORD wFormatTag;
- WORD wChannels;
- DWORD dwSamplesPerSec;
- DWORD dwAvgBytesPerSec;
- WORD wBlockAlign;
- WORD wBitsPerSample;
- };
- struct FMT_BLOCK
- {
- char szFmtID[4];
- DWORD dwFmtSize;
- WAVE_FORMAT wavFormat;
- };
为了产生出能够正确读出的WAV文件,必须严格注意以下几个分量间的特定关系,否则产生出的文件将无法正常播放:
58字节文件头如果不是Windows的标准WAV文件,而是经过了一些软件处理的,往往就是58字节的文件头,如下图所示.
它比44字节的多了一个fact子块.<fact—ck>储存了关于WAV文件内容的重要信息.该子块定义如下:
Fact Chunk
Fact Chunk是可选字段,一般当wav文件由某些软件转化而成,则包含该Chunk.结构定义如下:
- struct FACT_BLOCK
- {
- char szFactID[4];
- DWORD dwFactSize;
- };
"data"子块数据安排方式
"data"子块中装的是真正的声音数据.除非安装其它特殊软件,否则Windows目前仅提供WAVE_FORMAT_PCM一种数据格式,即脉冲编码调制(Pulse Code Modulation).针对此惭式,Windows中"data"子块中数据存放的形式如下图所示,根据声道数不同及取样位数的不同,安排4位的位置.
Data Chunk头结构定义如下:
- struct DATA_BLOCK
- {
- char szDataID[4];
- DWORD dwDataSize;
- };
注意:Windows中将16位值的范围定为[-32768,32767].另外,0并不一定代表无声,而是由中间数值来决定,即8位的时候为128,l6位时0才是无声.所以,编程中需要放入无声的数据时,必须先认清声音格式是l6位还是8位. 通常解压缩后得到的文件仅仅是裸数据,不能正常播放声音.了解了WAV文件格式后,就可以按照标准的44字节格式,在解码数据前编写一个正确的WAV文件头,使其成为一个有效的WAV文件.
下面是我实现的录音类GWaveIn:
头文件:
- #pragma once
-
- #include <Windows.h>
- #include <MMSystem.h>
- using namespace std;
-
- class GWaveIn
- {
- public:
- GWaveIn(void);
- virtual ~GWaveIn(void);
-
- bool IfWaveIn();
- bool PrepareWaveIn(HWND hwnd, BYTE* pbuf1, BYTE* pbuf2);
- bool AddBuffer(PWAVEHDR pwavehdr);
- bool StartRec();
- bool CloseWaveIn();
-
- public:
- HWAVEIN m_hWaveIn;
- UINT_PTR m_iWaveInID;
- WAVEHDR m_head1;
- WAVEHDR m_head2;
- };
CPP文件:
- #include "StdAfx.h"
- #include "GWaveIn.h"
-
- GWaveIn::GWaveIn(void)
- {
- }
-
- GWaveIn::~GWaveIn(void)
- {
- }
-
- bool GWaveIn::IfWaveIn()
- {
- MMRESULT mmresult = 0;
-
- mmresult = waveInGetNumDevs();
- if(mmresult == 0) return false;
-
- WAVEINCAPS waveincaps = {0};
- unsigned int i = 0;
- unsigned int num = mmresult;
- for( ; i<num; i++)
- {
- mmresult = waveInGetDevCaps(i,&waveincaps,sizeof(WAVEINCAPS));
- if(mmresult != MMSYSERR_NOERROR)
- return false;
- if((waveincaps.dwFormats & WAVE_FORMAT_1M08) == 0)
- continue;
- else
- {
- m_iWaveInID = i;
- return true;
- }
- }
-
- return false;
- }
-
- bool GWaveIn::PrepareWaveIn(HWND hwnd, BYTE* pbuf1, BYTE* pbuf2)
- {
- WAVEFORMATEX waveformatex = {0};
- waveformatex.wFormatTag = WAVE_FORMAT_PCM;
- waveformatex.nChannels = 1;
- waveformatex.nSamplesPerSec = 11025;
- waveformatex.nAvgBytesPerSec = 11025*1*8/8;
- waveformatex.nBlockAlign = 8*1/8;
- waveformatex.wBitsPerSample = 8;
- waveformatex.cbSize = 0;
-
- MMRESULT mmresult = 0;
- mmresult = waveInOpen(&m_hWaveIn,m_iWaveInID,&waveformatex,(DWORD)hwnd,0,CALLBACK_WINDOW);
- if(mmresult != MMSYSERR_NOERROR) return false;
-
-
- m_head1.lpData = (LPSTR)pbuf1;
- m_head1.dwBufferLength = 20480;
- m_head1.dwBytesRecorded = 0;
- m_head1.dwUser = 0;
- m_head1.dwFlags = 0;
- m_head1.dwLoops = 1;
- m_head1.lpNext = NULL;
- m_head1.reserved = 0;
-
- mmresult = waveInPrepareHeader(m_hWaveIn,&m_head1,sizeof(WAVEHDR));
- if(mmresult != MMSYSERR_NOERROR) return false;
-
- m_head2.lpData = (LPSTR)pbuf2;
- m_head2.dwBufferLength = 20480;
- m_head2.dwBytesRecorded = 0;
- m_head2.dwUser = 0;
- m_head2.dwFlags = 0;
- m_head2.dwLoops = 1;
- m_head2.lpNext = NULL;
- m_head2.reserved = 0;
-
- mmresult = waveInPrepareHeader(m_hWaveIn,&m_head2,sizeof(WAVEHDR));
- if(mmresult != MMSYSERR_NOERROR) return false;
-
- return true;
- }
-
- bool GWaveIn::AddBuffer(PWAVEHDR pwavehdr)
- {
- MMRESULT mmresult = 0;
- mmresult = waveInAddBuffer(m_hWaveIn,pwavehdr,sizeof(WAVEHDR));
- if(mmresult != MMSYSERR_NOERROR) return false;
-
- return true;
- }
-
- bool GWaveIn::StartRec()
- {
- MMRESULT mmresult = 0;
- mmresult = waveInStart(m_hWaveIn);
- if(mmresult != MMSYSERR_NOERROR) return false;
-
- return true;
- }
-
- bool GWaveIn::CloseWaveIn()
- {
- MMRESULT mmresult = 0;
- waveInStop(m_hWaveIn);
- waveInReset(m_hWaveIn);
- waveInClose(m_hWaveIn);
-
- if(m_head1.lpData != NULL) free(m_head1.lpData);
- waveInUnprepareHeader(m_hWaveIn,&m_head1,sizeof(WAVEHDR));
-
- if(m_head2.lpData != NULL) free(m_head2.lpData);
- waveInUnprepareHeader(m_hWaveIn,&m_head2,sizeof(WAVEHDR));
-
- return true;
- }
播放类GWaveOut:
头文件:
- #pragma once
-
- #include <windows.h>
- #include <MMSystem.h>
- using namespace std;
-
- class GWaveOut
- {
- public:
- GWaveOut(void);
- virtual ~GWaveOut(void);
-
- bool IfWaveOut();
- bool PrepareWaveOut(HWND hwnd, BYTE* pbuf1, BYTE* pbuf2);
- bool WaveOutWrite(PWAVEHDR pwavehdr);
- bool CloseWaveOut();
-
- public:
- HWAVEOUT m_hWaveOut;
- UINT_PTR m_iWaveOutID;
- WAVEHDR m_head1;
- WAVEHDR m_head2;
- };
CPP文件:
- #include "StdAfx.h"
- #include "GWaveOut.h"
-
- GWaveOut::GWaveOut(void)
- {
- }
-
- GWaveOut::~GWaveOut(void)
- {
- }
-
- bool GWaveOut::IfWaveOut()
- {
- MMRESULT mmresult = 0;
- mmresult = waveOutGetNumDevs();
- if(mmresult == 0) return false;
-
- WAVEOUTCAPS waveoutcaps = {0};
- unsigned int i = 0;
- unsigned int num = mmresult;
- for( ; i<num; i++)
- {
- mmresult = waveOutGetDevCaps(i,&waveoutcaps,sizeof(WAVEOUTCAPS));
- if(mmresult != MMSYSERR_NOERROR) return false;
- if((waveoutcaps.dwFormats & WAVE_FORMAT_1M08) == 0)
- continue;
- else
- {
- m_iWaveOutID = i;
- return true;
- }
- }
-
- return false;
- }
-
- bool GWaveOut::PrepareWaveOut(HWND hwnd, BYTE* pbuf1, BYTE* pbuf2)
- {
- WAVEFORMATEX waveformatex = {0};
- waveformatex.wFormatTag = WAVE_FORMAT_PCM;
- waveformatex.nChannels = 1;
- waveformatex.cbSize = 0;
- waveformatex.nAvgBytesPerSec = 11025*1*8/8;
- waveformatex.nBlockAlign = 1;
- waveformatex.nSamplesPerSec = 11025;
- waveformatex.wBitsPerSample = 8;
-
- MMRESULT mmresult = 0;
- mmresult = waveOutOpen(&m_hWaveOut,m_iWaveOutID,&waveformatex,(DWORD)hwnd,0,CALLBACK_WINDOW);
- if(mmresult != MMSYSERR_NOERROR) return false;
-
- m_head1.lpData = (LPSTR)pbuf1;
- m_head1.dwBufferLength = 20480;
- m_head1.dwBytesRecorded = 0;
- m_head1.dwUser = 0;
- m_head1.dwFlags = 0;
- m_head1.dwLoops = 1;
- m_head1.lpNext = NULL;
- m_head1.reserved = 0;
-
- mmresult = waveOutPrepareHeader(m_hWaveOut,&m_head1,sizeof(WAVEHDR));
- if(mmresult != MMSYSERR_NOERROR) return false;
-
- m_head2.lpData = (LPSTR)pbuf2;
- m_head2.dwBufferLength = 20480;
- m_head2.dwBytesRecorded = 0;
- m_head2.dwUser = 0;
- m_head2.dwFlags = 0;
- m_head2.dwLoops = 1;
- m_head2.lpNext = NULL;
- m_head2.reserved = 0;
-
- mmresult = waveOutPrepareHeader(m_hWaveOut,&m_head2,sizeof(WAVEHDR));
- if(mmresult != MMSYSERR_NOERROR) return false;
-
- return true;
- }
-
- bool GWaveOut::WaveOutWrite(PWAVEHDR pwavehdr)
- {
- MMRESULT mmresult = 0;
- waveOutWrite(m_hWaveOut,pwavehdr,sizeof(WAVEHDR));
- if(mmresult != MMSYSERR_NOERROR) return false;
-
- return true;
- }
-
- bool GWaveOut::CloseWaveOut()
- {
- waveOutReset(m_hWaveOut);
- waveOutClose(m_hWaveOut);
-
- if(m_head1.lpData != NULL) free(m_head1.lpData);
- waveOutUnprepareHeader(m_hWaveOut,&m_head1,sizeof(WAVEHDR));
-
- if(m_head2.lpData != NULL) free(m_head2.lpData);
- waveOutUnprepareHeader(m_hWaveOut,&m_head2,sizeof(WAVEHDR));
-
- return true;
- }
其实,掌握了音频的录制和播放的基本原理之后,难道就在于对内存的管理了,要考虑的问题包括内存不足、内存生存期的分配、内存浪费。。。等等。
最后附上我实现的工程:http://download.csdn.net/detail/gongluck93/9681666