src/audio/windib/SDL_dibaudio.c
author Sam Lantinga
Mon, 06 Feb 2006 08:28:51 +0000
changeset 1330 450721ad5436
parent 1312 c9b51268668f
child 1336 3692456e7b0f
permissions -rw-r--r--
It's now possible to build SDL without any C runtime at all on Windows,
using Visual C++ 2005
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2006 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@libsdl.org
    21 */
    22 
    23 /* Allow access to a raw mixing buffer */
    24 
    25 #include "SDL_windows.h"
    26 #include <mmsystem.h>
    27 
    28 #include "SDL_audio.h"
    29 #include "SDL_mutex.h"
    30 #include "SDL_timer.h"
    31 #include "SDL_stdlib.h"
    32 #include "SDL_string.h"
    33 #include "SDL_audio_c.h"
    34 #include "SDL_dibaudio.h"
    35 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
    36 #include "win_ce_semaphore.h"
    37 #endif
    38 
    39 
    40 /* Audio driver functions */
    41 static int DIB_OpenAudio(_THIS, SDL_AudioSpec *spec);
    42 static void DIB_ThreadInit(_THIS);
    43 static void DIB_WaitAudio(_THIS);
    44 static Uint8 *DIB_GetAudioBuf(_THIS);
    45 static void DIB_PlayAudio(_THIS);
    46 static void DIB_WaitDone(_THIS);
    47 static void DIB_CloseAudio(_THIS);
    48 
    49 /* Audio driver bootstrap functions */
    50 
    51 static int Audio_Available(void)
    52 {
    53 	return(1);
    54 }
    55 
    56 static void Audio_DeleteDevice(SDL_AudioDevice *device)
    57 {
    58 	free(device->hidden);
    59 	free(device);
    60 }
    61 
    62 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
    63 {
    64 	SDL_AudioDevice *this;
    65 
    66 	/* Initialize all variables that we clean on shutdown */
    67 	this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
    68 	if ( this ) {
    69 		memset(this, 0, (sizeof *this));
    70 		this->hidden = (struct SDL_PrivateAudioData *)
    71 				malloc((sizeof *this->hidden));
    72 	}
    73 	if ( (this == NULL) || (this->hidden == NULL) ) {
    74 		SDL_OutOfMemory();
    75 		if ( this ) {
    76 			free(this);
    77 		}
    78 		return(0);
    79 	}
    80 	memset(this->hidden, 0, (sizeof *this->hidden));
    81 
    82 	/* Set the function pointers */
    83 	this->OpenAudio = DIB_OpenAudio;
    84 	this->ThreadInit = DIB_ThreadInit;
    85 	this->WaitAudio = DIB_WaitAudio;
    86 	this->PlayAudio = DIB_PlayAudio;
    87 	this->GetAudioBuf = DIB_GetAudioBuf;
    88 	this->WaitDone = DIB_WaitDone;
    89 	this->CloseAudio = DIB_CloseAudio;
    90 
    91 	this->free = Audio_DeleteDevice;
    92 
    93 	return this;
    94 }
    95 
    96 AudioBootStrap WAVEOUT_bootstrap = {
    97 	"waveout", "Win95/98/NT/2000 WaveOut",
    98 	Audio_Available, Audio_CreateDevice
    99 };
   100 
   101 
   102 /* The Win32 callback for filling the WAVE device */
   103 static void CALLBACK FillSound(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance,
   104 						DWORD dwParam1, DWORD dwParam2)
   105 {
   106 	SDL_AudioDevice *this = (SDL_AudioDevice *)dwInstance;
   107 
   108 	/* Only service "buffer done playing" messages */
   109 	if ( uMsg != WOM_DONE )
   110 		return;
   111 
   112 	/* Signal that we are done playing a buffer */
   113 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
   114 	ReleaseSemaphoreCE(audio_sem, 1, NULL);
   115 #else
   116 	ReleaseSemaphore(audio_sem, 1, NULL);
   117 #endif
   118 }
   119 
   120 static void SetMMerror(char *function, MMRESULT code)
   121 {
   122 	int len;
   123 	char errbuf[MAXERRORLENGTH];
   124 #ifdef _WIN32_WCE
   125 	wchar_t werrbuf[MAXERRORLENGTH];
   126 #endif
   127 
   128 	snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function);
   129 	len = strlen(errbuf);
   130 
   131 #ifdef _WIN32_WCE
   132 	/* UNICODE version */
   133 	waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH-len);
   134 	WideCharToMultiByte(CP_ACP,0,werrbuf,-1,errbuf+len,MAXERRORLENGTH-len,NULL,NULL);
   135 #else
   136 	waveOutGetErrorText(code, errbuf+len, MAXERRORLENGTH-len);
   137 #endif
   138 
   139 	SDL_SetError("%s",errbuf);
   140 }
   141 
   142 /* Set high priority for the audio thread */
   143 static void DIB_ThreadInit(_THIS)
   144 {
   145 	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
   146 }
   147 
   148 void DIB_WaitAudio(_THIS)
   149 {
   150 	/* Wait for an audio chunk to finish */
   151 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
   152 	WaitForSemaphoreCE(audio_sem, INFINITE);
   153 #else
   154 	WaitForSingleObject(audio_sem, INFINITE);
   155 #endif
   156 }
   157 
   158 Uint8 *DIB_GetAudioBuf(_THIS)
   159 {
   160         Uint8 *retval;
   161 
   162 	retval = (Uint8 *)(wavebuf[next_buffer].lpData);
   163 	return retval;
   164 }
   165 
   166 void DIB_PlayAudio(_THIS)
   167 {
   168 	/* Queue it up */
   169 	waveOutWrite(sound, &wavebuf[next_buffer], sizeof(wavebuf[0]));
   170 	next_buffer = (next_buffer+1)%NUM_BUFFERS;
   171 }
   172 
   173 void DIB_WaitDone(_THIS)
   174 {
   175 	int i, left;
   176 
   177 	do {
   178 		left = NUM_BUFFERS;
   179 		for ( i=0; i<NUM_BUFFERS; ++i ) {
   180 			if ( wavebuf[i].dwFlags & WHDR_DONE ) {
   181 				--left;
   182 			}
   183 		}
   184 		if ( left > 0 ) {
   185 			SDL_Delay(100);
   186 		}
   187 	} while ( left > 0 );
   188 }
   189 
   190 void DIB_CloseAudio(_THIS)
   191 {
   192 	int i;
   193 
   194 	/* Close up audio */
   195 	if ( audio_sem ) {
   196 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
   197 		CloseSynchHandle(audio_sem);
   198 #else
   199 		CloseHandle(audio_sem);
   200 #endif
   201 	}
   202 	if ( sound ) {
   203 		waveOutClose(sound);
   204 	}
   205 
   206 	/* Clean up mixing buffers */
   207 	for ( i=0; i<NUM_BUFFERS; ++i ) {
   208 		if ( wavebuf[i].dwUser != 0xFFFF ) {
   209 			waveOutUnprepareHeader(sound, &wavebuf[i],
   210 						sizeof(wavebuf[i]));
   211 			wavebuf[i].dwUser = 0xFFFF;
   212 		}
   213 	}
   214 	/* Free raw mixing buffer */
   215 	if ( mixbuf != NULL ) {
   216 		free(mixbuf);
   217 		mixbuf = NULL;
   218 	}
   219 }
   220 
   221 int DIB_OpenAudio(_THIS, SDL_AudioSpec *spec)
   222 {
   223 	MMRESULT result;
   224 	int i;
   225 	WAVEFORMATEX waveformat;
   226 
   227 	/* Initialize the wavebuf structures for closing */
   228 	sound = NULL;
   229 	audio_sem = NULL;
   230 	for ( i = 0; i < NUM_BUFFERS; ++i )
   231 		wavebuf[i].dwUser = 0xFFFF;
   232 	mixbuf = NULL;
   233 
   234 	/* Set basic WAVE format parameters */
   235 	memset(&waveformat, 0, sizeof(waveformat));
   236 	waveformat.wFormatTag = WAVE_FORMAT_PCM;
   237 
   238 	/* Determine the audio parameters from the AudioSpec */
   239 	switch ( spec->format & 0xFF ) {
   240 		case 8:
   241 			/* Unsigned 8 bit audio data */
   242 			spec->format = AUDIO_U8;
   243 			waveformat.wBitsPerSample = 8;
   244 			break;
   245 		case 16:
   246 			/* Signed 16 bit audio data */
   247 			spec->format = AUDIO_S16;
   248 			waveformat.wBitsPerSample = 16;
   249 			break;
   250 		default:
   251 			SDL_SetError("Unsupported audio format");
   252 			return(-1);
   253 	}
   254 	waveformat.nChannels = spec->channels;
   255 	waveformat.nSamplesPerSec = spec->freq;
   256 	waveformat.nBlockAlign =
   257 		waveformat.nChannels * (waveformat.wBitsPerSample/8);
   258 	waveformat.nAvgBytesPerSec = 
   259 		waveformat.nSamplesPerSec * waveformat.nBlockAlign;
   260 
   261 	/* Check the buffer size -- minimum of 1/4 second (word aligned) */
   262 	if ( spec->samples < (spec->freq/4) )
   263 		spec->samples = ((spec->freq/4)+3)&~3;
   264 
   265 	/* Update the fragment size as size in bytes */
   266 	SDL_CalculateAudioSpec(spec);
   267 
   268 	/* Open the audio device */
   269 	result = waveOutOpen(&sound, WAVE_MAPPER, &waveformat,
   270 			(DWORD)FillSound, (DWORD)this, CALLBACK_FUNCTION);
   271 	if ( result != MMSYSERR_NOERROR ) {
   272 		SetMMerror("waveOutOpen()", result);
   273 		return(-1);
   274 	}
   275 
   276 #ifdef SOUND_DEBUG
   277 	/* Check the sound device we retrieved */
   278 	{
   279 		WAVEOUTCAPS caps;
   280 
   281 		result = waveOutGetDevCaps((UINT)sound, &caps, sizeof(caps));
   282 		if ( result != MMSYSERR_NOERROR ) {
   283 			SetMMerror("waveOutGetDevCaps()", result);
   284 			return(-1);
   285 		}
   286 		printf("Audio device: %s\n", caps.szPname);
   287 	}
   288 #endif
   289 
   290 	/* Create the audio buffer semaphore */
   291 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
   292 	audio_sem = CreateSemaphoreCE(NULL, NUM_BUFFERS-1, NUM_BUFFERS, NULL);
   293 #else
   294 	audio_sem = CreateSemaphore(NULL, NUM_BUFFERS-1, NUM_BUFFERS, NULL);
   295 #endif
   296 	if ( audio_sem == NULL ) {
   297 		SDL_SetError("Couldn't create semaphore");
   298 		return(-1);
   299 	}
   300 
   301 	/* Create the sound buffers */
   302 	mixbuf = (Uint8 *)malloc(NUM_BUFFERS*spec->size);
   303 	if ( mixbuf == NULL ) {
   304 		SDL_SetError("Out of memory");
   305 		return(-1);
   306 	}
   307 	for ( i = 0; i < NUM_BUFFERS; ++i ) {
   308 		memset(&wavebuf[i], 0, sizeof(wavebuf[i]));
   309 		wavebuf[i].lpData = (LPSTR) &mixbuf[i*spec->size];
   310 		wavebuf[i].dwBufferLength = spec->size;
   311 		wavebuf[i].dwFlags = WHDR_DONE;
   312 		result = waveOutPrepareHeader(sound, &wavebuf[i],
   313 							sizeof(wavebuf[i]));
   314 		if ( result != MMSYSERR_NOERROR ) {
   315 			SetMMerror("waveOutPrepareHeader()", result);
   316 			return(-1);
   317 		}
   318 	}
   319 
   320 	/* Ready to go! */
   321 	next_buffer = 0;
   322 	return(0);
   323 }