src/audio/SDL_audio.c
author Ryan C. Gordon
Wed, 18 Mar 2015 02:01:17 -0400
changeset 9394 bb28e5281770
parent 9393 ed79a66e57e5
child 9396 69c501ed36f3
permissions -rw-r--r--
Bunch of reworking to how we manage audio devices.

Device enumeration now happens at startup and then is managed exclusively
through hotplugging instead of full redetection. The device name list now has
a unique "handle" associated with each item and SDL will pass this to the
backend so they don't have to figure out how a human readable name maps to
real hardware for a second time.

Other cleanups, fixes, improvements, plus all the audio backends updated to
the new interface...largely untested at this point, though.
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@9394
   427
    SDL_assert(device->enabled);  /* called more than once?! */
icculus@9394
   428
icculus@9394
   429
    /* Ends the audio callback and mark the device as STOPPED, but the
icculus@9394
   430
       app still needs to close the device to free resources. */
icculus@9394
   431
    current_audio.impl.LockDevice(device);
icculus@9394
   432
    device->enabled = 0;
icculus@9394
   433
    current_audio.impl.UnlockDevice(device);
icculus@9393
   434
icculus@9394
   435
    /* Post the event, if desired */
icculus@9394
   436
    if (SDL_GetEventState(SDL_AUDIODEVICEREMOVED) == SDL_ENABLE) {
icculus@9394
   437
        SDL_Event event;
icculus@9394
   438
        event.adevice.type = SDL_AUDIODEVICEREMOVED;
icculus@9394
   439
        event.adevice.which = device->id;
icculus@9394
   440
        event.adevice.iscapture = device->iscapture ? 1 : 0;
icculus@9394
   441
        SDL_PushEvent(&event);
icculus@9394
   442
    }
icculus@9394
   443
}
icculus@9393
   444
icculus@9394
   445
static void
icculus@9394
   446
mark_device_removed(void *handle, SDL_AudioDeviceItem *devices, SDL_bool *removedFlag)
icculus@9394
   447
{
icculus@9394
   448
    SDL_AudioDeviceItem *item;
icculus@9394
   449
    SDL_assert(handle != NULL);
icculus@9394
   450
    for (item = devices; item != NULL; item = item->next) {
icculus@9394
   451
        if (item->handle == handle) {
icculus@9394
   452
            item->handle = NULL;
icculus@9394
   453
            *removedFlag = SDL_TRUE;
icculus@9394
   454
            return;
icculus@9393
   455
        }
icculus@9393
   456
    }
icculus@9394
   457
}
icculus@9393
   458
icculus@9394
   459
/* The audio backends call this when a device is removed from the system. */
icculus@9394
   460
void
icculus@9394
   461
SDL_RemoveAudioDevice(void *handle)
icculus@9394
   462
{
icculus@9394
   463
    SDL_LockMutex(current_audio.detectionLock);
icculus@9394
   464
    mark_device_removed(handle, current_audio.inputDevices, &current_audio.captureDevicesRemoved);
icculus@9394
   465
    mark_device_removed(handle, current_audio.outputDevices, &current_audio.outputDevicesRemoved);
icculus@9394
   466
    SDL_UnlockMutex(current_audio.detectionLock);
icculus@9394
   467
    current_audio.impl.FreeDeviceHandle(handle);
icculus@9393
   468
}
icculus@9393
   469
icculus@9393
   470
icculus@9012
   471
icculus@9012
   472
/* buffer queueing support... */
icculus@9012
   473
icculus@9012
   474
/* this expects that you managed thread safety elsewhere. */
icculus@9012
   475
static void
icculus@9012
   476
free_audio_queue(SDL_AudioBufferQueue *buffer)
icculus@9012
   477
{
icculus@9012
   478
    while (buffer) {
icculus@9012
   479
        SDL_AudioBufferQueue *next = buffer->next;
icculus@9012
   480
        SDL_free(buffer);
icculus@9012
   481
        buffer = next;
icculus@9012
   482
    }
icculus@9012
   483
}
icculus@9012
   484
icculus@9012
   485
static void SDLCALL
icculus@9012
   486
SDL_BufferQueueDrainCallback(void *userdata, Uint8 *stream, int _len)
icculus@9012
   487
{
icculus@9012
   488
    /* this function always holds the mixer lock before being called. */
icculus@9012
   489
    Uint32 len = (Uint32) _len;
icculus@9012
   490
    SDL_AudioDevice *device = (SDL_AudioDevice *) userdata;
icculus@9012
   491
    SDL_AudioBufferQueue *buffer;
icculus@9012
   492
icculus@9012
   493
    SDL_assert(device != NULL);  /* this shouldn't ever happen, right?! */
icculus@9012
   494
    SDL_assert(_len >= 0);  /* this shouldn't ever happen, right?! */
icculus@9012
   495
icculus@9012
   496
    while ((len > 0) && ((buffer = device->buffer_queue_head) != NULL)) {
icculus@9012
   497
        const Uint32 avail = buffer->datalen - buffer->startpos;
icculus@9012
   498
        const Uint32 cpy = SDL_min(len, avail);
icculus@9012
   499
        SDL_assert(device->queued_bytes >= avail);
icculus@9012
   500
icculus@9012
   501
        SDL_memcpy(stream, buffer->data + buffer->startpos, cpy);
icculus@9012
   502
        buffer->startpos += cpy;
icculus@9012
   503
        stream += cpy;
icculus@9012
   504
        device->queued_bytes -= cpy;
icculus@9012
   505
        len -= cpy;
icculus@9012
   506
icculus@9012
   507
        if (buffer->startpos == buffer->datalen) {  /* packet is done, put it in the pool. */
icculus@9012
   508
            device->buffer_queue_head = buffer->next;
icculus@9012
   509
            SDL_assert((buffer->next != NULL) || (buffer == device->buffer_queue_tail));
icculus@9012
   510
            buffer->next = device->buffer_queue_pool;
icculus@9012
   511
            device->buffer_queue_pool = buffer;
icculus@9012
   512
        }
icculus@9012
   513
    }
icculus@9012
   514
icculus@9012
   515
    SDL_assert((device->buffer_queue_head != NULL) == (device->queued_bytes != 0));
icculus@9012
   516
icculus@9012
   517
    if (len > 0) {  /* fill any remaining space in the stream with silence. */
icculus@9012
   518
        SDL_assert(device->buffer_queue_head == NULL);
icculus@9012
   519
        SDL_memset(stream, device->spec.silence, len);
icculus@9012
   520
    }
icculus@9012
   521
icculus@9012
   522
    if (device->buffer_queue_head == NULL) {
icculus@9012
   523
        device->buffer_queue_tail = NULL;  /* in case we drained the queue entirely. */
icculus@9012
   524
    }
icculus@9012
   525
}
icculus@9012
   526
icculus@9012
   527
int
icculus@9012
   528
