| 126 | 1 /* winmm audio driver for ft2play | 
|  | 2 ** | 
|  | 3 ** Warning: This might not be 100% thread-safe or lock-safe! | 
|  | 4 */ | 
|  | 5 | 
|  | 6 #define WIN32_LEAN_AND_MEAN | 
|  | 7 | 
|  | 8 #include <stdint.h> | 
|  | 9 #include <stdbool.h> | 
|  | 10 #include <stdlib.h> | 
|  | 11 #include <windows.h> | 
|  | 12 #include <mmsystem.h> | 
|  | 13 #include "../../pmp_mix.h" | 
|  | 14 | 
|  | 15 #define MIX_BUF_NUM 4 | 
|  | 16 | 
|  | 17 static volatile BOOL mixerOpened, mixerBusy, mixerLocked; | 
|  | 18 static uint8_t currBuffer; | 
|  | 19 static int16_t *audioBuffer[MIX_BUF_NUM]; | 
|  | 20 static int32_t bufferSize; | 
|  | 21 static HANDLE hThread, hAudioSem; | 
|  | 22 static WAVEHDR waveBlocks[MIX_BUF_NUM]; | 
|  | 23 static HWAVEOUT hWave; | 
|  | 24 | 
|  | 25 static DWORD WINAPI mixThread(LPVOID lpParam) | 
|  | 26 { | 
|  | 27 	(void)lpParam; | 
|  | 28 	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); | 
|  | 29 	while (mixerOpened) | 
|  | 30 	{ | 
|  | 31 		WAVEHDR *waveBlock = &waveBlocks[currBuffer]; | 
|  | 32 | 
|  | 33 		if (!mixerLocked) | 
|  | 34 		{ | 
|  | 35 			mixerBusy = true; | 
|  | 36 			mix_UpdateBuffer((int16_t *)waveBlock->lpData, bufferSize); // pmp_mix.c function | 
|  | 37 			mixerBusy = false; | 
|  | 38 		} | 
|  | 39 | 
|  | 40 		waveOutWrite(hWave, waveBlock, sizeof (WAVEHDR)); | 
|  | 41 		currBuffer = (currBuffer + 1) % MIX_BUF_NUM; | 
|  | 42 		WaitForSingleObject(hAudioSem, INFINITE); // wait for buffer fill request | 
|  | 43 	} | 
|  | 44 | 
|  | 45 	return 0; | 
|  | 46 } | 
|  | 47 | 
|  | 48 static void CALLBACK waveProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) | 
|  | 49 { | 
|  | 50 	if (uMsg == WOM_DONE) | 
|  | 51 		ReleaseSemaphore(hAudioSem, 1, NULL); | 
|  | 52 | 
|  | 53 	(void)hWaveOut; | 
|  | 54 	(void)uMsg; | 
|  | 55 	(void)dwInstance; | 
|  | 56 	(void)dwParam1; | 
|  | 57 	(void)dwParam2; | 
|  | 58 } | 
|  | 59 | 
|  | 60 void lockMixer(void) | 
|  | 61 { | 
|  | 62 	mixerLocked = true; | 
|  | 63 	while (mixerBusy); | 
|  | 64 } | 
|  | 65 | 
|  | 66 void unlockMixer(void) | 
|  | 67 { | 
|  | 68 	mixerBusy = false; | 
|  | 69 	mixerLocked = false; | 
|  | 70 } | 
|  | 71 | 
|  | 72 void closeMixer(void) | 
|  | 73 { | 
|  | 74 	mixerOpened = false; | 
|  | 75 	mixerBusy = false; | 
|  | 76 | 
|  | 77 	if (hAudioSem != NULL) | 
|  | 78 		ReleaseSemaphore(hAudioSem, 1, NULL); | 
|  | 79 | 
|  | 80 	if (hThread != NULL) | 
|  | 81 	{ | 
|  | 82 		WaitForSingleObject(hThread, INFINITE); | 
|  | 83 		CloseHandle(hThread); | 
|  | 84 		hThread = NULL; | 
|  | 85 	} | 
|  | 86 | 
|  | 87 	if (hAudioSem != NULL) | 
|  | 88 	{ | 
|  | 89 		CloseHandle(hAudioSem); | 
|  | 90 		hAudioSem = NULL; | 
|  | 91 	} | 
|  | 92 | 
|  | 93 	if (hWave != NULL) | 
|  | 94 	{ | 
|  | 95 		waveOutReset(hWave); | 
|  | 96 | 
|  | 97 		for (int32_t i = 0; i < MIX_BUF_NUM; i++) | 
|  | 98 		{ | 
|  | 99 			if (waveBlocks[i].dwUser != 0xFFFF) | 
|  | 100 				waveOutUnprepareHeader(hWave, &waveBlocks[i], sizeof (WAVEHDR)); | 
|  | 101 		} | 
|  | 102 | 
|  | 103 		waveOutClose(hWave); | 
|  | 104 		hWave = NULL; | 
|  | 105 	} | 
|  | 106 | 
|  | 107 	for (int32_t i = 0; i < MIX_BUF_NUM; i++) | 
|  | 108 	{ | 
|  | 109 		if (audioBuffer[i] != NULL) | 
|  | 110 		{ | 
|  | 111 			free(audioBuffer[i]); | 
|  | 112 			audioBuffer[i] = NULL; | 
|  | 113 		} | 
|  | 114 	} | 
|  | 115 } | 
|  | 116 | 
|  | 117 bool openMixer(int32_t mixingFrequency, int32_t mixingBufferSize) | 
|  | 118 { | 
|  | 119 	DWORD threadID; | 
|  | 120 	WAVEFORMATEX wfx; | 
|  | 121 | 
|  | 122 	// don't unprepare headers on error | 
|  | 123 	for (int32_t i = 0; i < MIX_BUF_NUM; i++) | 
|  | 124 		waveBlocks[i].dwUser = 0xFFFF; | 
|  | 125 | 
|  | 126 	closeMixer(); | 
|  | 127 	bufferSize = mixingBufferSize; | 
|  | 128 | 
|  | 129 	ZeroMemory(&wfx, sizeof (wfx)); | 
|  | 130 	wfx.nSamplesPerSec = mixingFrequency; | 
|  | 131 	wfx.wBitsPerSample = 16; | 
|  | 132 	wfx.nChannels = 2; | 
|  | 133 	wfx.wFormatTag = WAVE_FORMAT_PCM; | 
|  | 134 	wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8); | 
|  | 135 	wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; | 
|  | 136 | 
|  | 137 	if (waveOutOpen(&hWave, WAVE_MAPPER, &wfx, (DWORD_PTR)&waveProc, 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) | 
|  | 138 		goto omError; | 
|  | 139 | 
|  | 140 	// create semaphore for buffer fill requests | 
|  | 141 	hAudioSem = CreateSemaphore(NULL, MIX_BUF_NUM - 1, MIX_BUF_NUM, NULL); | 
|  | 142 	if (hAudioSem == NULL) | 
|  | 143 		goto omError; | 
|  | 144 | 
|  | 145 	// allocate WinMM mix buffers | 
|  | 146 	for (int32_t i = 0; i < MIX_BUF_NUM; i++) | 
|  | 147 	{ | 
|  | 148 		audioBuffer[i] = (int16_t *)calloc(mixingBufferSize, wfx.nBlockAlign); | 
|  | 149 		if (audioBuffer[i] == NULL) | 
|  | 150 			goto omError; | 
|  | 151 	} | 
|  | 152 | 
|  | 153 	// initialize WinMM mix headers | 
|  | 154 	memset(waveBlocks, 0, sizeof (waveBlocks)); | 
|  | 155 	for (int32_t i = 0; i < MIX_BUF_NUM; i++) | 
|  | 156 	{ | 
|  | 157 		waveBlocks[i].lpData = (LPSTR)audioBuffer[i]; | 
|  | 158 		waveBlocks[i].dwBufferLength = mixingBufferSize * wfx.nBlockAlign; | 
|  | 159 		waveBlocks[i].dwFlags = WHDR_DONE; | 
|  | 160 | 
|  | 161 		if (waveOutPrepareHeader(hWave, &waveBlocks[i], sizeof (WAVEHDR)) != MMSYSERR_NOERROR) | 
|  | 162 			goto omError; | 
|  | 163 	} | 
|  | 164 | 
|  | 165 	currBuffer = 0; | 
|  | 166 	mixerOpened = true; | 
|  | 167 | 
|  | 168 	hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)mixThread, NULL, 0, &threadID); | 
|  | 169 	if (hThread == NULL) | 
|  | 170 		goto omError; | 
|  | 171 | 
|  | 172 	return TRUE; | 
|  | 173 | 
|  | 174 omError: | 
|  | 175 	closeMixer(); | 
|  | 176 	return FALSE; | 
|  | 177 } |