1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2006 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
35typedef 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//---------------------------------------------------------------------
47LONG 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
76int 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
270void DART_ThreadInit(_THIS)
271{
272  return;
273}
274
275/* This function waits until it is possible to write a full sound buffer */
276void 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
295void 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
311Uint8 *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
340void 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
355void 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
391int Audio_Available(void)
392{
393  return(1);
394}
395
396void Audio_DeleteDevice(SDL_AudioDevice *device)
397{
398  SDL_free(device->hidden);
399  SDL_free(device);
400}
401
402SDL_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
437AudioBootStrap DART_bootstrap = {
438	"dart", "OS/2 Direct Audio RouTines (DART)",
439	Audio_Available, Audio_CreateDevice
440};
441
442