src/audio/nas/SDL_nasaudio.c
author Ryan C. Gordon
Tue, 26 Jul 2011 14:18:00 -0700
changeset 5594 e741303e08a8
parent 5535 96594ac5fd1a
child 5880 db03ff7a7398
permissions -rw-r--r--
Removed needless macros in various audio targets.
slouken@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@5535
     3
  Copyright (C) 1997-2011 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
*/
slouken@1402
    21
#include "SDL_config.h"
slouken@0
    22
slouken@0
    23
/* Allow access to a raw mixing buffer */
slouken@0
    24
slouken@0
    25
#include <signal.h>
slouken@0
    26
#include <unistd.h>
slouken@0
    27
slouken@1358
    28
#include "SDL_timer.h"
slouken@0
    29
#include "SDL_audio.h"
icculus@2049
    30
#include "SDL_loadso.h"
slouken@1361
    31
#include "../SDL_audiomem.h"
slouken@1361
    32
#include "../SDL_audio_c.h"
slouken@0
    33
#include "SDL_nasaudio.h"
slouken@0
    34
slouken@0
    35
static struct SDL_PrivateAudioData *this2 = NULL;
slouken@0
    36
slouken@0
    37
slouken@2060
    38
static void (*NAS_AuCloseServer) (AuServer *);
slouken@2060
    39
static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *);
slouken@2060
    40
static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *);
slouken@2060
    41
static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *);
slouken@2060
    42
static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *);
icculus@2049
    43
static void (*NAS_AuSetElements)
slouken@2060
    44
  (AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *);
icculus@2049
    45
static void (*NAS_AuWriteElement)
slouken@2060
    46
  (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *);
icculus@2049
    47
static AuServer *(*NAS_AuOpenServer)
slouken@2060
    48
  (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **);
icculus@2049
    49
static AuEventHandlerRec *(*NAS_AuRegisterEventHandler)
slouken@2060
    50
  (AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer);
icculus@2049
    51
icculus@2049
    52
icculus@2049
    53
#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
icculus@2049
    54
icculus@2049
    55
static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC;
icculus@2049
    56
static void *nas_handle = NULL;
slouken@0
    57
slouken@1895
    58
static int
icculus@2049
    59
load_nas_sym(const char *fn, void **addr)
slouken@0
    60
{
icculus@2049
    61
    *addr = SDL_LoadFunction(nas_handle, fn);
icculus@2049
    62
    if (*addr == NULL) {
slouken@1895
    63
        return 0;
icculus@2049
    64
    }
slouken@1895
    65
    return 1;
slouken@0
    66
}
slouken@0
    67
icculus@2049
    68
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
icculus@2049
    69
#define SDL_NAS_SYM(x) \
icculus@2049
    70
    if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1
icculus@2049
    71
#else
icculus@2049
    72
#define SDL_NAS_SYM(x) NAS_##x = x
icculus@2049
    73
#endif
icculus@2049
    74
slouken@2060
    75
static int
slouken@2060
    76
load_nas_syms(void)
slouken@0
    77
{
icculus@2049
    78
    SDL_NAS_SYM(AuCloseServer);
icculus@2049
    79
    SDL_NAS_SYM(AuNextEvent);
icculus@2049
    80
    SDL_NAS_SYM(AuDispatchEvent);
icculus@2049
    81
    SDL_NAS_SYM(AuCreateFlow);
icculus@2049
    82
    SDL_NAS_SYM(AuStartFlow);
icculus@2049
    83
    SDL_NAS_SYM(AuSetElements);
icculus@2049
    84
    SDL_NAS_SYM(AuWriteElement);
icculus@2049
    85
    SDL_NAS_SYM(AuOpenServer);
icculus@2049
    86
    SDL_NAS_SYM(AuRegisterEventHandler);
icculus@2049
    87
    return 0;
icculus@2049
    88
}
slouken@2060
    89
icculus@2049
    90
