// WinMM waveform-output smoke test over the native render-stream personality path. #include #include #include #include #include typedef void* HANDLE; typedef void* HWAVEOUT; typedef unsigned int MMRESULT; typedef unsigned int UINT; typedef uint32_t DWORD; typedef uintptr_t DWORD_PTR; typedef unsigned short WORD; typedef int BOOL; #define FALSE 0 #define TRUE 1 #define INFINITE 0xFFFFFFFFu #define WAIT_OBJECT_0 0u #define MMSYSERR_NOERROR 0u #define MMSYSERR_NOTSUPPORTED 8u #define WAVERR_BADFORMAT 32u #define WAVERR_STILLPLAYING 33u #define WAVE_FORMAT_PCM 1u #define WAVE_FORMAT_QUERY 0x00000001u #define WAVE_ALLOWSYNC 0x00000002u #define WAVE_FORMAT_DIRECT 0x00000008u #define CALLBACK_WINDOW 0x00010000u #define CALLBACK_THREAD 0x00020000u #define CALLBACK_FUNCTION 0x00030000u #define CALLBACK_EVENT 0x00050000u #define WAVE_MAPPER 0xFFFFFFFFu #define WHDR_DONE 0x00000001u #define WHDR_PREPARED 0x00000002u #define WHDR_BEGINLOOP 0x00000004u #define WHDR_ENDLOOP 0x00000008u #define WHDR_INQUEUE 0x00000010u #define WOM_OPEN 0x03BBu #define WOM_CLOSE 0x03BCu #define WOM_DONE 0x03BDu #define TIME_SAMPLES 0x0002u #define TEST_FRAMES 4096u #define TEST_CONTROL_FRAMES 16384u #define TEST_PHASE_MODULUS 1200u #define TEST_PHASE_STEP 11u #define TEST_QUARTER_PHASE 300u #define TEST_PEAK 8192 typedef struct WAVEFORMATEX { WORD wFormatTag; WORD nChannels; DWORD nSamplesPerSec; DWORD nAvgBytesPerSec; WORD nBlockAlign; WORD wBitsPerSample; WORD cbSize; } WAVEFORMATEX; typedef struct WAVEHDR { char* lpData; DWORD dwBufferLength; DWORD dwBytesRecorded; DWORD_PTR dwUser; DWORD dwFlags; DWORD dwLoops; struct WAVEHDR* lpNext; DWORD_PTR reserved; } WAVEHDR; typedef struct MMTIME { UINT wType; union { DWORD ms; DWORD sample; DWORD cb; DWORD ticks; } u; } MMTIME; typedef struct WAVEOUTCAPSA { WORD wMid; WORD wPid; DWORD vDriverVersion; char szPname[32]; DWORD dwFormats; WORD wChannels; WORD wReserved1; DWORD dwSupport; } WAVEOUTCAPSA; typedef char TEST_WAVEFORMATEX_SIZE[(sizeof(WAVEFORMATEX) == 20u) ? 1 : -1]; typedef char TEST_WAVEHDR_SIZE[(sizeof(WAVEHDR) == 48u) ? 1 : -1]; typedef char TEST_MMTIME_SIZE[(sizeof(MMTIME) == 8u) ? 1 : -1]; typedef char TEST_WAVEOUTCAPSA_SIZE[(sizeof(WAVEOUTCAPSA) == 52u) ? 1 : -1]; typedef void (__stdcall *WAVEOUTPROC)(HWAVEOUT, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR); __declspec(dllimport) UINT waveOutGetNumDevs(void); __declspec(dllimport) MMRESULT waveOutOpen(HWAVEOUT*, UINT, const WAVEFORMATEX*, DWORD_PTR, DWORD_PTR, DWORD); __declspec(dllimport) MMRESULT waveOutClose(HWAVEOUT); __declspec(dllimport) MMRESULT waveOutPrepareHeader(HWAVEOUT, WAVEHDR*, UINT); __declspec(dllimport) MMRESULT waveOutUnprepareHeader(HWAVEOUT, WAVEHDR*, UINT); __declspec(dllimport) MMRESULT waveOutWrite(HWAVEOUT, WAVEHDR*, UINT); __declspec(dllimport) MMRESULT waveOutPause(HWAVEOUT); __declspec(dllimport) MMRESULT waveOutRestart(HWAVEOUT); __declspec(dllimport) MMRESULT waveOutReset(HWAVEOUT); __declspec(dllimport) MMRESULT waveOutSetVolume(HWAVEOUT, DWORD); __declspec(dllimport) MMRESULT waveOutGetVolume(HWAVEOUT, DWORD*); __declspec(dllimport) MMRESULT waveOutGetPosition(HWAVEOUT, MMTIME*, UINT); __declspec(dllimport) MMRESULT waveOutGetID(HWAVEOUT, UINT*); __declspec(dllimport) MMRESULT waveOutGetDevCapsA(UINT, WAVEOUTCAPSA*, UINT); __declspec(dllimport) MMRESULT waveOutGetErrorTextA(MMRESULT, char*, UINT); __declspec(dllimport) HANDLE CreateEventA(void*, BOOL, BOOL, const char*); __declspec(dllimport) BOOL SetEvent(HANDLE); __declspec(dllimport) DWORD WaitForSingleObject(HANDLE, DWORD); __declspec(dllimport) BOOL CloseHandle(HANDLE); static int16_t gS16Frames[TEST_FRAMES * 2u]; static uint8_t gS24Frames[TEST_FRAMES * 2u * 3u]; static int16_t gControlFrames[TEST_CONTROL_FRAMES * 2u]; static HANDLE gFunctionEvent; static volatile long gOpenCallbacks; static volatile long gDoneCallbacks; static volatile long gCloseCallbacks; // Writes one NUL-terminated string to the kernel console. static void print(const char* text) { SYS_WRITE_CONSOLE_REQUEST request = { SysSvFromCstr(text) }; (void)SysWriteConsole(&request); } // Terminates the test process after one failure. static void fail(const char* message) { print("winmm_waveout_smoke: "); print(message); print("\n"); while (true) (void)SysExitProcess(1); } // Verifies one WinMM result exactly. static void expect_result(const char* operation, MMRESULT actual, MMRESULT expected) { if (actual != expected) { print("winmm_waveout_smoke: unexpected result from "); print(operation); print("\n"); while (true) (void)SysExitProcess(1); } } // Returns one exact integer 440 Hz triangle-wave sample at 48 kHz. static int16_t tone_sample(uint32_t phase) { int32_t sample; if (phase < TEST_QUARTER_PHASE) { sample = (int32_t)(phase * TEST_PEAK / TEST_QUARTER_PHASE); } else if (phase < TEST_QUARTER_PHASE * 2u) { sample = (int32_t)((TEST_QUARTER_PHASE * 2u - phase) * TEST_PEAK / TEST_QUARTER_PHASE); } else if (phase < TEST_QUARTER_PHASE * 3u) { sample = -(int32_t)((phase - TEST_QUARTER_PHASE * 2u) * TEST_PEAK / TEST_QUARTER_PHASE); } else { sample = -(int32_t)((TEST_PHASE_MODULUS - phase) * TEST_PEAK / TEST_QUARTER_PHASE); } return (int16_t)sample; } // Fills S16, packed-S24, and muted control buffers without floating point. static void fill_buffers(void) { uint32_t phase = 0; for (uint32_t frame = 0; frame < TEST_FRAMES; frame++) { int16_t sample = tone_sample(phase); gS16Frames[frame * 2u] = sample; gS16Frames[frame * 2u + 1u] = sample; int32_t sample24 = (int32_t)sample * 256; for (uint32_t channel = 0; channel < 2u; channel++) { size_t offset = ((size_t)frame * 2u + channel) * 3u; gS24Frames[offset] = (uint8_t)sample24; gS24Frames[offset + 1u] = (uint8_t)(sample24 >> 8u); gS24Frames[offset + 2u] = (uint8_t)(sample24 >> 16u); } phase += TEST_PHASE_STEP; if (phase >= TEST_PHASE_MODULUS) phase -= TEST_PHASE_MODULUS; } memset(gControlFrames, 0, sizeof(gControlFrames)); } // Receives function callbacks on the per-HWAVEOUT worker thread. static void __stdcall wave_callback(HWAVEOUT waveOut, UINT message, DWORD_PTR instance, DWORD_PTR parameter1, DWORD_PTR parameter2) { (void)waveOut; (void)instance; (void)parameter1; (void)parameter2; if (message == WOM_OPEN) _InterlockedIncrement(&gOpenCallbacks); else if (message == WOM_DONE) { _InterlockedIncrement(&gDoneCallbacks); (void)SetEvent(gFunctionEvent); } else if (message == WOM_CLOSE) _InterlockedIncrement(&gCloseCallbacks); } // Waits for one callback event with a finite deadline. static void wait_event(HANDLE event, const char* operation) { if (WaitForSingleObject(event, 5000u) != WAIT_OBJECT_0) fail(operation); } // Returns one canonical PCM format descriptor. static WAVEFORMATEX make_format_at(uint32_t sampleRate, uint16_t channels, uint16_t bitsPerSample) { WAVEFORMATEX format; memset(&format, 0, sizeof(format)); format.wFormatTag = WAVE_FORMAT_PCM; format.nChannels = channels; format.nSamplesPerSec = sampleRate; format.wBitsPerSample = bitsPerSample; format.nBlockAlign = (uint16_t)(channels * (bitsPerSample / 8u)); format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; return format; } // Returns the default stereo 48 kHz PCM descriptor used for playback tests. static WAVEFORMATEX make_format(uint16_t bitsPerSample) { return make_format_at(48000u, 2u, bitsPerSample); } // Verifies device enumeration, capabilities, and fixed error descriptions. static void test_device_surface(void) { WAVEOUTCAPSA caps; memset(&caps, 0, sizeof(caps)); expect_result("device capabilities", waveOutGetDevCapsA(WAVE_MAPPER, &caps, sizeof(caps)), MMSYSERR_NOERROR); if (caps.wChannels != 2u || !caps.szPname[0]) fail("device capabilities mismatch"); char errorText[64]; memset(errorText, 0, sizeof(errorText)); expect_result("error text", waveOutGetErrorTextA(WAVERR_BADFORMAT, errorText, sizeof(errorText)), MMSYSERR_NOERROR); if (!errorText[0]) fail("error text was empty"); } // Verifies function callbacks, queued completion, immediate copy, and position. static void test_function_callback(void) { WAVEFORMATEX format = make_format(16u); gFunctionEvent = CreateEventA(NULL, FALSE, FALSE, NULL); if (!gFunctionEvent) fail("CreateEventA for function callback failed"); HWAVEOUT waveOut = NULL; expect_result("function waveOutOpen", waveOutOpen(&waveOut, WAVE_MAPPER, &format, (DWORD_PTR)(WAVEOUTPROC)wave_callback, 0, CALLBACK_FUNCTION | WAVE_ALLOWSYNC | WAVE_FORMAT_DIRECT), MMSYSERR_NOERROR); if (!waveOut || gOpenCallbacks != 1) fail("function WOM_OPEN callback missing"); UINT deviceId = UINT32_MAX; expect_result("get device ID", waveOutGetID(waveOut, &deviceId), MMSYSERR_NOERROR); if (deviceId >= waveOutGetNumDevs()) fail("resolved device ID is out of range"); WAVEHDR first; WAVEHDR second; memset(&first, 0, sizeof(first)); memset(&second, 0, sizeof(second)); first.lpData = (char*)gS16Frames; first.dwBufferLength = sizeof(gS16Frames) / 2u; second.lpData = (char*)gS16Frames + first.dwBufferLength; second.dwBufferLength = sizeof(gS16Frames) - first.dwBufferLength; expect_result("prepare first", waveOutPrepareHeader(waveOut, &first, sizeof(first)), MMSYSERR_NOERROR); expect_result("prepare second", waveOutPrepareHeader(waveOut, &second, sizeof(second)), MMSYSERR_NOERROR); expect_result("write first", waveOutWrite(waveOut, &first, sizeof(first)), MMSYSERR_NOERROR); expect_result("write second", waveOutWrite(waveOut, &second, sizeof(second)), MMSYSERR_NOERROR); memset(gS16Frames, 0, sizeof(gS16Frames)); while (gDoneCallbacks < 2) wait_event(gFunctionEvent, "function WOM_DONE timed out"); if (gDoneCallbacks != 2 || (first.dwFlags & (WHDR_DONE | WHDR_PREPARED)) != (WHDR_DONE | WHDR_PREPARED) || (second.dwFlags & (WHDR_DONE | WHDR_PREPARED)) != (WHDR_DONE | WHDR_PREPARED) || (first.dwFlags & WHDR_INQUEUE) || (second.dwFlags & WHDR_INQUEUE)) fail("function header completion mismatch"); MMTIME position; memset(&position, 0, sizeof(position)); position.wType = TIME_SAMPLES; expect_result("get position", waveOutGetPosition(waveOut, &position, sizeof(position)), MMSYSERR_NOERROR); if (position.u.sample < TEST_FRAMES) fail("position did not reach submitted frames"); expect_result("set volume", waveOutSetVolume(waveOut, 0xC0008000u), MMSYSERR_NOERROR); DWORD volume = 0; expect_result("get volume", waveOutGetVolume(waveOut, &volume), MMSYSERR_NOERROR); if (volume != 0xC0008000u) fail("volume did not round trip"); expect_result("unprepare first", waveOutUnprepareHeader(waveOut, &first, sizeof(first)), MMSYSERR_NOERROR); expect_result("unprepare second", waveOutUnprepareHeader(waveOut, &second, sizeof(second)), MMSYSERR_NOERROR); expect_result("function close", waveOutClose(waveOut), MMSYSERR_NOERROR); if (gCloseCallbacks != 1) fail("function WOM_CLOSE callback missing"); if (!CloseHandle(gFunctionEvent)) fail("function callback event close failed"); gFunctionEvent = NULL; } // Verifies CALLBACK_EVENT and packed-S24 conversion through the native S16 stream. static void test_event_s24(void) { WAVEFORMATEX format = make_format(24u); HANDLE event = CreateEventA(NULL, FALSE, FALSE, NULL); if (!event) fail("CreateEventA for event callback failed"); HWAVEOUT waveOut = NULL; expect_result("event waveOutOpen", waveOutOpen(&waveOut, WAVE_MAPPER, &format, (DWORD_PTR)event, 0, CALLBACK_EVENT), MMSYSERR_NOERROR); wait_event(event, "event WOM_OPEN timed out"); WAVEHDR header; memset(&header, 0, sizeof(header)); header.lpData = (char*)gS24Frames; header.dwBufferLength = sizeof(gS24Frames); expect_result("event prepare", waveOutPrepareHeader(waveOut, &header, sizeof(header)), MMSYSERR_NOERROR); expect_result("event write", waveOutWrite(waveOut, &header, sizeof(header)), MMSYSERR_NOERROR); memset(gS24Frames, 0, sizeof(gS24Frames)); wait_event(event, "event WOM_DONE timed out"); if ((header.dwFlags & WHDR_DONE) == 0 || (header.dwFlags & WHDR_INQUEUE)) fail("event header completion mismatch"); expect_result("event unprepare", waveOutUnprepareHeader(waveOut, &header, sizeof(header)), MMSYSERR_NOERROR); expect_result("event close", waveOutClose(waveOut), MMSYSERR_NOERROR); wait_event(event, "event WOM_CLOSE timed out"); if (!CloseHandle(event)) fail("event callback handle close failed"); } // Verifies queued-close refusal and synchronous pause, restart, and reset. static void test_control_operations(void) { WAVEFORMATEX format = make_format(16u); HWAVEOUT waveOut = NULL; expect_result("control waveOutOpen", waveOutOpen(&waveOut, WAVE_MAPPER, &format, 0, 0, 0), MMSYSERR_NOERROR); expect_result("control mute", waveOutSetVolume(waveOut, 0), MMSYSERR_NOERROR); WAVEHDR header; memset(&header, 0, sizeof(header)); header.lpData = (char*)gControlFrames; header.dwBufferLength = sizeof(gControlFrames); expect_result("control prepare", waveOutPrepareHeader(waveOut, &header, sizeof(header)), MMSYSERR_NOERROR); header.dwFlags |= WHDR_BEGINLOOP | WHDR_ENDLOOP; header.dwLoops = 2u; expect_result("loop rejection", waveOutWrite(waveOut, &header, sizeof(header)), MMSYSERR_NOTSUPPORTED); header.dwFlags &= ~(WHDR_BEGINLOOP | WHDR_ENDLOOP); header.dwLoops = 0; expect_result("control write", waveOutWrite(waveOut, &header, sizeof(header)), MMSYSERR_NOERROR); expect_result("close while queued", waveOutClose(waveOut), WAVERR_STILLPLAYING); expect_result("unprepare while queued", waveOutUnprepareHeader(waveOut, &header, sizeof(header)), WAVERR_STILLPLAYING); expect_result("pause", waveOutPause(waveOut), MMSYSERR_NOERROR); expect_result("second pause", waveOutPause(waveOut), MMSYSERR_NOERROR); expect_result("restart", waveOutRestart(waveOut), MMSYSERR_NOERROR); expect_result("second restart", waveOutRestart(waveOut), MMSYSERR_NOERROR); expect_result("reset", waveOutReset(waveOut), MMSYSERR_NOERROR); if ((header.dwFlags & WHDR_DONE) == 0 || (header.dwFlags & WHDR_INQUEUE)) fail("reset did not complete queued header"); expect_result("control unprepare", waveOutUnprepareHeader(waveOut, &header, sizeof(header)), MMSYSERR_NOERROR); expect_result("control close", waveOutClose(waveOut), MMSYSERR_NOERROR); } // Verifies the complete accepted PCM matrix and explicit rejection paths. static void test_formats_and_rejections(void) { static const uint32_t rates[] = { 11025u, 22050u, 44100u, 48000u }; static const uint16_t bits[] = { 16u, 24u }; for (uint32_t rateIndex = 0; rateIndex < sizeof(rates) / sizeof(rates[0]); rateIndex++) { for (uint16_t channels = 1u; channels <= 2u; channels++) { for (uint32_t bitsIndex = 0; bitsIndex < sizeof(bits) / sizeof(bits[0]); bitsIndex++) { WAVEFORMATEX format = make_format_at(rates[rateIndex], channels, bits[bitsIndex]); expect_result("accepted PCM query", waveOutOpen(NULL, WAVE_MAPPER, &format, 0, 0, WAVE_FORMAT_QUERY), MMSYSERR_NOERROR); } } } WAVEFORMATEX s16 = make_format(16u); HWAVEOUT waveOut = NULL; expect_result("window callback rejection", waveOutOpen(&waveOut, WAVE_MAPPER, &s16, 1, 0, CALLBACK_WINDOW), MMSYSERR_NOTSUPPORTED); expect_result("thread callback rejection", waveOutOpen(&waveOut, WAVE_MAPPER, &s16, 1, 0, CALLBACK_THREAD), MMSYSERR_NOTSUPPORTED); WAVEFORMATEX floating = make_format_at(48000u, 2u, 32u); floating.wFormatTag = 3u; expect_result("Float32 rejection", waveOutOpen(NULL, WAVE_MAPPER, &floating, 0, 0, WAVE_FORMAT_QUERY), WAVERR_BADFORMAT); WAVEFORMATEX badRate = make_format_at(32000u, 2u, 16u); expect_result("32 kHz rejection", waveOutOpen(NULL, WAVE_MAPPER, &badRate, 0, 0, WAVE_FORMAT_QUERY), WAVERR_BADFORMAT); WAVEFORMATEX badChannels = make_format_at(48000u, 3u, 16u); expect_result("three-channel rejection", waveOutOpen(NULL, WAVE_MAPPER, &badChannels, 0, 0, WAVE_FORMAT_QUERY), WAVERR_BADFORMAT); } // Runs the focused WinMM waveform-output contract test. int main(void) { print("winmm_waveout_smoke: begin\n"); if (!waveOutGetNumDevs()) fail("no waveform output devices"); fill_buffers(); test_device_surface(); test_formats_and_rejections(); test_control_operations(); test_function_callback(); test_event_s24(); print("winmm_waveout_smoke: PASS\n"); while (true) (void)SysExitProcess(0); return 0; }