src/audio/SDL_sysaudio.h
author Ryan C. Gordon
Fri, 07 Oct 2016 15:13:46 -0400
changeset 10473 d6427519ca66
parent 10471 fd0bebfab510
child 10609 d702ecbd8ba7
permissions -rw-r--r--
audio: Don't trust audio drivers to drain pending audio.

This tends to be a frequent spot where drivers hang, and the waits were
often unreliable in any case.

Instead, our audio thread now alerts the driver that we're done streaming audio
(which currently XAudio2 uses to alert the system not to warn about the
impending underflow) and then SDL_Delay()'s for a duration that's reasonable
to drain the DMA buffers before closing the device.
slouken@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@9998
     3
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
slouken@0
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@0
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@0
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    20
*/
icculus@8093
    21
#include "../SDL_internal.h"
slouken@0
    22
slouken@0
    23
#ifndef _SDL_sysaudio_h
slouken@0
    24
#define _SDL_sysaudio_h
slouken@0
    25
slouken@0
    26
#include "SDL_mutex.h"
slouken@0
    27
#include "SDL_thread.h"
slouken@0
    28
icculus@10264
    29
/* !!! FIXME: These are wordy and unlocalized... */
icculus@10264
    30
#define DEFAULT_OUTPUT_DEVNAME "System audio output device"
icculus@10264
    31
#define DEFAULT_INPUT_DEVNAME "System audio capture device"
icculus@10264
    32
slouken@0
    33
/* The SDL audio driver */
slouken@0
    34
typedef struct SDL_AudioDevice SDL_AudioDevice;
slouken@7191
    35
#define _THIS   SDL_AudioDevice *_this
slouken@0
    36
icculus@9394
    37
/* Audio targets should call this as devices are added to the system (such as
icculus@9394
    38
   a USB headset being plugged in), and should also be called for
icculus@9394
    39
   for every device found during DetectDevices(). */
icculus@9394
    40
extern void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle);
icculus@9393
    41
icculus@9394
    42
/* Audio targets should call this as devices are removed, so SDL can update
icculus@9394
    43
   its list of available devices. */
icculus@9399
    44
extern void SDL_RemoveAudioDevice(const int iscapture, void *handle);
icculus@9393
    45
icculus@9394
    46
/* Audio targets should call this if an opened audio device is lost while
icculus@9394
    47
   being used. This can happen due to i/o errors, or a device being unplugged,
icculus@9394
    48
   etc. If the device is totally gone, please also call SDL_RemoveAudioDevice()
icculus@9394
    49
   as appropriate so SDL's list of devices is accurate. */
icculus@9394
    50
extern void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device);
icculus@9393
    51
icculus@5593
    52
icculus@9012
    53
/* This is the size of a packet when using SDL_QueueAudio(). We allocate
icculus@9012
    54
   these as necessary and pool them, under the assumption that we'll
icculus@9012
    55
   eventually end up with a handful that keep recycling, meeting whatever
icculus@9012
    56
   the app needs. We keep packing data tightly as more arrives to avoid
icculus@9012
    57
   wasting space, and if we get a giant block of data, we'll split them
icculus@9012
    58
   into multiple packets behind the scenes. My expectation is that most
icculus@9012
    59
   apps will have 2-3 of these in the pool. 8k should cover most needs, but
icculus@9012
    60
   if this is crippling for some embedded system, we can #ifdef this.
icculus@9012
    61
   The system preallocates enough packets for 2 callbacks' worth of data. */
icculus@9012
    62
#define SDL_AUDIOBUFFERQUEUE_PACKETLEN (8 * 1024)
icculus@9012
    63
icculus@9012
    64
/* Used by apps that queue audio instead of using the callback. */
icculus@9012
    65
typedef struct SDL_AudioBufferQueue
icculus@9012
    66
{
icculus@9012
    67
    Uint8 data[SDL_AUDIOBUFFERQUEUE_PACKETLEN];  /* packet data. */
icculus@9012
    68
    Uint32 datalen;  /* bytes currently in use in this packet. */
icculus@9012
    69
    Uint32 startpos;  /* bytes currently consumed in this packet. */
icculus@9012
    70
    struct SDL_AudioBufferQueue *next;  /* next item in linked list. */
icculus@9012
    71
} SDL_AudioBufferQueue;
icculus@9012
    72