SDL_QueueAudio(SDL_AudioDeviceID devid, const void *_data, Uint32 len)
icculus@9012
   529
{
icculus@9012
   530
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@9012
   531
    const Uint8 *data = (const Uint8 *) _data;
icculus@9012
   532
    SDL_AudioBufferQueue *orighead;
icculus@9012
   533
    SDL_AudioBufferQueue *origtail;
icculus@9012
   534
    Uint32 origlen;
icculus@9012
   535
    Uint32 datalen;
icculus@9012
   536
icculus@9012
   537
    if (!device) {
icculus@9012
   538
        return -1;  /* get_audio_device() will have set the error state */
icculus@9012
   539
    }
icculus@9012
   540
icculus@9012
   541
    if (device->spec.callback != SDL_BufferQueueDrainCallback) {
icculus@9012
   542
        return SDL_SetError("Audio device has a callback, queueing not allowed");
icculus@9012
   543
    }
icculus@9012
   544
icculus@9012
   545
    current_audio.impl.LockDevice(device);
icculus@9012
   546
icculus@9012
   547
    orighead = device->buffer_queue_head;
icculus@9012
   548
    origtail = device->buffer_queue_tail;
icculus@9012
   549
    origlen = origtail ? origtail->datalen : 0;
icculus@9012
   550
icculus@9012
   551
    while (len > 0) {
icculus@9012
   552
        SDL_AudioBufferQueue *packet = device->buffer_queue_tail;
icculus@9012
   553
        SDL_assert(!packet || (packet->datalen <= SDL_AUDIOBUFFERQUEUE_PACKETLEN));
icculus@9012
   554
        if (!packet || (packet->datalen >= SDL_AUDIOBUFFERQUEUE_PACKETLEN)) {
icculus@9012
   555
            /* tail packet missing or completely full; we need a new packet. */
icculus@9012
   556
            packet = device->buffer_queue_pool;
icculus@9012
   557
            if (packet != NULL) {
icculus@9012
   558
                /* we have one available in the pool. */
icculus@9012
   559
                device->buffer_queue_pool = packet->next;
icculus@9012
   560
            } else {
icculus@9012
   561
                /* Have to allocate a new one! */
icculus@9012
   562
                packet = (SDL_AudioBufferQueue *) SDL_malloc(sizeof (SDL_AudioBufferQueue));
icculus@9012
   563
                if (packet == NULL) {
icculus@9012
   564
                    /* uhoh, reset so we've queued nothing new, free what we can. */
icculus@9012
   565
                    if (!origtail) {
icculus@9012
   566
                        packet = device->buffer_queue_head;  /* whole queue. */
icculus@9012
   567
                    } else {
icculus@9012
   568
                        packet = origtail->next;  /* what we added to existing queue. */
icculus@9012
   569
                        origtail->next = NULL;
icculus@9012
   570
                        origtail->datalen = origlen;
icculus@9012
   571
                    }
icculus@9012
   572
                    device->buffer_queue_head = orighead;
icculus@9012
   573
                    device->buffer_queue_tail = origtail;
icculus@9012
   574
                    device->buffer_queue_pool = NULL;
icculus@9012
   575
icculus@9012
   576
                    current_audio.impl.UnlockDevice(device);
icculus@9012
   577
icculus@9012
   578
                    free_audio_queue(packet);  /* give back what we can. */
icculus@9012
   579
icculus@9012
   580
                    return SDL_OutOfMemory();
icculus@9012
   581
                }
icculus@9012
   582
            }
icculus@9012
   583
            packet->datalen = 0;
icculus@9012
   584
            packet->startpos = 0;
icculus@9012
   585
            packet->next = NULL;
icculus@9012
   586
icculus@9012
   587
            SDL_assert((device->buffer_queue_head != NULL) == (device->queued_bytes != 0));
icculus@9012
   588
            if (device->buffer_queue_tail == NULL) {
icculus@9012
   589
                device->buffer_queue_head = packet;
icculus@9012
   590
            } else {
icculus@9012
   591
                device->buffer_queue_tail->next = packet;
icculus@9012
   592
            }
icculus@9012
   593
            device->buffer_queue_tail = packet;
icculus@9012
   594
        }
icculus@9012
   595
icculus@9012
   596
        datalen = SDL_min(len, SDL_AUDIOBUFFERQUEUE_PACKETLEN - packet->datalen);
icculus@9012
   597
        SDL_memcpy(packet->data + packet->datalen, data, datalen);
icculus@9012
   598
        data += datalen;
icculus@9012
   599
        len -= datalen;
icculus@9012
   600
        packet->datalen += datalen;
icculus@9012
   601
        device->queued_bytes += datalen;
icculus@9012
   602
    }
icculus@9012
   603
icculus@9012
   604
    current_audio.impl.UnlockDevice(device);
icculus@9012
   605
icculus@9012
   606
    return 0;
icculus@9012
   607
}
icculus@9012
   608
icculus@9012
   609
Uint32
icculus@9012
   610
SDL_GetQueuedAudioSize(SDL_AudioDeviceID devid)
icculus@9012
   611
{
icculus@9012
   612
    Uint32 retval = 0;
icculus@9012
   613
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@9032
   614
icculus@9032
   615
    /* Nothing to do unless we're set up for queueing. */
icculus@9032
   616
    if (device && (device->spec.callback == SDL_BufferQueueDrainCallback)) {
icculus@9012
   617
        current_audio.impl.LockDevice(device);
icculus@9031
   618
        retval = device->queued_bytes + current_audio.impl.GetPendingBytes(device);
icculus@9012
   619
        current_audio.impl.UnlockDevice(device);
icculus@9012
   620
    }
icculus@9012
   621
icculus@9012
   622
    return retval;
icculus@9012
   623
}
icculus@9012
   624
icculus@9012
   625
void
icculus@9012
   626
SDL_ClearQueuedAudio(SDL_AudioDeviceID devid)
icculus@9012
   627
{
icculus@9012
   628
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@9012
   629
    SDL_AudioBufferQueue *buffer = NULL;
icculus@9012
   630
    if (!device) {
icculus@9012
   631
        return;  /* nothing to do. */
icculus@9012
   632
    }
icculus@9012
   633
icculus@9012
   634
    /* Blank out the device and release the mutex. Free it afterwards. */
icculus@9012
   635
    current_audio.impl.LockDevice(device);
icculus@9012
   636
    buffer = device->buffer_queue_head;
icculus@9012
   637
    device->buffer_queue_tail = NULL;
icculus@9012
   638
    device->buffer_queue_head = NULL;
icculus@9012
   639
    device->queued_bytes = 0;
icculus@9012
   640
    current_audio.impl.UnlockDevice(device);
icculus@9012
   641
icculus@9012
   642
    free_audio_queue(buffer);
icculus@9012
   643
}
icculus@9012
   644
icculus@9012
   645
dimitris@8761
   646
#if defined(__ANDROID__)
paul@4722
   647
#include <android/log.h>
aschiffler@4865
   648
#endif
paul@4722
   649
slouken@0
   650
/* The general mixing thread function */
slouken@1895
   651
int SDLCALL
icculus@2049
   652
