|
@@ -51,9 +51,7 @@ extern AudioBootStrap QSAAUDIO_bootstrap;
|
|
|
extern AudioBootStrap SUNAUDIO_bootstrap;
|
|
|
extern AudioBootStrap ARTS_bootstrap;
|
|
|
extern AudioBootStrap ESD_bootstrap;
|
|
|
-#if SDL_AUDIO_DRIVER_NACL
|
|
|
extern AudioBootStrap NACLAUD_bootstrap;
|
|
|
-#endif
|
|
|
extern AudioBootStrap NAS_bootstrap;
|
|
|
extern AudioBootStrap XAUDIO2_bootstrap;
|
|
|
extern AudioBootStrap DSOUND_bootstrap;
|
|
@@ -71,6 +69,7 @@ extern AudioBootStrap FUSIONSOUND_bootstrap;
|
|
|
extern AudioBootStrap ANDROIDAUD_bootstrap;
|
|
|
extern AudioBootStrap PSPAUD_bootstrap;
|
|
|
extern AudioBootStrap SNDIO_bootstrap;
|
|
|
+extern AudioBootStrap EmscriptenAudio_bootstrap;
|
|
|
|
|
|
|
|
|
/* Available audio drivers */
|
|
@@ -140,6 +139,9 @@ static const AudioBootStrap *const bootstrap[] = {
|
|
|
#endif
|
|
|
#if SDL_AUDIO_DRIVER_PSP
|
|
|
&PSPAUD_bootstrap,
|
|
|
+#endif
|
|
|
+#if SDL_AUDIO_DRIVER_EMSCRIPTEN
|
|
|
+ &EmscriptenAudio_bootstrap,
|
|
|
#endif
|
|
|
NULL
|
|
|
};
|
|
@@ -159,8 +161,16 @@ get_audio_device(SDL_AudioDeviceID id)
|
|
|
|
|
|
/* stubs for audio drivers that don't need a specific entry point... */
|
|
|
static void
|
|
|
-SDL_AudioDetectDevices_Default(int iscapture, SDL_AddAudioDevice addfn)
|
|
|
-{ /* no-op. */
|
|
|
+SDL_AudioDetectDevices_Default(void)
|
|
|
+{
|
|
|
+ /* you have to write your own implementation if these assertions fail. */
|
|
|
+ SDL_assert(current_audio.impl.OnlyHasDefaultOutputDevice);
|
|
|
+ SDL_assert(current_audio.impl.OnlyHasDefaultInputDevice || !current_audio.impl.HasCaptureSupport);
|
|
|
+
|
|
|
+ SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, (void *) ((size_t) 0x1));
|
|
|
+ if (current_audio.impl.HasCaptureSupport) {
|
|
|
+ SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, (void *) ((size_t) 0x2));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static void
|
|
@@ -205,10 +215,16 @@ SDL_AudioDeinitialize_Default(void)
|
|
|
{ /* no-op. */
|
|
|
}
|
|
|
|
|
|
+static void
|
|
|
+SDL_AudioFreeDeviceHandle_Default(void *handle)
|
|
|
+{ /* no-op. */
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
static int
|
|
|
-SDL_AudioOpenDevice_Default(_THIS, const char *devname, int iscapture)
|
|
|
+SDL_AudioOpenDevice_Default(_THIS, void *handle, const char *devname, int iscapture)
|
|
|
{
|
|
|
- return -1;
|
|
|
+ return SDL_Unsupported();
|
|
|
}
|
|
|
|
|
|
static SDL_INLINE SDL_bool
|
|
@@ -265,71 +281,139 @@ finalize_audio_entry_points(void)
|
|
|
FILL_STUB(CloseDevice);
|
|
|
FILL_STUB(LockDevice);
|
|
|
FILL_STUB(UnlockDevice);
|
|
|
+ FILL_STUB(FreeDeviceHandle);
|
|
|
FILL_STUB(Deinitialize);
|
|
|
#undef FILL_STUB
|
|
|
}
|
|
|
|
|
|
-#if 0 /* !!! FIXME: rewrite/remove this streamer code. */
|
|
|
-/* Streaming functions (for when the input and output buffer sizes are different) */
|
|
|
-/* Write [length] bytes from buf into the streamer */
|
|
|
-static void
|
|
|
-SDL_StreamWrite(SDL_AudioStreamer * stream, Uint8 * buf, int length)
|
|
|
-{
|
|
|
- int i;
|
|
|
|
|
|
- for (i = 0; i < length; ++i) {
|
|
|
- stream->buffer[stream->write_pos] = buf[i];
|
|
|
- ++stream->write_pos;
|
|
|
+/* device hotplug support... */
|
|
|
+
|
|
|
+static int
|
|
|
+add_audio_device(const char *name, void *handle, SDL_AudioDeviceItem **devices, int *devCount)
|
|
|
+{
|
|
|
+ int retval = -1;
|
|
|
+ const size_t size = sizeof (SDL_AudioDeviceItem) + SDL_strlen(name) + 1;
|
|
|
+ SDL_AudioDeviceItem *item = (SDL_AudioDeviceItem *) SDL_malloc(size);
|
|
|
+ if (item == NULL) {
|
|
|
+ return -1;
|
|
|
}
|
|
|
+
|
|
|
+ SDL_assert(handle != NULL); /* we reserve NULL, audio backends can't use it. */
|
|
|
+
|
|
|
+ item->handle = handle;
|
|
|
+ SDL_strlcpy(item->name, name, size - sizeof (SDL_AudioDeviceItem));
|
|
|
+
|
|
|
+ SDL_LockMutex(current_audio.detectionLock);
|
|
|
+ item->next = *devices;
|
|
|
+ *devices = item;
|
|
|
+ retval = (*devCount)++;
|
|
|
+ SDL_UnlockMutex(current_audio.detectionLock);
|
|
|
+
|
|
|
+ return retval;
|
|
|
}
|
|
|
|
|
|
-/* Read [length] bytes out of the streamer into buf */
|
|
|
-static void
|
|
|
-SDL_StreamRead(SDL_AudioStreamer * stream, Uint8 * buf, int length)
|
|
|
+static SDL_INLINE int
|
|
|
+add_capture_device(const char *name, void *handle)
|
|
|
{
|
|
|
- int i;
|
|
|
+ /* !!! FIXME: add this later. SDL_assert(current_audio.impl.HasCaptureSupport);*/
|
|
|
+ return add_audio_device(name, handle, ¤t_audio.inputDevices, ¤t_audio.inputDeviceCount);
|
|
|
+}
|
|
|
|
|
|
- for (i = 0; i < length; ++i) {
|
|
|
- buf[i] = stream->buffer[stream->read_pos];
|
|
|
- ++stream->read_pos;
|
|
|
- }
|
|
|
+static SDL_INLINE int
|
|
|
+add_output_device(const char *name, void *handle)
|
|
|
+{
|
|
|
+ return add_audio_device(name, handle, ¤t_audio.outputDevices, ¤t_audio.outputDeviceCount);
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
-SDL_StreamLength(SDL_AudioStreamer * stream)
|
|
|
+static void
|
|
|
+free_device_list(SDL_AudioDeviceItem **devices, int *devCount)
|
|
|
{
|
|
|
- return (stream->write_pos - stream->read_pos) % stream->max_len;
|
|
|
+ SDL_AudioDeviceItem *item, *next;
|
|
|
+ for (item = *devices; item != NULL; item = next) {
|
|
|
+ next = item->next;
|
|
|
+ if (item->handle != NULL) {
|
|
|
+ current_audio.impl.FreeDeviceHandle(item->handle);
|
|
|
+ }
|
|
|
+ SDL_free(item);
|
|
|
+ }
|
|
|
+ *devices = NULL;
|
|
|
+ *devCount = 0;
|
|
|
}
|
|
|
|
|
|
-/* Initialize the stream by allocating the buffer and setting the read/write heads to the beginning */
|
|
|
-#if 0
|
|
|
-static int
|
|
|
-SDL_StreamInit(SDL_AudioStreamer * stream, int max_len, Uint8 silence)
|
|
|
+
|
|
|
+/* The audio backends call this when a new device is plugged in. */
|
|
|
+void
|
|
|
+SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
|
|
|
{
|
|
|
- /* First try to allocate the buffer */
|
|
|
- stream->buffer = (Uint8 *) SDL_malloc(max_len);
|
|
|
- if (stream->buffer == NULL) {
|
|
|
- return -1;
|
|
|
+ const int device_index = iscapture ? add_capture_device(name, handle) : add_output_device(name, handle);
|
|
|
+ if (device_index != -1) {
|
|
|
+ /* Post the event, if desired */
|
|
|
+ if (SDL_GetEventState(SDL_AUDIODEVICEADDED) == SDL_ENABLE) {
|
|
|
+ SDL_Event event;
|
|
|
+ SDL_zero(event);
|
|
|
+ event.adevice.type = SDL_AUDIODEVICEADDED;
|
|
|
+ event.adevice.which = device_index;
|
|
|
+ event.adevice.iscapture = iscapture;
|
|
|
+ SDL_PushEvent(&event);
|
|
|
+ }
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- stream->max_len = max_len;
|
|
|
- stream->read_pos = 0;
|
|
|
- stream->write_pos = 0;
|
|
|
+/* The audio backends call this when a currently-opened device is lost. */
|
|
|
+void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
|
|
|
+{
|
|
|
+ SDL_assert(get_audio_device(device->id) == device);
|
|
|
|
|
|
- /* Zero out the buffer */
|
|
|
- SDL_memset(stream->buffer, silence, max_len);
|
|
|
+ if (!device->enabled) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- return 0;
|
|
|
+ /* Ends the audio callback and mark the device as STOPPED, but the
|
|
|
+ app still needs to close the device to free resources. */
|
|
|
+ current_audio.impl.LockDevice(device);
|
|
|
+ device->enabled = 0;
|
|
|
+ current_audio.impl.UnlockDevice(device);
|
|
|
+
|
|
|
+ /* Post the event, if desired */
|
|
|
+ if (SDL_GetEventState(SDL_AUDIODEVICEREMOVED) == SDL_ENABLE) {
|
|
|
+ SDL_Event event;
|
|
|
+ SDL_zero(event);
|
|
|
+ event.adevice.type = SDL_AUDIODEVICEREMOVED;
|
|
|
+ event.adevice.which = device->id;
|
|
|
+ event.adevice.iscapture = device->iscapture ? 1 : 0;
|
|
|
+ SDL_PushEvent(&event);
|
|
|
+ }
|
|
|
}
|
|
|
-#endif
|
|
|
|
|
|
-/* Deinitialize the stream simply by freeing the buffer */
|
|
|
static void
|
|
|
-SDL_StreamDeinit(SDL_AudioStreamer * stream)
|
|
|
+mark_device_removed(void *handle, SDL_AudioDeviceItem *devices, SDL_bool *removedFlag)
|
|
|
{
|
|
|
- SDL_free(stream->buffer);
|
|
|
+ SDL_AudioDeviceItem *item;
|
|
|
+ SDL_assert(handle != NULL);
|
|
|
+ for (item = devices; item != NULL; item = item->next) {
|
|
|
+ if (item->handle == handle) {
|
|
|
+ item->handle = NULL;
|
|
|
+ *removedFlag = SDL_TRUE;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* The audio backends call this when a device is removed from the system. */
|
|
|
+void
|
|
|
+SDL_RemoveAudioDevice(const int iscapture, void *handle)
|
|
|
+{
|
|
|
+ SDL_LockMutex(current_audio.detectionLock);
|
|
|
+ if (iscapture) {
|
|
|
+ mark_device_removed(handle, current_audio.inputDevices, ¤t_audio.captureDevicesRemoved);
|
|
|
+ } else {
|
|
|
+ mark_device_removed(handle, current_audio.outputDevices, ¤t_audio.outputDevicesRemoved);
|
|
|
+ }
|
|
|
+ SDL_UnlockMutex(current_audio.detectionLock);
|
|
|
+ current_audio.impl.FreeDeviceHandle(handle);
|
|
|
}
|
|
|
-#endif
|
|
|
+
|
|
|
|
|
|
|
|
|
/* buffer queueing support... */
|
|
@@ -506,26 +590,17 @@ SDL_ClearQueuedAudio(SDL_AudioDeviceID devid)
|
|
|
}
|
|
|
|
|
|
|
|
|
-#if defined(__ANDROID__)
|
|
|
-#include <android/log.h>
|
|
|
-#endif
|
|
|
-
|
|
|
/* The general mixing thread function */
|
|
|
int SDLCALL
|
|
|
SDL_RunAudio(void *devicep)
|
|
|
{
|
|
|
SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
|
|
|
+ const int silence = (int) device->spec.silence;
|
|
|
+ const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq);
|
|
|
+ const int stream_len = (device->convert.needed) ? device->convert.len : device->spec.size;
|
|
|
Uint8 *stream;
|
|
|
- int stream_len;
|
|
|
- void *udata;
|
|
|
- void (SDLCALL * fill) (void *userdata, Uint8 * stream, int len);
|
|
|
- Uint32 delay;
|
|
|
-
|
|
|
-#if 0 /* !!! FIXME: rewrite/remove this streamer code. */
|
|
|
- /* For streaming when the buffer sizes don't match up */
|
|
|
- Uint8 *istream;
|
|
|
- int istream_len = 0;
|
|
|
-#endif
|
|
|
+ void *udata = device->spec.userdata;
|
|
|
+ void (SDLCALL *fill) (void *, Uint8 *, int) = device->spec.callback;
|
|
|
|
|
|
/* The audio mixing is always a high priority thread */
|
|
|
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
|
|
@@ -534,197 +609,60 @@ SDL_RunAudio(void *devicep)
|
|
|
device->threadid = SDL_ThreadID();
|
|
|
current_audio.impl.ThreadInit(device);
|
|
|
|
|
|
- /* Set up the mixing function */
|
|
|
- fill = device->spec.callback;
|
|
|
- udata = device->spec.userdata;
|
|
|
-
|
|
|
- /* By default do not stream */
|
|
|
- device->use_streamer = 0;
|
|
|
-
|
|
|
- if (device->convert.needed) {
|
|
|
-#if 0 /* !!! FIXME: I took len_div out of the structure. Use rate_incr instead? */
|
|
|
- /* If the result of the conversion alters the length, i.e. resampling is being used, use the streamer */
|
|
|
- if (device->convert.len_mult != 1 || device->convert.len_div != 1) {
|
|
|
- /* The streamer's maximum length should be twice whichever is larger: spec.size or len_cvt */
|
|
|
- stream_max_len = 2 * device->spec.size;
|
|
|
- if (device->convert.len_mult > device->convert.len_div) {
|
|
|
- stream_max_len *= device->convert.len_mult;
|
|
|
- stream_max_len /= device->convert.len_div;
|
|
|
- }
|
|
|
- if (SDL_StreamInit(&device->streamer, stream_max_len, silence) <
|
|
|
- 0)
|
|
|
- return -1;
|
|
|
- device->use_streamer = 1;
|
|
|
-
|
|
|
- /* istream_len should be the length of what we grab from the callback and feed to conversion,
|
|
|
- so that we get close to spec_size. I.e. we want device.spec_size = istream_len * u / d
|
|
|
- */
|
|
|
- istream_len =
|
|
|
- device->spec.size * device->convert.len_div /
|
|
|
- device->convert.len_mult;
|
|
|
+ /* Loop, filling the audio buffers */
|
|
|
+ while (!device->shutdown) {
|
|
|
+ /* Fill the current buffer with sound */
|
|
|
+ if (device->convert.needed) {
|
|
|
+ stream = device->convert.buf;
|
|
|
+ } else if (device->enabled) {
|
|
|
+ stream = current_audio.impl.GetDeviceBuf(device);
|
|
|
+ } else {
|
|
|
+ /* if the device isn't enabled, we still write to the
|
|
|
+ fake_stream, so the app's callback will fire with
|
|
|
+ a regular frequency, in case they depend on that
|
|
|
+ for timing or progress. They can use hotplug
|
|
|
+ now to know if the device failed. */
|
|
|
+ stream = NULL;
|
|
|
}
|
|
|
-#endif
|
|
|
- stream_len = device->convert.len;
|
|
|
- } else {
|
|
|
- stream_len = device->spec.size;
|
|
|
- }
|
|
|
-
|
|
|
- /* Calculate the delay while paused */
|
|
|
- delay = ((device->spec.samples * 1000) / device->spec.freq);
|
|
|
-
|
|
|
- /* Determine if the streamer is necessary here */
|
|
|
-#if 0 /* !!! FIXME: rewrite/remove this streamer code. */
|
|
|
- if (device->use_streamer == 1) {
|
|
|
- /* This code is almost the same as the old code. The difference is, instead of reading
|
|
|
- directly from the callback into "stream", then converting and sending the audio off,
|
|
|
- we go: callback -> "istream" -> (conversion) -> streamer -> stream -> device.
|
|
|
- However, reading and writing with streamer are done separately:
|
|
|
- - We only call the callback and write to the streamer when the streamer does not
|
|
|
- contain enough samples to output to the device.
|
|
|
- - We only read from the streamer and tell the device to play when the streamer
|
|
|
- does have enough samples to output.
|
|
|
- This allows us to perform resampling in the conversion step, where the output of the
|
|
|
- resampling process can be any number. We will have to see what a good size for the
|
|
|
- stream's maximum length is, but I suspect 2*max(len_cvt, stream_len) is a good figure.
|
|
|
- */
|
|
|
- while (device->enabled) {
|
|
|
-
|
|
|
- if (device->paused) {
|
|
|
- SDL_Delay(delay);
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- /* Only read in audio if the streamer doesn't have enough already (if it does not have enough samples to output) */
|
|
|
- if (SDL_StreamLength(&device->streamer) < stream_len) {
|
|
|
- /* Set up istream */
|
|
|
- if (device->convert.needed) {
|
|
|
- if (device->convert.buf) {
|
|
|
- istream = device->convert.buf;
|
|
|
- } else {
|
|
|
- continue;
|
|
|
- }
|
|
|
- } else {
|
|
|
-/* FIXME: Ryan, this is probably wrong. I imagine we don't want to get
|
|
|
- * a device buffer both here and below in the stream output.
|
|
|
- */
|
|
|
- istream = current_audio.impl.GetDeviceBuf(device);
|
|
|
- if (istream == NULL) {
|
|
|
- istream = device->fake_stream;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* Read from the callback into the _input_ stream */
|
|
|
- SDL_LockMutex(device->mixer_lock);
|
|
|
- (*fill) (udata, istream, istream_len);
|
|
|
- SDL_UnlockMutex(device->mixer_lock);
|
|
|
-
|
|
|
- /* Convert the audio if necessary and write to the streamer */
|
|
|
- if (device->convert.needed) {
|
|
|
- SDL_ConvertAudio(&device->convert);
|
|
|
- if (istream == NULL) {
|
|
|
- istream = device->fake_stream;
|
|
|
- }
|
|
|
- /* SDL_memcpy(istream, device->convert.buf, device->convert.len_cvt); */
|
|
|
- SDL_StreamWrite(&device->streamer, device->convert.buf,
|
|
|
- device->convert.len_cvt);
|
|
|
- } else {
|
|
|
- SDL_StreamWrite(&device->streamer, istream, istream_len);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* Only output audio if the streamer has enough to output */
|
|
|
- if (SDL_StreamLength(&device->streamer) >= stream_len) {
|
|
|
- /* Set up the output stream */
|
|
|
- if (device->convert.needed) {
|
|
|
- if (device->convert.buf) {
|
|
|
- stream = device->convert.buf;
|
|
|
- } else {
|
|
|
- continue;
|
|
|
- }
|
|
|
- } else {
|
|
|
- stream = current_audio.impl.GetDeviceBuf(device);
|
|
|
- if (stream == NULL) {
|
|
|
- stream = device->fake_stream;
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- /* Now read from the streamer */
|
|
|
- SDL_StreamRead(&device->streamer, stream, stream_len);
|
|
|
-
|
|
|
- /* Ready current buffer for play and change current buffer */
|
|
|
- if (stream != device->fake_stream) {
|
|
|
- current_audio.impl.PlayDevice(device);
|
|
|
- /* Wait for an audio buffer to become available */
|
|
|
- current_audio.impl.WaitDevice(device);
|
|
|
- } else {
|
|
|
- SDL_Delay(delay);
|
|
|
- }
|
|
|
- }
|
|
|
+ if (stream == NULL) {
|
|
|
+ stream = device->fake_stream;
|
|
|
+ }
|
|
|
|
|
|
+ /* !!! FIXME: this should be LockDevice. */
|
|
|
+ SDL_LockMutex(device->mixer_lock);
|
|
|
+ if (device->paused) {
|
|
|
+ SDL_memset(stream, silence, stream_len);
|
|
|
+ } else {
|
|
|
+ (*fill) (udata, stream, stream_len);
|
|
|
}
|
|
|
- } else
|
|
|
-#endif
|
|
|
- {
|
|
|
- /* Otherwise, do not use the streamer. This is the old code. */
|
|
|
- const int silence = (int) device->spec.silence;
|
|
|
-
|
|
|
- /* Loop, filling the audio buffers */
|
|
|
- while (device->enabled) {
|
|
|
-
|
|
|
- /* Fill the current buffer with sound */
|
|
|
- if (device->convert.needed) {
|
|
|
- if (device->convert.buf) {
|
|
|
- stream = device->convert.buf;
|
|
|
- } else {
|
|
|
- continue;
|
|
|
- }
|
|
|
- } else {
|
|
|
- stream = current_audio.impl.GetDeviceBuf(device);
|
|
|
- if (stream == NULL) {
|
|
|
- stream = device->fake_stream;
|
|
|
- }
|
|
|
- }
|
|
|
+ SDL_UnlockMutex(device->mixer_lock);
|
|
|
|
|
|
- SDL_LockMutex(device->mixer_lock);
|
|
|
- if (device->paused) {
|
|
|
- SDL_memset(stream, silence, stream_len);
|
|
|
+ /* Convert the audio if necessary */
|
|
|
+ if (device->enabled && device->convert.needed) {
|
|
|
+ SDL_ConvertAudio(&device->convert);
|
|
|
+ stream = current_audio.impl.GetDeviceBuf(device);
|
|
|
+ if (stream == NULL) {
|
|
|
+ stream = device->fake_stream;
|
|
|
} else {
|
|
|
- (*fill) (udata, stream, stream_len);
|
|
|
- }
|
|
|
- SDL_UnlockMutex(device->mixer_lock);
|
|
|
-
|
|
|
- /* Convert the audio if necessary */
|
|
|
- if (device->convert.needed) {
|
|
|
- SDL_ConvertAudio(&device->convert);
|
|
|
- stream = current_audio.impl.GetDeviceBuf(device);
|
|
|
- if (stream == NULL) {
|
|
|
- stream = device->fake_stream;
|
|
|
- }
|
|
|
SDL_memcpy(stream, device->convert.buf,
|
|
|
device->convert.len_cvt);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- /* Ready current buffer for play and change current buffer */
|
|
|
- if (stream != device->fake_stream) {
|
|
|
- current_audio.impl.PlayDevice(device);
|
|
|
- /* Wait for an audio buffer to become available */
|
|
|
- current_audio.impl.WaitDevice(device);
|
|
|
- } else {
|
|
|
- SDL_Delay(delay);
|
|
|
- }
|
|
|
+ /* Ready current buffer for play and change current buffer */
|
|
|
+ if (stream == device->fake_stream) {
|
|
|
+ SDL_Delay(delay);
|
|
|
+ } else {
|
|
|
+ current_audio.impl.PlayDevice(device);
|
|
|
+ current_audio.impl.WaitDevice(device);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /* Wait for the audio to drain.. */
|
|
|
+ /* Wait for the audio to drain. */
|
|
|
current_audio.impl.WaitDone(device);
|
|
|
|
|
|
- /* If necessary, deinit the streamer */
|
|
|
-#if 0 /* !!! FIXME: rewrite/remove this streamer code. */
|
|
|
- if (device->use_streamer == 1)
|
|
|
- SDL_StreamDeinit(&device->streamer);
|
|
|
-#endif
|
|
|
-
|
|
|
- return (0);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
|
|
@@ -757,16 +695,16 @@ SDL_ParseAudioFormat(const char *string)
|
|
|
int
|
|
|
SDL_GetNumAudioDrivers(void)
|
|
|
{
|
|
|
- return (SDL_arraysize(bootstrap) - 1);
|
|
|
+ return SDL_arraysize(bootstrap) - 1;
|
|
|
}
|
|
|
|
|
|
const char *
|
|
|
SDL_GetAudioDriver(int index)
|
|
|
{
|
|
|
if (index >= 0 && index < SDL_GetNumAudioDrivers()) {
|
|
|
- return (bootstrap[index]->name);
|
|
|
+ return bootstrap[index]->name;
|
|
|
}
|
|
|
- return (NULL);
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
int
|
|
@@ -780,8 +718,8 @@ SDL_AudioInit(const char *driver_name)
|
|
|
SDL_AudioQuit(); /* shutdown driver if already running. */
|
|
|
}
|
|
|
|
|
|
- SDL_memset(¤t_audio, '\0', sizeof(current_audio));
|
|
|
- SDL_memset(open_devices, '\0', sizeof(open_devices));
|
|
|
+ SDL_zero(current_audio);
|
|
|
+ SDL_zero(open_devices);
|
|
|
|
|
|
/* Select the proper audio driver */
|
|
|
if (driver_name == NULL) {
|
|
@@ -797,7 +735,7 @@ SDL_AudioInit(const char *driver_name)
|
|
|
}
|
|
|
|
|
|
tried_to_init = 1;
|
|
|
- SDL_memset(¤t_audio, 0, sizeof(current_audio));
|
|
|
+ SDL_zero(current_audio);
|
|
|
current_audio.name = backend->name;
|
|
|
current_audio.desc = backend->desc;
|
|
|
initialized = backend->init(¤t_audio.impl);
|
|
@@ -813,13 +751,18 @@ SDL_AudioInit(const char *driver_name)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- SDL_memset(¤t_audio, 0, sizeof(current_audio));
|
|
|
- return (-1); /* No driver was available, so fail. */
|
|
|
+ SDL_zero(current_audio);
|
|
|
+ return -1; /* No driver was available, so fail. */
|
|
|
}
|
|
|
|
|
|
+ current_audio.detectionLock = SDL_CreateMutex();
|
|
|
+
|
|
|
finalize_audio_entry_points();
|
|
|
|
|
|
- return (0);
|
|
|
+ /* Make sure we have a list of devices available at startup. */
|
|
|
+ current_audio.impl.DetectDevices();
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -831,50 +774,32 @@ SDL_GetCurrentAudioDriver()
|
|
|
return current_audio.name;
|
|
|
}
|
|
|
|
|
|
+/* Clean out devices that we've removed but had to keep around for stability. */
|
|
|
static void
|
|
|
-free_device_list(char ***devices, int *devCount)
|
|
|
+clean_out_device_list(SDL_AudioDeviceItem **devices, int *devCount, SDL_bool *removedFlag)
|
|
|
{
|
|
|
- int i = *devCount;
|
|
|
- if ((i > 0) && (*devices != NULL)) {
|
|
|
- while (i--) {
|
|
|
- SDL_free((*devices)[i]);
|
|
|
+ SDL_AudioDeviceItem *item = *devices;
|
|
|
+ SDL_AudioDeviceItem *prev = NULL;
|
|
|
+ int total = 0;
|
|
|
+
|
|
|
+ while (item) {
|
|
|
+ SDL_AudioDeviceItem *next = item->next;
|
|
|
+ if (item->handle != NULL) {
|
|
|
+ total++;
|
|
|
+ prev = item;
|
|
|
+ } else {
|
|
|
+ if (prev) {
|
|
|
+ prev->next = next;
|
|
|
+ } else {
|
|
|
+ *devices = next;
|
|
|
+ }
|
|
|
+ SDL_free(item);
|
|
|
}
|
|
|
+ item = next;
|
|
|
}
|
|
|
|
|
|
- SDL_free(*devices);
|
|
|
-
|
|
|
- *devices = NULL;
|
|
|
- *devCount = 0;
|
|
|
-}
|
|
|
-
|
|
|
-static
|
|
|
-void SDL_AddCaptureAudioDevice(const char *_name)
|
|
|
-{
|
|
|
- char *name = NULL;
|
|
|
- void *ptr = SDL_realloc(current_audio.inputDevices,
|
|
|
- (current_audio.inputDeviceCount+1) * sizeof(char*));
|
|
|
- if (ptr == NULL) {
|
|
|
- return; /* oh well. */
|
|
|
- }
|
|
|
-
|
|
|
- current_audio.inputDevices = (char **) ptr;
|
|
|
- name = SDL_strdup(_name); /* if this returns NULL, that's okay. */
|
|
|
- current_audio.inputDevices[current_audio.inputDeviceCount++] = name;
|
|
|
-}
|
|
|
-
|
|
|
-static
|
|
|
-void SDL_AddOutputAudioDevice(const char *_name)
|
|
|
-{
|
|
|
- char *name = NULL;
|
|
|
- void *ptr = SDL_realloc(current_audio.outputDevices,
|
|
|
- (current_audio.outputDeviceCount+1) * sizeof(char*));
|
|
|
- if (ptr == NULL) {
|
|
|
- return; /* oh well. */
|
|
|
- }
|
|
|
-
|
|
|
- current_audio.outputDevices = (char **) ptr;
|
|
|
- name = SDL_strdup(_name); /* if this returns NULL, that's okay. */
|
|
|
- current_audio.outputDevices[current_audio.outputDeviceCount++] = name;
|
|
|
+ *devCount = total;
|
|
|
+ *removedFlag = SDL_FALSE;
|
|
|
}
|
|
|
|
|
|
|
|
@@ -887,29 +812,18 @@ SDL_GetNumAudioDevices(int iscapture)
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
- if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
|
|
|
- return 0;
|
|
|
+ SDL_LockMutex(current_audio.detectionLock);
|
|
|
+ if (iscapture && current_audio.captureDevicesRemoved) {
|
|
|
+ clean_out_device_list(¤t_audio.inputDevices, ¤t_audio.inputDeviceCount, ¤t_audio.captureDevicesRemoved);
|
|
|
}
|
|
|
|
|
|
- if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
|
|
|
- return 1;
|
|
|
+ if (!iscapture && current_audio.outputDevicesRemoved) {
|
|
|
+ clean_out_device_list(¤t_audio.outputDevices, ¤t_audio.outputDeviceCount, ¤t_audio.outputDevicesRemoved);
|
|
|
+ current_audio.outputDevicesRemoved = SDL_FALSE;
|
|
|
}
|
|
|
|
|
|
- if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
|
|
|
- return 1;
|
|
|
- }
|
|
|
-
|
|
|
- if (iscapture) {
|
|
|
- free_device_list(¤t_audio.inputDevices,
|
|
|
- ¤t_audio.inputDeviceCount);
|
|
|
- current_audio.impl.DetectDevices(iscapture, SDL_AddCaptureAudioDevice);
|
|
|
- retval = current_audio.inputDeviceCount;
|
|
|
- } else {
|
|
|
- free_device_list(¤t_audio.outputDevices,
|
|
|
- ¤t_audio.outputDeviceCount);
|
|
|
- current_audio.impl.DetectDevices(iscapture, SDL_AddOutputAudioDevice);
|
|
|
- retval = current_audio.outputDeviceCount;
|
|
|
- }
|
|
|
+ retval = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount;
|
|
|
+ SDL_UnlockMutex(current_audio.detectionLock);
|
|
|
|
|
|
return retval;
|
|
|
}
|
|
@@ -918,6 +832,8 @@ SDL_GetNumAudioDevices(int iscapture)
|
|
|
const char *
|
|
|
SDL_GetAudioDeviceName(int index, int iscapture)
|
|
|
{
|
|
|
+ const char *retval = NULL;
|
|
|
+
|
|
|
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
|
|
SDL_SetError("Audio subsystem is not initialized");
|
|
|
return NULL;
|
|
@@ -928,39 +844,28 @@ SDL_GetAudioDeviceName(int index, int iscapture)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- if (index < 0) {
|
|
|
- goto no_such_device;
|
|
|
- }
|
|
|
-
|
|
|
- if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
|
|
|
- if (index > 0) {
|
|
|
- goto no_such_device;
|
|
|
- }
|
|
|
- return DEFAULT_INPUT_DEVNAME;
|
|
|
- }
|
|
|
+ if (index >= 0) {
|
|
|
+ SDL_AudioDeviceItem *item;
|
|
|
+ int i;
|
|
|
|
|
|
- if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
|
|
|
- if (index > 0) {
|
|
|
- goto no_such_device;
|
|
|
+ SDL_LockMutex(current_audio.detectionLock);
|
|
|
+ item = iscapture ? current_audio.inputDevices : current_audio.outputDevices;
|
|
|
+ i = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount;
|
|
|
+ if (index < i) {
|
|
|
+ for (i--; i > index; i--, item = item->next) {
|
|
|
+ SDL_assert(item != NULL);
|
|
|
+ }
|
|
|
+ SDL_assert(item != NULL);
|
|
|
+ retval = item->name;
|
|
|
}
|
|
|
- return DEFAULT_OUTPUT_DEVNAME;
|
|
|
+ SDL_UnlockMutex(current_audio.detectionLock);
|
|
|
}
|
|
|
|
|
|
- if (iscapture) {
|
|
|
- if (index >= current_audio.inputDeviceCount) {
|
|
|
- goto no_such_device;
|
|
|
- }
|
|
|
- return current_audio.inputDevices[index];
|
|
|
- } else {
|
|
|
- if (index >= current_audio.outputDeviceCount) {
|
|
|
- goto no_such_device;
|
|
|
- }
|
|
|
- return current_audio.outputDevices[index];
|
|
|
+ if (retval == NULL) {
|
|
|
+ SDL_SetError("No such device");
|
|
|
}
|
|
|
|
|
|
-no_such_device:
|
|
|
- SDL_SetError("No such device");
|
|
|
- return NULL;
|
|
|
+ return retval;
|
|
|
}
|
|
|
|
|
|
|
|
@@ -968,6 +873,7 @@ static void
|
|
|
close_audio_device(SDL_AudioDevice * device)
|
|
|
{
|
|
|
device->enabled = 0;
|
|
|
+ device->shutdown = 1;
|
|
|
if (device->thread != NULL) {
|
|
|
SDL_WaitThread(device->thread, NULL);
|
|
|
}
|
|
@@ -1061,6 +967,8 @@ open_audio_device(const char *devname, int iscapture,
|
|
|
SDL_AudioSpec _obtained;
|
|
|
SDL_AudioDevice *device;
|
|
|
SDL_bool build_cvt;
|
|
|
+ void *handle = NULL;
|
|
|
+ Uint32 stream_len;
|
|
|
int i = 0;
|
|
|
|
|
|
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
|
@@ -1073,6 +981,18 @@ open_audio_device(const char *devname, int iscapture,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+ /* Find an available device ID... */
|
|
|
+ for (id = min_id - 1; id < SDL_arraysize(open_devices); id++) {
|
|
|
+ if (open_devices[id] == NULL) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (id == SDL_arraysize(open_devices)) {
|
|
|
+ SDL_SetError("Too many open audio devices");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
if (!obtained) {
|
|
|
obtained = &_obtained;
|
|
|
}
|
|
@@ -1108,9 +1028,7 @@ open_audio_device(const char *devname, int iscapture,
|
|
|
return 0;
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
|
|
|
+ } else if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
|
|
|
if ((devname) && (SDL_strcmp(devname, DEFAULT_OUTPUT_DEVNAME) != 0)) {
|
|
|
SDL_SetError("No such device");
|
|
|
return 0;
|
|
@@ -1123,6 +1041,30 @@ open_audio_device(const char *devname, int iscapture,
|
|
|
return 0;
|
|
|
}
|
|
|
}
|
|
|
+ } else if (devname != NULL) {
|
|
|
+ /* if the app specifies an exact string, we can pass the backend
|
|
|
+ an actual device handle thingey, which saves them the effort of
|
|
|
+ figuring out what device this was (such as, reenumerating
|
|
|
+ everything again to find the matching human-readable name).
|
|
|
+ It might still need to open a device based on the string for,
|
|
|
+ say, a network audio server, but this optimizes some cases. */
|
|
|
+ SDL_AudioDeviceItem *item;
|
|
|
+ SDL_LockMutex(current_audio.detectionLock);
|
|
|
+ for (item = iscapture ? current_audio.inputDevices : current_audio.outputDevices; item; item = item->next) {
|
|
|
+ if ((item->handle != NULL) && (SDL_strcmp(item->name, devname) == 0)) {
|
|
|
+ handle = item->handle;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ SDL_UnlockMutex(current_audio.detectionLock);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!current_audio.impl.AllowsArbitraryDeviceNames) {
|
|
|
+ /* has to be in our device list, or the default device. */
|
|
|
+ if ((handle == NULL) && (devname != NULL)) {
|
|
|
+ SDL_SetError("No such device.");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
device = (SDL_AudioDevice *) SDL_AllocAudioMem(sizeof(SDL_AudioDevice));
|
|
@@ -1131,12 +1073,13 @@ open_audio_device(const char *devname, int iscapture,
|
|
|
return 0;
|
|
|
}
|
|
|
SDL_zerop(device);
|
|
|
+ device->id = id + 1;
|
|
|
device->spec = *obtained;
|
|
|
device->enabled = 1;
|
|
|
device->paused = 1;
|
|
|
device->iscapture = iscapture;
|
|
|
|
|
|
- /* Create a semaphore for locking the sound buffers */
|
|
|
+ /* Create a mutex for locking the sound buffers */
|
|
|
if (!current_audio.impl.SkipMixerLock) {
|
|
|
device->mixer_lock = SDL_CreateMutex();
|
|
|
if (device->mixer_lock == NULL) {
|
|
@@ -1146,26 +1089,12 @@ open_audio_device(const char *devname, int iscapture,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /* force a device detection if we haven't done one yet. */
|
|
|
- if ( ((iscapture) && (current_audio.inputDevices == NULL)) ||
|
|
|
- ((!iscapture) && (current_audio.outputDevices == NULL)) ) {
|
|
|
- SDL_GetNumAudioDevices(iscapture);
|
|
|
- }
|
|
|
-
|
|
|
- if (current_audio.impl.OpenDevice(device, devname, iscapture) < 0) {
|
|
|
+ if (current_audio.impl.OpenDevice(device, handle, devname, iscapture) < 0) {
|
|
|
close_audio_device(device);
|
|
|
return 0;
|
|
|
}
|
|
|
device->opened = 1;
|
|
|
|
|
|
- /* Allocate a fake audio memory buffer */
|
|
|
- device->fake_stream = (Uint8 *)SDL_AllocAudioMem(device->spec.size);
|
|
|
- if (device->fake_stream == NULL) {
|
|
|
- close_audio_device(device);
|
|
|
- SDL_OutOfMemory();
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
/* See if we need to do any conversion */
|
|
|
build_cvt = SDL_FALSE;
|
|
|
if (obtained->freq != device->spec.freq) {
|
|
@@ -1224,6 +1153,19 @@ open_audio_device(const char *devname, int iscapture,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /* Allocate a fake audio memory buffer */
|
|
|
+ stream_len = (device->convert.needed) ? device->convert.len_cvt : 0;
|
|
|
+ if (device->spec.size > stream_len) {
|
|
|
+ stream_len = device->spec.size;
|
|
|
+ }
|
|
|
+ SDL_assert(stream_len > 0);
|
|
|
+ device->fake_stream = (Uint8 *)SDL_AllocAudioMem(stream_len);
|
|
|
+ if (device->fake_stream == NULL) {
|
|
|
+ close_audio_device(device);
|
|
|
+ SDL_OutOfMemory();
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
if (device->spec.callback == NULL) { /* use buffer queueing? */
|
|
|
/* pool a few packets to start. Enough for two callbacks. */
|
|
|
const int packetlen = SDL_AUDIOBUFFERQUEUE_PACKETLEN;
|
|
@@ -1243,25 +1185,14 @@ open_audio_device(const char *devname, int iscapture,
|
|
|
device->spec.userdata = device;
|
|
|
}
|
|
|
|
|
|
- /* Find an available device ID and store the structure... */
|
|
|
- for (id = min_id - 1; id < SDL_arraysize(open_devices); id++) {
|
|
|
- if (open_devices[id] == NULL) {
|
|
|
- open_devices[id] = device;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (id == SDL_arraysize(open_devices)) {
|
|
|
- SDL_SetError("Too many open audio devices");
|
|
|
- close_audio_device(device);
|
|
|
- return 0;
|
|
|
- }
|
|
|
+ /* add it to our list of open devices. */
|
|
|
+ open_devices[id] = device;
|
|
|
|
|
|
/* Start the audio thread if necessary */
|
|
|
if (!current_audio.impl.ProvidesOwnCallbackThread) {
|
|
|
/* Start the audio thread */
|
|
|
char name[64];
|
|
|
- SDL_snprintf(name, sizeof (name), "SDLAudioDev%d", (int) (id + 1));
|
|
|
+ SDL_snprintf(name, sizeof (name), "SDLAudioDev%d", (int) device->id);
|
|
|
/* !!! FIXME: this is nasty. */
|
|
|
#if defined(__WIN32__) && !defined(HAVE_LIBC)
|
|
|
#undef SDL_CreateThread
|
|
@@ -1274,13 +1205,13 @@ open_audio_device(const char *devname, int iscapture,
|
|
|
device->thread = SDL_CreateThread(SDL_RunAudio, name, device);
|
|
|
#endif
|
|
|
if (device->thread == NULL) {
|
|
|
- SDL_CloseAudioDevice(id + 1);
|
|
|
+ SDL_CloseAudioDevice(device->id);
|
|
|
SDL_SetError("Couldn't create audio thread");
|
|
|
return 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return id + 1;
|
|
|
+ return device->id;
|
|
|
}
|
|
|
|
|
|
|
|
@@ -1292,14 +1223,14 @@ SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained)
|
|
|
/* Start up the audio driver, if necessary. This is legacy behaviour! */
|
|
|
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
|
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
|
|
- return (-1);
|
|
|
+ return -1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* SDL_OpenAudio() is legacy and can only act on Device ID #1. */
|
|
|
if (open_devices[0] != NULL) {
|
|
|
SDL_SetError("Audio device is already opened");
|
|
|
- return (-1);
|
|
|
+ return -1;
|
|
|
}
|
|
|
|
|
|
if (obtained) {
|
|
@@ -1310,7 +1241,7 @@ SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained)
|
|
|
}
|
|
|
|
|
|
SDL_assert((id == 0) || (id == 1));
|
|
|
- return ((id == 0) ? -1 : 0);
|
|
|
+ return (id == 0) ? -1 : 0;
|
|
|
}
|
|
|
|
|
|
SDL_AudioDeviceID
|
|
@@ -1334,7 +1265,7 @@ SDL_GetAudioDeviceStatus(SDL_AudioDeviceID devid)
|
|
|
status = SDL_AUDIO_PLAYING;
|
|
|
}
|
|
|
}
|
|
|
- return (status);
|
|
|
+ return status;
|
|
|
}
|
|
|
|
|
|
|
|
@@ -1425,14 +1356,16 @@ SDL_AudioQuit(void)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ free_device_list(¤t_audio.outputDevices, ¤t_audio.outputDeviceCount);
|
|
|
+ free_device_list(¤t_audio.inputDevices, ¤t_audio.inputDeviceCount);
|
|
|
+
|
|
|
/* Free the driver data */
|
|
|
current_audio.impl.Deinitialize();
|
|
|
- free_device_list(¤t_audio.outputDevices,
|
|
|
- ¤t_audio.outputDeviceCount);
|
|
|
- free_device_list(¤t_audio.inputDevices,
|
|
|
- ¤t_audio.inputDeviceCount);
|
|
|
- SDL_memset(¤t_audio, '\0', sizeof(current_audio));
|
|
|
- SDL_memset(open_devices, '\0', sizeof(open_devices));
|
|
|
+
|
|
|
+ SDL_DestroyMutex(current_audio.detectionLock);
|
|
|
+
|
|
|
+ SDL_zero(current_audio);
|
|
|
+ SDL_zero(open_devices);
|
|
|
}
|
|
|
|
|
|
#define NUM_FORMATS 10
|
|
@@ -1470,16 +1403,16 @@ SDL_FirstAudioFormat(SDL_AudioFormat format)
|
|
|
}
|
|
|
}
|
|
|
format_idx_sub = 0;
|
|
|
- return (SDL_NextAudioFormat());
|
|
|
+ return SDL_NextAudioFormat();
|
|
|
}
|
|
|
|
|
|
SDL_AudioFormat
|
|
|
SDL_NextAudioFormat(void)
|
|
|
{
|
|
|
if ((format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS)) {
|
|
|
- return (0);
|
|
|
+ return 0;
|
|
|
}
|
|
|
- return (format_list[format_idx][format_idx_sub++]);
|
|
|
+ return format_list[format_idx][format_idx_sub++];
|
|
|
}
|
|
|
|
|
|
void
|