Mercurial > sdl-ios-xcode
comparison src/audio/dart/SDL_dart.c @ 1190:173c063d4f55
OS/2 port!
This was mostly, if not entirely, written by "Doodle" and "Caetano":
doodle@scenergy.dfmk.hu
daniel@caetano.eng.br
--ryan.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Wed, 23 Nov 2005 07:29:56 +0000 |
parents | |
children | c9b51268668f |
comparison
equal
deleted
inserted
replaced
1189:c96b326b90ba | 1190:173c063d4f55 |
---|---|
1 /* | |
2 SDL - Simple DirectMedia Layer | |
3 Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga | |
4 | |
5 This library is free software; you can redistribute it and/or | |
6 modify it under the terms of the GNU Library General Public | |
7 License as published by the Free Software Foundation; either | |
8 version 2 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 Library General Public License for more details. | |
14 | |
15 You should have received a copy of the GNU Library General Public | |
16 License along with this library; if not, write to the Free | |
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | |
19 Sam Lantinga | |
20 slouken@libsdl.org | |
21 */ | |
22 | |
23 #ifdef SAVE_RCSID | |
24 static char rcsid = | |
25 "@(#) $Id$"; | |
26 #endif | |
27 | |
28 /* Allow access to a raw mixing buffer */ | |
29 | |
30 #include <stdio.h> | |
31 #include <stdlib.h> | |
32 #include <string.h> | |
33 | |
34 #include "SDL_types.h" | |
35 #include "SDL_error.h" | |
36 #include "SDL_timer.h" | |
37 #include "SDL_audio.h" | |
38 #include "SDL_audio_c.h" | |
39 #include "SDL_dart.h" | |
40 | |
41 // Buffer states: | |
42 #define BUFFER_EMPTY 0 | |
43 #define BUFFER_USED 1 | |
44 | |
45 typedef struct _tMixBufferDesc { | |
46 int iBufferUsage; // BUFFER_EMPTY or BUFFER_USED | |
47 SDL_AudioDevice *pSDLAudioDevice; | |
48 } tMixBufferDesc, *pMixBufferDesc; | |
49 | |
50 | |
51 //--------------------------------------------------------------------- | |
52 // DARTEventFunc | |
53 // | |
54 // This function is called by DART, when an event occures, like end of | |
55 // playback of a buffer, etc... | |
56 //--------------------------------------------------------------------- | |
57 LONG APIENTRY DARTEventFunc(ULONG ulStatus, | |
58 PMCI_MIX_BUFFER pBuffer, | |
59 ULONG ulFlags) | |
60 { | |
61 if (ulFlags && MIX_WRITE_COMPLETE) | |
62 { // Playback of buffer completed! | |
63 | |
64 // Get pointer to buffer description | |
65 pMixBufferDesc pBufDesc; | |
66 | |
67 if (pBuffer) | |
68 { | |
69 pBufDesc = (pMixBufferDesc) (*pBuffer).ulUserParm; | |
70 | |
71 if (pBufDesc) | |
72 { | |
73 SDL_AudioDevice *pSDLAudioDevice = pBufDesc->pSDLAudioDevice; | |
74 // Set the buffer to be empty | |
75 pBufDesc->iBufferUsage = BUFFER_EMPTY; | |
76 // And notify DART feeder thread that it will have to work a bit. | |
77 if (pSDLAudioDevice) | |
78 DosPostEventSem(pSDLAudioDevice->hidden->hevAudioBufferPlayed); | |
79 } | |
80 } | |
81 } | |
82 return TRUE; | |
83 } | |
84 | |
85 | |
86 int DART_OpenAudio(_THIS, SDL_AudioSpec *spec) | |
87 { | |
88 MCI_AMP_OPEN_PARMS AmpOpenParms; | |
89 MCI_GENERIC_PARMS GenericParms; | |
90 int iDeviceOrd = 0; // Default device to be used | |
91 int bOpenShared = 1; // Try opening it shared | |
92 int iBits = 16; // Default is 16 bits signed | |
93 int iFreq = 44100; // Default is 44KHz | |
94 int iChannels = 2; // Default is 2 channels (Stereo) | |
95 int iNumBufs = 2; // Number of audio buffers: 2 | |
96 int iBufSize; | |
97 int iOpenMode; | |
98 int iSilence; | |
99 int rc; | |
100 | |
101 // First thing is to try to open a given DART device! | |
102 memset(&AmpOpenParms, 0, sizeof(MCI_AMP_OPEN_PARMS)); | |
103 // pszDeviceType should contain the device type in low word, and device ordinal in high word! | |
104 AmpOpenParms.pszDeviceType = (PSZ) (MCI_DEVTYPE_AUDIO_AMPMIX | (iDeviceOrd << 16)); | |
105 | |
106 iOpenMode = MCI_WAIT | MCI_OPEN_TYPE_ID; | |
107 if (bOpenShared) iOpenMode |= MCI_OPEN_SHAREABLE; | |
108 | |
109 rc = mciSendCommand( 0, MCI_OPEN, | |
110 iOpenMode, | |
111 (PVOID) &AmpOpenParms, 0); | |
112 if (rc!=MCIERR_SUCCESS) // No audio available?? | |
113 return (-1); | |
114 // Save the device ID we got from DART! | |
115 // We will use this in the next calls! | |
116 iDeviceOrd = AmpOpenParms.usDeviceID; | |
117 | |
118 // Determine the audio parameters from the AudioSpec | |
119 switch ( spec->format & 0xFF ) | |
120 { | |
121 case 8: | |
122 /* Unsigned 8 bit audio data */ | |
123 spec->format = AUDIO_U8; | |
124 iSilence = 0x80; | |
125 iBits = 8; | |
126 break; | |
127 case 16: | |
128 /* Signed 16 bit audio data */ | |
129 spec->format = AUDIO_S16; | |
130 iSilence = 0x00; | |
131 iBits = 16; | |
132 break; | |
133 default: | |
134 // Close DART, and exit with error code! | |
135 mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0); | |
136 SDL_SetError("Unsupported audio format"); | |
137 return(-1); | |
138 } | |
139 iFreq = spec->freq; | |
140 iChannels = spec->channels; | |
141 /* Update the fragment size as size in bytes */ | |
142 SDL_CalculateAudioSpec(spec); | |
143 iBufSize = spec->size; | |
144 | |
145 // Now query this device if it supports the given freq/bits/channels! | |
146 memset(&(_this->hidden->MixSetupParms), 0, sizeof(MCI_MIXSETUP_PARMS)); | |
147 _this->hidden->MixSetupParms.ulBitsPerSample = iBits; | |
148 _this->hidden->MixSetupParms.ulFormatTag = MCI_WAVE_FORMAT_PCM; | |
149 _this->hidden->MixSetupParms.ulSamplesPerSec = iFreq; | |
150 _this->hidden->MixSetupParms.ulChannels = iChannels; | |
151 _this->hidden->MixSetupParms.ulFormatMode = MCI_PLAY; | |
152 _this->hidden->MixSetupParms.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO; | |
153 _this->hidden->MixSetupParms.pmixEvent = DARTEventFunc; | |
154 rc = mciSendCommand (iDeviceOrd, MCI_MIXSETUP, | |
155 MCI_WAIT | MCI_MIXSETUP_QUERYMODE, | |
156 &(_this->hidden->MixSetupParms), 0); | |
157 if (rc!=MCIERR_SUCCESS) | |
158 { // The device cannot handle this format! | |
159 // Close DART, and exit with error code! | |
160 mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0); | |
161 SDL_SetError("Audio device doesn't support requested audio format"); | |
162 return(-1); | |
163 } | |
164 // The device can handle this format, so initialize! | |
165 rc = mciSendCommand(iDeviceOrd, MCI_MIXSETUP, | |
166 MCI_WAIT | MCI_MIXSETUP_INIT, | |
167 &(_this->hidden->MixSetupParms), 0); | |
168 if (rc!=MCIERR_SUCCESS) | |
169 { // The device could not be opened! | |
170 // Close DART, and exit with error code! | |
171 mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0); | |
172 SDL_SetError("Audio device could not be set up"); | |
173 return(-1); | |
174 } | |
175 // Ok, the device is initialized. | |
176 // Now we should allocate buffers. For this, we need a place where | |
177 // the buffer descriptors will be: | |
178 _this->hidden->pMixBuffers = (MCI_MIX_BUFFER *) malloc(sizeof(MCI_MIX_BUFFER)*iNumBufs); | |
179 if (!(_this->hidden->pMixBuffers)) | |
180 { // Not enough memory! | |
181 // Close DART, and exit with error code! | |
182 mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0); | |
183 SDL_SetError("Not enough memory for audio buffer descriptors"); | |
184 return(-1); | |
185 } | |
186 // Now that we have the place for buffer list, we can ask DART for the | |
187 // buffers! | |
188 _this->hidden->BufferParms.ulNumBuffers = iNumBufs; // Number of buffers | |
189 _this->hidden->BufferParms.ulBufferSize = iBufSize; // each with this size | |
190 _this->hidden->BufferParms.pBufList = _this->hidden->pMixBuffers; // getting descriptorts into this list | |
191 // Allocate buffers! | |
192 rc = mciSendCommand(iDeviceOrd, MCI_BUFFER, | |
193 MCI_WAIT | MCI_ALLOCATE_MEMORY, | |
194 &(_this->hidden->BufferParms), 0); | |
195 if ((rc!=MCIERR_SUCCESS) || (iNumBufs != _this->hidden->BufferParms.ulNumBuffers) || (_this->hidden->BufferParms.ulBufferSize==0)) | |
196 { // Could not allocate memory! | |
197 // Close DART, and exit with error code! | |
198 free(_this->hidden->pMixBuffers); _this->hidden->pMixBuffers = NULL; | |
199 mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0); | |
200 SDL_SetError("DART could not allocate buffers"); | |
201 return(-1); | |
202 } | |
203 // Ok, we have all the buffers allocated, let's mark them! | |
204 { | |
205 int i; | |
206 for (i=0; i<iNumBufs; i++) | |
207 { | |
208 pMixBufferDesc pBufferDesc = (pMixBufferDesc) malloc(sizeof(tMixBufferDesc));; | |
209 // Check if this buffer was really allocated by DART | |
210 if ((!(_this->hidden->pMixBuffers[i].pBuffer)) || (!pBufferDesc)) | |
211 { // Wrong buffer! | |
212 // Close DART, and exit with error code! | |
213 // Free buffer descriptions | |
214 { int j; | |
215 for (j=0; j<i; j++) free((void *)(_this->hidden->pMixBuffers[j].ulUserParm)); | |
216 } | |
217 // and cleanup | |
218 mciSendCommand(iDeviceOrd, MCI_BUFFER, MCI_WAIT | MCI_DEALLOCATE_MEMORY, &(_this->hidden->BufferParms), 0); | |
219 free(_this->hidden->pMixBuffers); _this->hidden->pMixBuffers = NULL; | |
220 mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0); | |
221 SDL_SetError("Error at internal buffer check"); | |
222 return(-1); | |
223 } | |
224 pBufferDesc->iBufferUsage = BUFFER_EMPTY; | |
225 pBufferDesc->pSDLAudioDevice = _this; | |
226 | |
227 _this->hidden->pMixBuffers[i].ulBufferLength = _this->hidden->BufferParms.ulBufferSize; | |
228 _this->hidden->pMixBuffers[i].ulUserParm = (ULONG) pBufferDesc; // User parameter: Description of buffer | |
229 _this->hidden->pMixBuffers[i].ulFlags = 0; // Some stuff should be flagged here for DART, like end of | |
230 // audio data, but as we will continously send | |
231 // audio data, there will be no end.:) | |
232 memset(_this->hidden->pMixBuffers[i].pBuffer, iSilence, iBufSize); | |
233 } | |
234 } | |
235 _this->hidden->iNextFreeBuffer = 0; | |
236 _this->hidden->iLastPlayedBuf = -1; | |
237 // Create event semaphore | |
238 if (DosCreateEventSem(NULL, &(_this->hidden->hevAudioBufferPlayed), 0, FALSE)!=NO_ERROR) | |
239 { | |
240 // Could not create event semaphore! | |
241 { | |
242 int i; | |
243 for (i=0; i<iNumBufs; i++) free((void *)(_this->hidden->pMixBuffers[i].ulUserParm)); | |
244 } | |
245 mciSendCommand(iDeviceOrd, MCI_BUFFER, MCI_WAIT | MCI_DEALLOCATE_MEMORY, &(_this->hidden->BufferParms), 0); | |
246 free(_this->hidden->pMixBuffers); _this->hidden->pMixBuffers = NULL; | |
247 mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0); | |
248 SDL_SetError("Could not create event semaphore"); | |
249 return(-1); | |
250 } | |
251 | |
252 // Store the new settings in global variables | |
253 _this->hidden->iCurrDeviceOrd = iDeviceOrd; | |
254 _this->hidden->iCurrFreq = iFreq; | |
255 _this->hidden->iCurrBits = iBits; | |
256 _this->hidden->iCurrChannels = iChannels; | |
257 _this->hidden->iCurrNumBufs = iNumBufs; | |
258 _this->hidden->iCurrBufSize = iBufSize; | |
259 | |
260 return (0); | |
261 } | |
262 | |
263 | |
264 | |
265 void DART_ThreadInit(_THIS) | |
266 { | |
267 return; | |
268 } | |
269 | |
270 /* This function waits until it is possible to write a full sound buffer */ | |
271 void DART_WaitAudio(_THIS) | |
272 { | |
273 int i; | |
274 pMixBufferDesc pBufDesc; | |
275 ULONG ulPostCount; | |
276 | |
277 DosResetEventSem(_this->hidden->hevAudioBufferPlayed, &ulPostCount); | |
278 // If there is already an empty buffer, then return now! | |
279 for (i=0; i<_this->hidden->iCurrNumBufs; i++) | |
280 { | |
281 pBufDesc = (pMixBufferDesc) _this->hidden->pMixBuffers[i].ulUserParm; | |
282 if (pBufDesc->iBufferUsage == BUFFER_EMPTY) | |
283 return; | |
284 } | |
285 // If there is no empty buffer, wait for one to be empty! | |
286 DosWaitEventSem(_this->hidden->hevAudioBufferPlayed, 1000); // Wait max 1 sec!!! Important! | |
287 return; | |
288 } | |
289 | |
290 void DART_PlayAudio(_THIS) | |
291 { | |
292 int iFreeBuf = _this->hidden->iNextFreeBuffer; | |
293 pMixBufferDesc pBufDesc; | |
294 | |
295 pBufDesc = (pMixBufferDesc) _this->hidden->pMixBuffers[iFreeBuf].ulUserParm; | |
296 pBufDesc->iBufferUsage = BUFFER_USED; | |
297 // Send it to DART to be queued | |
298 _this->hidden->MixSetupParms.pmixWrite(_this->hidden->MixSetupParms.ulMixHandle, | |
299 &(_this->hidden->pMixBuffers[iFreeBuf]), 1); | |
300 | |
301 _this->hidden->iLastPlayedBuf = iFreeBuf; | |
302 iFreeBuf = (iFreeBuf+1) % _this->hidden->iCurrNumBufs; | |
303 _this->hidden->iNextFreeBuffer = iFreeBuf; | |
304 } | |
305 | |
306 Uint8 *DART_GetAudioBuf(_THIS) | |
307 { | |
308 int iFreeBuf; | |
309 Uint8 *pResult; | |
310 pMixBufferDesc pBufDesc; | |
311 | |
312 if (_this) | |
313 { | |
314 if (_this->hidden) | |
315 { | |
316 iFreeBuf = _this->hidden->iNextFreeBuffer; | |
317 pBufDesc = (pMixBufferDesc) _this->hidden->pMixBuffers[iFreeBuf].ulUserParm; | |
318 | |
319 if (pBufDesc) | |
320 { | |
321 if (pBufDesc->iBufferUsage == BUFFER_EMPTY) | |
322 { | |
323 pResult = _this->hidden->pMixBuffers[iFreeBuf].pBuffer; | |
324 return pResult; | |
325 } | |
326 } else | |
327 printf("[DART_GetAudioBuf] : ERROR! pBufDesc = %p\n", pBufDesc); | |
328 } else | |
329 printf("[DART_GetAudioBuf] : ERROR! _this->hidden = %p\n", _this->hidden); | |
330 } else | |
331 printf("[DART_GetAudioBuf] : ERROR! _this = %p\n", _this); | |
332 return NULL; | |
333 } | |
334 | |
335 void DART_WaitDone(_THIS) | |
336 { | |
337 pMixBufferDesc pBufDesc; | |
338 ULONG ulPostCount; | |
339 APIRET rc; | |
340 | |
341 pBufDesc = (pMixBufferDesc) _this->hidden->pMixBuffers[_this->hidden->iLastPlayedBuf].ulUserParm; | |
342 rc = NO_ERROR; | |
343 while ((pBufDesc->iBufferUsage != BUFFER_EMPTY) && (rc==NO_ERROR)) | |
344 { | |
345 DosResetEventSem(_this->hidden->hevAudioBufferPlayed, &ulPostCount); | |
346 rc = DosWaitEventSem(_this->hidden->hevAudioBufferPlayed, 1000); // 1 sec timeout! Important! | |
347 } | |
348 } | |
349 | |
350 void DART_CloseAudio(_THIS) | |
351 { | |
352 MCI_GENERIC_PARMS GenericParms; | |
353 int rc; | |
354 | |
355 // Stop DART playback | |
356 rc = mciSendCommand(_this->hidden->iCurrDeviceOrd, MCI_STOP, MCI_WAIT, &GenericParms, 0); | |
357 if (rc!=MCIERR_SUCCESS) | |
358 { | |
359 #ifdef SFX_DEBUG_BUILD | |
360 printf("Could not stop DART playback!\n"); | |
361 fflush(stdout); | |
362 #endif | |
363 } | |
364 | |
365 // Close event semaphore | |
366 DosCloseEventSem(_this->hidden->hevAudioBufferPlayed); | |
367 | |
368 // Free memory of buffer descriptions | |
369 { | |
370 int i; | |
371 for (i=0; i<_this->hidden->iCurrNumBufs; i++) free((void *)(_this->hidden->pMixBuffers[i].ulUserParm)); | |
372 } | |
373 | |
374 // Deallocate buffers | |
375 rc = mciSendCommand(_this->hidden->iCurrDeviceOrd, MCI_BUFFER, MCI_WAIT | MCI_DEALLOCATE_MEMORY, &(_this->hidden->BufferParms), 0); | |
376 | |
377 // Free bufferlist | |
378 free(_this->hidden->pMixBuffers); _this->hidden->pMixBuffers = NULL; | |
379 | |
380 // Close dart | |
381 rc = mciSendCommand(_this->hidden->iCurrDeviceOrd, MCI_CLOSE, MCI_WAIT, &(GenericParms), 0); | |
382 } | |
383 | |
384 /* Audio driver bootstrap functions */ | |
385 | |
386 int Audio_Available(void) | |
387 { | |
388 return(1); | |
389 } | |
390 | |
391 void Audio_DeleteDevice(SDL_AudioDevice *device) | |
392 { | |
393 free(device->hidden); | |
394 free(device); | |
395 } | |
396 | |
397 SDL_AudioDevice *Audio_CreateDevice(int devindex) | |
398 { | |
399 SDL_AudioDevice *this; | |
400 | |
401 /* Initialize all variables that we clean on shutdown */ | |
402 this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice)); | |
403 if ( this ) | |
404 { | |
405 memset(this, 0, (sizeof *this)); | |
406 this->hidden = (struct SDL_PrivateAudioData *) | |
407 malloc((sizeof *this->hidden)); | |
408 } | |
409 if ( (this == NULL) || (this->hidden == NULL) ) | |
410 { | |
411 SDL_OutOfMemory(); | |
412 if ( this ) | |
413 free(this); | |
414 return(0); | |
415 } | |
416 memset(this->hidden, 0, (sizeof *this->hidden)); | |
417 | |
418 /* Set the function pointers */ | |
419 this->OpenAudio = DART_OpenAudio; | |
420 this->ThreadInit = DART_ThreadInit; | |
421 this->WaitAudio = DART_WaitAudio; | |
422 this->PlayAudio = DART_PlayAudio; | |
423 this->GetAudioBuf = DART_GetAudioBuf; | |
424 this->WaitDone = DART_WaitDone; | |
425 this->CloseAudio = DART_CloseAudio; | |
426 | |
427 this->free = Audio_DeleteDevice; | |
428 | |
429 return this; | |
430 } | |
431 | |
432 AudioBootStrap DART_bootstrap = { | |
433 "dart", "OS/2 Direct Audio RouTines (DART)", | |
434 Audio_Available, Audio_CreateDevice | |
435 }; | |
436 |