src/audio/arts/SDL_artsaudio.c
author Sam Lantinga
Mon, 09 Jan 2017 11:58:01 -0800
changeset 10802 6afc9b833867
parent 10737 3406a0f8b041
child 11811 5d94cb6b24d3
permissions -rw-r--r--
We only need the first few keymaps corresponding to the following constants:
K_NORMTAB, K_SHIFTTAB, K_ALTTAB, K_ALTSHIFTTAB

In the normal case we'll load all the keymaps from the kernel, but this reduces the size of the SDL library for the fallback case when we can't get to the tty.
slouken@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@10737
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@0
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@0
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@0
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@0
    22
slouken@6044
    23
#if SDL_AUDIO_DRIVER_ARTS
slouken@6044
    24
slouken@0
    25
/* Allow access to a raw mixing buffer */
slouken@0
    26
icculus@2155
    27
#ifdef HAVE_SIGNAL_H
icculus@2155
    28
#include <signal.h>
icculus@2155
    29
#endif
icculus@2155
    30
#include <unistd.h>
slouken@3068
    31
#include <errno.h>
icculus@2155
    32
slouken@1358
    33
#include "SDL_timer.h"
slouken@0
    34
#include "SDL_audio.h"
slouken@1361
    35
#include "../SDL_audio_c.h"
slouken@0
    36
#include "SDL_artsaudio.h"
slouken@0
    37
icculus@6046
    38
#ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
slouken@294
    39
#include "SDL_name.h"
slouken@294
    40
#include "SDL_loadso.h"
slouken@294
    41
#else
slouken@294
    42
#define SDL_NAME(X)	X
slouken@294
    43
#endif
slouken@294
    44
icculus@6046
    45
#ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
slouken@294
    46
slouken@1361
    47
static const char *arts_library = SDL_AUDIO_DRIVER_ARTS_DYNAMIC;
slouken@294
    48
static void *arts_handle = NULL;
slouken@294
    49
icculus@2049
    50
/* !!! FIXME: I hate this SDL_NAME clutter...it makes everything so messy! */
slouken@1895
    51
static int (*SDL_NAME(arts_init)) (void);
slouken@1895
    52
static void (*SDL_NAME(arts_free)) (void);
slouken@1895
    53
static arts_stream_t(*SDL_NAME(arts_play_stream)) (int rate, int bits,
slouken@1895
    54
                                                   int channels,
slouken@1895
    55
                                                   const char *name);
slouken@1895
    56
static int (*SDL_NAME(arts_stream_set)) (arts_stream_t s,
slouken@1895
    57
                                         arts_parameter_t param, int value);
slouken@1895
    58
static int (*SDL_NAME(arts_stream_get)) (arts_stream_t s,
slouken@1895
    59
                                         arts_parameter_t param);
slouken@1895
    60
static int (*SDL_NAME(arts_write)) (arts_stream_t s, const void *buffer,
slouken@1895
    61
                                    int count);
slouken@1895
    62
static void (*SDL_NAME(arts_close_stream)) (arts_stream_t s);
icculus@6152
    63
static int (*SDL_NAME(arts_suspend))(void);
slouken@2141
    64
static int (*SDL_NAME(arts_suspended)) (void);
icculus@2049
    65
static const char *(*SDL_NAME(arts_error_text)) (int errorcode);
slouken@301
    66
icculus@2049
    67
#define SDL_ARTS_SYM(x) { #x, (void **) (char *) &SDL_NAME(x) }
slouken@1895
    68
