src/audio/SDL_audio.c
author Ryan C. Gordon
Sun, 14 Jul 2013 12:42:12 -0400
changeset 7447 6860de5bcb8c
parent 7446 6cbb6aaedc20
child 7518 febc83bc2a43
permissions -rw-r--r--
Protect SDL_PauseAudio*() with the audio callback lock.

Otherwise, you can pause audio and still have the callback running, or run
one more time. This makes sure the callback is definitely stopped by the
time you return from SDL_PauseAudio().
slouken@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@6885
     3
  Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
slouken@0
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@0
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@0
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    20
*/
slouken@1402
    21
#include "SDL_config.h"
slouken@0
    22
slouken@0
    23
/* Allow access to a raw mixing buffer */
slouken@0
    24
slouken@0
    25
#include "SDL.h"
slouken@2984
    26
#include "SDL_audio.h"
slouken@0
    27
#include "SDL_audio_c.h"
slouken@0
    28
#include "SDL_audiomem.h"
slouken@0
    29
#include "SDL_sysaudio.h"
slouken@0
    30
aschiffler@4865
    31
#define _THIS SDL_AudioDevice *_this
icculus@2049
    32
icculus@2049
    33
static SDL_AudioDriver current_audio;
icculus@2049
    34
static SDL_AudioDevice *open_devices[16];
icculus@2049
    35
icculus@2049
    36
/* !!! FIXME: These are wordy and unlocalized... */
icculus@2049
    37
#define DEFAULT_OUTPUT_DEVNAME "System audio output device"
icculus@2049
    38
#define DEFAULT_INPUT_DEVNAME "System audio capture device"
icculus@2049
    39
icculus@2049
    40
icculus@2049
    41
/*
icculus@2049
    42
 * Not all of these will be compiled and linked in, but it's convenient
icculus@2049
    43
 *  to have a complete list here and saves yet-another block of #ifdefs...
icculus@2049
    44
 *  Please see bootstrap[], below, for the actual #ifdef mess.
icculus@2049
    45
 */
icculus@2049
    46
extern AudioBootStrap BSD_AUDIO_bootstrap;
icculus@2049
    47
extern AudioBootStrap DSP_bootstrap;
icculus@2049
    48
extern AudioBootStrap ALSA_bootstrap;
icculus@2271
    49
extern AudioBootStrap PULSEAUDIO_bootstrap;
slouken@3099
    50
extern AudioBootStrap QSAAUDIO_bootstrap;
icculus@2049
    51
extern AudioBootStrap SUNAUDIO_bootstrap;
icculus@2049
    52
extern AudioBootStrap ARTS_bootstrap;
icculus@2049
    53
extern AudioBootStrap ESD_bootstrap;
icculus@2049
    54
extern AudioBootStrap NAS_bootstrap;
icculus@5592
    55
extern AudioBootStrap XAUDIO2_bootstrap;
icculus@2049
    56
extern AudioBootStrap DSOUND_bootstrap;
icculus@5588
    57
extern AudioBootStrap WINMM_bootstrap;
icculus@2049
    58
extern AudioBootStrap PAUDIO_bootstrap;
icculus@2049
    59
extern AudioBootStrap BEOSAUDIO_bootstrap;
icculus@2049
    60
extern AudioBootStrap COREAUDIO_bootstrap;
icculus@2049
    61
extern AudioBootStrap SNDMGR_bootstrap;
icculus@2049
    62
extern AudioBootStrap DISKAUD_bootstrap;
icculus@2049
    63
extern AudioBootStrap DUMMYAUD_bootstrap;
icculus@2049
    64
extern AudioBootStrap DCAUD_bootstrap;
icculus@2049
    65
extern AudioBootStrap DART_bootstrap;
slouken@2735
    66
extern AudioBootStrap NDSAUD_bootstrap;
slouken@2947
    67
extern AudioBootStrap FUSIONSOUND_bootstrap;
paul@4718
    68
extern AudioBootStrap ANDROIDAUD_bootstrap;
kimonline@7009
    69
extern AudioBootStrap PSPAUD_bootstrap;
icculus@7367
    70
extern AudioBootStrap SNDIO_bootstrap;
icculus@1190
    71
slouken@0
    72
/* Available audio drivers */
slouken@3162
    73
