Home | History | Annotate | Download | only in windib
      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