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