static const AudioBootStrap *const bootstrap[] = {
icculus@2939
    74
#if SDL_AUDIO_DRIVER_PULSEAUDIO
icculus@2939
    75
    &PULSEAUDIO_bootstrap,
slouken@0
    76
#endif
slouken@1361
    77
#if SDL_AUDIO_DRIVER_ALSA
slouken@1895
    78
    &ALSA_bootstrap,
slouken@0
    79
#endif
icculus@7367
    80
#if SDL_AUDIO_DRIVER_SNDIO
icculus@7367
    81
    &SNDIO_bootstrap,
icculus@7367
    82
#endif
slouken@4548
    83
#if SDL_AUDIO_DRIVER_BSD
slouken@4548
    84
    &BSD_AUDIO_bootstrap,
slouken@4548
    85
#endif
icculus@2939
    86
#if SDL_AUDIO_DRIVER_OSS
icculus@2939
    87
    &DSP_bootstrap,
icculus@2271
    88
#endif
slouken@3099
    89
#if SDL_AUDIO_DRIVER_QSA
slouken@3099
    90
    &QSAAUDIO_bootstrap,
slouken@663
    91
#endif
slouken@1361
    92
#if SDL_AUDIO_DRIVER_SUNAUDIO
slouken@1895
    93
    &SUNAUDIO_bootstrap,
slouken@148
    94
#endif
slouken@1361
    95
#if SDL_AUDIO_DRIVER_ARTS
slouken@1895
    96
    &ARTS_bootstrap,
slouken@0
    97
#endif
slouken@1361
    98
#if SDL_AUDIO_DRIVER_ESD
slouken@1895
    99
    &ESD_bootstrap,
slouken@0
   100
#endif
slouken@1361
   101
#if SDL_AUDIO_DRIVER_NAS
slouken@1895
   102
    &NAS_bootstrap,
slouken@0
   103
#endif
icculus@5592
   104
#if SDL_AUDIO_DRIVER_XAUDIO2
icculus@5592
   105
    &XAUDIO2_bootstrap,
icculus@5592
   106
#endif
slouken@1361
   107
#if SDL_AUDIO_DRIVER_DSOUND
slouken@1895
   108
    &DSOUND_bootstrap,
slouken@0
   109
#endif
icculus@5588
   110
#if SDL_AUDIO_DRIVER_WINMM
icculus@5588
   111
    &WINMM_bootstrap,
slouken@0
   112
#endif
icculus@2049
   113
#if SDL_AUDIO_DRIVER_PAUDIO
icculus@2049
   114
    &PAUDIO_bootstrap,
slouken@1361
   115
#endif
icculus@2049
   116
#if SDL_AUDIO_DRIVER_BEOSAUDIO
icculus@2049
   117
    &BEOSAUDIO_bootstrap,
slouken@0
   118
#endif
slouken@1361
   119
#if SDL_AUDIO_DRIVER_COREAUDIO
slouken@1895
   120
    &COREAUDIO_bootstrap,
slouken@935
   121
#endif
slouken@1361
   122
#if SDL_AUDIO_DRIVER_DISK
slouken@1895
   123
    &DISKAUD_bootstrap,
slouken@68
   124
#endif
icculus@1532
   125
#if SDL_AUDIO_DRIVER_DUMMY
slouken@1895
   126
    &DUMMYAUD_bootstrap,
icculus@1532
   127
#endif
slouken@2947
   128
#if SDL_AUDIO_DRIVER_FUSIONSOUND
slouken@2947
   129
    &FUSIONSOUND_bootstrap,
slouken@2947
   130
#endif
paul@4718
   131
#if SDL_AUDIO_DRIVER_ANDROID
paul@4718
   132
    &ANDROIDAUD_bootstrap,
paul@4718
   133
#endif
kimonline@7009
   134
#if SDL_AUDIO_DRIVER_PSP
kimonline@7009
   135
    &PSPAUD_bootstrap,
kimonline@7009
   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@5593
   155
SDL_AudioDetectDevices_Default(int iscapture, SDL_AddAudioDevice addfn)
icculus@5593
   156
{                               /* no-op. */
slouken@2060
   157
}
slouken@2735
   158
slouken@2060
   159
static void
slouken@2060
   160
SDL_AudioThreadInit_Default(_THIS)
slouken@2060
   161
{                               /* no-op. */
slouken@2060
   162
}
slouken@2735
   163
slouken@2060
   164
static void
slouken@2060
   165
SDL_AudioWaitDevice_Default(_THIS)
slouken@2060
   166
{                               /* no-op. */
slouken@2060
   167
}
slouken@2735
   168
slouken@2060
   169
static void
slouken@2060
   170
SDL_AudioPlayDevice_Default(_THIS)
slouken@2060
   171
{                               /* no-op. */
slouken@2060
   172
}
slouken@2735
   173
slouken@2060
   174
static Uint8 *
slouken@2060
   175
SDL_AudioGetDeviceBuf_Default(_THIS)
slouken@2060
   176
{
slouken@2060
   177
    return NULL;
slouken@2060
   178
}
slouken@2735
   179
slouken@2060
   180
static void
slouken@2060
   181
SDL_AudioWaitDone_Default(_THIS)
slouken@2060
   182
{                               /* no-op. */
slouken@2060
   183
}
slouken@2735
   184
slouken@2060
   185
static void
slouken@2060
   186
SDL_AudioCloseDevice_Default(_THIS)
slouken@2060
   187
{                               /* no-op. */
slouken@2060
   188
}
slouken@2735
   189
slouken@2060
   190
static void
slouken@2060
   191
SDL_AudioDeinitialize_Default(void)
slouken@2060
   192
{                               /* no-op. */
slouken@2060
   193
}
icculus@2049
   194
icculus@2049
   195
static int
icculus@2049
   196
SDL_AudioOpenDevice_Default(_THIS, const char *devname, int iscapture)
icculus@2049
   197
{
icculus@7038
   198
    return -1;
icculus@2049
   199
}
icculus@2049
   200
icculus@2049
   201
static void
icculus@2049
   202
SDL_AudioLockDevice_Default(SDL_AudioDevice * device)
icculus@2049
   203
{
icculus@2049
   204
    if (device->thread && (SDL_ThreadID() == device->threadid)) {
icculus@2049
   205
        return;
icculus@2049
   206
    }
slouken@6977
   207
    SDL_LockMutex(device->mixer_lock);
icculus@2049
   208
}
icculus@2049
   209
icculus@2049
   210
static void
icculus@2049
   211
SDL_AudioUnlockDevice_Default(SDL_AudioDevice * device)
icculus@2049
   212
{
icculus@2049
   213
    if (device->thread && (SDL_ThreadID() == device->threadid)) {
icculus@2049
   214
        return;
icculus@2049
   215
    }
slouken@6977
   216
    SDL_UnlockMutex(device->mixer_lock);
icculus@2049
   217
}
icculus@2049
   218
slouken@0
   219
slouken@2060
   220
static void
slouken@2060
   221
finalize_audio_entry_points(void)
icculus@2049
   222
{
icculus@2049
   223
    /*
icculus@2049
   224
     * Fill in stub functions for unused driver entry points. This lets us
icculus@2049
   225
     *  blindly call them without having to check for validity first.
icculus@2049
   226
     */
icculus@2049
   227
slouken@2060
   228
#define FILL_STUB(x) \
icculus@2049
   229
        if (current_audio.impl.x == NULL) { \
icculus@2049
   230
            current_audio.impl.x = SDL_Audio##x##_Default; \
icculus@2049
   231
        }
icculus@2049
   232
    FILL_STUB(DetectDevices);
icculus@2049
   233
    FILL_STUB(OpenDevice);
icculus@2049
   234
    FILL_STUB(ThreadInit);
icculus@2049
   235
    FILL_STUB(WaitDevice);
icculus@2049
   236
    FILL_STUB(PlayDevice);
icculus@2049
   237
    FILL_STUB(GetDeviceBuf);
icculus@2049
   238
    FILL_STUB(WaitDone);
icculus@2049
   239
    FILL_STUB(CloseDevice);
icculus@2049
   240
    FILL_STUB(LockDevice);
icculus@2049
   241
    FILL_STUB(UnlockDevice);
icculus@2049
   242
    FILL_STUB(Deinitialize);
slouken@2060
   243
#undef FILL_STUB
icculus@2049
   244
}
icculus@2049
   245
slouken@2716
   246
/* Streaming functions (for when the input and output buffer sizes are different) */
slouken@2716
   247
/* Write [length] bytes from buf into the streamer */
slouken@4472
   248
static void
slouken@2716
   249
SDL_StreamWrite(SDL_AudioStreamer * stream, Uint8 * buf, int length)
slouken@2716
   250
{
slouken@2716
   251
    int i;
slouken@2716
   252
slouken@2716
   253
    for (i = 0; i < length; ++i) {
slouken@2716
   254
        stream->buffer[stream->write_pos] = buf[i];
slouken@2716
   255
        ++stream->write_pos;
slouken@2716
   256
    }
slouken@2716
   257
}
slouken@2716
   258
slouken@2716
   259
/* Read [length] bytes out of the streamer into buf */
slouken@4472
   260
static void
slouken@2716
   261
SDL_StreamRead(SDL_AudioStreamer * stream, Uint8 * buf, int length)
slouken@2716
   262
{
slouken@2716
   263
    int i;
slouken@2716
   264
slouken@2716
   265
    for (i = 0; i < length; ++i) {
slouken@2716
   266
        buf[i] = stream->buffer[stream->read_pos];
slouken@2716
   267
        ++stream->read_pos;
slouken@2716
   268
    }
slouken@2716
   269
}
slouken@2716
   270
slouken@4472
   271
static int
slouken@2716
   272
SDL_StreamLength(SDL_AudioStreamer * stream)
slouken@2716
   273
{
slouken@2716
   274
    return (stream->write_pos - stream->read_pos) % stream->max_len;
slouken@2716
   275
}
slouken@2716
   276
slouken@2716
   277
/* Initialize the stream by allocating the buffer and setting the read/write heads to the beginning */
slouken@4507
   278
#if 0
slouken@4472
   279
static int
slouken@2716
   280
SDL_StreamInit(SDL_AudioStreamer * stream, int max_len, Uint8 silence)
slouken@2716
   281
{
slouken@2716
   282
    /* First try to allocate the buffer */
slouken@2728
   283
    stream->buffer = (Uint8 *) SDL_malloc(max_len);
slouken@2716
   284
    if (stream->buffer == NULL) {
slouken@2716
   285
        return -1;
slouken@2716
   286
    }
slouken@2716
   287
slouken@2716
   288
    stream->max_len = max_len;
slouken@2716
   289
    stream->read_pos = 0;
slouken@2716
   290
    stream->write_pos = 0;
slouken@2716
   291
slouken@2716
   292
    /* Zero out the buffer */
slouken@2728
   293
    SDL_memset(stream->buffer, silence, max_len);
slouken@2728
   294
slouken@2728
   295
    return 0;
slouken@2716
   296
}
slouken@4507
   297
#endif
slouken@2716
   298
slouken@2716
   299
/* Deinitialize the stream simply by freeing the buffer */
slouken@4472
   300
static void
slouken@2716
   301
SDL_StreamDeinit(SDL_AudioStreamer * stream)
slouken@2716
   302
{
slouken@2716
   303
    if (stream->buffer != NULL) {
slouken@2728
   304
        SDL_free(stream->buffer);
slouken@2716
   305
    }
slouken@2716
   306
}
slouken@2716
   307
aschiffler@4865
   308
#if defined(ANDROID)
paul@4722
   309
#include <android/log.h>
aschiffler@4865
   310
#endif
paul@4722
   311
slouken@0
   312
/* The general mixing thread function */
slouken@1895
   313
int SDLCALL
icculus@2049
   314
SDL_RunAudio(void *devicep)
slouken@0
   315
{
icculus@2049
   316
    SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
slouken@1895
   317
    Uint8 *stream;
slouken@1895
   318
    int stream_len;
slouken@1895
   319
    void *udata;
slouken@1895
   320
    void (SDLCALL * fill) (void *userdata, Uint8 * stream, int len);
slouken@3336
   321
    Uint32 delay;
airlangga@5526
   322
    /* For streaming when the buffer sizes don't match up */
airlangga@5526
   323
    Uint8 *istream;
airlangga@5526
   324
    int istream_len = 0;
slouken@2716
   325
slouken@5509
   326
    /* The audio mixing is always a high priority thread */
slouken@5509
   327
    SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
slouken@5509
   328
slouken@1895
   329
    /* Perform any thread setup */
icculus@2049
   330
    device->threadid = SDL_ThreadID();
icculus@2049
   331
    current_audio.impl.ThreadInit(device);
slouken@0
   332
slouken@1895
   333
    /* Set up the mixing function */
icculus@2049
   334
    fill = device->spec.callback;
icculus@2049
   335
    udata = device->spec.userdata;
slouken@21
   336
slouken@2716
   337
    /* By default do not stream */
slouken@2716
   338
    device->use_streamer = 0;
slouken@2716
   339
icculus@2049
   340
    if (device->convert.needed) {
slouken@3040
   341
#if 0                           /* !!! FIXME: I took len_div out of the structure. Use rate_incr instead? */
slouken@2716
   342
        /* If the result of the conversion alters the length, i.e. resampling is being used, use the streamer */
slouken@2716
   343
        if (device->convert.len_mult != 1 || device->convert.len_div != 1) {
slouken@2716
   344
            /* The streamer's maximum length should be twice whichever is larger: spec.size or len_cvt */
slouken@2716
   345
            stream_max_len = 2 * device->spec.size;
slouken@2716
   346
            if (device->convert.len_mult > device->convert.len_div) {
slouken@2716
   347
                stream_max_len *= device->convert.len_mult;
slouken@2716
   348
                stream_max_len /= device->convert.len_div;
slouken@2716
   349
            }
slouken@2716
   350
            if (SDL_StreamInit(&device->streamer, stream_max_len, silence) <
slouken@2716
   351
                0)
slouken@2716
   352
                return -1;
slouken@2716
   353
            device->use_streamer = 1;
slouken@2716
   354
slouken@2716
   355
            /* istream_len should be the length of what we grab from the callback and feed to conversion,
slouken@2716
   356
               so that we get close to spec_size. I.e. we want device.spec_size = istream_len * u / d
slouken@2716
   357
             */
slouken@2716
   358
            istream_len =
slouken@2716
   359
                device->spec.size * device->convert.len_div /
slouken@2716
   360
                device->convert.len_mult;
slouken@2716
   361
        }
icculus@3021
   362
#endif
slouken@2716
   363
slouken@2716
   364
        /* stream_len = device->convert.len; */
slouken@2716
   365
        stream_len = device->spec.size;
slouken@1895
   366
    } else {
icculus@2049
   367
        stream_len = device->spec.size;
slouken@1895
   368
    }
icculus@1561
   369
slouken@3336
   370
    /* Calculate the delay while paused */
slouken@3336
   371
    delay = ((device->spec.samples * 1000) / device->spec.freq);
slouken@3336
   372
slouken@2716
   373
    /* Determine if the streamer is necessary here */
slouken@2716
   374
    if (device->use_streamer == 1) {
slouken@3336
   375
        /* This code is almost the same as the old code. The difference is, instead of reading
slouken@2716
   376
           directly from the callback into "stream", then converting and sending the audio off,
slouken@2716
   377
           we go: callback -> "istream" -> (conversion) -> streamer -> stream -> device.
slouken@2716
   378
           However, reading and writing with streamer are done separately:
slouken@2716
   379
           - We only call the callback and write to the streamer when the streamer does not
slouken@2716
   380
           contain enough samples to output to the device.
slouken@2716
   381
           - We only read from the streamer and tell the device to play when the streamer
slouken@2716
   382
           does have enough samples to output.
slouken@2716
   383
           This allows us to perform resampling in the conversion step, where the output of the
slouken@2716
   384
           resampling process can be any number. We will have to see what a good size for the
slouken@2716
   385
           stream's maximum length is, but I suspect 2*max(len_cvt, stream_len) is a good figure.
slouken@2716
   386
         */
slouken@2716
   387
        while (device->enabled) {
slouken@3336
   388
slouken@3336
   389
            if (device->paused) {
slouken@3336
   390
                SDL_Delay(delay);
slouken@3336
   391
                continue;
slouken@3336
   392
            }
slouken@3336
   393
slouken@2716
   394
            /* Only read in audio if the streamer doesn't have enough already (if it does not have enough samples to output) */
slouken@2716
   395
            if (SDL_StreamLength(&device->streamer) < stream_len) {
slouken@2716
   396
                /* Set up istream */
slouken@2716
   397
                if (device->convert.needed) {
slouken@2716
   398
                    if (device->convert.buf) {
slouken@2716
   399
                        istream = device->convert.buf;
slouken@2716
   400
                    } else {
slouken@2716
   401
                        continue;
slouken@2716
   402
                    }
slouken@2716
   403
                } else {
slouken@3336
   404
/* FIXME: Ryan, this is probably wrong.  I imagine we don't want to get
slouken@3336
   405
 * a device buffer both here and below in the stream output.
slouken@3336
   406
 */
slouken@2716
   407
                    istream = current_audio.impl.GetDeviceBuf(device);
slouken@2716
   408
                    if (istream == NULL) {
slouken@2716
   409
                        istream = device->fake_stream;
slouken@2716
   410
                    }
slouken@2716
   411
                }
slouken@0
   412
slouken@2716
   413
                /* Read from the callback into the _input_ stream */
slouken@6977
   414
                SDL_LockMutex(device->mixer_lock);
slouken@3336
   415
                (*fill) (udata, istream, istream_len);
slouken@6977
   416
                SDL_UnlockMutex(device->mixer_lock);
slouken@2716
   417
slouken@2716
   418
                /* Convert the audio if necessary and write to the streamer */
slouken@2716
   419
                if (device->convert.needed) {
slouken@2716
   420
                    SDL_ConvertAudio(&device->convert);
slouken@2716
   421
                    if (istream == NULL) {
slouken@2716
   422
                        istream = device->fake_stream;
slouken@2716
   423
                    }
slouken@2716
   424
                    /*SDL_memcpy(istream, device->convert.buf, device->convert.len_cvt); */
slouken@2716
   425
                    SDL_StreamWrite(&device->streamer, device->convert.buf,
slouken@2716
   426
                                    device->convert.len_cvt);
slouken@2716
   427
                } else {
slouken@2716
   428
                    SDL_StreamWrite(&device->streamer, istream, istream_len);
slouken@2716
   429
                }
slouken@1895
   430
            }
slouken@2716
   431
slouken@2716
   432
            /* Only output audio if the streamer has enough to output */
slouken@2716
   433
            if (SDL_StreamLength(&device->streamer) >= stream_len) {
slouken@2716
   434
                /* Set up the output stream */
slouken@2716
   435
                if (device->convert.needed) {
slouken@2716
   436
                    if (device->convert.buf) {
slouken@2716
   437
                        stream = device->convert.buf;
slouken@2716
   438
                    } else {
slouken@2716
   439
                        continue;
slouken@2716
   440
                    }
slouken@2716
   441
                } else {
slouken@2716
   442
                    stream = current_audio.impl.GetDeviceBuf(device);
slouken@2716
   443
                    if (stream == NULL) {
slouken@2716
   444
                        stream = device->fake_stream;
slouken@2716
   445
                    }
slouken@2716
   446
                }
slouken@2716
   447
slouken@2716
   448
                /* Now read from the streamer */
slouken@2716
   449
                SDL_StreamRead(&device->streamer, stream, stream_len);
slouken@2716
   450
slouken@2716
   451
                /* Ready current buffer for play and change current buffer */
slouken@3336
   452
                if (stream != device->fake_stream) {
slouken@2716
   453
                    current_audio.impl.PlayDevice(device);
slouken@3096
   454
                    /* Wait for an audio buffer to become available */
slouken@3096
   455
                    current_audio.impl.WaitDevice(device);
slouken@2716
   456
                } else {
slouken@3336
   457
                    SDL_Delay(delay);
slouken@2716
   458
                }
slouken@1895
   459
            }
slouken@0
   460
slouken@1895
   461
        }
slouken@2716
   462
    } else {
slouken@2716
   463
        /* Otherwise, do not use the streamer. This is the old code. */
icculus@7406
   464
        const int silence = (int) device->spec.silence;
slouken@0
   465
slouken@2716
   466
        /* Loop, filling the audio buffers */
slouken@2716
   467
        while (device->enabled) {
slouken@2716
   468
slouken@2716
   469
            /* Fill the current buffer with sound */
slouken@2716
   470
            if (device->convert.needed) {
slouken@2716
   471
                if (device->convert.buf) {
slouken@2716
   472
                    stream = device->convert.buf;
slouken@2716
   473
                } else {
slouken@2716
   474
                    continue;
slouken@2716
   475
                }
slouken@2716
   476
            } else {
slouken@2716
   477
                stream = current_audio.impl.GetDeviceBuf(device);
slouken@2716
   478
                if (stream == NULL) {
slouken@2716
   479
                    stream = device->fake_stream;
slouken@2716
   480
                }
slouken@1895
   481
            }
slouken@0
   482
icculus@7447
   483
            SDL_LockMutex(device->mixer_lock);
icculus@7406
   484
            if (device->paused) {
icculus@7406
   485
                SDL_memset(stream, silence, stream_len);
icculus@7406
   486
            } else {
icculus@7406
   487
                (*fill) (udata, stream, stream_len);
icculus@7406
   488
            }
icculus@7447
   489
            SDL_UnlockMutex(device->mixer_lock);
slouken@1562
   490
slouken@2716
   491
            /* Convert the audio if necessary */
slouken@2716
   492
            if (device->convert.needed) {
slouken@2716
   493
                SDL_ConvertAudio(&device->convert);
slouken@2716
   494
                stream = current_audio.impl.GetDeviceBuf(device);
slouken@2716
   495
                if (stream == NULL) {
slouken@2716
   496
                    stream = device->fake_stream;
slouken@2716
   497
                }
slouken@2716
   498
                SDL_memcpy(stream, device->convert.buf,
slouken@2716
   499
                           device->convert.len_cvt);
slouken@2716
   500
            }
slouken@2716
   501
slouken@2716
   502
            /* Ready current buffer for play and change current buffer */
slouken@3336
   503
            if (stream != device->fake_stream) {
slouken@2716
   504
                current_audio.impl.PlayDevice(device);
slouken@3096
   505
                /* Wait for an audio buffer to become available */
slouken@3096
   506
                current_audio.impl.WaitDevice(device);
slouken@2716
   507
            } else {
slouken@3336
   508
                SDL_Delay(delay);
slouken@2716
   509
            }
slouken@1895
   510
        }
slouken@1895
   511
    }
slouken@1562
   512
slouken@1895
   513
    /* Wait for the audio to drain.. */
icculus@2049
   514
    current_audio.impl.WaitDone(device);
slouken@21
   515
slouken@2716
   516
    /* If necessary, deinit the streamer */
slouken@2716
   517
    if (device->use_streamer == 1)
slouken@2716
   518
        SDL_StreamDeinit(&device->streamer);
slouken@2716
   519
slouken@1895
   520
    return (0);
slouken@0
   521
}
slouken@0
   522
slouken@322
   523
icculus@1982
   524
static SDL_AudioFormat
slouken@1895
   525
SDL_ParseAudioFormat(const char *string)
slouken@1794
   526
{
icculus@2076
   527
#define CHECK_FMT_STRING(x) if (SDL_strcmp(string, #x) == 0) return AUDIO_##x
icculus@2049
   528
    CHECK_FMT_STRING(U8);
icculus@2049
   529
    CHECK_FMT_STRING(S8);
icculus@2049
   530
    CHECK_FMT_STRING(U16LSB);
icculus@2049
   531
    CHECK_FMT_STRING(S16LSB);
icculus@2049
   532
    CHECK_FMT_STRING(U16MSB);
icculus@2049
   533
    CHECK_FMT_STRING(S16MSB);
icculus@2049
   534
    CHECK_FMT_STRING(U16SYS);
icculus@2049
   535
    CHECK_FMT_STRING(S16SYS);
icculus@2049
   536
    CHECK_FMT_STRING(U16);
icculus@2049
   537
    CHECK_FMT_STRING(S16);
icculus@2049
   538
    CHECK_FMT_STRING(S32LSB);
icculus@2049
   539
    CHECK_FMT_STRING(S32MSB);
icculus@2049
   540
    CHECK_FMT_STRING(S32SYS);
icculus@2049
   541
    CHECK_FMT_STRING(S32);
icculus@2049
   542
    CHECK_FMT_STRING(F32LSB);
icculus@2049
   543
    CHECK_FMT_STRING(F32MSB);
icculus@2049
   544
    CHECK_FMT_STRING(F32SYS);
icculus@2049
   545
    CHECK_FMT_STRING(F32);
slouken@2060
   546
#undef CHECK_FMT_STRING
icculus@2049
   547
    return 0;
slouken@1895
   548
}
slouken@1895
   549
slouken@1895
   550
int
slouken@1895
   551
SDL_GetNumAudioDrivers(void)
slouken@1895
   552
{
slouken@1895
   553
    return (SDL_arraysize(bootstrap) - 1);
slouken@1895
   554
}
slouken@1895
   555
slouken@1895
   556
const char *
slouken@1895
   557
SDL_GetAudioDriver(int index)
slouken@1895
   558
{
slouken@1895
   559
    if (index >= 0 && index < SDL_GetNumAudioDrivers()) {
slouken@1895
   560
        return (bootstrap[index]->name);
slouken@1895
   561
    }
slouken@1895
   562
    return (NULL);
slouken@1794
   563
}
slouken@1794
   564
slouken@1895
   565
int
slouken@1895
   566
SDL_AudioInit(const char *driver_name)
slouken@0
   567
{
icculus@2049
   568
    int i = 0;
icculus@2049
   569
    int initialized = 0;
icculus@2049
   570
    int tried_to_init = 0;
slouken@0
   571
icculus@2049
   572
    if (SDL_WasInit(SDL_INIT_AUDIO)) {
slouken@2060
   573
        SDL_AudioQuit();        /* shutdown driver if already running. */
slouken@1895
   574
    }
slouken@0
   575
slouken@2060
   576
    SDL_memset(&current_audio, '\0', sizeof(current_audio));
slouken@2060
   577
    SDL_memset(open_devices, '\0', sizeof(open_devices));
icculus@2049
   578
slouken@1895
   579
    /* Select the proper audio driver */
slouken@1909
   580
    if (driver_name == NULL) {
slouken@1909
   581
        driver_name = SDL_getenv("SDL_AUDIODRIVER");
slouken@1909
   582
    }
icculus@2049
   583
icculus@2049
   584
    for (i = 0; (!initialized) && (bootstrap[i]); ++i) {
icculus@2049
   585
        /* make sure we should even try this driver before doing so... */
icculus@2049
   586
        const AudioBootStrap *backend = bootstrap[i];
slouken@6900
   587
        if ((driver_name && (SDL_strncasecmp(backend->name, driver_name, SDL_strlen(driver_name)) != 0)) ||
slouken@6900
   588
            (!driver_name && backend->demand_only)) {
icculus@2049
   589
            continue;
icculus@2049
   590
        }
slouken@0
   591
icculus@2049
   592
        tried_to_init = 1;
slouken@2060
   593
        SDL_memset(&current_audio, 0, sizeof(current_audio));
icculus@2049
   594
        current_audio.name = backend->name;
icculus@2049
   595
        current_audio.desc = backend->desc;
icculus@3699
   596
        initialized = backend->init(&current_audio.impl);
slouken@1895
   597
    }
icculus@2049
   598
icculus@2049
   599
    if (!initialized) {
icculus@2049
   600
        /* specific drivers will set the error message if they fail... */
icculus@2049
   601
        if (!tried_to_init) {
slouken@1895
   602
            if (driver_name) {
icculus@3699
   603
                SDL_SetError("Audio target '%s' not available", driver_name);
slouken@1895
   604
            } else {
slouken@1895
   605
                SDL_SetError("No available audio device");
slouken@1895
   606
            }
slouken@1895
   607
        }
icculus@2049
   608
slouken@2060
   609
        SDL_memset(&current_audio, 0, sizeof(current_audio));
slouken@2060
   610
        return (-1);            /* No driver was available, so fail. */
slouken@1895
   611
    }
icculus@2049
   612
icculus@2049
   613
    finalize_audio_entry_points();
icculus@2049
   614
slouken@1895
   615
    return (0);
slouken@0
   616
}
slouken@0
   617
slouken@1895
   618
/*
slouken@1895
   619
 * Get the current audio driver name
slouken@1895
   620
 */
slouken@1895
   621
const char *
slouken@1895
   622
SDL_GetCurrentAudioDriver()
slouken@0
   623
{
icculus@2049
   624
    return current_audio.name;
slouken@0
   625
}
slouken@0
   626
icculus@5593
   627
static void
icculus@5593
   628
free_device_list(char ***devices, int *devCount)
icculus@5593
   629
{
icculus@5593
   630
    int i = *devCount;
icculus@5593
   631
    if ((i > 0) && (*devices != NULL)) {
icculus@5593
   632
        while (i--) {
icculus@5593
   633
            SDL_free((*devices)[i]);
icculus@5593
   634
        }
icculus@5593
   635
    }
icculus@5593
   636
icculus@5593
   637
    if (*devices != NULL) {
icculus@5593
   638
        SDL_free(*devices);
icculus@5593
   639
    }
icculus@5593
   640
icculus@5593
   641
    *devices = NULL;
icculus@5593
   642
    *devCount = 0;
icculus@5593
   643
}
icculus@5593
   644
icculus@5593
   645
static
icculus@5593
   646
void SDL_AddCaptureAudioDevice(const char *_name)
icculus@5593
   647
{
icculus@5593
   648
    char *name = NULL;
icculus@5593
   649
    void *ptr = SDL_realloc(current_audio.inputDevices,
icculus@5593
   650
                          (current_audio.inputDeviceCount+1) * sizeof(char*));
icculus@5593
   651
    if (ptr == NULL) {
icculus@5593
   652
        return;  /* oh well. */
icculus@5593
   653
    }
icculus@5593
   654
icculus@5593
   655
    current_audio.inputDevices = (char **) ptr;
icculus@5593
   656
    name = SDL_strdup(_name);  /* if this returns NULL, that's okay. */
icculus@5593
   657
    current_audio.inputDevices[current_audio.inputDeviceCount++] = name;
icculus@5593
   658
}
icculus@5593
   659
icculus@5593
   660
static
icculus@5593
   661
void SDL_AddOutputAudioDevice(const char *_name)
icculus@5593
   662
{
icculus@5593
   663
    char *name = NULL;
icculus@5593
   664
    void *ptr = SDL_realloc(current_audio.outputDevices,
icculus@5593
   665
                          (current_audio.outputDeviceCount+1) * sizeof(char*));
icculus@5593
   666
    if (ptr == NULL) {
icculus@5593
   667
        return;  /* oh well. */
icculus@5593
   668
    }
icculus@5593
   669
icculus@5593
   670
    current_audio.outputDevices = (char **) ptr;
icculus@5593
   671
    name = SDL_strdup(_name);  /* if this returns NULL, that's okay. */
icculus@5593
   672
    current_audio.outputDevices[current_audio.outputDeviceCount++] = name;
icculus@5593
   673
}
icculus@5593
   674
icculus@2049
   675
slouken@1895
   676
int
icculus@2049
   677
SDL_GetNumAudioDevices(int iscapture)
slouken@0
   678
{
icculus@5593
   679
    int retval = 0;
icculus@5593
   680
icculus@2049
   681
    if (!SDL_WasInit(SDL_INIT_AUDIO)) {
icculus@2049
   682
        return -1;
icculus@2049
   683
    }
icculus@5593
   684
icculus@2049
   685
    if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
icculus@2049
   686
        return 0;
icculus@2049
   687
    }
icculus@2049
   688
icculus@2049
   689
    if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
icculus@2049
   690
        return 1;
icculus@2049
   691
    }