SDL_RunAudio(void *devicep)
slouken@0
   653
{
icculus@2049
   654
    SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
slouken@1895
   655
    Uint8 *stream;
slouken@1895
   656
    int stream_len;
slouken@1895
   657
    void *udata;
slouken@1895
   658
    void (SDLCALL * fill) (void *userdata, Uint8 * stream, int len);
slouken@3336
   659
    Uint32 delay;
icculus@8660
   660
icculus@8660
   661
#if 0  /* !!! FIXME: rewrite/remove this streamer code. */
airlangga@5526
   662
    /* For streaming when the buffer sizes don't match up */
airlangga@5526
   663
    Uint8 *istream;
airlangga@5526
   664
    int istream_len = 0;
icculus@8660
   665
#endif
slouken@2716
   666
slouken@5509
   667
    /* The audio mixing is always a high priority thread */
slouken@5509
   668
    SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
slouken@5509
   669
slouken@1895
   670
    /* Perform any thread setup */
icculus@2049
   671
    device->threadid = SDL_ThreadID();
icculus@2049
   672
    current_audio.impl.ThreadInit(device);
slouken@0
   673
slouken@1895
   674
    /* Set up the mixing function */
icculus@2049
   675
    fill = device->spec.callback;
icculus@2049
   676
    udata = device->spec.userdata;
slouken@21
   677
slouken@2716
   678
    /* By default do not stream */
slouken@2716
   679
    device->use_streamer = 0;
slouken@2716
   680
icculus@2049
   681
    if (device->convert.needed) {
slouken@3040
   682
#if 0                           /* !!! FIXME: I took len_div out of the structure. Use rate_incr instead? */
slouken@2716
   683
        /* If the result of the conversion alters the length, i.e. resampling is being used, use the streamer */
slouken@2716
   684
        if (device->convert.len_mult != 1 || device->convert.len_div != 1) {
slouken@2716
   685
            /* The streamer's maximum length should be twice whichever is larger: spec.size or len_cvt */
slouken@2716
   686
            stream_max_len = 2 * device->spec.size;
slouken@2716
   687
            if (device->convert.len_mult > device->convert.len_div) {
slouken@2716
   688
                stream_max_len *= device->convert.len_mult;
slouken@2716
   689
                stream_max_len /= device->convert.len_div;
slouken@2716
   690
            }
slouken@2716
   691
            if (SDL_StreamInit(&device->streamer, stream_max_len, silence) <
slouken@2716
   692
                0)
slouken@2716
   693
                return -1;
slouken@2716
   694
            device->use_streamer = 1;
slouken@2716
   695
slouken@2716
   696
            /* istream_len should be the length of what we grab from the callback and feed to conversion,
slouken@2716
   697
               so that we get close to spec_size. I.e. we want device.spec_size = istream_len * u / d
slouken@2716
   698
             */
slouken@2716
   699
            istream_len =
slouken@2716
   700
                device->spec.size * device->convert.len_div /
slouken@2716
   701
                device->convert.len_mult;
slouken@2716
   702
        }
icculus@3021
   703
#endif
slouken@7518
   704
        stream_len = device->convert.len;
slouken@1895
   705
    } else {
icculus@2049
   706
        stream_len = device->spec.size;
slouken@1895
   707
    }
icculus@1561
   708
slouken@3336
   709
    /* Calculate the delay while paused */
slouken@3336
   710
    delay = ((device->spec.samples * 1000) / device->spec.freq);
slouken@3336
   711
slouken@2716
   712
    /* Determine if the streamer is necessary here */
icculus@8658
   713
#if 0  /* !!! FIXME: rewrite/remove this streamer code. */
slouken@2716
   714
    if (device->use_streamer == 1) {
slouken@3336
   715
        /* This code is almost the same as the old code. The difference is, instead of reading
slouken@2716
   716
           directly from the callback into "stream", then converting and sending the audio off,
slouken@2716
   717
           we go: callback -> "istream" -> (conversion) -> streamer -> stream -> device.
slouken@2716
   718
           However, reading and writing with streamer are done separately:
slouken@2716
   719
           - We only call the callback and write to the streamer when the streamer does not
slouken@2716
   720
           contain enough samples to output to the device.
slouken@2716
   721
           - We only read from the streamer and tell the device to play when the streamer
slouken@2716
   722
           does have enough samples to output.
slouken@2716
   723
           This allows us to perform resampling in the conversion step, where the output of the
slouken@2716
   724
           resampling process can be any number. We will have to see what a good size for the
slouken@2716
   725
           stream's maximum length is, but I suspect 2*max(len_cvt, stream_len) is a good figure.
slouken@2716
   726
         */
slouken@2716
   727
        while (device->enabled) {
slouken@3336
   728
slouken@3336
   729
            if (device->paused) {
slouken@3336
   730
                SDL_Delay(delay);
slouken@3336
   731
                continue;
slouken@3336
   732
            }
slouken@3336
   733
slouken@2716
   734
            /* Only read in audio if the streamer doesn't have enough already (if it does not have enough samples to output) */
slouken@2716
   735
            if (SDL_StreamLength(&device->streamer) < stream_len) {
slouken@2716
   736
                /* Set up istream */
slouken@2716
   737
                if (device->convert.needed) {
slouken@2716
   738
                    if (device->convert.buf) {
slouken@2716
   739
                        istream = device->convert.buf;
slouken@2716
   740
                    } else {
slouken@2716
   741
                        continue;
slouken@2716
   742
                    }
slouken@2716
   743
                } else {
slouken@3336
   744
/* FIXME: Ryan, this is probably wrong.  I imagine we don't want to get
slouken@3336
   745
 * a device buffer both here and below in the stream output.
slouken@3336
   746
 */
slouken@2716
   747
                    istream = current_audio.impl.GetDeviceBuf(device);
slouken@2716
   748
                    if (istream == NULL) {
slouken@2716
   749
                        istream = device->fake_stream;
slouken@2716
   750
                    }
slouken@2716
   751
                }
slouken@0
   752
slouken@2716
   753
                /* Read from the callback into the _input_ stream */
icculus@9391
   754
                // !!! FIXME: this should be LockDevice.
slouken@6977
   755
                SDL_LockMutex(device->mixer_lock);
slouken@3336
   756
                (*fill) (udata, istream, istream_len);
slouken@6977
   757
                SDL_UnlockMutex(device->mixer_lock);
slouken@2716
   758
slouken@2716
   759
                /* Convert the audio if necessary and write to the streamer */
slouken@2716
   760
                if (device->convert.needed) {
slouken@2716
   761
                    SDL_ConvertAudio(&device->convert);
slouken@2716
   762
                    if (istream == NULL) {
slouken@2716
   763
                        istream = device->fake_stream;
slouken@2716
   764
                    }
gabomdq@7678
   765
                    /* SDL_memcpy(istream, device->convert.buf, device->convert.len_cvt); */
slouken@2716
   766
                    SDL_StreamWrite(&device->streamer, device->convert.buf,
slouken@2716
   767
                                    device->convert.len_cvt);
slouken@2716
   768
                } else {
slouken@2716
   769
                    SDL_StreamWrite(&device->streamer, istream, istream_len);
slouken@2716
   770
                }
slouken@1895
   771
            }
slouken@2716
   772
slouken@2716
   773
            /* Only output audio if the streamer has enough to output */
slouken@2716
   774
            if (SDL_StreamLength(&device->streamer) >= stream_len) {
slouken@2716
   775
                /* Set up the output stream */
slouken@2716
   776
                if (device->convert.needed) {
slouken@2716
   777
                    if (device->convert.buf) {
slouken@2716
   778
                        stream = device->convert.buf;
slouken@2716
   779
                    } else {
slouken@2716
   780
                        continue;
slouken@2716
   781
                    }
slouken@2716
   782
                } else {
slouken@2716
   783
                    stream = current_audio.impl.GetDeviceBuf(device);
slouken@2716
   784
                    if (stream == NULL) {
slouken@2716
   785
                        stream = device->fake_stream;
slouken@2716
   786
                    }
slouken@2716
   787
                }
slouken@2716
   788
slouken@2716
   789
                /* Now read from the streamer */
slouken@2716
   790
                SDL_StreamRead(&device->streamer, stream, stream_len);
slouken@2716
   791
slouken@2716
   792
                /* Ready current buffer for play and change current buffer */
slouken@3336
   793
                if (stream != device->fake_stream) {
slouken@2716
   794
                    current_audio.impl.PlayDevice(device);
slouken@3096
   795
                    /* Wait for an audio buffer to become available */
slouken@3096
   796
                    current_audio.impl.WaitDevice(device);
slouken@2716
   797
                } else {
slouken@3336
   798
                    SDL_Delay(delay);
slouken@2716
   799
                }
slouken@1895
   800
            }
slouken@0
   801
slouken@1895
   802
        }
icculus@8660
   803
    } else
icculus@8658
   804
#endif
icculus@8660
   805
    {
slouken@2716
   806
        /* Otherwise, do not use the streamer. This is the old code. */
icculus@7406
   807
        const int silence = (int) device->spec.silence;
slouken@0
   808
slouken@2716
   809
        /* Loop, filling the audio buffers */
slouken@2716
   810
        while (device->enabled) {
slouken@2716
   811
slouken@2716
   812
            /* Fill the current buffer with sound */
slouken@2716
   813
            if (device->convert.needed) {
slouken@2716
   814
                if (device->convert.buf) {
slouken@2716
   815
                    stream = device->convert.buf;
slouken@2716
   816
                } else {
slouken@2716
   817
                    continue;
slouken@2716
   818
                }
slouken@2716
   819
            } else {
slouken@2716
   820
                stream = current_audio.impl.GetDeviceBuf(device);
slouken@2716
   821
                if (stream == NULL) {
slouken@2716
   822
                    stream = device->fake_stream;
slouken@2716
   823
                }
slouken@1895
   824
            }
slouken@0
   825
icculus@9391
   826
            /* !!! FIXME: this should be LockDevice. */
icculus@7447
   827
            SDL_LockMutex(device->mixer_lock);
icculus@9393
   828
icculus@9393
   829
            /* Check again, in case device was removed while a lock was held. */
icculus@9393
   830
            if (!device->enabled) {
icculus@9393
   831
                SDL_UnlockMutex(device->mixer_lock);
icculus@9393
   832
                break;
icculus@9393
   833
            }
icculus@9393
   834
icculus@7406
   835
            if (device->paused) {
icculus@7406
   836
                SDL_memset(stream, silence, stream_len);
icculus@7406
   837
            } else {
icculus@7406
   838
                (*fill) (udata, stream, stream_len);
icculus@7406
   839
            }
icculus@7447
   840
            SDL_UnlockMutex(device->mixer_lock);
slouken@1562
   841
slouken@2716
   842
            /* Convert the audio if necessary */
slouken@2716
   843
            if (device->convert.needed) {
slouken@2716
   844
                SDL_ConvertAudio(&device->convert);
slouken@2716
   845
                stream = current_audio.impl.GetDeviceBuf(device);
slouken@2716
   846
                if (stream == NULL) {
slouken@2716
   847
                    stream = device->fake_stream;
slouken@2716
   848
                }
slouken@2716
   849
                SDL_memcpy(stream, device->convert.buf,
slouken@2716
   850
                           device->convert.len_cvt);
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@2049
  1084
    if (device->thread != NULL) {
icculus@2049
  1085
        SDL_WaitThread(device->thread, NULL);
icculus@2049
  1086
    }
icculus@2049
  1087
    if (device->mixer_lock != NULL) {
icculus@2049
  1088
        SDL_DestroyMutex(device->mixer_lock);
icculus@2049
  1089
    }
slouken@7719
  1090
    SDL_FreeAudioMem(device->fake_stream);
icculus@2049
  1091
    if (device->convert.needed) {
icculus@2049
  1092
        SDL_FreeAudioMem(device->convert.buf);
icculus@2049
  1093
    }
icculus@2049
  1094
    if (device->opened) {
icculus@2049
  1095
        current_audio.impl.CloseDevice(device);
icculus@2049
  1096
        device->opened = 0;
icculus@2049
  1097
    }
icculus@9012
  1098
icculus@9012
  1099
    free_audio_queue(device->buffer_queue_head);
icculus@9012
  1100
    free_audio_queue(device->buffer_queue_pool);
icculus@9012
  1101
icculus@2049
  1102
    SDL_FreeAudioMem(device);
icculus@2049
  1103
}
icculus@2049
  1104
icculus@2049
  1105
icculus@2049
  1106
/*
icculus@2049
  1107
 * Sanity check desired AudioSpec for SDL_OpenAudio() in (orig).
icculus@2049
  1108
 *  Fills in a sanitized copy in (prepared).
icculus@2049
  1109
 *  Returns non-zero if okay, zero on fatal parameters in (orig).
icculus@2049
  1110
 */
icculus@2049
  1111
static int
slouken@2060
  1112
prepare_audiospec(const SDL_AudioSpec * orig, SDL_AudioSpec * prepared)
icculus@2049
  1113
{
slouken@2060
  1114
    SDL_memcpy(prepared, orig, sizeof(SDL_AudioSpec));
icculus@2049
  1115
icculus@2049
  1116
    if (orig->freq == 0) {
icculus@2049
  1117
        const char *env = SDL_getenv("SDL_AUDIO_FREQUENCY");
slouken@2060
  1118
        if ((!env) || ((prepared->freq = SDL_atoi(env)) == 0)) {
slouken@2060
  1119
            prepared->freq = 22050;     /* a reasonable default */
slouken@1895
  1120
        }
slouken@1895
  1121
    }
icculus@2049
  1122
icculus@2049
  1123
    if (orig->format == 0) {
icculus@2049
  1124
        const char *env = SDL_getenv("SDL_AUDIO_FORMAT");
icculus@2049
  1125
        if ((!env) || ((prepared->format = SDL_ParseAudioFormat(env)) == 0)) {
slouken@2060
  1126
            prepared->format = AUDIO_S16;       /* a reasonable default */
slouken@1895
  1127
        }
slouken@1895
  1128
    }
icculus@2049
  1129
icculus@2049
  1130
    switch (orig->channels) {
slouken@2060
  1131
    case 0:{
slouken@2060
  1132
            const char *env = SDL_getenv("SDL_AUDIO_CHANNELS");
slouken@2141
  1133
            if ((!env) || ((prepared->channels = (Uint8) SDL_atoi(env)) == 0)) {
slouken@2060
  1134
                prepared->channels = 2; /* a reasonable default */
slouken@2060
  1135
            }
slouken@2060
  1136
            break;
icculus@2049
  1137
        }
slouken@1895
  1138
    case 1:                    /* Mono */
slouken@1895
  1139
    case 2:                    /* Stereo */
slouken@1895
  1140
    case 4:                    /* surround */
slouken@1895
  1141
    case 6:                    /* surround with center and lfe */
slouken@1895
  1142
        break;
slouken@1895
  1143
    default:
icculus@2049
  1144
        SDL_SetError("Unsupported number of audio channels.");
icculus@2049
  1145
        return 0;
slouken@1895
  1146
    }
icculus@2049
  1147
icculus@2049
  1148
    if (orig->samples == 0) {
icculus@2049
  1149
        const char *env = SDL_getenv("SDL_AUDIO_SAMPLES");
slouken@2060
  1150
        if ((!env) || ((prepared->samples = (Uint16) SDL_atoi(env)) == 0)) {
icculus@2049
  1151
            /* Pick a default of ~46 ms at desired frequency */
icculus@2049
  1152
            /* !!! FIXME: remove this when the non-Po2 resampling is in. */
icculus@2049
  1153
            const int samples = (prepared->freq / 1000) * 46;
icculus@2049
  1154
            int power2 = 1;
icculus@2049
  1155
            while (power2 < samples) {
icculus@2049
  1156
                power2 *= 2;
icculus@2049
  1157
            }
icculus@2049
  1158
            prepared->samples = power2;
slouken@1895
  1159
        }
slouken@1895
  1160
    }
slouken@0
  1161
slouken@1895
  1162
    /* Calculate the silence and size of the audio specification */
icculus@2049
  1163
    SDL_CalculateAudioSpec(prepared);
slouken@21
  1164
icculus@2049
  1165
    return 1;
icculus@2049
  1166
}
slouken@1408
  1167
icculus@2049
  1168
static SDL_AudioDeviceID
icculus@2049
  1169
open_audio_device(const char *devname, int iscapture,
slouken@2866
  1170
                  const SDL_AudioSpec * desired, SDL_AudioSpec * obtained,
slouken@2866
  1171
                  int allowed_changes, int min_id)
icculus@2049
  1172
{
icculus@2049
  1173
    SDL_AudioDeviceID id = 0;
slouken@2866
  1174
    SDL_AudioSpec _obtained;
icculus@2049
  1175
    SDL_AudioDevice *device;
slouken@2866
  1176
    SDL_bool build_cvt;
icculus@9394
  1177
    void *handle = NULL;
icculus@2049
  1178
    int i = 0;
slouken@21
  1179
icculus@2049
  1180
    if (!SDL_WasInit(SDL_INIT_AUDIO)) {
icculus@2049
  1181
        SDL_SetError("Audio subsystem is not initialized");
icculus@2049
  1182
        return 0;
icculus@2049
  1183
    }
icculus@2049
  1184
icculus@2049
  1185
    if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
icculus@2049
  1186
        SDL_SetError("No capture support");
icculus@2049
  1187
        return 0;
icculus@2049
  1188
    }
icculus@2049
  1189
icculus@9393
  1190
    /* Find an available device ID... */
icculus@9393
  1191
    for (id = min_id - 1; id < SDL_arraysize(open_devices); id++) {
icculus@9393
  1192
        if (open_devices[id] == NULL) {
icculus@9393
  1193
            break;
icculus@9393
  1194
        }
icculus@9393
  1195
    }
icculus@9393
  1196
icculus@9393
  1197
    if (id == SDL_arraysize(open_devices)) {
icculus@9393
  1198
        SDL_SetError("Too many open audio devices");
icculus@9393
  1199
        return 0;
icculus@9393
  1200
    }
icculus@9393
  1201
slouken@2866
  1202
    if (!obtained) {
slouken@2866
  1203
        obtained = &_obtained;
slouken@2866
  1204
    }
slouken@2866
  1205
    if (!prepare_audiospec(desired, obtained)) {
icculus@2049
  1206
        return 0;
icculus@2049
  1207
    }
icculus@2049
  1208
icculus@2049
  1209
    /* If app doesn't care about a specific device, let the user override. */
icculus@2049
  1210
    if (devname == NULL) {
icculus@2049
  1211
        devname = SDL_getenv("SDL_AUDIO_DEVICE_NAME");
slouken@1895
  1212
    }
slouken@21
  1213
icculus@2049
  1214
    /*
icculus@2049
  1215
     * Catch device names at the high level for the simple case...
icculus@2049
  1216
     * This lets us have a basic "device enumeration" for systems that
icculus@2049
  1217
     *  don't have multiple devices, but makes sure the device name is
icculus@2049
  1218
     *  always NULL when it hits the low level.
icculus@2049
  1219
     *
icculus@2049
  1220
     * Also make sure that the simple case prevents multiple simultaneous
icculus@2049
  1221
     *  opens of the default system device.
icculus@2049
  1222
     */
icculus@2049
  1223
icculus@2049
  1224
    if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
icculus@2049
  1225
        if ((devname) && (SDL_strcmp(devname, DEFAULT_INPUT_DEVNAME) != 0)) {
icculus@2049
  1226
            SDL_SetError("No such device");
icculus@2049
  1227
            return 0;
icculus@2049
  1228
        }
icculus@2049
  1229
        devname = NULL;
icculus@2049
  1230
icculus@2049
  1231
        for (i = 0; i < SDL_arraysize(open_devices); i++) {
icculus@2049
  1232
            if ((open_devices[i]) && (open_devices[i]->iscapture)) {
icculus@2049
  1233
                SDL_SetError("Audio device already open");
icculus@2049
  1234
                return 0;
icculus@2049
  1235
            }
icculus@2049
  1236
        }
icculus@9394
  1237
    } else if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
icculus@2049
  1238
        if ((devname) && (SDL_strcmp(devname, DEFAULT_OUTPUT_DEVNAME) != 0)) {
icculus@2049
  1239
            SDL_SetError("No such device");
icculus@2049
  1240
            return 0;
icculus@2049
  1241
        }
icculus@2049
  1242
        devname = NULL;
icculus@2049
  1243
icculus@2049
  1244
        for (i = 0; i < SDL_arraysize(open_devices); i++) {
icculus@2049
  1245
            if ((open_devices[i]) && (!open_devices[i]->iscapture)) {
icculus@2049
  1246
                SDL_SetError("Audio device already open");
icculus@2049
  1247
                return 0;
icculus@2049
  1248
            }
icculus@2049
  1249
        }
icculus@9394
  1250
    } else if (devname != NULL) {
icculus@9394
  1251
        /* if the app specifies an exact string, we can pass the backend
icculus@9394
  1252
           an actual device handle thingey, which saves them the effort of
icculus@9394
  1253
           figuring out what device this was (such as, reenumerating
icculus@9394
  1254
           everything again to find the matching human-readable name).
icculus@9394
  1255
           It might still need to open a device based on the string for,
icculus@9394
  1256
           say, a network audio server, but this optimizes some cases. */
icculus@9394
  1257
        SDL_AudioDeviceItem *item;
icculus@9394
  1258
        SDL_LockMutex(current_audio.detectionLock);
icculus@9394
  1259
        for (item = iscapture ? current_audio.inputDevices : current_audio.outputDevices; item; item = item->next) {
icculus@9394
  1260
            if ((item->handle != NULL) && (SDL_strcmp(item->name, devname) == 0)) {
icculus@9394
  1261
                handle = item->handle;
icculus@9394
  1262
                break;
icculus@9394
  1263
            }
icculus@9394
  1264
        }
icculus@9394
  1265
        SDL_UnlockMutex(current_audio.detectionLock);
icculus@9394
  1266
    }
icculus@9394
  1267
icculus@9394
  1268
    if (!current_audio.impl.AllowsArbitraryDeviceNames) {
icculus@9394
  1269
        /* has to be in our device list, or the default device. */
icculus@9394
  1270
        if ((handle == NULL) && (devname != NULL)) {
icculus@9394
  1271
            SDL_SetError("No such device.");
icculus@9394
  1272
            return 0;
icculus@9394
  1273
        }
icculus@2049
  1274
    }
icculus@2049
  1275
slouken@2060
  1276
    device = (SDL_AudioDevice *) SDL_AllocAudioMem(sizeof(SDL_AudioDevice));
icculus@2049
  1277
    if (device == NULL) {
icculus@2049
  1278
        SDL_OutOfMemory();
icculus@2049
  1279
        return 0;
icculus@2049
  1280
    }
icculus@9012
  1281
    SDL_zerop(device);
icculus@9393
  1282
    device->id = id + 1;
slouken@2866
  1283
    device->spec = *obtained;
icculus@2049
  1284
    device->enabled = 1;
icculus@2049
  1285
    device->paused = 1;
icculus@2049
  1286
    device->iscapture = iscapture;
icculus@2049
  1287
icculus@9391
  1288
    /* Create a mutex for locking the sound buffers */
icculus@2049
  1289
    if (!current_audio.impl.SkipMixerLock) {
icculus@2049
  1290
        device->mixer_lock = SDL_CreateMutex();
icculus@2049
  1291
        if (device->mixer_lock == NULL) {
icculus@2049
  1292
            close_audio_device(device);
icculus@2049
  1293
            SDL_SetError("Couldn't create mixer lock");
icculus@2049
  1294
            return 0;
icculus@2049
  1295
        }
icculus@2049
  1296
    }
icculus@2049
  1297
icculus@9394
  1298
    if (current_audio.impl.OpenDevice(device, handle, devname, iscapture) < 0) {
icculus@2049
  1299
        close_audio_device(device);
icculus@2049
  1300
        return 0;
icculus@2049
  1301
    }
icculus@2049
  1302
    device->opened = 1;
slouken@0
  1303
slouken@1895
  1304
    /* Allocate a fake audio memory buffer */
aschiffler@4865
  1305
    device->fake_stream = (Uint8 *)SDL_AllocAudioMem(device->spec.size);
icculus@2049
  1306
    if (device->fake_stream == NULL) {
icculus@2049
  1307
        close_audio_device(device);
slouken@1895
  1308
        SDL_OutOfMemory();
icculus@2049
  1309
        return 0;
slouken@1895
  1310
    }
slouken@0
  1311
slouken@1895
  1312
    /* See if we need to do any conversion */
slouken@2866
  1313
    build_cvt = SDL_FALSE;
slouken@2866
  1314
    if (obtained->freq != device->spec.freq) {
slouken@2866
  1315
        if (allowed_changes & SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) {
slouken@2866
  1316
            obtained->freq = device->spec.freq;
slouken@2866
  1317
        } else {
slouken@2866
  1318
            build_cvt = SDL_TRUE;
slouken@2866
  1319
        }
slouken@2866
  1320
    }
slouken@2866
  1321
    if (obtained->format != device->spec.format) {
slouken@2866
  1322
        if (allowed_changes & SDL_AUDIO_ALLOW_FORMAT_CHANGE) {
slouken@2866
  1323
            obtained->format = device->spec.format;
slouken@2866
  1324
        } else {
slouken@2866
  1325
            build_cvt = SDL_TRUE;
slouken@2866
  1326
        }
slouken@2866
  1327
    }
slouken@2866
  1328
    if (obtained->channels != device->spec.channels) {
slouken@2866
  1329
        if (allowed_changes & SDL_AUDIO_ALLOW_CHANNELS_CHANGE) {
slouken@2866
  1330
            obtained->channels = device->spec.channels;
slouken@2866
  1331
        } else {
slouken@2866
  1332
            build_cvt = SDL_TRUE;
slouken@2866
  1333
        }
slouken@2866
  1334
    }
slouken@7518
  1335
slouken@7518
  1336
    /* If the audio driver changes the buffer size, accept it.
slouken@7518
  1337
       This needs to be done after the format is modified above,
slouken@7518
  1338
       otherwise it might not have the correct buffer size.
slouken@7518
  1339
     */
slouken@7518
  1340
    if (device->spec.samples != obtained->samples) {
slouken@7518
  1341
        obtained->samples = device->spec.samples;
slouken@7518
  1342
        SDL_CalculateAudioSpec(obtained);
slouken@7518
  1343
    }
slouken@7518
  1344
slouken@2866
  1345
    if (build_cvt) {
slouken@1895
  1346
        /* Build an audio conversion block */
icculus@2049
  1347
        if (SDL_BuildAudioCVT(&device->convert,
slouken@2866
  1348
                              obtained->format, obtained->channels,
slouken@2866
  1349
                              obtained->freq,
icculus@2049
  1350
                              device->spec.format, device->spec.channels,
icculus@2049
  1351
                              device->spec.freq) < 0) {
icculus@2049
  1352
            close_audio_device(device);
icculus@2049
  1353
            return 0;
slouken@1895
  1354
        }
icculus@2049
  1355
        if (device->convert.needed) {
slouken@7518
  1356
            device->convert.len = (int) (((double) device->spec.size) /
slouken@2060
  1357
                                         device->convert.len_ratio);
icculus@2053
  1358
icculus@2049
  1359
            device->convert.buf =
icculus@2049
  1360
                (Uint8 *) SDL_AllocAudioMem(device->convert.len *
icculus@2049
  1361
                                            device->convert.len_mult);
icculus@2049
  1362
            if (device->convert.buf == NULL) {
icculus@2049
  1363
                close_audio_device(device);
slouken@1895
  1364
                SDL_OutOfMemory();
icculus@2049
  1365
                return 0;
slouken@1895
  1366
            }
slouken@1895
  1367
        }
slouken@1895
  1368
    }
icculus@2049
  1369
icculus@9012
  1370
    if (device->spec.callback == NULL) {  /* use buffer queueing? */
icculus@9012
  1371
        /* pool a few packets to start. Enough for two callbacks. */
icculus@9012
  1372
        const int packetlen = SDL_AUDIOBUFFERQUEUE_PACKETLEN;
icculus@9012
  1373
        const int wantbytes = ((device->convert.needed) ? device->convert.len : device->spec.size) * 2;
icculus@9012
  1374
        const int wantpackets = (wantbytes / packetlen) + ((wantbytes % packetlen) ? packetlen : 0);
icculus@9012
  1375
        for (i = 0; i < wantpackets; i++) {
icculus@9012
  1376
            SDL_AudioBufferQueue *packet = (SDL_AudioBufferQueue *) SDL_malloc(sizeof (SDL_AudioBufferQueue));
icculus@9012
  1377
            if (packet) { /* don't care if this fails, we'll deal later. */
icculus@9012
  1378
                packet->datalen = 0;
icculus@9012
  1379
                packet->startpos = 0;
icculus@9012
  1380
                packet->next = device->buffer_queue_pool;
icculus@9012
  1381
                device->buffer_queue_pool = packet;
icculus@9012
  1382
            }
icculus@9012
  1383
        }
icculus@9012
  1384
icculus@9012
  1385
        device->spec.callback = SDL_BufferQueueDrainCallback;
icculus@9012
  1386
        device->spec.userdata = device;
icculus@9012
  1387
    }
icculus@9012
  1388
icculus@9393
  1389
    /* add it to our list of open devices. */
icculus@9393
  1390
    open_devices[id] = device;
icculus@2049
  1391
slouken@1895
  1392
    /* Start the audio thread if necessary */
icculus@2049
  1393
    if (!current_audio.impl.ProvidesOwnCallbackThread) {
slouken@1895
  1394
        /* Start the audio thread */
icculus@5969
  1395
        char name[64];
icculus@9393
  1396
        SDL_snprintf(name, sizeof (name), "SDLAudioDev%d", (int) device->id);
icculus@2049
  1397
/* !!! FIXME: this is nasty. */
icculus@6430
  1398
#if defined(__WIN32__) && !defined(HAVE_LIBC)
slouken@1330
  1399
#undef SDL_CreateThread
icculus@8094
  1400
#if SDL_DYNAMIC_API
icculus@8094
  1401
        device->thread = SDL_CreateThread_REAL(SDL_RunAudio, name, device, NULL, NULL);
icculus@8094
  1402
#else
icculus@5969
  1403
        device->thread = SDL_CreateThread(SDL_RunAudio, name, device, NULL, NULL);
icculus@8094
  1404
#endif
slouken@1330
  1405
#else
icculus@5969
  1406
        device->thread = SDL_CreateThread(SDL_RunAudio, name, device);
slouken@1330
  1407
#endif
icculus@2049
  1408
        if (device->thread == NULL) {
icculus@9393
  1409
            SDL_CloseAudioDevice(device->id);
slouken@1895
  1410
            SDL_SetError("Couldn't create audio thread");
icculus@2049
  1411
            return 0;
icculus@2049
  1412
        }
icculus@2049
  1413
    }
icculus@2049
  1414
icculus@9393
  1415
    return device->id;
icculus@2049
  1416
}
icculus@2049
  1417
