在windows下用waveout接口播放音频

时间:2024-03-11 15:30:45
  • 首先需要定义几个对象:
  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)来关闭一个流设备。
以上几步声明一个流设备对象,完成了打开、关闭一个流设备,并设置音频流参数

  • 接下来完成基本语音播放功能,播放语音需要做两个工作:
  1. 语音数据
  2. 写语音数据到流设备中
 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已经准备好数据以供播放。
(由于最近事情比较多,回头补全这部分)