- 首先需要定义几个对象:
HWAVEOUT hWaveOut; /* device handle */ WAVEFORMATEX wfx; /* look this up in your documentation */ MMRESULT result;/* for waveOut return values */ 然后需要设置音频流的参数信息,一下只是给出了我在使用过程中的参数,可以根据个人需求自行调节: wfx.nSamplesPerSec = 8000; /* sample rate */ wfx.wBitsPerSample = 16; /* sample size */ wfx.nChannels = 1; /* channels*/ 另外,还有一些额外的参数需要设置: wfx.cbSize = 0; /* size of _extra_ info */ wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
/*WAVE_MAPPER是一个在mmsystem.h中定义的常数,总是指向系统的默认流设备*/ 通过函数waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL)打开一个流设备,设置wfx音频流参数,
并使得hWaveOut为该设备的handle,可以通过判断函数返回值是否等于MMSYSERR_NOERROR)判断设备是否打开成功了
相应的可以通过调用waveOutClose(hWaveOut)来关闭一个流设备。
以上几步声明一个流设备对象,完成了打开、关闭一个流设备,并设置音频流参数
- 接下来完成基本语音播放功能,播放语音需要做两个工作:
- 语音数据
- 写语音数据到流设备中
writeAudioBlock. To write audio data you use up to three functions. These are waveOutPrepareHeader,waveOutWrite,
and waveOutUnprepareHeader and are called in the order I have listed them.
void writeAudioBlock(HWAVEOUT hWaveOut, LPSTR block, DWORD size);函数实现如下说明:
声明一个WAVEHDR对象:WAVEHDR header,结构如下
typedef struct wavehdr_tag { LPSTR lpData; DWORD dwBufferLength; DWORD dwBytesRecorded; DWORD_PTR dwUser; DWORD dwFlags; DWORD dwLoops; struct wavehdr_tag * lpNext; DWORD_PTR reserved; } WAVEHDR, *LPWAVEHDR;
lpData:Pointer to the waveform buffer.
dwBufferLength:Length, in bytes, of the buffer.
dwUser:User data.
dwFlags:Flags supplying information about the buffer. The following values are defined,其中包含了一个WHDR_PREPARED ,Set by Windows to indicate that the buffer has been prepared with the waveOutPrepareHeader function.
然后初始化header为0,并传入参数数据大小、数据指针。
ZeroMemory(&header, sizeof(WAVEHDR));
header.dwBufferLength = size;
header.lpData = block;
准备播放数据块: waveOutPrepareHeader(hWaveOut, &header, sizeof(WAVEHDR));
写数据块到流设备中: waveOutWrite(hWaveOut, &header, sizeof(WAVEHDR));
等待数据流播放完,释放header 调用函数waveOutUnprepareHeader(hWaveOut, &header, sizeof(WAVEHDR)) 释放掉header,在释放data前一定要调用waveOutUnprepareHeader()
可以根据返回值是否等于WAVERR_STILLPLAYING判断是否播放完毕
函数定义说明:
MMRESULT waveOutPrepareHeader(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
MMRESULT waveOutWrite(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
MMRESULT waveOutUnprepareHeader(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
- hwo:Handle to the waveform-audio output device.
- pwh:Pointer to a WAVEHDR structure containing information about the data block.
- cbwh:Size, in bytes, of the WAVEHDR structure.
至此,将带播放的数据通过传入数据大小、数据指针给函数writeAudioBlock(HWAVEOUT hWaveOut, LPSTR block, DWORD size)就完成了基本的播放功能。
- 接下来完成播放一个音频流:
在上个播放语音实现中有一个大问题就是,每次只能播放一次读取数据,如果文件小的话,可以一次全部读取然后播放,但如果大文件就无法这样做了心所以需要寻找一种方法,
能够像流媒体一样,语音数据能一块一块顺序播放。
面临的另一个问题是,如果是单纯的顺序读取-播放,回会造成在两次播放之间有一个时间间隔,使得语音出现不连续的现象。解决方法是定义多个buffer,在播放的时候也能
够进行读数据到buffer里,这样每次一个buffer播放完,会有其他buffer已经准备好数据以供播放。
(由于最近事情比较多,回头补全这部分)