src/audio/esd/SDL_esdaudio.c
author Ryan C. Gordon
Wed, 18 Mar 2015 02:01:17 -0400
changeset 9394 bb28e5281770
parent 9393 ed79a66e57e5
child 9619 b94b6d0bff0f
permissions -rw-r--r--
Bunch of reworking to how we manage audio devices.

Device enumeration now happens at startup and then is managed exclusively
through hotplugging instead of full redetection. The device name list now has
a unique "handle" associated with each item and SDL will pass this to the
backend so they don't have to figure out how a human readable name maps to
real hardware for a second time.

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