audio: Replace SDL_CreateAndBindAudioStream with SDL_OpenAudioDeviceStream.

This is meant to offer a simplified API for people that are either migrating
directly from SDL2 with minimal effort or just want to make noise without
any of the fancy new API features.

Users of this API can just deal with a single SDL_AudioStream as their only
object/handle into the audio subsystem.

They are still allowed to open multiple devices (or open the same device
multiple times), but cannot change stream bindings on logical devices opened
through this function.

Destroying the single audio stream will also close the logical device behind
the scenes.
This commit is contained in:
Ryan C. Gordon 2023-08-27 13:32:33 -04:00
parent bd088c2f99
commit 1e775e0eef
13 changed files with 164 additions and 137 deletions

View File

@ -53,15 +53,15 @@ The following structures have been renamed:
## SDL_audio.h
The audio subsystem in SDL3 is dramatically different than SDL2. The primary way to play audio is no longer an audio callback; instead you bind SDL_AudioStreams to devices.
The audio subsystem in SDL3 is dramatically different than SDL2. The primary way to play audio is no longer an audio callback; instead you bind SDL_AudioStreams to devices; however, there is still a callback method available if needed.
The SDL 1.2 audio compatibility API has also been removed, as it was a simplified version of the audio callback interface.
SDL3 will not implicitly initialize the audio subsystem on your behalf if you open a device without doing so. Please explicitly call SDL_Init(SDL_INIT_AUDIO) at some point.
If your app depends on the callback method, there is a similar approach you can take. But first, this is the new approach:
SDL3's audio subsystem offers an enormous amount of power over SDL2, but if you just want a simple migration of your existing code, you can ignore most of it. The simplest migration path from SDL2 looks something like this:
In SDL2, you might have done something like this to play audio:
In SDL2, you might have done something like this to play audio...
```c
void SDLCALL MyAudioCallback(void *userdata, Uint8 * stream, int len)
@ -82,20 +82,7 @@ In SDL2, you might have done something like this to play audio:
SDL_PauseAudioDevice(my_audio_device, 0);
```
in SDL3:
```c
/* ...somewhere near startup... */
SDL_AudioSpec spec = { SDL_AUDIO_S16, 2, 44100 };
SDL_AudioDeviceID my_audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec);
SDL_AudioSteam *stream = SDL_CreateAndBindAudioStream(my_audio_device, &spec);
/* ...in your main loop... */
/* calculate a little more audio into `buf`, add it to `stream` */
SDL_PutAudioStreamData(stream, buf, buflen);
```
If you absolutely require the callback method, SDL_AudioStreams can use a callback whenever more data is to be read from them, which can be used to simulate SDL2 semantics:
...in SDL3, you can do this...
```c
void SDLCALL MyAudioCallback(SDL_AudioStream *stream, int len, void *userdata)
@ -105,19 +92,32 @@ If you absolutely require the callback method, SDL_AudioStreams can use a callba
}
/* ...somewhere near startup... */
SDL_AudioSpec spec = { SDL_AUDIO_S16, 2, 44100 };
SDL_AudioDeviceID my_audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec);
SDL_AudioSteam *stream = SDL_CreateAndBindAudioStream(my_audio_device, &spec);
SDL_SetAudioStreamGetCallback(stream, MyAudioCallback);
/* MyAudioCallback will be called whenever the device requests more audio data. */
const SDL_AudioSpec spec = { SDL_AUDIO_S16, 2, 44100 };
SDL_AudioStream *stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec, MyAudioCallback, &my_audio_callback_user_data);
SDL_ResumeAudioDevice(SDL_GetAudioStreamBinding(stream));
```
If you used SDL_QueueAudio instead of a callback in SDL2, this is also straightforward.
```c
/* ...somewhere near startup... */
const SDL_AudioSpec spec = { SDL_AUDIO_S16, 2, 44100 };
SDL_AudioStream *stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec, NULL, NULL);
SDL_ResumeAudioDevice(SDL_GetAudioStreamBinding(stream));
/* ...in your main loop... */
/* calculate a little more audio into `buf`, add it to `stream` */
SDL_PutAudioStreamData(stream, buf, buflen);
```
...these same migration examples apply to audio capture, just using SDL_GetAudioStreamData instead of SDL_PutAudioStreamData.
SDL_AudioInit() and SDL_AudioQuit() have been removed. Instead you can call SDL_InitSubSystem() and SDL_QuitSubSystem() with SDL_INIT_AUDIO, which will properly refcount the subsystems. You can choose a specific audio driver using SDL_AUDIO_DRIVER hint.
The `SDL_AUDIO_ALLOW_*` symbols have been removed; now one may request the format they desire from the audio device, but ultimately SDL_AudioStream will manage the difference. One can use SDL_GetAudioDeviceFormat() to see what the final format is, if any "allowed" changes should be accomodated by the app.
SDL_AudioDeviceID now represents both an open audio device's handle (a "logical" device) and the instance ID that the hardware owns as long as it exists on the system (a "physical" device). The separation between device instances and device indexes is gone.
SDL_AudioDeviceID now represents both an open audio device's handle (a "logical" device) and the instance ID that the hardware owns as long as it exists on the system (a "physical" device). The separation between device instances and device indexes is gone, and logical and physical devices are almost entirely interchangeable at the API level.
Devices are opened by physical device instance ID, and a new logical instance ID is generated by the open operation; This allows any device to be opened multiple times, possibly by unrelated pieces of code. SDL will manage the logical devices to provide a single stream of audio to the physical device behind the scenes.