icculus@2049
   692
icculus@2049
   693
    if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
icculus@2049
   694
        return 1;
icculus@2049
   695
    }
icculus@2049
   696
icculus@5593
   697
    if (iscapture) {
icculus@5593
   698
        free_device_list(&current_audio.inputDevices,
icculus@5593
   699
                         &current_audio.inputDeviceCount);
icculus@5593
   700
        current_audio.impl.DetectDevices(iscapture, SDL_AddCaptureAudioDevice);
icculus@5593
   701
        retval = current_audio.inputDeviceCount;
icculus@5593
   702
    } else {
icculus@5593
   703
        free_device_list(&current_audio.outputDevices,
icculus@5593
   704
                         &current_audio.outputDeviceCount);
icculus@5593
   705
        current_audio.impl.DetectDevices(iscapture, SDL_AddOutputAudioDevice);
icculus@5593
   706
        retval = current_audio.outputDeviceCount;
icculus@5593
   707
    }
icculus@5593
   708
icculus@5593
   709
    return retval;
icculus@2049
   710
}
icculus@2049
   711
slouken@0
   712
icculus@2049
   713
const char *
icculus@2049
   714
SDL_GetAudioDeviceName(int index, int iscapture)
icculus@2049
   715
{
icculus@2049
   716
    if (!SDL_WasInit(SDL_INIT_AUDIO)) {
icculus@2049
   717
        SDL_SetError("Audio subsystem is not initialized");
icculus@2049
   718
        return NULL;
icculus@2049
   719
    }
icculus@2049
   720
icculus@2049
   721
    if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
icculus@2049
   722
        SDL_SetError("No capture support");
icculus@2049
   723
        return NULL;
slouken@1895
   724
    }
