src/audio/esd/SDL_esdaudio.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_ESD
slouken@6044
    24
slouken@0
    25
/* Allow access to an ESD network stream mixing buffer */
slouken@0
    26
slouken@1616
    27
#include <sys/types.h>
slouken@1616
    28
#include <unistd.h>
slouken@1616
    29
#include <signal.h>
slouken@0
    30
#include <errno.h>
slouken@0
    31
#include <esd.h>
slouken@0
    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_esdaudio.h"
slouken@0
    37
icculus@6046
    38
#ifdef SDL_AUDIO_DRIVER_ESD_DYNAMIC
slouken@294
    39
#include "SDL_name.h"
slouken@294
    40
#include "SDL_loadso.h"
slouken@294
    41
#else
slouken@7191
    42
#define SDL_NAME(X) X
slouken@294
    43
#endif
slouken@294
    44
icculus@6046
    45
#ifdef SDL_AUDIO_DRIVER_ESD_DYNAMIC
slouken@294
    46
slouken@1361
    47
static const char *esd_library = SDL_AUDIO_DRIVER_ESD_DYNAMIC;
slouken@294
    48
static void *esd_handle = NULL;
slouken@294
    49
slouken@1895
    50
static int (*SDL_NAME(esd_open_sound)) (const char *host);
slouken@1895
    51
static int (*SDL_NAME(esd_close)) (int esd);
slouken@1895
    52
static int (*SDL_NAME(esd_play_stream)) (esd_format_t format, int rate,
slouken@1895
    53
                                         const char *host, const char *name);
icculus@2049
    54
icculus@2049
    55
#define SDL_ESD_SYM(x) { #x, (void **) (char *) &SDL_NAME(x) }
slouken@1895
    56
static struct
slouken@1895
    57
{
slouken@1895
    58
    const char *name;
slouken@1895
    59
    void **func;
slouken@3162
    60
} const esd_functions[] = {
slouken@3162
    61
    SDL_ESD_SYM(esd_open_sound),
slouken@3162
    62
    SDL_ESD_SYM(esd_close), SDL_ESD_SYM(esd_play_stream),
slouken@3162
    63
};
slouken@3162
    64
icculus@2049
    65
#undef SDL_ESD_SYM
slouken@294
    66
slouken@1895
    67
static void
slouken@1895
    68
UnloadESDLibrary()
slouken@294
    69
{
icculus@2049
    70
    if (esd_handle != NULL) {
slouken@1895
    71
        SDL_UnloadObject(esd_handle);
slouken@1895
    72
        esd_handle = NULL;
slouken@1895
    73
    }
slouken@294
    74
}
slouken@294
    75
slouken@1895
    76
static int
slouken@1895
    77
LoadESDLibrary(void)
slouken@294
    78
{
slouken@1895
    79
    int i, retval = -1;
slouken@294
    80
icculus@2049
    81
    if (esd_handle == NULL) {
icculus@2049
    82
        esd_handle = SDL_LoadObject(esd_library);
icculus@2049
    83
        if (esd_handle) {
icculus@2049
    84
            retval = 0;
icculus@2049
    85
            for (i = 0; i < SDL_arraysize(esd_functions); ++i) {
icculus@2049
    86
                *esd_functions[i].func =
icculus@2049
    87
                    SDL_LoadFunction(esd_handle, esd_functions[i].name);
icculus@2049
    88
                if (!*esd_functions[i].func) {
icculus@2049
    89
                    retval = -1;
icculus@2049
    90
                    UnloadESDLibrary();
icculus@2049
    91
                    break;
icculus@2049
    92
                }
slouken@1895
    93
            }
slouken@1895
    94
        }
slouken@1895
    95
    }
slouken@1895
    96
    return retval;
slouken@294
    97
}
slouken@294
    98
slouken@294
    99
#else
slouken@294
   100
slouken@1895
   101
static void
slouken@1895
   102
UnloadESDLibrary()
slouken@294
   103
{
slouken@1895
   104
    return;
slouken@294
   105
}
slouken@294
   106