icculus@2049
  1418
icculus@2049
  1419
int
slouken@2866
  1420
SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained)
icculus@2049
  1421
{
icculus@2049
  1422
    SDL_AudioDeviceID id = 0;
icculus@2049
  1423
icculus@2049
  1424
    /* Start up the audio driver, if necessary. This is legacy behaviour! */
icculus@2049
  1425
    if (!SDL_WasInit(SDL_INIT_AUDIO)) {
icculus@2049
  1426
        if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
icculus@9382
  1427
            return -1;
slouken@1895
  1428
        }
icculus@2049
  1429
    }
slouken@0
  1430
icculus@2049
  1431
    /* SDL_OpenAudio() is legacy and can only act on Device ID #1. */
icculus@2049
  1432
    if (open_devices[0] != NULL) {
icculus@2049
  1433
        SDL_SetError("Audio device is already opened");
icculus@9382
  1434
        return -1;
slouken@1895
  1435
    }
icculus@2049
  1436
slouken@2866
  1437
    if (obtained) {
slouken@2866
  1438
        id = open_audio_device(NULL, 0, desired, obtained,
slouken@2866
  1439
                               SDL_AUDIO_ALLOW_ANY_CHANGE, 1);
slouken@2866
  1440
    } else {
slouken@8919
  1441
        id = open_audio_device(NULL, 0, desired, NULL, 0, 1);
slouken@2866
  1442
    }
