sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_artsaudio.c (10609B)


      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: */