Mercurial > codedump
view win95kggui/dep/ft2play/audiodrivers/winmm/winmm.c @ 133:0d8eabdd12ab default tip
create: write H:MM:SS timestamps, add option to fill with gaussian-blur instead of black
many albums are longer than one hour so writing H:MM:SS is a
necessity. if anything there will just be verbose info that
isn't important for my use-case.
however the gaussian-blur is simply broken. It works, and it plays
locally just fine, but YouTube in particular elongates the video
to fit the full width. I'm not entirely sure why it does this, but
it makes it useless and ugly.
| author | Paper <paper@tflc.us> |
|---|---|
| date | Sat, 03 Jan 2026 20:25:38 -0500 |
| parents | 8e4ee43d3b81 |
| children |
line wrap: on
line source
/* winmm audio driver for ft2play ** ** Warning: This might not be 100% thread-safe or lock-safe! */ #define WIN32_LEAN_AND_MEAN #include <stdint.h> #include <stdbool.h> #include <stdlib.h> #include <windows.h> #include <mmsystem.h> #include "../../pmp_mix.h" #define MIX_BUF_NUM 4 static volatile BOOL mixerOpened, mixerBusy, mixerLocked; static uint8_t currBuffer; static int16_t *audioBuffer[MIX_BUF_NUM]; static int32_t bufferSize; static HANDLE hThread, hAudioSem; static WAVEHDR waveBlocks[MIX_BUF_NUM]; static HWAVEOUT hWave; static DWORD WINAPI mixThread(LPVOID lpParam) { (void)lpParam; SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); while (mixerOpened) { WAVEHDR *waveBlock = &waveBlocks[currBuffer]; if (!mixerLocked) { mixerBusy = true; mix_UpdateBuffer((int16_t *)waveBlock->lpData, bufferSize); // pmp_mix.c function mixerBusy = false; } waveOutWrite(hWave, waveBlock, sizeof (WAVEHDR)); currBuffer = (currBuffer + 1) % MIX_BUF_NUM; WaitForSingleObject(hAudioSem, INFINITE); // wait for buffer fill request } return 0; } static void CALLBACK waveProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { if (uMsg == WOM_DONE) ReleaseSemaphore(hAudioSem, 1, NULL); (void)hWaveOut; (void)uMsg; (void)dwInstance; (void)dwParam1; (void)dwParam2; } void lockMixer(void) { mixerLocked = true; while (mixerBusy); } void unlockMixer(void) { mixerBusy = false; mixerLocked = false; } void closeMixer(void) { mixerOpened = false; mixerBusy = false; if (hAudioSem != NULL) ReleaseSemaphore(hAudioSem, 1, NULL); if (hThread != NULL) { WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); hThread = NULL; } if (hAudioSem != NULL) { CloseHandle(hAudioSem); hAudioSem = NULL; } if (hWave != NULL) { waveOutReset(hWave); for (int32_t i = 0; i < MIX_BUF_NUM; i++) { if (waveBlocks[i].dwUser != 0xFFFF) waveOutUnprepareHeader(hWave, &waveBlocks[i], sizeof (WAVEHDR)); } waveOutClose(hWave); hWave = NULL; } for (int32_t i = 0; i < MIX_BUF_NUM; i++) { if (audioBuffer[i] != NULL) { free(audioBuffer[i]); audioBuffer[i] = NULL; } } } bool openMixer(int32_t mixingFrequency, int32_t mixingBufferSize) { DWORD threadID; WAVEFORMATEX wfx; // don't unprepare headers on error for (int32_t i = 0; i < MIX_BUF_NUM; i++) waveBlocks[i].dwUser = 0xFFFF; closeMixer(); bufferSize = mixingBufferSize; ZeroMemory(&wfx, sizeof (wfx)); wfx.nSamplesPerSec = mixingFrequency; wfx.wBitsPerSample = 16; wfx.nChannels = 2; wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8); wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; if (waveOutOpen(&hWave, WAVE_MAPPER, &wfx, (DWORD_PTR)&waveProc, 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) goto omError; // create semaphore for buffer fill requests hAudioSem = CreateSemaphore(NULL, MIX_BUF_NUM - 1, MIX_BUF_NUM, NULL); if (hAudioSem == NULL) goto omError; // allocate WinMM mix buffers for (int32_t i = 0; i < MIX_BUF_NUM; i++) { audioBuffer[i] = (int16_t *)calloc(mixingBufferSize, wfx.nBlockAlign); if (audioBuffer[i] == NULL) goto omError; } // initialize WinMM mix headers memset(waveBlocks, 0, sizeof (waveBlocks)); for (int32_t i = 0; i < MIX_BUF_NUM; i++) { waveBlocks[i].lpData = (LPSTR)audioBuffer[i]; waveBlocks[i].dwBufferLength = mixingBufferSize * wfx.nBlockAlign; waveBlocks[i].dwFlags = WHDR_DONE; if (waveOutPrepareHeader(hWave, &waveBlocks[i], sizeof (WAVEHDR)) != MMSYSERR_NOERROR) goto omError; } currBuffer = 0; mixerOpened = true; hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)mixThread, NULL, 0, &threadID); if (hThread == NULL) goto omError; return TRUE; omError: closeMixer(); return FALSE; }
