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