src/audio/arts/SDL_artsaudio.c
author Ryan C. Gordon
Fri, 06 Oct 2006 02:40:02 +0000
branchSDL-ryan-multiple-audio-device
changeset 3812 35d1367020a3
parent 3809 7852b5b78af5
child 3813 92f7304e50ff
permissions -rw-r--r--
Updated arts driver for 1.3 audio API.
     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_audiomem.h"
    29 #include "../SDL_audio_c.h"
    30 #include "SDL_artsaudio.h"
    31 
    32 #ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
    33 #include "SDL_name.h"
    34 #include "SDL_loadso.h"
    35 #else
    36 #define SDL_NAME(X)	X
    37 #endif
    38 
    39 /* The tag name used by artsc audio */
    40 #define ARTS_DRIVER_NAME         "arts"
    41 
    42 /* Audio driver functions */
    43 static int ARTS_OpenDevice(_THIS, const char *devname, int iscapture);
    44 static void ARTS_WaitDevice(_THIS);
    45 static void ARTS_PlayDevice(_THIS);
    46 static Uint8 *ARTS_GetDeviceBuf(_THIS);
    47 static void ARTS_CloseDevice(_THIS);
    48 static void ARTS_WaitDone(_THIS);
    49 
    50 #ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
    51 
    52 static const char *arts_library = SDL_AUDIO_DRIVER_ARTS_DYNAMIC;
    53 static void *arts_handle = NULL;
    54 static int arts_loaded = 0;
    55 
    56 /* !!! FIXME: I hate this SDL_NAME clutter...it makes everything so messy! */
    57 static int (*SDL_NAME(arts_init)) (void);
    58 static void (*SDL_NAME(arts_free)) (void);
    59 static arts_stream_t(*SDL_NAME(arts_play_stream)) (int rate, int bits,
    60                                                    int channels,
    61                                                    const char *name);
    62 static int (*SDL_NAME(arts_stream_set)) (arts_stream_t s,
    63                                          arts_parameter_t param, int value);
    64 static int (*SDL_NAME(arts_stream_get)) (arts_stream_t s,
    65                                          arts_parameter_t param);
    66 static int (*SDL_NAME(arts_write)) (arts_stream_t s, const void *buffer,
    67                                     int count);
    68 static void (*SDL_NAME(arts_close_stream)) (arts_stream_t s);
    69 static void (*SDL_NAME(arts_close_stream)) (arts_stream_t s);
    70 static const char *(*SDL_NAME(arts_error_text)) (int errorcode);
    71 
    72 #define SDL_ARTS_SYM(x) { #x, (void **) (char *) &SDL_NAME(x) }
    73 static struct
    74 {
    75     const char *name;
    76     void **func;
    77 } arts_functions[] = {
    78     SDL_ARTS_SYM(arts_init),
    79     SDL_ARTS_SYM(arts_free),
    80     SDL_ARTS_SYM(arts_play_stream),
    81     SDL_ARTS_SYM(arts_stream_set),
    82     SDL_ARTS_SYM(arts_stream_get),
    83     SDL_ARTS_SYM(arts_write),
    84     SDL_ARTS_SYM(arts_close_stream),
    85     SDL_ARTS_SYM(arts_error_text),
    86 };
    87 #undef SDL_ARTS_SYM
    88 
    89 static void
    90 UnloadARTSLibrary()
    91 {
    92     if (arts_loaded) {
    93         SDL_UnloadObject(arts_handle);
    94         arts_handle = NULL;
    95         arts_loaded = 0;
    96     }
    97 }
    98 
    99 static int
   100 LoadARTSLibrary(void)
   101 {
   102     int i, retval = -1;
   103 
   104     if (!arts_loaded) {
   105         arts_handle = SDL_LoadObject(arts_library);
   106         if (arts_handle) {
   107             arts_loaded = 1;
   108             retval = 0;
   109             for (i = 0; i < SDL_arraysize(arts_functions); ++i) {
   110                 *arts_functions[i].func =
   111                     SDL_LoadFunction(arts_handle, arts_functions[i].name);
   112                 if (!*arts_functions[i].func) {
   113                     retval = -1;
   114                     UnloadARTSLibrary();
   115                     break;
   116                 }
   117             }
   118         }
   119     }
   120 
   121     return retval;
   122 }
   123 
   124 #else
   125 
   126 static void
   127 UnloadARTSLibrary()
   128 {
   129     return;
   130 }
   131 
   132 static int
   133 LoadARTSLibrary(void)
   134 {
   135     return 0;
   136 }
   137 
   138 #endif /* SDL_AUDIO_DRIVER_ARTS_DYNAMIC */
   139 
   140 /* Audio driver bootstrap functions */
   141 
   142 static int
   143 ARTS_Available(void)
   144 {
   145     int available = 0;
   146 
   147     if (LoadARTSLibrary() == 0) {
   148         if (SDL_NAME(arts_init) () == 0) {
   149 #define ARTS_CRASH_HACK         /* Play a stream so aRts doesn't crash */
   150 #ifdef ARTS_CRASH_HACK
   151             arts_stream_t stream;
   152             stream = SDL_NAME(arts_play_stream) (44100, 16, 2, "SDL");
   153             SDL_NAME(arts_write) (stream, "", 0);
   154             SDL_NAME(arts_close_stream) (stream);
   155 #endif
   156             available = 1;
   157             SDL_NAME(arts_free) ();
   158         }
   159         UnloadARTSLibrary();
   160     }
   161 
   162     return available;
   163 }
   164 
   165 
   166 static int
   167 ARTS_Init(SDL_AudioDriverImpl *impl)
   168 {
   169     /* Set the function pointers */
   170     impl->OpenDevice = ARTS_OpenDevice;
   171     impl->PlayDevice = ARTS_PlayDevice;
   172     impl->WaitDevice = ARTS_WaitDevice;
   173     impl->GetDeviceBuf = ARTS_GetDeviceBuf;
   174     impl->CloseDevice = ARTS_CloseDevice;
   175     impl->WaitDone = ARTS_WaitDone;
   176     impl->OnlyHasDefaultOutputDevice = 1;
   177 
   178     return 1;
   179 }
   180 
   181 
   182 AudioBootStrap ARTS_bootstrap = {
   183     ARTS_DRIVER_NAME, "Analog RealTime Synthesizer",
   184     ARTS_Available, ARTS_Init, 0
   185 };
   186 
   187 
   188 /* This function waits until it is possible to write a full sound buffer */
   189 static void
   190 ARTS_WaitDevice(_THIS)
   191 {
   192     Sint32 ticks;
   193 
   194     /* Check to see if the thread-parent process is still alive */
   195     {
   196         static int cnt = 0;
   197         /* Note that this only works with thread implementations
   198            that use a different process id for each thread.
   199          */
   200         /* Check every 10 loops */
   201         if (this->hidden->parent && (((++cnt) % 10) == 0)) {
   202             if (kill(this->hidden->parent, 0) < 0) {
   203                 this->enabled = 0;
   204             }
   205         }
   206     }
   207 
   208     /* Use timer for general audio synchronization */
   209     ticks = ((Sint32) (this->hidden->next_frame-SDL_GetTicks())) - FUDGE_TICKS;
   210     if (ticks > 0) {
   211         SDL_Delay(ticks);
   212     }
   213 }
   214 
   215 static void
   216 ARTS_PlayDevice(_THIS)
   217 {
   218     /* Write the audio data */
   219     int written = SDL_NAME(arts_write) (
   220                         this->hidden->stream,
   221                         this->hidden->mixbuf,
   222                         this->hidden->mixlen);
   223 
   224     /* If timer synchronization is enabled, set the next write frame */
   225     if (this->hidden->frame_ticks) {
   226         this->hidden->next_frame += this->hidden->frame_ticks;
   227     }
   228 
   229     /* If we couldn't write, assume fatal error for now */
   230     if (written < 0) {
   231         this->enabled = 0;
   232     }
   233 #ifdef DEBUG_AUDIO
   234     fprintf(stderr, "Wrote %d bytes of audio data\n", written);
   235 #endif
   236 }
   237 
   238 static void
   239 ARTS_WaitDone(_THIS)
   240 {
   241     /* !!! FIXME: camp here until buffer drains... SDL_Delay(???); */
   242 }
   243 
   244 
   245 static Uint8 *
   246 ARTS_GetDeviceBuf(_THIS)
   247 {
   248     return (this->hidden->mixbuf);
   249 }
   250 
   251 
   252 static void
   253 ARTS_CloseDevice(_THIS)
   254 {
   255     if (this->hidden != NULL) {
   256         if (this->hidden->mixbuf != NULL) {
   257             SDL_FreeAudioMem(this->hidden->mixbuf);
   258             this->hidden->mixbuf = NULL;
   259         }
   260         if (this->hidden->stream) {
   261             SDL_NAME(arts_close_stream) (this->hidden->stream);
   262             this->hidden->stream = 0;
   263         }
   264         SDL_NAME(arts_free) ();
   265         SDL_free(this->hidden);
   266         this->hidden = NULL;
   267     }
   268     UnloadARTSLibrary();
   269 }
   270 
   271 
   272 static int
   273 ARTS_OpenDevice(_THIS, const char *devname, int iscapture)
   274 {
   275     int rc = 0;
   276     int bits, frag_spec;
   277     SDL_AudioFormat test_format, format;
   278 
   279     /* Initialize all variables that we clean on shutdown */
   280     this->hidden = (struct SDL_PrivateAudioData *)
   281                         SDL_malloc((sizeof *this->hidden));
   282     if (this->hidden == NULL) {
   283         SDL_OutOfMemory();
   284         return 0;
   285     }
   286     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   287 
   288     if (LoadARTSLibrary() < 0) {
   289         ARTS_CloseDevice(this);
   290         SDL_SetError("ARTS: failed to load library: %s", SDL_GetError());
   291         return 0;
   292     }
   293 
   294     /* Try for a closest match on audio format */
   295     format = 0;
   296     bits = 0;
   297     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   298          !format && test_format;) {
   299 #ifdef DEBUG_AUDIO
   300         fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   301 #endif
   302         switch (test_format) {
   303         case AUDIO_U8:
   304             bits = 8;
   305             format = 1;
   306             break;
   307         case AUDIO_S16LSB:
   308             bits = 16;
   309             format = 1;
   310             break;
   311         default:
   312             format = 0;
   313             break;
   314         }
   315         if (!format) {
   316             test_format = SDL_NextAudioFormat();
   317         }
   318     }
   319     if (format == 0) {
   320         ARTS_CloseDevice(this);
   321         SDL_SetError("Couldn't find any hardware audio formats");
   322         return 0;
   323     }
   324     this->spec.format = test_format;
   325 
   326     if ((rc = SDL_NAME(arts_init) ()) != 0) {
   327         ARTS_CloseDevice(this);
   328         SDL_SetError( "Unable to initialize ARTS: %s",
   329                       SDL_NAME(arts_error_text)(rc) );
   330         return 0;
   331     }
   332     this->hidden->stream = SDL_NAME(arts_play_stream) (
   333                                             this->spec.freq,
   334                                             bits, this->spec.channels,
   335                                             "SDL");
   336 
   337     /* Calculate the final parameters for this audio specification */
   338     SDL_CalculateAudioSpec(&this->spec);
   339 
   340     /* Determine the power of two of the fragment size */
   341     for (frag_spec = 0; (0x01 << frag_spec) < this->spec.size; ++frag_spec);
   342     if ((0x01 << frag_spec) != this->spec.size) {
   343         ARTS_CloseDevice(this);
   344         SDL_SetError("Fragment size must be a power of two");
   345         return 0;
   346     }
   347     frag_spec |= 0x00020000;    /* two fragments, for low latency */
   348 
   349 #ifdef ARTS_P_PACKET_SETTINGS
   350     SDL_NAME(arts_stream_set) (this->hidden->stream,
   351                                 ARTS_P_PACKET_SETTINGS, frag_spec);
   352 #else
   353     SDL_NAME(arts_stream_set) (this->hidden->stream, ARTS_P_PACKET_SIZE,
   354                                frag_spec & 0xffff);
   355     SDL_NAME(arts_stream_set) (this->hidden->stream, ARTS_P_PACKET_COUNT,
   356                                frag_spec >> 16);
   357 #endif
   358     this->spec.size = SDL_NAME(arts_stream_get) (this->hidden->stream,
   359                                                  ARTS_P_PACKET_SIZE);
   360 
   361     /* Allocate mixing buffer */
   362     this->hidden->mixlen = this->spec.size;
   363     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
   364     if (this->hidden->mixbuf == NULL) {
   365         ARTS_CloseDevice(this);
   366         SDL_OutOfMemory();
   367         return 0;
   368     }
   369     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   370 
   371     /* Get the parent process id (we're the parent of the audio thread) */
   372     this->hidden->parent = getpid();
   373 
   374     /* We're ready to rock and roll. :-) */
   375     return 1;
   376 }
   377 
   378 /* vi: set ts=4 sw=4 expandtab: */