src/audio/SDL_audio.c
author Ryan C. Gordon
Thu, 19 Mar 2015 13:27:10 -0400
changeset 9397 d72d2aa46341
parent 9396 69c501ed36f3
child 9398 c41dd34e4996
permissions -rw-r--r--
Disconnected/broken/lost audio devices now continue to fire their callback.

The data produced by the callback is just thrown away and the audio thread
delays as if it's waiting for the hardware to drain.

This lets apps that rely on their audio callback firing regularly continue
to make progress to function as properly as possible in the face of disaster.
Apps that want to know that the device is really gone and deal with that
scenario can use the new hotplug functionality.
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@0
    23
/* Allow access to a raw mixing buffer */
slouken@0
    24
slouken@0
    25
#include "SDL.h"
slouken@2984
    26
#include "SDL_audio.h"
slouken@0
    27
#include "SDL_audio_c.h"
slouken@0
    28
#include "SDL_audiomem.h"
slouken@0
    29
#include "SDL_sysaudio.h"
slouken@0
    30
aschiffler@4865
    31
#define _THIS SDL_AudioDevice *_this
icculus@2049
    32
icculus@2049
    33
static SDL_AudioDriver current_audio;
icculus@2049
    34
static SDL_AudioDevice *open_devices[16];
icculus@2049
    35
icculus@2049
    36
/* !!! FIXME: These are wordy and unlocalized... */
icculus@2049
    37
#define DEFAULT_OUTPUT_DEVNAME "System audio output device"
icculus@2049
    38
#define DEFAULT_INPUT_DEVNAME "System audio capture device"
icculus@2049
    39
icculus@2049
    40
icculus@2049
    41
/*
icculus@2049
    42
 * Not all of these will be compiled and linked in, but it's convenient
icculus@2049
    43
 *  to have a complete list here and saves yet-another block of #ifdefs...
icculus@2049
    44
 *  Please see bootstrap[], below, for the actual #ifdef mess.
icculus@2049
    45
 */
icculus@2049
    46
extern AudioBootStrap BSD_AUDIO_bootstrap;
icculus@2049
    47
extern AudioBootStrap DSP_bootstrap;
icculus@2049
    48
extern AudioBootStrap ALSA_bootstrap;
icculus@2271
    49
extern AudioBootStrap PULSEAUDIO_bootstrap;
slouken@3099
    50
extern AudioBootStrap QSAAUDIO_bootstrap;
icculus@2049
    51
extern AudioBootStrap SUNAUDIO_bootstrap;
icculus@2049
    52
extern AudioBootStrap ARTS_bootstrap;
icculus@2049
    53
extern AudioBootStrap ESD_bootstrap;
gabomdq@8833
    54
extern AudioBootStrap NACLAUD_bootstrap;
icculus@2049
    55
extern AudioBootStrap NAS_bootstrap;
icculus@5592
    56
extern AudioBootStrap XAUDIO2_bootstrap;
icculus@2049
    57
extern AudioBootStrap DSOUND_bootstrap;
icculus@5588
    58
extern AudioBootStrap WINMM_bootstrap;
icculus@2049
    59
extern AudioBootStrap PAUDIO_bootstrap;
icculus@7981
    60
extern AudioBootStrap HAIKUAUDIO_bootstrap;
icculus@2049
    61
extern AudioBootStrap COREAUDIO_bootstrap;
icculus@2049
    62
extern AudioBootStrap SNDMGR_bootstrap;
icculus@2049
    63
extern AudioBootStrap DISKAUD_bootstrap;
icculus@2049
    64
extern AudioBootStrap DUMMYAUD_bootstrap;
icculus@2049
    65
extern AudioBootStrap DCAUD_bootstrap;
icculus@2049
    66
extern AudioBootStrap DART_bootstrap;
slouken@2735
    67
extern AudioBootStrap NDSAUD_bootstrap;
slouken@2947
    68
extern AudioBootStrap FUSIONSOUND_bootstrap;
paul@4718
    69
extern AudioBootStrap ANDROIDAUD_bootstrap;
kimonline@7009
    70
extern AudioBootStrap PSPAUD_bootstrap;
icculus@7367
    71
extern AudioBootStrap SNDIO_bootstrap;
icculus@9278
    72
extern AudioBootStrap EmscriptenAudio_bootstrap;
icculus@1190
    73
gabomdq@8833
    74
slouken@0
    75
/* Available audio drivers */
slouken@3162
    76
static const AudioBootStrap *const bootstrap[] = {
icculus@2939
    77
#if SDL_AUDIO_DRIVER_PULSEAUDIO
icculus@2939
    78
    &PULSEAUDIO_bootstrap,
slouken@0
    79
#endif
slouken@1361
    80
#if SDL_AUDIO_DRIVER_ALSA
slouken@1895
    81
    &ALSA_bootstrap,
slouken@0
    82
#endif
icculus@7367
    83
#if SDL_AUDIO_DRIVER_SNDIO
icculus@7367
    84
    &SNDIO_bootstrap,
icculus@7367
    85
#endif
slouken@4548
    86
#if SDL_AUDIO_DRIVER_BSD
slouken@4548
    87
    &BSD_AUDIO_bootstrap,
slouken@4548
    88
#endif
icculus@2939
    89
#if SDL_AUDIO_DRIVER_OSS
icculus@2939
    90
    &DSP_bootstrap,
icculus@2271
    91
#endif
slouken@3099
    92
#if SDL_AUDIO_DRIVER_QSA
slouken@3099
    93
    &QSAAUDIO_bootstrap,
slouken@663
    94
#endif
slouken@1361
    95
#if SDL_AUDIO_DRIVER_SUNAUDIO
slouken@1895
    96
    &SUNAUDIO_bootstrap,
slouken@148
    97
#endif
slouken@1361
    98
#if SDL_AUDIO_DRIVER_ARTS
slouken@1895
    99
    &ARTS_bootstrap,
slouken@0
   100
#endif
slouken@1361
   101
#if SDL_AUDIO_DRIVER_ESD
slouken@1895
   102
    &ESD_bootstrap,
slouken@0
   103
#endif
gabomdq@8833
   104
#if SDL_AUDIO_DRIVER_NACL
gabomdq@8833
   105
   &NACLAUD_bootstrap,
gabomdq@8833
   106
#endif
slouken@1361
   107
#if SDL_AUDIO_DRIVER_NAS
slouken@1895
   108
    &NAS_bootstrap,
slouken@0
   109
#endif
icculus@5592
   110
#if SDL_AUDIO_DRIVER_XAUDIO2
icculus@5592
   111
    &XAUDIO2_bootstrap,
icculus@5592
   112
#endif
slouken@1361
   113
#if SDL_AUDIO_DRIVER_DSOUND
slouken@1895
   114
    &DSOUND_bootstrap,
slouken@0
   115
#endif
icculus@5588
   116
#if SDL_AUDIO_DRIVER_WINMM
icculus@5588
   117
    &WINMM_bootstrap,
slouken@0
   118
#endif
icculus@2049
   119
#if SDL_AUDIO_DRIVER_PAUDIO
icculus@2049
   120
    &PAUDIO_bootstrap,
slouken@1361
   121
#endif
icculus@7981
   122
#if SDL_AUDIO_DRIVER_HAIKU
icculus@7981
   123
    &HAIKUAUDIO_bootstrap,
slouken@0
   124
#endif
slouken@1361
   125
#if SDL_AUDIO_DRIVER_COREAUDIO
slouken@1895
   126
    &COREAUDIO_bootstrap,
slouken@935
   127
#endif
slouken@1361
   128
#if SDL_AUDIO_DRIVER_DISK
slouken@1895
   129
    &DISKAUD_bootstrap,
slouken@68
   130
#endif
icculus@1532
   131
#if SDL_AUDIO_DRIVER_DUMMY
slouken@1895
   132
    &DUMMYAUD_bootstrap,
icculus@1532
   133
#endif
slouken@2947
   134
#if SDL_AUDIO_DRIVER_FUSIONSOUND
slouken@2947
   135
    &FUSIONSOUND_bootstrap,
slouken@2947
   136
#endif
paul@4718
   137
#if SDL_AUDIO_DRIVER_ANDROID
paul@4718
   138
    &ANDROIDAUD_bootstrap,
paul@4718
   139
#endif
kimonline@7009
   140
#if SDL_AUDIO_DRIVER_PSP
kimonline@7009
   141
    &PSPAUD_bootstrap,
kimonline@7009
   142
#endif
icculus@9278
   143
#if SDL_AUDIO_DRIVER_EMSCRIPTEN
icculus@9278
   144
    &EmscriptenAudio_bootstrap,
icculus@9278
   145
#endif
slouken@1895
   146
    NULL
slouken@0
   147
};
icculus@2049
   148
slouken@2060
   149
static SDL_AudioDevice *
slouken@2060
   150
get_audio_device(SDL_AudioDeviceID id)
icculus@2049
   151
{
icculus@2049
   152
    id--;
slouken@2060
   153
    if ((id >= SDL_arraysize(open_devices)) || (open_devices[id] == NULL)) {
icculus@2049
   154
        SDL_SetError("Invalid audio device ID");
icculus@2049
   155
        return NULL;
icculus@2049
   156
    }
icculus@2049
   157
icculus@2049
   158
    return open_devices[id];
icculus@2049
   159
}
icculus@2049
   160
icculus@2049
   161
icculus@2049
   162
/* stubs for audio drivers that don't need a specific entry point... */
icculus@5593
   163
static void
icculus@9394
   164
SDL_AudioDetectDevices_Default(void)
icculus@9394
   165
{
icculus@9394
   166
    /* you have to write your own implementation if these assertions fail. */
icculus@9394
   167
    SDL_assert(current_audio.impl.OnlyHasDefaultOutputDevice);
icculus@9394
   168
    SDL_assert(current_audio.impl.OnlyHasDefaultInputDevice || !current_audio.impl.HasCaptureSupport);
icculus@9394
   169
icculus@9394
   170
    SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, (void *) ((size_t) 0x1));
icculus@9394
   171
    if (current_audio.impl.HasCaptureSupport) {
icculus@9394
   172
        SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, (void *) ((size_t) 0x2));
icculus@9394
   173
    }
slouken@2060
   174
}
slouken@2735
   175
slouken@2060
   176
static void
slouken@2060
   177
SDL_AudioThreadInit_Default(_THIS)
slouken@2060
   178
{                               /* no-op. */
slouken@2060
   179
}
slouken@2735
   180
slouken@2060
   181
static void
slouken@2060
   182
SDL_AudioWaitDevice_Default(_THIS)
slouken@2060
   183
{                               /* no-op. */
slouken@2060
   184
}
slouken@2735
   185
slouken@2060
   186
static void
slouken@2060
   187
SDL_AudioPlayDevice_Default(_THIS)
slouken@2060
   188
{                               /* no-op. */
slouken@2060
   189
}
slouken@2735
   190
icculus@9031
   191
static int
icculus@9031
   192
SDL_AudioGetPendingBytes_Default(_THIS)
icculus@9031
   193
{
icculus@9031
   194
    return 0;
icculus@9031
   195
}
icculus@9031
   196
slouken@2060
   197
static Uint8 *
slouken@2060
   198
SDL_AudioGetDeviceBuf_Default(_THIS)
slouken@2060
   199
{
slouken@2060
   200
    return NULL;
slouken@2060
   201
}
slouken@2735
   202
slouken@2060
   203
static void
slouken@2060
   204
SDL_AudioWaitDone_Default(_THIS)
slouken@2060
   205
{                               /* no-op. */
slouken@2060
   206
}
slouken@2735
   207
slouken@2060
   208
