src/audio/nas/SDL_nasaudio.c
author Ryan C. Gordon
Wed, 04 Oct 2006 21:27:53 +0000
branchSDL-ryan-multiple-audio-device
changeset 3798 c8b3d3d13ed1
parent 1982 3b4ce57c6215
child 3809 7852b5b78af5
permissions -rw-r--r--
Audio bootstraps can now specify that a driver is only to be used if
explicitly requested (for things like the "disk" driver that is always
available but you would never want to default to using).

Trimmed out code that can be handled by stubs in the core. The "dummy" driver
is pretty damned small now. :)
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@0
    22
    This driver was written by:
slouken@0
    23
    Erik Inge Bols
slouken@0
    24
    knan@mo.himolde.no
slouken@0
    25
*/
slouken@1402
    26
#include "SDL_config.h"
slouken@0
    27
slouken@0
    28
/* Allow access to a raw mixing buffer */
slouken@0
    29
slouken@0
    30
#include <signal.h>
slouken@0
    31
#include <unistd.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@1361
    37
#include "../SDL_audiodev_c.h"
slouken@0
    38
#include "SDL_nasaudio.h"
slouken@0
    39
slouken@0
    40
/* The tag name used by artsc audio */
slouken@0
    41
#define NAS_DRIVER_NAME         "nas"
slouken@0
    42
slouken@0
    43
static struct SDL_PrivateAudioData *this2 = NULL;
slouken@0
    44
slouken@0
    45
/* Audio driver functions */
slouken@1895
    46
static int NAS_OpenAudio(_THIS, SDL_AudioSpec * spec);
slouken@0
    47
static void NAS_WaitAudio(_THIS);
slouken@0
    48
static void NAS_PlayAudio(_THIS);
slouken@0
    49
static Uint8 *NAS_GetAudioBuf(_THIS);
slouken@0
    50
static void NAS_CloseAudio(_THIS);
slouken@0
    51
slouken@0
    52
/* Audio driver bootstrap functions */
slouken@0
    53
slouken@1895
    54
static int
slouken@1895
    55
Audio_Available(void)
slouken@0
    56
{
slouken@1895
    57
    AuServer *aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
slouken@1895
    58
    if (!aud)
slouken@1895
    59
        return 0;
slouken@0
    60
slouken@1895
    61
    AuCloseServer(aud);
slouken@1895
    62
    return 1;
slouken@0
    63
}
slouken@0
    64
slouken@1895
    65
static void
slouken@1895
    66
Audio_DeleteDevice(SDL_AudioDevice * device)
slouken@0
    67
{
slouken@1895
    68
    SDL_free(device->hidden);
slouken@1895
    69
    SDL_free(device);
slouken@0
    70
}
slouken@0
    71
slouken@1895
    72
static SDL_AudioDevice *
slouken@1895
    73
Audio_CreateDevice(int devindex)
slouken@0
    74
{
slouken@1895
    75
    SDL_AudioDevice *this;
slouken@0
    76
slouken@1895
    77
    /* Initialize all variables that we clean on shutdown */
slouken@1895
    78
    this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice));
slouken@1895
    79
    if (this) {
slouken@1895
    80
        SDL_memset(this, 0, (sizeof *this));
slouken@1895
    81
        this->hidden = (struct SDL_PrivateAudioData *)
slouken@1895
    82
            SDL_malloc((sizeof *this->hidden));
slouken@1895
    83
    }
slouken@1895
    84
    if ((this == NULL) || (this->hidden == NULL)) {
slouken@1895
    85
        SDL_OutOfMemory();
slouken@1895
    86
        if (this) {
slouken@1895
    87
            SDL_free(this);
slouken@1895
    88
        }
slouken@1895
    89
        return (0);
slouken@1895
    90
    }
slouken@1895
    91
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
slouken@0
    92
slouken@1895
    93
    /* Set the function pointers */
slouken@1895
    94
    this->OpenAudio = NAS_OpenAudio;
slouken@1895
    95
    this->WaitAudio = NAS_WaitAudio;
slouken@1895
    96
    this->PlayAudio = NAS_PlayAudio;
slouken@1895
    97
    this->GetAudioBuf = NAS_GetAudioBuf;
slouken@1895
    98
    this->CloseAudio = NAS_CloseAudio;
slouken@0
    99
slouken@1895
   100
    this->free = Audio_DeleteDevice;
slouken@0
   101
slouken@1895
   102
    return this;
slouken@0
   103
}
slouken@0
   104
slouken@0
   105
AudioBootStrap NAS_bootstrap = {
slouken@1895
   106
    NAS_DRIVER_NAME, "Network Audio System",
icculus@3798
   107
    Audio_Available, Audio_CreateDevice, 0
slouken@0
   108
};
slouken@0
   109
slouken@0
   110
/* This function waits until it is possible to write a full sound buffer */
slouken@1895
   111
static void
slouken@1895
   112
