src/audio/SDL_audio.c
author Sam Lantinga
Mon, 09 Jan 2017 11:58:01 -0800
changeset 10802 6afc9b833867
parent 10798 1af225fe35cc
child 10815 71bbe3233508
permissions -rw-r--r--
We only need the first few keymaps corresponding to the following constants:
K_NORMTAB, K_SHIFTTAB, K_ALTTAB, K_ALTSHIFTTAB

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