#undef SDL_NAS_SYM
icculus@2049
    91
icculus@2049
    92
#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
icculus@2049
    93
icculus@2049
    94
static void
icculus@2049
    95
UnloadNASLibrary(void)
icculus@2049
    96
{
icculus@2049
    97
    if (nas_handle != NULL) {
icculus@2049
    98
        SDL_UnloadObject(nas_handle);
icculus@2049
    99
        nas_handle = NULL;
icculus@2049
   100
    }
slouken@0
   101
}
slouken@0
   102
icculus@2049
   103
static int
icculus@2049
   104
LoadNASLibrary(void)
slouken@0
   105
{
icculus@2049
   106
    int retval = 0;
icculus@2049
   107
    if (nas_handle == NULL) {
icculus@2049
   108
        nas_handle = SDL_LoadObject(nas_library);
icculus@2049
   109
        if (nas_handle == NULL) {
icculus@2049
   110
            /* Copy error string so we can use it in a new SDL_SetError(). */
icculus@2049
   111
            char *origerr = SDL_GetError();
icculus@2049
   112
            size_t len = SDL_strlen(origerr) + 1;
icculus@2049
   113
            char *err = (char *) alloca(len);
icculus@2049
   114
            SDL_strlcpy(err, origerr, len);
icculus@2049
   115
            retval = -1;
icculus@2049
   116
            SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s\n",
slouken@2060
   117
                         nas_library, err);
icculus@2049
   118
        } else {
icculus@2049
   119
            retval = load_nas_syms();
icculus@2049
   120
            if (retval < 0) {
icculus@2049
   121
                UnloadNASLibrary();
icculus@2049
   122
            }
icculus@2049
   123
        }
slouken@1895
   124
    }
icculus@2049
   125
    return retval;
slouken@0
   126
}
slouken@0
   127
icculus@2049
   128
#else
icculus@2049
   129
icculus@2049
   130
static void
icculus@2049
   131
UnloadNASLibrary(void)
icculus@2049
   132
{
icculus@2049
   133
}
icculus@2049
   134
icculus@2049
   135
static int
icculus@2049
   136
LoadNASLibrary(void)
icculus@2049
   137
{
icculus@2049
   138
    load_nas_syms();
icculus@2049
   139
    return 0;
icculus@2049
   140
}
icculus@2049
   141
icculus@2049
   142
#endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */
slouken@0
   143
slouken@0
   144
/* This function waits until it is possible to write a full sound buffer */
slouken@1895
   145
static void
icculus@2049
   146
NAS_WaitDevice(_THIS)
slouken@0
   147
{
slouken@1895
   148
    while (this->hidden->buf_free < this->hidden->mixlen) {
slouken@1895
   149
        AuEvent ev;
icculus@2049
   150
        NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
icculus@2049
   151
        NAS_AuDispatchEvent(this->hidden->aud, &ev);
slouken@1895
   152
    }
slouken@0
   153
}
slouken@0
   154
slouken@1895
   155
static void
icculus@2049
   156
NAS_PlayDevice(_THIS)
slouken@0
   157
{
icculus@2049
   158
    while (this->hidden->mixlen > this->hidden->buf_free) {
icculus@2049
   159
        /*
icculus@2049
   160
         * We think the buffer is full? Yikes! Ask the server for events,
icculus@2049
   161
         *  in the hope that some of them is LowWater events telling us more
icculus@2049
   162
         *  of the buffer is free now than what we think.
icculus@2049
   163
         */
slouken@1895
   164
        AuEvent ev;
icculus@2049
   165
        NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
icculus@2049
   166
        NAS_AuDispatchEvent(this->hidden->aud, &ev);
slouken@1895
   167
    }
slouken@1895
   168
    this->hidden->buf_free -= this->hidden->mixlen;
slouken@0
   169
slouken@1895
   170
    /* Write the audio data */
icculus@2049
   171
    NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
slouken@2060
   172
                       this->hidden->mixlen, this->hidden->mixbuf, AuFalse,
slouken@2060
   173
                       NULL);