icculus@2049
   725
icculus@2049
   726
    if (index < 0) {
icculus@5593
   727
        goto no_such_device;
icculus@2049
   728
    }
slouken@0
   729
icculus@2049
   730
    if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
icculus@2049
   731
        return DEFAULT_INPUT_DEVNAME;
icculus@2049
   732
    }
icculus@2049
   733
icculus@2049
   734
    if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
icculus@2049
   735
        return DEFAULT_OUTPUT_DEVNAME;
slouken@1895
   736
    }
slouken@262
   737
icculus@5593
   738
    if (iscapture) {
icculus@5593
   739
        if (index >= current_audio.inputDeviceCount) {
icculus@5593
   740
            goto no_such_device;
icculus@5593
   741
        }
icculus@5593
   742
        return current_audio.inputDevices[index];
icculus@5593
   743
    } else {
icculus@5593
   744
        if (index >= current_audio.outputDeviceCount) {
icculus@5593
   745
            goto no_such_device;
icculus@5593
   746
        }
icculus@5593
   747
        return current_audio.outputDevices[index];
icculus@5593
   748
    }
icculus@5593
   749
icculus@5593
   750
no_such_device:
icculus@5593
   751
    SDL_SetError("No such device");
icculus@5593
   752
    return NULL;
icculus@2049
   753
}
icculus@2049
   754
