1190
+ − 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