slouken@21
  1443
icculus@5964
  1444
    SDL_assert((id == 0) || (id == 1));
icculus@9382
  1445
    return (id == 0) ? -1 : 0;
icculus@2049
  1446
}
slouken@21
  1447
icculus@2049
  1448
SDL_AudioDeviceID
icculus@2049
  1449
SDL_OpenAudioDevice(const char *device, int iscapture,
slouken@2866
  1450
                    const SDL_AudioSpec * desired, SDL_AudioSpec * obtained,
slouken@2866
  1451
                    int allowed_changes)
icculus@2049
  1452
{
slouken@2866
  1453
    return open_audio_device(device, iscapture, desired, obtained,
slouken@2866
  1454
                             allowed_changes, 2);
slouken@1895
  1455
}
slouken@1895
  1456
slouken@3537
  1457
SDL_AudioStatus
icculus@2049
  1458
SDL_GetAudioDeviceStatus(SDL_AudioDeviceID devid)
slouken@1895
  1459
{
icculus@2049
  1460
    SDL_AudioDevice *device = get_audio_device(devid);
slouken@3537
  1461
    SDL_AudioStatus status = SDL_AUDIO_STOPPED;
icculus@2049
  1462
    if (device && device->enabled) {
icculus@2049
  1463
        if (device->paused) {
slouken@1895
  1464
            status = SDL_AUDIO_PAUSED;
slouken@1895
  1465
        } else {
slouken@1895
  1466
            status = SDL_AUDIO_PLAYING;
slouken@1895
  1467
        }
slouken@1895
  1468
    }
icculus@9382
  1469
    return status;
slouken@0
  1470
}
slouken@0
  1471
