src/audio/arts/SDL_artsaudio.c
author Sam Lantinga
Wed, 03 Oct 2012 19:26:07 -0700
changeset 6552 caa7decef32b
parent 6152 ff0bc7241515
child 6885 700f1b25f77f
permissions -rw-r--r--
Merged Ryan's changes
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 #if SDL_AUDIO_DRIVER_ARTS
    24 
    25 /* Allow access to a raw mixing buffer */
    26 
    27 #ifdef HAVE_SIGNAL_H
    28 #include <signal.h>
    29 #endif
    30 #include <unistd.h>
    31 #include <errno.h>
    32 
    33 #include "SDL_timer.h"
    34 #include "SDL_audio.h"
    35 #include "../SDL_audiomem.h"
    36 #include "../SDL_audio_c.h"
    37 #include "SDL_artsaudio.h"
    38 
    39 #ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
    40 #include "SDL_name.h"
    41 #include "SDL_loadso.h"
    42 #else
    43 #define SDL_NAME(X)	X
    44 #endif
    45 
    46 #ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
    47 
    48 static const char *arts_library = SDL_AUDIO_DRIVER_ARTS_DYNAMIC;
    49 static void *arts_handle = NULL;
    50 
    51 /* !!! FIXME: I hate this SDL_NAME clutter...it makes everything so messy! */
    52 static int (*SDL_NAME(arts_init)) (void);
    53 static void (*SDL_NAME(arts_free)) (void);
    54 static arts_stream_t(*SDL_NAME(arts_play_stream)) (int rate, int bits,
    55                                                    int channels,
    56                                                    const char *name);
    57 static int (*SDL_NAME(arts_stream_set)) (arts_stream_t s,
    58                                          arts_parameter_t param, int value);
    59 static int (*SDL_NAME(arts_stream_get)) (arts_stream_t s,
    60                                          arts_parameter_t param);
    61 static int (*SDL_NAME(arts_write)) (arts_stream_t s, const void *buffer,
    62                                     int count);
    63 static void (*SDL_NAME(arts_close_stream)) (arts_stream_t s);
    64 static int (*SDL_NAME(arts_suspend))(void);
    65 static int (*SDL_NAME(arts_suspended)) (void);
    66 static const char *(*SDL_NAME(arts_error_text)) (int errorcode);
    67 
    68 #define SDL_ARTS_SYM(x) { #x, (void **) (char *) &SDL_NAME(x) }
    69 static struct
    70 {
    71     const char *name;
    72     void **func;
    73 } arts_functions[] = {
    74 /* *INDENT-OFF* */
    75     SDL_ARTS_SYM(arts_init),
    76     SDL_ARTS_SYM(arts_free),
    77     SDL_ARTS_SYM(arts_play_stream),
    78     SDL_ARTS_SYM(arts_stream_set),
    79     SDL_ARTS_SYM(arts_stream_get),
    80     SDL_ARTS_SYM(arts_write),
    81     SDL_ARTS_SYM(arts_close_stream),
    82     SDL_ARTS_SYM(arts_suspend),
    83     SDL_ARTS_SYM(arts_suspended),
    84     SDL_ARTS_SYM(arts_error_text),
    85 /* *INDENT-ON* */
    86 };
    87 
    88 #undef SDL_ARTS_SYM
    89 
    90 static void
    91 UnloadARTSLibrary()
    92 {
    93     if (arts_handle != NULL) {
    94         SDL_UnloadObject(arts_handle);
    95         arts_handle = NULL;
    96     }
    97 }
    98 
    99 static int
   100 LoadARTSLibrary(void)
   101 {
   102     int i, retval = -1;
   103 
   104     if (arts_handle == NULL) {
   105         arts_handle = SDL_LoadObject(arts_library);
   106         if (arts_handle != NULL) {
   107             retval = 0;
   108             for (i = 0; i < SDL_arraysize(arts_functions); ++i) {
   109                 *arts_functions[i].func =
   110                     SDL_LoadFunction(arts_handle, arts_functions[i].name);
   111                 if (!*arts_functions[i].func) {
   112                     retval = -1;
   113                     UnloadARTSLibrary();
   114                     break;
   115                 }
   116             }
   117         }
   118     }
   119 
   120     return retval;
   121 }
   122 
   123 #else
   124 
   125 static void
   126 UnloadARTSLibrary()
   127 {
   128     return;
   129 }
   130 
   131 static int
   132 LoadARTSLibrary(void)
   133 {
   134     return 0;
   135 }
   136 
   137 #endif /* SDL_AUDIO_DRIVER_ARTS_DYNAMIC */
   138 
   139 /* This function waits until it is possible to write a full sound buffer */
   140 static void
   141 ARTS_WaitDevice(_THIS)
   142 {
   143     Sint32 ticks;
   144 
   145     /* Check to see if the thread-parent process is still alive */
   146     {
   147         static int cnt = 0;
   148         /* Note that this only works with thread implementations
   149            that use a different process id for each thread.
   150          */
   151         /* Check every 10 loops */
   152         if (this->hidden->parent && (((++cnt) % 10) == 0)) {
   153             if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) {
   154                 this->enabled = 0;
   155             }
   156         }
   157     }
   158 
   159     /* Use timer for general audio synchronization */
   160     ticks =
   161         ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
   162     if (ticks > 0) {
   163         SDL_Delay(ticks);
   164     }
   165 }
   166 
   167 static void
   168 ARTS_PlayDevice(_THIS)
   169 {
   170     /* Write the audio data */
   171     int written = SDL_NAME(arts_write) (this->hidden->stream,
   172                                         this->hidden->mixbuf,
   173                                         this->hidden->mixlen);
   174 
   175     /* If timer synchronization is enabled, set the next write frame */
   176     if (this->hidden->frame_ticks) {
   177         this->hidden->next_frame += this->hidden->frame_ticks;
   178     }
   179 
   180     /* If we couldn't write, assume fatal error for now */
   181     if (written < 0) {
   182         this->enabled = 0;
   183     }
   184 #ifdef DEBUG_AUDIO
   185     fprintf(stderr, "Wrote %d bytes of audio data\n", written);
   186 #endif
   187 }
   188 
   189 static void
   190 ARTS_WaitDone(_THIS)
   191 {
   192     /* !!! FIXME: camp here until buffer drains... SDL_Delay(???); */
   193 }
   194 
   195 
   196 static Uint8 *
   197 ARTS_GetDeviceBuf(_THIS)
   198 {
   199     return (this->hidden->mixbuf);
   200 }
   201 
   202 
   203 static void
   204 ARTS_CloseDevice(_THIS)
   205 {
   206     if (this->hidden != NULL) {
   207         if (this->hidden->mixbuf != NULL) {
   208             SDL_FreeAudioMem(this->hidden->mixbuf);
   209             this->hidden->mixbuf = NULL;
   210         }
   211         if (this->hidden->stream) {
   212             SDL_NAME(arts_close_stream) (this->hidden->stream);
   213             this->hidden->stream = 0;
   214         }
   215         SDL_NAME(arts_free) ();
   216         SDL_free(this->hidden);
   217         this->hidden = NULL;
   218     }
   219 }
   220 
   221 static int
   222 ARTS_Suspend(void)
   223 {
   224     const Uint32 abortms = SDL_GetTicks() + 3000; /* give up after 3 secs */
   225     while ( (!SDL_NAME(arts_suspended)()) && (SDL_GetTicks() < abortms) ) {
   226         if ( SDL_NAME(arts_suspend)() ) {
   227             break;
   228         }
   229     }
   230     return SDL_NAME(arts_suspended)();
   231 }
   232 
   233 static int
   234 ARTS_OpenDevice(_THIS, const char *devname, int iscapture)
   235 {
   236     int rc = 0;
   237     int bits = 0, frag_spec = 0;
   238     SDL_AudioFormat test_format = 0, format = 0;
   239 
   240     /* Initialize all variables that we clean on shutdown */
   241     this->hidden = (struct SDL_PrivateAudioData *)
   242         SDL_malloc((sizeof *this->hidden));
   243     if (this->hidden == NULL) {
   244         SDL_OutOfMemory();
   245         return 0;
   246     }
   247     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   248 
   249     /* Try for a closest match on audio format */
   250     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   251          !format && test_format;) {
   252 #ifdef DEBUG_AUDIO
   253         fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   254 #endif
   255         switch (test_format) {
   256         case AUDIO_U8:
   257             bits = 8;
   258             format = 1;
   259             break;
   260         case AUDIO_S16LSB:
   261             bits = 16;
   262             format = 1;
   263             break;
   264         default:
   265             format = 0;
   266             break;
   267         }
   268         if (!format) {
   269             test_format = SDL_NextAudioFormat();
   270         }
   271     }
   272     if (format == 0) {
   273         ARTS_CloseDevice(this);
   274         SDL_SetError("Couldn't find any hardware audio formats");
   275         return 0;
   276     }
   277     this->spec.format = test_format;
   278 
   279     if ((rc = SDL_NAME(arts_init) ()) != 0) {
   280         ARTS_CloseDevice(this);
   281         SDL_SetError("Unable to initialize ARTS: %s",
   282                      SDL_NAME(arts_error_text) (rc));
   283         return 0;
   284     }
   285 
   286     if (!ARTS_Suspend()) {
   287         ARTS_CloseDevice(this);
   288         SDL_SetError("ARTS can not open audio device");
   289         return 0;
   290     }
   291 
   292     this->hidden->stream = SDL_NAME(arts_play_stream) (this->spec.freq,
   293                                                        bits,
   294                                                        this->spec.channels,
   295                                                        "SDL");
   296 
   297     /* Play nothing so we have at least one write (server bug workaround). */
   298     SDL_NAME(arts_write) (this->hidden->stream, "", 0);
   299 
   300     /* Calculate the final parameters for this audio specification */
   301     SDL_CalculateAudioSpec(&this->spec);
   302 
   303     /* Determine the power of two of the fragment size */
   304     for (frag_spec = 0; (0x01 << frag_spec) < this->spec.size; ++frag_spec);
   305     if ((0x01 << frag_spec) != this->spec.size) {
   306         ARTS_CloseDevice(this);
   307         SDL_SetError("Fragment size must be a power of two");
   308         return 0;
   309     }
   310     frag_spec |= 0x00020000;    /* two fragments, for low latency */
   311 
   312 #ifdef ARTS_P_PACKET_SETTINGS
   313     SDL_NAME(arts_stream_set) (this->hidden->stream,
   314                                ARTS_P_PACKET_SETTINGS, frag_spec);
   315 #else
   316     SDL_NAME(arts_stream_set) (this->hidden->stream, ARTS_P_PACKET_SIZE,
   317                                frag_spec & 0xffff);
   318     SDL_NAME(arts_stream_set) (this->hidden->stream, ARTS_P_PACKET_COUNT,
   319                                frag_spec >> 16);
   320 #endif
   321     this->spec.size = SDL_NAME(arts_stream_get) (this->hidden->stream,
   322                                                  ARTS_P_PACKET_SIZE);
   323 
   324     /* Allocate mixing buffer */
   325     this->hidden->mixlen = this->spec.size;
   326     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
   327     if (this->hidden->mixbuf == NULL) {
   328         ARTS_CloseDevice(this);
   329         SDL_OutOfMemory();
   330         return 0;
   331     }
   332     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   333 
   334     /* Get the parent process id (we're the parent of the audio thread) */
   335     this->hidden->parent = getpid();
   336 
   337     /* We're ready to rock and roll. :-) */
   338     return 1;
   339 }
   340 
   341 
   342 static void
   343 ARTS_Deinitialize(void)
   344 {
   345     UnloadARTSLibrary();
   346 }
   347 
   348 
   349 static int
   350 ARTS_Init(SDL_AudioDriverImpl * impl)
   351 {
   352     if (LoadARTSLibrary() < 0) {
   353         return 0;
   354     } else {
   355         if (SDL_NAME(arts_init) () != 0) {
   356             UnloadARTSLibrary();
   357             SDL_SetError("ARTS: arts_init failed (no audio server?)");
   358             return 0;
   359         }
   360 
   361         /* Play a stream so aRts doesn't crash */
   362         if (ARTS_Suspend()) {
   363             arts_stream_t stream;
   364             stream = SDL_NAME(arts_play_stream) (44100, 16, 2, "SDL");
   365             SDL_NAME(arts_write) (stream, "", 0);
   366             SDL_NAME(arts_close_stream) (stream);
   367         }
   368 
   369         SDL_NAME(arts_free) ();
   370     }
   371 
   372     /* Set the function pointers */
   373     impl->OpenDevice = ARTS_OpenDevice;
   374     impl->PlayDevice = ARTS_PlayDevice;
   375     impl->WaitDevice = ARTS_WaitDevice;
   376     impl->GetDeviceBuf = ARTS_GetDeviceBuf;
   377     impl->CloseDevice = ARTS_CloseDevice;
   378     impl->WaitDone = ARTS_WaitDone;
   379     impl->Deinitialize = ARTS_Deinitialize;
   380     impl->OnlyHasDefaultOutputDevice = 1;
   381 
   382     return 1;   /* this audio target is available. */
   383 }
   384 
   385 
   386 AudioBootStrap ARTS_bootstrap = {
   387     "arts", "Analog RealTime Synthesizer", ARTS_Init, 0
   388 };
   389 
   390 #endif /* SDL_AUDIO_DRIVER_ARTS */
   391 
   392 /* vi: set ts=4 sw=4 expandtab: */