static void
slouken@2060
   209
SDL_AudioCloseDevice_Default(_THIS)
slouken@2060
   210
{                               /* no-op. */
slouken@2060
   211
}
slouken@2735
   212
slouken@2060
   213
static void
slouken@2060
   214
SDL_AudioDeinitialize_Default(void)
slouken@2060
   215
{                               /* no-op. */
slouken@2060
   216
}
icculus@2049
   217
icculus@9394
   218
static void
icculus@9394
   219
SDL_AudioFreeDeviceHandle_Default(void *handle)
icculus@9394
   220
{                               /* no-op. */
icculus@9394
   221
}
icculus@9394
   222
icculus@9394
   223
icculus@2049
   224
static int
icculus@9394
   225
SDL_AudioOpenDevice_Default(_THIS, void *handle, const char *devname, int iscapture)
icculus@2049
   226
{
icculus@9394
   227
    return SDL_Unsupported();
icculus@2049
   228
}
icculus@2049
   229
icculus@9010
   230
static SDL_INLINE SDL_bool
icculus@9010
   231
is_in_audio_device_thread(SDL_AudioDevice * device)
icculus@9010
   232
{
icculus@9010
   233
    /* The device thread locks the same mutex, but not through the public API.
icculus@9010
   234
       This check is in case the application, in the audio callback,
icculus@9010
   235
       tries to lock the thread that we've already locked from the
icculus@9010
   236
       device thread...just in case we only have non-recursive mutexes. */
icculus@9010
   237
    if (device->thread && (SDL_ThreadID() == device->threadid)) {
icculus@9010
   238
        return SDL_TRUE;
icculus@9010
   239
    }
icculus@9010
   240
icculus@9010
   241
    return SDL_FALSE;
icculus@9010
   242
}
icculus@9010
   243
icculus@2049
   244
static void
icculus@2049
   245
SDL_AudioLockDevice_Default(SDL_AudioDevice * device)
icculus@2049
   246
{
icculus@9010
   247
    if (!is_in_audio_device_thread(device)) {
icculus@9010
   248
        SDL_LockMutex(device->mixer_lock);
icculus@2049
   249
    }
icculus@2049
   250
}
icculus@2049
   251
icculus@2049
   252
static void
icculus@2049
   253
SDL_AudioUnlockDevice_Default(SDL_AudioDevice * device)
icculus@2049
   254
{
icculus@9010
   255
    if (!is_in_audio_device_thread(device)) {
icculus@9010
   256
        SDL_UnlockMutex(device->mixer_lock);
icculus@2049
   257
    }
icculus@2049
   258
}
icculus@2049
   259
slouken@0
   260
slouken@2060
   261
static void
slouken@2060
   262
finalize_audio_entry_points(void)
icculus@2049
   263
{
icculus@2049
   264
    /*
icculus@2049
   265
     * Fill in stub functions for unused driver entry points. This lets us
icculus@2049
   266
     *  blindly call them without having to check for validity first.
icculus@2049
   267
     */
icculus@2049
   268
slouken@2060
   269
#define FILL_STUB(x) \
icculus@2049
   270
        if (current_audio.impl.x == NULL) { \
icculus@2049
   271
            current_audio.impl.x = SDL_Audio##x##_Default; \
icculus@2049
   272
        }
icculus@2049
   273
    FILL_STUB(DetectDevices);
icculus@2049
   274
    FILL_STUB(OpenDevice);
icculus@2049
   275
    FILL_STUB(ThreadInit);
icculus@2049
   276
    FILL_STUB(WaitDevice);
icculus@2049
   277
    FILL_STUB(PlayDevice);
icculus@9031
   278
    FILL_STUB(GetPendingBytes);
icculus@2049
   279
    FILL_STUB(GetDeviceBuf);
icculus@2049
   280
    FILL_STUB(WaitDone);
icculus@2049
   281
    FILL_STUB(CloseDevice);
icculus@2049
   282
    FILL_STUB(LockDevice);
icculus@2049
   283
    FILL_STUB(UnlockDevice);
icculus@9394
   284
    FILL_STUB(FreeDeviceHandle);
icculus@2049
   285
    FILL_STUB(Deinitialize);
slouken@2060
   286
#undef FILL_STUB
icculus@2049
   287
}
icculus@2049
   288
icculus@8660
   289
#if 0  /* !!! FIXME: rewrite/remove this streamer code. */
slouken@2716
   290
/* Streaming functions (for when the input and output buffer sizes are different) */
slouken@2716
   291
/* Write [length] bytes from buf into the streamer */
slouken@4472
   292
static void
slouken@2716
   293
SDL_StreamWrite(SDL_AudioStreamer * stream, Uint8 * buf, int length)
slouken@2716
   294
{
slouken@2716
   295
    int i;
slouken@2716
   296
slouken@2716
   297
    for (i = 0; i < length; ++i) {
slouken@2716
   298
        stream->buffer[stream->write_pos] = buf[i];
slouken@2716
   299
        ++stream->write_pos;
slouken@2716
   300
    }
slouken@2716
   301
}
slouken@2716
   302
slouken@2716
   303
/* Read [length] bytes out of the streamer into buf */
slouken@4472
   304
static void
slouken@2716
   305
SDL_StreamRead(SDL_AudioStreamer * stream, Uint8 * buf, int length)
slouken@2716
   306
{
slouken@2716
   307
    int i;
slouken@2716
   308
slouken@2716
   309
    for (i = 0; i < length; ++i) {
slouken@2716
   310
        buf[i] = stream->buffer[stream->read_pos];
slouken@2716
   311
        ++stream->read_pos;
slouken@2716
   312
    }
slouken@2716
   313
}
slouken@2716
   314
slouken@4472
   315
static int
slouken@2716
   316
SDL_StreamLength(SDL_AudioStreamer * stream)
slouken@2716
   317
{
slouken@2716
   318
    return (stream->write_pos - stream->read_pos) % stream->max_len;
slouken@2716
   319
}
slouken@2716
   320
slouken@2716
   321
/* Initialize the stream by allocating the buffer and setting the read/write heads to the beginning */
slouken@4507
   322
#if 0
slouken@4472
   323
static int
slouken@2716
   324
SDL_StreamInit(SDL_AudioStreamer * stream, int max_len, Uint8 silence)
slouken@2716
   325
{
slouken@2716
   326
    /* First try to allocate the buffer */
slouken@2728
   327
    stream->buffer = (Uint8 *) SDL_malloc(max_len);
slouken@2716
   328
    if (stream->buffer == NULL) {
slouken@2716
   329
        return -1;
slouken@2716
   330
    }
slouken@2716
   331
slouken@2716
   332
    stream->max_len = max_len;
slouken@2716
   333
    stream->read_pos = 0;
slouken@2716
   334
    stream->write_pos = 0;
slouken@2716
   335
slouken@2716
   336
    /* Zero out the buffer */
slouken@2728
   337
    SDL_memset(stream->buffer, silence, max_len);
slouken@2728
   338
slouken@2728
   339
    return 0;
slouken@2716
   340
}
slouken@4507
   341
#endif
slouken@2716
   342
slouken@2716
   343
/* Deinitialize the stream simply by freeing the buffer */
slouken@4472
   344
static void
slouken@2716
   345
SDL_StreamDeinit(SDL_AudioStreamer * stream)
slouken@2716
   346
{
slouken@7719
   347
    SDL_free(stream->buffer);
slouken@2716
   348
}
icculus@8660
   349
#endif
slouken@2716
   350
icculus@9393
   351
/* device hotplug support... */
icculus@9393
   352
icculus@9393
   353
static int
icculus@9394
   354
add_audio_device(const char *name, void *handle, SDL_AudioDeviceItem **devices, int *devCount)
icculus@9393
   355
{
icculus@9393
   356
    int retval = -1;
icculus@9394
   357
    const size_t size = sizeof (SDL_AudioDeviceItem) + SDL_strlen(name) + 1;
icculus@9394
   358
    SDL_AudioDeviceItem *item = (SDL_AudioDeviceItem *) SDL_malloc(size);
icculus@9394
   359
    if (item == NULL) {
icculus@9394
   360
        return -1;
icculus@9394
   361
    }
icculus@9393
   362
icculus@9394
   363
    SDL_assert(handle != NULL);
icculus@9394
   364
icculus@9394
   365
    item->handle = handle;
icculus@9394
   366
    SDL_strlcpy(item->name, name, size - sizeof (SDL_AudioDeviceItem));
icculus@9394
   367
icculus@9394
   368
    SDL_LockMutex(current_audio.detectionLock);
icculus@9394
   369
    item->next = *devices;
icculus@9394
   370
    *devices = item;
icculus@9394
   371
    retval = (*devCount)++;
icculus@9394
   372
    SDL_UnlockMutex(current_audio.detectionLock);
icculus@9393
   373
icculus@9393
   374
    return retval;
icculus@9393
   375
}
icculus@9393
   376
icculus@9394
   377
static SDL_INLINE int
icculus@9394
   378
add_capture_device(const char *name, void *handle)
icculus@9393
   379
{
icculus@9393
   380
    /* !!! FIXME: add this later. SDL_assert(current_audio.impl.HasCaptureSupport);*/
icculus@9394
   381
    return add_audio_device(name, handle, &current_audio.inputDevices, &current_audio.inputDeviceCount);
icculus@9393
   382
}
icculus@9393
   383
icculus@9394
   384
static SDL_INLINE int
icculus@9394
   385
add_output_device(const char *name, void *handle)
icculus@9393
   386
{
icculus@9394
   387
    return add_audio_device(name, handle, &current_audio.outputDevices, &current_audio.outputDeviceCount);
icculus@9393
   388
}
icculus@9393
   389
icculus@9393
   390
static void
icculus@9394
   391
free_device_list(SDL_AudioDeviceItem **devices, int *devCount)
icculus@9393
   392
{
icculus@9394
   393
    SDL_AudioDeviceItem *item, *next;
icculus@9394
   394
    for (item = *devices; item != NULL; item = next) {
icculus@9394
   395
        next = item->next;
icculus@9394
   396
        if (item->handle != NULL) {
icculus@9394
   397
            current_audio.impl.FreeDeviceHandle(item->handle);
icculus@9393
   398
        }
icculus@9394
   399
        SDL_free(item);
icculus@9393
   400
    }
icculus@9393
   401
    *devices = NULL;
icculus@9393
   402
    *devCount = 0;
icculus@9393
   403
}
icculus@9393
   404
icculus@9393
   405
icculus@9393
   406
/* The audio backends call this when a new device is plugged in. */
icculus@9393
   407
void
icculus@9394
   408
SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
icculus@9393
   409
{
icculus@9394
   410
    const int device_index = iscapture ? add_capture_device(name, handle) : add_output_device(name, handle);
icculus@9393
   411
    if (device_index != -1) {
icculus@9393
   412
        /* Post the event, if desired */
icculus@9393
   413
        if (SDL_GetEventState(SDL_AUDIODEVICEADDED) == SDL_ENABLE) {
icculus@9393
   414
            SDL_Event event;
icculus@9393
   415
            event.adevice.type = SDL_AUDIODEVICEADDED;
icculus@9393
   416
            event.adevice.which = device_index;
icculus@9393
   417
            event.adevice.iscapture = iscapture;
icculus@9393
   418
            SDL_PushEvent(&event);
icculus@9393
   419
        }
icculus@9393
   420
    }
icculus@9393
   421
}
icculus@9393
   422
icculus@9394
   423