slouken@0
   174
slouken@1895
   175
    this->hidden->written += this->hidden->mixlen;
slouken@1895
   176
slouken@0
   177
#ifdef DEBUG_AUDIO
slouken@1895
   178
    fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
slouken@0
   179
#endif
slouken@0
   180
}
slouken@0
   181
slouken@1895
   182
static Uint8 *
icculus@2049
   183
NAS_GetDeviceBuf(_THIS)
slouken@0
   184
{
slouken@1895
   185
    return (this->hidden->mixbuf);
slouken@0
   186
}
slouken@0
   187
slouken@1895
   188
static void
icculus@2049
   189
NAS_CloseDevice(_THIS)
slouken@0
   190
{
icculus@2049
   191
    if (this->hidden != NULL) {
icculus@2049
   192
        if (this->hidden->mixbuf != NULL) {
icculus@2049
   193
            SDL_FreeAudioMem(this->hidden->mixbuf);
icculus@2049
   194
            this->hidden->mixbuf = NULL;
icculus@2049
   195
        }
icculus@2049
   196
        if (this->hidden->aud) {
icculus@2049
   197
            NAS_AuCloseServer(this->hidden->aud);
icculus@2049
   198
            this->hidden->aud = 0;
icculus@2049
   199
        }
icculus@2049
   200
        SDL_free(this->hidden);
icculus@2049
   201
        this2 = this->hidden = NULL;
slouken@1895
   202
    }
slouken@0
   203
}
slouken@0
   204
slouken@1895
   205
static unsigned char
slouken@1895
   206
sdlformat_to_auformat(unsigned int fmt)
slouken@0
   207
{
slouken@1895
   208
    switch (fmt) {
slouken@0
   209
    case AUDIO_U8:
slouken@1895
   210
        return AuFormatLinearUnsigned8;
slouken@0
   211
    case AUDIO_S8:
slouken@1895
   212
        return AuFormatLinearSigned8;
slouken@0
   213
    case AUDIO_U16LSB:
slouken@1895
   214
        return AuFormatLinearUnsigned16LSB;
slouken@0
   215
    case AUDIO_U16MSB:
slouken@1895
   216
        return AuFormatLinearUnsigned16MSB;
slouken@0
   217
    case AUDIO_S16LSB:
slouken@1895
   218
        return AuFormatLinearSigned16LSB;
slouken@0
   219
    case AUDIO_S16MSB:
slouken@1895
   220
        return AuFormatLinearSigned16MSB;
slouken@0
   221
    }
slouken@1895
   222
    return AuNone;
slouken@0
   223
}
slouken@0
   224
slouken@0
   225
static AuBool
slouken@1895
   226
event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
slouken@0
   227
{
slouken@1895
   228
    switch (ev->type) {
slouken@1895
   229
    case AuEventTypeElementNotify:
slouken@1895
   230
        {
slouken@1895
   231
            AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
slouken@0
   232
slouken@1895
   233
            switch (event->kind) {
slouken@1895
   234
            case AuElementNotifyKindLowWater:
slouken@1895
   235
                if (this2->buf_free >= 0) {
slouken@1895
   236
                    this2->really += event->num_bytes;
slouken@1895
   237
                    gettimeofday(&this2->last_tv, 0);
slouken@1895
   238
                    this2->buf_free += event->num_bytes;
slouken@1895
   239
                } else {
slouken@1895
   240
                    this2->buf_free = event->num_bytes;
slouken@1895
   241
                }
slouken@1895
   242
                break;
slouken@1895
   243
            case AuElementNotifyKindState:
slouken@1895
   244
                switch (event->cur_state) {
slouken@1895
   245
                case AuStatePause:
slouken@1895
   246
                    if (event->reason != AuReasonUser) {
slouken@1895
   247
                        if (this2->buf_free >= 0) {
slouken@1895
   248
                            this2->really += event->num_bytes;
slouken@1895
   249
                            gettimeofday(&this2->last_tv, 0);
slouken@1895
   250
                            this2->buf_free += event->num_bytes;
slouken@1895
   251
                        } else {
slouken@1895
   252
                            this2->buf_free = event->num_bytes;
slouken@1895
   253
                        }
slouken@1895
   254
                    }
slouken@1895
   255
                    break;
slouken@1895
   256
                }
slouken@1895
   257
            }
slouken@1895
   258
        }
slouken@1895
   259
    }
slouken@1895
   260
    return AuTrue;
slouken@0
   261
}
slouken@0
   262
