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