/* The audio backends call this when a currently-opened device is lost. */
icculus@9394
   424
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
icculus@9393
   425
{
icculus@9394
   426
    SDL_assert(get_audio_device(device->id) == device);
icculus@9396
   427
icculus@9396
   428
    if (!device->enabled) {
icculus@9396
   429
        return;
icculus@9396
   430
    }
icculus@9394
   431
icculus@9394
   432
    /* Ends the audio callback and mark the device as STOPPED, but the
icculus@9394
   433
       app still needs to close the device to free resources. */
icculus@9394
   434
    current_audio.impl.LockDevice(device);
icculus@9394
   435
    device->enabled = 0;
icculus@9394
   436
    current_audio.impl.UnlockDevice(device);
icculus@9393
   437
icculus@9394
   438
    /* Post the event, if desired */
icculus@9394
   439
    if (SDL_GetEventState(SDL_AUDIODEVICEREMOVED) == SDL_ENABLE) {
icculus@9394
   440
        SDL_Event event;
icculus@9394
   441
        event.adevice.type = SDL_AUDIODEVICEREMOVED;
icculus@9394
   442
        event.adevice.which = device->id;
icculus@9394
   443
        event.adevice.iscapture = device->iscapture ? 1 : 0;
icculus@9394
   444
        SDL_PushEvent(&event);
icculus@9394
   445
    }
icculus@9394
   446
}
icculus@9393
   447
icculus@9394
   448
static void
icculus@9394
   449
mark_device_removed(void *handle, SDL_AudioDeviceItem *devices, SDL_bool *removedFlag)
icculus@9394
   450
{
icculus@9394
   451
    SDL_AudioDeviceItem *item;
icculus@9394
   452
    SDL_assert(handle != NULL);
icculus@9394
   453
    for (item = devices; item != NULL; item = item->next) {
icculus@9394
   454
        if (item->handle == handle) {
icculus@9394
   455
            item->handle = NULL;
icculus@9394
   456
            *removedFlag = SDL_TRUE;
icculus@9394
   457
            return;
icculus@9393
   458
        }
icculus@9393
   459
    }
icculus@9394
   460
}
icculus@9393
   461
icculus@9394
   462
/* The audio backends call this when a device is removed from the system. */
icculus@9394
   463
void
icculus@9394
   464
SDL_RemoveAudioDevice(void *handle)
icculus@9394
   465
{
icculus@9394
   466
    SDL_LockMutex(current_audio.detectionLock);
icculus@9394
   467
    mark_device_removed(handle, current_audio.inputDevices, &current_audio.captureDevicesRemoved);
icculus@9394
   468
    mark_device_removed(handle, current_audio.outputDevices, &current_audio.outputDevicesRemoved);
icculus@9394
   469
    SDL_UnlockMutex(current_audio.detectionLock);
icculus@9394
   470
    current_audio.impl.FreeDeviceHandle(handle);
icculus@9393
   471
}
icculus@9393
   472
icculus@9393
   473
icculus@9012
   474
icculus@9012
   475
/* buffer queueing support... */
icculus@9012
   476
icculus@9012
   477
/* this expects that you managed thread safety elsewhere. */
icculus@9012
   478
static void
icculus@9012
   479
free_audio_queue(SDL_AudioBufferQueue *buffer)
icculus@9012
   480
{
icculus@9012
   481
    while (buffer) {
icculus@9012
   482
        SDL_AudioBufferQueue *next = buffer->next;
icculus@9012
   483
        SDL_free(buffer);
icculus@9012
   484
        buffer = next;
icculus@9012
   485
    }
icculus@9012
   486
}
icculus@9012
   487
icculus@9012
   488
static void SDLCALL
icculus@9012
   489
SDL_BufferQueueDrainCallback(void *userdata, Uint8 *stream, int _len)
icculus@9012
   490
{
icculus@9012
   491
    /* this function always holds the mixer lock before being called. */
icculus@9012
   492
    Uint32 len = (Uint32) _len;
icculus@9012
   493
    SDL_AudioDevice *device = (SDL_AudioDevice *) userdata;
icculus@9012
   494
    SDL_AudioBufferQueue *buffer;
icculus@9012
   495
icculus@9012
   496
    SDL_assert(device != NULL);  /* this shouldn't ever happen, right?! */
icculus@9012
   497
    SDL_assert(_len >= 0);  /* this shouldn't ever happen, right?! */
icculus@9012
   498
icculus@9012
   499
    while ((len > 0) && ((buffer = device->buffer_queue_head) != NULL)) {
icculus@9012
   500
        const Uint32 avail = buffer->datalen - buffer->startpos;
icculus@9012
   501
        const Uint32 cpy = SDL_min(len, avail);
icculus@9012
   502
        SDL_assert(device->queued_bytes >= avail);
icculus@9012
   503
icculus@9012
   504
        SDL_memcpy(stream, buffer->data + buffer->startpos, cpy);
icculus@9012
   505
        buffer->startpos += cpy;
icculus@9012
   506
        stream += cpy;
icculus@9012
   507
        device->queued_bytes -= cpy;
icculus@9012
   508
        len -= cpy;
icculus@9012
   509
icculus@9012
   510
        if (buffer->startpos == buffer->datalen) {  /* packet is done, put it in the pool. */
icculus@9012
   511
            device->buffer_queue_head = buffer->next;
icculus@9012
   512
            SDL_assert((buffer->next != NULL) || (buffer == device->buffer_queue_tail));
icculus@9012
   513
            buffer->next = device->buffer_queue_pool;
icculus@9012
   514
            device->buffer_queue_pool = buffer;
icculus@9012
   515
        }
icculus@9012
   516
    }
icculus@9012
   517
icculus@9012
   518
    SDL_assert((device->buffer_queue_head != NULL) == (device->queued_bytes != 0));
icculus@9012
   519
icculus@9012
   520
    if (len > 0) {  /* fill any remaining space in the stream with silence. */
icculus@9012
   521
        SDL_assert(device->buffer_queue_head == NULL);
icculus@9012
   522
        SDL_memset(stream, device->spec.silence, len);
icculus@9012
   523
    }
icculus@9012
   524
icculus@9012
   525
    if (device->buffer_queue_head == NULL) {
icculus@9012
   526
        device->buffer_queue_tail = NULL;  /* in case we drained the queue entirely. */
icculus@9012
   527
    }
icculus@9012
   528
}
icculus@9012
   529
icculus@9012
   530
int
icculus@9012
   531
SDL_QueueAudio(SDL_AudioDeviceID devid, const void *_data, Uint32 len)
icculus@9012
   532
{
icculus@9012
   533
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@9012
   534
    const Uint8 *data = (const Uint8 *) _data;
icculus@9012
   535
    SDL_AudioBufferQueue *orighead;
icculus@9012
   536
    SDL_AudioBufferQueue *origtail;
icculus@9012
   537
    Uint32 origlen;
icculus@9012
   538
    Uint32 datalen;
icculus@9012
   539
icculus@9012
   540
    if (!device) {
icculus@9012
   541
        return -1;  /* get_audio_device() will have set the error state */
icculus@9012
   542
    }
icculus@9012
   543
icculus@9012
   544
    if (device->spec.callback != SDL_BufferQueueDrainCallback) {
icculus@9012
   545
        return SDL_SetError("Audio device has a callback, queueing not allowed");
icculus@9012
   546
    }
icculus@9012
   547
icculus@9012
   548
    current_audio.impl.LockDevice(device);
icculus@9012
   549
icculus@9012
   550
    orighead = device->buffer_queue_head;
icculus@9012
   551
    origtail = device->buffer_queue_tail;
icculus@9012
   552
    origlen = origtail ? origtail->datalen : 0;
icculus@9012
   553
icculus@9012
   554
    while (len > 0) {
icculus@9012
   555
        SDL_AudioBufferQueue *packet = device->buffer_queue_tail;
icculus@9012
   556
        SDL_assert(!packet || (packet->datalen <= SDL_AUDIOBUFFERQUEUE_PACKETLEN));
icculus@9012
   557
        if (!packet || (packet->datalen >= SDL_AUDIOBUFFERQUEUE_PACKETLEN)) {
icculus@9012
   558
            /* tail packet missing or completely full; we need a new packet. */
icculus@9012
   559
            packet = device->buffer_queue_pool;
icculus@9012
   560
            if (packet != NULL) {
icculus@9012
   561
                /* we have one available in the pool. */
icculus@9012
   562
                device->buffer_queue_pool = packet->next;
icculus@9012
   563
            } else {
icculus@9012
   564
                /* Have to allocate a new one! */
icculus@9012
   565
                packet = (SDL_AudioBufferQueue *) SDL_malloc(sizeof (SDL_AudioBufferQueue));
icculus@9012
   566
                if (packet == NULL) {
icculus@9012
   567
                    /* uhoh, reset so we've queued nothing new, free what we can. */
icculus@9012
   568
                    if (!origtail) {
icculus@9012
   569
                        packet = device->buffer_queue_head;  /* whole queue. */
icculus@9012
   570
                    } else {
icculus@9012
   571
                        packet = origtail->next;  /* what we added to existing queue. */
icculus@9012
   572
                        origtail->next = NULL;
icculus@9012
   573
                        origtail->datalen = origlen;
icculus@9012
   574
                    }
icculus@9012
   575
                    device->buffer_queue_head = orighead;
icculus@9012
   576
                    device->buffer_queue_tail = origtail;
icculus@9012
   577
                    device->buffer_queue_pool = NULL;
icculus@9012
   578
icculus@9012
   579
                    current_audio.impl.UnlockDevice(device);
icculus@9012
   580
icculus@9012
   581
                    free_audio_queue(packet);  /* give back what we can. */
icculus@9012
   582
icculus@9012
   583
                    return SDL_OutOfMemory();
icculus@9012
   584
                }
icculus@9012
   585
            }
icculus@9012
   586
            packet->datalen = 0;
icculus@9012
   587
            packet->startpos = 0;
icculus@9012
   588
            packet->next = NULL;
icculus@9012
   589
icculus@9012
   590
            SDL_assert((device->buffer_queue_head != NULL) == (device->queued_bytes != 0));
icculus@9012
   591
            if (device->buffer_queue_tail == NULL) {
icculus@9012
   592
                device->buffer_queue_head = packet;
icculus@9012
   593
            } else {
icculus@9012
   594
                device->buffer_queue_tail->next = packet;
icculus@9012
   595
            }
icculus@9012
   596
            device->buffer_queue_tail = packet;
icculus@9012
   597
        }
icculus@9012
   598
icculus@9012
   599
        datalen = SDL_min(len, SDL_AUDIOBUFFERQUEUE_PACKETLEN - packet->datalen);
icculus@9012
   600
        SDL_memcpy(packet->data + packet->datalen, data, datalen);
icculus@9012
   601
        data += datalen;
icculus@9012
   602
        len -= datalen;
icculus@9012
   603
        packet->datalen += datalen;
icculus@9012
   604
        device->queued_bytes += datalen;
icculus@9012
   605
    }
icculus@9012
   606
icculus@9012
   607
    current_audio.impl.UnlockDevice(device);
icculus@9012
   608
icculus@9012
   609
    return 0;
icculus@9012
   610
}
icculus@9012
   611
icculus@9012
   612
Uint32
icculus@9012
   613
SDL_GetQueuedAudioSize(SDL_AudioDeviceID devid)
icculus@9012
   614
{
icculus@9012
   615
    Uint32 retval = 0;
icculus@9012
   616
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@9032
   617
icculus@9032
   618
    /* Nothing to do unless we're set up for queueing. */
icculus@9032
   619
    if (device && (device->spec.callback == SDL_BufferQueueDrainCallback)) {
icculus@9012
   620
        current_audio.impl.LockDevice(device);
icculus@9031
   621
        retval = device->queued_bytes + current_audio.impl.GetPendingBytes(device);
icculus@9012
   622
        current_audio.impl.UnlockDevice(device);
icculus@9012
   623
    }
icculus@9012
   624
icculus@9012
   625
    return retval;
icculus@9012
   626
}
icculus@9012
   627