slouken@0
   263
static AuDeviceID
slouken@0
   264
find_device(_THIS, int nch)
slouken@0
   265
{
icculus@2049
   266
    /* These "Au" things are all macros, not functions... */
slouken@1895
   267
    int i;
slouken@1895
   268
    for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
slouken@1895
   269
        if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
slouken@1895
   270
             AuComponentKindPhysicalOutput) &&
slouken@1895
   271
            AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
slouken@1895
   272
            return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
slouken@1895
   273
        }
slouken@1895
   274
    }
slouken@1895
   275
    return AuNone;
slouken@0
   276
}
slouken@0
   277
slouken@1895
   278
static int
icculus@2049
   279
NAS_OpenDevice(_THIS, const char *devname, int iscapture)
slouken@0
   280
{
slouken@1895
   281
    AuElement elms[3];
slouken@1895
   282
    int buffer_size;
icculus@1982
   283
    SDL_AudioFormat test_format, format;
slouken@1895
   284
icculus@2049
   285
    /* Initialize all variables that we clean on shutdown */
icculus@2049
   286
    this->hidden = (struct SDL_PrivateAudioData *)
slouken@2060
   287
        SDL_malloc((sizeof *this->hidden));
icculus@2049
   288
    if (this->hidden == NULL) {
icculus@2049
   289
        SDL_OutOfMemory();
icculus@2049
   290
        return 0;
icculus@2049
   291
    }
icculus@2049
   292
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
slouken@0
   293
slouken@1895
   294
    /* Try for a closest match on audio format */
slouken@1895
   295
    format = 0;
icculus@2049
   296
    for (test_format = SDL_FirstAudioFormat(this->spec.format);
slouken@1895
   297
         !format && test_format;) {
slouken@1895
   298
        format = sdlformat_to_auformat(test_format);
slouken@1895
   299
        if (format == AuNone) {
slouken@1895
   300
            test_format = SDL_NextAudioFormat();
slouken@1895
   301
        }
slouken@1895
   302
    }
slouken@1895
   303
    if (format == 0) {
icculus@2049
   304
        NAS_CloseDevice(this);
icculus@2049
   305
        SDL_SetError("NAS: Couldn't find any hardware audio formats");
icculus@2049
   306
        return 0;
slouken@1895
   307
    }
icculus@2049
   308
    this->spec.format = test_format;
slouken@0
   309
icculus@2049
   310
    this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
slouken@1895
   311
    if (this->hidden->aud == 0) {
icculus@2049
   312
        NAS_CloseDevice(this);
icculus@2049
   313
        SDL_SetError("NAS: Couldn't open connection to NAS server");
icculus@2049
   314
        return 0;
slouken@1895
   315
    }
slouken@1895
   316
icculus@2049
   317
    this->hidden->dev = find_device(this, this->spec.channels);
slouken@1895
   318
    if ((this->hidden->dev == AuNone)
icculus@2049
   319
        || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
icculus@2049
   320
        NAS_CloseDevice(this);
icculus@2049
   321
        SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
icculus@2049
   322
        return 0;
slouken@1895
   323
    }
slouken@0
   324
icculus@2049
   325
    buffer_size = this->spec.freq;
slouken@1895
   326
    if (buffer_size < 4096)
slouken@1895
   327
        buffer_size = 4096;
slouken@0
   328
slouken@1895
   329
    if (buffer_size > 32768)
slouken@1895
   330
        buffer_size = 32768;    /* So that the buffer won't get unmanageably big. */
slouken@0
   331
slouken@1895
   332
    /* Calculate the final parameters for this audio specification */
icculus@2049
   333
    SDL_CalculateAudioSpec(&this->spec);
slouken@1895
   334
slouken@1895
   335
    this2 = this->hidden;
slouken@0
   336
slouken@2060
   337
    AuMakeElementImportClient(elms, this->spec.freq, format,
slouken@2060
   338
                              this->spec.channels, AuTrue, buffer_size,
slouken@2060
   339
                              buffer_size / 4, 0, NULL);
icculus@2049
   340
    AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
slouken@1895
   341
                              AuUnlimitedSamples, 0, NULL);