icculus@2049
   755
icculus@2049
   756
static void
slouken@2060
   757
close_audio_device(SDL_AudioDevice * device)
icculus@2049
   758
{
icculus@2049
   759
    device->enabled = 0;
icculus@2049
   760
    if (device->thread != NULL) {
icculus@2049
   761
        SDL_WaitThread(device->thread, NULL);
icculus@2049
   762
    }
icculus@2049
   763
    if (device->mixer_lock != NULL) {
icculus@2049
   764
        SDL_DestroyMutex(device->mixer_lock);
icculus@2049
   765
    }
icculus@2049
   766
    if (device->fake_stream != NULL) {
icculus@2049
   767
        SDL_FreeAudioMem(device->fake_stream);
icculus@2049
   768
    }
icculus@2049
   769
    if (device->convert.needed) {
icculus@2049
   770
        SDL_FreeAudioMem(device->convert.buf);
icculus@2049
   771
    }
icculus@2049
   772
    if (device->opened) {
icculus@2049
   773
        current_audio.impl.CloseDevice(device);
icculus@2049
   774
        device->opened = 0;
icculus@2049
   775
    }
icculus@2049
   776
    SDL_FreeAudioMem(device);
icculus@2049
   777
}
icculus@2049
   778
icculus@2049
   779
icculus@2049
   780
/*
icculus@2049
   781
 * Sanity check desired AudioSpec for SDL_OpenAudio() in (orig).
icculus@2049
   782
 *  Fills in a sanitized copy in (prepared).
icculus@2049
   783
 *  Returns non-zero if okay, zero on fatal parameters in (orig).
icculus@2049
   784
 */
icculus@2049
   785
static int
slouken@2060
   786
prepare_audiospec(const SDL_AudioSpec * orig, SDL_AudioSpec * prepared)
icculus@2049
   787
{
slouken@2060
   788
    SDL_memcpy(prepared, orig, sizeof(SDL_AudioSpec));
icculus@2049
   789
icculus@2049
   790
    if (orig->callback == NULL) {
icculus@2049
   791
        SDL_SetError("SDL_OpenAudio() passed a NULL callback");
icculus@2049
   792
        return 0;
icculus@2049
   793
    }
icculus@2049
   794
icculus@2049
   795
    if (orig->freq == 0) {
icculus@2049
   796
        const char *env = SDL_getenv("SDL_AUDIO_FREQUENCY");
slouken@2060
   797
        if ((!env) || ((prepared->freq = SDL_atoi(env)) == 0)) {
slouken@2060
   798
            prepared->freq = 22050;     /* a reasonable default */
slouken@1895
   799
        }
slouken@1895
   800
    }
icculus@2049
   801
icculus@2049
   802
    if (orig->format == 0) {
icculus@2049
   803
        const char *env = SDL_getenv("SDL_AUDIO_FORMAT");
icculus@2049
   804
        if ((!env) || ((prepared->format = SDL_ParseAudioFormat(env)) == 0)) {
slouken@2060
   805
            prepared->format = AUDIO_S16;       /* a reasonable default */
slouken@1895
   806
        }
slouken@1895
   807
    }
icculus@2049
   808
icculus@2049
   809
    switch (orig->channels) {
slouken@2060
   810
    case 0:{
slouken@2060
   811
            const char *env = SDL_getenv("SDL_AUDIO_CHANNELS");
slouken@2141
   812
            if ((!env) || ((prepared->channels = (Uint8) SDL_atoi(env)) == 0)) {
slouken@2060
   813
                prepared->channels = 2; /* a reasonable default */
slouken@2060
   814
            }
slouken@2060
   815
            break;
icculus@2049
   816
        }
slouken@1895
   817
    case 1:                    /* Mono */
slouken@1895
   818
    case 2:                    /* Stereo */
slouken@1895
   819
    case 4:                    /* surround */
slouken@1895
   820
    case 6:                    /* surround with center and lfe */
slouken@1895
   821
        break;
slouken@1895
   822
    default:
icculus@2049
   823
        SDL_SetError("Unsupported number of audio channels.");
icculus@2049
   824
        return 0;
slouken@1895
   825
    }
icculus@2049
   826
icculus@2049
   827
    if (orig->samples == 0) {
icculus@2049
   828
        const char *env = SDL_getenv("SDL_AUDIO_SAMPLES");
slouken@2060
   829
        if ((!env) || ((prepared->samples = (Uint16) SDL_atoi(env)) == 0)) {
icculus@2049
   830
            /* Pick a default of ~46 ms at desired frequency */
icculus@2049
   831
            /* !!! FIXME: remove this when the non-Po2 resampling is in. */
icculus@2049
   832
            const int samples = (prepared->freq / 1000) * 46;
icculus@2049
   833
            int power2 = 1;
icculus@2049
   834
            while (power2 < samples) {
icculus@2049
   835
                power2 *= 2;
icculus@2049
   836
            }
icculus@2049
   837
            prepared->samples = power2;
slouken@1895
   838
        }
slouken@1895
   839
    }
slouken@0
   840
slouken@1895
   841
    /* Calculate the silence and size of the audio specification */
icculus@2049
   842
    SDL_CalculateAudioSpec(prepared);
slouken@21
   843
icculus@2049
   844
    return 1;
icculus@2049
   845
}
slouken@1408
   846
