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