src/audio/SDL_audio.c
author Sam Lantinga
Mon, 23 Mar 2009 05:21:40 +0000
changeset 3096 ae4e80dbe330
parent 3040 62d4992e5a92
child 3099 82e60908fab1
permissions -rw-r--r--
Date: Tue, 17 Feb 2009 14:00:25 +0100
From: Stefan Klug
Subject: [SDL] Possible bug, paused audio playing garbage

On my WinCE device a paused audio device plays random garbage.
This might also be the issue in the thread "sound cracks with SDL_mixer
and AUDIO_S16LSB"

I don't have that much knowledge of the SDL audio part, but the attached
patch fixes it for me, and collapses two redundant ifs.

I'm not sure if this is the correct way to fix this.
Shouldn't the complete stream conversion part of the RunAudio loop be
dependent on the paused property of the device? (not only the call to
(*fill)(udata, istream, istream_len).

Anyways. Would be great if the patch or a fix could find its way to SVN ;-)

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