View File

@ -1031,33 +1031,55 @@ extern DECLSPEC void SDLCALL SDL_DestroyAudioStream(SDL_AudioStream *stream);
/**
* Convenience function to create and bind an audio stream in one step.
* Convenience function for straightforward audio init for the common case.
*
* This manages the creation of an audio stream, and setting its format
* correctly to match both the app and the audio device's needs. This is
* optional, but slightly less cumbersome to set up for a common use case.
* If all your app intends to do is provide a single source of PCM audio,
* this function allows you to do all your audio setup in a single call.
*
* This is intended to be a clean means to migrate apps from SDL2.
*
* This function will open an audio device, create a stream and bind it.
* Unlike other methods of setup, the audio device will be closed when this
* stream is destroyed, so the app can treat the returned SDL_AudioStream
* as the only object needed to manage audio playback.
*
* Also unlike other functions, the audio device begins paused. This is
* to map more closely to SDL2-style behavior, and since there is no extra
* step here to bind a stream to begin audio flowing.
*
* This function works with both playback and capture devices.
*
* The `spec` parameter represents the app's side of the audio stream. That
* is, for recording audio, this will be the output format, and for playing
* audio, this will be the input format. This function will set the other side
* of the audio stream to the device's format.
* audio, this will be the input format.
*
* \param devid an audio device to bind a stream to. This must be an opened
* device, and can not be zero.
* \param spec the audio stream's input format
* \returns a bound audio stream on success, ready to use. NULL on error; call
* SDL_GetError() for more information.
* If you don't care about opening a specific audio device, you can (and
* probably _should_), use SDL_AUDIO_DEVICE_DEFAULT_OUTPUT for playback and
* SDL_AUDIO_DEVICE_DEFAULT_CAPTURE for recording.
*
* One can optionally provide a callback function; if NULL, the app is
* expected to queue audio data for playback (or unqueue audio data if
* capturing). Otherwise, the callback will begin to fire once the device is
* unpaused.
*
* \param devid an audio device to open, or SDL_AUDIO_DEVICE_DEFAULT_OUTPUT
* or SDL_AUDIO_DEVICE_DEFAULT_CAPTURE.
* \param spec the audio stream's input format. Required.
* \param callback A callback where the app will provide new data for playback,
* or receive new data for capture. Can be NULL, in which case
* the app will need to call SDL_PutAudioStreamData or
* SDL_GetAudioStreamData as necessary.
* \param userdata App-controlled pointer passed to callback. Can be NULL.
* Ignored if callback is NULL.
* \returns an audio stream on success, ready to use. NULL on error; call
* SDL_GetError() for more information. When done with this stream,
* call SDL_DestroyAudioStream to free resources and close the device.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_BindAudioStreams
* \sa SDL_UnbindAudioStreams
* \sa SDL_UnbindAudioStream
*/
extern DECLSPEC SDL_AudioStream *SDLCALL SDL_CreateAndBindAudioStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec);
extern DECLSPEC SDL_AudioStream *SDLCALL SDL_OpenAudioDeviceStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec, SDL_AudioStreamRequestCallback callback, void *userdata);
/**
* Load the audio data of a WAVE file into memory.

View File

@ -1357,6 +1357,9 @@ int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int
return SDL_SetError("Audio streams are bound to device ids from SDL_OpenAudioDevice, not raw physical devices");
} else if ((logdev = ObtainLogicalAudioDevice(devid)) == NULL) {
return -1; // ObtainLogicalAudioDevice set the error message.
} else if (logdev->is_simplified) {
SDL_UnlockMutex(logdev->physical_device->lock);
return SDL_SetError("Cannot change stream bindings on device opened with SDL_OpenAudioDeviceStream");
}
// make sure start of list is sane.
@ -1460,7 +1463,8 @@ void SDL_UnbindAudioStreams(SDL_AudioStream **streams, int num_streams)
// everything is locked, start unbinding streams.
for (int i = 0; i < num_streams; i++) {
SDL_AudioStream *stream = streams[i];
if (stream && stream->bound_device) {
// don't allow unbinding from "simplified" devices (opened with SDL_OpenAudioDeviceStream). Just ignore them.
if (stream && stream->bound_device && !stream->bound_device->is_simplified) {
if (stream->bound_device->bound_streams == stream) {
SDL_assert(stream->prev_binding == NULL);
stream->bound_device->bound_streams = stream->next_binding;
@ -1507,33 +1511,55 @@ SDL_AudioDeviceID SDL_GetAudioStreamBinding(SDL_AudioStream *stream)
return retval;
}
SDL_AudioStream *SDL_CreateAndBindAudioStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec)
SDL_AudioStream *SDL_OpenAudioDeviceStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec, SDL_AudioStreamRequestCallback callback, void *userdata)
{
const SDL_bool islogical = (devid & (1<<1)) ? SDL_FALSE : SDL_TRUE;
if (!islogical) {
SDL_SetError("Audio streams are bound to device ids from SDL_OpenAudioDevice, not raw physical devices");
return NULL;
SDL_AudioDeviceID logdevid = SDL_OpenAudioDevice(devid, spec);
if (!logdevid) {
return NULL; // error string should already be set.
}
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(logdevid);
if (logdev == NULL) { // this shouldn't happen, but just in case.
SDL_CloseAudioDevice(logdevid);
return NULL; // error string should already be set.
}
SDL_AudioDevice *physdevice = logdev->physical_device;
SDL_assert(physdevice != NULL);
SDL_UnlockMutex(physdevice->lock); // we don't need to hold the lock for any of this.
const SDL_bool iscapture = physdevice->iscapture;
SDL_AtomicSet(&logdev->paused, 1); // start the device paused, to match SDL2.
SDL_AudioStream *stream = NULL;
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid);
if (logdev) {
SDL_AudioDevice *device = logdev->physical_device;
if (device->iscapture) {
stream = SDL_CreateAudioStream(&device->spec, spec);
} else {
stream = SDL_CreateAudioStream(spec, &device->spec);
}
if (stream) {
if (SDL_BindAudioStream(devid, stream) == -1) {
SDL_DestroyAudioStream(stream);
stream = NULL;
}
}
SDL_UnlockMutex(device->lock);
if (iscapture) {
stream = SDL_CreateAudioStream(&physdevice->spec, spec);
} else {
stream = SDL_CreateAudioStream(spec, &physdevice->spec);
}
return stream;
if (!stream) {
SDL_CloseAudioDevice(logdevid);
} else if (SDL_BindAudioStream(logdevid, stream) == -1) {
SDL_DestroyAudioStream(stream);
SDL_CloseAudioDevice(logdevid);
stream = NULL;
}
logdev->is_simplified = SDL_TRUE; // forbid further binding changes on this logical device.
stream->is_simplified = SDL_TRUE; // so we know to close the audio device when this is destroyed.
if (callback) {
int rc;
if (iscapture) {
rc = SDL_SetAudioStreamGetCallback(stream, callback, userdata);
} else {
rc = SDL_SetAudioStreamPutCallback(stream, callback, userdata);
}
SDL_assert(rc == 0); // should only fail if stream==NULL atm.
}
return stream; // ready to rock.
}
#define NUM_FORMATS 8

View File

@ -1281,7 +1281,14 @@ int SDL_ClearAudioStream(SDL_AudioStream *stream)
void SDL_DestroyAudioStream(SDL_AudioStream *stream)
{
if (stream) {
SDL_UnbindAudioStream(stream);
const SDL_bool is_simplified = stream->is_simplified;
if (is_simplified) {
SDL_assert(stream->bound_device->is_simplified);
SDL_CloseAudioDevice(stream->bound_device->instance_id); // this will unbind the stream.
} else {
SDL_UnbindAudioStream(stream);
}
// do not destroy stream->lock! it's a copy of `stream->queue`'s mutex, so destroying the queue will handle it.
SDL_DestroyDataQueue(stream->queue);
SDL_aligned_free(stream->work_buffer);

View File

@ -187,6 +187,8 @@ struct SDL_AudioStream
int packetlen;
SDL_bool is_simplified; // SDL_TRUE if created via SDL_OpenAudioDeviceStream
SDL_LogicalAudioDevice *bound_device;
SDL_AudioStream *next_binding;
SDL_AudioStream *prev_binding;
@ -213,6 +215,9 @@ struct SDL_LogicalAudioDevice
// SDL_TRUE if this was opened as a default device.
SDL_bool is_default;
// SDL_TRUE if device was opened with SDL_OpenAudioDeviceStream (so it forbids binding changes, etc).
SDL_bool is_simplified;
// double-linked list of opened devices on the same physical device.
SDL_LogicalAudioDevice *next;
SDL_LogicalAudioDevice *prev;

View File

@ -876,7 +876,7 @@ SDL3_0.0.0 {
SDL_SetAudioStreamGetCallback;
SDL_SetAudioStreamPutCallback;
SDL_DestroyAudioStream;
SDL_CreateAndBindAudioStream;
SDL_OpenAudioDeviceStream;
SDL_LoadWAV_RW;
SDL_LoadWAV;
SDL_MixAudioFormat;

View File

@ -901,7 +901,7 @@
#define SDL_SetAudioStreamGetCallback SDL_SetAudioStreamGetCallback_REAL
#define SDL_SetAudioStreamPutCallback SDL_SetAudioStreamPutCallback_REAL
#define SDL_DestroyAudioStream SDL_DestroyAudioStream_REAL
#define SDL_CreateAndBindAudioStream SDL_CreateAndBindAudioStream_REAL
#define SDL_OpenAudioDeviceStream SDL_OpenAudioDeviceStream_REAL
#define SDL_LoadWAV_RW SDL_LoadWAV_RW_REAL
#define SDL_LoadWAV SDL_LoadWAV_REAL
#define SDL_MixAudioFormat SDL_MixAudioFormat_REAL

View File

@ -946,7 +946,7 @@ SDL_DYNAPI_PROC(int,SDL_UnlockAudioStream,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamGetCallback,(SDL_AudioStream *a, SDL_AudioStreamRequestCallback b, void *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamPutCallback,(SDL_AudioStream *a, SDL_AudioStreamRequestCallback b, void *c),(a,b,c),return)
SDL_DYNAPI_PROC(void,SDL_DestroyAudioStream,(SDL_AudioStream *a),(a),)
SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_CreateAndBindAudioStream,(SDL_AudioDeviceID a, const SDL_AudioSpec *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_OpenAudioDeviceStream,(SDL_AudioDeviceID a, const SDL_AudioSpec *b, SDL_AudioStreamRequestCallback c, void *d),(a,b,c,d),return)
SDL_DYNAPI_PROC(int,SDL_LoadWAV_RW,(SDL_RWops *a, SDL_bool b, SDL_AudioSpec *c, Uint8 **d, Uint32 *e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_MixAudioFormat,(Uint8 *a, const Uint8 *b, SDL_AudioFormat c, Uint32 d, int e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_ConvertAudioSamples,(const SDL_AudioSpec *a, const Uint8 *b, int c, const SDL_AudioSpec *d, Uint8 **e, int *f),(a,b,c,d,e,f),return)

View File

@ -34,7 +34,6 @@ static struct
Uint32 soundpos;
} wave;
static SDL_AudioDeviceID device;
static SDL_AudioStream *stream;
static void fillerup(void)
@ -58,30 +57,22 @@ quit(int rc)
static void
close_audio(void)
{
if (device != 0) {
if (stream) {
SDL_DestroyAudioStream(stream);
stream = NULL;
SDL_CloseAudioDevice(device);
device = 0;
}
}
static void
open_audio(void)
{
device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &wave.spec);
if (!device) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", SDL_GetError());
SDL_free(wave.sound);
quit(2);
}
stream = SDL_CreateAndBindAudioStream(device, &wave.spec);
stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &wave.spec, NULL, NULL);
if (!stream) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create audio stream: %s\n", SDL_GetError());
SDL_CloseAudioDevice(device);
SDL_free(wave.sound);
quit(2);
}
SDL_ResumeAudioDevice(SDL_GetAudioStreamBinding(stream));
}

View File

@ -180,11 +180,15 @@ int main(int argc, char **argv)
}
SDL_PauseAudioDevice(device);
SDL_GetAudioDeviceFormat(device, &outspec);
stream_out = SDL_CreateAndBindAudioStream(device, &outspec);
stream_out = SDL_CreateAudioStream(&outspec, &outspec);
if (!stream_out) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create an audio stream for playback: %s!\n", SDL_GetError());
SDL_Quit();
exit(1);
} else if (SDL_BindAudioStream(device, stream_out) == -1) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't bind an audio stream for playback: %s!\n", SDL_GetError());
SDL_Quit();
exit(1);
}
SDL_Log("Opening capture device %s%s%s...\n",
@ -200,11 +204,15 @@ int main(int argc, char **argv)
}
SDL_PauseAudioDevice(device);
SDL_GetAudioDeviceFormat(device, &inspec);
stream_in = SDL_CreateAndBindAudioStream(device, &inspec);
stream_in = SDL_CreateAudioStream(&inspec, &inspec);
if (!stream_in) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create an audio stream for capture: %s!\n", SDL_GetError());
SDL_Quit();
exit(1);
} else if (SDL_BindAudioStream(device, stream_in) == -1) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't bind an audio stream for capture: %s!\n", SDL_GetError());
SDL_Quit();
exit(1);
}
SDL_SetAudioStreamFormat(stream_in, NULL, &outspec); /* make sure we output at the playback format. */