slouken@21
   847
icculus@2049
   848
static SDL_AudioDeviceID
icculus@2049
   849
open_audio_device(const char *devname, int iscapture,
slouken@2866
   850
                  const SDL_AudioSpec * desired, SDL_AudioSpec * obtained,
slouken@2866
   851
                  int allowed_changes, int min_id)
icculus@2049
   852
{
icculus@2049
   853
    SDL_AudioDeviceID id = 0;
slouken@2866
   854
    SDL_AudioSpec _obtained;
icculus@2049
   855
    SDL_AudioDevice *device;
slouken@2866
   856
    SDL_bool build_cvt;
icculus@2049
   857
    int i = 0;
slouken@21
   858
icculus@2049
   859
    if (!SDL_WasInit(SDL_INIT_AUDIO)) {
icculus@2049
   860
        SDL_SetError("Audio subsystem is not initialized");
icculus@2049
   861
        return 0;
icculus@2049
   862
    }
icculus@2049
   863
icculus@2049
   864
    if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
icculus@2049
   865
        SDL_SetError("No capture support");
icculus@2049
   866
        return 0;
icculus@2049
   867
    }
icculus@2049
   868
slouken@2866
   869
    if (!obtained) {
slouken@2866
   870
        obtained = &_obtained;
slouken@2866
   871
    }
slouken@2866
   872
    if (!prepare_audiospec(desired, obtained)) {
icculus@2049
   873
        return 0;
icculus@2049
   874
    }
icculus@2049
   875
icculus@2049
   876
    /* If app doesn't care about a specific device, let the user override. */
icculus@2049
   877
    if (devname == NULL) {
icculus@2049
   878
        devname = SDL_getenv("SDL_AUDIO_DEVICE_NAME");
slouken@1895
   879
    }
slouken@21
   880
icculus@2049
   881
    /*
icculus@2049
   882
     * Catch device names at the high level for the simple case...
icculus@2049
   883
     * This lets us have a basic "device enumeration" for systems that
icculus@2049
   884
     *  don't have multiple devices, but makes sure the device name is
icculus@2049
   885
     *  always NULL when it hits the low level.
icculus@2049
   886
     *
icculus@2049
   887
     * Also make sure that the simple case prevents multiple simultaneous
icculus@2049
   888
     *  opens of the default system device.
icculus@2049
   889
     */
icculus@2049
   890
icculus@2049
   891
    if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
icculus@2049
   892
        if ((devname) && (SDL_strcmp(devname, DEFAULT_INPUT_DEVNAME) != 0)) {
icculus@2049
   893
            SDL_SetError("No such device");
icculus@2049
   894
            return 0;
icculus@2049
   895
        }
icculus@2049
   896
        devname = NULL;
icculus@2049
   897
icculus@2049
   898
        for (i = 0; i < SDL_arraysize(open_devices); i++) {
icculus@2049
   899
            if ((open_devices[i]) && (open_devices[i]->iscapture)) {
icculus@2049
   900
                SDL_SetError("Audio device already open");
icculus@2049
   901
                return 0;
icculus@2049
   902
            }
icculus@2049
   903
        }
icculus@2049
   904
    }
icculus@2049
   905
icculus@2049
   906
    if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
icculus@2049
   907
        if ((devname) && (SDL_strcmp(devname, DEFAULT_OUTPUT_DEVNAME) != 0)) {
icculus@2049
   908
            SDL_SetError("No such device");
icculus@2049
   909
            return 0;
icculus@2049
   910
        }
icculus@2049
   911
        devname = NULL;
icculus@2049
   912
icculus@2049
   913
        for (i = 0; i < SDL_arraysize(open_devices); i++) {
icculus@2049
   914
            if ((open_devices[i]) && (!open_devices[i]->iscapture)) {
icculus@2049
   915
                SDL_SetError("Audio device already open");
icculus@2049
   916
                return 0;
icculus@2049
   917
            }
icculus@2049
   918
        }
icculus@2049
   919
    }
icculus@2049
   920
slouken@2060
   921
    device = (SDL_AudioDevice *) SDL_AllocAudioMem(sizeof(SDL_AudioDevice));
icculus@2049
   922
    if (device == NULL) {
icculus@2049
   923
        SDL_OutOfMemory();
icculus@2049
   924
        return 0;
icculus@2049
   925
    }
slouken@2060
   926
    SDL_memset(device, '\0', sizeof(SDL_AudioDevice));
slouken@2866
   927
    device->spec = *obtained;
icculus@2049
   928
    device->enabled = 1;
icculus@2049
   929
    device->paused = 1;
icculus@2049
   930
    device->iscapture = iscapture;
icculus@2049
   931
icculus@2049
   932
    /* Create a semaphore for locking the sound buffers */
icculus@2049
   933
    if (!current_audio.impl.SkipMixerLock) {
icculus@2049
   934
        device->mixer_lock = SDL_CreateMutex();
icculus@2049
   935
        if (device->mixer_lock == NULL) {
icculus@2049
   936
            close_audio_device(device);
icculus@2049
   937
            SDL_SetError("Couldn't create mixer lock");
icculus@2049
   938
            return 0;
icculus@2049
   939
        }
icculus@2049
   940
    }
icculus@2049
   941
icculus@5593
   942
    /* force a device detection if we haven't done one yet. */
icculus@5593
   943
    if ( ((iscapture) && (current_audio.inputDevices == NULL)) ||
icculus@5593
   944
         ((!iscapture) && (current_audio.outputDevices == NULL)) )
icculus@5593
   945
        SDL_GetNumAudioDevices(iscapture);
icculus@5593
   946
icculus@7038
   947
    if (current_audio.impl.OpenDevice(device, devname, iscapture) < 0) {
icculus@2049
   948
        close_audio_device(device);
icculus@2049
   949
        return 0;
icculus@2049
   950
    }
icculus@2049
   951
    device->opened = 1;
slouken@0
   952
slouken@1895
   953
    /* Allocate a fake audio memory buffer */
aschiffler@4865
   954
    device->fake_stream = (Uint8 *)SDL_AllocAudioMem(device->spec.size);
icculus@2049
   955
    if (device->fake_stream == NULL) {
icculus@2049
   956
        close_audio_device(device);
slouken@1895
   957
        SDL_OutOfMemory();
icculus@2049
   958
        return 0;
slouken@1895
   959
    }
slouken@0
   960
slouken@2866
   961
    /* If the audio driver changes the buffer size, accept it */
slouken@2866
   962
    if (device->spec.samples != obtained->samples) {
slouken@2866
   963
        obtained->samples = device->spec.samples;
slouken@2866
   964
        SDL_CalculateAudioSpec(obtained);
slouken@2866
   965
    }
slouken@2866
   966
slouken@1895
   967
    /* See if we need to do any conversion */
slouken@2866
   968
    build_cvt = SDL_FALSE;
