src/audio/arts/SDL_artsaudio.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 26 May 2015 06:27:46 -0700
changeset 9619 b94b6d0bff0f
parent 9394 bb28e5281770
child 9998 f67cf37e9cd4
permissions -rw-r--r--
Updated the copyright year to 2015
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2015 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_internal.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                 SDL_OpenedAudioDeviceDisconnected(this);
   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         SDL_OpenedAudioDeviceDisconnected(this);
   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         SDL_FreeAudioMem(this->hidden->mixbuf);
   208         this->hidden->mixbuf = NULL;
   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 static int
   220 ARTS_Suspend(void)
   221 {
   222     const Uint32 abortms = SDL_GetTicks() + 3000; /* give up after 3 secs */
   223     while ( (!SDL_NAME(arts_suspended)()) && !SDL_TICKS_PASSED(SDL_GetTicks(), abortms) ) {
   224         if ( SDL_NAME(arts_suspend)() ) {
   225             break;
   226         }
   227     }
   228     return SDL_NAME(arts_suspended)();
   229 }
   230 
   231 static int
   232 ARTS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
   233 {
   234     int rc = 0;
   235     int bits = 0, frag_spec = 0;
   236     SDL_AudioFormat test_format = 0, format = 0;
   237 
   238     /* Initialize all variables that we clean on shutdown */
   239     this->hidden = (struct SDL_PrivateAudioData *)
   240         SDL_malloc((sizeof *this->hidden));
   241     if (this->hidden == NULL) {
   242         return SDL_OutOfMemory();
   243     }
   244     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   245 
   246     /* Try for a closest match on audio format */
   247     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   248          !format && test_format;) {
   249 #ifdef DEBUG_AUDIO
   250         fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   251 #endif
   252         switch (test_format) {
   253         case AUDIO_U8:
   254             bits = 8;
   255             format = 1;
   256             break;
   257         case AUDIO_S16LSB:
   258             bits = 16;
   259             format = 1;
   260             break;
   261         default:
   262             format = 0;
   263             break;
   264         }
   265         if (!format) {
   266             test_format = SDL_NextAudioFormat();
   267         }
   268     }
   269     if (format == 0) {
   270         ARTS_CloseDevice(this);
   271         return SDL_SetError("Couldn't find any hardware audio formats");
   272     }
   273     this->spec.format = test_format;
   274 
   275     if ((rc = SDL_NAME(arts_init) ()) != 0) {
   276         ARTS_CloseDevice(this);
   277         return SDL_SetError("Unable to initialize ARTS: %s",
   278                             SDL_NAME(arts_error_text) (rc));
   279     }
   280 
   281     if (!ARTS_Suspend()) {
   282         ARTS_CloseDevice(this);
   283         return SDL_SetError("ARTS can not open audio device");
   284     }
   285 
   286     this->hidden->stream = SDL_NAME(arts_play_stream) (this->spec.freq,
   287                                                        bits,
   288                                                        this->spec.channels,
   289                                                        "SDL");
   290 
   291     /* Play nothing so we have at least one write (server bug workaround). */
   292     SDL_NAME(arts_write) (this->hidden->stream, "", 0);
   293 
   294     /* Calculate the final parameters for this audio specification */
   295     SDL_CalculateAudioSpec(&this->spec);
   296 
   297     /* Determine the power of two of the fragment size */
   298     for (frag_spec = 0; (0x01 << frag_spec) < this->spec.size; ++frag_spec);
   299     if ((0x01 << frag_spec) != this->spec.size) {
   300         ARTS_CloseDevice(this);
   301         return SDL_SetError("Fragment size must be a power of two");
   302     }
   303     frag_spec |= 0x00020000;    /* two fragments, for low latency */
   304 
   305 #ifdef ARTS_P_PACKET_SETTINGS
   306     SDL_NAME(arts_stream_set) (this->hidden->stream,
   307                                ARTS_P_PACKET_SETTINGS, frag_spec);
   308 #else
   309     SDL_NAME(arts_stream_set) (this->hidden->stream, ARTS_P_PACKET_SIZE,
   310                                frag_spec & 0xffff);
   311     SDL_NAME(arts_stream_set) (this->hidden->stream, ARTS_P_PACKET_COUNT,
   312                                frag_spec >> 16);
   313 #endif
   314     this->spec.size = SDL_NAME(arts_stream_get) (this->hidden->stream,
   315                                                  ARTS_P_PACKET_SIZE);
   316 
   317     /* Allocate mixing buffer */
   318     this->hidden->mixlen = this->spec.size;
   319     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
   320     if (this->hidden->mixbuf == NULL) {
   321         ARTS_CloseDevice(this);
   322         return SDL_OutOfMemory();
   323     }
   324     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   325 
   326     /* Get the parent process id (we're the parent of the audio thread) */
   327     this->hidden->parent = getpid();
   328 
   329     /* We're ready to rock and roll. :-) */
   330     return 0;
   331 }
   332 
   333 
   334 static void
   335 ARTS_Deinitialize(void)
   336 {
   337     UnloadARTSLibrary();
   338 }
   339 
   340 
   341 static int
   342 ARTS_Init(SDL_AudioDriverImpl * impl)
   343 {
   344     if (LoadARTSLibrary() < 0) {
   345         return 0;
   346     } else {
   347         if (SDL_NAME(arts_init) () != 0) {
   348             UnloadARTSLibrary();
   349             SDL_SetError("ARTS: arts_init failed (no audio server?)");
   350             return 0;
   351         }
   352 
   353         /* Play a stream so aRts doesn't crash */
   354         if (ARTS_Suspend()) {
   355             arts_stream_t stream;
   356             stream = SDL_NAME(arts_play_stream) (44100, 16, 2, "SDL");
   357             SDL_NAME(arts_write) (stream, "", 0);
   358             SDL_NAME(arts_close_stream) (stream);
   359         }
   360 
   361         SDL_NAME(arts_free) ();
   362     }
   363 
   364     /* Set the function pointers */
   365     impl->OpenDevice = ARTS_OpenDevice;
   366     impl->PlayDevice = ARTS_PlayDevice;
   367     impl->WaitDevice = ARTS_WaitDevice;
   368     impl->GetDeviceBuf = ARTS_GetDeviceBuf;
   369     impl->CloseDevice = ARTS_CloseDevice;
   370     impl->WaitDone = ARTS_WaitDone;
   371     impl->Deinitialize = ARTS_Deinitialize;
   372     impl->OnlyHasDefaultOutputDevice = 1;
   373 
   374     return 1;   /* this audio target is available. */
   375 }
   376 
   377 
   378 AudioBootStrap ARTS_bootstrap = {
   379     "arts", "Analog RealTime Synthesizer", ARTS_Init, 0
   380 };
   381 
   382 #endif /* SDL_AUDIO_DRIVER_ARTS */
   383 
   384 /* vi: set ts=4 sw=4 expandtab: */