winapiwindows-7windows-vistawinmmwaveout

Why does waveOutOpen() on Windows Vista/newer say the default output device can't play 44100hz mono 8-bit PCM when waveOutGetDevCaps() says it can?


I am trying to open a 44100hz 8-bit mono PCM stream with winmm (waveOutXxxx()) without software autoconversions (I want to be in full control of the output wave, without filtering that may result from such conversions).

// 2 september 2014
#define UNICODE
#define _UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
// get Windows version right; right now Windows XP
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define _WIN32_WINDOWS 0x0501       /* according to Microsoft's winperf.h */
#define _WIN32_IE 0x0600            /* according to Microsoft's sdkddkver.h */
#define NTDDI_VERSION 0x05010000    /* according to Microsoft's sdkddkver.h */
#include <windows.h>
#include <stdio.h>

int main(void)
{
    HWAVEOUT wo;
    HANDLE event;
    WAVEFORMATEX fmt;
    MMRESULT err;

    event = CreateEvent(NULL, TRUE, TRUE, NULL);            // start off signaled just in case
    if (event == NULL) {
        fprintf(stderr, "CreateEvent() failed (last error %d)\n", GetLastError());
        return 3;
    }

    ZeroMemory(&fmt, sizeof (WAVEFORMATEX));
    fmt.wFormatTag = WAVE_FORMAT_PCM;
    fmt.nChannels = 1;
    fmt.nSamplesPerSec = 44100;
    fmt.wBitsPerSample = 8;
    fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample;
    fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
    fmt.cbSize = 0;
    err = waveOutOpen(&wo, WAVE_MAPPER, &fmt,
        (DWORD_PTR) event, 0,
        CALLBACK_EVENT | WAVE_FORMAT_DIRECT);
    if (err != MMSYSERR_NOERROR) {
        WCHAR errmsg[MAXERRORLENGTH + 1];
        MMRESULT converr;

        converr = waveOutGetErrorTextW(err, errmsg, MAXERRORLENGTH + 1);
        if (converr != MMSYSERR_NOERROR) {
            fprintf(stderr, "open error %x (message conversion error %x)\n", err, converr);
            return 2;
        }
        fwprintf(stderr, L"open error: %s\n", errmsg);
        return 1;
    }
    fprintf(stderr, "open successful\n");
    return 0;
}

This works fine in Windows XP and in wine. But on Windows Vista and newer (have tried Vista, 7, and 8.1), I get

open error: The specified format is not supported or cannot be translated.  Use the Capabilities function to determine the supported formats.

So let's do that:

// 1 september 2014
#define UNICODE
#define _UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
// get Windows version right; right now Windows XP
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define _WIN32_WINDOWS 0x0501       /* according to Microsoft's winperf.h */
#define _WIN32_IE 0x0600            /* according to Microsoft's sdkddkver.h */
#define NTDDI_VERSION 0x05010000    /* according to Microsoft's sdkddkver.h */
#include <windows.h>
#include <stdio.h>

int main(void)
{
    MMRESULT err;
    WAVEOUTCAPS caps;

    ZeroMemory(&caps, sizeof (WAVEOUTCAPS));
    err = waveOutGetDevCaps((DWORD_PTR) WAVE_MAPPER, &caps, sizeof (WAVEOUTCAPS));
    if (err != MMSYSERR_NOERROR) {
        fprintf(stderr, "mmsys err %x\n", err);
        return 1;
    }

    printf("formats: ");
#define FORMATS(x) if((caps.dwFormats & x) != 0) printf("%s ", #x);
    FORMATS(WAVE_FORMAT_1M08);
    FORMATS(WAVE_FORMAT_1M16);
    FORMATS(WAVE_FORMAT_1S08);
    FORMATS(WAVE_FORMAT_1S16);
    FORMATS(WAVE_FORMAT_2M08);
    FORMATS(WAVE_FORMAT_2M16);
    FORMATS(WAVE_FORMAT_2S08);
    FORMATS(WAVE_FORMAT_2S16);
    FORMATS(WAVE_FORMAT_4M08);
    FORMATS(WAVE_FORMAT_4M16);
    FORMATS(WAVE_FORMAT_4S08);
    FORMATS(WAVE_FORMAT_4S16);
    FORMATS(WAVE_FORMAT_96M08);
    FORMATS(WAVE_FORMAT_96M16);
    FORMATS(WAVE_FORMAT_96S08);
    FORMATS(WAVE_FORMAT_96S16);
    printf("\n");

    printf("channels: %d\n", caps.wChannels);

    printf("supports: ");
#define SUPPORTS(x) if((caps.dwSupport & x) != 0) printf("%s ", #x);
    SUPPORTS(WAVECAPS_LRVOLUME);
    SUPPORTS(WAVECAPS_PITCH);
    SUPPORTS(WAVECAPS_PLAYBACKRATE);
    SUPPORTS(WAVECAPS_SYNC);
    SUPPORTS(WAVECAPS_VOLUME);
    SUPPORTS(WAVECAPS_SAMPLEACCURATE);
    printf("\n");

    return 0;
}

and now the output is

formats: WAVE_FORMAT_1M08 WAVE_FORMAT_1M16 WAVE_FORMAT_1S08 WAVE_FORMAT_1S16 WAVE_FORMAT_2M08 WAVE_FORMAT_2M16 WAVE_FORMAT_2S08 WAVE_FORMAT_2S16 WAVE_FORMAT_4M08 WAVE_FORMAT_4M16 WAVE_FORMAT_4S08 WAVE_FORMAT_4S16 WAVE_FORMAT_96M08 WAVE_FORMAT_96M16 WAVE_FORMAT_96S08 WAVE_FORMAT_96S16
channels: 2
supports: WAVECAPS_LRVOLUME WAVECAPS_VOLUME WAVECAPS_SAMPLEACCURATE

...but wait, WAVE_FORMAT_4M08 is listed as supported!

What gives? Thanks.


Solution

  • The error means waveOutOpen() rejects your WAVEFORMATEX struct, likely because your nBlockAlign value is wrong. The documentation states:

    If wFormatTag is WAVE_FORMAT_PCM, nBlockAlign must equal (nChannels × wBitsPerSample) / 8

    You are missing the / 8 portion:

    fmt.nBlockAlign = (fmt.nChannels * fmt.wBitsPerSample) / 8;