slouken@1895
   107
static int
slouken@1895
   108
LoadESDLibrary(void)
slouken@294
   109
{
slouken@1895
   110
    return 0;
slouken@294
   111
}
slouken@294
   112
slouken@1361
   113
#endif /* SDL_AUDIO_DRIVER_ESD_DYNAMIC */
slouken@294
   114
slouken@0
   115
slouken@0
   116
/* This function waits until it is possible to write a full sound buffer */
slouken@1895
   117
static void
icculus@2049
   118
ESD_WaitDevice(_THIS)
slouken@0
   119
{
slouken@1895
   120
    Sint32 ticks;
slouken@0
   121
slouken@1895
   122
    /* Check to see if the thread-parent process is still alive */
slouken@1895
   123
    {
slouken@1895
   124
        static int cnt = 0;
slouken@7191
   125
        /* Note that this only works with thread implementations
slouken@1895
   126
           that use a different process id for each thread.
slouken@1895
   127
         */
icculus@2049
   128
        /* Check every 10 loops */
icculus@2049
   129
        if (this->hidden->parent && (((++cnt) % 10) == 0)) {
slouken@3068
   130
            if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) {
icculus@9394
   131
                SDL_OpenedAudioDeviceDisconnected(this);
slouken@1895
   132
            }
slouken@1895
   133
        }
slouken@1895
   134
    }
slouken@0
   135
slouken@1895
   136
    /* Use timer for general audio synchronization */
slouken@7857
   137
    ticks = ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
slouken@1895
   138
    if (ticks > 0) {
slouken@1895
   139
        SDL_Delay(ticks);
slouken@1895
   140
    }
slouken@0
   141
}
slouken@0
   142
slouken@1895
   143
static void
icculus@2049
   144
ESD_PlayDevice(_THIS)
slouken@0
   145
{
icculus@2049
   146
    int written = 0;
slouken@0
   147
slouken@1895
   148
    /* Write the audio data, checking for EAGAIN on broken audio drivers */
slouken@1895
   149
    do {
icculus@2049
   150
        written = write(this->hidden->audio_fd,
slouken@2060
   151
                        this->hidden->mixbuf, this->hidden->mixlen);
slouken@1895
   152
        if ((written < 0) && ((errno == 0) || (errno == EAGAIN))) {
slouken@1895
   153
            SDL_Delay(1);       /* Let a little CPU time go by */
slouken@1895
   154
        }
slouken@2735
   155
    } while ((written < 0) &&
slouken@2735
   156
             ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)));
slouken@0
   157
slouken@1895
   158
    /* Set the next write frame */
icculus@2049
   159
    this->hidden->next_frame += this->hidden->frame_ticks;
slouken@0
   160
slouken@1895
   161
    /* If we couldn't write, assume fatal error for now */
slouken@1895
   162
    if (written < 0) {
icculus@9394
   163
        SDL_OpenedAudioDeviceDisconnected(this);
slouken@1895
   164
    }
slouken@0
   165
}
slouken@0
   166
slouken@1895
   167
static Uint8 *
icculus@2049
   168
ESD_GetDeviceBuf(_THIS)
slouken@0
   169
{
icculus@2049
   170
    return (this->hidden->mixbuf);
slouken@0
   171
}
slouken@0
   172
slouken@1895
   173
static void
icculus@2049
   174
ESD_CloseDevice(_THIS)
slouken@0
   175
{
icculus@10255
   176
    if (this->hidden->audio_fd >= 0) {
icculus@10255
   177
        SDL_NAME(esd_close) (this->hidden->audio_fd);
slouken@1895
   178
    }
icculus@10256
   179
    SDL_free(this->hidden->mixbuf);
icculus@10255
   180
    SDL_free(this->hidden);
slouken@0
   181
}
slouken@0
   182
slouken@0
   183
/* Try to get the name of the program */
slouken@1895
   184
static char *
slouken@1895
   185
