c++ - waveOutWrite 缓冲区永远不会返回到应用程序

标签 c++ waveout

我对 Microsoft 的 WaveOut API 有疑问:

edit1:添加了示例项目的链接: edit2:删除了链接,它不代表问题

播放一些音频后,当我想终止给定的播放流时,我调用该函数:

waveOutClose(hWaveOut_);

但是,即使在调用waveOutClose()之后,有时库仍然会访问之前由waveOutWrite()传递给它的内存,从而导致无效的内存访问。

然后,我尝试确保所有缓冲区在释放缓冲区之前都标记为已完成:

PcmPlayback::~PcmPlayback()
{
if(hWaveOut_ == nullptr)
    return;

    waveOutReset(hWaveOut_); // infinite-loops, never returns

for(auto it = buffers_.begin(); it != buffers_.end(); ++it)
    waveOutUnprepareHeader(hWaveOut_, &it->wavehdr_, sizeof(WAVEHDR));

while( buffers_.empty() == false ) // infinite loops
    removeCompletedBuffers();

waveOutClose(hWaveOut_);

//Unhandled exception at 0x75629E80 (msvcrt.dll) in app.exe: 
// 0xC0000005: Access violation reading location 0xFEEEFEEE.
}

void PcmPlayback::removeCompletedBuffers()
{
for(auto it = buffers_.begin(); it != buffers_.end();)
{
    if( it->wavehdr_.dwFlags & WHDR_DONE )
    {
        waveOutUnprepareHeader(hWaveOut_, &it->wavehdr_, sizeof(WAVEHDR));
        it = buffers_.erase(it);
    }
    else
        ++it;
}
}

但是,这种情况永远不会发生 - 缓冲区永远不会变空。将会剩下 4-5 个 block ,wavehdr_.dwFlags == 18(我相信这意味着这些 block 仍然标记为播放中)

如何解决这个问题?

@ Martin Schlott(“您能提供将缓冲区写入waveOutWrite 的循环吗?”) 它不完全是一个循环,而是我有一个函数,每当我通过网络接收到音频数据包时就会调用该函数:

void PcmPlayback::addData(const std::vector<short> &rhs)
{
removeCompletedBuffers();

if(rhs.empty())
    return;

// add new data
buffers_.push_back(Buffer());

Buffer & buffer = buffers_.back();
buffer.data_ = rhs;
ZeroMemory(&buffers_.back().wavehdr_, sizeof(WAVEHDR));
buffer.wavehdr_.dwBufferLength = buffer.data_.size() * sizeof(short);
buffer.wavehdr_.lpData = (char *)(buffer.data_.data());
waveOutPrepareHeader(hWaveOut_, &buffer.wavehdr_, sizeof(WAVEHDR)); // prepare block for playback
waveOutWrite(hWaveOut_, &buffer.wavehdr_, sizeof(WAVEHDR));
}

最佳答案

如果您不调用,则可能会发生所描述的行为

waveOutUnprepareHeader

在使用之前使用过的每个缓冲区

waveOutClose

标志字段 _dwFlags 似乎表明缓冲区仍在排队(WHDR_INQUEUE | WHDR_PREPARED)尝试:

waveOutReset

在未准备缓冲区之前。

分析你的代码后,我发现了两个与waveOut无关的问题/错误(有趣的是,你使用C++11,但是最古老的媒体接口(interface))。您使用 vector 作为缓冲区。在某些调用操作期间, vector 会被复制!我发现的一个错误是:

typedef std::function<void(std::vector<short>)> CALLBACK_FN;

而不是:

typedef std::function<void(std::vector<short>&)> CALLBACK_FN;

强制复制 vector 。 如果您希望主要将其用作原始缓冲区,请尽量避免使用 vector 。最好使用 std::unique_pointer 作为缓冲区指针。

记录器中的回调不受互斥体监控,也不检查析构函数是否已被调用。析构发生在回调期间(大部分),这会导致异常。

对于您的测试程序,请在指责 waveOut 之前返回并使用原始指针和静态回调。您的代码还不错,但第一个错误已经表明,一个小错误将导致不可预测的错误。由于您还在 std::array 中组织缓冲区,因此我会在那里搜索错误。我猜想,您无意中复制了整个缓冲区数组,从而未准备错误的缓冲区。

我没有时间深入挖掘,但我想这些就是问题所在。

关于c++ - waveOutWrite 缓冲区永远不会返回到应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19490633/

相关文章:

c++ - 为什么 std::distance 打印出 `-` ?

c++ - OpenGL 属性偏移量未按预期工作

c++ - 如何获取当前 Windows 音频播放的采样率?

c# - 内存样本中的Naudio回放会产生声音的延迟和不美观的间隙

c++ - 无法从 std::shared_ptr<_Ty> 转换为 std::shared_ptr<_Ty>

c++ - CGAL:找到一个点所属的面/三角形?

c++ - 我怎样才能让 boost::function 不那么宽松?

c++ - 播放波形文件立即结束(C++,Windows)

audio - Windows 波形函数 - 8 和 16 以外的位深度的 WAVEOUTCAPS