static struct
slouken@1895
    69
{
slouken@1895
    70
    const char *name;
slouken@1895
    71
    void **func;
slouken@294
    72
} arts_functions[] = {
slouken@2141
    73
/* *INDENT-OFF* */
slouken@2141
    74
    SDL_ARTS_SYM(arts_init),
slouken@2141
    75
    SDL_ARTS_SYM(arts_free),
slouken@2141
    76
    SDL_ARTS_SYM(arts_play_stream),
slouken@2141
    77
    SDL_ARTS_SYM(arts_stream_set),
slouken@2141
    78
    SDL_ARTS_SYM(arts_stream_get),
slouken@2141
    79
    SDL_ARTS_SYM(arts_write),
slouken@2141
    80
    SDL_ARTS_SYM(arts_close_stream),
icculus@6152
    81
    SDL_ARTS_SYM(arts_suspend),
slouken@2141
    82
    SDL_ARTS_SYM(arts_suspended),
slouken@2141
    83
    SDL_ARTS_SYM(arts_error_text),
slouken@2141
    84
/* *INDENT-ON* */
icculus@2121
    85
};
slouken@2141
    86
icculus@2049
    87
#undef SDL_ARTS_SYM
slouken@294
    88
slouken@1895
    89
static void
slouken@1895
    90
UnloadARTSLibrary()
slouken@294
    91
{
icculus@2049
    92
    if (arts_handle != NULL) {
slouken@1895
    93
        SDL_UnloadObject(arts_handle);
slouken@1895
    94
        arts_handle = NULL;
slouken@1895
    95
    }
slouken@294
    96
}
slouken@294
    97
slouken@1895
    98
static int
slouken@1895
    99
LoadARTSLibrary(void)
slouken@294
   100
{
slouken@1895
   101
    int i, retval = -1;
slouken@294
   102
icculus@2049
   103
    if (arts_handle == NULL) {
icculus@2049
   104
        arts_handle = SDL_LoadObject(arts_library);
icculus@2049
   105
        if (arts_handle != NULL) {
icculus@2049
   106
            retval = 0;
icculus@2049
   107
            for (i = 0; i < SDL_arraysize(arts_functions); ++i) {
icculus@2049
   108
                *arts_functions[i].func =
icculus@2049
   109
                    SDL_LoadFunction(arts_handle, arts_functions[i].name);
icculus@2049
   110
                if (!*arts_functions[i].func) {
icculus@2049
   111
                    retval = -1;
icculus@2049
   112
                    UnloadARTSLibrary();
icculus@2049
   113
                    break;
icculus@2049
   114
                }
slouken@1895
   115
            }
slouken@1895
   116
        }
slouken@1895
   117
    }
icculus@2049
   118
slouken@1895
   119
    return retval;
slouken@294
   120
}
slouken@294
   121
slouken@294
   122
#else
slouken@294
   123
slouken@1895
   124
static void
slouken@1895
   125
UnloadARTSLibrary()
slouken@294
   126
{
slouken@1895
   127
    return;
slouken@294
   128
}
slouken@294
   129
slouken@1895
   130
static int
slouken@1895
   131
LoadARTSLibrary(void)
slouken@294
   132
{
slouken@1895
   133
    return 0;
slouken@294
   134
}
slouken@294
   135
slouken@1361
   136
#endif /* SDL_AUDIO_DRIVER_ARTS_DYNAMIC */
slouken@294
   137
slouken@0
   138
/* This function waits until it is possible to write a full sound buffer */
slouken@1895
   139
static void
icculus@2049
   140
ARTS_WaitDevice(_THIS)
slouken@0
   141
{
slouken@1895
   142
    Sint32 ticks;
slouken@0
   143
slouken@1895
   144
    /* Check to see if the thread-parent process is still alive */
slouken@1895
   145
    {
slouken@1895
   146
        static int cnt = 0;
icculus@2049
   147
        /* Note that this only works with thread implementations
slouken@1895
   148
           that use a different process id for each thread.
slouken@1895
   149
         */
icculus@2049
   150
        /* Check every 10 loops */
icculus@2049
   151
        if (this->hidden->parent && (((++cnt) % 10) == 0)) {
slouken@3068
   152
            if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) {
icculus@9394
   153
                SDL_OpenedAudioDeviceDisconnected(this);
slouken@1895
   154
            }
slouken@1895
   155
        }
slouken@1895
   156
    }
