1 /* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2012 Sam Lantinga 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 19 Sam Lantinga 20 slouken (at) libsdl.org 21 */ 22 #include "SDL_config.h" 23 24 /* Allow access to a raw mixing buffer */ 25 26 #define WIN32_LEAN_AND_MEAN 27 #include <windows.h> 28 #include <mmsystem.h> 29 30 #include "SDL_timer.h" 31 #include "SDL_audio.h" 32 #include "../SDL_audio_c.h" 33 #include "SDL_dibaudio.h" 34 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300) 35 #include "win_ce_semaphore.h" 36 #endif 37 38 39 /* Audio driver functions */ 40 static int DIB_OpenAudio(_THIS, SDL_AudioSpec *spec); 41 static void DIB_ThreadInit(_THIS); 42 static void DIB_WaitAudio(_THIS); 43 static Uint8 *DIB_GetAudioBuf(_THIS); 44 static void DIB_PlayAudio(_THIS); 45 static void DIB_WaitDone(_THIS); 46 static void DIB_CloseAudio(_THIS); 47 48 int volatile dibaudio_thread_debug = 0; 49 int volatile dibaudio_cb_debug = 0; 50 int volatile dibaudio_main_debug = 0; 51 52 /* Audio driver bootstrap functions */ 53 54 static int Audio_Available(void) 55 { 56 return(1); 57 } 58 59 static void Audio_DeleteDevice(SDL_AudioDevice *device) 60 { 61 SDL_free(device->hidden); 62 SDL_free(device); 63 } 64 65 static SDL_AudioDevice *Audio_CreateDevice(int devindex) 66 { 67 SDL_AudioDevice *this; 68 69 /* Initialize all variables that we clean on shutdown */ 70 this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); 71 if ( this ) { 72 SDL_memset(this, 0, (sizeof *this)); 73 this->hidden = (struct SDL_PrivateAudioData *) 74 SDL_malloc((sizeof *this->hidden)); 75 } 76 if ( (this == NULL) || (this->hidden == NULL) ) { 77 SDL_OutOfMemory(); 78 if ( this ) { 79 SDL_free(this); 80 } 81 return(0); 82 } 83 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 84 85 /* Set the function pointers */ 86 this->OpenAudio = DIB_OpenAudio; 87 this->ThreadInit = DIB_ThreadInit; 88 this->WaitAudio = DIB_WaitAudio; 89 this->PlayAudio = DIB_PlayAudio; 90 this->GetAudioBuf = DIB_GetAudioBuf; 91 this->WaitDone = DIB_WaitDone; 92 this->CloseAudio = DIB_CloseAudio; 93 94 this->free = Audio_DeleteDevice; 95 96 return this; 97 } 98 99 AudioBootStrap WAVEOUT_bootstrap = { 100 "waveout", "Win95/98/NT/2000 WaveOut", 101 Audio_Available, Audio_CreateDevice 102 }; 103 104 105 /* The Win32 callback for filling the WAVE device */ 106 static void CALLBACK FillSound(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, 107 DWORD dwParam1, DWORD dwParam2) 108 { 109 SDL_AudioDevice *this = (SDL_AudioDevice *)dwInstance; 110 111 /* Only service "buffer done playing" messages */ 112 if ( uMsg != WOM_DONE ) 113 return; 114 115 /* Signal that we are done playing a buffer */ 116 dibaudio_cb_debug = 1; 117 EnterCriticalSection(&audio_cs); 118 dibaudio_cb_debug = 2; 119 SetEvent(audio_event); 120 dibaudio_cb_debug = 3; 121 LeaveCriticalSection(&audio_cs); 122 dibaudio_cb_debug = 4; 123 } 124 125 static void SetMMerror(char *function, MMRESULT code) 126 { 127 size_t len; 128 char errbuf[MAXERRORLENGTH]; 129 #ifdef _WIN32_WCE 130 wchar_t werrbuf[MAXERRORLENGTH]; 131 #endif 132 133 SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function); 134 len = SDL_strlen(errbuf); 135 136 #ifdef _WIN32_WCE 137 /* UNICODE version */ 138 waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH-len); 139 WideCharToMultiByte(CP_ACP,0,werrbuf,-1,errbuf+len,MAXERRORLENGTH-len,NULL,NULL); 140 #else 141 waveOutGetErrorText(code, errbuf+len, (UINT)(MAXERRORLENGTH-len)); 142 #endif 143 144 SDL_SetError("%s",errbuf); 145 } 146 147 /* Set high priority for the audio thread */ 148 static void DIB_ThreadInit(_THIS) 149 { 150 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); 151 } 152 153 void DIB_WaitAudio(_THIS) 154 { 155 /* Wait for an audio chunk to finish */ 156 dibaudio_thread_debug = 1; 157 WaitForSingleObject(audio_event, INFINITE); 158 dibaudio_thread_debug = 2; 159 ResetEvent(audio_event); 160 dibaudio_thread_debug = 3; 161 } 162 163 Uint8 *DIB_GetAudioBuf(_THIS) 164 { 165 Uint8 *retval; 166 167 dibaudio_thread_debug = 4; 168 EnterCriticalSection(&audio_cs); 169 dibaudio_thread_debug = 5; 170 retval = (Uint8 *)(wavebuf[next_buffer].lpData); 171 return retval; 172 } 173 174 void DIB_PlayAudio(_THIS) 175 { 176 /* Queue it up */ 177 dibaudio_thread_debug = 6; 178 waveOutWrite(sound, &wavebuf[next_buffer], sizeof(wavebuf[0])); 179 dibaudio_thread_debug = 7; 180 next_buffer = (next_buffer+1)%NUM_BUFFERS; 181 LeaveCriticalSection(&audio_cs); 182 dibaudio_thread_debug = 8; 183 } 184 185 void DIB_WaitDone(_THIS) 186 { 187 int i, left, tries = 5; 188 189 dibaudio_thread_debug = 9; 190 191 /* give some time for the wave output to send the last buffer, 192 but don't hang if one was cancelled 193 */ 194 do { 195 left = NUM_BUFFERS; 196 for ( i=0; i<NUM_BUFFERS; ++i ) { 197 if ( wavebuf[i].dwFlags & WHDR_DONE ) { 198 --left; 199 } 200 } 201 if ( left == 0 ) 202 break; 203 204 SDL_Delay(100); 205 } while ( --tries > 0 ); 206 207 dibaudio_thread_debug = 10; 208 } 209 210 void DIB_CloseAudio(_THIS) 211 { 212 int i; 213 214 /* Close up audio */ 215 if (audio_event != INVALID_HANDLE_VALUE ) { 216 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300) 217 CloseSynchHandle(audio_event); 218 #else 219 CloseHandle(audio_event); 220 #endif 221 } 222 if ( sound ) { 223 waveOutReset(sound); 224 } 225 226 /* Clean up mixing buffers */ 227 for ( i=0; i<NUM_BUFFERS; ++i ) { 228 if ( wavebuf[i].dwUser != 0xFFFF ) { 229 waveOutUnprepareHeader(sound, &wavebuf[i], 230 sizeof(wavebuf[i])); 231 wavebuf[i].dwUser = 0xFFFF; 232 } 233 } 234 /* Free raw mixing buffer */ 235 if ( mixbuf != NULL ) { 236 SDL_free(mixbuf); 237 mixbuf = NULL; 238 } 239 240 if ( sound ) 241 waveOutClose(sound); 242 } 243 244 int DIB_OpenAudio(_THIS, SDL_AudioSpec *spec) 245 { 246 MMRESULT result; 247 int i; 248 WAVEFORMATEX waveformat; 249 250 /* Initialize the wavebuf structures for closing */ 251 sound = NULL; 252 InitializeCriticalSection(&audio_cs); 253 audio_event = INVALID_HANDLE_VALUE; 254 for ( i = 0; i < NUM_BUFFERS; ++i ) 255 wavebuf[i].dwUser = 0xFFFF; 256 mixbuf = NULL; 257 258 /* Set basic WAVE format parameters */ 259 SDL_memset(&waveformat, 0, sizeof(waveformat)); 260 waveformat.wFormatTag = WAVE_FORMAT_PCM; 261 262 /* Determine the audio parameters from the AudioSpec */ 263 switch ( spec->format & 0xFF ) { 264 case 8: 265 /* Unsigned 8 bit audio data */ 266 spec->format = AUDIO_U8; 267 waveformat.wBitsPerSample = 8; 268 break; 269 case 16: 270 /* Signed 16 bit audio data */ 271 spec->format = AUDIO_S16; 272 waveformat.wBitsPerSample = 16; 273 break; 274 default: 275 SDL_SetError("Unsupported audio format"); 276 return(-1); 277 } 278 waveformat.nChannels = spec->channels; 279 waveformat.nSamplesPerSec = spec->freq; 280 waveformat.nBlockAlign = 281 waveformat.nChannels * (waveformat.wBitsPerSample/8); 282 waveformat.nAvgBytesPerSec = 283 waveformat.nSamplesPerSec * waveformat.nBlockAlign; 284 285 /* Check the buffer size -- minimum of 1/4 second (word aligned) */ 286 if ( spec->samples < (spec->freq/4) ) 287 spec->samples = ((spec->freq/4)+3)&~3; 288 289 /* Update the fragment size as size in bytes */ 290 SDL_CalculateAudioSpec(spec); 291 292 /* Open the audio device */ 293 result = waveOutOpen(&sound, WAVE_MAPPER, &waveformat, 294 (DWORD_PTR)FillSound, (DWORD_PTR)this, CALLBACK_FUNCTION); 295 if ( result != MMSYSERR_NOERROR ) { 296 SetMMerror("waveOutOpen()", result); 297 return(-1); 298 } 299 300 #ifdef SOUND_DEBUG 301 /* Check the sound device we retrieved */ 302 { 303 WAVEOUTCAPS caps; 304 305 result = waveOutGetDevCaps((UINT)sound, &caps, sizeof(caps)); 306 if ( result != MMSYSERR_NOERROR ) { 307 SetMMerror("waveOutGetDevCaps()", result); 308 return(-1); 309 } 310 printf("Audio device: %s\n", caps.szPname); 311 } 312 #endif 313 314 /* Create the audio buffer semaphore */ 315 audio_event = CreateEvent( NULL, TRUE, FALSE, NULL ); 316 if (audio_event == NULL) { 317 SDL_SetError("Couldn't create semaphore"); 318 return(-1); 319 } 320 321 /* Create the sound buffers */ 322 mixbuf = (Uint8 *)SDL_malloc(NUM_BUFFERS*spec->size); 323 if ( mixbuf == NULL ) { 324 SDL_SetError("Out of memory"); 325 return(-1); 326 } 327 for ( i = 0; i < NUM_BUFFERS; ++i ) { 328 SDL_memset(&wavebuf[i], 0, sizeof(wavebuf[i])); 329 wavebuf[i].lpData = (LPSTR) &mixbuf[i*spec->size]; 330 wavebuf[i].dwBufferLength = spec->size; 331 wavebuf[i].dwFlags = WHDR_DONE; 332 result = waveOutPrepareHeader(sound, &wavebuf[i], 333 sizeof(wavebuf[i])); 334 if ( result != MMSYSERR_NOERROR ) { 335 SetMMerror("waveOutPrepareHeader()", result); 336 return(-1); 337 } 338 } 339 340 /* Ready to go! */ 341 next_buffer = 0; 342 return(0); 343 } 344