get_progname(void)
slouken@0
   186
{
slouken@1895
   187
    char *progname = NULL;
slouken@1402
   188
#ifdef __LINUX__
slouken@1895
   189
    FILE *fp;
slouken@1895
   190
    static char temp[BUFSIZ];
slouken@0
   191
slouken@1895
   192
    SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid());
slouken@1895
   193
    fp = fopen(temp, "r");
slouken@1895
   194
    if (fp != NULL) {
slouken@1895
   195
        if (fgets(temp, sizeof(temp) - 1, fp)) {
slouken@1895
   196
            progname = SDL_strrchr(temp, '/');
slouken@1895
   197
            if (progname == NULL) {
slouken@1895
   198
                progname = temp;
slouken@1895
   199
            } else {
slouken@1895
   200
                progname = progname + 1;
slouken@1895
   201
            }
slouken@1895
   202
        }
slouken@1895
   203
        fclose(fp);
slouken@1895
   204
    }
slouken@0
   205
#endif
slouken@1895
   206
    return (progname);
slouken@0
   207
}
slouken@0
   208
icculus@2049
   209
slouken@1895
   210
static int
icculus@9394
   211
ESD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
slouken@0
   212
{
icculus@2049
   213
    esd_format_t format = (ESD_STREAM | ESD_PLAY);
icculus@2049
   214
    SDL_AudioFormat test_format = 0;
icculus@2049
   215
    int found = 0;
icculus@2049
   216
icculus@2049
   217
    /* Initialize all variables that we clean on shutdown */
icculus@2049
   218
    this->hidden = (struct SDL_PrivateAudioData *)
slouken@2060
   219
        SDL_malloc((sizeof *this->hidden));
icculus@2049
   220
    if (this->hidden == NULL) {
icculus@7038
   221
        return SDL_OutOfMemory();
icculus@2049
   222
    }
icculus@10257
   223
    SDL_zerop(this->hidden);
icculus@2049
   224
    this->hidden->audio_fd = -1;
slouken@0
   225
slouken@1895
   226
    /* Convert audio spec to the ESD audio format */
icculus@2049
   227
    /* Try for a closest match on audio format */
icculus@2049
   228
    for (test_format = SDL_FirstAudioFormat(this->spec.format);
icculus@2049
   229
         !found && test_format; test_format = SDL_NextAudioFormat()) {
icculus@2049
   230
#ifdef DEBUG_AUDIO
icculus@2049
   231
        fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
icculus@2049
   232
#endif
icculus@2049
   233
        found = 1;
icculus@2049
   234
        switch (test_format) {
slouken@2060
   235
        case AUDIO_U8:
slouken@2060
   236
            format |= ESD_BITS8;
slouken@2060
   237
            break;
slouken@2060
   238
        case AUDIO_S16SYS:
slouken@2060
   239
            format |= ESD_BITS16;
slouken@2060
   240
            break;
slouken@2060
   241
        default:
slouken@2060
   242
            found = 0;
slouken@2060
   243
            break;
icculus@2049
   244
        }
slouken@1895
   245
    }
icculus@2049
   246
icculus@2049
   247
    if (!found) {
icculus@7038
   248
        return SDL_SetError("Couldn't find any hardware audio formats");
icculus@2049
   249
    }
icculus@2049
   250
icculus@2049
   251
    if (this->spec.channels == 1) {
slouken@1895
   252
        format |= ESD_MONO;
slouken@1895
   253
    } else {
slouken@1895
   254
        format |= ESD_STEREO;
slouken@1895
   255
    }
slouken@0
   256
#if 0
slouken@2060
   257
    this->spec.samples = ESD_BUF_SIZE;  /* Darn, no way to change this yet */
slouken@0
   258
#endif
slouken@0
   259
slouken@1895
   260
    /* Open a connection to the ESD audio server */
icculus@2049
   261
    this->hidden->audio_fd =
slouken@2060
   262
        SDL_NAME(esd_play_stream) (format, this->spec.freq, NULL,
slouken@2060
   263
                                   get_progname());
icculus@2049
   264
icculus@2049
   265
    if (this->hidden->audio_fd < 0) {
icculus@7038
   266
        return SDL_SetError("Couldn't open ESD connection");
slouken@1895
   267
    }
slouken@0
   268
slouken@1895
   269
    /* Calculate the final parameters for this audio specification */
icculus@2049
   270
    SDL_CalculateAudioSpec(&this->spec);
slouken@2060
   271
    this->hidden->frame_ticks =
slouken@2060
   272
        (float) (this->spec.samples * 1000) / this->spec.freq;
icculus@2049
   273
    this->hidden->next_frame = SDL_GetTicks() + this->hidden->frame_ticks;
slouken@0
   274
slouken@1895
   275
    /* Allocate mixing buffer */
icculus@2049
   276
    this->hidden->mixlen = this->spec.size;
icculus@10256
   277
    this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
icculus@2049
   278
    if (this->hidden->mixbuf == NULL) {
icculus@7038
   279
        return SDL_OutOfMemory();
slouken@1895
   280
    }
icculus@2049
   281
    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
slouken@0
   282
slouken@1895
   283
    /* Get the parent process id (we're the parent of the audio thread) */
icculus@2049
   284
    this->hidden->parent = getpid();
slouken@0
   285
slouken@1895
   286
    /* We're ready to rock and roll. :-) */
icculus@7038
   287
    return 0;
icculus@2049
   288
}
icculus@2049
   289