slouken@0
   157
slouken@1895
   158
    /* Use timer for general audio synchronization */
slouken@2060
   159
    ticks =
slouken@2060
   160
        ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
slouken@1895
   161
    if (ticks > 0) {
slouken@1895
   162
        SDL_Delay(ticks);
slouken@1895
   163
    }
slouken@0
   164
}
slouken@0
   165
slouken@1895
   166
static void
icculus@2049
   167
ARTS_PlayDevice(_THIS)
slouken@0
   168
{
slouken@1895
   169
    /* Write the audio data */
slouken@2060
   170
    int written = SDL_NAME(arts_write) (this->hidden->stream,
slouken@2060
   171
                                        this->hidden->mixbuf,
slouken@2060
   172
                                        this->hidden->mixlen);
slouken@0
   173
slouken@1895
   174
    /* If timer synchronization is enabled, set the next write frame */
icculus@2049
   175
    if (this->hidden->frame_ticks) {
icculus@2049
   176
        this->hidden->next_frame += this->hidden->frame_ticks;
slouken@1895
   177
    }
slouken@0
   178
slouken@1895
   179
    /* If we couldn't write, assume fatal error for now */
slouken@1895
   180
    if (written < 0) {
icculus@9394
   181
        SDL_OpenedAudioDeviceDisconnected(this);
slouken@1895
   182
    }
slouken@0
   183
#ifdef DEBUG_AUDIO
slouken@1895
   184
    fprintf(stderr, "Wrote %d bytes of audio data\n", written);
slouken@0
   185
#endif
slouken@0
   186
}
slouken@0
   187
icculus@2049
   188
static Uint8 *
icculus@2049
   189
ARTS_GetDeviceBuf(_THIS)
icculus@2049
   190
{
icculus@2049
   191
    return (this->hidden->mixbuf);
icculus@2049
   192
}
icculus@2049
   193
icculus@2049
   194
slouken@1895
   195
static void
icculus@2049
   196
ARTS_CloseDevice(_THIS)
slouken@0
   197
{
icculus@10255
   198
    if (this->hidden->stream) {
icculus@10255
   199
        SDL_NAME(arts_close_stream) (this->hidden->stream);
slouken@1895
   200
    }
icculus@10255
   201
    SDL_NAME(arts_free) ();
icculus@10256
   202
    SDL_free(this->hidden->mixbuf);
icculus@10255
   203
    SDL_free(this->hidden);
slouken@0
   204
}
slouken@0
   205
icculus@6152
   206
static int
icculus@6152
   207
ARTS_Suspend(void)
icculus@6152
   208
{
icculus@6152
   209
    const Uint32 abortms = SDL_GetTicks() + 3000; /* give up after 3 secs */
slouken@7857
   210
    while ( (!SDL_NAME(arts_suspended)()) && !SDL_TICKS_PASSED(SDL_GetTicks(), abortms) ) {
icculus@6152
   211
        if ( SDL_NAME(arts_suspend)() ) {
icculus@6152
   212
            break;
icculus@6152
   213
        }
icculus@6152
   214
    }
icculus@6152
   215
    return SDL_NAME(arts_suspended)();
icculus@6152
   216
}
icculus@2049
   217
slouken@1895
   218
static int
icculus@9394
   219