icculus@9012
   628
void
icculus@9012
   629
SDL_ClearQueuedAudio(SDL_AudioDeviceID devid)
icculus@9012
   630
{
icculus@9012
   631
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@9012
   632
    SDL_AudioBufferQueue *buffer = NULL;
icculus@9012
   633
    if (!device) {
icculus@9012
   634
        return;  /* nothing to do. */
icculus@9012
   635
    }
icculus@9012
   636
icculus@9012
   637
    /* Blank out the device and release the mutex. Free it afterwards. */
icculus@9012
   638
    current_audio.impl.LockDevice(device);
icculus@9012
   639
    buffer = device->buffer_queue_head;
icculus@9012
   640
    device->buffer_queue_tail = NULL;
icculus@9012
   641
    device->buffer_queue_head = NULL;
icculus@9012
   642
    device->queued_bytes = 0;
icculus@9012
   643
    current_audio.impl.UnlockDevice(device);
icculus@9012
   644
icculus@9012
   645
    free_audio_queue(buffer);
icculus@9012
   646
}
icculus@9012
   647
icculus@9012
   648
dimitris@8761
   649
#if defined(__ANDROID__)
paul@4722
   650
#include <android/log.h>
aschiffler@4865
   651
#endif
paul@4722
   652
slouken@0
   653
/* The general mixing thread function */
slouken@1895
   654
int SDLCALL
icculus@2049
   655
SDL_RunAudio(void *devicep)
slouken@0
   656
{
icculus@2049
   657
    SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
slouken@1895
   658
    Uint8 *stream;
slouken@1895
   659
    int stream_len;
slouken@1895
   660
    void *udata;
slouken@1895
   661
    void (SDLCALL * fill) (void *userdata, Uint8 * stream, int len);
slouken@3336
   662
    Uint32 delay;
icculus@8660
   663
icculus@8660
   664
#if 0  /* !!! FIXME: rewrite/remove this streamer code. */
airlangga@5526
   665
    /* For streaming when the buffer sizes don't match up */
airlangga@5526
   666
    Uint8 *istream;
airlangga@5526
   667
    int istream_len = 0;
icculus@8660
   668
#endif
slouken@2716
   669
slouken@5509
   670
    /* The audio mixing is always a high priority thread */
slouken@5509
   671
    SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
slouken@5509
   672
slouken@1895
   673
    /* Perform any thread setup */
icculus@2049
   674
    device->threadid = SDL_ThreadID();
icculus@2049
   675
    current_audio.impl.ThreadInit(device);
slouken@0
   676
slouken@1895
   677
    /* Set up the mixing function */
icculus@2049
   678
    fill = device->spec.callback;
icculus@2049
   679
    udata = device->spec.userdata;
slouken@21
   680
slouken@2716
   681
    /* By default do not stream */
slouken@2716
   682
    device->use_streamer = 0;
slouken@2716
   683
icculus@2049
   684
    if (device->convert.needed) {
slouken@3040
   685
#if 0                           /* !!! FIXME: I took len_div out of the structure. Use rate_incr instead? */
slouken@2716
   686
        /* If the result of the conversion alters the length, i.e. resampling is being used, use the streamer */
slouken@2716
   687
        if (device->convert.len_mult != 1 || device->convert.len_div != 1) {
slouken@2716
   688
            /* The streamer's maximum length should be twice whichever is larger: spec.size or len_cvt */
slouken@2716
   689
            stream_max_len = 2 * device->spec.size;
slouken@2716
   690
            if (device->convert.len_mult > device->convert.len_div) {
slouken@2716
   691
                stream_max_len *= device->convert.len_mult;
slouken@2716
   692
                stream_max_len /= device->convert.len_div;
slouken@2716
   693
            }
slouken@2716
   694
            if (SDL_StreamInit(&device->streamer, stream_max_len, silence) <
slouken@2716
   695
                0)
slouken@2716
   696
                return -1;
slouken@2716
   697
            device->use_streamer = 1;
slouken@2716
   698
slouken@2716
   699
            /* istream_len should be the length of what we grab from the callback and feed to conversion,
slouken@2716
   700
               so that we get close to spec_size. I.e. we want device.spec_size = istream_len * u / d
slouken@2716
   701
             */
slouken@2716
   702
            istream_len =
slouken@2716
   703
                device->spec.size * device->convert.len_div /
slouken@2716
   704
                device->convert.len_mult;
slouken@2716
   705
        }
icculus@3021
   706
#endif
slouken@7518
   707
        stream_len = device->convert.len;
slouken@1895
   708
    } else {
icculus@2049
   709
        stream_len = device->spec.size;
slouken@1895
   710
    }
icculus@1561
   711
slouken@3336
   712
    /* Calculate the delay while paused */
slouken@3336
   713
    delay = ((device->spec.samples * 1000) / device->spec.freq);
slouken@3336
   714
slouken@2716
   715
    /* Determine if the streamer is necessary here */
icculus@8658
   716
#if 0  /* !!! FIXME: rewrite/remove this streamer code. */
slouken@2716
   717
    if (device->use_streamer == 1) {
slouken@3336
   718
        /* This code is almost the same as the old code. The difference is, instead of reading
slouken@2716
   719
           directly from the callback into "stream", then converting and sending the audio off,
slouken@2716
   720
           we go: callback -> "istream" -> (conversion) -> streamer -> stream -> device.
slouken@2716
   721
           However, reading and writing with streamer are done separately:
slouken@2716
   722
           - We only call the callback and write to the streamer when the streamer does not
slouken@2716
   723
           contain enough samples to output to the device.
slouken@2716
   724
           - We only read from the streamer and tell the device to play when the streamer
slouken@2716
   725
           does have enough samples to output.
slouken@2716
   726
           This allows us to perform resampling in the conversion step, where the output of the
slouken@2716
   727
           resampling process can be any number. We will have to see what a good size for the
slouken@2716
   728
           stream's maximum length is, but I suspect 2*max(len_cvt, stream_len) is a good figure.
slouken@2716
   729
         */
icculus@9397
   730
        while (!device->shutdown) {
slouken@3336
   731
slouken@3336
   732
            if (device->paused) {
slouken@3336
   733
                SDL_Delay(delay);
slouken@3336
   734
                continue;
slouken@3336
   735
            }
slouken@3336
   736
slouken@2716
   737
            /* Only read in audio if the streamer doesn't have enough already (if it does not have enough samples to output) */
slouken@2716
   738
            if (SDL_StreamLength(&device->streamer) < stream_len) {
slouken@2716
   739
                /* Set up istream */
slouken@2716
   740
                if (device->convert.needed) {
slouken@2716
   741
                    if (device->convert.buf) {
slouken@2716
   742
                        istream = device->convert.buf;
slouken@2716
   743
                    } else {
slouken@2716
   744
                        continue;
slouken@2716
   745
                    }
slouken@2716
   746
                } else {
slouken@3336
   747
/* FIXME: Ryan, this is probably wrong.  I imagine we don't want to get
slouken@3336
   748
 * a device buffer both here and below in the stream output.
slouken@3336
   749
 */
slouken@2716
   750
                    istream = current_audio.impl.GetDeviceBuf(device);
slouken@2716
   751
                    if (istream == NULL) {
slouken@2716
   752
                        istream = device->fake_stream;
slouken@2716
   753
                    }
slouken@2716
   754
                }
slouken@0
   755
slouken@2716
   756
                /* Read from the callback into the _input_ stream */
icculus@9391
   757
                // !!! FIXME: this should be LockDevice.
slouken@6977
   758
                SDL_LockMutex(device->mixer_lock);
slouken@3336
   759
                (*fill) (udata, istream, istream_len);
slouken@6977
   760
                SDL_UnlockMutex(device->mixer_lock);
slouken@2716
   761
slouken@2716
   762
                /* Convert the audio if necessary and write to the streamer */
slouken@2716
   763
                if (device->convert.needed) {
slouken@2716
   764
                    SDL_ConvertAudio(&device->convert);
slouken@2716
   765
                    if (istream == NULL) {
slouken@2716
   766
                        istream = device->fake_stream;
slouken@2716
   767
                    }
gabomdq@7678
   768
                    /* SDL_memcpy(istream, device->convert.buf, device->convert.len_cvt); */
slouken@2716
   769
                    SDL_StreamWrite(&device->streamer, device->convert.buf,
slouken@2716
   770
                                    device->convert.len_cvt);
slouken@2716
   771
                } else {
slouken@2716
   772
                    SDL_StreamWrite(&device->streamer, istream, istream_len);
slouken@2716
   773
                }
slouken@1895
   774
            }
slouken@2716
   775
slouken@2716
   776
            /* Only output audio if the streamer has enough to output */
slouken@2716
   777
            if (SDL_StreamLength(&device->streamer) >= stream_len) {
slouken@2716
   778
                /* Set up the output stream */
slouken@2716
   779
                if (device->convert.needed) {
slouken@2716
   780
                    if (device->convert.buf) {
slouken@2716
   781
                        stream = device->convert.buf;
slouken@2716
   782
                    } else {
slouken@2716
   783
                        continue;
slouken@2716
   784
                    }
slouken@2716
   785
                } else {
slouken@2716
   786
                    stream = current_audio.impl.GetDeviceBuf(device);
slouken@2716
   787
                    if (stream == NULL) {
slouken@2716
   788
                        stream = device->fake_stream;
slouken@2716
   789
                    }
slouken@2716
   790
                }
slouken@2716
   791
slouken@2716
   792
                /* Now read from the streamer */
slouken@2716
   793
                SDL_StreamRead(&device->streamer, stream, stream_len);
slouken@2716
   794
slouken@2716
   795
                /* Ready current buffer for play and change current buffer */
slouken@3336
   796
                if (stream != device->fake_stream) {
slouken@2716
   797
                    current_audio.impl.PlayDevice(device);
slouken@3096
   798
                    /* Wait for an audio buffer to become available */
slouken@3096
   799
                    current_audio.impl.WaitDevice(device);
slouken@2716
   800
                } else {
slouken@3336
   801
                    SDL_Delay(delay);
slouken@2716
   802
                }
slouken@1895
   803
            }
slouken@0
   804
slouken@1895
   805
        }
icculus@8660
   806
    } else
icculus@8658
   807
#endif
icculus@8660
   808
    {
slouken@2716
   809
        /* Otherwise, do not use the streamer. This is the old code. */
icculus@7406
   810
        const int silence = (int) device->spec.silence;
slouken@0
   811
slouken@2716
   812
        /* Loop, filling the audio buffers */
icculus@9397
   813
        while (!device->shutdown) {
slouken@2716
   814
            /* Fill the current buffer with sound */
slouken@2716
   815
            if (device->convert.needed) {
icculus@9397
   816
                stream = device->convert.buf;
icculus@9397
   817
            } else if (device->enabled) {
icculus@9397
   818
                stream = current_audio.impl.GetDeviceBuf(device);
slouken@2716
   819
            } else {
icculus@9397
   820
                /* if the device isn't enabled, we still write to the
icculus@9397
   821
                    fake_stream, so the app's callback will fire with
icculus@9397
   822
                    a regular frequency, in case they depend on that
icculus@9397
   823
                    for timing or progress. They can use hotplug
icculus@9397
   824
                    now to know if the device failed. */
icculus@9397
   825
                stream = NULL;
icculus@9397
   826
            }
icculus@9397
   827
icculus@9397
   828
            if (stream == NULL) {
icculus@9397
   829
                stream = device->fake_stream;
slouken@1895
   830
            }
slouken@0
   831
icculus@9391
   832
            /* !!! FIXME: this should be LockDevice. */
icculus@7447
   833
            SDL_LockMutex(device->mixer_lock);
icculus@7406
   834
            if (device->paused) {
icculus@7406
   835
                SDL_memset(stream, silence, stream_len);
icculus@7406
   836
            } else {
icculus@7406
   837
                (*fill) (udata, stream, stream_len);
icculus@7406
   838
            }
icculus@7447
   839
            SDL_UnlockMutex(device->mixer_lock);
slouken@1562
   840
slouken@2716
   841
            /* Convert the audio if necessary */
icculus@9397
   842
            if (device->enabled && device->convert.needed) {
slouken@2716
   843
                SDL_ConvertAudio(&device->convert);
slouken@2716
   844
                stream = current_audio.impl.GetDeviceBuf(device);
slouken@2716
   845
                if (stream == NULL) {
slouken@2716
   846
                    stream = device->fake_stream;
icculus@9397
   847
                } else {
icculus@9397
   848
                    SDL_memcpy(stream, device->convert.buf,
icculus@9397
   849
                               device->convert.len_cvt);
slouken@2716
   850
                }
slouken@2716
   851
            }
slouken@2716
   852
slouken@2716
   853
            /* Ready current buffer for play and change current buffer */
slouken@3336
   854
            if (stream != device->fake_stream) {
slouken@2716
   855
                current_audio.impl.PlayDevice(device);
slouken@3096
   856
                /* Wait for an audio buffer to become available */
slouken@3096
   857
                current_audio.impl.WaitDevice(device);
slouken@2716
   858
            } else {
slouken@3336
   859
                SDL_Delay(delay);
slouken@2716
   860
            }
slouken@1895
   861
        }
slouken@1895
   862
    }
slouken@1562
   863
slouken@1895
   864
    /* Wait for the audio to drain.. */
icculus@2049
   865
    current_audio.impl.WaitDone(device);
slouken@21
   866
slouken@2716
   867
    /* If necessary, deinit the streamer */
icculus@8658
   868
#if 0  /* !!! FIXME: rewrite/remove this streamer code. */
slouken@2716
   869
    if (device->use_streamer == 1)
slouken@2716
   870
        SDL_StreamDeinit(&device->streamer);
icculus@8658
   871
#endif
slouken@2716
   872
icculus@9382
   873
    return 0;
slouken@0
   874
}
slouken@0
   875
