src/audio/esd/SDL_esdaudio.c
author Sam Lantinga
Mon, 10 Jul 2006 21:04:37 +0000
changeset 1895 c121d94672cb
parent 1616 9f836cec0521
child 2049 5f6550e5184f
child 3798 c8b3d3d13ed1
permissions -rw-r--r--
SDL 1.2 is moving to a branch, and SDL 1.3 is becoming the head.
slouken@0
     1
/*
slouken@0
     2
    SDL - Simple DirectMedia Layer
slouken@1312
     3
    Copyright (C) 1997-2006 Sam Lantinga
slouken@0
     4
slouken@0
     5
    This library is free software; you can redistribute it and/or
slouken@1312
     6
    modify it under the terms of the GNU Lesser General Public
slouken@0
     7
    License as published by the Free Software Foundation; either
slouken@1312
     8
    version 2.1 of the License, or (at your option) any later version.
slouken@0
     9
slouken@0
    10
    This library is distributed in the hope that it will be useful,
slouken@0
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@0
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@1312
    13
    Lesser General Public License for more details.
slouken@0
    14
slouken@1312
    15
    You should have received a copy of the GNU Lesser General Public
slouken@1312
    16
    License along with this library; if not, write to the Free Software
slouken@1312
    17
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
slouken@0
    18
slouken@0
    19
    Sam Lantinga
slouken@252
    20
    slouken@libsdl.org
slouken@0
    21
*/
slouken@1402
    22
#include "SDL_config.h"
slouken@0
    23
slouken@0
    24
/* Allow access to an ESD network stream mixing buffer */
slouken@0
    25
slouken@1616
    26
#include <sys/types.h>
slouken@1616
    27
#include <unistd.h>
slouken@1616
    28
#include <signal.h>
slouken@0
    29
#include <errno.h>
slouken@0
    30
#include <esd.h>
slouken@0
    31
slouken@1358
    32
#include "SDL_timer.h"
slouken@0
    33
#include "SDL_audio.h"
slouken@1361
    34
#include "../SDL_audiomem.h"
slouken@1361
    35
#include "../SDL_audio_c.h"
slouken@1361
    36
#include "../SDL_audiodev_c.h"
slouken@0
    37
#include "SDL_esdaudio.h"
slouken@0
    38
slouken@1361
    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@294
    43
#define SDL_NAME(X)	X
slouken@294
    44
#endif
slouken@294
    45
slouken@0
    46
/* The tag name used by ESD audio */
slouken@0
    47
#define ESD_DRIVER_NAME		"esd"
slouken@0
    48
slouken@0
    49
/* Audio driver functions */
slouken@1895
    50
static int ESD_OpenAudio(_THIS, SDL_AudioSpec * spec);
slouken@0
    51
static void ESD_WaitAudio(_THIS);
slouken@0
    52
static void ESD_PlayAudio(_THIS);
slouken@0
    53
static Uint8 *ESD_GetAudioBuf(_THIS);
slouken@0
    54
static void ESD_CloseAudio(_THIS);
slouken@0
    55
slouken@1361
    56
#ifdef SDL_AUDIO_DRIVER_ESD_DYNAMIC
slouken@294
    57
slouken@1361
    58
static const char *esd_library = SDL_AUDIO_DRIVER_ESD_DYNAMIC;
slouken@294
    59
static void *esd_handle = NULL;
slouken@294
    60
static int esd_loaded = 0;
slouken@294
    61
slouken@1895
    62
static int (*SDL_NAME(esd_open_sound)) (const char *host);
slouken@1895
    63
static int (*SDL_NAME(esd_close)) (int esd);
slouken@1895
    64
static int (*SDL_NAME(esd_play_stream)) (esd_format_t format, int rate,
slouken@1895
    65
                                         const char *host, const char *name);
slouken@1895
    66
static struct
slouken@1895
    67
{
slouken@1895
    68
    const char *name;
slouken@1895
    69
    void **func;
slouken@294
    70
} esd_functions[] = {
slouken@1895
    71
    {
slouken@1895
    72
    "esd_open_sound", (void **) &SDL_NAME(esd_open_sound)}, {
slouken@1895
    73
    "esd_close", (void **) &SDL_NAME(esd_close)}, {
slouken@1895
    74
"esd_play_stream", (void **) &SDL_NAME(esd_play_stream)},};
slouken@294
    75
slouken@1895
    76
static void
slouken@1895
    77