View File

@ -69,7 +69,7 @@ static void iteration(void)
done = 1;
}
} else if (e.type == SDL_EVENT_AUDIO_DEVICE_ADDED) {
const SDL_AudioDeviceID which = (SDL_AudioDeviceID ) e.adevice.which;
const SDL_AudioDeviceID which = (SDL_AudioDeviceID) e.adevice.which;
const SDL_bool iscapture = e.adevice.iscapture ? SDL_TRUE : SDL_FALSE;
char *name = SDL_GetAudioDeviceName(which);
if (name != NULL) {
@ -80,20 +80,15 @@ static void iteration(void)
continue;
}
if (!iscapture) {
dev = SDL_OpenAudioDevice(which, &spec);
if (!dev) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open '%s': %s\n", name, SDL_GetError());
SDL_AudioStream *stream = SDL_OpenAudioDeviceStream(which, &spec, NULL, NULL);
if (!stream) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create/bind an audio stream to %u ('%s'): %s", (unsigned int) which, name, SDL_GetError());
} else {
SDL_AudioStream *stream;
SDL_Log("Opened '%s' as %u\n", name, (unsigned int)dev);
stream = SDL_CreateAndBindAudioStream(dev, &spec);
if (!stream) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create/bind an audio stream to %u ('%s'): %s", (unsigned int) dev, name, SDL_GetError());
SDL_CloseAudioDevice(dev);
}
SDL_Log("Opened '%s' as %u\n", name, (unsigned int) which);
/* !!! FIXME: laziness, this used to loop the audio, but we'll just play it once for now on each connect. */
SDL_PutAudioStreamData(stream, sound, soundlen);
SDL_FlushAudioStream(stream);
SDL_ResumeAudioDevice(SDL_GetAudioStreamBinding(stream));
/* !!! FIXME: this is leaking the stream for now. We'll wire it up to a dictionary or whatever later. */
}
}
@ -101,7 +96,7 @@ static void iteration(void)
} else if (e.type == SDL_EVENT_AUDIO_DEVICE_REMOVED) {
dev = (SDL_AudioDeviceID)e.adevice.which;
SDL_Log("%s device %u removed.\n", devtypestr(e.adevice.iscapture), (unsigned int)dev);
SDL_CloseAudioDevice(dev);
/* !!! FIXME: we need to keep track of our streams and destroy them here. */
}
}
}

