WaveOut API无声音输出及WOM_DONE未触发问题求助
问题描述
- 所有函数返回成功,回调函数能响应
MM_WOM_OPEN和MM_WOM_CLOSE事件,但声卡无声音输出 - 在C++Builder6中运行程序时,Windows音量混合器可见Wave条目,但无声音播放
- 调用
DatasRefresh(...)本应修改载波波形,但未生效 WOM_DONE事件未触发,而MM_WOM_OPEN和MM_WOM_CLOSE可正常触发
代码示例
#include <windows.h> #include <mmsystem.h> typedef size_t herz_t; typedef float progress_t; class audio32_t { public: audio32_t(); virtual ~audio32_t(); void DatasRefresh(const herz_t &_carrier, const progress_t &_level) { audio32_t::_carrier(_carrier, _level, this->_header); ::waveOutWrite(this->_apiout, &this->_header, sizeof(WAVEHDR)); } private: HANDLE _apiclock; HWAVEOUT _apiout; WAVEHDR _header; static WAVEFORMATEX _w32style; static void _carrier(const herz_t &, const progress_t &, WAVEHDR &); static void _w32proc(HWAVEOUT, UINT, DWORD, DWORD, DWORD); }; //--------------------------------------------------------------------------- WAVEFORMATEX audio32_t::_w32style; audio32_t::audio32_t() : _apiclock(0), _apiout(0), _header() { audio32_t::_w32style.wFormatTag = WAVE_FORMAT_PCM; audio32_t::_w32style.nChannels = 2; audio32_t::_w32style.nSamplesPerSec = 44100; audio32_t::_w32style.wBitsPerSample = audio32_t::_w32style.nChannels * 8; audio32_t::_w32style.cbSize = 0; audio32_t::_w32style.nBlockAlign = static_cast<BYTE>( audio32_t::_w32style.nChannels * (audio32_t::_w32style.wBitsPerSample / 8) ); audio32_t::_w32style.nAvgBytesPerSec = audio32_t::_w32style.nSamplesPerSec * audio32_t::_w32style.nBlockAlign; this->_header.dwBufferLength = 0; this->_header.lpData = 0; this->_header.dwBytesRecorded = 0; this->_header.dwUser = 0; this->_header.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP; this->_header.dwLoops = 0xFFFF; this->_header.lpNext = NULL; this->_header.reserved = 0; this->_header.dwBufferLength = audio32_t::_w32style.nAvgBytesPerSec; this->_header.lpData = ::new char[this->_header.dwBufferLength * 2]; ::waveOutOpen( &this->_apiout, WAVE_MAPPER, &audio32_t::_w32style, reinterpret_cast<DWORD>(&audio32_t::_w32proc), reinterpret_cast<DWORD>(this), CALLBACK_FUNCTION ); } audio32_t::~audio32_t() { ::waveOutClose(this->_apiout); } void audio32_t::_carrier(const herz_t &_herz, const progress_t &_lvl, WAVEHDR &_header) { const size_t _half = (_header.dwBufferLength / 2); const double _w = 2.0 * M_PI * double(_herz) / double(audio32_t::_w32style.nSamplesPerSec); for(size_t _x = 0; _x < _header.dwBufferLength; _x += 4) { const double _byte = ::sin(double(_x / 4) * _w); const short _sample = static_cast<short>(_byte * 32767.0 * (_lvl / 100.0f)); if(_x > _half) { // 左声道静音,右声道发声 reinterpret_cast<short*>(_header.lpData)[_x / 4 * 2 + 0] = 0; reinterpret_cast<short*>(_header.lpData)[_x / 4 * 2 + 1] = _sample; } else { // 左声道发声,右声道静音 reinterpret_cast<short*>(_header.lpData)[_x / 4 * 2 + 0] = _sample; reinterpret_cast<short*>(_header.lpData)[_x / 4 * 2 + 1] = 0; } } } void audio32_t::_w32proc(HWAVEOUT _out, UINT _request, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { audio32_t *_this = reinterpret_cast<audio32_t*>(dwInstance); switch(_request) { case MM_WOM_OPEN: { ::waveOutPrepareHeader(_this->_apiout, &_this->_header, sizeof(WAVEHDR)); ::waveOutWrite(_this->_apiout, &_this->_header, sizeof(WAVEHDR)); break; } case WOM_DONE: { // 如果需要循环播放,这里重新调用waveOutWrite // ::waveOutWrite(_this->_apiout, &_this->_header, sizeof(WAVEHDR)); break; } case MM_WOM_CLOSE: { ::waveOutReset(_this->_apiout); ::waveOutUnprepareHeader(_this->_apiout, &_this->_header, sizeof(WAVEHDR)); if(_this->_header.lpData != 0) { ::delete [] _this->_header.lpData; _this->_header.lpData = 0; } break; } default: break; } } #include <conio.h> #include <iostream.h> void main() { audio32_t _audio; _audio.DatasRefresh(0, 0); _audio.DatasRefresh(1200, 25); ::Sleep(500); cout << "+"; _audio.DatasRefresh(1200, 50); ::Sleep(500); cout << "+"; _audio.DatasRefresh(1200, 75); ::Sleep(500); cout << "+"; _audio.DatasRefresh(1200, 100); ::Sleep(500); cout << "="; _audio.DatasRefresh(1200, 75); ::Sleep(500); cout << "-"; _audio.DatasRefresh(1200, 50); ::Sleep(500); cout << "-"; _audio.DatasRefresh(1200, 25); ::Sleep(500); cout << "-"; _audio.DatasRefresh(1200, 0); ::Sleep(500); cout << "0"; ::cout << ::endl << "ok"; ::getch(); }
问题分析与修复建议
1. 音频格式与数据生成不匹配
- 问题:设置的是双声道16位PCM格式(
nChannels=2,wBitsPerSample=16),但_carrier函数按单字节步长处理数据,生成的是8位单声道格式的无效数据,导致设备无法正确解码播放。 - 修复:
- 循环步长改为4(每个采样占4字节:2通道×2字节/通道)
- 生成16位采样值(范围-32768到32767),而非8位的0-255偏移值
- 用
short类型直接操作采样数据,避免字节级错误
2. WOM_DONE事件未触发
- 问题:给
WAVEHDR设置了WHDR_BEGINLOOP | WHDR_ENDLOOP标志,且dwLoops=0xFFFF无限循环,设备会持续播放该缓冲区,不会触发WOM_DONE。 - 修复:
- 若需要触发
WOM_DONE,移除循环标志,设置dwLoops=0,然后在WOM_DONE回调中重新调用waveOutWrite实现循环 - 若保留循环,
WOM_DONE会在循环结束或调用waveOutReset后触发
- 若需要触发
3. DatasRefresh修改载波无效
- 问题:缓冲区处于循环播放状态时,设备正在使用该缓冲区,直接修改数据可能被设备缓存覆盖,且未停止播放就重新调用
waveOutWrite不会生效。 - 修复:
- 修改数据前调用
waveOutPause暂停播放,修改后调用waveOutRestart恢复 - 或者使用双缓冲区机制:准备两个缓冲区,交替提交播放,在
WOM_DONE回调中更新另一个缓冲区的数据
- 修改数据前调用
4. 构造函数的潜在错误
- 问题:
main函数中调用audio32_t _audio("mmsystem");,但类的构造函数是无参的,会导致编译错误;另外未检查waveOutOpen的返回值,无法确认设备是否成功打开。 - 修复:
- 将构造函数调用改为
audio32_t _audio; - 添加
waveOutOpen返回值检查:MMRESULT result = ::waveOutOpen(...); if (result != MMSYSERR_NOERROR) { // 处理错误,比如弹窗提示 }
- 将构造函数调用改为
5. 其他细节修复
byte_t未定义,替换为Windows原生的BYTE类型- 回调函数中
dwInstance参数是类实例指针,需用reinterpret_cast正确转换,避免类型错误
内容的提问来源于stack exchange,提问作者blueperfect
相关产品推荐
相关产品推荐