UnloadESDLibrary()
slouken@294
    78
{
slouken@1895
    79
    if (esd_loaded) {
slouken@1895
    80
        SDL_UnloadObject(esd_handle);
slouken@1895
    81
        esd_handle = NULL;
slouken@1895
    82
        esd_loaded = 0;
slouken@1895
    83
    }
slouken@294
    84
}
slouken@294
    85
slouken@1895
    86
static int
slouken@1895
    87
LoadESDLibrary(void)
slouken@294
    88
{
slouken@1895
    89
    int i, retval = -1;
slouken@294
    90
slouken@1895
    91
    esd_handle = SDL_LoadObject(esd_library);
slouken@1895
    92
    if (esd_handle) {
slouken@1895
    93
        esd_loaded = 1;
slouken@1895
    94
        retval = 0;
slouken@1895
    95
        for (i = 0; i < SDL_arraysize(esd_functions); ++i) {
slouken@1895
    96
            *esd_functions[i].func =
slouken@1895
    97
                SDL_LoadFunction(esd_handle, esd_functions[i].name);
slouken@1895
    98
            if (!*esd_functions[i].func) {
slouken@1895
    99
                retval = -1;
slouken@1895
   100
                UnloadESDLibrary();
slouken@1895
   101
                break;
slouken@1895
   102
            }
slouken@1895
   103
        }
slouken@1895
   104
    }
slouken@1895
   105
    return retval;
slouken@294
   106
}
slouken@294
   107
slouken@294
   108
#else
slouken@294
   109
slouken@1895
   110
static void
slouken@1895
   111
UnloadESDLibrary()
slouken@294
   112
{
slouken@1895
   113
    return;
slouken@294
   114
}
slouken@294
   115
slouken@1895
   116
static int
slouken@1895
   117
LoadESDLibrary(void)
slouken@294
   118
{
slouken@1895
   119
    return 0;
slouken@294
   120
}
slouken@294
   121
slouken@1361
   122
#endif /* SDL_AUDIO_DRIVER_ESD_DYNAMIC */
slouken@294
   123
slouken@0
   124
/* Audio driver bootstrap functions */
slouken@0
   125
slouken@1895
   126
static int
slouken@1895
   127
Audio_Available(void)
slouken@0
   128
{
slouken@1895
   129
    int connection;
slouken@1895
   130
    int available;
slouken@0
   131
slouken@1895
   132
    available = 0;
slouken@1895
   133
    if (LoadESDLibrary() < 0) {
slouken@1895
   134
        return available;
slouken@1895
   135
    }
slouken@1895
   136
    connection = SDL_NAME(esd_open_sound) (NULL);
slouken@1895
   137
    if (connection >= 0) {
slouken@1895
   138
        available = 1;
slouken@1895
   139
        SDL_NAME(esd_close) (connection);
slouken@1895
   140
    }
slouken@1895
   141
    UnloadESDLibrary();
slouken@1895
   142
    return (available);
slouken@0
   143
}
slouken@0
   144
slouken@1895
   145
static void
slouken@1895
   146
Audio_DeleteDevice(SDL_AudioDevice * device)
slouken@0
   147
{
slouken@1895
   148
    SDL_free(device->hidden);
slouken@1895
   149
    SDL_free(device);
slouken@1895
   150
    UnloadESDLibrary();
slouken@0
   151
}
slouken@0
   152
slouken@1895
   153
static SDL_AudioDevice *
slouken@1895
   154
Audio_CreateDevice(int devindex)
slouken@0
   155
{
slouken@1895
   156
    SDL_AudioDevice *this;
slouken@0
   157
slouken@1895
   158
    /* Initialize all variables that we clean on shutdown */
slouken@1895
   159
    LoadESDLibrary();
slouken@1895
   160
    this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice));
slouken@1895
   161
    if (this) {
slouken@1895
   162
        SDL_memset(this, 0, (sizeof *this));
slouken@1895
   163
        this->hidden = (struct SDL_PrivateAudioData *)
slouken@1895
   164
            SDL_malloc((sizeof *this->hidden));
slouken@1895
   165
    }
slouken@1895
   166
    if ((this == NULL) || (this->hidden == NULL)) {
slouken@1895
   167
        SDL_OutOfMemory();
slouken@1895
   168
        if (this) {
slouken@1895
   169
            SDL_free(this);
slouken@1895
   170
        }
slouken@1895
   171
        return (0);
slouken@1895
   172
    }