ARTS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
slouken@0
   220
{
icculus@2049
   221
    int rc = 0;
icculus@2049
   222
    int bits = 0, frag_spec = 0;
icculus@2049
   223
    SDL_AudioFormat test_format = 0, format = 0;
slouken@0
   224
icculus@2049
   225
    /* Initialize all variables that we clean on shutdown */
icculus@2049
   226
    this->hidden = (struct SDL_PrivateAudioData *)
slouken@2060
   227
        SDL_malloc((sizeof *this->hidden));
icculus@2049
   228
    if (this->hidden == NULL) {
icculus@7038
   229
        return SDL_OutOfMemory();
icculus@2049
   230
    }
icculus@10257
   231
    SDL_zerop(this->hidden);
slouken@0
   232
slouken@1895
   233
    /* Try for a closest match on audio format */
icculus@2049
   234
    for (test_format = SDL_FirstAudioFormat(this->spec.format);
slouken@1895
   235
         !format && test_format;) {
slouken@0
   236
#ifdef DEBUG_AUDIO
slouken@1895
   237
        fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
slouken@0
   238
#endif
slouken@1895
   239
        switch (test_format) {
slouken@1895
   240
        case AUDIO_U8:
slouken@1895
   241
            bits = 8;
slouken@1895
   242
            format = 1;
slouken@1895
   243
            break;
slouken@1895
   244
        case AUDIO_S16LSB:
slouken@1895
   245
            bits = 16;
slouken@1895
   246
            format = 1;
slouken@1895
   247
            break;
slouken@1895
   248
        default:
slouken@1895
   249
            format = 0;
slouken@1895
   250
            break;
slouken@1895
   251
        }
slouken@1895
   252
        if (!format) {
slouken@1895
   253
            test_format = SDL_NextAudioFormat();
slouken@1895
   254
        }
slouken@1895
   255
    }
slouken@1895
   256
    if (format == 0) {
icculus@7038
   257
        return SDL_SetError("Couldn't find any hardware audio formats");
slouken@1895
   258
    }
icculus@2049
   259
    this->spec.format = test_format;
slouken@0
   260
icculus@2049
   261
    if ((rc = SDL_NAME(arts_init) ()) != 0) {
icculus@7038
   262
        return SDL_SetError("Unable to initialize ARTS: %s",
icculus@7038
   263
                            SDL_NAME(arts_error_text) (rc));
slouken@1895
   264
    }
icculus@2121
   265
icculus@10254
   266
    if (!ARTS_Suspend()) {
icculus@10254
   267
        return SDL_SetError("ARTS can not open audio device");
icculus@2121
   268
    }
icculus@2121
   269
icculus@10254
   270
    this->hidden->stream = SDL_NAME(arts_play_stream) (this->spec.freq,
icculus@10254
   271
                                                       bits,
icculus@10254
   272
                                                       this->spec.channels,
icculus@10254
   273
                                                       "SDL");
icculus@10254
   274
icculus@10254
   275
    /* Play nothing so we have at least one write (server bug workaround). */
icculus@10254
   276
    SDL_NAME(arts_write) (this->hidden->stream, "", 0);
icculus@10254
   277
slouken@1895
   278
    /* Calculate the final parameters for this audio specification */
icculus@2049
   279
    SDL_CalculateAudioSpec(&this->spec);
slouken@0
   280
slouken@1895
   281
    /* Determine the power of two of the fragment size */
icculus@2049
   282
    for (frag_spec = 0; (0x01 << frag_spec) < this->spec.size; ++frag_spec);
icculus@2049
   283
    if ((0x01 << frag_spec) != this->spec.size) {
icculus@7038
   284
        return SDL_SetError("Fragment size must be a power of two");
slouken@1895
   285
    }
slouken@1895
   286
    frag_spec |= 0x00020000;    /* two fragments, for low latency */
slouken@0
   287
slouken@0
   288
#ifdef ARTS_P_PACKET_SETTINGS
icculus@2049
   289
    SDL_NAME(arts_stream_set) (this->hidden->stream,
slouken@2060
   290
                               ARTS_P_PACKET_SETTINGS, frag_spec);
slouken@0
   291
#else
icculus@2049
   292
    SDL_NAME(arts_stream_set) (this->hidden->stream, ARTS_P_PACKET_SIZE,
slouken@1895
   293
                               frag_spec & 0xffff);
icculus@2049
   294
    SDL_NAME(arts_stream_set) (this->hidden->stream, ARTS_P_PACKET_COUNT,
icculus@2049
   295
                               frag_spec >> 16);
slouken@0
   296
#endif
icculus@2049
   297
    this->spec.size = SDL_NAME(arts_stream_get) (this->hidden->stream,
icculus@2049
   298
                                                 ARTS_P_PACKET_SIZE);
slouken@0
   299
slouken@1895
   300
    /* Allocate mixing buffer */
icculus@2049
   301
    this->hidden->mixlen = this->spec.size;
icculus@10256
   302
    this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
icculus@2049
   303
    if (this->hidden->mixbuf == NULL) {
icculus@7038
   304
        return SDL_OutOfMemory();
slouken@1895
   305
    }
icculus@2049
   306
    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
slouken@0
   307
slouken@1895
   308
    /* Get the parent process id (we're the parent of the audio thread) */
icculus@2049
   309
    this->hidden->parent = getpid();
slouken@0
   310
slouken@1895
   311
    /* We're ready to rock and roll. :-) */
icculus@7038
   312
    return 0;
icculus@2049
   313
}
icculus@2049
   314