icculus@2049
    73
typedef struct SDL_AudioDriverImpl
icculus@2049
    74
{
icculus@9394
    75
    void (*DetectDevices) (void);
icculus@9394
    76
    int (*OpenDevice) (_THIS, void *handle, const char *devname, int iscapture);
icculus@2049
    77
    void (*ThreadInit) (_THIS); /* Called by audio thread at start */
icculus@2049
    78
    void (*WaitDevice) (_THIS);
icculus@2049
    79
    void (*PlayDevice) (_THIS);
icculus@9031
    80
    int (*GetPendingBytes) (_THIS);
icculus@2049
    81
    Uint8 *(*GetDeviceBuf) (_THIS);
icculus@10239
    82
    int (*CaptureFromDevice) (_THIS, void *buffer, int buflen);
icculus@10239
    83
    void (*FlushCapture) (_THIS);
icculus@10473
    84
    void (*PrepareToClose) (_THIS);  /**< Called between run and draining wait for playback devices */
icculus@2049
    85
    void (*CloseDevice) (_THIS);
icculus@2049
    86
    void (*LockDevice) (_THIS);
icculus@2049
    87
    void (*UnlockDevice) (_THIS);
icculus@9394
    88
    void (*FreeDeviceHandle) (void *handle);  /**< SDL is done with handle from SDL_AddAudioDevice() */
icculus@2049
    89
    void (*Deinitialize) (void);
icculus@2049
    90
icculus@5590
    91
    /* !!! FIXME: add pause(), so we can optimize instead of mixing silence. */
icculus@5590
    92
icculus@2049
    93
    /* Some flags to push duplicate code into the core and reduce #ifdefs. */
icculus@9391
    94
    /* !!! FIXME: these should be SDL_bool */
icculus@5589
    95
    int ProvidesOwnCallbackThread;
icculus@10471
    96
    int SkipMixerLock;
icculus@5589
    97
    int HasCaptureSupport;
icculus@5589
    98
    int OnlyHasDefaultOutputDevice;
icculus@10258
    99
    int OnlyHasDefaultCaptureDevice;
icculus@9394
   100
    int AllowsArbitraryDeviceNames;
icculus@2049
   101
} SDL_AudioDriverImpl;
icculus@2049
   102
icculus@2049
   103
icculus@9394
   104
typedef struct SDL_AudioDeviceItem
icculus@9394
   105
{
icculus@9394
   106
    void *handle;
icculus@9394
   107
    struct SDL_AudioDeviceItem *next;
icculus@9407
   108
    #if (defined(__GNUC__) && (__GNUC__ <= 2))
icculus@9407
   109
    char name[1];  /* actually variable length. */
icculus@9407
   110
    #else
icculus@9394
   111
    char name[];
icculus@9407
   112
    #endif
icculus@9394
   113
} SDL_AudioDeviceItem;
icculus@9394
   114
icculus@9394
   115
icculus@2049
   116
typedef struct SDL_AudioDriver
slouken@1895
   117
{
slouken@1895
   118
    /* * * */
slouken@1895
   119
    /* The name of this audio driver */
slouken@1895
   120
    const char *name;
slouken@0
   121
slouken@1895
   122
    /* * * */
slouken@1895
   123
    /* The description of this audio driver */
slouken@1895
   124
    const char *desc;
slouken@0
   125
icculus@2049
   126
    SDL_AudioDriverImpl impl;
icculus@5593
   127
icculus@9393
   128
    /* A mutex for device detection */
icculus@9394
   129
    SDL_mutex *detectionLock;
icculus@9394
   130
    SDL_bool captureDevicesRemoved;
icculus@9394
   131
    SDL_bool outputDevicesRemoved;
icculus@5593
   132
    int outputDeviceCount;
icculus@5593
   133
    int inputDeviceCount;
icculus@9394
   134
    SDL_AudioDeviceItem *outputDevices;
icculus@9394
   135
    SDL_AudioDeviceItem *inputDevices;
icculus@2049
   136
} SDL_AudioDriver;
icculus@2049
   137