slouken@1895
   173
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
slouken@1895
   174
    audio_fd = -1;
slouken@0
   175
slouken@1895
   176
    /* Set the function pointers */
slouken@1895
   177
    this->OpenAudio = ESD_OpenAudio;
slouken@1895
   178
    this->WaitAudio = ESD_WaitAudio;
slouken@1895
   179
    this->PlayAudio = ESD_PlayAudio;
slouken@1895
   180
    this->GetAudioBuf = ESD_GetAudioBuf;
slouken@1895
   181
    this->CloseAudio = ESD_CloseAudio;
slouken@0
   182
slouken@1895
   183
    this->free = Audio_DeleteDevice;
slouken@0
   184
slouken@1895
   185
    return this;
slouken@0
   186
}
slouken@0
   187
slouken@0
   188
AudioBootStrap ESD_bootstrap = {
slouken@1895
   189
    ESD_DRIVER_NAME, "Enlightened Sound Daemon",
slouken@1895
   190
    Audio_Available, Audio_CreateDevice
slouken@0
   191
};
slouken@0
   192
slouken@0
   193
/* This function waits until it is possible to write a full sound buffer */
slouken@1895
   194
static void
slouken@1895
   195
ESD_WaitAudio(_THIS)
slouken@0
   196
{
slouken@1895
   197
    Sint32 ticks;
slouken@0
   198
slouken@1895
   199
    /* Check to see if the thread-parent process is still alive */
slouken@1895
   200
    {
slouken@1895
   201
        static int cnt = 0;
slouken@1895
   202
        /* Note that this only works with thread implementations 
slouken@1895
   203
           that use a different process id for each thread.
slouken@1895
   204
         */
slouken@1895
   205
        if (parent && (((++cnt) % 10) == 0)) {  /* Check every 10 loops */
slouken@1895
   206
            if (kill(parent, 0) < 0) {
slouken@1895
   207
                this->enabled = 0;
slouken@1895
   208
            }
slouken@1895
   209
        }
slouken@1895
   210
    }
slouken@0
   211
slouken@1895
   212
    /* Use timer for general audio synchronization */
slouken@1895
   213
    ticks = ((Sint32) (next_frame - SDL_GetTicks())) - FUDGE_TICKS;
slouken@1895
   214
    if (ticks > 0) {
slouken@1895
   215
        SDL_Delay(ticks);
slouken@1895
   216
    }
slouken@0
   217
}
slouken@0
   218
slouken@1895
   219
static void
slouken@1895
   220
ESD_PlayAudio(_THIS)
slouken@0
   221
{
slouken@1895
   222
    int written;
slouken@0
   223
slouken@1895
   224
    /* Write the audio data, checking for EAGAIN on broken audio drivers */
slouken@1895
   225
    do {
slouken@1895
   226
        written = write(audio_fd, mixbuf, mixlen);
slouken@1895
   227
        if ((written < 0) && ((errno == 0) || (errno == EAGAIN))) {
slouken@1895
   228
            SDL_Delay(1);       /* Let a little CPU time go by */
slouken@1895
   229
        }
slouken@1895
   230
    }
slouken@1895
   231
    while ((written < 0) &&
slouken@1895
   232
           ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)));
slouken@0
   233
slouken@1895
   234
    /* Set the next write frame */
slouken@1895
   235
    next_frame += frame_ticks;
slouken@0
   236
slouken@1895
   237
    /* If we couldn't write, assume fatal error for now */
slouken@1895
   238
    if (written < 0) {
slouken@1895
   239
        this->enabled = 0;
slouken@1895
   240
    }
slouken@0
   241
}
slouken@0
   242
slouken@1895
   243
static Uint8 *
slouken@1895
   244
ESD_GetAudioBuf(_THIS)
slouken@0
   245
{
slouken@1895
   246
    return (mixbuf);
slouken@0
   247
}
slouken@0
   248
slouken@1895
   249
static void
slouken@1895
   250
ESD_CloseAudio(_THIS)
slouken@0
   251
{
slouken@1895
   252
    if (mixbuf != NULL) {
slouken@1895
   253
        SDL_FreeAudioMem(mixbuf);
slouken@1895
   254
        mixbuf = NULL;
slouken@1895
   255
    }
slouken@1895
   256
    if (audio_fd >= 0) {
slouken@1895
   257
        SDL_NAME(esd_close) (audio_fd);
slouken@1895
   258
        audio_fd = -1;
slouken@1895
   259
    }
slouken@0
   260
}
slouken@0
   261