icculus@2049
  1472
slouken@3537
  1473
SDL_AudioStatus
icculus@2049
  1474
SDL_GetAudioStatus(void)
icculus@2049
  1475
{
icculus@2049
  1476
    return SDL_GetAudioDeviceStatus(1);
icculus@2049
  1477
}
icculus@2049
  1478
icculus@2049
  1479
void
icculus@2049
  1480
SDL_PauseAudioDevice(SDL_AudioDeviceID devid, int pause_on)
icculus@2049
  1481
{
icculus@2049
  1482
    SDL_AudioDevice *device = get_audio_device(devid);
gabomdq@9148
  1483
    if (device) {
gabomdq@9148
  1484
        current_audio.impl.LockDevice(device);
icculus@2049
  1485
        device->paused = pause_on;
gabomdq@9148
  1486
        current_audio.impl.UnlockDevice(device);
icculus@2049
  1487
    }
icculus@2049
  1488
}
icculus@2049
  1489
slouken@1895
  1490
void
slouken@1895
  1491
SDL_PauseAudio(int pause_on)
slouken@0
  1492
{
gabomdq@9148
  1493
    SDL_PauseAudioDevice(1, pause_on);
icculus@2049
  1494
}
icculus@2049
  1495
slouken@0
  1496
icculus@2049
  1497
