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