slouken@2866
   969
    if (obtained->freq != device->spec.freq) {
slouken@2866
   970
        if (allowed_changes & SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) {
slouken@2866
   971
            obtained->freq = device->spec.freq;
slouken@2866
   972
        } else {
slouken@2866
   973
            build_cvt = SDL_TRUE;
slouken@2866
   974
        }
slouken@2866
   975
    }
slouken@2866
   976
    if (obtained->format != device->spec.format) {
slouken@2866
   977
        if (allowed_changes & SDL_AUDIO_ALLOW_FORMAT_CHANGE) {
slouken@2866
   978
            obtained->format = device->spec.format;
slouken@2866
   979
        } else {
slouken@2866
   980
            build_cvt = SDL_TRUE;
slouken@2866
   981
        }
slouken@2866
   982
    }
slouken@2866
   983
    if (obtained->channels != device->spec.channels) {
slouken@2866
   984
        if (allowed_changes & SDL_AUDIO_ALLOW_CHANNELS_CHANGE) {
slouken@2866
   985
            obtained->channels = device->spec.channels;
slouken@2866
   986
        } else {
slouken@2866
   987
            build_cvt = SDL_TRUE;
slouken@2866
   988
        }
slouken@2866
   989
    }
slouken@2866
   990
    if (build_cvt) {
slouken@1895
   991
        /* Build an audio conversion block */
icculus@2049
   992
        if (SDL_BuildAudioCVT(&device->convert,
slouken@2866
   993
                              obtained->format, obtained->channels,
slouken@2866
   994
                              obtained->freq,
icculus@2049
   995
                              device->spec.format, device->spec.channels,
icculus@2049
   996
                              device->spec.freq) < 0) {
icculus@2049
   997
            close_audio_device(device);
icculus@2049
   998
            return 0;
slouken@1895
   999
        }
icculus@2049
  1000
        if (device->convert.needed) {
slouken@2866
  1001
            device->convert.len = (int) (((double) obtained->size) /
slouken@2060
  1002
                                         device->convert.len_ratio);
icculus@2053
  1003
icculus@2049
  1004
            device->convert.buf =
icculus@2049
  1005
                (Uint8 *) SDL_AllocAudioMem(device->convert.len *
icculus@2049
  1006
                                            device->convert.len_mult);
icculus@2049
  1007
            if (device->convert.buf == NULL) {
icculus@2049
  1008
                close_audio_device(device);
slouken@1895
  1009
                SDL_OutOfMemory();
icculus@2049
  1010
                return 0;
slouken@1895
  1011
            }
slouken@1895
  1012
        }
slouken@1895
  1013
    }
icculus@2049
  1014
icculus@2049
  1015
    /* Find an available device ID and store the structure... */
slouken@2060
  1016
    for (id = min_id - 1; id < SDL_arraysize(open_devices); id++) {
icculus@2049
  1017
        if (open_devices[id] == NULL) {
icculus@2049
  1018
            open_devices[id] = device;
icculus@2049
  1019
            break;
icculus@2049
  1020
        }
icculus@2049
  1021
    }
icculus@2049
  1022
icculus@2049
  1023
    if (id == SDL_arraysize(open_devices)) {
icculus@2049
  1024
        SDL_SetError("Too many open audio devices");
icculus@2049
  1025
        close_audio_device(device);
icculus@2049
  1026
        return 0;
icculus@2049
  1027
    }
icculus@2049
  1028
slouken@1895
  1029
    /* Start the audio thread if necessary */
icculus@2049
  1030
    if (!current_audio.impl.ProvidesOwnCallbackThread) {
slouken@1895
  1031
        /* Start the audio thread */
icculus@5969
  1032
        char name[64];
icculus@5969
  1033
        SDL_snprintf(name, sizeof (name), "SDLAudioDev%d", (int) (id + 1));
icculus@2049
  1034
/* !!! FIXME: this is nasty. */
icculus@6430
  1035
#if defined(__WIN32__) && !defined(HAVE_LIBC)
slouken@1330
  1036
#undef SDL_CreateThread
icculus@5969
  1037
        device->thread = SDL_CreateThread(SDL_RunAudio, name, device, NULL, NULL);
slouken@1330
  1038
#else
icculus@5969
  1039
        device->thread = SDL_CreateThread(SDL_RunAudio, name, device);
slouken@1330
  1040
#endif
icculus@2049
  1041
        if (device->thread == NULL) {
slouken@2060
  1042
            SDL_CloseAudioDevice(id + 1);
slouken@1895
  1043
            SDL_SetError("Couldn't create audio thread");
icculus@2049
  1044
            return 0;
icculus@2049
  1045
        }
icculus@2049
  1046
    }
icculus@2049
  1047
slouken@2060
  1048
    return id + 1;
icculus@2049
  1049
}
icculus@2049
  1050
icculus@2049
  1051
icculus@2049
  1052
int
slouken@2866
  1053
SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained)
icculus@2049
  1054
{
icculus@2049
  1055
    SDL_AudioDeviceID id = 0;
icculus@2049
  1056
icculus@2049
  1057
    /* Start up the audio driver, if necessary. This is legacy behaviour! */
icculus@2049
  1058
    if (!SDL_WasInit(SDL_INIT_AUDIO)) {
icculus@2049
  1059
        if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
slouken@1895
  1060
            return (-1);
slouken@1895
  1061
        }
icculus@2049
  1062
    }
slouken@0
  1063
icculus@2049
  1064
    /* SDL_OpenAudio() is legacy and can only act on Device ID #1. */
icculus@2049
  1065
    if (open_devices[0] != NULL) {
icculus@2049
  1066
        SDL_SetError("Audio device is already opened");
icculus@2049
  1067
        return (-1);
slouken@1895
  1068
    }
icculus@2049
  1069
slouken@2866
  1070
    if (obtained) {
slouken@2866
  1071
        id = open_audio_device(NULL, 0, desired, obtained,
slouken@2866
  1072
                               SDL_AUDIO_ALLOW_ANY_CHANGE, 1);
slouken@2866
  1073
    } else {
slouken@2866
  1074
        id = open_audio_device(NULL, 0, desired, desired, 0, 1);
slouken@2866
  1075
    }
slouken@21
  1076
icculus@5964
  1077
    SDL_assert((id == 0) || (id == 1));
icculus@2049
  1078
    return ((id == 0) ? -1 : 0);
icculus@2049
  1079
}
slouken@21
  1080
icculus@2049
  1081
SDL_AudioDeviceID
icculus@2049
  1082
SDL_OpenAudioDevice(const char *device, int iscapture,
slouken@2866
  1083
                    const SDL_AudioSpec * desired, SDL_AudioSpec * obtained,
slouken@2866
  1084
                    int allowed_changes)
icculus@2049
  1085
{
slouken@2866
  1086
    return open_audio_device(device, iscapture, desired, obtained,
slouken@2866
  1087
                             allowed_changes, 2);
slouken@1895
  1088
}
slouken@1895
  1089
slouken@3537
  1090
SDL_AudioStatus
icculus@2049
  1091
SDL_GetAudioDeviceStatus(SDL_AudioDeviceID devid)
slouken@1895
  1092
{
icculus@2049
  1093
    SDL_AudioDevice *device = get_audio_device(devid);
slouken@3537
  1094
    SDL_AudioStatus status = SDL_AUDIO_STOPPED;
icculus@2049
  1095
    if (device && device->enabled) {
icculus@2049
  1096
        if (device->paused) {
slouken@1895
  1097
            status = SDL_AUDIO_PAUSED;
slouken@1895
  1098
        } else {
slouken@1895
  1099
            status = SDL_AUDIO_PLAYING;
slouken@1895
  1100
        }
slouken@1895
  1101
    }
slouken@1895
  1102
    return (status);
slouken@0
  1103
}
slouken@0
  1104
icculus@2049
  1105
slouken@3537
  1106
SDL_AudioStatus
icculus@2049
  1107
SDL_GetAudioStatus(void)
icculus@2049
  1108
{
icculus@2049
  1109
    return SDL_GetAudioDeviceStatus(1);
icculus@2049
  1110
}
icculus@2049
  1111
icculus@2049
  1112
void
icculus@2049
  1113
SDL_PauseAudioDevice(SDL_AudioDeviceID devid, int pause_on)
icculus@2049
  1114
{
icculus@2049
  1115
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@2049
  1116
    if (device) {
icculus@7447
  1117
        current_audio.impl.LockDevice(device);
icculus@2049
  1118
        device->paused = pause_on;
icculus@7447
  1119
        current_audio.impl.UnlockDevice(device);
icculus@2049
  1120
    }
icculus@2049
  1121
}
icculus@2049
  1122
slouken@1895
  1123
void
slouken@1895
  1124
SDL_PauseAudio(int pause_on)
slouken@0
  1125
{
icculus@2049
  1126
    SDL_PauseAudioDevice(1, pause_on);
icculus@2049
  1127
}
icculus@2049
  1128
