mirror of
https://github.com/Ryujinx/SDL.git
synced 2024-12-23 13:25:33 +00:00
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_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 */
|
||||
SDL_CONTROLLERAXISMOTION = 0x650, /**< Game controller axis motion */
|
||||
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 */
|
||||
} 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.*)
|
||||
|
@ -516,6 +534,7 @@ typedef union SDL_Event
|
|||
SDL_ControllerAxisEvent caxis; /**< Game Controller axis event data */
|
||||
SDL_ControllerButtonEvent cbutton; /**< Game Controller button 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_UserEvent user; /**< Custom event data */
|
||||
SDL_SysWMEvent syswm; /**< System dependent window event data */
|
||||
|
|
|
@ -333,6 +333,144 @@ SDL_StreamDeinit(SDL_AudioStreamer * stream)
|
|||
}
|
||||
#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... */
|
||||
|
||||
|
@ -690,6 +828,13 @@ SDL_RunAudio(void *devicep)
|
|||
|
||||
/* !!! FIXME: this should be LockDevice. */
|
||||
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) {
|
||||
SDL_memset(stream, silence, stream_len);
|
||||
} else {
|
||||
|
@ -821,8 +966,34 @@ SDL_AudioInit(const char *driver_name)
|
|||
return -1; /* No driver was available, so fail. */
|
||||
}
|
||||
|
||||
current_audio.detection_lock = SDL_CreateMutex();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -835,53 +1006,6 @@ SDL_GetCurrentAudioDriver()
|
|||
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
|
||||
SDL_GetNumAudioDevices(int iscapture)
|
||||
{
|
||||
|
@ -903,18 +1027,20 @@ SDL_GetNumAudioDevices(int iscapture)
|
|||
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;
|
||||
if (current_audio.need_capture_device_redetect) {
|
||||
current_audio.need_capture_device_redetect = SDL_FALSE;
|
||||
perform_full_device_redetect(SDL_TRUE);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -922,6 +1048,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;
|
||||
|
@ -950,16 +1078,18 @@ SDL_GetAudioDeviceName(int index, int iscapture)
|
|||
return DEFAULT_OUTPUT_DEVNAME;
|
||||
}
|
||||
|
||||
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];
|
||||
SDL_LockMutex(current_audio.detection_lock);
|
||||
if (iscapture && (index < current_audio.inputDeviceCount)) {
|
||||
retval = current_audio.inputDevices[index];
|
||||
} else if (!iscapture && (index < current_audio.outputDeviceCount)) {
|
||||
retval = current_audio.outputDevices[index];
|
||||
}
|
||||
SDL_UnlockMutex(current_audio.detection_lock);
|
||||
|
||||
/* !!! FIXME: a device could be removed after being returned here, freeing retval's pointer. */
|
||||
|
||||
if (retval != NULL) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
no_such_device:
|
||||
|
@ -1077,6 +1207,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;
|
||||
}
|
||||
|
@ -1135,6 +1277,7 @@ 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;
|
||||
|
@ -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) {
|
||||
close_audio_device(device);
|
||||
return 0;
|
||||
|
@ -1247,25 +1384,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
|
||||
|
@ -1278,13 +1404,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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1431,12 +1557,16 @@ SDL_AudioQuit(void)
|
|||
|
||||
/* 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.detection_lock);
|
||||
|
||||
SDL_zero(current_audio);
|
||||
SDL_zero(open_devices);
|
||||
}
|
||||
|
||||
#define NUM_FORMATS 10
|
||||
|
|
|
@ -31,7 +31,16 @@ typedef struct SDL_AudioDevice SDL_AudioDevice;
|
|||
#define _THIS SDL_AudioDevice *_this
|
||||
|
||||
/* 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
|
||||
these as necessary and pool them, under the assumption that we'll
|
||||
|
@ -92,6 +101,12 @@ typedef struct SDL_AudioDriver
|
|||
|
||||
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;
|
||||
int outputDeviceCount;
|
||||
|
||||
|
@ -114,6 +129,7 @@ struct SDL_AudioDevice
|
|||
{
|
||||
/* * * */
|
||||
/* Data common to all devices */
|
||||
SDL_AudioDeviceID id;
|
||||
|
||||
/* The current audio specification (shared with audio thread) */
|
||||
SDL_AudioSpec spec;
|
||||
|
|
|
@ -320,7 +320,7 @@ ALSA_PlayDevice(_THIS)
|
|||
/* Hmm, not much we can do - abort */
|
||||
fprintf(stderr, "ALSA write failed (unrecoverable): %s\n",
|
||||
ALSA_snd_strerror(status));
|
||||
this->enabled = 0;
|
||||
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
|
|
|
@ -151,7 +151,7 @@ ARTS_WaitDevice(_THIS)
|
|||
/* Check every 10 loops */
|
||||
if (this->hidden->parent && (((++cnt) % 10) == 0)) {
|
||||
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 (written < 0) {
|
||||
this->enabled = 0;
|
||||
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||
|
|
|
@ -150,7 +150,7 @@ BSDAUDIO_WaitDevice(_THIS)
|
|||
the user know what happened.
|
||||
*/
|
||||
fprintf(stderr, "SDL: %s\n", message);
|
||||
this->enabled = 0;
|
||||
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||
/* Don't try to close - may hang */
|
||||
this->hidden->audio_fd = -1;
|
||||
#ifdef DEBUG_AUDIO
|
||||
|
@ -195,7 +195,7 @@ BSDAUDIO_PlayDevice(_THIS)
|
|||
|
||||
/* If we couldn't write, assume fatal error for now */
|
||||
if (written < 0) {
|
||||
this->enabled = 0;
|
||||
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||
|
|
|
@ -40,13 +40,50 @@ static void COREAUDIO_CloseDevice(_THIS);
|
|||
}
|
||||
|
||||
#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
|
||||
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;
|
||||
addfn(name);
|
||||
if (add_to_internal_dev_list(iscapture, devId)) {
|
||||
addfn(name);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct
|
||||
|
@ -57,7 +94,7 @@ typedef struct
|
|||
} FindDevIdData;
|
||||
|
||||
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;
|
||||
if (!data->found) {
|
||||
|
@ -77,14 +114,8 @@ build_device_list(int iscapture, addDevFn addfn, void *addfndata)
|
|||
UInt32 i = 0;
|
||||
UInt32 max = 0;
|
||||
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioHardwarePropertyDevices,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
|
||||
0, NULL, &size);
|
||||
result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
|
||||
&devlist_address, 0, NULL, &size);
|
||||
if (result != kAudioHardwareNoError)
|
||||
return;
|
||||
|
||||
|
@ -92,8 +123,8 @@ build_device_list(int iscapture, addDevFn addfn, void *addfndata)
|
|||
if (devs == NULL)
|
||||
return;
|
||||
|
||||
result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
|
||||
0, NULL, &size, devs);
|
||||
result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
|
||||
&devlist_address, 0, NULL, &size, devs);
|
||||
if (result != kAudioHardwareNoError)
|
||||
return;
|
||||
|
||||
|
@ -105,10 +136,17 @@ build_device_list(int iscapture, addDevFn addfn, void *addfndata)
|
|||
AudioBufferList *buflist = NULL;
|
||||
int usable = 0;
|
||||
CFIndex len = 0;
|
||||
const AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyStreamConfiguration,
|
||||
iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
|
||||
kAudioDevicePropertyScopeOutput;
|
||||
addr.mSelector = kAudioDevicePropertyStreamConfiguration;
|
||||
const AudioObjectPropertyAddress nameaddr = {
|
||||
kAudioObjectPropertyName,
|
||||
iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
result = AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size);
|
||||
if (result != noErr)
|
||||
|
@ -136,9 +174,9 @@ build_device_list(int iscapture, addDevFn addfn, void *addfndata)
|
|||
if (!usable)
|
||||
continue;
|
||||
|
||||
addr.mSelector = kAudioObjectPropertyName;
|
||||
|
||||
size = sizeof (CFStringRef);
|
||||
result = AudioObjectGetPropertyData(dev, &addr, 0, NULL, &size, &cfstr);
|
||||
result = AudioObjectGetPropertyData(dev, &nameaddr, 0, NULL, &size, &cfstr);
|
||||
if (result != kAudioHardwareNoError)
|
||||
continue;
|
||||
|
||||
|
@ -169,18 +207,96 @@ build_device_list(int iscapture, addDevFn addfn, void *addfndata)
|
|||
((iscapture) ? "capture" : "output"),
|
||||
(int) *devCount, ptr, (int) dev);
|
||||
#endif
|
||||
addfn(ptr, dev, addfndata);
|
||||
addfn(ptr, iscapture, dev, addfndata);
|
||||
}
|
||||
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
|
||||
COREAUDIO_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
|
||||
{
|
||||
free_audio_device_list(iscapture ? &capture_devs : &output_devs);
|
||||
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
|
||||
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
|
||||
COREAUDIO_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden != NULL) {
|
||||
if (this->hidden->audioUnitOpened) {
|
||||
#if MACOSX_COREAUDIO
|
||||
/* Unregister our disconnect callback. */
|
||||
AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
|
||||
#endif
|
||||
|
||||
AURenderCallbackStruct callback;
|
||||
const AudioUnitElement output_bus = 0;
|
||||
const AudioUnitElement input_bus = 1;
|
||||
|
@ -355,7 +514,6 @@ COREAUDIO_CloseDevice(_THIS)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
prepare_audiounit(_THIS, const char *devname, int iscapture,
|
||||
const AudioStreamBasicDescription * strdesc)
|
||||
|
@ -454,6 +612,11 @@ prepare_audiounit(_THIS, const char *devname, int iscapture,
|
|||
result = AudioOutputUnitStart(this->hidden->audioUnit);
|
||||
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! */
|
||||
return 1;
|
||||
}
|
||||
|
@ -527,15 +690,27 @@ COREAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
|
|||
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
|
||||
COREAUDIO_Init(SDL_AudioDriverImpl * impl)
|
||||
{
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = COREAUDIO_OpenDevice;
|
||||
impl->CloseDevice = COREAUDIO_CloseDevice;
|
||||
impl->Deinitialize = COREAUDIO_Deinitialize;
|
||||
|
||||
#if MACOSX_COREAUDIO
|
||||
impl->DetectDevices = COREAUDIO_DetectDevices;
|
||||
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
|
||||
#else
|
||||
impl->OnlyHasDefaultOutputDevice = 1;
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ DISKAUD_PlayDevice(_THIS)
|
|||
|
||||
/* If we couldn't write, assume fatal error for now */
|
||||
if (written != this->hidden->mixlen) {
|
||||
this->enabled = 0;
|
||||
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||
|
|
|
@ -270,7 +270,7 @@ DSP_PlayDevice(_THIS)
|
|||
const int mixlen = this->hidden->mixlen;
|
||||
if (write(this->hidden->audio_fd, mixbuf, mixlen) == -1) {
|
||||
perror("Audio write");
|
||||
this->enabled = 0;
|
||||
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen);
|
||||
|
|
|
@ -129,7 +129,7 @@ ESD_WaitDevice(_THIS)
|
|||
/* Check every 10 loops */
|
||||
if (this->hidden->parent && (((++cnt) % 10) == 0)) {
|
||||
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 (written < 0) {
|
||||
this->enabled = 0;
|
||||
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -143,7 +143,7 @@ SDL_FS_PlayDevice(_THIS)
|
|||
this->hidden->mixsamples);
|
||||
/* If we couldn't write, assume fatal error for now */
|
||||
if (ret) {
|
||||
this->enabled = 0;
|
||||
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
|
||||
|
|
|
@ -176,7 +176,7 @@ PAUDIO_WaitDevice(_THIS)
|
|||
* the user know what happened.
|
||||
*/
|
||||
fprintf(stderr, "SDL: %s - %s\n", strerror(errno), message);
|
||||
this->enabled = 0;
|
||||
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||
/* Don't try to close - may hang */
|
||||
this->hidden->audio_fd = -1;
|
||||
#ifdef DEBUG_AUDIO
|
||||
|
@ -212,7 +212,7 @@ PAUDIO_PlayDevice(_THIS)
|
|||
|
||||
/* If we couldn't write, assume fatal error for now */
|
||||
if (written < 0) {
|
||||
this->enabled = 0;
|
||||
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
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 ||
|
||||
PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
|
||||
PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
|
||||
this->enabled = 0;
|
||||
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||
return;
|
||||
}
|
||||
if (PULSEAUDIO_pa_stream_writable_size(h->stream) >= h->mixlen) {
|
||||
|
@ -318,7 +318,7 @@ PULSEAUDIO_PlayDevice(_THIS)
|
|||
struct SDL_PrivateAudioData *h = this->hidden;
|
||||
if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, h->mixlen, NULL, 0LL,
|
||||
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 (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 ( written == 0 ) {
|
||||
this->enabled = 0;
|
||||
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
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,
|
||||
this->hidden->fragsize) < 0) {
|
||||
/* Assume fatal error, for now */
|
||||
this->enabled = 0;
|
||||
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||
}
|
||||
this->hidden->written += this->hidden->fragsize;
|
||||
} else {
|
||||
|
@ -168,7 +168,7 @@ SUNAUDIO_PlayDevice(_THIS)
|
|||
if (write(this->hidden->audio_fd, this->hidden->mixbuf,
|
||||
this->spec.size) < 0) {
|
||||
/* Assume fatal error, for now */
|
||||
this->enabled = 0;
|
||||
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||
}
|
||||
this->hidden->written += this->hidden->fragsize;
|
||||
}
|
||||
|
|
|
@ -221,7 +221,7 @@ XAUDIO2_PlayDevice(_THIS)
|
|||
|
||||
if (result != S_OK) { /* uhoh, panic! */
|
||||
IXAudio2SourceVoice_FlushSourceBuffers(source);
|
||||
this->enabled = 0;
|
||||
SDL_AudioDeviceDisconnected(SDL_FALSE, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ TARGETS = \
|
|||
testloadso$(EXE) \
|
||||
testlock$(EXE) \
|
||||
testmultiaudio$(EXE) \
|
||||
testaudiohotplug$(EXE) \
|
||||
testnative$(EXE) \
|
||||
testoverlay2$(EXE) \
|
||||
testplatform$(EXE) \
|
||||
|
@ -105,6 +106,9 @@ testautomation$(EXE): $(srcdir)/testautomation.c \
|
|||
testmultiaudio$(EXE): $(srcdir)/testmultiaudio.c
|
||||
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
||||
|
||||
testaudiohotplug$(EXE): $(srcdir)/testaudiohotplug.c
|
||||
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
||||
|
||||
testatomic$(EXE): $(srcdir)/testatomic.c
|
||||
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
||||
|
||||
|
|
182
test/testaudiohotplug.c
Normal file
182
test/testaudiohotplug.c
Normal file
|
@ -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 a new issue