slouken@322
   876
icculus@1982
   877
static SDL_AudioFormat
slouken@1895
   878
SDL_ParseAudioFormat(const char *string)
slouken@1794
   879
{
icculus@2076
   880
#define CHECK_FMT_STRING(x) if (SDL_strcmp(string, #x) == 0) return AUDIO_##x
icculus@2049
   881
    CHECK_FMT_STRING(U8);
icculus@2049
   882
    CHECK_FMT_STRING(S8);
icculus@2049
   883
    CHECK_FMT_STRING(U16LSB);
icculus@2049
   884
    CHECK_FMT_STRING(S16LSB);
icculus@2049
   885
    CHECK_FMT_STRING(U16MSB);
icculus@2049
   886
    CHECK_FMT_STRING(S16MSB);
icculus@2049
   887
    CHECK_FMT_STRING(U16SYS);
icculus@2049
   888
    CHECK_FMT_STRING(S16SYS);
icculus@2049
   889
    CHECK_FMT_STRING(U16);
icculus@2049
   890
    CHECK_FMT_STRING(S16);
icculus@2049
   891
    CHECK_FMT_STRING(S32LSB);
icculus@2049
   892
    CHECK_FMT_STRING(S32MSB);
icculus@2049
   893
    CHECK_FMT_STRING(S32SYS);
icculus@2049
   894
    CHECK_FMT_STRING(S32);
icculus@2049
   895
    CHECK_FMT_STRING(F32LSB);
icculus@2049
   896
    CHECK_FMT_STRING(F32MSB);
icculus@2049
   897
    CHECK_FMT_STRING(F32SYS);
icculus@2049
   898
    CHECK_FMT_STRING(F32);
slouken@2060
   899
#undef CHECK_FMT_STRING
icculus@2049
   900
    return 0;
slouken@1895
   901
}
slouken@1895
   902
slouken@1895
   903
int
slouken@1895
   904
SDL_GetNumAudioDrivers(void)
slouken@1895
   905
{
icculus@9382
   906
    return SDL_arraysize(bootstrap) - 1;
slouken@1895
   907
}
slouken@1895
   908
slouken@1895
   909
const char *
slouken@1895
   910
SDL_GetAudioDriver(int index)
slouken@1895
   911
{
slouken@1895
   912
    if (index >= 0 && index < SDL_GetNumAudioDrivers()) {
icculus@9382
   913
        return bootstrap[index]->name;
slouken@1895
   914
    }
icculus@9382
   915
    return NULL;
slouken@1794
   916
}
slouken@1794
   917
slouken@1895
   918
int
slouken@1895
   919
SDL_AudioInit(const char *driver_name)
slouken@0
   920
{
icculus@2049
   921
    int i = 0;
icculus@2049
   922
    int initialized = 0;
icculus@2049
   923
    int tried_to_init = 0;
slouken@0
   924
icculus@2049
   925
    if (SDL_WasInit(SDL_INIT_AUDIO)) {
slouken@2060
   926
        SDL_AudioQuit();        /* shutdown driver if already running. */
slouken@1895
   927
    }
slouken@0
   928
icculus@9392
   929
    SDL_zero(current_audio);
icculus@9392
   930
    SDL_zero(open_devices);
icculus@2049
   931
slouken@1895
   932
    /* Select the proper audio driver */
slouken@1909
   933
    if (driver_name == NULL) {
slouken@1909
   934
        driver_name = SDL_getenv("SDL_AUDIODRIVER");
slouken@1909
   935
    }
icculus@2049
   936
icculus@2049
   937
    for (i = 0; (!initialized) && (bootstrap[i]); ++i) {
icculus@2049
   938
        /* make sure we should even try this driver before doing so... */
icculus@2049
   939
        const AudioBootStrap *backend = bootstrap[i];
slouken@6900
   940
        if ((driver_name && (SDL_strncasecmp(backend->name, driver_name, SDL_strlen(driver_name)) != 0)) ||
slouken@6900
   941
            (!driver_name && backend->demand_only)) {
icculus@2049
   942
            continue;
icculus@2049
   943
        }
slouken@0
   944
icculus@2049
   945
        tried_to_init = 1;
icculus@9392
   946
        SDL_zero(current_audio);
icculus@2049
   947
        current_audio.name = backend->name;
icculus@2049
   948
        current_audio.desc = backend->desc;
icculus@3699
   949
        initialized = backend->init(&current_audio.impl);
slouken@1895
   950
    }
icculus@2049
   951
icculus@2049
   952
    if (!initialized) {
icculus@2049
   953
        /* specific drivers will set the error message if they fail... */
icculus@2049
   954
        if (!tried_to_init) {
slouken@1895
   955
            if (driver_name) {
icculus@3699
   956
                SDL_SetError("Audio target '%s' not available", driver_name);
slouken@1895
   957
            } else {
slouken@1895
   958
                SDL_SetError("No available audio device");
slouken@1895
   959
            }
slouken@1895
   960
        }
icculus@2049
   961
icculus@9392
   962
        SDL_zero(current_audio);
icculus@9382
   963
        return -1;            /* No driver was available, so fail. */
slouken@1895
   964
    }
icculus@2049
   965
icculus@9394
   966
    current_audio.detectionLock = SDL_CreateMutex();
icculus@9393
   967
icculus@2049
   968
    finalize_audio_entry_points();
icculus@2049
   969
icculus@9393
   970
    /* Make sure we have a list of devices available at startup. */
icculus@9394
   971
    current_audio.impl.DetectDevices();
icculus@9393
   972
icculus@9382
   973
    return 0;
slouken@0
   974
}
slouken@0
   975
slouken@1895
   976
/*
slouken@1895
   977
 * Get the current audio driver name
slouken@1895
   978
 */
slouken@1895
   979
const char *
slouken@1895
   980
SDL_GetCurrentAudioDriver()
slouken@0
   981
{
icculus@2049
   982
    return current_audio.name;
slouken@0
   983
}
slouken@0
   984
icculus@9394
   985
/* Clean out devices that we've removed but had to keep around for stability. */
icculus@9394
   986
static void
icculus@9394
   987
clean_out_device_list(SDL_AudioDeviceItem **devices, int *devCount, SDL_bool *removedFlag)
icculus@9394
   988
{
icculus@9394
   989
    SDL_AudioDeviceItem *item = *devices;
icculus@9394
   990
    SDL_AudioDeviceItem *prev = NULL;
icculus@9394
   991
    int total = 0;
icculus@9394
   992
icculus@9394
   993
    while (item) {
icculus@9394
   994
        SDL_AudioDeviceItem *next = item->next;
icculus@9394
   995
        if (item->handle != NULL) {
icculus@9394
   996
            total++;
icculus@9394
   997
            prev = item;
icculus@9394
   998
        } else {
icculus@9394
   999
            if (prev) {
icculus@9394
  1000
                prev->next = next;
icculus@9394
  1001
            } else {
icculus@9394
  1002
                *devices = next;
icculus@9394
  1003
            }
icculus@9394
  1004
            SDL_free(item);
icculus@9394
  1005
        }
icculus@9394
  1006
        item = next;
icculus@9394
  1007
    }
icculus@9394
  1008
icculus@9394
  1009
    *devCount = total;
icculus@9394
  1010
    *removedFlag = SDL_FALSE;
icculus@9394
  1011
}
icculus@9394
  1012
icculus@9394
  1013
slouken@1895
  1014
int
icculus@2049
  1015
SDL_GetNumAudioDevices(int iscapture)
slouken@0
  1016
{
icculus@5593
  1017
    int retval = 0;
icculus@5593
  1018
icculus@2049
  1019
    if (!SDL_WasInit(SDL_INIT_AUDIO)) {
icculus@2049
  1020
        return -1;
icculus@2049
  1021
    }
icculus@5593
  1022
icculus@9394
  1023
    SDL_LockMutex(current_audio.detectionLock);
icculus@9394
  1024
    if (iscapture && current_audio.captureDevicesRemoved) {
icculus@9394
  1025
        clean_out_device_list(&current_audio.inputDevices, &current_audio.inputDeviceCount, &current_audio.captureDevicesRemoved);
icculus@2049
  1026
    }
icculus@2049
  1027
icculus@9394
  1028
    if (!iscapture && current_audio.outputDevicesRemoved) {
icculus@9394
  1029
        clean_out_device_list(&current_audio.outputDevices, &current_audio.outputDeviceCount, &current_audio.outputDevicesRemoved);
icculus@9394
  1030
        current_audio.outputDevicesRemoved = SDL_FALSE;
icculus@5593
  1031
    }
icculus@5593
  1032
icculus@9393
  1033
    retval = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount;
icculus@9394
  1034
    SDL_UnlockMutex(current_audio.detectionLock);
icculus@9393
  1035
icculus@5593
  1036
    return retval;
icculus@2049
  1037
}
icculus@2049
  1038
slouken@0
  1039
icculus@2049
  1040
const char *
icculus@2049
  1041