slouken@2060
   342
    NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms,
slouken@2060
   343
                      NULL);
icculus@2049
   344
    NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
icculus@2049
   345
                               this->hidden->flow, event_handler,
icculus@2049
   346
                               (AuPointer) NULL);
slouken@0
   347
icculus@2049
   348
    NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
slouken@0
   349
slouken@1895
   350
    /* Allocate mixing buffer */
icculus@2049
   351
    this->hidden->mixlen = this->spec.size;
slouken@1895
   352
    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
slouken@1895
   353
    if (this->hidden->mixbuf == NULL) {
icculus@2049
   354
        NAS_CloseDevice(this);
icculus@2049
   355
        SDL_OutOfMemory();
icculus@2049
   356
        return 0;
slouken@1895
   357
    }
icculus@2049
   358
    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
slouken@0
   359
slouken@1895
   360
    /* We're ready to rock and roll. :-) */
icculus@2049
   361
    return 1;
icculus@2049
   362
}
icculus@2049
   363
icculus@2049
   364
static void
icculus@2049
   365
NAS_Deinitialize(void)
icculus@2049
   366
{
icculus@2049
   367
    UnloadNASLibrary();
slouken@0
   368
}
slouken@1895
   369
icculus@2049
   370
static int
slouken@2060
   371
NAS_Init(SDL_AudioDriverImpl * impl)
icculus@2049
   372
{
icculus@2049
   373
    if (LoadNASLibrary() < 0) {
icculus@2049
   374
        return 0;
icculus@2049
   375
    } else {
icculus@2049
   376
        AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
icculus@2049
   377
        if (aud == NULL) {
icculus@2049
   378
            SDL_SetError("NAS: AuOpenServer() failed (no audio server?)");
icculus@2049
   379
            return 0;
icculus@2049
   380
        }
icculus@2049
   381
        NAS_AuCloseServer(aud);
icculus@2049
   382
    }
icculus@2049
   383
icculus@2049
   384
    /* Set the function pointers */
icculus@2049
   385
    impl->OpenDevice = NAS_OpenDevice;
icculus@2049
   386
    impl->PlayDevice = NAS_PlayDevice;
icculus@2049
   387
    impl->WaitDevice = NAS_WaitDevice;
icculus@2049
   388
    impl->GetDeviceBuf = NAS_GetDeviceBuf;
icculus@2049
   389
    impl->CloseDevice = NAS_CloseDevice;
icculus@2049
   390
    impl->Deinitialize = NAS_Deinitialize;
slouken@2060
   391
    impl->OnlyHasDefaultOutputDevice = 1;       /* !!! FIXME: is this true? */
icculus@2049
   392
icculus@3699
   393
    return 1;   /* this audio target is available. */
icculus@2049
   394
}
icculus@2049
   395
icculus@2049
   396
AudioBootStrap NAS_bootstrap = {
icculus@5594
   397
    "nas", "Network Audio System", NAS_Init, 0
icculus@2049
   398
};
icculus@2049
   399
slouken@1895
   400
/* vi: set ts=4 sw=4 expandtab: */