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