SDL_GetAudioDeviceName(int index, int iscapture)
icculus@2049
  1042
{
icculus@9393
  1043
    const char *retval = NULL;
icculus@9393
  1044
icculus@2049
  1045
    if (!SDL_WasInit(SDL_INIT_AUDIO)) {
icculus@2049
  1046
        SDL_SetError("Audio subsystem is not initialized");
icculus@2049
  1047
        return NULL;
icculus@2049
  1048
    }
icculus@2049
  1049
icculus@2049
  1050
    if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
icculus@2049
  1051
        SDL_SetError("No capture support");
icculus@2049
  1052
        return NULL;
slouken@1895
  1053
    }
icculus@2049
  1054
icculus@9394
  1055
    if (index >= 0) {
icculus@9394
  1056
        SDL_AudioDeviceItem *item;
icculus@9394
  1057
        int i;
slouken@0
  1058
icculus@9394
  1059
        SDL_LockMutex(current_audio.detectionLock);
icculus@9394
  1060
        item = iscapture ? current_audio.inputDevices : current_audio.outputDevices;
icculus@9394
  1061
        i = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount;
icculus@9394
  1062
        if (index < i) {
icculus@9394
  1063
            for (i--; i > index; i--, item = item->next) {
icculus@9394
  1064
                SDL_assert(item != NULL);
icculus@9394
  1065
            }
icculus@9394
  1066
            SDL_assert(item != NULL);
icculus@9394
  1067
            retval = item->name;
slouken@7904
  1068
        }
icculus@9394
  1069
        SDL_UnlockMutex(current_audio.detectionLock);
slouken@1895
  1070
    }
slouken@262
  1071
icculus@9394
  1072
    if (retval == NULL) {
icculus@9394
  1073
        SDL_SetError("No such device");
icculus@5593
  1074
    }
icculus@5593
  1075
icculus@9394
  1076
    return retval;
icculus@2049
  1077
}
icculus@2049
  1078
icculus@2049
  1079
icculus@2049
  1080
static void
slouken@2060
  1081
close_audio_device(SDL_AudioDevice * device)
icculus@2049
  1082
{
icculus@2049
  1083
    device->enabled = 0;
icculus@9397
  1084
    device->shutdown = 1;
icculus@2049
  1085
    if (device->thread != NULL) {
icculus@2049
  1086
        SDL_WaitThread(device->thread, NULL);
icculus@2049
  1087
    }
icculus@2049
  1088
    if (device->mixer_lock != NULL) {
icculus@2049
  1089
        SDL_DestroyMutex(device->mixer_lock);
icculus@2049
  1090
    }
slouken@7719
  1091
    SDL_FreeAudioMem(device->fake_stream);
icculus@2049
  1092
    if (device->convert.needed) {
icculus@2049
  1093
        SDL_FreeAudioMem(device->convert.buf);
icculus@2049
  1094
    }
icculus@2049
  1095
    if (device->opened) {
icculus@2049
  1096
        current_audio.impl.CloseDevice(device);
icculus@2049
  1097
        device->opened = 0;
icculus@2049
  1098
    }
icculus@9012
  1099
icculus@9012
  1100
    free_audio_queue(device->buffer_queue_head);
icculus@9012
  1101
    free_audio_queue(device->buffer_queue_pool);
icculus@9012
  1102
icculus@2049
  1103
    SDL_FreeAudioMem(device);
icculus@2049
  1104
}
icculus@2049
  1105
icculus@2049
  1106
icculus@2049
  1107
/*
icculus@2049
  1108
 * Sanity check desired AudioSpec for SDL_OpenAudio() in (orig).
icculus@2049
  1109
 *  Fills in a sanitized copy in (prepared).
icculus@2049
  1110
 *  Returns non-zero if okay, zero on fatal parameters in (orig).
icculus@2049
  1111
 */
icculus@2049
  1112
static int
slouken@2060
  1113
prepare_audiospec(const SDL_AudioSpec * orig, SDL_AudioSpec * prepared)
icculus@2049
  1114
{
slouken@2060
  1115
    SDL_memcpy(prepared, orig, sizeof(SDL_AudioSpec));
icculus@2049
  1116
icculus@2049
  1117
    if (orig->freq == 0) {
icculus@2049
  1118
        const char *env = SDL_getenv("SDL_AUDIO_FREQUENCY");
slouken@2060
  1119
        if ((!env) || ((prepared->freq = SDL_atoi(env)) == 0)) {
slouken@2060
  1120
            prepared->freq = 22050;     /* a reasonable default */
slouken@1895
  1121
        }
slouken@1895
  1122
    }
icculus@2049
  1123
icculus@2049
  1124
    if (orig->format == 0) {
icculus@2049
  1125
        const char *env = SDL_getenv("SDL_AUDIO_FORMAT");
icculus@2049
  1126
        if ((!env) || ((prepared->format = SDL_ParseAudioFormat(env)) == 0)) {
slouken@2060
  1127
            prepared->format = AUDIO_S16;       /* a reasonable default */
slouken@1895
  1128
        }
slouken@1895
  1129
    }
icculus@2049
  1130
icculus@2049
  1131
    switch (orig->channels) {
slouken@2060
  1132
    case 0:{
slouken@2060
  1133
            const char *env = SDL_getenv("SDL_AUDIO_CHANNELS");
slouken@2141
  1134
            if ((!env) || ((prepared->channels = (Uint8) SDL_atoi(env)) == 0)) {
slouken@2060
  1135
                prepared->channels = 2; /* a reasonable default */
slouken@2060
  1136
            }
slouken@2060
  1137
            break;
icculus@2049
  1138
        }
slouken@1895
  1139
    case 1:                    /* Mono */
slouken@1895
  1140
    case 2:                    /* Stereo */
slouken@1895
  1141
    case 4:                    /* surround */
slouken@1895
  1142
    case 6:                    /* surround with center and lfe */
slouken@1895
  1143
        break;
slouken@1895
  1144
    default:
icculus@2049
  1145
        SDL_SetError("Unsupported number of audio channels.");
icculus@2049
  1146
        return 0;
slouken@1895
  1147
    }
icculus@2049
  1148
icculus@2049
  1149
    if (orig->samples == 0) {
icculus@2049
  1150
        const char *env = SDL_getenv("SDL_AUDIO_SAMPLES");
slouken@2060
  1151
        if ((!env) || ((prepared->samples = (Uint16) SDL_atoi(env)) == 0)) {
icculus@2049
  1152
            /* Pick a default of ~46 ms at desired frequency */
icculus@2049
  1153
            /* !!! FIXME: remove this when the non-Po2 resampling is in. */
icculus@2049
  1154
            const int samples = (prepared->freq / 1000) * 46;
icculus@2049
  1155
            int power2 = 1;
icculus@2049
  1156
            while (power2 < samples) {
icculus@2049
  1157
                power2 *= 2;
icculus@2049
  1158
            }
icculus@2049
  1159
            prepared->samples = power2;
slouken@1895
  1160
        }
slouken@1895
  1161
    }
slouken@0
  1162
slouken@1895
  1163
    /* Calculate the silence and size of the audio specification */
icculus@2049
  1164
    SDL_CalculateAudioSpec(prepared);
slouken@21
  1165
icculus@2049
  1166
    return 1;
icculus@2049
  1167
}
slouken@1408
  1168
icculus@2049
  1169
static SDL_AudioDeviceID
icculus@2049
  1170
open_audio_device(const char *devname, int iscapture,
slouken@2866
  1171
                  const SDL_AudioSpec * desired, SDL_AudioSpec * obtained,
slouken@2866
  1172
                  int allowed_changes, int min_id)
icculus@2049
  1173
{
icculus@2049
  1174
    SDL_AudioDeviceID id = 0;
slouken@2866
  1175
    SDL_AudioSpec _obtained;
icculus@2049
  1176
    SDL_AudioDevice *device;
slouken@2866
  1177
    SDL_bool build_cvt;
icculus@9394
  1178
    void *handle = NULL;
icculus@9397
  1179
    int stream_len;
icculus@2049
  1180
    int i = 0;
slouken@21
  1181
icculus@2049
  1182
    if (!SDL_WasInit(SDL_INIT_AUDIO)) {
icculus@2049
  1183
        SDL_SetError("Audio subsystem is not initialized");
icculus@2049
  1184
        return 0;
icculus@2049
  1185
    }
icculus@2049
  1186
icculus@2049
  1187
    if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
icculus@2049
  1188
        SDL_SetError("No capture support");
icculus@2049
  1189
        return 0;
icculus@2049
  1190
    }
icculus@2049
  1191
icculus@9393
  1192
    /* Find an available device ID... */
icculus@9393
  1193
    for (id = min_id - 1; id < SDL_arraysize(open_devices); id++) {
icculus@9393
  1194
        if (open_devices[id] == NULL) {
icculus@9393
  1195
            break;
icculus@9393
  1196
        }
icculus@9393
  1197
    }
icculus@9393
  1198
icculus@9393
  1199
    if (id == SDL_arraysize(open_devices)) {
icculus@9393
  1200
        SDL_SetError("Too many open audio devices");
icculus@9393
  1201
        return 0;
icculus@9393
  1202
    }
icculus@9393
  1203
slouken@2866
  1204
    if (!obtained) {
slouken@2866
  1205
        obtained = &_obtained;
slouken@2866
  1206
    }
slouken@2866
  1207
    if (!prepare_audiospec(desired, obtained)) {
icculus@2049
  1208
        return 0;
icculus@2049
  1209
    }
icculus@2049
  1210
icculus@2049
  1211
    /* If app doesn't care about a specific device, let the user override. */
icculus@2049
  1212
    if (devname == NULL) {
icculus@2049
  1213
        devname = SDL_getenv("SDL_AUDIO_DEVICE_NAME");
slouken@1895
  1214
    }
slouken@21
  1215
icculus@2049
  1216
    /*
icculus@2049
  1217
     * Catch device names at the high level for the simple case...
icculus@2049
  1218
     * This lets us have a basic "device enumeration" for systems that
icculus@2049
  1219
     *  don't have multiple devices, but makes sure the device name is
icculus@2049
  1220
     *  always NULL when it hits the low level.
icculus@2049
  1221
     *
icculus@2049
  1222
     * Also make sure that the simple case prevents multiple simultaneous
icculus@2049
  1223
     *  opens of the default system device.
icculus@2049
  1224
     */
icculus@2049
  1225
icculus@2049
  1226
    if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
icculus@2049
  1227
        if ((devname) && (SDL_strcmp(devname, DEFAULT_INPUT_DEVNAME) != 0)) {
icculus@2049
  1228
            SDL_SetError("No such device");
icculus@2049
  1229
            return 0;
icculus@2049
  1230
        }
icculus@2049
  1231
        devname = NULL;
icculus@2049
  1232
icculus@2049
  1233
        for (i = 0; i < SDL_arraysize(open_devices); i++) {
icculus@2049
  1234
            if ((open_devices[i]) && (open_devices[i]->iscapture)) {
icculus@2049
  1235
                SDL_SetError("Audio device already open");
icculus@2049
  1236
                return 0;
icculus@2049
  1237
            }
icculus@2049
  1238
        }
icculus@9394
  1239
    } else if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
icculus@2049
  1240
        if ((devname) && (SDL_strcmp(devname, DEFAULT_OUTPUT_DEVNAME) != 0)) {
icculus@2049
  1241
            SDL_SetError("No such device");
icculus@2049
  1242
            return 0;
icculus@2049
  1243
        }
icculus@2049
  1244
        devname = NULL;
icculus@2049
  1245
icculus@2049
  1246
        for (i = 0; i < SDL_arraysize(open_devices); i++) {
icculus@2049
  1247
            if ((open_devices[i]) && (!open_devices[i]->iscapture)) {
icculus@2049
  1248
                SDL_SetError("Audio device already open");
icculus@2049
  1249
                return 0;
icculus@2049
  1250
            }
icculus@2049
  1251
        }
