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.
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;