icculus@2049
   315
icculus@2049
   316
static void
icculus@2049
   317
ARTS_Deinitialize(void)
icculus@2049
   318
{
icculus@2049
   319
    UnloadARTSLibrary();
slouken@0
   320
}
slouken@1895
   321
icculus@2049
   322
icculus@2049
   323
static int
slouken@2060
   324
ARTS_Init(SDL_AudioDriverImpl * impl)
icculus@2049
   325
{
icculus@2049
   326
    if (LoadARTSLibrary() < 0) {
icculus@2049
   327
        return 0;
icculus@2049
   328
    } else {
icculus@2049
   329
        if (SDL_NAME(arts_init) () != 0) {
icculus@2049
   330
            UnloadARTSLibrary();
icculus@2049
   331
            SDL_SetError("ARTS: arts_init failed (no audio server?)");
icculus@2049
   332
            return 0;
icculus@2049
   333
        }
icculus@2049
   334
icculus@2049
   335
        /* Play a stream so aRts doesn't crash */
icculus@6152
   336
        if (ARTS_Suspend()) {
icculus@2121
   337
            arts_stream_t stream;
icculus@2121
   338
            stream = SDL_NAME(arts_play_stream) (44100, 16, 2, "SDL");
icculus@2121
   339
            SDL_NAME(arts_write) (stream, "", 0);
icculus@2121
   340
            SDL_NAME(arts_close_stream) (stream);
icculus@2121
   341
        }
icculus@2121
   342
icculus@2049
   343
        SDL_NAME(arts_free) ();
icculus@2049
   344
    }
icculus@2049
   345
icculus@2049
   346
    /* Set the function pointers */
icculus@2049
   347
    impl->OpenDevice = ARTS_OpenDevice;
icculus@2049
   348
    impl->PlayDevice = ARTS_PlayDevice;
icculus@2049
   349
    impl->WaitDevice = ARTS_WaitDevice;
icculus@2049
   350
    impl->GetDeviceBuf = ARTS_GetDeviceBuf;
icculus@2049
   351
    impl->CloseDevice = ARTS_CloseDevice;
icculus@2049
   352
    impl->Deinitialize = ARTS_Deinitialize;
icculus@2049
   353
    impl->OnlyHasDefaultOutputDevice = 1;
icculus@2049
   354
icculus@3699
   355
    return 1;   /* this audio target is available. */
icculus@2049
   356
}
icculus@2049
   357
icculus@2049
   358
icculus@2049
   359
AudioBootStrap ARTS_bootstrap = {
icculus@5594
   360
    "arts", "Analog RealTime Synthesizer", ARTS_Init, 0
icculus@2049
   361
};
icculus@2049
   362
slouken@6044
   363
#endif /* SDL_AUDIO_DRIVER_ARTS */
slouken@6044
   364
slouken@1895
   365
/* vi: set ts=4 sw=4 expandtab: */