icculus@9394
  1252
    } else if (devname != NULL) {
icculus@9394
  1253
        /* if the app specifies an exact string, we can pass the backend
icculus@9394
  1254
           an actual device handle thingey, which saves them the effort of
icculus@9394
  1255
           figuring out what device this was (such as, reenumerating
icculus@9394
  1256
           everything again to find the matching human-readable name).
icculus@9394
  1257
           It might still need to open a device based on the string for,
icculus@9394
  1258
           say, a network audio server, but this optimizes some cases. */
icculus@9394
  1259
        SDL_AudioDeviceItem *item;
icculus@9394
  1260
        SDL_LockMutex(current_audio.detectionLock);
icculus@9394
  1261
        for (item = iscapture ? current_audio.inputDevices : current_audio.outputDevices; item; item = item->next) {
icculus@9394
  1262
            if ((item->handle != NULL) && (SDL_strcmp(item->name, devname) == 0)) {
icculus@9394
  1263
                handle = item->handle;
icculus@9394
  1264
                break;
icculus@9394
  1265
            }
icculus@9394
  1266
        }
icculus@9394
  1267
        SDL_UnlockMutex(current_audio.detectionLock);
icculus@9394
  1268
    }
icculus@9394
  1269
icculus@9394
  1270
    if (!current_audio.impl.AllowsArbitraryDeviceNames) {
icculus@9394
  1271
        /* has to be in our device list, or the default device. */
icculus@9394
  1272
        if ((handle == NULL) && (devname != NULL)) {
icculus@9394
  1273
            SDL_SetError("No such device.");
icculus@9394
  1274
            return 0;
icculus@9394
  1275
        }
icculus@2049
  1276
    }
icculus@2049
  1277
slouken@2060
  1278
    device = (SDL_AudioDevice *) SDL_AllocAudioMem(sizeof(SDL_AudioDevice));
icculus@2049
  1279
    if (device == NULL) {
icculus@2049
  1280
        SDL_OutOfMemory();
icculus@2049
  1281
        return 0;
icculus@2049
  1282
    }
icculus@9012
  1283
    SDL_zerop(device);
icculus@9393
  1284
    device->id = id + 1;
slouken@2866
  1285
    device->spec = *obtained;
icculus@2049
  1286
    device->enabled = 1;
icculus@2049
  1287
    device->paused = 1;
icculus@2049
  1288
    device->iscapture = iscapture;
icculus@2049
  1289
icculus@9391
  1290
    /* Create a mutex for locking the sound buffers */
icculus@2049
  1291
    if (!current_audio.impl.SkipMixerLock) {
icculus@2049
  1292
        device->mixer_lock = SDL_CreateMutex();
icculus@2049
  1293
        if (device->mixer_lock == NULL) {
icculus@2049
  1294
            close_audio_device(device);
icculus@2049
  1295
            SDL_SetError("Couldn't create mixer lock");
icculus@2049
  1296
            return 0;
icculus@2049
  1297
        }
icculus@2049
  1298
    }
icculus@2049
  1299
icculus@9394
  1300
    if (current_audio.impl.OpenDevice(device, handle, devname, iscapture) < 0) {
icculus@2049
  1301
        close_audio_device(device);
icculus@2049
  1302
        return 0;
icculus@2049
  1303
    }
icculus@2049
  1304
    device->opened = 1;
slouken@0
  1305
slouken@1895
  1306
    /* See if we need to do any conversion */
slouken@2866
  1307
    build_cvt = SDL_FALSE;
slouken@2866
  1308
    if (obtained->freq != device->spec.freq) {
slouken@2866
  1309
        if (allowed_changes & SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) {
slouken@2866
  1310
            obtained->freq = device->spec.freq;
slouken@2866
  1311
        } else {
slouken@2866
  1312
            build_cvt = SDL_TRUE;
slouken@2866
  1313
        }
slouken@2866
  1314
    }
slouken@2866
  1315
    if (obtained->format != device->spec.format) {
slouken@2866
  1316
        if (allowed_changes & SDL_AUDIO_ALLOW_FORMAT_CHANGE) {
slouken@2866
  1317
            obtained->format = device->spec.format;
slouken@2866
  1318
        } else {
slouken@2866
  1319
            build_cvt = SDL_TRUE;
slouken@2866
  1320
        }
slouken@2866
  1321
    }
slouken@2866
  1322
    if (obtained->channels != device->spec.channels) {
slouken@2866
  1323
        if (allowed_changes & SDL_AUDIO_ALLOW_CHANNELS_CHANGE) {
slouken@2866
  1324
            obtained->channels = device->spec.channels;
slouken@2866
  1325
        } else {
slouken@2866
  1326
            build_cvt = SDL_TRUE;
slouken@2866
  1327
        }
slouken@2866
  1328
    }
slouken@7518
  1329
slouken@7518
  1330
    /* If the audio driver changes the buffer size, accept it.
slouken@7518
  1331
       This needs to be done after the format is modified above,
slouken@7518
  1332
       otherwise it might not have the correct buffer size.
slouken@7518
  1333
     */
slouken@7518
  1334
    if (device->spec.samples != obtained->samples) {
slouken@7518
  1335
        obtained->samples = device->spec.samples;
slouken@7518
  1336
        SDL_CalculateAudioSpec(obtained);
slouken@7518
  1337
    }
slouken@7518
  1338
slouken@2866
  1339
    if (build_cvt) {
slouken@1895
  1340
        /* Build an audio conversion block */
icculus@2049
  1341
        if (SDL_BuildAudioCVT(&device->convert,
slouken@2866
  1342
                              obtained->format, obtained->channels,
slouken@2866
  1343
                              obtained->freq,
icculus@2049
  1344
                              device->spec.format, device->spec.channels,
icculus@2049
  1345
                              device->spec.freq) < 0) {
icculus@2049
  1346
            close_audio_device(device);
icculus@2049
  1347
            return 0;
slouken@1895
  1348
        }
icculus@2049
  1349
        if (device->convert.needed) {
slouken@7518
  1350
            device->convert.len = (int) (((double) device->spec.size) /
slouken@2060
  1351
                                         device->convert.len_ratio);
icculus@2053
  1352
icculus@2049
  1353
            device->convert.buf =
icculus@2049
  1354
                (Uint8 *) SDL_AllocAudioMem(device->convert.len *
icculus@2049
  1355
                                            device->convert.len_mult);
icculus@2049
  1356
            if (device->convert.buf == NULL) {
icculus@2049
  1357
                close_audio_device(device);
slouken@1895
  1358
                SDL_OutOfMemory();
icculus@2049
  1359
                return 0;
slouken@1895
  1360
            }
slouken@1895
  1361
        }
slouken@1895
  1362
    }
icculus@2049
  1363
icculus@9397
  1364
    /* Allocate a fake audio memory buffer */
icculus@9397
  1365
    stream_len = (device->convert.needed) ? device->convert.len_cvt : 0;
icculus@9397
  1366
    if (device->spec.size > stream_len) {
icculus@9397
  1367
        stream_len = device->spec.size;
icculus@9397
  1368
    }
icculus@9397
  1369
    device->fake_stream = (Uint8 *)SDL_AllocAudioMem(stream_len);
icculus@9397
  1370
    if (device->fake_stream == NULL) {
icculus@9397
  1371
        close_audio_device(device);
icculus@9397
  1372
        SDL_OutOfMemory();
icculus@9397
  1373
        return 0;
icculus@9397
  1374
    }
icculus@9397
  1375
icculus@9012
  1376
    if (device->spec.callback == NULL) {  /* use buffer queueing? */
icculus@9012
  1377
        /* pool a few packets to start. Enough for two callbacks. */
icculus@9012
  1378
        const int packetlen = SDL_AUDIOBUFFERQUEUE_PACKETLEN;
icculus@9012
  1379
        const int wantbytes = ((device->convert.needed) ? device->convert.len : device->spec.size) * 2;
icculus@9012
  1380
        const int wantpackets = (wantbytes / packetlen) + ((wantbytes % packetlen) ? packetlen : 0);
icculus@9012
  1381
        for (i = 0; i < wantpackets; i++) {
icculus@9012
  1382
            SDL_AudioBufferQueue *packet = (SDL_AudioBufferQueue *) SDL_malloc(sizeof (SDL_AudioBufferQueue));
icculus@9012
  1383
            if (packet) { /* don't care if this fails, we'll deal later. */
icculus@9012
  1384
                packet->datalen = 0;
icculus@9012
  1385
                packet->startpos = 0;
icculus@9012
  1386
                packet->next = device->buffer_queue_pool;
icculus@9012
  1387
                device->buffer_queue_pool = packet;
icculus@9012
  1388
            }
icculus@9012
  1389
        }
icculus@9012
  1390
icculus@9012
  1391
        device->spec.callback = SDL_BufferQueueDrainCallback;
icculus@9012
  1392
        device->spec.userdata = device;
icculus@9012
  1393
    }
icculus@9012
  1394
icculus@9393
  1395
    /* add it to our list of open devices. */
icculus@9393
  1396
    open_devices[id] = device;
icculus@2049
  1397
slouken@1895
  1398
    /* Start the audio thread if necessary */
icculus@2049
  1399
    if (!current_audio.impl.ProvidesOwnCallbackThread) {
slouken@1895
  1400
        /* Start the audio thread */
icculus@5969
  1401
        char name[64];
icculus@9393
  1402
        SDL_snprintf(name, sizeof (name), "SDLAudioDev%d", (int) device->id);
icculus@2049
  1403
/* !!! FIXME: this is nasty. */
icculus@6430
  1404
#if defined(__WIN32__) && !defined(HAVE_LIBC)
slouken@1330
  1405
#undef SDL_CreateThread
icculus@8094
  1406
#if SDL_DYNAMIC_API
icculus@8094
  1407
        device->thread = SDL_CreateThread_REAL(SDL_RunAudio, name, device, NULL, NULL);
icculus@8094
  1408
#else
icculus@5969
  1409
        device->thread = SDL_CreateThread(SDL_RunAudio, name, device, NULL, NULL);
icculus@8094
  1410
#endif
slouken@1330
  1411
#else
icculus@5969
  1412
        device->thread = SDL_CreateThread(SDL_RunAudio, name, device);
slouken@1330
  1413
#endif
icculus@2049
  1414
        if (device->thread == NULL) {
icculus@9393
  1415
            SDL_CloseAudioDevice(device->id);
slouken@1895
  1416
            SDL_SetError("Couldn't create audio thread");
icculus@2049
  1417
            return 0;
icculus@2049
  1418
        }
icculus@2049
  1419
    }
icculus@2049
  1420
icculus@9393
  1421
    return device->id;
icculus@2049
  1422
}
icculus@2049
  1423
icculus@2049
  1424
icculus@2049
  1425
int
slouken@2866
  1426
SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained)
icculus@2049
  1427
{
icculus@2049
  1428
    SDL_AudioDeviceID id = 0;
icculus@2049
  1429
icculus@2049
  1430
    /* Start up the audio driver, if necessary. This is legacy behaviour! */
icculus@2049
  1431
    if (!SDL_WasInit(SDL_INIT_AUDIO)) {
icculus@2049
  1432
        if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
icculus@9382
  1433
            return -1;
slouken@1895
  1434
        }
icculus@2049
  1435
    }
slouken@0
  1436
icculus@2049
  1437
    /* SDL_OpenAudio() is legacy and can only act on Device ID #1. */
icculus@2049
  1438
    if (open_devices[0] != NULL) {
icculus@2049
  1439
        SDL_SetError("Audio device is already opened");
icculus@9382
  1440
        return -1;
slouken@1895
  1441
    }