icculus@2049
   290
static void
icculus@2049
   291
ESD_Deinitialize(void)
icculus@2049
   292
{
icculus@2049
   293
    UnloadESDLibrary();
slouken@0
   294
}
slouken@1895
   295
icculus@2049
   296
static int
slouken@2060
   297
ESD_Init(SDL_AudioDriverImpl * impl)
icculus@2049
   298
{
icculus@2049
   299
    if (LoadESDLibrary() < 0) {
icculus@2049
   300
        return 0;
icculus@2049
   301
    } else {
icculus@2049
   302
        int connection = 0;
icculus@2049
   303
icculus@2049
   304
        /* Don't start ESD if it's not running */
icculus@3581
   305
        SDL_setenv("ESD_NO_SPAWN", "1", 0);
icculus@2049
   306
icculus@2049
   307
        connection = SDL_NAME(esd_open_sound) (NULL);
icculus@2049
   308
        if (connection < 0) {
icculus@2049
   309
            UnloadESDLibrary();
icculus@2049
   310
            SDL_SetError("ESD: esd_open_sound failed (no audio server?)");
icculus@2049
   311
            return 0;
icculus@2049
   312
        }
icculus@2049
   313
        SDL_NAME(esd_close) (connection);
icculus@2049
   314
    }
icculus@2049
   315
icculus@2049
   316
    /* Set the function pointers */
icculus@2049
   317
    impl->OpenDevice = ESD_OpenDevice;
icculus@2049
   318
    impl->PlayDevice = ESD_PlayDevice;
icculus@2049
   319
    impl->WaitDevice = ESD_WaitDevice;
icculus@2049
   320
    impl->GetDeviceBuf = ESD_GetDeviceBuf;
icculus@2049
   321
    impl->CloseDevice = ESD_CloseDevice;
icculus@2049
   322
    impl->Deinitialize = ESD_Deinitialize;
icculus@2049
   323
    impl->OnlyHasDefaultOutputDevice = 1;
icculus@2049
   324
icculus@3699
   325
    return 1;   /* this audio target is available. */
icculus@2049
   326
}
icculus@2049
   327
icculus@2049
   328
icculus@2049
   329
AudioBootStrap ESD_bootstrap = {
icculus@5594
   330
    "esd", "Enlightened Sound Daemon", ESD_Init, 0
icculus@2049
   331
};
icculus@2049
   332
slouken@6044
   333
#endif /* SDL_AUDIO_DRIVER_ESD */
slouken@6044
   334
slouken@1895
   335
/* vi: set ts=4 sw=4 expandtab: */