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