mirror of https://github.com/encounter/SDL.git
Initial work on audio device hotplug support.
This fills in the core pieces and fully implements it for Mac OS X. Most other platforms, at the moment, will report a disconnected device if it fails to write audio, but don't notice if the system's device list changed at all.
This commit is contained in:
parent
338bf5d297
commit
0e02ce0856
|
@ -110,6 +110,10 @@ typedef enum
|
||||||
SDL_JOYDEVICEADDED, /**< A new joystick has been inserted into the system */
|
SDL_JOYDEVICEADDED, /**< A new joystick has been inserted into the system */
|
||||||
SDL_JOYDEVICEREMOVED, /**< An opened joystick has been removed */
|
SDL_JOYDEVICEREMOVED, /**< An opened joystick has been removed */
|
||||||
|
|
||||||
|
/* Audio hotplug events */
|
||||||
|
SDL_AUDIODEVICEADDED = 0x700, /**< A new audio device is available */
|
||||||
|
SDL_AUDIODEVICEREMOVED, /**< An audio device has been removed. */
|
||||||
|
|
||||||
/* Game controller events */
|
/* Game controller events */
|
||||||
SDL_CONTROLLERAXISMOTION = 0x650, /**< Game controller axis motion */
|
SDL_CONTROLLERAXISMOTION = 0x650, /**< Game controller axis motion */
|
||||||
SDL_CONTROLLERBUTTONDOWN, /**< Game controller button pressed */
|
SDL_CONTROLLERBUTTONDOWN, /**< Game controller button pressed */
|
||||||
|
@ -382,6 +386,20 @@ typedef struct SDL_ControllerDeviceEvent
|
||||||
Sint32 which; /**< The joystick device index for the ADDED event, instance id for the REMOVED or REMAPPED event */
|
Sint32 which; /**< The joystick device index for the ADDED event, instance id for the REMOVED or REMAPPED event */
|
||||||
} SDL_ControllerDeviceEvent;
|
} SDL_ControllerDeviceEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Audio device event structure (event.adevice.*)
|
||||||
|
*/
|
||||||
|
typedef struct SDL_AudioDeviceEvent
|
||||||
|
{
|
||||||
|
Uint32 type; /**< ::SDL_AUDIODEVICEADDED, or ::SDL_AUDIODEVICEREMOVED */
|
||||||
|
Uint32 timestamp;
|
||||||
|
Uint32 which; /**< The audio device index for the ADDED event (valid until next SDL_GetNumAudioDevices() call), SDL_AudioDeviceID for the REMOVED event */
|
||||||
|
Uint8 iscapture; /**< zero if an output device, non-zero if a capture device. */
|
||||||
|
Uint8 padding1;
|
||||||
|
Uint8 padding2;
|
||||||
|
Uint8 padding3;
|
||||||
|
} SDL_AudioDeviceEvent;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Touch finger event structure (event.tfinger.*)
|
* \brief Touch finger event structure (event.tfinger.*)
|
||||||
|
@ -516,6 +534,7 @@ typedef union SDL_Event
|
||||||
SDL_ControllerAxisEvent caxis; /**< Game Controller axis event data */
|
SDL_ControllerAxisEvent caxis; /**< Game Controller axis event data */
|
||||||
SDL_ControllerButtonEvent cbutton; /**< Game Controller button event data */
|
SDL_ControllerButtonEvent cbutton; /**< Game Controller button event data */
|
||||||
SDL_ControllerDeviceEvent cdevice; /**< Game Controller device event data */
|
SDL_ControllerDeviceEvent cdevice; /**< Game Controller device event data */
|
||||||
|
SDL_AudioDeviceEvent adevice; /**< Audio device event data */
|
||||||
SDL_QuitEvent quit; /**< Quit request event data */
|
SDL_QuitEvent quit; /**< Quit request event data */
|
||||||
SDL_UserEvent user; /**< Custom event data */
|
SDL_UserEvent user; /**< Custom event data */
|
||||||
SDL_SysWMEvent syswm; /**< System dependent window event data */
|
SDL_SysWMEvent syswm; /**< System dependent window event data */
|
||||||
|
|
|
@ -333,6 +333,144 @@ SDL_StreamDeinit(SDL_AudioStreamer * stream)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* device hotplug support... */
|
||||||
|
|
||||||
|
/* this function expects its caller to hold current_audio.detection_lock */
|
||||||
|
static int
|
||||||
|
add_audio_device(const char *_name, char ***_devices, int *_devCount)
|
||||||
|
{
|
||||||
|
char *name = SDL_strdup(_name);
|
||||||
|
int retval = -1;
|
||||||
|
|
||||||
|
if (name != NULL) {
|
||||||
|
char **devices = *_devices;
|
||||||
|
int devCount = *_devCount;
|
||||||
|
void *ptr = SDL_realloc(devices, (devCount+1) * sizeof(char*));
|
||||||
|
if (ptr == NULL) {
|
||||||
|
SDL_free(name);
|
||||||
|
} else {
|
||||||
|
retval = devCount;
|
||||||
|
devices = (char **) ptr;
|
||||||
|
devices[devCount++] = name;
|
||||||
|
*_devices = devices;
|
||||||
|
*_devCount = devCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
add_capture_device(const char *name)
|
||||||
|
{
|
||||||
|
/* !!! FIXME: add this later. SDL_assert(current_audio.impl.HasCaptureSupport);*/
|
||||||
|
return add_audio_device(name, ¤t_audio.inputDevices, ¤t_audio.inputDeviceCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
add_output_device(const char *name)
|
||||||
|
{
|
||||||
|
return add_audio_device(name, ¤t_audio.outputDevices, ¤t_audio.outputDeviceCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_device_list(char ***devices, int *devCount)
|
||||||
|
{
|
||||||
|
int i = *devCount;
|
||||||
|
if ((i > 0) && (*devices != NULL)) {
|
||||||
|
while (i--) {
|
||||||
|
SDL_free((*devices)[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_free(*devices);
|
||||||
|
|
||||||
|
*devices = NULL;
|
||||||
|
*devCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
perform_full_device_redetect(const int iscapture)
|
||||||
|
{
|
||||||
|
SDL_LockMutex(current_audio.detection_lock);
|
||||||
|
|
||||||
|
if (iscapture) {
|
||||||
|
if (!current_audio.impl.OnlyHasDefaultOutputDevice) {
|
||||||
|
free_device_list(¤t_audio.outputDevices, ¤t_audio.outputDeviceCount);
|
||||||
|
current_audio.impl.DetectDevices(SDL_FALSE, add_output_device);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((current_audio.impl.HasCaptureSupport) && (!current_audio.impl.OnlyHasDefaultInputDevice)) {
|
||||||
|
free_device_list(¤t_audio.inputDevices, ¤t_audio.inputDeviceCount);
|
||||||
|
current_audio.impl.DetectDevices(SDL_TRUE, add_capture_device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_UnlockMutex(current_audio.detection_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The audio backends call this when a new device is plugged in. */
|
||||||
|
void
|
||||||
|
SDL_AudioDeviceConnected(const int iscapture, const char *name)
|
||||||
|
{
|
||||||
|
int device_index = -1;
|
||||||
|
|
||||||
|
SDL_LockMutex(current_audio.detection_lock);
|
||||||
|
if (iscapture) {
|
||||||
|
device_index = add_capture_device(name);
|
||||||
|
} else {
|
||||||
|
device_index = add_output_device(name);
|
||||||
|
}
|
||||||
|
SDL_UnlockMutex(current_audio.detection_lock);
|
||||||
|
|
||||||
|
if (device_index != -1) {
|
||||||
|
/* Post the event, if desired */
|
||||||
|
if (SDL_GetEventState(SDL_AUDIODEVICEADDED) == SDL_ENABLE) {
|
||||||
|
SDL_Event event;
|
||||||
|
event.adevice.type = SDL_AUDIODEVICEADDED;
|
||||||
|
event.adevice.which = device_index;
|
||||||
|
event.adevice.iscapture = iscapture;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The audio backends call this when a device is unplugged. */
|
||||||
|
void
|
||||||
|
SDL_AudioDeviceDisconnected(const int iscapture, SDL_AudioDevice *device)
|
||||||
|
{
|
||||||
|
/* device==NULL means an unopened device was lost; do the redetect only. */
|
||||||
|
if (device != NULL) {
|
||||||
|
SDL_assert(get_audio_device(device->id) == device);
|
||||||
|
SDL_assert(device->enabled); /* called more than once?! */
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
event.adevice.type = SDL_AUDIODEVICEREMOVED;
|
||||||
|
event.adevice.which = device->id;
|
||||||
|
event.adevice.iscapture = device->iscapture ? 1 : 0;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we don't really know which name (if any) was associated with this
|
||||||
|
device in the device list, so drop the entire list and rebuild it.
|
||||||
|
(we should probably change the API in 2.1 to make this more clear?) */
|
||||||
|
if (iscapture) {
|
||||||
|
current_audio.need_capture_device_redetect = SDL_TRUE;
|
||||||
|
} else {
|
||||||
|
current_audio.need_output_device_redetect = SDL_TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* buffer queueing support... */
|
/* buffer queueing support... */
|
||||||
|
|
||||||
|
@ -690,6 +828,13 @@ SDL_RunAudio(void *devicep)
|
||||||
|
|
||||||
/* !!! FIXME: this should be LockDevice. */
|
/* !!! FIXME: this should be LockDevice. */
|
||||||
SDL_LockMutex(device->mixer_lock);
|
SDL_LockMutex(device->mixer_lock);
|
||||||
|
|
||||||
|
/* Check again, in case device was removed while a lock was held. */
|
||||||
|
if (!device->enabled) {
|
||||||
|
SDL_UnlockMutex(device->mixer_lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (device->paused) {
|
if (device->paused) {
|
||||||
SDL_memset(stream, silence, stream_len);
|
SDL_memset(stream, silence, stream_len);
|
||||||
} else {
|
} else {
|
||||||
|
@ -821,8 +966,34 @@ SDL_AudioInit(const char *driver_name)
|
||||||
return -1; /* No driver was available, so fail. */
|
return -1; /* No driver was available, so fail. */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
current_audio.detection_lock = SDL_CreateMutex();
|
||||||
|
|
||||||
finalize_audio_entry_points();
|
finalize_audio_entry_points();
|
||||||
|
|
||||||
|
/* Make sure we have a list of devices available at startup. */
|
||||||
|
perform_full_device_redetect(SDL_TRUE);
|
||||||
|
perform_full_device_redetect(SDL_FALSE);
|
||||||
|
|
||||||
|
/* Post an add event for each initial device, if desired */
|
||||||
|
if (SDL_GetEventState(SDL_AUDIODEVICEADDED) == SDL_ENABLE) {
|
||||||
|
SDL_Event event;
|
||||||
|
|
||||||
|
SDL_zero(event);
|
||||||
|
event.adevice.type = SDL_AUDIODEVICEADDED;
|
||||||
|
|
||||||
|
event.adevice.iscapture = 0;
|
||||||
|
for (i = 0; i < current_audio.outputDeviceCount; i++) {
|
||||||
|
event.adevice.which = i;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
}
|
||||||
|
|
||||||
|
event.adevice.iscapture = 1;
|
||||||
|
for (i = 0; i < current_audio.inputDeviceCount; i++) {
|
||||||
|
event.adevice.which = i;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -835,53 +1006,6 @@ SDL_GetCurrentAudioDriver()
|
||||||
return current_audio.name;
|
return current_audio.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
free_device_list(char ***devices, int *devCount)
|
|
||||||
{
|
|
||||||
int i = *devCount;
|
|
||||||
if ((i > 0) && (*devices != NULL)) {
|
|
||||||
while (i--) {
|
|
||||||
SDL_free((*devices)[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
SDL_GetNumAudioDevices(int iscapture)
|
SDL_GetNumAudioDevices(int iscapture)
|
||||||
{
|
{
|
||||||
|
@ -903,18 +1027,20 @@ SDL_GetNumAudioDevices(int iscapture)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iscapture) {
|
if (current_audio.need_capture_device_redetect) {
|
||||||
free_device_list(¤t_audio.inputDevices,
|
current_audio.need_capture_device_redetect = SDL_FALSE;
|
||||||
¤t_audio.inputDeviceCount);
|
perform_full_device_redetect(SDL_TRUE);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (current_audio.need_output_device_redetect) {
|
||||||
|
current_audio.need_output_device_redetect = SDL_FALSE;
|
||||||
|
perform_full_device_redetect(SDL_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_LockMutex(current_audio.detection_lock);
|
||||||
|
retval = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount;
|
||||||
|
SDL_UnlockMutex(current_audio.detection_lock);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -922,6 +1048,8 @@ SDL_GetNumAudioDevices(int iscapture)
|
||||||
const char *
|
const char *
|
||||||
SDL_GetAudioDeviceName(int index, int iscapture)
|
SDL_GetAudioDeviceName(int index, int iscapture)
|
||||||
{
|
{
|
||||||
|
const char *retval = NULL;
|
||||||
|
|
||||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||||
SDL_SetError("Audio subsystem is not initialized");
|
SDL_SetError("Audio subsystem is not initialized");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -950,16 +1078,18 @@ SDL_GetAudioDeviceName(int index, int iscapture)
|
||||||
return DEFAULT_OUTPUT_DEVNAME;
|
return DEFAULT_OUTPUT_DEVNAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iscapture) {
|
SDL_LockMutex(current_audio.detection_lock);
|
||||||
if (index >= current_audio.inputDeviceCount) {
|
if (iscapture && (index < current_audio.inputDeviceCount)) {
|
||||||
goto no_such_device;
|
retval = current_audio.inputDevices[index];
|
||||||
|
} else if (!iscapture && (index < current_audio.outputDeviceCount)) {
|
||||||
|
retval = current_audio.outputDevices[index];
|
||||||
}
|
}
|
||||||
return current_audio.inputDevices[index];
|
SDL_UnlockMutex(current_audio.detection_lock);
|
||||||
} else {
|
|
||||||
if (index >= current_audio.outputDeviceCount) {
|
/* !!! FIXME: a device could be removed after being returned here, freeing retval's pointer. */
|
||||||
goto no_such_device;
|
|
||||||
}
|
if (retval != NULL) {
|
||||||
return current_audio.outputDevices[index];
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
no_such_device:
|
no_such_device:
|
||||||
|
@ -1077,6 +1207,18 @@ open_audio_device(const char *devname, int iscapture,
|
||||||
return 0;
|
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) {
|
if (!obtained) {
|
||||||
obtained = &_obtained;
|
obtained = &_obtained;
|
||||||
}
|
}
|
||||||
|
@ -1135,6 +1277,7 @@ open_audio_device(const char *devname, int iscapture,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
SDL_zerop(device);
|
SDL_zerop(device);
|
||||||
|
device->id = id + 1;
|
||||||
device->spec = *obtained;
|
device->spec = *obtained;
|
||||||
device->enabled = 1;
|
device->enabled = 1;
|
||||||
device->paused = 1;
|
device->paused = 1;
|
||||||
|
@ -1150,12 +1293,6 @@ 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, devname, iscapture) < 0) {
|
||||||
close_audio_device(device);
|
close_audio_device(device);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1247,25 +1384,14 @@ open_audio_device(const char *devname, int iscapture,
|
||||||
device->spec.userdata = device;
|
device->spec.userdata = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find an available device ID and store the structure... */
|
/* add it to our list of open devices. */
|
||||||
for (id = min_id - 1; id < SDL_arraysize(open_devices); id++) {
|
|
||||||
if (open_devices[id] == NULL) {
|
|
||||||
open_devices[id] = device;
|
open_devices[id] = device;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id == SDL_arraysize(open_devices)) {
|
|
||||||
SDL_SetError("Too many open audio devices");
|
|
||||||
close_audio_device(device);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start the audio thread if necessary */
|
/* Start the audio thread if necessary */
|
||||||
if (!current_audio.impl.ProvidesOwnCallbackThread) {
|
if (!current_audio.impl.ProvidesOwnCallbackThread) {
|
||||||
/* Start the audio thread */
|
/* Start the audio thread */
|
||||||
char name[64];
|
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. */
|
/* !!! FIXME: this is nasty. */
|
||||||
#if defined(__WIN32__) && !defined(HAVE_LIBC)
|
#if defined(__WIN32__) && !defined(HAVE_LIBC)
|
||||||
#undef SDL_CreateThread
|
#undef SDL_CreateThread
|
||||||
|
@ -1278,13 +1404,13 @@ open_audio_device(const char *devname, int iscapture,
|
||||||
device->thread = SDL_CreateThread(SDL_RunAudio, name, device);
|
device->thread = SDL_CreateThread(SDL_RunAudio, name, device);
|
||||||
#endif
|
#endif
|
||||||
if (device->thread == NULL) {
|
if (device->thread == NULL) {
|
||||||
SDL_CloseAudioDevice(id + 1);
|
SDL_CloseAudioDevice(device->id);
|
||||||
SDL_SetError("Couldn't create audio thread");
|
SDL_SetError("Couldn't create audio thread");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return id + 1;
|
return device->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1431,12 +1557,16 @@ SDL_AudioQuit(void)
|
||||||
|
|
||||||
/* Free the driver data */
|
/* Free the driver data */
|
||||||
current_audio.impl.Deinitialize();
|
current_audio.impl.Deinitialize();
|
||||||
|
|
||||||
free_device_list(¤t_audio.outputDevices,
|
free_device_list(¤t_audio.outputDevices,
|
||||||
¤t_audio.outputDeviceCount);
|
¤t_audio.outputDeviceCount);
|
||||||
free_device_list(¤t_audio.inputDevices,
|
free_device_list(¤t_audio.inputDevices,
|
||||||
¤t_audio.inputDeviceCount);
|
¤t_audio.inputDeviceCount);
|
||||||
SDL_memset(¤t_audio, '\0', sizeof(current_audio));
|
|
||||||
SDL_memset(open_devices, '\0', sizeof(open_devices));
|
SDL_DestroyMutex(current_audio.detection_lock);
|
||||||
|
|
||||||
|
SDL_zero(current_audio);
|
||||||
|
SDL_zero(open_devices);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define NUM_FORMATS 10
|
#define NUM_FORMATS 10
|
||||||
|
|
|
@ -31,7 +31,16 @@ typedef struct SDL_AudioDevice SDL_AudioDevice;
|
||||||
#define _THIS SDL_AudioDevice *_this
|
#define _THIS SDL_AudioDevice *_this
|
||||||
|
|
||||||
/* Used by audio targets during DetectDevices() */
|
/* Used by audio targets during DetectDevices() */
|
||||||
typedef void (*SDL_AddAudioDevice)(const char *name);
|
typedef int (*SDL_AddAudioDevice)(const char *name);
|
||||||
|
|
||||||
|
/* Audio targets should call this as devices are hotplugged. Don't call
|
||||||
|
during DetectDevices(), this is for hotplugging a device later. */
|
||||||
|
extern void SDL_AudioDeviceConnected(const int iscapture, const char *name);
|
||||||
|
|
||||||
|
/* Audio targets should call this as devices are unplugged.
|
||||||
|
(device) can be NULL if an unopened device is lost. */
|
||||||
|
extern void SDL_AudioDeviceDisconnected(const int iscapture, SDL_AudioDevice *device);
|
||||||
|
|
||||||
|
|
||||||
/* This is the size of a packet when using SDL_QueueAudio(). We allocate
|
/* This is the size of a packet when using SDL_QueueAudio(). We allocate
|
||||||
these as necessary and pool them, under the assumption that we'll
|
these as necessary and pool them, under the assumption that we'll
|
||||||
|
@ -92,6 +101,12 @@ typedef struct SDL_AudioDriver
|
||||||
|
|
||||||
SDL_AudioDriverImpl impl;
|
SDL_AudioDriverImpl impl;
|
||||||
|
|
||||||
|
/* A mutex for device detection */
|
||||||
|
SDL_mutex *detection_lock;
|
||||||
|
|
||||||
|
SDL_bool need_capture_device_redetect;
|
||||||
|
SDL_bool need_output_device_redetect;
|
||||||
|
|
||||||
char **outputDevices;
|
char **outputDevices;
|
||||||
int outputDeviceCount;
|
int outputDeviceCount;
|
||||||
|
|
||||||
|
@ -114,6 +129,7 @@ struct SDL_AudioDevice
|
||||||
{
|
{
|
||||||
/* * * */
|
/* * * */
|
||||||
/* Data common to all devices */
|
/* Data common to all devices */
|
||||||
|
SDL_AudioDeviceID id;
|
||||||
|
|
||||||
/* The current audio specification (shared with audio thread) */
|
/* The current audio specification (shared with audio thread) */
|
||||||
SDL_AudioSpec spec;
|
SDL_AudioSpec spec;
|
||||||
|
|
|
@ -320,7 +320,7 @@ ALSA_PlayDevice(_THIS)
|
||||||
/* Hmm, not much we can do - abort */
|
/* Hmm, not much we can do - abort */
|
||||||
fprintf(stderr, "ALSA write failed (unrecoverable): %s\n",
|
fprintf(stderr, "ALSA write failed (unrecoverable): %s\n",
|
||||||
ALSA_snd_strerror(status));
|
ALSA_snd_strerror(status));
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -151,7 +151,7 @@ ARTS_WaitDevice(_THIS)
|
||||||
/* Check every 10 loops */
|
/* Check every 10 loops */
|
||||||
if (this->hidden->parent && (((++cnt) % 10) == 0)) {
|
if (this->hidden->parent && (((++cnt) % 10) == 0)) {
|
||||||
if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) {
|
if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) {
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,7 @@ ARTS_PlayDevice(_THIS)
|
||||||
|
|
||||||
/* If we couldn't write, assume fatal error for now */
|
/* If we couldn't write, assume fatal error for now */
|
||||||
if (written < 0) {
|
if (written < 0) {
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_AUDIO
|
#ifdef DEBUG_AUDIO
|
||||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||||
|
|
|
@ -150,7 +150,7 @@ BSDAUDIO_WaitDevice(_THIS)
|
||||||
the user know what happened.
|
the user know what happened.
|
||||||
*/
|
*/
|
||||||
fprintf(stderr, "SDL: %s\n", message);
|
fprintf(stderr, "SDL: %s\n", message);
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
/* Don't try to close - may hang */
|
/* Don't try to close - may hang */
|
||||||
this->hidden->audio_fd = -1;
|
this->hidden->audio_fd = -1;
|
||||||
#ifdef DEBUG_AUDIO
|
#ifdef DEBUG_AUDIO
|
||||||
|
@ -195,7 +195,7 @@ BSDAUDIO_PlayDevice(_THIS)
|
||||||
|
|
||||||
/* If we couldn't write, assume fatal error for now */
|
/* If we couldn't write, assume fatal error for now */
|
||||||
if (written < 0) {
|
if (written < 0) {
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_AUDIO
|
#ifdef DEBUG_AUDIO
|
||||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||||
|
|
|
@ -40,14 +40,51 @@ static void COREAUDIO_CloseDevice(_THIS);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if MACOSX_COREAUDIO
|
#if MACOSX_COREAUDIO
|
||||||
typedef void (*addDevFn)(const char *name, AudioDeviceID devId, void *data);
|
static const AudioObjectPropertyAddress devlist_address = {
|
||||||
|
kAudioHardwarePropertyDevices,
|
||||||
|
kAudioObjectPropertyScopeGlobal,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*addDevFn)(const char *name, const int iscapture, AudioDeviceID devId, void *data);
|
||||||
|
|
||||||
|
typedef struct AudioDeviceList
|
||||||
|
{
|
||||||
|
AudioDeviceID devid;
|
||||||
|
SDL_bool alive;
|
||||||
|
struct AudioDeviceList *next;
|
||||||
|
} AudioDeviceList;
|
||||||
|
|
||||||
|
static AudioDeviceList *output_devs = NULL;
|
||||||
|
static AudioDeviceList *capture_devs = NULL;
|
||||||
|
|
||||||
|
static SDL_bool
|
||||||
|
add_to_internal_dev_list(const int iscapture, AudioDeviceID devId)
|
||||||
|
{
|
||||||
|
AudioDeviceList *item = (AudioDeviceList *) SDL_malloc(sizeof (AudioDeviceList));
|
||||||
|
if (item == NULL) {
|
||||||
|
return SDL_FALSE;
|
||||||
|
}
|
||||||
|
item->devid = devId;
|
||||||
|
item->alive = SDL_TRUE;
|
||||||
|
item->next = iscapture ? capture_devs : output_devs;
|
||||||
|
if (iscapture) {
|
||||||
|
capture_devs = item;
|
||||||
|
} else {
|
||||||
|
output_devs = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDL_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
addToDevList(const char *name, AudioDeviceID devId, void *data)
|
addToDevList(const char *name, const int iscapture, AudioDeviceID devId, void *data)
|
||||||
{
|
{
|
||||||
SDL_AddAudioDevice addfn = (SDL_AddAudioDevice) data;
|
SDL_AddAudioDevice addfn = (SDL_AddAudioDevice) data;
|
||||||
|
if (add_to_internal_dev_list(iscapture, devId)) {
|
||||||
addfn(name);
|
addfn(name);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
@ -57,7 +94,7 @@ typedef struct
|
||||||
} FindDevIdData;
|
} FindDevIdData;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
findDevId(const char *name, AudioDeviceID devId, void *_data)
|
findDevId(const char *name, const int iscapture, AudioDeviceID devId, void *_data)
|
||||||
{
|
{
|
||||||
FindDevIdData *data = (FindDevIdData *) _data;
|
FindDevIdData *data = (FindDevIdData *) _data;
|
||||||
if (!data->found) {
|
if (!data->found) {
|
||||||
|
@ -77,14 +114,8 @@ build_device_list(int iscapture, addDevFn addfn, void *addfndata)
|
||||||
UInt32 i = 0;
|
UInt32 i = 0;
|
||||||
UInt32 max = 0;
|
UInt32 max = 0;
|
||||||
|
|
||||||
AudioObjectPropertyAddress addr = {
|
result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
|
||||||
kAudioHardwarePropertyDevices,
|
&devlist_address, 0, NULL, &size);
|
||||||
kAudioObjectPropertyScopeGlobal,
|
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
};
|
|
||||||
|
|
||||||
result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
|
|
||||||
0, NULL, &size);
|
|
||||||
if (result != kAudioHardwareNoError)
|
if (result != kAudioHardwareNoError)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -92,8 +123,8 @@ build_device_list(int iscapture, addDevFn addfn, void *addfndata)
|
||||||
if (devs == NULL)
|
if (devs == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
|
result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
|
||||||
0, NULL, &size, devs);
|
&devlist_address, 0, NULL, &size, devs);
|
||||||
if (result != kAudioHardwareNoError)
|
if (result != kAudioHardwareNoError)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -105,10 +136,17 @@ build_device_list(int iscapture, addDevFn addfn, void *addfndata)
|
||||||
AudioBufferList *buflist = NULL;
|
AudioBufferList *buflist = NULL;
|
||||||
int usable = 0;
|
int usable = 0;
|
||||||
CFIndex len = 0;
|
CFIndex len = 0;
|
||||||
|
const AudioObjectPropertyAddress addr = {
|
||||||
|
kAudioDevicePropertyStreamConfiguration,
|
||||||
|
iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
|
||||||
addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
|
const AudioObjectPropertyAddress nameaddr = {
|
||||||
kAudioDevicePropertyScopeOutput;
|
kAudioObjectPropertyName,
|
||||||
addr.mSelector = kAudioDevicePropertyStreamConfiguration;
|
iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
|
||||||
result = AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size);
|
result = AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size);
|
||||||
if (result != noErr)
|
if (result != noErr)
|
||||||
|
@ -136,9 +174,9 @@ build_device_list(int iscapture, addDevFn addfn, void *addfndata)
|
||||||
if (!usable)
|
if (!usable)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
addr.mSelector = kAudioObjectPropertyName;
|
|
||||||
size = sizeof (CFStringRef);
|
size = sizeof (CFStringRef);
|
||||||
result = AudioObjectGetPropertyData(dev, &addr, 0, NULL, &size, &cfstr);
|
result = AudioObjectGetPropertyData(dev, &nameaddr, 0, NULL, &size, &cfstr);
|
||||||
if (result != kAudioHardwareNoError)
|
if (result != kAudioHardwareNoError)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -169,18 +207,96 @@ build_device_list(int iscapture, addDevFn addfn, void *addfndata)
|
||||||
((iscapture) ? "capture" : "output"),
|
((iscapture) ? "capture" : "output"),
|
||||||
(int) *devCount, ptr, (int) dev);
|
(int) *devCount, ptr, (int) dev);
|
||||||
#endif
|
#endif
|
||||||
addfn(ptr, dev, addfndata);
|
addfn(ptr, iscapture, dev, addfndata);
|
||||||
}
|
}
|
||||||
SDL_free(ptr); /* addfn() would have copied the string. */
|
SDL_free(ptr); /* addfn() would have copied the string. */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_audio_device_list(AudioDeviceList **list)
|
||||||
|
{
|
||||||
|
AudioDeviceList *item = *list;
|
||||||
|
while (item) {
|
||||||
|
AudioDeviceList *next = item->next;
|
||||||
|
SDL_free(item);
|
||||||
|
item = next;
|
||||||
|
}
|
||||||
|
*list = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
COREAUDIO_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
|
COREAUDIO_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
|
||||||
{
|
{
|
||||||
|
free_audio_device_list(iscapture ? &capture_devs : &output_devs);
|
||||||
build_device_list(iscapture, addToDevList, addfn);
|
build_device_list(iscapture, addToDevList, addfn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
build_device_change_list(const char *name, const int iscapture, AudioDeviceID devId, void *data)
|
||||||
|
{
|
||||||
|
AudioDeviceList **list = (AudioDeviceList **) data;
|
||||||
|
AudioDeviceList *item;
|
||||||
|
for (item = *list; item != NULL; item = item->next) {
|
||||||
|
if (item->devid == devId) {
|
||||||
|
item->alive = SDL_TRUE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add_to_internal_dev_list(iscapture, devId); /* new device, add it. */
|
||||||
|
SDL_AudioDeviceConnected(iscapture, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SDL_bool
|
||||||
|
reprocess_device_list(const int iscapture, AudioDeviceList **list)
|
||||||
|
{
|
||||||
|
SDL_bool was_disconnect = SDL_FALSE;
|
||||||
|
AudioDeviceList *item;
|
||||||
|
AudioDeviceList *prev = NULL;
|
||||||
|
for (item = *list; item != NULL; item = item->next) {
|
||||||
|
item->alive = SDL_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
build_device_list(iscapture, build_device_change_list, list);
|
||||||
|
|
||||||
|
/* free items in the list that aren't still alive. */
|
||||||
|
item = *list;
|
||||||
|
while (item != NULL) {
|
||||||
|
AudioDeviceList *next = item->next;
|
||||||
|
if (item->alive) {
|
||||||
|
prev = item;
|
||||||
|
} else {
|
||||||
|
was_disconnect = SDL_TRUE;
|
||||||
|
if (prev) {
|
||||||
|
prev->next = item->next;
|
||||||
|
} else {
|
||||||
|
*list = item->next;
|
||||||
|
}
|
||||||
|
SDL_free(item);
|
||||||
|
}
|
||||||
|
item = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return was_disconnect;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* this is called when the system's list of available audio devices changes. */
|
||||||
|
static OSStatus
|
||||||
|
device_list_changed(AudioObjectID systemObj, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
|
||||||
|
{
|
||||||
|
if (reprocess_device_list(SDL_TRUE, &capture_devs)) {
|
||||||
|
SDL_AudioDeviceDisconnected(SDL_TRUE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reprocess_device_list(SDL_FALSE, &output_devs)) {
|
||||||
|
SDL_AudioDeviceDisconnected(SDL_FALSE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
find_device_by_name(_THIS, const char *devname, int iscapture)
|
find_device_by_name(_THIS, const char *devname, int iscapture)
|
||||||
{
|
{
|
||||||
|
@ -317,11 +433,54 @@ inputCallback(void *inRefCon,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if MACOSX_COREAUDIO
|
||||||
|
static const AudioObjectPropertyAddress alive_address =
|
||||||
|
{
|
||||||
|
kAudioDevicePropertyDeviceIsAlive,
|
||||||
|
kAudioObjectPropertyScopeGlobal,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
|
||||||
|
static OSStatus
|
||||||
|
device_unplugged(AudioObjectID devid, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
|
||||||
|
{
|
||||||
|
SDL_AudioDevice *this = (SDL_AudioDevice *) data;
|
||||||
|
SDL_bool dead = SDL_FALSE;
|
||||||
|
UInt32 isAlive = 1;
|
||||||
|
UInt32 size = sizeof (isAlive);
|
||||||
|
OSStatus error;
|
||||||
|
|
||||||
|
if (!this->enabled) {
|
||||||
|
return 0; /* already known to be dead. */
|
||||||
|
}
|
||||||
|
|
||||||
|
error = AudioObjectGetPropertyData(this->hidden->deviceID, &alive_address,
|
||||||
|
0, NULL, &size, &isAlive);
|
||||||
|
|
||||||
|
if (error == kAudioHardwareBadDeviceError) {
|
||||||
|
dead = SDL_TRUE; /* device was unplugged. */
|
||||||
|
} else if ((error == kAudioHardwareNoError) && (!isAlive)) {
|
||||||
|
dead = SDL_TRUE; /* device died in some other way. */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dead) {
|
||||||
|
SDL_AudioDeviceDisconnected(this->iscapture, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
COREAUDIO_CloseDevice(_THIS)
|
COREAUDIO_CloseDevice(_THIS)
|
||||||
{
|
{
|
||||||
if (this->hidden != NULL) {
|
if (this->hidden != NULL) {
|
||||||
if (this->hidden->audioUnitOpened) {
|
if (this->hidden->audioUnitOpened) {
|
||||||
|
#if MACOSX_COREAUDIO
|
||||||
|
/* Unregister our disconnect callback. */
|
||||||
|
AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
|
||||||
|
#endif
|
||||||
|
|
||||||
AURenderCallbackStruct callback;
|
AURenderCallbackStruct callback;
|
||||||
const AudioUnitElement output_bus = 0;
|
const AudioUnitElement output_bus = 0;
|
||||||
const AudioUnitElement input_bus = 1;
|
const AudioUnitElement input_bus = 1;
|
||||||
|
@ -355,7 +514,6 @@ COREAUDIO_CloseDevice(_THIS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
prepare_audiounit(_THIS, const char *devname, int iscapture,
|
prepare_audiounit(_THIS, const char *devname, int iscapture,
|
||||||
const AudioStreamBasicDescription * strdesc)
|
const AudioStreamBasicDescription * strdesc)
|
||||||
|
@ -454,6 +612,11 @@ prepare_audiounit(_THIS, const char *devname, int iscapture,
|
||||||
result = AudioOutputUnitStart(this->hidden->audioUnit);
|
result = AudioOutputUnitStart(this->hidden->audioUnit);
|
||||||
CHECK_RESULT("AudioOutputUnitStart");
|
CHECK_RESULT("AudioOutputUnitStart");
|
||||||
|
|
||||||
|
#if MACOSX_COREAUDIO
|
||||||
|
/* Fire a callback if the device stops being "alive" (disconnected, etc). */
|
||||||
|
AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* We're running! */
|
/* We're running! */
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -527,15 +690,27 @@ COREAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
|
||||||
return 0; /* good to go. */
|
return 0; /* good to go. */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
COREAUDIO_Deinitialize(void)
|
||||||
|
{
|
||||||
|
#if MACOSX_COREAUDIO
|
||||||
|
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
|
||||||
|
free_audio_device_list(&capture_devs);
|
||||||
|
free_audio_device_list(&output_devs);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
COREAUDIO_Init(SDL_AudioDriverImpl * impl)
|
COREAUDIO_Init(SDL_AudioDriverImpl * impl)
|
||||||
{
|
{
|
||||||
/* Set the function pointers */
|
/* Set the function pointers */
|
||||||
impl->OpenDevice = COREAUDIO_OpenDevice;
|
impl->OpenDevice = COREAUDIO_OpenDevice;
|
||||||
impl->CloseDevice = COREAUDIO_CloseDevice;
|
impl->CloseDevice = COREAUDIO_CloseDevice;
|
||||||
|
impl->Deinitialize = COREAUDIO_Deinitialize;
|
||||||
|
|
||||||
#if MACOSX_COREAUDIO
|
#if MACOSX_COREAUDIO
|
||||||
impl->DetectDevices = COREAUDIO_DetectDevices;
|
impl->DetectDevices = COREAUDIO_DetectDevices;
|
||||||
|
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
|
||||||
#else
|
#else
|
||||||
impl->OnlyHasDefaultOutputDevice = 1;
|
impl->OnlyHasDefaultOutputDevice = 1;
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ DISKAUD_PlayDevice(_THIS)
|
||||||
|
|
||||||
/* If we couldn't write, assume fatal error for now */
|
/* If we couldn't write, assume fatal error for now */
|
||||||
if (written != this->hidden->mixlen) {
|
if (written != this->hidden->mixlen) {
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_AUDIO
|
#ifdef DEBUG_AUDIO
|
||||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||||
|
|
|
@ -270,7 +270,7 @@ DSP_PlayDevice(_THIS)
|
||||||
const int mixlen = this->hidden->mixlen;
|
const int mixlen = this->hidden->mixlen;
|
||||||
if (write(this->hidden->audio_fd, mixbuf, mixlen) == -1) {
|
if (write(this->hidden->audio_fd, mixbuf, mixlen) == -1) {
|
||||||
perror("Audio write");
|
perror("Audio write");
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_AUDIO
|
#ifdef DEBUG_AUDIO
|
||||||
fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen);
|
fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen);
|
||||||
|
|
|
@ -129,7 +129,7 @@ ESD_WaitDevice(_THIS)
|
||||||
/* Check every 10 loops */
|
/* Check every 10 loops */
|
||||||
if (this->hidden->parent && (((++cnt) % 10) == 0)) {
|
if (this->hidden->parent && (((++cnt) % 10) == 0)) {
|
||||||
if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) {
|
if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) {
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ ESD_PlayDevice(_THIS)
|
||||||
|
|
||||||
/* If we couldn't write, assume fatal error for now */
|
/* If we couldn't write, assume fatal error for now */
|
||||||
if (written < 0) {
|
if (written < 0) {
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -143,7 +143,7 @@ SDL_FS_PlayDevice(_THIS)
|
||||||
this->hidden->mixsamples);
|
this->hidden->mixsamples);
|
||||||
/* If we couldn't write, assume fatal error for now */
|
/* If we couldn't write, assume fatal error for now */
|
||||||
if (ret) {
|
if (ret) {
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_AUDIO
|
#ifdef DEBUG_AUDIO
|
||||||
fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
|
fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
|
||||||
|
|
|
@ -176,7 +176,7 @@ PAUDIO_WaitDevice(_THIS)
|
||||||
* the user know what happened.
|
* the user know what happened.
|
||||||
*/
|
*/
|
||||||
fprintf(stderr, "SDL: %s - %s\n", strerror(errno), message);
|
fprintf(stderr, "SDL: %s - %s\n", strerror(errno), message);
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
/* Don't try to close - may hang */
|
/* Don't try to close - may hang */
|
||||||
this->hidden->audio_fd = -1;
|
this->hidden->audio_fd = -1;
|
||||||
#ifdef DEBUG_AUDIO
|
#ifdef DEBUG_AUDIO
|
||||||
|
@ -212,7 +212,7 @@ PAUDIO_PlayDevice(_THIS)
|
||||||
|
|
||||||
/* If we couldn't write, assume fatal error for now */
|
/* If we couldn't write, assume fatal error for now */
|
||||||
if (written < 0) {
|
if (written < 0) {
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_AUDIO
|
#ifdef DEBUG_AUDIO
|
||||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||||
|
|
|
@ -302,7 +302,7 @@ PULSEAUDIO_WaitDevice(_THIS)
|
||||||
if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
|
if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
|
||||||
PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
|
PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
|
||||||
PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
|
PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (PULSEAUDIO_pa_stream_writable_size(h->stream) >= h->mixlen) {
|
if (PULSEAUDIO_pa_stream_writable_size(h->stream) >= h->mixlen) {
|
||||||
|
@ -318,7 +318,7 @@ PULSEAUDIO_PlayDevice(_THIS)
|
||||||
struct SDL_PrivateAudioData *h = this->hidden;
|
struct SDL_PrivateAudioData *h = this->hidden;
|
||||||
if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, h->mixlen, NULL, 0LL,
|
if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, h->mixlen, NULL, 0LL,
|
||||||
PA_SEEK_RELATIVE) < 0) {
|
PA_SEEK_RELATIVE) < 0) {
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -300,7 +300,7 @@ QSA_PlayDevice(_THIS)
|
||||||
|
|
||||||
/* If we couldn't write, assume fatal error for now */
|
/* If we couldn't write, assume fatal error for now */
|
||||||
if (towrite != 0) {
|
if (towrite != 0) {
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -158,7 +158,7 @@ SNDIO_PlayDevice(_THIS)
|
||||||
|
|
||||||
/* If we couldn't write, assume fatal error for now */
|
/* If we couldn't write, assume fatal error for now */
|
||||||
if ( written == 0 ) {
|
if ( written == 0 ) {
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_AUDIO
|
#ifdef DEBUG_AUDIO
|
||||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||||
|
|
|
@ -158,7 +158,7 @@ SUNAUDIO_PlayDevice(_THIS)
|
||||||
if (write(this->hidden->audio_fd, this->hidden->ulaw_buf,
|
if (write(this->hidden->audio_fd, this->hidden->ulaw_buf,
|
||||||
this->hidden->fragsize) < 0) {
|
this->hidden->fragsize) < 0) {
|
||||||
/* Assume fatal error, for now */
|
/* Assume fatal error, for now */
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
}
|
}
|
||||||
this->hidden->written += this->hidden->fragsize;
|
this->hidden->written += this->hidden->fragsize;
|
||||||
} else {
|
} else {
|
||||||
|
@ -168,7 +168,7 @@ SUNAUDIO_PlayDevice(_THIS)
|
||||||
if (write(this->hidden->audio_fd, this->hidden->mixbuf,
|
if (write(this->hidden->audio_fd, this->hidden->mixbuf,
|
||||||
this->spec.size) < 0) {
|
this->spec.size) < 0) {
|
||||||
/* Assume fatal error, for now */
|
/* Assume fatal error, for now */
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
}
|
}
|
||||||
this->hidden->written += this->hidden->fragsize;
|
this->hidden->written += this->hidden->fragsize;
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,7 +221,7 @@ XAUDIO2_PlayDevice(_THIS)
|
||||||
|
|
||||||
if (result != S_OK) { /* uhoh, panic! */
|
if (result != S_OK) { /* uhoh, panic! */
|
||||||
IXAudio2SourceVoice_FlushSourceBuffers(source);
|
IXAudio2SourceVoice_FlushSourceBuffers(source);
|
||||||
this->enabled = 0;
|
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ TARGETS = \
|
||||||
testloadso$(EXE) \
|
testloadso$(EXE) \
|
||||||
testlock$(EXE) \
|
testlock$(EXE) \
|
||||||
testmultiaudio$(EXE) \
|
testmultiaudio$(EXE) \
|
||||||
|
testaudiohotplug$(EXE) \
|
||||||
testnative$(EXE) \
|
testnative$(EXE) \
|
||||||
testoverlay2$(EXE) \
|
testoverlay2$(EXE) \
|
||||||
testplatform$(EXE) \
|
testplatform$(EXE) \
|
||||||
|
@ -105,6 +106,9 @@ testautomation$(EXE): $(srcdir)/testautomation.c \
|
||||||
testmultiaudio$(EXE): $(srcdir)/testmultiaudio.c
|
testmultiaudio$(EXE): $(srcdir)/testmultiaudio.c
|
||||||
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
||||||
|
|
||||||
|
testaudiohotplug$(EXE): $(srcdir)/testaudiohotplug.c
|
||||||
|
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
||||||
|
|
||||||
testatomic$(EXE): $(srcdir)/testatomic.c
|
testatomic$(EXE): $(srcdir)/testatomic.c
|
||||||
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Program to test hotplugging of audio devices */
|
||||||
|
|
||||||
|
#include "SDL_config.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#if HAVE_SIGNAL_H
|
||||||
|
#include <signal.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
#include <emscripten/emscripten.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "SDL.h"
|
||||||
|
|
||||||
|
static SDL_AudioSpec spec;
|
||||||
|
static Uint8 *sound = NULL; /* Pointer to wave data */
|
||||||
|
static Uint32 soundlen = 0; /* Length of wave data */
|
||||||
|
|
||||||
|
static int posindex = 0;
|
||||||
|
static Uint32 positions[64];
|
||||||
|
|
||||||
|
/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
|
||||||
|
static void
|
||||||
|
quit(int rc)
|
||||||
|
{
|
||||||
|
SDL_Quit();
|
||||||
|
exit(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDLCALL
|
||||||
|
fillerup(void *_pos, Uint8 * stream, int len)
|
||||||
|
{
|
||||||
|
Uint32 pos = *((Uint32 *) _pos);
|
||||||
|
Uint8 *waveptr;
|
||||||
|
int waveleft;
|
||||||
|
|
||||||
|
/* Set up the pointers */
|
||||||
|
waveptr = sound + pos;
|
||||||
|
waveleft = soundlen - pos;
|
||||||
|
|
||||||
|
/* Go! */
|
||||||
|
while (waveleft <= len) {
|
||||||
|
SDL_memcpy(stream, waveptr, waveleft);
|
||||||
|
stream += waveleft;
|
||||||
|
len -= waveleft;
|
||||||
|
waveptr = sound;
|
||||||
|
waveleft = soundlen;
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
SDL_memcpy(stream, waveptr, len);
|
||||||
|
pos += len;
|
||||||
|
*((Uint32 *) _pos) = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int done = 0;
|
||||||
|
void
|
||||||
|
poked(int sig)
|
||||||
|
{
|
||||||
|
done = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
iteration()
|
||||||
|
{
|
||||||
|
SDL_Event e;
|
||||||
|
SDL_AudioDeviceID dev;
|
||||||
|
while (SDL_PollEvent(&e)) {
|
||||||
|
if (e.type == SDL_QUIT) {
|
||||||
|
done = 1;
|
||||||
|
} else if (e.type == SDL_AUDIODEVICEADDED) {
|
||||||
|
const char *name = SDL_GetAudioDeviceName(e.adevice.which, 0);
|
||||||
|
SDL_Log("New %s audio device: %s\n", e.adevice.iscapture ? "capture" : "output", name);
|
||||||
|
if (!e.adevice.iscapture) {
|
||||||
|
positions[posindex] = 0;
|
||||||
|
spec.userdata = &positions[posindex++];
|
||||||
|
spec.callback = fillerup;
|
||||||
|
dev = SDL_OpenAudioDevice(name, 0, &spec, NULL, 0);
|
||||||
|
if (!dev) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open '%s': %s\n", name, SDL_GetError());
|
||||||
|
} else {
|
||||||
|
SDL_Log("Opened '%s' as %u\n", name, (unsigned int) dev);
|
||||||
|
SDL_PauseAudioDevice(dev, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (e.type == SDL_AUDIODEVICEREMOVED) {
|
||||||
|
dev = (SDL_AudioDeviceID) e.adevice.which;
|
||||||
|
SDL_Log("%s device %u removed.\n", e.adevice.iscapture ? "capture" : "output", (unsigned int) dev);
|
||||||
|
SDL_CloseAudioDevice(dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
void
|
||||||
|
loop()
|
||||||
|
{
|
||||||
|
if(done)
|
||||||
|
emscripten_cancel_main_loop();
|
||||||
|
else
|
||||||
|
iteration();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char filename[4096];
|
||||||
|
|
||||||
|
/* Enable standard application logging */
|
||||||
|
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
|
||||||
|
|
||||||
|
/* Load the SDL library */
|
||||||
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_CreateWindow("testaudiohotplug", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
SDL_strlcpy(filename, argv[1], sizeof(filename));
|
||||||
|
} else {
|
||||||
|
SDL_strlcpy(filename, "sample.wav", sizeof(filename));
|
||||||
|
}
|
||||||
|
/* Load the wave file into memory */
|
||||||
|
if (SDL_LoadWAV(filename, &spec, &sound, &soundlen) == NULL) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename, SDL_GetError());
|
||||||
|
quit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HAVE_SIGNAL_H
|
||||||
|
/* Set the signals */
|
||||||
|
#ifdef SIGHUP
|
||||||
|
signal(SIGHUP, poked);
|
||||||
|
#endif
|
||||||
|
signal(SIGINT, poked);
|
||||||
|
#ifdef SIGQUIT
|
||||||
|
signal(SIGQUIT, poked);
|
||||||
|
#endif
|
||||||
|
signal(SIGTERM, poked);
|
||||||
|
#endif /* HAVE_SIGNAL_H */
|
||||||
|
|
||||||
|
/* Show the list of available drivers */
|
||||||
|
SDL_Log("Available audio drivers:");
|
||||||
|
for (i = 0; i < SDL_GetNumAudioDrivers(); ++i) {
|
||||||
|
SDL_Log("%i: %s", i, SDL_GetAudioDriver(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Log("Using audio driver: %s\n", SDL_GetCurrentAudioDriver());
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
emscripten_set_main_loop(loop, 0, 1);
|
||||||
|
#else
|
||||||
|
while (!done) {
|
||||||
|
SDL_Delay(100);
|
||||||
|
iteration();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Clean up on signal */
|
||||||
|
SDL_Quit();
|
||||||
|
SDL_FreeWAV(sound);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vi: set ts=4 sw=4 expandtab: */
|
Loading…
Reference in New Issue