slouken@0
  1129
icculus@2049
  1130
void
icculus@2049
  1131
SDL_LockAudioDevice(SDL_AudioDeviceID devid)
icculus@2049
  1132
{
icculus@2049
  1133
    /* Obtain a lock on the mixing buffers */
icculus@2049
  1134
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@2049
  1135
    if (device) {
icculus@2049
  1136
        current_audio.impl.LockDevice(device);
slouken@1895
  1137
    }
slouken@0
  1138
}
slouken@0
  1139
slouken@1895
  1140
void
slouken@1895
  1141
SDL_LockAudio(void)
slouken@0
  1142
{
icculus@2049
  1143
    SDL_LockAudioDevice(1);
icculus@2049
  1144
}
slouken@0
  1145
icculus@2049
  1146
void
icculus@2049
  1147
SDL_UnlockAudioDevice(SDL_AudioDeviceID devid)
icculus@2049
  1148
{
slouken@1895
  1149
    /* Obtain a lock on the mixing buffers */
icculus@2049
  1150
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@2049
  1151
    if (device) {
icculus@2049
  1152
        current_audio.impl.UnlockDevice(device);
slouken@1895
  1153
    }
slouken@0
  1154
}
slouken@0
  1155
slouken@1895
  1156
void
slouken@1895
  1157
SDL_UnlockAudio(void)
slouken@0
  1158
{
icculus@2049
  1159
    SDL_UnlockAudioDevice(1);
icculus@2049
  1160
}
slouken@0
  1161
icculus@2049
  1162
void
icculus@2049
  1163
SDL_CloseAudioDevice(SDL_AudioDeviceID devid)
icculus@2049
  1164
{
icculus@2049
  1165
    SDL_AudioDevice *device = get_audio_device(devid);
icculus@2049
  1166
    if (device) {
icculus@2049
  1167
        close_audio_device(device);
slouken@2060
  1168
        open_devices[devid - 1] = NULL;
slouken@1895
  1169
    }
slouken@0
  1170
}
slouken@0
  1171
slouken@1895
  1172
void
slouken@1895
  1173
SDL_CloseAudio(void)
slouken@0
  1174
{
icculus@2049
  1175
    SDL_CloseAudioDevice(1);
slouken@0
  1176
}
slouken@0
  1177
slouken@1895
  1178
void
slouken@1895
  1179
SDL_AudioQuit(void)
slouken@0
  1180
{
icculus@7348
  1181
    SDL_AudioDeviceID i;
icculus@7348
  1182
icculus@7345
  1183
    if (!current_audio.name) {  /* not initialized?! */
icculus@7345
  1184
        return;
icculus@7345
  1185
    }
icculus@7345
  1186
icculus@2049
  1187
    for (i = 0; i < SDL_arraysize(open_devices); i++) {
icculus@7342
  1188
        if (open_devices[i] != NULL) {
icculus@7446
  1189
            SDL_CloseAudioDevice(i+1);
icculus@7342
  1190
        }
icculus@2049
  1191
    }
slouken@0
  1192
icculus@2049
  1193
    /* Free the driver data */
icculus@2049
  1194
    current_audio.impl.Deinitialize();
icculus@5593
  1195
    free_device_list(&current_audio.outputDevices,
icculus@5593
  1196
                     &current_audio.outputDeviceCount);
icculus@5593
  1197
    free_device_list(&current_audio.inputDevices,
icculus@5593
  1198
                     &current_audio.inputDeviceCount);
slouken@2060
  1199
    SDL_memset(&current_audio, '\0', sizeof(current_audio));
slouken@2060
  1200
    SDL_memset(open_devices, '\0', sizeof(open_devices));
slouken@0
  1201
}
slouken@0
  1202
icculus@1982
  1203
#define NUM_FORMATS 10
slouken@0
  1204
static int format_idx;
slouken@0
  1205
static int format_idx_sub;
icculus@1982
  1206
static SDL_AudioFormat format_list[NUM_FORMATS][NUM_FORMATS] = {
slouken@1895
  1207
    {AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB,
icculus@1982
  1208
     AUDIO_U16MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB},
slouken@1895
  1209
    {AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB,
icculus@1982
  1210
     AUDIO_U16MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB},
icculus@1982
  1211
    {AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S32LSB,
icculus@1982
  1212
     AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_U8, AUDIO_S8},
icculus@1982
  1213
    {AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S32MSB,
icculus@1982
  1214
     AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_U8, AUDIO_S8},
icculus@1982
  1215
    {AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_S32LSB,
icculus@1982
  1216
     AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_U8, AUDIO_S8},
icculus@1982
  1217
    {AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_S32MSB,
icculus@1982
  1218
     AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_U8, AUDIO_S8},
icculus@1993
  1219
    {AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_S16LSB,
icculus@1993
  1220
     AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8},
icculus@1993
  1221
    {AUDIO_S32MSB, AUDIO_S32LSB, AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_S16MSB,
icculus@1993
  1222
     AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8},
icculus@1993
  1223
    {AUDIO_F32LSB, AUDIO_F32MSB, AUDIO_S32LSB, AUDIO_S32MSB, AUDIO_S16LSB,
icculus@1993
  1224
     AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8},
icculus@1993
  1225
    {AUDIO_F32MSB, AUDIO_F32LSB, AUDIO_S32MSB, AUDIO_S32LSB, AUDIO_S16MSB,
icculus@1993
  1226
     AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8},
slouken@0
  1227
};
slouken@0
  1228
icculus@1982
  1229
SDL_AudioFormat
icculus@1982
  1230
SDL_FirstAudioFormat(SDL_AudioFormat format)
slouken@0
  1231
{
slouken@1895
  1232
    for (format_idx = 0; format_idx < NUM_FORMATS; ++format_idx) {
slouken@1895
  1233
        if (format_list[format_idx][0] == format) {
slouken@1895
  1234
            break;
slouken@1895
  1235
        }
slouken@1895
  1236
    }
slouken@1895
  1237
    format_idx_sub = 0;
slouken@1895
  1238
    return (SDL_NextAudioFormat());
slouken@0
  1239
}
slouken@0
  1240
icculus@1982
  1241
SDL_AudioFormat
slouken@1895
  1242
SDL_NextAudioFormat(void)
slouken@0
  1243
{
slouken@1895
  1244
    if ((format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS)) {
slouken@1895
  1245
        return (0);
slouken@1895
  1246
    }
slouken@1895
  1247
    return (format_list[format_idx][format_idx_sub++]);
slouken@0
  1248
}
slouken@0
  1249
slouken@1895
  1250
void
slouken@1895
  1251
SDL_CalculateAudioSpec(SDL_AudioSpec * spec)
slouken@0
  1252
{
slouken@1895
  1253
    switch (spec->format) {
slouken@1895
  1254
    case AUDIO_U8:
slouken@1895
  1255
        spec->silence = 0x80;
slouken@1895
  1256
        break;
slouken@1895
  1257
    default:
slouken@1895
  1258
        spec->silence = 0x00;
slouken@1895
  1259
        break;
slouken@1895
  1260
    }
icculus@2049
  1261
    spec->size = SDL_AUDIO_BITSIZE(spec->format) / 8;
slouken@1895
  1262
    spec->size *= spec->channels;
slouken@1895
  1263
    spec->size *= spec->samples;
slouken@0
  1264
}
slouken@1895
  1265
icculus@2049
  1266
icculus@2049
  1267
/*
icculus@2049
  1268
 * Moved here from SDL_mixer.c, since it relies on internals of an opened
icculus@2049
  1269
 *  audio device (and is deprecated, by the way!).
icculus@2049
  1270
 */
icculus@2049
  1271
void
icculus@2049
  1272
SDL_MixAudio(Uint8 * dst, const Uint8 * src, Uint32 len, int volume)
icculus@2049
  1273
{
icculus@2049
  1274
    /* Mix the user-level audio format */
icculus@2049
  1275
    SDL_AudioDevice *device = get_audio_device(1);
icculus@2049
  1276
    if (device != NULL) {
icculus@2049
  1277
        SDL_AudioFormat format;
icculus@2049
  1278
        if (device->convert.needed) {
icculus@2049
  1279
            format = device->convert.src_format;
icculus@2049
  1280
        } else {
icculus@2049
  1281
            format = device->spec.format;
icculus@2049
  1282
        }
icculus@2049
  1283
        SDL_MixAudioFormat(dst, src, format, len, volume);
icculus@2049
  1284
    }
icculus@2049
  1285
}
icculus@2049
  1286
slouken@1895
  1287
/* vi: set ts=4 sw=4 expandtab: */