NAS_WaitAudio(_THIS)
slouken@0
   113
{
slouken@1895
   114
    while (this->hidden->buf_free < this->hidden->mixlen) {
slouken@1895
   115
        AuEvent ev;
slouken@1895
   116
        AuNextEvent(this->hidden->aud, AuTrue, &ev);
slouken@1895
   117
        AuDispatchEvent(this->hidden->aud, &ev);
slouken@1895
   118
    }
slouken@0
   119
}
slouken@0
   120
slouken@1895
   121
static void
slouken@1895
   122
NAS_PlayAudio(_THIS)
slouken@0
   123
{
slouken@1895
   124
    while (this->hidden->mixlen > this->hidden->buf_free) {     /* We think the buffer is full? Yikes! Ask the server for events,
slouken@1895
   125
                                                                   in the hope that some of them is LowWater events telling us more
slouken@1895
   126
                                                                   of the buffer is free now than what we think. */
slouken@1895
   127
        AuEvent ev;
slouken@1895
   128
        AuNextEvent(this->hidden->aud, AuTrue, &ev);
slouken@1895
   129
        AuDispatchEvent(this->hidden->aud, &ev);
slouken@1895
   130
    }
slouken@1895
   131
    this->hidden->buf_free -= this->hidden->mixlen;
slouken@0
   132
slouken@1895
   133
    /* Write the audio data */
slouken@1895
   134
    AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
slouken@1895
   135
                   this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
slouken@0
   136
slouken@1895
   137
    this->hidden->written += this->hidden->mixlen;
slouken@1895
   138
slouken@0
   139
#ifdef DEBUG_AUDIO
slouken@1895
   140
    fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
slouken@0
   141
#endif
slouken@0
   142
}
slouken@0
   143
slouken@1895
   144
static Uint8 *
slouken@1895
   145
NAS_GetAudioBuf(_THIS)
slouken@0
   146
{
slouken@1895
   147
    return (this->hidden->mixbuf);
slouken@0
   148
}
slouken@0
   149
slouken@1895
   150
static void
slouken@1895
   151
NAS_CloseAudio(_THIS)
slouken@0
   152
{
slouken@1895
   153
    if (this->hidden->mixbuf != NULL) {
slouken@1895
   154
        SDL_FreeAudioMem(this->hidden->mixbuf);
slouken@1895
   155
        this->hidden->mixbuf = NULL;
slouken@1895
   156
    }
slouken@1895
   157
    if (this->hidden->aud) {
slouken@1895
   158
        AuCloseServer(this->hidden->aud);
slouken@1895
   159
        this->hidden->aud = 0;
slouken@1895
   160
    }
slouken@0
   161
}
slouken@0
   162
slouken@1895
   163
static unsigned char
slouken@1895
   164
sdlformat_to_auformat(unsigned int fmt)
slouken@0
   165
{
slouken@1895
   166
    switch (fmt) {
slouken@0
   167
    case AUDIO_U8:
slouken@1895
   168
        return AuFormatLinearUnsigned8;
slouken@0
   169
    case AUDIO_S8:
slouken@1895
   170
        return AuFormatLinearSigned8;
slouken@0
   171
    case AUDIO_U16LSB:
slouken@1895
   172
        return AuFormatLinearUnsigned16LSB;
slouken@0
   173
    case AUDIO_U16MSB:
slouken@1895
   174
        return AuFormatLinearUnsigned16MSB;
slouken@0
   175
    case AUDIO_S16LSB:
slouken@1895
   176
        return AuFormatLinearSigned16LSB;
slouken@0
   177
    case AUDIO_S16MSB:
slouken@1895
   178
        return AuFormatLinearSigned16MSB;
slouken@0
   179
    }
slouken@1895
   180
    return AuNone;
slouken@0
   181
}
slouken@0
   182
slouken@0
   183
static AuBool
slouken@1895
   184
event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
slouken@0
   185
{
slouken@1895
   186
    switch (ev->type) {
slouken@1895
   187
    case AuEventTypeElementNotify:
slouken@1895
   188
        {
slouken@1895
   189
            AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
slouken@0
   190
slouken@1895
   191
            switch (event->kind) {
slouken@1895
   192
            case AuElementNotifyKindLowWater:
slouken@1895
   193
                if (this2->buf_free >= 0) {
slouken@1895
   194
                    this2->really += event->num_bytes;
slouken@1895
   195
                    gettimeofday(&this2->last_tv, 0);
slouken@1895
   196
                    this2->buf_free += event->num_bytes;
slouken@1895
   197
                } else {
slouken@1895
   198
                    this2->buf_free = event->num_bytes;
slouken@1895
   199
                }
slouken@1895
   200
                break;
slouken@1895
   201
            case AuElementNotifyKindState:
slouken@1895
   202
                switch (event->cur_state) {
slouken@1895
   203
                case AuStatePause:
slouken@1895
   204
                    if (event->reason != AuReasonUser) {
slouken@1895
   205
                        if (this2->buf_free >= 0) {
slouken@1895
   206
                            this2->really += event->num_bytes;
slouken@1895
   207
                            gettimeofday(&this2->last_tv, 0);
slouken@1895
   208
                            this2->buf_free += event->num_bytes;
slouken@1895
   209
                        } else {
slouken@1895
   210
                            this2->buf_free = event->num_bytes;
slouken@1895
   211
                        }
slouken@1895
   212
                    }
slouken@1895
   213
                    break;
slouken@1895
   214
                }
slouken@1895
   215
            }
slouken@1895
   216
        }
slouken@1895
   217
    }
slouken@1895
   218
    return AuTrue;
slouken@0
   219
}
slouken@0
   220