slouken@0
   262
/* Try to get the name of the program */
slouken@1895
   263
static char *
slouken@1895
   264
get_progname(void)
slouken@0
   265
{
slouken@1895
   266
    char *progname = NULL;
slouken@1402
   267
#ifdef __LINUX__
slouken@1895
   268
    FILE *fp;
slouken@1895
   269
    static char temp[BUFSIZ];
slouken@0
   270
slouken@1895
   271
    SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid());
slouken@1895
   272
    fp = fopen(temp, "r");
slouken@1895
   273
    if (fp != NULL) {
slouken@1895
   274
        if (fgets(temp, sizeof(temp) - 1, fp)) {
slouken@1895
   275
            progname = SDL_strrchr(temp, '/');
slouken@1895
   276
            if (progname == NULL) {
slouken@1895
   277
                progname = temp;
slouken@1895
   278
            } else {
slouken@1895
   279
                progname = progname + 1;
slouken@1895
   280
            }
slouken@1895
   281
        }
slouken@1895
   282
        fclose(fp);
slouken@1895
   283
    }
slouken@0
   284
#endif
slouken@1895
   285
    return (progname);
slouken@0
   286
}
slouken@0
   287
slouken@1895
   288
static int
slouken@1895
   289
ESD_OpenAudio(_THIS, SDL_AudioSpec * spec)
slouken@0
   290
{
slouken@1895
   291
    esd_format_t format;
slouken@0
   292
slouken@1895
   293
    /* Convert audio spec to the ESD audio format */
slouken@1895
   294
    format = (ESD_STREAM | ESD_PLAY);
slouken@1895
   295
    switch (spec->format & 0xFF) {
slouken@1895
   296
    case 8:
slouken@1895
   297
        format |= ESD_BITS8;
slouken@1895
   298
        break;
slouken@1895
   299
    case 16:
slouken@1895
   300
        format |= ESD_BITS16;
slouken@1895
   301
        break;
slouken@1895
   302
    default:
slouken@1895
   303
        SDL_SetError("Unsupported ESD audio format");
slouken@1895
   304
        return (-1);
slouken@1895
   305
    }
slouken@1895
   306
    if (spec->channels == 1) {
slouken@1895
   307
        format |= ESD_MONO;
slouken@1895
   308
    } else {
slouken@1895
   309
        format |= ESD_STEREO;
slouken@1895
   310
    }
slouken@0
   311
#if 0
slouken@1895
   312
    spec->samples = ESD_BUF_SIZE;       /* Darn, no way to change this yet */
slouken@0
   313
#endif
slouken@0
   314
slouken@1895
   315
    /* Open a connection to the ESD audio server */
slouken@1895
   316
    audio_fd =
slouken@1895
   317
        SDL_NAME(esd_play_stream) (format, spec->freq, NULL, get_progname());
slouken@1895
   318
    if (audio_fd < 0) {
slouken@1895
   319
        SDL_SetError("Couldn't open ESD connection");
slouken@1895
   320
        return (-1);
slouken@1895
   321
    }
slouken@0
   322
slouken@1895
   323
    /* Calculate the final parameters for this audio specification */
slouken@1895
   324
    SDL_CalculateAudioSpec(spec);
slouken@1895
   325
    frame_ticks = (float) (spec->samples * 1000) / spec->freq;
slouken@1895
   326
    next_frame = SDL_GetTicks() + frame_ticks;
slouken@0
   327
slouken@1895
   328
    /* Allocate mixing buffer */
slouken@1895
   329
    mixlen = spec->size;
slouken@1895
   330
    mixbuf = (Uint8 *) SDL_AllocAudioMem(mixlen);
slouken@1895
   331
    if (mixbuf == NULL) {
slouken@1895
   332
        return (-1);
slouken@1895
   333
    }
slouken@1895
   334
    SDL_memset(mixbuf, spec->silence, spec->size);
slouken@0
   335
slouken@1895
   336
    /* Get the parent process id (we're the parent of the audio thread) */
slouken@1895
   337
    parent = getpid();
slouken@0
   338
slouken@1895
   339
    /* We're ready to rock and roll. :-) */
slouken@1895
   340
    return (0);
slouken@0
   341
}
slouken@1895
   342
slouken@1895
   343
/* vi: set ts=4 sw=4 expandtab: */