void
icculus@2049
  1498
SDL_LockAudioDevice(SDL_AudioDeviceID devid)
icculus@2049
  1499
{
icculus@2049
  1500
    /* Obtain a lock on the mixing buffers */
icculus@2049
  1501
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@2049
  1502
    if (device) {
icculus@2049
  1503
        current_audio.impl.LockDevice(device);
slouken@1895
  1504
    }
slouken@0
  1505
}
slouken@0
  1506
slouken@1895
  1507
void
slouken@1895
  1508
SDL_LockAudio(void)
slouken@0
  1509
{
icculus@2049
  1510
    SDL_LockAudioDevice(1);
icculus@2049
  1511
}
slouken@0
  1512
icculus@2049
  1513
void
icculus@2049
  1514
SDL_UnlockAudioDevice(SDL_AudioDeviceID devid)
icculus@2049
  1515
{
slouken@1895
  1516
    /* Obtain a lock on the mixing buffers */
icculus@2049
  1517
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@2049
  1518
    if (device) {
icculus@2049
  1519
        current_audio.impl.UnlockDevice(device);
slouken@1895
  1520
    }
slouken@0
  1521
}
slouken@0
  1522
slouken@1895
  1523
void
slouken@1895
  1524
SDL_UnlockAudio(void)
slouken@0
  1525
{
icculus@2049
  1526
    SDL_UnlockAudioDevice(1);
icculus@2049
  1527
}
slouken@0
  1528
