src/audio/SDL_audio.c
author Ryan C. Gordon
Fri, 07 Oct 2016 14:35:25 -0400
changeset 10471 fd0bebfab510
parent 10468 9f14a7dfaa6a
child 10472 a3d50ebe2a3b
permissions -rw-r--r--
audio: threading and device hang improvements.

This tries to make SDL robust against device drivers that have hung up,
apps don't freeze in catastrophic (but not necessarily uncommon) conditions.

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