icculus@2049
  1442
slouken@2866
  1443
    if (obtained) {
slouken@2866
  1444
        id = open_audio_device(NULL, 0, desired, obtained,
slouken@2866
  1445
                               SDL_AUDIO_ALLOW_ANY_CHANGE, 1);
slouken@2866
  1446
    } else {
slouken@8919
  1447
        id = open_audio_device(NULL, 0, desired, NULL, 0, 1);
slouken@2866
  1448
    }
slouken@21
  1449
icculus@5964
  1450
    SDL_assert((id == 0) || (id == 1));
icculus@9382
  1451
    return (id == 0) ? -1 : 0;
icculus@2049
  1452
}
slouken@21
  1453
icculus@2049
  1454
SDL_AudioDeviceID
icculus@2049
  1455
SDL_OpenAudioDevice(const char *device, int iscapture,
slouken@2866
  1456
                    const SDL_AudioSpec * desired, SDL_AudioSpec * obtained,
slouken@2866
  1457
                    int allowed_changes)
icculus@2049
  1458
{
slouken@2866
  1459
    return open_audio_device(device, iscapture, desired, obtained,
slouken@2866
  1460
                             allowed_changes, 2);
slouken@1895
  1461
}
slouken@1895
  1462
slouken@3537
  1463
SDL_AudioStatus
icculus@2049
  1464
SDL_GetAudioDeviceStatus(SDL_AudioDeviceID devid)
slouken@1895
  1465
{
icculus@2049
  1466
    SDL_AudioDevice *device = get_audio_device(devid);
slouken@3537
  1467
    SDL_AudioStatus status = SDL_AUDIO_STOPPED;
icculus@2049
  1468
    if (device && device->enabled) {
icculus@2049
  1469
        if (device->paused) {
slouken@1895
  1470
            status = SDL_AUDIO_PAUSED;
slouken@1895
  1471
        } else {
slouken@1895
  1472
            status = SDL_AUDIO_PLAYING;
slouken@1895
  1473
        }
slouken@1895
  1474
    }
icculus@9382
  1475
    return status;
slouken@0
  1476
}
slouken@0
  1477
icculus@2049
  1478
slouken@3537
  1479
SDL_AudioStatus
icculus@2049
  1480
SDL_GetAudioStatus(void)
icculus@2049
  1481
{
icculus@2049
  1482
    return SDL_GetAudioDeviceStatus(1);
icculus@2049
  1483
}
icculus@2049
  1484
icculus@2049
  1485
void
icculus@2049
  1486
SDL_PauseAudioDevice(SDL_AudioDeviceID devid, int pause_on)
icculus@2049
  1487
{
icculus@2049
  1488
    SDL_AudioDevice *device = get_audio_device(devid);
gabomdq@9148
  1489
    if (device) {
gabomdq@9148
  1490
        current_audio.impl.LockDevice(device);
icculus@2049
  1491
        device->paused = pause_on;
gabomdq@9148
  1492
        current_audio.impl.UnlockDevice(device);
icculus@2049
  1493
    }
icculus@2049
  1494
}
icculus@2049
  1495
slouken@1895
  1496
void
slouken@1895
  1497
SDL_PauseAudio(int pause_on)
slouken@0
  1498
{
gabomdq@9148
  1499
    SDL_PauseAudioDevice(1, pause_on);
icculus@2049
  1500
}
icculus@2049
  1501
slouken@0
  1502
icculus@2049
  1503
void
icculus@2049
  1504
SDL_LockAudioDevice(SDL_AudioDeviceID devid)
icculus@2049
  1505
{
icculus@2049
  1506
    /* Obtain a lock on the mixing buffers */
icculus@2049
  1507
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@2049
  1508
    if (device) {
icculus@2049
  1509
        current_audio.impl.LockDevice(device);
slouken@1895
  1510
    }
slouken@0
  1511
}
slouken@0
  1512
slouken@1895
  1513
void
slouken@1895
  1514
SDL_LockAudio(void)
slouken@0
  1515
{
icculus@2049
  1516
    SDL_LockAudioDevice(1);
icculus@2049
  1517
}
slouken@0
  1518
icculus@2049
  1519
void
icculus@2049
  1520
SDL_UnlockAudioDevice(SDL_AudioDeviceID devid)
icculus@2049
  1521
{
slouken@1895
  1522
    /* Obtain a lock on the mixing buffers */
icculus@2049
  1523
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@2049
  1524
    if (device) {
icculus@2049
  1525
        current_audio.impl.UnlockDevice(device);
slouken@1895
  1526
    }
slouken@0
  1527
}
slouken@0
  1528
slouken@1895
  1529
void
slouken@1895
  1530
SDL_UnlockAudio(void)
slouken@0
  1531
{
icculus@2049
  1532
    SDL_UnlockAudioDevice(1);
icculus@2049
  1533
}
slouken@0
  1534
icculus@2049
  1535
void
icculus@2049
  1536
SDL_CloseAudioDevice(SDL_AudioDeviceID devid)
icculus@2049
  1537
{
icculus@2049
  1538
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@2049
  1539
    if (device) {
icculus@2049
  1540
        close_audio_device(device);
slouken@2060
  1541
        open_devices[devid - 1] = NULL;
slouken@1895
  1542
    }
slouken@0
  1543
}
slouken@0
  1544
slouken@1895
  1545
void
slouken@1895
  1546
SDL_CloseAudio(void)
slouken@0
  1547
{
icculus@2049
  1548
    SDL_CloseAudioDevice(1);
slouken@0
  1549
}
slouken@0
  1550
slouken@1895
  1551
void
slouken@1895
  1552
SDL_AudioQuit(void)
slouken@0
  1553
{
icculus@7348
  1554
    SDL_AudioDeviceID i;
icculus@7348
  1555
icculus@7345
  1556
    if (!current_audio.name) {  /* not initialized?! */
icculus@7345
  1557
        return;
icculus@7345
  1558
    }
icculus@7345
  1559
icculus@2049
  1560
    for (i = 0; i < SDL_arraysize(open_devices); i++) {
icculus@7342
  1561
        if (open_devices[i] != NULL) {
icculus@7446
  1562
            SDL_CloseAudioDevice(i+1);
icculus@7342
  1563
        }
icculus@2049
  1564
    }
slouken@0
  1565
icculus@9394
  1566
    free_device_list(&current_audio.outputDevices, &current_audio.outputDeviceCount);
icculus@9394
  1567
    free_device_list(&current_audio.inputDevices, &current_audio.inputDeviceCount);
icculus@9394
  1568
icculus@2049
  1569
    /* Free the driver data */
icculus@2049
  1570
    current_audio.impl.Deinitialize();
icculus@9393
  1571
icculus@9394
  1572
    SDL_DestroyMutex(current_audio.detectionLock);
icculus@9393
  1573
icculus@9393
  1574
    SDL_zero(current_audio);
icculus@9393
  1575
    SDL_zero(open_devices);
slouken@0
  1576
}
slouken@0
  1577
icculus@1982
  1578
#define NUM_FORMATS 10
slouken@0
  1579
static int format_idx;
slouken@0
  1580
static int format_idx_sub;
icculus@1982
  1581
static SDL_AudioFormat format_list[NUM_FORMATS][NUM_FORMATS] = {
slouken@1895
  1582
    {AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB,
icculus@1982
  1583
     AUDIO_U16MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB},
slouken@1895
  1584
    {AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB,
icculus@1982
  1585
     AUDIO_U16MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB},
icculus@1982
  1586
    {AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S32LSB,
icculus@1982
  1587
     AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_U8, AUDIO_S8},
icculus@1982
  1588
    {AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S32MSB,
icculus@1982
  1589
     AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_U8, AUDIO_S8},
icculus@1982
  1590
    {AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_S32LSB,
icculus@1982
  1591
     AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_U8, AUDIO_S8},
icculus@1982
  1592
    {AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_S32MSB,
icculus@1982
  1593
     AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_U8, AUDIO_S8},
icculus@1993
  1594
    {AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_S16LSB,
icculus@1993
  1595
     AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8},
icculus@1993
  1596
    {AUDIO_S32MSB, AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_S16MSB,
icculus@1993
  1597
     AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8},
icculus@1993
  1598
    {AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_S16LSB,
icculus@1993
  1599
     AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8},
icculus@1993
  1600
    {AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_S32MSB, AUDIO_S32LSB, AUDIO_S16MSB,
icculus@1993
  1601
     AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8},
slouken@0
  1602
};
slouken@0
  1603
icculus@1982
  1604
SDL_AudioFormat
icculus@1982
  1605
SDL_FirstAudioFormat(SDL_AudioFormat format)
slouken@0
  1606
{
slouken@1895
  1607
    for (format_idx = 0; format_idx < NUM_FORMATS; ++format_idx) {
slouken@1895
  1608
        if (format_list[format_idx][0] == format) {
slouken@1895
  1609
            break;
slouken@1895
  1610
        }
slouken@1895
  1611
    }
slouken@1895
  1612
    format_idx_sub = 0;
icculus@9382
  1613
    return SDL_NextAudioFormat();
slouken@0
  1614
}
slouken@0
  1615
icculus@1982
  1616
SDL_AudioFormat
slouken@1895
  1617
SDL_NextAudioFormat(void)
slouken@0
  1618
{
slouken@1895
  1619
    if ((format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS)) {
icculus@9382
  1620
        return 0;
slouken@1895
  1621
    }
icculus@9382
  1622
    return format_list[format_idx][format_idx_sub++];
slouken@0
  1623
}
slouken@0
  1624
slouken@1895
  1625
void
slouken@1895
  1626
SDL_CalculateAudioSpec(SDL_AudioSpec * spec)
slouken@0
  1627
{
slouken@1895
  1628
    switch (spec->format) {
slouken@1895
  1629
    case AUDIO_U8:
slouken@1895
  1630
        spec->silence = 0x80;
slouken@1895
  1631
        break;
slouken@1895
  1632
    default:
slouken@1895
  1633
        spec->silence = 0x00;
slouken@1895
  1634
        break;
slouken@1895
  1635
    }
icculus@2049
  1636
    spec->size = SDL_AUDIO_BITSIZE(spec->format) / 8;
slouken@1895
  1637
    spec->size *= spec->channels;
slouken@1895
  1638
    spec->size *= spec->samples;
slouken@0
  1639
}
slouken@1895
  1640
icculus@2049
  1641
icculus@2049
  1642
/*
icculus@2049
  1643
 * Moved here from SDL_mixer.c, since it relies on internals of an opened
icculus@2049
  1644
 *  audio device (and is deprecated, by the way!).
icculus@2049
  1645
 */
icculus@2049
  1646
void
icculus@2049
  1647
SDL_MixAudio(Uint8 * dst, const Uint8 * src, Uint32 len, int volume)
icculus@2049
  1648
{
icculus@2049
  1649
    /* Mix the user-level audio format */
icculus@2049
  1650
    SDL_AudioDevice *device = get_audio_device(1);
icculus@2049
  1651
    if (device != NULL) {
icculus@2049
  1652
        SDL_AudioFormat format;
icculus@2049
  1653
        if (device->convert.needed) {
icculus@2049
  1654
            format = device->convert.src_format;
icculus@2049
  1655
        } else {
icculus@2049
  1656
            format = device->spec.format;
icculus@2049
  1657
        }
icculus@2049
  1658
        SDL_MixAudioFormat(dst, src, format, len, volume);
icculus@2049
  1659
    }
icculus@2049
  1660
}
icculus@2049
  1661
slouken@1895
  1662
/* vi: set ts=4 sw=4 expandtab: */