View File

@ -28,7 +28,6 @@ static Uint8 *sound = NULL; /* Pointer to wave data */
static Uint32 soundlen = 0; /* Length of wave data */
/* these have to be in globals so the Emscripten port can see them in the mainloop. :/ */
static SDL_AudioDeviceID device = 0;
static SDL_AudioStream *stream = NULL;
@ -37,7 +36,6 @@ static void loop(void)
{
if (SDL_GetAudioStreamAvailable(stream) == 0) {
SDL_Log("done.");
SDL_CloseAudioDevice(device);
SDL_DestroyAudioStream(stream);
SDL_free(sound);
SDL_Quit();
@ -65,13 +63,10 @@ test_multi_audio(SDL_AudioDeviceID *devices, int devcount)
SDL_Log("Playing on device #%d of %d: id=%u, name='%s'...", i, devcount, (unsigned int) devices[i], devname);
device = SDL_OpenAudioDevice(devices[i], &spec);
if (device == 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Open device failed: %s", SDL_GetError());
} else if ((stream = SDL_CreateAndBindAudioStream(device, &spec)) == NULL) { /* we can reuse these, but we'll just make one each time for now. */
if ((stream = SDL_OpenAudioDeviceStream(devices[i], &spec, NULL, NULL)) == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Audio stream creation failed: %s", SDL_GetError());
SDL_CloseAudioDevice(device);
} else {
SDL_ResumeAudioDevice(SDL_GetAudioStreamBinding(stream));
SDL_PutAudioStreamData(stream, sound, soundlen);
SDL_FlushAudioStream(stream);
#ifdef __EMSCRIPTEN__
@ -87,7 +82,6 @@ test_multi_audio(SDL_AudioDeviceID *devices, int devcount)
}
#endif
SDL_Log("done.");
SDL_CloseAudioDevice(device);
SDL_DestroyAudioStream(stream);
}
SDL_free(devname);
@ -101,29 +95,19 @@ test_multi_audio(SDL_AudioDeviceID *devices, int devcount)
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!");
} else {
for (i = 0; i < devcount; i++) {
char *devname = SDL_GetAudioDeviceName(devices[i]);
device = SDL_OpenAudioDevice(devices[i], &spec);
if (device == 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Open device %d of %d (id=%u, name='%s') failed: %s\n", i, devcount, (unsigned int) devices[i], devname, SDL_GetError());
}
SDL_free(devname);
devices[i] = device; /* just replace the physical device ID with the newly-opened logical device ID. */
if (device) {
SDL_PauseAudioDevice(device); /* hold while we set up all the streams. */
streams[i] = SDL_CreateAndBindAudioStream(device, &spec);
if (streams[i] == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Audio stream creation failed for device %d of %d: %s", i, devcount, SDL_GetError());
} else {
SDL_PutAudioStreamData(streams[i], sound, soundlen);
SDL_FlushAudioStream(streams[i]);
}
streams[i] = SDL_OpenAudioDeviceStream(devices[i], &spec, NULL, NULL);
if (streams[i] == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Audio stream creation failed for device %d of %d: %s", i, devcount, SDL_GetError());
} else {
SDL_PutAudioStreamData(streams[i], sound, soundlen);
SDL_FlushAudioStream(streams[i]);
}
}
/* try to start all the devices about the same time. SDL does not guarantee sync across physical devices. */
for (i = 0; i < devcount; i++) {
if (devices[i]) {
SDL_ResumeAudioDevice(devices[i]);
if (streams[i]) {
SDL_ResumeAudioDevice(SDL_GetAudioStreamBinding(streams[i]));
}
}
@ -143,7 +127,6 @@ test_multi_audio(SDL_AudioDeviceID *devices, int devcount)
}
for (i = 0; i < devcount; i++) {
SDL_CloseAudioDevice(devices[i]);
SDL_DestroyAudioStream(streams[i]);
}

View File

@ -193,7 +193,6 @@ int main(int argc, char *argv[])
char *devname = SDL_GetAudioDeviceName(devices[i]);
int j;
SDL_AudioSpec spec;
SDL_AudioDeviceID dev;
SDL_Log("Testing audio device: %s\n", devname);
SDL_free(devname);
@ -208,24 +207,16 @@ int main(int argc, char *argv[])
spec.freq = SAMPLE_RATE_HZ;
spec.format = SDL_AUDIO_S16SYS;
dev = SDL_OpenAudioDevice(devices[i], &spec);
if (dev == 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_OpenAudioDevice() failed: %s\n", SDL_GetError());
continue;
}
stream = SDL_CreateAndBindAudioStream(dev, &spec);
if (stream == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_CreateAndBindAudioStream() failed: %s\n", SDL_GetError());
SDL_CloseAudioDevice(dev);
continue;
}
/* These are used by the fill_buffer callback */
total_channels = spec.channels;
active_channel = 0;
SDL_SetAudioStreamGetCallback(stream, fill_buffer, NULL);
stream = SDL_OpenAudioDeviceStream(devices[i], &spec, fill_buffer, NULL);
if (stream == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_OpenAudioDeviceStream() failed: %s\n", SDL_GetError());
continue;
}
SDL_ResumeAudioDevice(SDL_GetAudioStreamBinding(stream));
for (j = 0; j < total_channels; j++) {
const int sine_freq = is_lfe_channel(j, total_channels) ? LFE_SINE_FREQ_HZ : SINE_FREQ_HZ;
@ -240,7 +231,6 @@ int main(int argc, char *argv[])
}
}
SDL_CloseAudioDevice(dev);
SDL_DestroyAudioStream(stream);
}