【问题标题】:Using WAVEFORMATEXTENSIBLE with WAVE_FORMAT_IEEE_FLOAT, waveOutOpen returns WAVERR_BADFORMAT将 WAVEFORMATEXTENSIBLE 与 WAVE_FORMAT_IEEE_FLOAT 一起使用,waveOutOpen 返回 WAVERR_BADFORMAT
【发布时间】:2021-05-01 01:37:15
【问题描述】:

我一直在使用 WAVEFORMATEX 和 WaveOut 在 Windows 中播放音频,使用 WAVE_FORMAT_IEEE_FLOAT 以 44.1KHz 到 192KHz 的速率播放立体声音频。该程序用 C++ 编写并在 MinGW 中编译。这一切正常:

https://github.com/Raptor007/AutoDJ/blob/60f4debca2103e669a5d1b822b04c73cdcdaf05b/AutoDJ.cpp#L2412-L2435

现在我正在尝试扩展到四声道多声道输出,这似乎需要WAVEFORMATEXTENSIBLEWAVEFORMATEX 的超集。以下是应用了这些更改的相关代码:

WAVEFORMATEXTENSIBLE wfx;
memset( &wfx, 0, sizeof(wfx) );
wfx.Format.nChannels = want.channels;
wfx.dwChannelMask = (want.channels == 4) ? 0x33 : ((want.channels == 1) ? 0x4 : 0x3);
wfx.Format.nSamplesPerSec = want.freq;
wfx.Format.cbSize = sizeof(wfx) - sizeof(wfx.Format);
MMRESULT wave_out_result = ~MMSYSERR_NOERROR;
if( userdata.HighRes )
{
    wfx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
    wfx.SubFormat = {0x00000003,0x0000,0x0010,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71}; //KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
    wfx.Format.wBitsPerSample = 32;
    wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
    wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8;
    wfx.Format.nAvgBytesPerSec = wfx.Format.nBlockAlign * wfx.Format.nSamplesPerSec;
    wave_out_result = waveOutOpen( &WaveOutHandle, WAVE_MAPPER, &(wfx.Format), (DWORD_PTR) &WaveOutCallback, 0, CALLBACK_FUNCTION );
}
if( wave_out_result != MMSYSERR_NOERROR )
{
    if( userdata.HighRes )
        fprintf( stderr, "waveOutOpen returned %i%s when attempting float output\n", wave_out_result, (wave_out_result == WAVERR_BADFORMAT)?" (WAVERR_BADFORMAT)":"" );
    wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
    wfx.SubFormat = {0x00000001,0x0000,0x0010,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71}; //KSDATAFORMAT_SUBTYPE_PCM
    wfx.Format.wBitsPerSample = 16;
    wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
    wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8;
    wfx.Format.nAvgBytesPerSec = wfx.Format.nBlockAlign * wfx.Format.nSamplesPerSec;
    wave_out_result = waveOutOpen( &WaveOutHandle, WAVE_MAPPER, &(wfx.Format), (DWORD_PTR) &WaveOutCallback, 0, CALLBACK_FUNCTION );
    if( wave_out_result == MMSYSERR_NOERROR )
        userdata.HighRes = false;
    else
        fprintf( stderr, "waveOutOpen returned %i%s when attempting int16 output\n", wave_out_result, (wave_out_result == WAVERR_BADFORMAT)?" (WAVERR_BADFORMAT)":"" );
}

如果我将Format.nChannels = 2; dwChannelMask = 0x3; 设置为立体声,则第一次尝试使用IEEE-float 格式的waveOutOpen 失败,返回码为WAVERR_BADFORMAT,但第二次尝试使用PCM 格式成功。

如果我尝试使用 Format.nChannels = 4; dwChannelMask = 0x33; 进行四声道,IEEE-float 和 PCM waveOutOpen 尝试都会失败并返回 WAVERR_BADFORMAT

但是,如果我设置了Format.cbSize = 0;,那么任何一种格式的 2 个通道都可以正常工作,这是有道理的,因为这基本上就是我之前对 WAVEFORMATEX 所做的事情。但这不适用于 4 个频道。

我做错了什么?我的最终目标是 IEEE-float 格式的四声道或 5.1 环绕声输出。我特别困惑为什么我什至无法使用WAVEFORMATEXTENSIBLE 使立体声 IEEE-float 输出工作,但它与WAVEFORMATEX 完美配合。

【问题讨论】:

    标签: c++ windows surround waveout


    【解决方案1】:

    如果使用WAVEFORMATEXTENSIBLE 结构,则必须通过在“base”WAVEFORMATEX 中设置正确的格式标记来表明这一点。

    wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
    

    请参阅WAVEFORMATEXTENSIBLE 的 Format 成员的文档。

    这使得被调用代码(只有一个指向 WAVEFORMATEX 的指针)可以辨别它是在处理 WAVEFORMATEX 还是 WAVEFORMATEXTENSIBLE 结构。
    WAVEFORMATEXTENSIBLE 的情况下,实际的音频格式由SubFormat 成员唯一标识。

    【讨论】:

    • 谢谢!这就是问题所在。我现在看到我误解了有关与WAVEFORMATEX 向后兼容性的文档部分,该部分列出了wFormatTag 的旧值,旁边是对应的SubFormat。我认为意图是将两者一起使用,并且它单独使用cbSize 来辨别它是否是WAVEFORMATEXTENSIBLE
    • 啊哈,我还发现了为什么我使用 wFormatTag = WAVE_FORMAT_PCM 的不正确立体声实现在 WAVE_FORMAT_IEEE_FLOAT 不起作用时起作用。 docs.microsoft.com/en-us/windows/win32/api/mmreg/… "cbSize ... 对于 WAVE_FORMAT_PCM 格式,客户端应忽略此成员(其值隐式为零)。"
    猜你喜欢
    • 2018-07-22
    • 1970-01-01
    • 2011-05-28
    • 1970-01-01
    • 2018-12-19
    • 2021-05-26
    • 2013-07-20
    • 2018-03-04
    • 2023-03-13
    相关资源
    最近更新 更多