icculus@2049
  1529
void
icculus@2049
  1530
SDL_CloseAudioDevice(SDL_AudioDeviceID devid)
icculus@2049
  1531
{
icculus@2049
  1532
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@2049
  1533
    if (device) {
icculus@2049
  1534
        close_audio_device(device);
slouken@2060
  1535
        open_devices[devid - 1] = NULL;
slouken@1895
  1536
    }
slouken@0
  1537
}
slouken@0
  1538
slouken@1895
  1539
void
slouken@1895
  1540
SDL_CloseAudio(void)
slouken@0
  1541
{
icculus@2049
  1542
    SDL_CloseAudioDevice(1);
slouken@0
  1543
}
slouken@0
  1544
slouken@1895
  1545
void
slouken@1895
  1546
SDL_AudioQuit(void)
slouken@0
  1547
{
icculus@7348
  1548
    SDL_AudioDeviceID i;
icculus@7348
  1549
icculus@7345
  1550
    if (!current_audio.name) {  /* not initialized?! */
icculus@7345
  1551
        return;
icculus@7345
  1552
    }
icculus@7345
  1553
icculus@2049
  1554
    for (i = 0; i < SDL_arraysize(open_devices); i++) {
icculus@7342
  1555
        if (open_devices[i] != NULL) {
icculus@7446
  1556
            SDL_CloseAudioDevice(i+1);
icculus@7342
  1557
        }
icculus@2049
  1558
    }
slouken@0
  1559
icculus@9394
  1560
    free_device_list(&current_audio.outputDevices, &current_audio.outputDeviceCount);
icculus@9394
  1561
    free_device_list(&current_audio.inputDevices, &current_audio.inputDeviceCount);
icculus@9394
  1562
icculus@2049
  1563
    /* Free the driver data */
icculus@2049
  1564
    current_audio.impl.Deinitialize();
icculus@9393
  1565
icculus@9394
  1566
    SDL_DestroyMutex(current_audio.detectionLock);
icculus@9393
  1567
icculus@9393
  1568
    SDL_zero(current_audio);
icculus@9393
  1569
    SDL_zero(open_devices);
slouken@0
  1570
}
slouken@0
  1571
icculus@1982
  1572
#define NUM_FORMATS 10
slouken@0
  1573
static int format_idx;
slouken@0
  1574
static int format_idx_sub;
icculus@1982
  1575
static SDL_AudioFormat format_list[NUM_FORMATS][NUM_FORMATS] = {
slouken@1895
  1576
    {AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB,
icculus@1982
  1577
     AUDIO_U16MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB},
slouken@1895
  1578
    {AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB,
icculus@1982
  1579
     AUDIO_U16MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB},
icculus@1982
  1580
    {AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S32LSB,
icculus@1982
  1581
     AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_U8, AUDIO_S8},
icculus@1982
  1582
    {AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S32MSB,
icculus@1982
  1583
     AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_U8, AUDIO_S8},
icculus@1982
  1584
    {AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_S32LSB,
icculus@1982
  1585
     AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_U8, AUDIO_S8},
icculus@1982
  1586
    {AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_S32MSB,
icculus@1982
  1587
     AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_U8, AUDIO_S8},
icculus@1993
  1588
    {AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_S16LSB,
icculus@1993
  1589
     AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8},
icculus@1993
  1590
    {AUDIO_S32MSB, AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_S16MSB,
icculus@1993
  1591
     AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8},
icculus@1993
  1592
    {AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_S16LSB,
icculus@1993
  1593
     AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8},
icculus@1993
  1594
    {AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_S32MSB, AUDIO_S32LSB, AUDIO_S16MSB,
icculus@1993
  1595
     AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8},
slouken@0
  1596
};
slouken@0
  1597
icculus@1982
  1598
SDL_AudioFormat
icculus@1982
  1599
SDL_FirstAudioFormat(SDL_AudioFormat format)
slouken@0
  1600
{
slouken@1895
  1601
    for (format_idx = 0; format_idx < NUM_FORMATS; ++format_idx) {
slouken@1895
  1602
        if (format_list[format_idx][0] == format) {
slouken@1895
  1603
            break;
slouken@1895
  1604
        }
slouken@1895
  1605
    }
slouken@1895
  1606
    format_idx_sub = 0;
icculus@9382
  1607
    return SDL_NextAudioFormat();
slouken@0
  1608
}
slouken@0
  1609
icculus@1982
  1610
SDL_AudioFormat
slouken@1895
  1611
SDL_NextAudioFormat(void)
slouken@0
  1612
{
slouken@1895
  1613
    if ((format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS)) {
icculus@9382
  1614
        return 0;
slouken@1895
  1615
    }
icculus@9382
  1616
    return format_list[format_idx][format_idx_sub++];
slouken@0
  1617
}
slouken@0
  1618
slouken@1895
  1619
void
slouken@1895
  1620
SDL_CalculateAudioSpec(SDL_AudioSpec * spec)
slouken@0
  1621
{
slouken@1895
  1622
    switch (spec->format) {
slouken@1895
  1623
    case AUDIO_U8:
slouken@1895
  1624
        spec->silence = 0x80;
slouken@1895
  1625
        break;
slouken@1895
  1626
    default:
slouken@1895
  1627
        spec->silence = 0x00;
slouken@1895
  1628
        break;
slouken@1895
  1629
    }
icculus@2049
  1630
    spec->size = SDL_AUDIO_BITSIZE(spec->format) / 8;
slouken@1895
  1631
    spec->size *= spec->channels;
slouken@1895
  1632
    spec->size *= spec->samples;
slouken@0
  1633
}
slouken@1895
  1634
icculus@2049
  1635
icculus@2049
  1636
/*
icculus@2049
  1637
 * Moved here from SDL_mixer.c, since it relies on internals of an opened
icculus@2049
  1638
 *  audio device (and is deprecated, by the way!).
icculus@2049
  1639
 */
icculus@2049
  1640
void
icculus@2049
  1641
SDL_MixAudio(Uint8 * dst, const Uint8 * src, Uint32 len, int volume)
icculus@2049
  1642
{
icculus@2049
  1643
    /* Mix the user-level audio format */
icculus@2049
  1644
    SDL_AudioDevice *device = get_audio_device(1);
icculus@2049
  1645
    if (device != NULL) {
icculus@2049
  1646
        SDL_AudioFormat format;
icculus@2049
  1647
        if (device->convert.needed) {
icculus@2049
  1648
            format = device->convert.src_format;
icculus@2049
  1649
        } else {
icculus@2049
  1650
            format = device->spec.format;
icculus@2049
  1651
        }
icculus@2049
  1652
        SDL_MixAudioFormat(dst, src, format, len, volume);
icculus@2049
  1653
    }
icculus@2049
  1654
}
icculus@2049
  1655
slouken@1895
  1656
/* vi: set ts=4 sw=4 expandtab: */