| 
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 }
 |