symbian-qemu-0.9.1-12/libsdl-trunk/src/audio/macrom/SDL_romaudio.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     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 #if defined(__APPLE__) && defined(__MACH__)
       
    25 #  include <Carbon/Carbon.h>
       
    26 #elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335)
       
    27 #  include <Carbon.h>
       
    28 #else
       
    29 #  include <Sound.h> /* SoundManager interface */
       
    30 #  include <Gestalt.h>
       
    31 #  include <DriverServices.h>
       
    32 #endif
       
    33 
       
    34 #if !defined(NewSndCallBackUPP) && (UNIVERSAL_INTERFACES_VERSION < 0x0335)
       
    35 #if !defined(NewSndCallBackProc) /* avoid circular redefinition... */
       
    36 #define NewSndCallBackUPP NewSndCallBackProc
       
    37 #endif
       
    38 #if !defined(NewSndCallBackUPP)
       
    39 #define NewSndCallBackUPP NewSndCallBackProc
       
    40 #endif
       
    41 #endif
       
    42 
       
    43 #include "SDL_audio.h"
       
    44 #include "../SDL_audio_c.h"
       
    45 #include "../SDL_sysaudio.h"
       
    46 #include "SDL_romaudio.h"
       
    47 
       
    48 /* Audio driver functions */
       
    49 
       
    50 static void Mac_CloseAudio(_THIS);
       
    51 static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec);
       
    52 static void Mac_LockAudio(_THIS);
       
    53 static void Mac_UnlockAudio(_THIS);
       
    54 
       
    55 /* Audio driver bootstrap functions */
       
    56 
       
    57 
       
    58 static int Audio_Available(void)
       
    59 {
       
    60     return(1);
       
    61 }
       
    62 
       
    63 static void Audio_DeleteDevice(SDL_AudioDevice *device)
       
    64 {
       
    65     SDL_free(device->hidden);
       
    66     SDL_free(device);
       
    67 }
       
    68 
       
    69 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
       
    70 {
       
    71     SDL_AudioDevice *this;
       
    72 
       
    73     /* Initialize all variables that we clean on shutdown */
       
    74     this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
       
    75     if ( this ) {
       
    76         SDL_memset(this, 0, (sizeof *this));
       
    77         this->hidden = (struct SDL_PrivateAudioData *)
       
    78                 SDL_malloc((sizeof *this->hidden));
       
    79     }
       
    80     if ( (this == NULL) || (this->hidden == NULL) ) {
       
    81         SDL_OutOfMemory();
       
    82         if ( this ) {
       
    83             SDL_free(this);
       
    84         }
       
    85         return(0);
       
    86     }
       
    87     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
       
    88 
       
    89     /* Set the function pointers */
       
    90     this->OpenAudio   = Mac_OpenAudio;
       
    91     this->CloseAudio  = Mac_CloseAudio;
       
    92     this->LockAudio   = Mac_LockAudio;
       
    93     this->UnlockAudio = Mac_UnlockAudio;
       
    94     this->free        = Audio_DeleteDevice;
       
    95 
       
    96 #ifdef __MACOSX__	/* Mac OS X uses threaded audio, so normal thread code is okay */
       
    97     this->LockAudio   = NULL;
       
    98     this->UnlockAudio = NULL;
       
    99 #endif
       
   100     return this;
       
   101 }
       
   102 
       
   103 AudioBootStrap SNDMGR_bootstrap = {
       
   104 	"sndmgr", "MacOS SoundManager 3.0",
       
   105 	Audio_Available, Audio_CreateDevice
       
   106 };
       
   107 
       
   108 #if defined(TARGET_API_MAC_CARBON) || defined(USE_RYANS_SOUNDCODE)
       
   109 /* This works correctly on Mac OS X */
       
   110 
       
   111 #pragma options align=power
       
   112 
       
   113 static volatile SInt32 audio_is_locked = 0;
       
   114 static volatile SInt32 need_to_mix = 0;
       
   115 
       
   116 static UInt8  *buffer[2];
       
   117 static volatile UInt32 running = 0;
       
   118 static CmpSoundHeader header;
       
   119 static volatile Uint32 fill_me = 0;
       
   120 
       
   121 static void mix_buffer(SDL_AudioDevice *audio, UInt8 *buffer)
       
   122 {
       
   123    if ( ! audio->paused ) {
       
   124 #ifdef __MACOSX__
       
   125         SDL_mutexP(audio->mixer_lock);
       
   126 #endif
       
   127         if ( audio->convert.needed ) {
       
   128             audio->spec.callback(audio->spec.userdata,
       
   129                     (Uint8 *)audio->convert.buf,audio->convert.len);
       
   130             SDL_ConvertAudio(&audio->convert);
       
   131             if ( audio->convert.len_cvt != audio->spec.size ) {
       
   132                 /* Uh oh... probably crashes here */;
       
   133             }
       
   134             SDL_memcpy(buffer, audio->convert.buf, audio->convert.len_cvt);
       
   135         } else {
       
   136             audio->spec.callback(audio->spec.userdata, buffer, audio->spec.size);
       
   137         }
       
   138 #ifdef __MACOSX__
       
   139         SDL_mutexV(audio->mixer_lock);
       
   140 #endif
       
   141     }
       
   142 
       
   143     DecrementAtomic((SInt32 *) &need_to_mix);
       
   144 }
       
   145 
       
   146 static void Mac_LockAudio(_THIS)
       
   147 {
       
   148     IncrementAtomic((SInt32 *) &audio_is_locked);
       
   149 }
       
   150 
       
   151 static void Mac_UnlockAudio(_THIS)
       
   152 {
       
   153     SInt32 oldval;
       
   154          
       
   155     oldval = DecrementAtomic((SInt32 *) &audio_is_locked);
       
   156     if ( oldval != 1 )  /* != 1 means audio is still locked. */
       
   157         return;
       
   158 
       
   159     /* Did we miss the chance to mix in an interrupt? Do it now. */
       
   160     if ( BitAndAtomic (0xFFFFFFFF, (UInt32 *) &need_to_mix) ) {
       
   161         /*
       
   162          * Note that this could be a problem if you missed an interrupt
       
   163          *  while the audio was locked, and get preempted by a second
       
   164          *  interrupt here, but that means you locked for way too long anyhow.
       
   165          */
       
   166         mix_buffer (this, buffer[fill_me]);
       
   167     }
       
   168 }
       
   169 
       
   170 static void callBackProc (SndChannel *chan, SndCommand *cmd_passed ) {
       
   171    UInt32 play_me;
       
   172    SndCommand cmd; 
       
   173    SDL_AudioDevice *audio = (SDL_AudioDevice *)chan->userInfo;
       
   174 
       
   175    IncrementAtomic((SInt32 *) &need_to_mix);
       
   176 
       
   177    fill_me = cmd_passed->param2;  /* buffer that has just finished playing, so fill it */      
       
   178    play_me = ! fill_me;           /* filled buffer to play _now_ */
       
   179 
       
   180    if ( ! audio->enabled ) {
       
   181       return;
       
   182    }
       
   183    
       
   184    /* queue previously mixed buffer for playback. */
       
   185    header.samplePtr = (Ptr)buffer[play_me];
       
   186    cmd.cmd = bufferCmd;
       
   187    cmd.param1 = 0; 
       
   188    cmd.param2 = (long)&header;
       
   189    SndDoCommand (chan, &cmd, 0);
       
   190 
       
   191    memset (buffer[fill_me], 0, audio->spec.size);
       
   192 
       
   193    /*
       
   194     * if audio device isn't locked, mix the next buffer to be queued in
       
   195     *  the memory block that just finished playing.
       
   196     */
       
   197    if ( ! BitAndAtomic(0xFFFFFFFF, (UInt32 *) &audio_is_locked) ) {
       
   198       mix_buffer (audio, buffer[fill_me]);
       
   199    } 
       
   200 
       
   201    /* set this callback to run again when current buffer drains. */
       
   202    if ( running ) {
       
   203       cmd.cmd = callBackCmd;
       
   204       cmd.param1 = 0;
       
   205       cmd.param2 = play_me;
       
   206    
       
   207       SndDoCommand (chan, &cmd, 0);
       
   208    }
       
   209 }
       
   210 
       
   211 static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec) {
       
   212 
       
   213    SndCallBackUPP callback;
       
   214    int sample_bits;
       
   215    int i;
       
   216    long initOptions;
       
   217       
       
   218    /* Very few conversions are required, but... */
       
   219     switch (spec->format) {
       
   220         case AUDIO_S8:
       
   221         spec->format = AUDIO_U8;
       
   222         break;
       
   223         case AUDIO_U16LSB:
       
   224         spec->format = AUDIO_S16LSB;
       
   225         break;
       
   226         case AUDIO_U16MSB:
       
   227         spec->format = AUDIO_S16MSB;
       
   228         break;
       
   229     }
       
   230     SDL_CalculateAudioSpec(spec);
       
   231     
       
   232     /* initialize bufferCmd header */
       
   233     memset (&header, 0, sizeof(header));
       
   234     callback = (SndCallBackUPP) NewSndCallBackUPP (callBackProc);
       
   235     sample_bits = spec->size / spec->samples / spec->channels * 8;
       
   236 
       
   237 #ifdef DEBUG_AUDIO
       
   238     fprintf(stderr,
       
   239 	"Audio format 0x%x, channels = %d, sample_bits = %d, frequency = %d\n",
       
   240 	spec->format, spec->channels, sample_bits, spec->freq);
       
   241 #endif /* DEBUG_AUDIO */
       
   242     
       
   243     header.numChannels = spec->channels;
       
   244     header.sampleSize  = sample_bits;
       
   245     header.sampleRate  = spec->freq << 16;
       
   246     header.numFrames   = spec->samples;
       
   247     header.encode      = cmpSH;
       
   248     
       
   249     /* Note that we install the 16bitLittleEndian Converter if needed. */
       
   250     if ( spec->format == 0x8010 ) {
       
   251         header.compressionID = fixedCompression;
       
   252         header.format = k16BitLittleEndianFormat;
       
   253     }
       
   254     
       
   255     /* allocate 2 buffers */
       
   256     for (i=0; i<2; i++) {
       
   257        buffer[i] = (UInt8*)malloc (sizeof(UInt8) * spec->size);
       
   258       if (buffer[i] == NULL) {
       
   259          SDL_OutOfMemory();
       
   260          return (-1);
       
   261       }
       
   262      memset (buffer[i], 0, spec->size);
       
   263    }
       
   264    
       
   265    /* Create the sound manager channel */
       
   266     channel = (SndChannelPtr)SDL_malloc(sizeof(*channel));
       
   267     if ( channel == NULL ) {
       
   268         SDL_OutOfMemory();
       
   269         return(-1);
       
   270     }
       
   271     if ( spec->channels >= 2 ) {
       
   272         initOptions = initStereo;
       
   273     } else {
       
   274         initOptions = initMono;
       
   275     }
       
   276     channel->userInfo = (long)this;
       
   277     channel->qLength = 128;
       
   278     if ( SndNewChannel(&channel, sampledSynth, initOptions, callback) != noErr ) {
       
   279         SDL_SetError("Unable to create audio channel");
       
   280         SDL_free(channel);
       
   281         channel = NULL;
       
   282         return(-1);
       
   283     }
       
   284    
       
   285    /* start playback */
       
   286    {
       
   287       SndCommand cmd;
       
   288       cmd.cmd = callBackCmd;
       
   289       cmd.param2 = 0;
       
   290       running = 1;
       
   291       SndDoCommand (channel, &cmd, 0);
       
   292    }
       
   293    
       
   294    return 1;
       
   295 }
       
   296 
       
   297 static void Mac_CloseAudio(_THIS) {
       
   298    
       
   299    int i;
       
   300    
       
   301    running = 0;
       
   302    
       
   303    if (channel) {
       
   304       SndDisposeChannel (channel, true);
       
   305       channel = NULL;
       
   306    }
       
   307    
       
   308     for ( i=0; i<2; ++i ) {
       
   309         if ( buffer[i] ) {
       
   310             SDL_free(buffer[i]);
       
   311             buffer[i] = NULL;
       
   312         }
       
   313     }
       
   314 }
       
   315 
       
   316 #else /* !TARGET_API_MAC_CARBON && !USE_RYANS_SOUNDCODE */
       
   317 
       
   318 static void Mac_LockAudio(_THIS)
       
   319 {
       
   320     /* no-op. */
       
   321 }
       
   322 
       
   323 static void Mac_UnlockAudio(_THIS)
       
   324 {
       
   325     /* no-op. */
       
   326 }
       
   327 
       
   328 
       
   329 /* This function is called by Sound Manager when it has exhausted one of
       
   330    the buffers, so we'll zero it to silence and fill it with audio if
       
   331    we're not paused.
       
   332 */
       
   333 static pascal
       
   334 void sndDoubleBackProc (SndChannelPtr chan, SndDoubleBufferPtr newbuf)
       
   335 {
       
   336     SDL_AudioDevice *audio = (SDL_AudioDevice *)newbuf->dbUserInfo[0];
       
   337 
       
   338     /* If audio is quitting, don't do anything */
       
   339     if ( ! audio->enabled ) {
       
   340         return;
       
   341     }
       
   342     memset (newbuf->dbSoundData, 0, audio->spec.size);
       
   343     newbuf->dbNumFrames = audio->spec.samples;
       
   344     if ( ! audio->paused ) {
       
   345         if ( audio->convert.needed ) {
       
   346             audio->spec.callback(audio->spec.userdata,
       
   347                 (Uint8 *)audio->convert.buf,audio->convert.len);
       
   348             SDL_ConvertAudio(&audio->convert);
       
   349 #if 0
       
   350             if ( audio->convert.len_cvt != audio->spec.size ) {
       
   351                 /* Uh oh... probably crashes here */;
       
   352             }
       
   353 #endif
       
   354             SDL_memcpy(newbuf->dbSoundData, audio->convert.buf,
       
   355                             audio->convert.len_cvt);
       
   356         } else {
       
   357             audio->spec.callback(audio->spec.userdata,
       
   358                 (Uint8 *)newbuf->dbSoundData, audio->spec.size);
       
   359         }
       
   360     }
       
   361     newbuf->dbFlags    |= dbBufferReady;
       
   362 }
       
   363 
       
   364 static int DoubleBufferAudio_Available(void)
       
   365 {
       
   366     int available;
       
   367     NumVersion sndversion;
       
   368     long response;
       
   369 
       
   370     available = 0;
       
   371     sndversion = SndSoundManagerVersion();
       
   372     if ( sndversion.majorRev >= 3 ) {
       
   373         if ( Gestalt(gestaltSoundAttr, &response) == noErr ) {
       
   374             if ( (response & (1 << gestaltSndPlayDoubleBuffer)) ) {
       
   375                 available = 1;
       
   376             }
       
   377         }
       
   378     } else {
       
   379         if ( Gestalt(gestaltSoundAttr, &response) == noErr ) {
       
   380             if ( (response & (1 << gestaltHasASC)) ) {
       
   381                 available = 1;
       
   382             }
       
   383         }
       
   384     }
       
   385     return(available);
       
   386 }
       
   387 
       
   388 static void Mac_CloseAudio(_THIS)
       
   389 {
       
   390     int i;
       
   391 
       
   392     if ( channel != NULL ) {
       
   393         /* Clean up the audio channel */
       
   394         SndDisposeChannel(channel, true);
       
   395         channel = NULL;
       
   396     }
       
   397     for ( i=0; i<2; ++i ) {
       
   398         if ( audio_buf[i] ) {
       
   399             SDL_free(audio_buf[i]);
       
   400             audio_buf[i] = NULL;
       
   401         }
       
   402     }
       
   403 }
       
   404 
       
   405 static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec)
       
   406 {
       
   407     SndDoubleBufferHeader2 audio_dbh;
       
   408     int i;
       
   409     long initOptions;
       
   410     int sample_bits;
       
   411     SndDoubleBackUPP doubleBackProc;
       
   412 
       
   413     /* Check to make sure double-buffered audio is available */
       
   414     if ( ! DoubleBufferAudio_Available() ) {
       
   415         SDL_SetError("Sound manager doesn't support double-buffering");
       
   416         return(-1);
       
   417     }
       
   418 
       
   419     /* Very few conversions are required, but... */
       
   420     switch (spec->format) {
       
   421         case AUDIO_S8:
       
   422         spec->format = AUDIO_U8;
       
   423         break;
       
   424         case AUDIO_U16LSB:
       
   425         spec->format = AUDIO_S16LSB;
       
   426         break;
       
   427         case AUDIO_U16MSB:
       
   428         spec->format = AUDIO_S16MSB;
       
   429         break;
       
   430     }
       
   431     SDL_CalculateAudioSpec(spec);
       
   432 
       
   433     /* initialize the double-back header */
       
   434     SDL_memset(&audio_dbh, 0, sizeof(audio_dbh));
       
   435     doubleBackProc = NewSndDoubleBackProc (sndDoubleBackProc);
       
   436     sample_bits = spec->size / spec->samples / spec->channels * 8;
       
   437     
       
   438     audio_dbh.dbhNumChannels = spec->channels;
       
   439     audio_dbh.dbhSampleSize    = sample_bits;
       
   440     audio_dbh.dbhCompressionID = 0;
       
   441     audio_dbh.dbhPacketSize    = 0;
       
   442     audio_dbh.dbhSampleRate    = spec->freq << 16;
       
   443     audio_dbh.dbhDoubleBack    = doubleBackProc;
       
   444     audio_dbh.dbhFormat    = 0;
       
   445 
       
   446     /* Note that we install the 16bitLittleEndian Converter if needed. */
       
   447     if ( spec->format == 0x8010 ) {
       
   448         audio_dbh.dbhCompressionID = fixedCompression;
       
   449         audio_dbh.dbhFormat = k16BitLittleEndianFormat;
       
   450     }
       
   451 
       
   452     /* allocate the 2 double-back buffers */
       
   453     for ( i=0; i<2; ++i ) {
       
   454         audio_buf[i] = SDL_calloc(1, sizeof(SndDoubleBuffer)+spec->size);
       
   455         if ( audio_buf[i] == NULL ) {
       
   456             SDL_OutOfMemory();
       
   457             return(-1);
       
   458         }
       
   459         audio_buf[i]->dbNumFrames = spec->samples;
       
   460         audio_buf[i]->dbFlags = dbBufferReady;
       
   461         audio_buf[i]->dbUserInfo[0] = (long)this;
       
   462         audio_dbh.dbhBufferPtr[i] = audio_buf[i];
       
   463     }
       
   464 
       
   465     /* Create the sound manager channel */
       
   466     channel = (SndChannelPtr)SDL_malloc(sizeof(*channel));
       
   467     if ( channel == NULL ) {
       
   468         SDL_OutOfMemory();
       
   469         return(-1);
       
   470     }
       
   471     if ( spec->channels >= 2 ) {
       
   472         initOptions = initStereo;
       
   473     } else {
       
   474         initOptions = initMono;
       
   475     }
       
   476     channel->userInfo = 0;
       
   477     channel->qLength = 128;
       
   478     if ( SndNewChannel(&channel, sampledSynth, initOptions, 0L) != noErr ) {
       
   479         SDL_SetError("Unable to create audio channel");
       
   480         SDL_free(channel);
       
   481         channel = NULL;
       
   482         return(-1);
       
   483     }
       
   484  
       
   485     /* Start playback */
       
   486     if ( SndPlayDoubleBuffer(channel, (SndDoubleBufferHeaderPtr)&audio_dbh)
       
   487                                 != noErr ) {
       
   488         SDL_SetError("Unable to play double buffered audio");
       
   489         return(-1);
       
   490     }
       
   491     
       
   492     return 1;
       
   493 }
       
   494 
       
   495 #endif /* TARGET_API_MAC_CARBON || USE_RYANS_SOUNDCODE */
       
   496