slouken@0
   138
slouken@2716
   139
/* Streamer */
slouken@2716
   140
typedef struct
slouken@2716
   141
{
slouken@2716
   142
    Uint8 *buffer;
slouken@2716
   143
    int max_len;                /* the maximum length in bytes */
slouken@2716
   144
    int read_pos, write_pos;    /* the position of the write and read heads in bytes */
slouken@2716
   145
} SDL_AudioStreamer;
slouken@2716
   146
slouken@2716
   147
icculus@2049
   148
/* Define the SDL audio driver structure */
icculus@2049
   149
struct SDL_AudioDevice
icculus@2049
   150
{
slouken@1895
   151
    /* * * */
slouken@1895
   152
    /* Data common to all devices */
icculus@9393
   153
    SDL_AudioDeviceID id;
slouken@0
   154
slouken@1895
   155
    /* The current audio specification (shared with audio thread) */
slouken@1895
   156
    SDL_AudioSpec spec;
slouken@0
   157
slouken@1895
   158
    /* An audio conversion block for audio format emulation */
slouken@1895
   159
    SDL_AudioCVT convert;
slouken@0
   160
slouken@2716
   161
    /* The streamer, if sample rate conversion necessitates it */
slouken@2716
   162
    int use_streamer;
slouken@2716
   163
    SDL_AudioStreamer streamer;
slouken@2716
   164
slouken@1895
   165
    /* Current state flags */
icculus@10233
   166
    SDL_atomic_t shutdown; /* true if we are signaling the play thread to end. */
icculus@10238
   167
    SDL_atomic_t enabled;  /* true if device is functioning and connected. */
icculus@10238
   168
    SDL_atomic_t paused;
icculus@10235
   169
    SDL_bool iscapture;
slouken@0
   170
slouken@1895
   171
    /* Fake audio buffer for when the audio hardware is busy */
slouken@1895
   172
    Uint8 *fake_stream;
slouken@0
   173
icculus@9391
   174
    /* A mutex for locking the mixing buffers */
slouken@1895
   175
    SDL_mutex *mixer_lock;
slouken@0
   176
slouken@1895
   177
    /* A thread to feed the audio device */
slouken@1895
   178
    SDL_Thread *thread;
slouken@3578
   179
    SDL_threadID threadid;
slouken@0
   180
icculus@9012
   181
    /* Queued buffers (if app not using callback). */
icculus@9012
   182
    SDL_AudioBufferQueue *buffer_queue_head; /* device fed from here. */
icculus@9012
   183
    SDL_AudioBufferQueue *buffer_queue_tail; /* queue fills to here. */
icculus@9012
   184
    SDL_AudioBufferQueue *buffer_queue_pool; /* these are unused packets. */
icculus@9012
   185
    Uint32 queued_bytes;  /* number of bytes of audio data in the queue. */
icculus@9012
   186
slouken@1895
   187
    /* * * */
slouken@1895
   188
    /* Data private to this driver */
slouken@1895
   189
    struct SDL_PrivateAudioData *hidden;
slouken@10467
   190
slouken@10467
   191
    void *handle;
slouken@0
   192
};
slouken@0
   193
#undef _THIS
slouken@0
   194
slouken@1895
   195
typedef struct AudioBootStrap
slouken@1895
   196
{
slouken@1895
   197
    const char *name;
slouken@1895
   198
    const char *desc;
slouken@2060
   199
    int (*init) (SDL_AudioDriverImpl * impl);
icculus@5578
   200
    int demand_only;  /* 1==request explicitly, or it won't be available. */
slouken@0
   201
} AudioBootStrap;
slouken@0
   202
icculus@2049
   203
#endif /* _SDL_sysaudio_h */
slouken@0
   204
slouken@1895
   205
/* vi: set ts=4 sw=4 expandtab: */