How to validate and log a WAVEFORMATEX

How to validate and log a WAVEFORMATEX

  • Comments 4

Last time I wrote about how to query audio endpoint properties.

This time, a few words about WAVEFORMATEX structures.  WAVEFORMATEX is a variable-sized structure - it can be as small as a PCMWAVEFORMAT or larger than a WAVEFORMATEXTENSIBLE.

If the wFormatTag member (the first field) is WAVE_FORMAT_PCM, then you should assume that it is a PCMWAVEFORMAT.  In particular you should not touch the cbSize field since that may not be memory you own!

If wFormatTag is anything else, you can look at the cbSize to see how much bigger than a WAVEFORMATEX the structure is.  For example, if the wFormatTag is WAVE_FORMAT_EXTENSIBLE, you have the right to expect that cbSize be at least sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX).

I updated the attachment to the post linked above.  Here's the new output:

PKEY_AudioEngine_DeviceFormat VT_BLOB of size 40
    WAVEFORMATEXTENSIBLE
    Format (
        wFormatTag: 65534 (WAVE_FORMAT_EXTENSIBLE)
        nChannels: 4
        nSamplesPerSec: 16000
        nAvgBytesPerSec: 128000
        nBlockAlign: 8
        wBitsPerSample: 16
        cbSize: 22
    )
    Samples (
        wValidBitsPerSample / wSamplesPerBlock / wReserved: 16
    )
    dwChannelMask: 0x0
    SubFormat: {00000001-0000-0010-8000-00AA00389B71} (KSDATAFORMAT_SUBTYPE_PCM)

And here's the code that produces it:

HRESULT LogWaveFormat(LPCWAVEFORMATEX pWfx, UINT nBytes) {
    if (NULL == pWfx) {
        ERR(L"LogWaveFormat called with a NULL pointer");
        return E_POINTER;
    }
    
    if (nBytes < sizeof(PCMWAVEFORMAT)) {
        ERR(L"Wave format is only %u bytes, smaller even than a PCMWAVEFORMAT (%Iu bytes)", nBytes, sizeof(PCMWAVEFORMAT));
        return E_INVALIDARG;
    } else if (nBytes == sizeof(PCMWAVEFORMAT)) {
        const PCMWAVEFORMAT *pPcm = reinterpret_cast<const PCMWAVEFORMAT *>(pWfx);
        
        // PCMWAVEFORMAT must have a wFormatTag of WAVE_FORMAT_PCM
        if (WAVE_FORMAT_PCM != pPcm->wf.wFormatTag) {
            ERR(L"PCMWAVEFORMAT has invalid format tag %u (expected %u)", pPcm->wf.wFormatTag, WAVE_FORMAT_PCM);
            return E_INVALIDARG;
        }
        
        LOG(
            L"    PCMWAVEFORMAT\n"
            L"        wf.wFormatTag: %u (%s)\n"
            L"        wf.nChannels: %u\n"
            L"        wf.nSamplesPerSec: %u\n"
            L"        wf.nAvgBytesPerSec: %u\n"
            L"        wf.nBlockAlign: %u\n"
            L"        wBitsPerSample: %u",
            pPcm->wf.wFormatTag, StringFromWaveFormatTag(pPcm->wf.wFormatTag),
            pPcm->wf.nChannels,
            pPcm->wf.nSamplesPerSec,
            pPcm->wf.nAvgBytesPerSec,
            pPcm->wf.nBlockAlign,
            pPcm->wBitsPerSample
        );
        return S_OK;
    } else if (nBytes < sizeof(WAVEFORMATEX)) {
        ERR(
            L"Wave format is %u bytes, "
            L"bigger than a PCMWAVEFORMAT (%Iu bytes) "
            L"but smaller than a WAVEFORMATEX (%Iu bytes)",
            nBytes,
            sizeof(PCMWAVEFORMAT),
            sizeof(WAVEFORMATEX)
        );
        return E_INVALIDARG;
    } else if (nBytes != sizeof(WAVEFORMATEX) + pWfx->cbSize) {
        ERR(
            L"Wave format takes up %u bytes "
            L"but sizeof(WAVEFORMATEX) + pWfx->cbSize is %Iu bytes",
            nBytes,
            sizeof(WAVEFORMATEX) + pWfx->cbSize
        );
        return E_INVALIDARG;
    } else {
        // nBytes matches cbSize
        switch (pWfx->wFormatTag) {
           
            case WAVE_FORMAT_EXTENSIBLE:
                {
                    if (pWfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) {
                        ERR(
                            L"cbSize for a WAVE_FORMAT_EXTENSIBLE format must be at least "
                            L"sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) (%Iu), "
                            L"not %u",
                            sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX),
                            pWfx->cbSize
                        );
                        return E_INVALIDARG;
                    }
                
                    const WAVEFORMATEXTENSIBLE *pWfxExt = reinterpret_cast<const WAVEFORMATEXTENSIBLE *>(pWfx);
                    
                    LOG(
                        L"    WAVEFORMATEXTENSIBLE\n"
                        L"    Format (\n"
                        L"        wFormatTag: %u (%s)\n"
                        L"        nChannels: %u\n"
                        L"        nSamplesPerSec: %u\n"
                        L"        nAvgBytesPerSec: %u\n"
                        L"        nBlockAlign: %u\n"
                        L"        wBitsPerSample: %u\n"
                        L"        cbSize: %u\n"
                        L"    )\n"
                        L"    Samples (\n"
                        L"        wValidBitsPerSample / wSamplesPerBlock / wReserved: %u\n"
                        L"    )\n"
                        L"    dwChannelMask: 0x%x\n"
                        L"    SubFormat: {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X} (%s)",
                        pWfxExt->Format.wFormatTag, StringFromWaveFormatTag(pWfxExt->Format.wFormatTag),
                        pWfxExt->Format.nChannels,
                        pWfxExt->Format.nSamplesPerSec,
                        pWfxExt->Format.nAvgBytesPerSec,
                        pWfxExt->Format.nBlockAlign,
                        pWfxExt->Format.wBitsPerSample,
                        pWfxExt->Format.cbSize,
                        pWfxExt->Samples.wValidBitsPerSample,
                        pWfxExt->dwChannelMask,
                        pWfxExt->SubFormat.Data1,
                        pWfxExt->SubFormat.Data2,
                        pWfxExt->SubFormat.Data3,
                        pWfxExt->SubFormat.Data4[0],
                        pWfxExt->SubFormat.Data4[1],
                        pWfxExt->SubFormat.Data4[2],
                        pWfxExt->SubFormat.Data4[3],
                        pWfxExt->SubFormat.Data4[4],
                        pWfxExt->SubFormat.Data4[5],
                        pWfxExt->SubFormat.Data4[6],
                        pWfxExt->SubFormat.Data4[7],
                        StringFromSubFormat(pWfxExt->SubFormat)
                    );

                    if (pWfx->cbSize > sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) {
                        LOG(L"    Trailing bytes:\n");
                        return LogBytes(
                            reinterpret_cast<const BYTE *>(pWfx) + sizeof(WAVEFORMATEXTENSIBLE),
                            pWfx->cbSize - (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
                        );
                    }
                    
                    return S_OK;
                }
                
            case WAVE_FORMAT_PCM:
                if (pWfx->cbSize > 0) {
                    ERR(L"cbSize for a WAVE_FORMAT_PCM format must be 0, not %u", pWfx->cbSize);
                    return E_INVALIDARG;
                }
                // intentionally fall through
            default:
                LOG(
                    L"    WAVEFORMATEX\n"
                    L"        wFormatTag: %u (%s)\n"
                    L"        nChannels: %u\n"
                    L"        nSamplesPerSec: %u\n"
                    L"        nAvgBytesPerSec: %u\n"
                    L"        nBlockAlign: %u\n"
                    L"        wBitsPerSample: %u\n"
                    L"        cbSize: %u",
                    pWfx->wFormatTag, StringFromWaveFormatTag(pWfx->wFormatTag),
                    pWfx->nChannels,
                    pWfx->nSamplesPerSec,
                    pWfx->nAvgBytesPerSec,
                    pWfx->nBlockAlign,
                    pWfx->wBitsPerSample,
                    pWfx->cbSize
                );
                
                if (pWfx->cbSize > 0) {
                    LOG(L"    Trailing bytes:");
                    return LogBytes(reinterpret_cast<const BYTE *>(pWfx) + sizeof(WAVEFORMATEX), pWfx->cbSize);
                }

                return S_OK;
        }
    }
}


Leave a Comment
  • Please add 4 and 4 and type the answer here:
  • Post
  • Initially, in my WDM audio PortCls miniport, I checked wFormatTag first, comparing it with WAVE_FORMAT_PCM. But I noticed that some requests from DirectSound applications come with extensible format descriptors having zero in wFormatTag. Popular device drivers accept such descriptors so I had to make a workaround, checking cbSize first.

    Don't remember now was it under XP or 2k.

  • In a miniport you have the size of the format as KSDATAFORMAT.FormatSize - sizeof(KSDATAFORMAT).  If this is large enough for there to be a WAVEFORMATEX.cbSize, the .cbSize should be consistent with the KSDATAFORMAT.FormatSize.  In particular, KSDATAFORMAT.FormatSize == sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATEX) + KSDATAFORMAT_WAVEFORMATEX.WaveFormatEx.cbSize should hold.

  • Thus, is it valid for an extensible format descriptor to have zero in wFormatTag? In my universal audio library, I wanted to have an universal PCMWAVEFORMAT/WAVEFORMATEX/WAVEFORMATEXTENSIBLE validity checker.

  • 0 is WAVE_FORMAT_UNKNOWN; 1 is WAVE_FORMAT_PCM; 3 is WAVE_FORMAT_IEEE_FLOAT.

    A list of many, many more is in mmreg.h in the SDK.

    I would consider WAVE_FORMAT_UNKNOWN to be a bogus wave format most of the time, though it appears that Audio Compression Manager drivers treat this specially.

Page 1 of 1 (4 items)