slouken@0
   221
static AuDeviceID
slouken@0
   222
find_device(_THIS, int nch)
slouken@0
   223
{
slouken@1895
   224
    int i;
slouken@1895
   225
    for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
slouken@1895
   226
        if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
slouken@1895
   227
             AuComponentKindPhysicalOutput) &&
slouken@1895
   228
            AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
slouken@1895
   229
            return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
slouken@1895
   230
        }
slouken@1895
   231
    }
slouken@1895
   232
    return AuNone;
slouken@0
   233
}
slouken@0
   234
slouken@1895
   235
static int
slouken@1895
   236
NAS_OpenAudio(_THIS, SDL_AudioSpec * spec)
slouken@0
   237
{
slouken@1895
   238
    AuElement elms[3];
slouken@1895
   239
    int buffer_size;
icculus@1982
   240
    SDL_AudioFormat test_format, format;
slouken@1895
   241
slouken@1895
   242
    this->hidden->mixbuf = NULL;
slouken@0
   243
slouken@1895
   244
    /* Try for a closest match on audio format */
slouken@1895
   245
    format = 0;
slouken@1895
   246
    for (test_format = SDL_FirstAudioFormat(spec->format);
slouken@1895
   247
         !format && test_format;) {
slouken@1895
   248
        format = sdlformat_to_auformat(test_format);
slouken@0
   249
slouken@1895
   250
        if (format == AuNone) {
slouken@1895
   251
            test_format = SDL_NextAudioFormat();
slouken@1895
   252
        }
slouken@1895
   253
    }
slouken@1895
   254
    if (format == 0) {
slouken@1895
   255
        SDL_SetError("Couldn't find any hardware audio formats");
slouken@1895
   256
        return (-1);
slouken@1895
   257
    }
slouken@1895
   258
    spec->format = test_format;
slouken@0
   259
slouken@1895
   260
    this->hidden->aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
slouken@1895
   261
    if (this->hidden->aud == 0) {
slouken@1895
   262
        SDL_SetError("Couldn't open connection to NAS server");
slouken@1895
   263
        return (-1);
slouken@1895
   264
    }
slouken@1895
   265
slouken@1895
   266
    this->hidden->dev = find_device(this, spec->channels);
slouken@1895
   267
    if ((this->hidden->dev == AuNone)
slouken@1895
   268
        || (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) {
slouken@1895
   269
        AuCloseServer(this->hidden->aud);
slouken@1895
   270
        this->hidden->aud = 0;
slouken@1895
   271
        SDL_SetError("Couldn't find a fitting playback device on NAS server");
slouken@1895
   272
        return (-1);
slouken@1895
   273
    }
slouken@0
   274
slouken@1895
   275
    buffer_size = spec->freq;
slouken@1895
   276
    if (buffer_size < 4096)
slouken@1895
   277
        buffer_size = 4096;
slouken@0
   278
slouken@1895
   279
    if (buffer_size > 32768)
slouken@1895
   280
        buffer_size = 32768;    /* So that the buffer won't get unmanageably big. */
slouken@0
   281
slouken@1895
   282
    /* Calculate the final parameters for this audio specification */
slouken@1895
   283
    SDL_CalculateAudioSpec(spec);
slouken@1895
   284
slouken@1895
   285
    this2 = this->hidden;
slouken@0
   286
slouken@1895
   287
    AuMakeElementImportClient(elms, spec->freq, format, spec->channels,
slouken@1895
   288
                              AuTrue, buffer_size, buffer_size / 4, 0, NULL);
slouken@1895
   289
    AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, spec->freq,
slouken@1895
   290
                              AuUnlimitedSamples, 0, NULL);
slouken@1895
   291
    AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms,
slouken@1895
   292
                  NULL);
slouken@1895
   293
    AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
slouken@1895
   294
                           this->hidden->flow, event_handler,
slouken@1895
   295
                           (AuPointer) NULL);
slouken@0
   296
slouken@1895
   297
    AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
slouken@0
   298
slouken@1895
   299
    /* Allocate mixing buffer */
slouken@1895
   300
    this->hidden->mixlen = spec->size;
slouken@1895
   301
    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
slouken@1895
   302
    if (this->hidden->mixbuf == NULL) {
slouken@1895
   303
        return (-1);
slouken@1895
   304
    }
slouken@1895
   305
    SDL_memset(this->hidden->mixbuf, spec->silence, spec->size);
slouken@0
   306
slouken@1895
   307
    /* Get the parent process id (we're the parent of the audio thread) */
slouken@1895
   308
    this->hidden->parent = getpid();
slouken@0
   309
slouken@1895
   310
    /* We're ready to rock and roll. :-) */
slouken@1895
   311
    return (0);
slouken@0
   312
}
slouken@1895
   313
slouken@1895
   314
/* vi: set ts=4 sw=4 expandtab: */