audio: Separate channel maps out of SDL_AudioSpec.

This commit is contained in:
Ryan C. Gordon 2024-07-10 00:10:37 -04:00
parent 1664ac4fcb
commit 4755055bc3
12 changed files with 455 additions and 114 deletions

View file

@ -43,7 +43,12 @@
* if you aren't reading from a file) as a basic means to load sound data into * if you aren't reading from a file) as a basic means to load sound data into
* your program. * your program.
* *
* ## Channel layouts as SDL expects them * ## Channel layouts
*
* Audio data passing through SDL is uncompressed PCM data, interleaved.
* One can provide their own decompression through an MP3, etc, decoder, but
* SDL does not provide this directly. Each interleaved channel of data is
* meant to be in a specific order.
* *
* Abbreviations: * Abbreviations:
* *
@ -76,7 +81,7 @@
* platforms; SDL will swizzle the channels as necessary if a platform expects * platforms; SDL will swizzle the channels as necessary if a platform expects
* something different. * something different.
* *
* SDL_AudioStream can also be provided a channel map to change this ordering * SDL_AudioStream can also be provided channel maps to change this ordering
* to whatever is necessary, in other audio processing scenarios. * to whatever is necessary, in other audio processing scenarios.
*/ */
@ -301,18 +306,6 @@ typedef Uint32 SDL_AudioDeviceID;
*/ */
#define SDL_AUDIO_DEVICE_DEFAULT_RECORDING ((SDL_AudioDeviceID) 0xFFFFFFFE) #define SDL_AUDIO_DEVICE_DEFAULT_RECORDING ((SDL_AudioDeviceID) 0xFFFFFFFE)
/**
* Maximum channels that an SDL_AudioSpec channel map can handle.
*
* This is (currently) double the number of channels that SDL supports, to
* allow for future expansion while maintaining binary compatibility.
*
* \since This macro is available since SDL 3.0.0.
*
* \sa SDL_AudioSpec
*/
#define SDL_MAX_CHANNEL_MAP_SIZE 16
/** /**
* Format specifier for audio data. * Format specifier for audio data.
* *
@ -325,8 +318,6 @@ typedef struct SDL_AudioSpec
SDL_AudioFormat format; /**< Audio data format */ SDL_AudioFormat format; /**< Audio data format */
int channels; /**< Number of channels: 1 mono, 2 stereo, etc */ int channels; /**< Number of channels: 1 mono, 2 stereo, etc */
int freq; /**< sample rate: sample frames per second */ int freq; /**< sample rate: sample frames per second */
SDL_bool use_channel_map; /**< If SDL_FALSE, ignore `channel_map` and use default order. */
Uint8 channel_map[SDL_MAX_CHANNEL_MAP_SIZE]; /**< `channels` items of channel order. */
} SDL_AudioSpec; } SDL_AudioSpec;
/** /**
@ -560,6 +551,29 @@ extern SDL_DECLSPEC const char *SDLCALL SDL_GetAudioDeviceName(SDL_AudioDeviceID
*/ */
extern SDL_DECLSPEC int SDLCALL SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec, int *sample_frames); extern SDL_DECLSPEC int SDLCALL SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec, int *sample_frames);
/**
* Get the current channel map of an audio device.
*
* Channel maps are optional; most things do not need them, instead passing
* data in the [order that SDL expects](CategoryAudio#channel-layouts).
*
* Audio devices usually have no remapping applied. This is represented by
* returning NULL, and does not signify an error.
*
* The returned array follows the SDL_GetStringRule (even though, strictly
* speaking, it isn't a string, it has the same memory manangement rules).
*
* \param devid the instance ID of the device to query.
* \param count On output, set to number of channels in the map. Can be NULL.
* \returns an array of the current channel mapping, with as many elements as the current output spec's channels, or NULL if default.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetAudioStreamInputChannelMap
*/
extern SDL_DECLSPEC const int * SDLCALL SDL_GetAudioDeviceChannelMap(SDL_AudioDeviceID devid, int *count);
/** /**
* Open a specific audio device. * Open a specific audio device.
@ -1081,6 +1095,151 @@ extern SDL_DECLSPEC float SDLCALL SDL_GetAudioStreamGain(SDL_AudioStream *stream
*/ */
extern SDL_DECLSPEC int SDLCALL SDL_SetAudioStreamGain(SDL_AudioStream *stream, float gain); extern SDL_DECLSPEC int SDLCALL SDL_SetAudioStreamGain(SDL_AudioStream *stream, float gain);
/**
* Get the current input channel map of an audio stream.
*
* Channel maps are optional; most things do not need them, instead passing
* data in the [order that SDL expects](CategoryAudio#channel-layouts).
*
* Audio streams default to no remapping applied. This is represented by
* returning NULL, and does not signify an error.
*
* The returned array follows the SDL_GetStringRule (even though, strictly
* speaking, it isn't a string, it has the same memory manangement rules).
*
* \param stream the SDL_AudioStream to query.
* \param count On output, set to number of channels in the map. Can be NULL.
* \returns an array of the current channel mapping, with as many elements as the current output spec's channels, or NULL if default.
*
* \threadsafety It is safe to call this function from any thread, as it holds
* a stream-specific mutex while running.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetAudioStreamInputChannelMap
*/
extern SDL_DECLSPEC const int * SDLCALL SDL_GetAudioStreamInputChannelMap(SDL_AudioStream *stream, int *count);
/**
* Get the current output channel map of an audio stream.
*
* Channel maps are optional; most things do not need them, instead passing
* data in the [order that SDL expects](CategoryAudio#channel-layouts).
*
* Audio streams default to no remapping applied. This is represented by
* returning NULL, and does not signify an error.
*
* The returned array follows the SDL_GetStringRule (even though, strictly
* speaking, it isn't a string, it has the same memory manangement rules).
*
* \param stream the SDL_AudioStream to query.
* \param count On output, set to number of channels in the map. Can be NULL.
* \returns an array of the current channel mapping, with as many elements as the current output spec's channels, or NULL if default.
*
* \threadsafety It is safe to call this function from any thread, as it holds
* a stream-specific mutex while running.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetAudioStreamInputChannelMap
*/
extern SDL_DECLSPEC const int * SDLCALL SDL_GetAudioStreamOutputChannelMap(SDL_AudioStream *stream, int *count);
/**
* Set the current input channel map of an audio stream.
*
* Channel maps are optional; most things do not need them, instead passing
* data in the [order that SDL expects](CategoryAudio#channel-layouts).
*
* The input channel map reorders data that is added to a stream via
* SDL_PutAudioStreamData. Future calls to SDL_PutAudioStreamData
* must provide data in the new channel order.
*
* Each item in the array represents an input channel, and its value is the
* channel that it should be remapped to. To reverse a stereo signal's left
* and right values, you'd have an array of `{ 1, 0 }`. It is legal to remap
* multiple channels to the same thing, so `{ 1, 1 }` would duplicate the
* right channel to both channels of a stereo signal. You cannot change the
* number of channels through a channel map, just reorder them.
*
* Data that was previously queued in the stream will still be operated on in
* the order that was current when it was added, which is to say you can put
* the end of a sound file in one order to a stream, change orders for the
* next sound file, and start putting that new data while the previous sound
* file is still queued, and everything will still play back correctly.
*
* Audio streams default to no remapping applied. Passing a NULL channel map
* is legal, and turns off remapping.
*
* SDL will copy the channel map; the caller does not have to save this array
* after this call.
*
* If `count` is not equal to the current number of channels in the audio
* stream's format, this will fail. This is a safety measure to make sure a
* a race condition hasn't changed the format while you this call is setting
* the channel map.
*
* \param stream the SDL_AudioStream to change.
* \param chmap the new channel map, NULL to reset to default.
* \param count The number of channels in the map.
* \returns 0 on success, -1 on error.
*
* \threadsafety It is safe to call this function from any thread, as it holds
* a stream-specific mutex while running. Don't change the
* stream's format to have a different number of channels from a
* a different thread at the same time, though!
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetAudioStreamInputChannelMap
*/
extern SDL_DECLSPEC int SDLCALL SDL_SetAudioStreamInputChannelMap(SDL_AudioStream *stream, const int *chmap, int count);
/**
* Set the current output channel map of an audio stream.
*
* Channel maps are optional; most things do not need them, instead passing
* data in the [order that SDL expects](CategoryAudio#channel-layouts).
*
* The output channel map reorders data that leaving a stream via
* SDL_GetAudioStreamData.
*
* Each item in the array represents an output channel, and its value is the
* channel that it should be remapped to. To reverse a stereo signal's left
* and right values, you'd have an array of `{ 1, 0 }`. It is legal to remap
* multiple channels to the same thing, so `{ 1, 1 }` would duplicate the
* right channel to both channels of a stereo signal. You cannot change the
* number of channels through a channel map, just reorder them.
*
* The output channel map can be changed at any time, as output remapping is
* applied during SDL_GetAudioStreamData.
*
* Audio streams default to no remapping applied. Passing a NULL channel map
* is legal, and turns off remapping.
*
* SDL will copy the channel map; the caller does not have to save this array
* after this call.
*
* If `count` is not equal to the current number of channels in the audio
* stream's format, this will fail. This is a safety measure to make sure a
* a race condition hasn't changed the format while you this call is setting
* the channel map.
*
* \param stream the SDL_AudioStream to change.
* \param chmap the new channel map, NULL to reset to default.
* \param count The number of channels in the map.
* \returns 0 on success, -1 on error.
*
* \threadsafety It is safe to call this function from any thread, as it holds
* a stream-specific mutex while running. Don't change the
* stream's format to have a different number of channels from a
* a different thread at the same time, though!
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetAudioStreamInputChannelMap
*/
extern SDL_DECLSPEC int SDLCALL SDL_SetAudioStreamOutputChannelMap(SDL_AudioStream *stream, const int *chmap, int count);
/** /**
* Add data to the stream. * Add data to the stream.
@ -1505,7 +1664,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_DestroyAudioStream(SDL_AudioStream *stream)
* Also unlike other functions, the audio device begins paused. This is to map * Also unlike other functions, the audio device begins paused. This is to map
* more closely to SDL2-style behavior, since there is no extra step here to * more closely to SDL2-style behavior, since there is no extra step here to
* bind a stream to begin audio flowing. The audio device should be resumed * bind a stream to begin audio flowing. The audio device should be resumed
* with `SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(stream));` * with `SDL_ResumeAudioStreamDevice(stream);`
* *
* This function works with both playback and recording devices. * This function works with both playback and recording devices.
* *
@ -1547,7 +1706,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_DestroyAudioStream(SDL_AudioStream *stream)
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
* *
* \sa SDL_GetAudioStreamDevice * \sa SDL_GetAudioStreamDevice
* \sa SDL_ResumeAudioDevice * \sa SDL_ResumeAudioStreamDevice
*/ */
extern SDL_DECLSPEC SDL_AudioStream *SDLCALL SDL_OpenAudioDeviceStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec, SDL_AudioStreamCallback callback, void *userdata); extern SDL_DECLSPEC SDL_AudioStream *SDLCALL SDL_OpenAudioDeviceStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec, SDL_AudioStreamCallback callback, void *userdata);

View file

@ -167,6 +167,16 @@ static int GetDefaultSampleFramesFromFreq(const int freq)
} }
} }
int *SDL_ChannelMapDup(const int *origchmap, int channels)
{
const size_t chmaplen = sizeof (*origchmap) * channels;
int *chmap = (int *) SDL_malloc(chmaplen);
if (chmap) {
SDL_memcpy(chmap, origchmap, chmaplen);
}
return chmap;
}
void OnAudioStreamCreated(SDL_AudioStream *stream) void OnAudioStreamCreated(SDL_AudioStream *stream)
{ {
SDL_assert(stream != NULL); SDL_assert(stream != NULL);
@ -243,17 +253,18 @@ static void UpdateAudioStreamFormatsPhysical(SDL_AudioDevice *device)
// SDL_SetAudioStreamFormat does a ton of validation just to memcpy an audiospec. // SDL_SetAudioStreamFormat does a ton of validation just to memcpy an audiospec.
SDL_LockMutex(stream->lock); SDL_LockMutex(stream->lock);
SDL_copyp(&stream->dst_spec, &spec); SDL_copyp(&stream->dst_spec, &spec);
SDL_SetAudioStreamOutputChannelMap(stream, device->chmap, spec.channels); // this should be fast for normal cases, though!
SDL_UnlockMutex(stream->lock); SDL_UnlockMutex(stream->lock);
} }
} }
} }
} }
SDL_bool SDL_AudioSpecsEqual(const SDL_AudioSpec *a, const SDL_AudioSpec *b) SDL_bool SDL_AudioSpecsEqual(const SDL_AudioSpec *a, const SDL_AudioSpec *b, const int *channel_map_a, const int *channel_map_b)
{ {
if ((a->format != b->format) || (a->channels != b->channels) || (a->freq != b->freq) || (a->use_channel_map != b->use_channel_map)) { if ((a->format != b->format) || (a->channels != b->channels) || (a->freq != b->freq) || ((channel_map_a != NULL) != (channel_map_b != NULL))) {
return SDL_FALSE; return SDL_FALSE;
} else if (a->use_channel_map && (SDL_memcmp(a->channel_map, b->channel_map, sizeof (a->channel_map[0]) * a->channels) != 0)) { } else if (channel_map_a && (SDL_memcmp(channel_map_a, channel_map_b, sizeof (*channel_map_a) * a->channels) != 0)) {
return SDL_FALSE; return SDL_FALSE;
} }
return SDL_TRUE; return SDL_TRUE;
@ -533,6 +544,7 @@ static void DestroyPhysicalAudioDevice(SDL_AudioDevice *device)
SDL_DestroyMutex(device->lock); SDL_DestroyMutex(device->lock);
SDL_DestroyCondition(device->close_cond); SDL_DestroyCondition(device->close_cond);
SDL_free(device->work_buffer); SDL_free(device->work_buffer);
SDL_FreeLater(device->chmap); // this pointer is handed to the app during SDL_GetAudioDeviceChannelMap
SDL_FreeLater(device->name); // this pointer is handed to the app during SDL_GetAudioDeviceName SDL_FreeLater(device->name); // this pointer is handed to the app during SDL_GetAudioDeviceName
SDL_free(device); SDL_free(device);
} }
@ -648,7 +660,6 @@ SDL_AudioDevice *SDL_AddAudioDevice(SDL_bool recording, const char *name, const
spec.channels = default_channels; spec.channels = default_channels;
spec.freq = default_freq; spec.freq = default_freq;
} else { } else {
SDL_assert(!inspec->use_channel_map); // backends shouldn't set a channel map here! Set it when opening the device!
spec.format = (inspec->format != 0) ? inspec->format : default_format; spec.format = (inspec->format != 0) ? inspec->format : default_format;
spec.channels = (inspec->channels != 0) ? inspec->channels : default_channels; spec.channels = (inspec->channels != 0) ? inspec->channels : default_channels;
spec.freq = (inspec->freq != 0) ? inspec->freq : default_freq; spec.freq = (inspec->freq != 0) ? inspec->freq : default_freq;
@ -1101,7 +1112,7 @@ SDL_bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device)
SDL_AudioStream *stream = logdev->bound_streams; SDL_AudioStream *stream = logdev->bound_streams;
// We should have updated this elsewhere if the format changed! // We should have updated this elsewhere if the format changed!
SDL_assert(SDL_AudioSpecsEqual(&stream->dst_spec, &device->spec)); SDL_assert(SDL_AudioSpecsEqual(&stream->dst_spec, &device->spec, stream->dst_chmap, device->chmap));
const int br = SDL_AtomicGet(&logdev->paused) ? 0 : SDL_GetAudioStreamDataAdjustGain(stream, device_buffer, buffer_size, logdev->gain); const int br = SDL_AtomicGet(&logdev->paused) ? 0 : SDL_GetAudioStreamDataAdjustGain(stream, device_buffer, buffer_size, logdev->gain);
if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow. if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow.
@ -1137,7 +1148,7 @@ SDL_bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device)
for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) { for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) {
// We should have updated this elsewhere if the format changed! // We should have updated this elsewhere if the format changed!
SDL_assert(SDL_AudioSpecsEqual(&stream->dst_spec, &outspec)); SDL_assert(SDL_AudioSpecsEqual(&stream->dst_spec, &outspec, stream->dst_chmap, device->chmap));
/* this will hold a lock on `stream` while getting. We don't explicitly lock the streams /* this will hold a lock on `stream` while getting. We don't explicitly lock the streams
for iterating here because the binding linked list can only change while the device lock is held. for iterating here because the binding linked list can only change while the device lock is held.
@ -1448,6 +1459,25 @@ int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec, int *
return retval; return retval;
} }
const int *SDL_GetAudioDeviceChannelMap(SDL_AudioDeviceID devid, int *count)
{
const int *retval = NULL;
int channels = 0;
SDL_AudioDevice *device = ObtainPhysicalAudioDeviceDefaultAllowed(devid);
if (device) {
retval = device->chmap;
channels = device->spec.channels;
}
ReleaseAudioDevice(device);
if (count) {
*count = channels;
}
return retval;
}
// this is awkward, but this makes sure we can release the device lock // this is awkward, but this makes sure we can release the device lock
// so the device thread can terminate but also not have two things // so the device thread can terminate but also not have two things
// race to close or open the device while the lock is unprotected. // race to close or open the device while the lock is unprotected.
@ -1618,7 +1648,6 @@ static int OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec
device->spec.format = (SDL_AUDIO_BITSIZE(device->default_spec.format) >= SDL_AUDIO_BITSIZE(spec.format)) ? device->default_spec.format : spec.format; device->spec.format = (SDL_AUDIO_BITSIZE(device->default_spec.format) >= SDL_AUDIO_BITSIZE(spec.format)) ? device->default_spec.format : spec.format;
device->spec.freq = SDL_max(device->default_spec.freq, spec.freq); device->spec.freq = SDL_max(device->default_spec.freq, spec.freq);
device->spec.channels = SDL_max(device->default_spec.channels, spec.channels); device->spec.channels = SDL_max(device->default_spec.channels, spec.channels);
device->spec.use_channel_map = SDL_FALSE; // all initial channel map requests are denied, since we might have to change channel counts.
device->sample_frames = GetDefaultSampleFramesFromFreq(device->spec.freq); device->sample_frames = GetDefaultSampleFramesFromFreq(device->spec.freq);
SDL_UpdatedAudioDeviceFormat(device); // start this off sane. SDL_UpdatedAudioDeviceFormat(device); // start this off sane.
@ -1906,6 +1935,7 @@ int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int
if (logdev->postmix) { if (logdev->postmix) {
stream->src_spec.format = SDL_AUDIO_F32; stream->src_spec.format = SDL_AUDIO_F32;
} }
SDL_SetAudioStreamInputChannelMap(stream, device->chmap, device->spec.channels); // this should be fast for normal cases, though!
} }
SDL_UnlockMutex(stream->lock); SDL_UnlockMutex(stream->lock);
@ -2208,7 +2238,8 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device)
} }
if (needs_migration) { if (needs_migration) {
const SDL_bool spec_changed = !SDL_AudioSpecsEqual(&current_default_device->spec, &new_default_device->spec); // we don't currently report channel map changes, so we'll leave them as NULL for now.
const SDL_bool spec_changed = !SDL_AudioSpecsEqual(&current_default_device->spec, &new_default_device->spec, NULL, NULL);
SDL_LogicalAudioDevice *next = NULL; SDL_LogicalAudioDevice *next = NULL;
for (SDL_LogicalAudioDevice *logdev = current_default_device->logical_devices; logdev; logdev = next) { for (SDL_LogicalAudioDevice *logdev = current_default_device->logical_devices; logdev; logdev = next) {
next = logdev->next; next = logdev->next;
@ -2288,7 +2319,8 @@ int SDL_AudioDeviceFormatChangedAlreadyLocked(SDL_AudioDevice *device, const SDL
{ {
const int orig_work_buffer_size = device->work_buffer_size; const int orig_work_buffer_size = device->work_buffer_size;
if (SDL_AudioSpecsEqual(&device->spec, newspec) && (new_sample_frames == device->sample_frames)) { // we don't currently have any place where channel maps change from under you, but we can check that if necessary later.
if (SDL_AudioSpecsEqual(&device->spec, newspec, NULL, NULL) && (new_sample_frames == device->sample_frames)) {
return 0; // we're already in that format. return 0; // we're already in that format.
} }

View file

@ -124,11 +124,12 @@ static SDL_bool SDL_IsSupportedChannelCount(const int channels)
return ((channels >= 1) && (channels <= 8)); return ((channels >= 1) && (channels <= 8));
} }
SDL_bool SDL_ChannelMapIsBogus(const Uint8 *map, int channels) SDL_bool SDL_ChannelMapIsBogus(const int *chmap, int channels)
{ {
if (map) { if (chmap) {
for (int i = 0; i < channels; i++) { for (int i = 0; i < channels; i++) {
if (map[i] >= ((Uint8) channels)) { const int mapping = chmap[i];
if ((mapping < 0) || (mapping >= channels)) {
return SDL_TRUE; return SDL_TRUE;
} }
} }
@ -136,11 +137,11 @@ SDL_bool SDL_ChannelMapIsBogus(const Uint8 *map, int channels)
return SDL_FALSE; return SDL_FALSE;
} }
SDL_bool SDL_ChannelMapIsDefault(const Uint8 *map, int channels) SDL_bool SDL_ChannelMapIsDefault(const int *chmap, int channels)
{ {
if (map) { if (chmap) {
for (int i = 0; i < channels; i++) { for (int i = 0; i < channels; i++) {
if (map[i] != i) { if (chmap[i] != i) {
return SDL_FALSE; return SDL_FALSE;
} }
} }
@ -149,7 +150,7 @@ SDL_bool SDL_ChannelMapIsDefault(const Uint8 *map, int channels)
} }
// Swizzle audio channels. src and dst can be the same pointer. It does not change the buffer size. // Swizzle audio channels. src and dst can be the same pointer. It does not change the buffer size.
static void SwizzleAudio(const int num_frames, void *dst, const void *src, int channels, const Uint8 *map, int bitsize) static void SwizzleAudio(const int num_frames, void *dst, const void *src, int channels, const int *map, int bitsize)
{ {
#define CHANNEL_SWIZZLE(bits) { \ #define CHANNEL_SWIZZLE(bits) { \
Uint##bits *tdst = (Uint##bits *) dst; /* treat as UintX; we only care about moving bits and not the type here. */ \ Uint##bits *tdst = (Uint##bits *) dst; /* treat as UintX; we only care about moving bits and not the type here. */ \
@ -161,9 +162,9 @@ static void SwizzleAudio(const int num_frames, void *dst, const void *src, int c
} \ } \
} \ } \
} else { \ } else { \
Uint##bits tmp[SDL_MAX_CHANNEL_MAP_SIZE]; \ SDL_bool isstack; \
SDL_zeroa(tmp); \ Uint##bits *tmp = (Uint##bits *) SDL_small_alloc(int, channels, &isstack); /* !!! FIXME: allocate this when setting the channel map instead. */ \
SDL_assert(SDL_arraysize(tmp) >= channels); \ if (tmp) { \
for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \ for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \
for (int ch = 0; ch < channels; ch++) { \ for (int ch = 0; ch < channels; ch++) { \
tmp[ch] = tsrc[map[ch]]; \ tmp[ch] = tsrc[map[ch]]; \
@ -172,6 +173,8 @@ static void SwizzleAudio(const int num_frames, void *dst, const void *src, int c
tdst[ch] = tmp[ch]; \ tdst[ch] = tmp[ch]; \
} \ } \
} \ } \
SDL_small_free(tmp, isstack); \
} \
} \ } \
} }
@ -199,8 +202,8 @@ static void SwizzleAudio(const int num_frames, void *dst, const void *src, int c
// we also handle gain adjustment here, so we don't have to make another pass over the data later. // we also handle gain adjustment here, so we don't have to make another pass over the data later.
// Strictly speaking, this is also a "conversion". :) // Strictly speaking, this is also a "conversion". :)
void ConvertAudio(int num_frames, void ConvertAudio(int num_frames,
const void *src, SDL_AudioFormat src_format, int src_channels, const Uint8 *src_map, const void *src, SDL_AudioFormat src_format, int src_channels, const int *src_map,
void *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_map, void *dst, SDL_AudioFormat dst_format, int dst_channels, const int *dst_map,
void* scratch, float gain) void* scratch, float gain)
{ {
SDL_assert(src != NULL); SDL_assert(src != NULL);
@ -374,9 +377,9 @@ static Sint64 GetAudioStreamResampleRate(SDL_AudioStream* stream, int src_freq,
return resample_rate; return resample_rate;
} }
static int UpdateAudioStreamInputSpec(SDL_AudioStream *stream, const SDL_AudioSpec *spec) static int UpdateAudioStreamInputSpec(SDL_AudioStream *stream, const SDL_AudioSpec *spec, const int *chmap)
{ {
if (SDL_AudioSpecsEqual(&stream->input_spec, spec)) { if (SDL_AudioSpecsEqual(&stream->input_spec, spec, stream->input_chmap, chmap)) {
return 0; return 0;
} }
@ -384,6 +387,14 @@ static int UpdateAudioStreamInputSpec(SDL_AudioStream *stream, const SDL_AudioSp
return -1; return -1;
} }
if (!chmap) {
stream->input_chmap = NULL;
} else {
const size_t chmaplen = sizeof (*chmap) * spec->channels;
stream->input_chmap = stream->input_chmap_storage;
SDL_memcpy(stream->input_chmap, chmap, chmaplen);
}
SDL_copyp(&stream->input_spec, spec); SDL_copyp(&stream->input_spec, spec);
return 0; return 0;
@ -524,8 +535,6 @@ int SDL_SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_s
return SDL_SetError("Source rate is too low"); return SDL_SetError("Source rate is too low");
} else if (src_spec->freq > max_freq) { } else if (src_spec->freq > max_freq) {
return SDL_SetError("Source rate is too high"); return SDL_SetError("Source rate is too high");
} else if (src_spec->use_channel_map && SDL_ChannelMapIsBogus(src_spec->channel_map, src_spec->channels)) {
return SDL_SetError("Source channel map is invalid");
} }
} }
@ -540,8 +549,6 @@ int SDL_SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_s
return SDL_SetError("Destination rate is too low"); return SDL_SetError("Destination rate is too low");
} else if (dst_spec->freq > max_freq) { } else if (dst_spec->freq > max_freq) {
return SDL_SetError("Destination rate is too high"); return SDL_SetError("Destination rate is too high");
} else if (dst_spec->use_channel_map && SDL_ChannelMapIsBogus(dst_spec->channel_map, dst_spec->channels)) {
return SDL_SetError("Destination channel map is invalid");
} }
} }
@ -557,27 +564,114 @@ int SDL_SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_s
} }
if (src_spec) { if (src_spec) {
SDL_copyp(&stream->src_spec, src_spec); if (src_spec->channels != stream->src_spec.channels) {
if (src_spec->use_channel_map && SDL_ChannelMapIsDefault(src_spec->channel_map, src_spec->channels)) { SDL_FreeLater(stream->src_chmap); // this pointer is handed to the app during SDL_GetAudioStreamInputChannelMap
stream->src_spec.use_channel_map = SDL_FALSE; // turn off the channel map, as this is just unnecessary work. stream->src_chmap = NULL;
} }
SDL_copyp(&stream->src_spec, src_spec);
} }
if (dst_spec) { if (dst_spec) {
if (dst_spec->channels != stream->dst_spec.channels) {
SDL_FreeLater(stream->dst_chmap); // this pointer is handed to the app during SDL_GetAudioStreamInputChannelMap
stream->dst_chmap = NULL;
}
SDL_copyp(&stream->dst_spec, dst_spec); SDL_copyp(&stream->dst_spec, dst_spec);
if (dst_spec->use_channel_map && !stream->src_spec.use_channel_map && SDL_ChannelMapIsDefault(dst_spec->channel_map, dst_spec->channels)) {
stream->dst_spec.use_channel_map = SDL_FALSE; // turn off the channel map, as this is just unnecessary work.
} }
}
// !!! FIXME: decide if the source and dest channel maps would swizzle us back to the starting order and just turn them both off.
// !!! FIXME: (but in this case, you can only do it if the channel count isn't changing, because source order is important to that.)
SDL_UnlockMutex(stream->lock); SDL_UnlockMutex(stream->lock);
return 0; return 0;
} }
static int SetAudioStreamChannelMap(SDL_AudioStream *stream, const SDL_AudioSpec *spec, int **stream_chmap, const int *chmap, int channels, SDL_bool isinput)
{
if (!stream) {
return SDL_InvalidParamError("stream");
}
int retval = 0;
SDL_LockMutex(stream->lock);
if (channels != spec->channels) {
retval = SDL_SetError("Wrong number of channels");
} else if (!*stream_chmap && !chmap) {
// already at default, we're good.
} else if (*stream_chmap && chmap && (SDL_memcmp(*stream_chmap, chmap, sizeof (*chmap) * channels) == 0)) {
// already have this map, don't allocate/copy it again.
} else if (SDL_ChannelMapIsBogus(chmap, channels)) {
retval = SDL_SetError("Invalid channel mapping");
} else if (stream->bound_device && (!!isinput == !!stream->bound_device->physical_device->recording)) {
// quietly refuse to change the format of the end currently bound to a device.
} else {
if (SDL_ChannelMapIsDefault(chmap, channels)) {
chmap = NULL; // just apply a default mapping.
}
if (chmap) {
int *dupmap = SDL_ChannelMapDup(chmap, channels);
if (!dupmap) {
retval = SDL_SetError("Invalid channel mapping");
} else {
SDL_FreeLater(*stream_chmap); // this pointer is handed to the app during SDL_GetAudioStreamInputChannelMap
*stream_chmap = dupmap;
}
} else {
SDL_FreeLater(*stream_chmap); // this pointer is handed to the app during SDL_GetAudioStreamInputChannelMap
*stream_chmap = NULL;
}
}
SDL_UnlockMutex(stream->lock);
return retval;
}
int SDL_SetAudioStreamInputChannelMap(SDL_AudioStream *stream, const int *chmap, int channels)
{
return SetAudioStreamChannelMap(stream, &stream->src_spec, &stream->src_chmap, chmap, channels, SDL_TRUE);
}
int SDL_SetAudioStreamOutputChannelMap(SDL_AudioStream *stream, const int *chmap, int channels)
{
return SetAudioStreamChannelMap(stream, &stream->dst_spec, &stream->dst_chmap, chmap, channels, SDL_FALSE);
}
const int *SDL_GetAudioStreamInputChannelMap(SDL_AudioStream *stream, int *count)
{
const int *retval = NULL;
int channels = 0;
if (stream) {
SDL_LockMutex(stream->lock);
retval = stream->src_chmap;
channels = stream->src_spec.channels;
SDL_UnlockMutex(stream->lock);
}
if (count) {
*count = channels;
}
return retval;
}
const int *SDL_GetAudioStreamOutputChannelMap(SDL_AudioStream *stream, int *count)
{
const int *retval = NULL;
int channels = 0;
if (stream) {
SDL_LockMutex(stream->lock);
retval = stream->dst_chmap;
channels = stream->dst_spec.channels;
SDL_UnlockMutex(stream->lock);
}
if (count) {
*count = channels;
}
return retval;
}
float SDL_GetAudioStreamFrequencyRatio(SDL_AudioStream *stream) float SDL_GetAudioStreamFrequencyRatio(SDL_AudioStream *stream)
{ {
if (!stream) { if (!stream) {
@ -676,7 +770,7 @@ static int PutAudioStreamBuffer(SDL_AudioStream *stream, const void *buf, int le
SDL_AudioTrack* track = NULL; SDL_AudioTrack* track = NULL;
if (callback) { if (callback) {
track = SDL_CreateAudioTrack(stream->queue, &stream->src_spec, (Uint8 *)buf, len, len, callback, userdata); track = SDL_CreateAudioTrack(stream->queue, &stream->src_spec, stream->src_chmap, (Uint8 *)buf, len, len, callback, userdata);
if (!track) { if (!track) {
SDL_UnlockMutex(stream->lock); SDL_UnlockMutex(stream->lock);
@ -691,7 +785,7 @@ static int PutAudioStreamBuffer(SDL_AudioStream *stream, const void *buf, int le
if (track) { if (track) {
SDL_AddTrackToAudioQueue(stream->queue, track); SDL_AddTrackToAudioQueue(stream->queue, track);
} else { } else {
retval = SDL_WriteToAudioQueue(stream->queue, &stream->src_spec, (const Uint8 *)buf, len); retval = SDL_WriteToAudioQueue(stream->queue, &stream->src_spec, stream->src_chmap, (const Uint8 *)buf, len);
} }
if (retval == 0) { if (retval == 0) {
@ -782,16 +876,21 @@ static Uint8 *EnsureAudioStreamWorkBufferSize(SDL_AudioStream *stream, size_t ne
} }
static Sint64 NextAudioStreamIter(SDL_AudioStream* stream, void** inout_iter, static Sint64 NextAudioStreamIter(SDL_AudioStream* stream, void** inout_iter,
Sint64* inout_resample_offset, SDL_AudioSpec* out_spec, SDL_bool* out_flushed) Sint64* inout_resample_offset, SDL_AudioSpec* out_spec, int **out_chmap, SDL_bool* out_flushed)
{ {
SDL_AudioSpec spec; SDL_AudioSpec spec;
SDL_bool flushed; SDL_bool flushed;
size_t queued_bytes = SDL_NextAudioQueueIter(stream->queue, inout_iter, &spec, &flushed); int *chmap;
size_t queued_bytes = SDL_NextAudioQueueIter(stream->queue, inout_iter, &spec, &chmap, &flushed);
if (out_spec) { if (out_spec) {
SDL_copyp(out_spec, &spec); SDL_copyp(out_spec, &spec);
} }
if (out_chmap) {
*out_chmap = chmap;
}
// There is infinite audio available, whether or not we are resampling // There is infinite audio available, whether or not we are resampling
if (queued_bytes == SDL_SIZE_MAX) { if (queued_bytes == SDL_SIZE_MAX) {
*inout_resample_offset = 0; *inout_resample_offset = 0;
@ -839,7 +938,7 @@ static Sint64 GetAudioStreamAvailableFrames(SDL_AudioStream* stream, Sint64* out
Sint64 output_frames = 0; Sint64 output_frames = 0;
while (iter) { while (iter) {
output_frames += NextAudioStreamIter(stream, &iter, &resample_offset, NULL, NULL); output_frames += NextAudioStreamIter(stream, &iter, &resample_offset, NULL, NULL, NULL);
// Already got loads of frames. Just clamp it to something reasonable // Already got loads of frames. Just clamp it to something reasonable
if (output_frames >= SDL_MAX_SINT32) { if (output_frames >= SDL_MAX_SINT32) {
@ -855,7 +954,7 @@ static Sint64 GetAudioStreamAvailableFrames(SDL_AudioStream* stream, Sint64* out
return output_frames; return output_frames;
} }
static Sint64 GetAudioStreamHead(SDL_AudioStream* stream, SDL_AudioSpec* out_spec, SDL_bool* out_flushed) static Sint64 GetAudioStreamHead(SDL_AudioStream* stream, SDL_AudioSpec* out_spec, int **out_chmap, SDL_bool* out_flushed)
{ {
void* iter = SDL_BeginAudioQueueIter(stream->queue); void* iter = SDL_BeginAudioQueueIter(stream->queue);
@ -866,7 +965,7 @@ static Sint64 GetAudioStreamHead(SDL_AudioStream* stream, SDL_AudioSpec* out_spe
} }
Sint64 resample_offset = stream->resample_offset; Sint64 resample_offset = stream->resample_offset;
return NextAudioStreamIter(stream, &iter, &resample_offset, out_spec, out_flushed); return NextAudioStreamIter(stream, &iter, &resample_offset, out_spec, out_chmap, out_flushed);
} }
// You must hold stream->lock and validate your parameters before calling this! // You must hold stream->lock and validate your parameters before calling this!
@ -881,7 +980,7 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
const SDL_AudioFormat dst_format = dst_spec->format; const SDL_AudioFormat dst_format = dst_spec->format;
const int dst_channels = dst_spec->channels; const int dst_channels = dst_spec->channels;
const Uint8 *dst_map = dst_spec->use_channel_map ? dst_spec->channel_map : NULL; const int *dst_map = stream->dst_chmap;
const int max_frame_size = CalculateMaxFrameSize(src_format, src_channels, dst_format, dst_channels); const int max_frame_size = CalculateMaxFrameSize(src_format, src_channels, dst_format, dst_channels);
const Sint64 resample_rate = GetAudioStreamResampleRate(stream, src_spec->freq, stream->resample_offset); const Sint64 resample_rate = GetAudioStreamResampleRate(stream, src_spec->freq, stream->resample_offset);
@ -1061,21 +1160,23 @@ int SDL_GetAudioStreamDataAdjustGain(SDL_AudioStream *stream, void *voidbuf, int
while (total < len) { while (total < len) {
// Audio is processed a track at a time. // Audio is processed a track at a time.
SDL_AudioSpec input_spec; SDL_AudioSpec input_spec;
int *input_chmap;
SDL_bool flushed; SDL_bool flushed;
const Sint64 available_frames = GetAudioStreamHead(stream, &input_spec, &flushed); const Sint64 available_frames = GetAudioStreamHead(stream, &input_spec, &input_chmap, &flushed);
if (available_frames == 0) { if (available_frames == 0) {
if (flushed) { if (flushed) {
SDL_PopAudioQueueHead(stream->queue); SDL_PopAudioQueueHead(stream->queue);
SDL_zero(stream->input_spec); SDL_zero(stream->input_spec);
stream->resample_offset = 0; stream->resample_offset = 0;
stream->input_chmap = NULL;
continue; continue;
} }
// There are no frames available, but the track hasn't been flushed, so more might be added later. // There are no frames available, but the track hasn't been flushed, so more might be added later.
break; break;
} }
if (UpdateAudioStreamInputSpec(stream, &input_spec) != 0) { if (UpdateAudioStreamInputSpec(stream, &input_spec, input_chmap) != 0) {
total = total ? total : -1; total = total ? total : -1;
break; break;
} }
@ -1160,6 +1261,7 @@ int SDL_ClearAudioStream(SDL_AudioStream *stream)
SDL_ClearAudioQueue(stream->queue); SDL_ClearAudioQueue(stream->queue);
SDL_zero(stream->input_spec); SDL_zero(stream->input_spec);
stream->input_chmap = NULL;
stream->resample_offset = 0; stream->resample_offset = 0;
SDL_UnlockMutex(stream->lock); SDL_UnlockMutex(stream->lock);

View file

@ -36,6 +36,7 @@ struct SDL_MemoryPool
struct SDL_AudioTrack struct SDL_AudioTrack
{ {
SDL_AudioSpec spec; SDL_AudioSpec spec;
int *chmap;
SDL_bool flushed; SDL_bool flushed;
SDL_AudioTrack *next; SDL_AudioTrack *next;
@ -46,6 +47,8 @@ struct SDL_AudioTrack
size_t head; size_t head;
size_t tail; size_t tail;
size_t capacity; size_t capacity;
int chmap_storage[SDL_MAX_CHANNELMAP_CHANNELS]; // !!! FIXME: this needs to grow if SDL ever supports more channels. But if it grows, we should probably be more clever about allocations.
}; };
struct SDL_AudioQueue struct SDL_AudioQueue
@ -226,7 +229,7 @@ void SDL_PopAudioQueueHead(SDL_AudioQueue *queue)
} }
SDL_AudioTrack *SDL_CreateAudioTrack( SDL_AudioTrack *SDL_CreateAudioTrack(
SDL_AudioQueue *queue, const SDL_AudioSpec *spec, SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const int *chmap,
Uint8 *data, size_t len, size_t capacity, Uint8 *data, size_t len, size_t capacity,
SDL_ReleaseAudioBufferCallback callback, void *userdata) SDL_ReleaseAudioBufferCallback callback, void *userdata)
{ {
@ -237,6 +240,13 @@ SDL_AudioTrack *SDL_CreateAudioTrack(
} }
SDL_zerop(track); SDL_zerop(track);
if (chmap) {
SDL_assert(SDL_arraysize(track->chmap_storage) >= spec->channels);
SDL_memcpy(track->chmap_storage, chmap, sizeof (*chmap) * spec->channels);
track->chmap = track->chmap_storage;
}
SDL_copyp(&track->spec, spec); SDL_copyp(&track->spec, spec);
track->userdata = userdata; track->userdata = userdata;
@ -256,7 +266,7 @@ static void SDLCALL FreeChunkedAudioBuffer(void *userdata, const void *buf, int
FreeMemoryPoolBlock(&queue->chunk_pool, (void *)buf); FreeMemoryPoolBlock(&queue->chunk_pool, (void *)buf);
} }
static SDL_AudioTrack *CreateChunkedAudioTrack(SDL_AudioQueue *queue, const SDL_AudioSpec *spec) static SDL_AudioTrack *CreateChunkedAudioTrack(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const int *chmap)
{ {
void *chunk = AllocMemoryPoolBlock(&queue->chunk_pool); void *chunk = AllocMemoryPoolBlock(&queue->chunk_pool);
@ -267,7 +277,7 @@ static SDL_AudioTrack *CreateChunkedAudioTrack(SDL_AudioQueue *queue, const SDL_
size_t capacity = queue->chunk_pool.block_size; size_t capacity = queue->chunk_pool.block_size;
capacity -= capacity % SDL_AUDIO_FRAMESIZE(*spec); capacity -= capacity % SDL_AUDIO_FRAMESIZE(*spec);
SDL_AudioTrack *track = SDL_CreateAudioTrack(queue, spec, chunk, 0, capacity, FreeChunkedAudioBuffer, queue); SDL_AudioTrack *track = SDL_CreateAudioTrack(queue, spec, chmap, chunk, 0, capacity, FreeChunkedAudioBuffer, queue);
if (!track) { if (!track) {
FreeMemoryPoolBlock(&queue->chunk_pool, chunk); FreeMemoryPoolBlock(&queue->chunk_pool, chunk);
@ -283,7 +293,7 @@ void SDL_AddTrackToAudioQueue(SDL_AudioQueue *queue, SDL_AudioTrack *track)
if (tail) { if (tail) {
// If the spec has changed, make sure to flush the previous track // If the spec has changed, make sure to flush the previous track
if (!SDL_AudioSpecsEqual(&tail->spec, &track->spec)) { if (!SDL_AudioSpecsEqual(&tail->spec, &track->spec, tail->chmap, track->chmap)) {
FlushAudioTrack(tail); FlushAudioTrack(tail);
} }
@ -308,7 +318,7 @@ static size_t WriteToAudioTrack(SDL_AudioTrack *track, const Uint8 *data, size_t
return len; return len;
} }
int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const Uint8 *data, size_t len) int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const int *chmap, const Uint8 *data, size_t len)
{ {
if (len == 0) { if (len == 0) {
return 0; return 0;
@ -317,12 +327,12 @@ int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, cons
SDL_AudioTrack *track = queue->tail; SDL_AudioTrack *track = queue->tail;
if (track) { if (track) {
if (!SDL_AudioSpecsEqual(&track->spec, spec)) { if (!SDL_AudioSpecsEqual(&track->spec, spec, track->chmap, chmap)) {
FlushAudioTrack(track); FlushAudioTrack(track);
} }
} else { } else {
SDL_assert(!queue->head); SDL_assert(!queue->head);
track = CreateChunkedAudioTrack(queue, spec); track = CreateChunkedAudioTrack(queue, spec, chmap);
if (!track) { if (!track) {
return -1; return -1;
@ -333,7 +343,7 @@ int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, cons
} }
for (;;) { for (;;) {
size_t written = WriteToAudioTrack(track, data, len); const size_t written = WriteToAudioTrack(track, data, len);
data += written; data += written;
len -= written; len -= written;
@ -341,7 +351,7 @@ int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, cons
break; break;
} }
SDL_AudioTrack *new_track = CreateChunkedAudioTrack(queue, spec); SDL_AudioTrack *new_track = CreateChunkedAudioTrack(queue, spec, chmap);
if (!new_track) { if (!new_track) {
return -1; return -1;
@ -360,12 +370,13 @@ void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue)
return queue->head; return queue->head;
} }
size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, SDL_bool *out_flushed) size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, int **out_chmap, SDL_bool *out_flushed)
{ {
SDL_AudioTrack *iter = (SDL_AudioTrack *)(*inout_iter); SDL_AudioTrack *iter = (SDL_AudioTrack *)(*inout_iter);
SDL_assert(iter != NULL); SDL_assert(iter != NULL);
SDL_copyp(out_spec, &iter->spec); SDL_copyp(out_spec, &iter->spec);
*out_chmap = iter->chmap;
SDL_bool flushed = SDL_FALSE; SDL_bool flushed = SDL_FALSE;
size_t queued_bytes = 0; size_t queued_bytes = 0;
@ -512,7 +523,7 @@ static const Uint8 *PeekIntoAudioQueueFuture(SDL_AudioQueue *queue, Uint8 *data,
} }
const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue, const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue,
Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_map, Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels, const int *dst_map,
int past_frames, int present_frames, int future_frames, int past_frames, int present_frames, int future_frames,
Uint8 *scratch, float gain) Uint8 *scratch, float gain)
{ {
@ -524,7 +535,7 @@ const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue,
SDL_AudioFormat src_format = track->spec.format; SDL_AudioFormat src_format = track->spec.format;
int src_channels = track->spec.channels; int src_channels = track->spec.channels;
const Uint8 *src_map = track->spec.use_channel_map ? track->spec.channel_map : NULL; const int *src_map = track->chmap;
size_t src_frame_size = SDL_AUDIO_BYTESIZE(src_format) * src_channels; size_t src_frame_size = SDL_AUDIO_BYTESIZE(src_format) * src_channels;
size_t dst_frame_size = SDL_AUDIO_BYTESIZE(dst_format) * dst_channels; size_t dst_frame_size = SDL_AUDIO_BYTESIZE(dst_format) * dst_channels;
@ -597,9 +608,10 @@ size_t SDL_GetAudioQueueQueued(SDL_AudioQueue *queue)
while (iter) { while (iter) {
SDL_AudioSpec src_spec; SDL_AudioSpec src_spec;
int *src_chmap;
SDL_bool flushed; SDL_bool flushed;
size_t avail = SDL_NextAudioQueueIter(queue, &iter, &src_spec, &flushed); size_t avail = SDL_NextAudioQueueIter(queue, &iter, &src_spec, &src_chmap, &flushed);
if (avail >= SDL_SIZE_MAX - total) { if (avail >= SDL_SIZE_MAX - total) {
total = SDL_SIZE_MAX; total = SDL_SIZE_MAX;

View file

@ -48,11 +48,11 @@ void SDL_PopAudioQueueHead(SDL_AudioQueue *queue);
// Write data to the end of queue // Write data to the end of queue
// REQUIRES: If the spec has changed, the last track must have been flushed // REQUIRES: If the spec has changed, the last track must have been flushed
int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const Uint8 *data, size_t len); int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const int *chmap, const Uint8 *data, size_t len);
// Create a track where the input data is owned by the caller // Create a track where the input data is owned by the caller
SDL_AudioTrack *SDL_CreateAudioTrack(SDL_AudioQueue *queue, SDL_AudioTrack *SDL_CreateAudioTrack(SDL_AudioQueue *queue,
const SDL_AudioSpec *spec, Uint8 *data, size_t len, size_t capacity, const SDL_AudioSpec *spec, const int *chmap, Uint8 *data, size_t len, size_t capacity,
SDL_ReleaseAudioBufferCallback callback, void *userdata); SDL_ReleaseAudioBufferCallback callback, void *userdata);
// Add a track to the end of the queue // Add a track to the end of the queue
@ -64,10 +64,10 @@ void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue);
// Query and update the track iterator // Query and update the track iterator
// REQUIRES: `*inout_iter != NULL` (a valid iterator) // REQUIRES: `*inout_iter != NULL` (a valid iterator)
size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, SDL_bool *out_flushed); size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, int **out_chmap, SDL_bool *out_flushed);
const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue, const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue,
Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_map, Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels, const int *dst_map,
int past_frames, int present_frames, int future_frames, int past_frames, int present_frames, int future_frames,
Uint8 *scratch, float gain); Uint8 *scratch, float gain);

View file

@ -48,6 +48,8 @@
#define DEFAULT_AUDIO_RECORDING_CHANNELS 1 #define DEFAULT_AUDIO_RECORDING_CHANNELS 1
#define DEFAULT_AUDIO_RECORDING_FREQUENCY 44100 #define DEFAULT_AUDIO_RECORDING_FREQUENCY 44100
#define SDL_MAX_CHANNELMAP_CHANNELS 8 // !!! FIXME: if SDL ever supports more channels, clean this out and make those parts dynamic.
typedef struct SDL_AudioDevice SDL_AudioDevice; typedef struct SDL_AudioDevice SDL_AudioDevice;
typedef struct SDL_LogicalAudioDevice SDL_LogicalAudioDevice; typedef struct SDL_LogicalAudioDevice SDL_LogicalAudioDevice;
@ -111,18 +113,22 @@ extern void ConvertAudioToFloat(float *dst, const void *src, int num_samples, SD
extern void ConvertAudioFromFloat(void *dst, const float *src, int num_samples, SDL_AudioFormat dst_fmt); extern void ConvertAudioFromFloat(void *dst, const float *src, int num_samples, SDL_AudioFormat dst_fmt);
extern void ConvertAudioSwapEndian(void* dst, const void* src, int num_samples, int bitsize); extern void ConvertAudioSwapEndian(void* dst, const void* src, int num_samples, int bitsize);
extern SDL_bool SDL_ChannelMapIsDefault(const Uint8 *map, int channels); extern SDL_bool SDL_ChannelMapIsDefault(const int *map, int channels);
extern SDL_bool SDL_ChannelMapIsBogus(const Uint8 *map, int channels); extern SDL_bool SDL_ChannelMapIsBogus(const int *map, int channels);
// this gets used from the audio device threads. It has rules, don't use this if you don't know how to use it! // this gets used from the audio device threads. It has rules, don't use this if you don't know how to use it!
extern void ConvertAudio(int num_frames, extern void ConvertAudio(int num_frames,
const void *src, SDL_AudioFormat src_format, int src_channels, const Uint8 *src_map, const void *src, SDL_AudioFormat src_format, int src_channels, const int *src_map,
void *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_map, void *dst, SDL_AudioFormat dst_format, int dst_channels, const int *dst_map,
void* scratch, float gain); void* scratch, float gain);
// Compare two SDL_AudioSpecs, return SDL_TRUE if they match exactly. // Compare two SDL_AudioSpecs, return SDL_TRUE if they match exactly.
// Using SDL_memcmp directly isn't safe, since potential padding (and unused parts of the channel map) might not be initialized. // Using SDL_memcmp directly isn't safe, since potential padding might not be initialized.
extern SDL_bool SDL_AudioSpecsEqual(const SDL_AudioSpec *a, const SDL_AudioSpec *b); // either channel maps can be NULL for the default (and both should be if you don't care about them).
extern SDL_bool SDL_AudioSpecsEqual(const SDL_AudioSpec *a, const SDL_AudioSpec *b, const int *channel_map_a, const int *channel_map_b);
// allocate+copy a channel map.
extern int *SDL_ChannelMapDup(const int *origchmap, int channels);
// Special case to let something in SDL_audiocvt.c access something in SDL_audio.c. Don't use this. // Special case to let something in SDL_audiocvt.c access something in SDL_audio.c. Don't use this.
extern void OnAudioStreamCreated(SDL_AudioStream *stream); extern void OnAudioStreamCreated(SDL_AudioStream *stream);
@ -197,12 +203,16 @@ struct SDL_AudioStream
SDL_AudioSpec src_spec; SDL_AudioSpec src_spec;
SDL_AudioSpec dst_spec; SDL_AudioSpec dst_spec;
int *src_chmap;
int *dst_chmap;
float freq_ratio; float freq_ratio;
float gain; float gain;
struct SDL_AudioQueue* queue; struct SDL_AudioQueue* queue;
SDL_AudioSpec input_spec; // The spec of input data currently being processed SDL_AudioSpec input_spec; // The spec of input data currently being processed
int *input_chmap;
int input_chmap_storage[SDL_MAX_CHANNELMAP_CHANNELS]; // !!! FIXME: this needs to grow if SDL ever supports more channels. But if it grows, we should probably be more clever about allocations.
Sint64 resample_offset; Sint64 resample_offset;
Uint8 *work_buffer; // used for scratch space during data conversion/resampling. Uint8 *work_buffer; // used for scratch space during data conversion/resampling.
@ -288,6 +298,8 @@ struct SDL_AudioDevice
SDL_AudioSpec spec; SDL_AudioSpec spec;
int buffer_size; int buffer_size;
int *chmap;
// The device's default audio specification // The device's default audio specification
SDL_AudioSpec default_spec; SDL_AudioSpec default_spec;

View file

@ -249,13 +249,13 @@ static const char *get_audio_device(void *handle, const int channels)
// https://bugzilla.libsdl.org/show_bug.cgi?id=110 // https://bugzilla.libsdl.org/show_bug.cgi?id=110
// "For Linux ALSA, this is FL-FR-RL-RR-C-LFE // "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
// and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR" // and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
static const Uint8 swizzle_alsa_channels_6[6] = { 0, 1, 4, 5, 2, 3 }; static const int swizzle_alsa_channels_6[6] = { 0, 1, 4, 5, 2, 3 };
// 7.1 swizzle: // 7.1 swizzle:
// https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/mapping-stream-formats-to-speaker-configurations // https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/mapping-stream-formats-to-speaker-configurations
// For Linux ALSA, this appears to be FL-FR-RL-RR-C-LFE-SL-SR // For Linux ALSA, this appears to be FL-FR-RL-RR-C-LFE-SL-SR
// and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-SL-SR-RL-RR" // and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-SL-SR-RL-RR"
static const Uint8 swizzle_alsa_channels_8[8] = { 0, 1, 6, 7, 2, 3, 4, 5 }; static const int swizzle_alsa_channels_8[8] = { 0, 1, 6, 7, 2, 3, 4, 5 };
@ -533,17 +533,15 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device)
device->spec.channels = channels; device->spec.channels = channels;
} }
// Validate number of channels and determine if swizzling is necessary. const int *swizmap = NULL;
// Assume original swizzling, until proven otherwise.
if (channels == 6) { if (channels == 6) {
device->spec.use_channel_map = SDL_TRUE; swizmap = swizzle_alsa_channels_6;
SDL_memcpy(device->spec.channel_map, swizzle_alsa_channels_6, sizeof (device->spec.channel_map[0]) * channels);
} else if (channels == 8) { } else if (channels == 8) {
device->spec.use_channel_map = SDL_TRUE; swizmap = swizzle_alsa_channels_8;
SDL_memcpy(device->spec.channel_map, swizzle_alsa_channels_8, sizeof (device->spec.channel_map[0]) * channels);
} }
#ifdef SND_CHMAP_API_VERSION #ifdef SND_CHMAP_API_VERSION
if (swizmap) {
snd_pcm_chmap_t *chmap = ALSA_snd_pcm_get_chmap(pcm_handle); snd_pcm_chmap_t *chmap = ALSA_snd_pcm_get_chmap(pcm_handle);
if (chmap) { if (chmap) {
char chmap_str[64]; char chmap_str[64];
@ -551,15 +549,25 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device)
if ( (channels == 6) && if ( (channels == 6) &&
((SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0) || ((SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0) ||
(SDL_strcmp("FL FR FC LFE SL SR", chmap_str) == 0)) ) { (SDL_strcmp("FL FR FC LFE SL SR", chmap_str) == 0)) ) {
device->spec.use_channel_map = SDL_FALSE; swizmap = NULL;
} else if ((channels == 8) && (SDL_strcmp("FL FR FC LFE SL SR RL RR", chmap_str) == 0)) { } else if ((channels == 8) && (SDL_strcmp("FL FR FC LFE SL SR RL RR", chmap_str) == 0)) {
device->spec.use_channel_map = SDL_FALSE; swizmap = NULL;
} }
} }
free(chmap); // This should NOT be SDL_free() free(chmap); // This should NOT be SDL_free()
} }
}
#endif // SND_CHMAP_API_VERSION #endif // SND_CHMAP_API_VERSION
// Validate number of channels and determine if swizzling is necessary.
// Assume original swizzling, until proven otherwise.
if (swizmap) {
device->chmap = SDL_ChannelMapDup(swizmap, channels);
if (!device->chmap) {
return -1;
}
}
// Set the audio rate // Set the audio rate
unsigned int rate = device->spec.freq; unsigned int rate = device->spec.freq;
status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams,

View file

@ -165,6 +165,7 @@ SDL3_0.0.0 {
SDL_GetAndroidSDKVersion; SDL_GetAndroidSDKVersion;
SDL_GetAssertionHandler; SDL_GetAssertionHandler;
SDL_GetAssertionReport; SDL_GetAssertionReport;
SDL_GetAudioDeviceChannelMap;
SDL_GetAudioDeviceFormat; SDL_GetAudioDeviceFormat;
SDL_GetAudioDeviceGain; SDL_GetAudioDeviceGain;
SDL_GetAudioDeviceName; SDL_GetAudioDeviceName;
@ -177,6 +178,8 @@ SDL3_0.0.0 {
SDL_GetAudioStreamFormat; SDL_GetAudioStreamFormat;
SDL_GetAudioStreamFrequencyRatio; SDL_GetAudioStreamFrequencyRatio;
SDL_GetAudioStreamGain; SDL_GetAudioStreamGain;
SDL_GetAudioStreamInputChannelMap;
SDL_GetAudioStreamOutputChannelMap;
SDL_GetAudioStreamProperties; SDL_GetAudioStreamProperties;
SDL_GetAudioStreamQueued; SDL_GetAudioStreamQueued;
SDL_GetBasePath; SDL_GetBasePath;
@ -693,6 +696,8 @@ SDL3_0.0.0 {
SDL_SetAudioStreamFrequencyRatio; SDL_SetAudioStreamFrequencyRatio;
SDL_SetAudioStreamGain; SDL_SetAudioStreamGain;
SDL_SetAudioStreamGetCallback; SDL_SetAudioStreamGetCallback;
SDL_SetAudioStreamInputChannelMap;
SDL_SetAudioStreamOutputChannelMap;
SDL_SetAudioStreamPutCallback; SDL_SetAudioStreamPutCallback;
SDL_SetBooleanProperty; SDL_SetBooleanProperty;
SDL_SetClipboardData; SDL_SetClipboardData;

View file

@ -189,6 +189,7 @@
#define SDL_GetAndroidSDKVersion SDL_GetAndroidSDKVersion_REAL #define SDL_GetAndroidSDKVersion SDL_GetAndroidSDKVersion_REAL
#define SDL_GetAssertionHandler SDL_GetAssertionHandler_REAL #define SDL_GetAssertionHandler SDL_GetAssertionHandler_REAL
#define SDL_GetAssertionReport SDL_GetAssertionReport_REAL #define SDL_GetAssertionReport SDL_GetAssertionReport_REAL
#define SDL_GetAudioDeviceChannelMap SDL_GetAudioDeviceChannelMap_REAL
#define SDL_GetAudioDeviceFormat SDL_GetAudioDeviceFormat_REAL #define SDL_GetAudioDeviceFormat SDL_GetAudioDeviceFormat_REAL
#define SDL_GetAudioDeviceGain SDL_GetAudioDeviceGain_REAL #define SDL_GetAudioDeviceGain SDL_GetAudioDeviceGain_REAL
#define SDL_GetAudioDeviceName SDL_GetAudioDeviceName_REAL #define SDL_GetAudioDeviceName SDL_GetAudioDeviceName_REAL
@ -201,6 +202,8 @@
#define SDL_GetAudioStreamFormat SDL_GetAudioStreamFormat_REAL #define SDL_GetAudioStreamFormat SDL_GetAudioStreamFormat_REAL
#define SDL_GetAudioStreamFrequencyRatio SDL_GetAudioStreamFrequencyRatio_REAL #define SDL_GetAudioStreamFrequencyRatio SDL_GetAudioStreamFrequencyRatio_REAL
#define SDL_GetAudioStreamGain SDL_GetAudioStreamGain_REAL #define SDL_GetAudioStreamGain SDL_GetAudioStreamGain_REAL
#define SDL_GetAudioStreamInputChannelMap SDL_GetAudioStreamInputChannelMap_REAL
#define SDL_GetAudioStreamOutputChannelMap SDL_GetAudioStreamOutputChannelMap_REAL
#define SDL_GetAudioStreamProperties SDL_GetAudioStreamProperties_REAL #define SDL_GetAudioStreamProperties SDL_GetAudioStreamProperties_REAL
#define SDL_GetAudioStreamQueued SDL_GetAudioStreamQueued_REAL #define SDL_GetAudioStreamQueued SDL_GetAudioStreamQueued_REAL
#define SDL_GetBasePath SDL_GetBasePath_REAL #define SDL_GetBasePath SDL_GetBasePath_REAL
@ -717,6 +720,8 @@
#define SDL_SetAudioStreamFrequencyRatio SDL_SetAudioStreamFrequencyRatio_REAL #define SDL_SetAudioStreamFrequencyRatio SDL_SetAudioStreamFrequencyRatio_REAL
#define SDL_SetAudioStreamGain SDL_SetAudioStreamGain_REAL #define SDL_SetAudioStreamGain SDL_SetAudioStreamGain_REAL
#define SDL_SetAudioStreamGetCallback SDL_SetAudioStreamGetCallback_REAL #define SDL_SetAudioStreamGetCallback SDL_SetAudioStreamGetCallback_REAL
#define SDL_SetAudioStreamInputChannelMap SDL_SetAudioStreamInputChannelMap_REAL
#define SDL_SetAudioStreamOutputChannelMap SDL_SetAudioStreamOutputChannelMap_REAL
#define SDL_SetAudioStreamPutCallback SDL_SetAudioStreamPutCallback_REAL #define SDL_SetAudioStreamPutCallback SDL_SetAudioStreamPutCallback_REAL
#define SDL_SetBooleanProperty SDL_SetBooleanProperty_REAL #define SDL_SetBooleanProperty SDL_SetBooleanProperty_REAL
#define SDL_SetClipboardData SDL_SetClipboardData_REAL #define SDL_SetClipboardData SDL_SetClipboardData_REAL

View file

@ -208,6 +208,7 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadSensorEnabled,(SDL_Gamepad *a, SDL_SensorTyp
SDL_DYNAPI_PROC(int,SDL_GetAndroidSDKVersion,(void),(),return) SDL_DYNAPI_PROC(int,SDL_GetAndroidSDKVersion,(void),(),return)
SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetAssertionHandler,(void **a),(a),return) SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetAssertionHandler,(void **a),(a),return)
SDL_DYNAPI_PROC(const SDL_AssertData*,SDL_GetAssertionReport,(void),(),return) SDL_DYNAPI_PROC(const SDL_AssertData*,SDL_GetAssertionReport,(void),(),return)
SDL_DYNAPI_PROC(const int*,SDL_GetAudioDeviceChannelMap,(SDL_AudioDeviceID a, int *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceFormat,(SDL_AudioDeviceID a, SDL_AudioSpec *b, int *c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceFormat,(SDL_AudioDeviceID a, SDL_AudioSpec *b, int *c),(a,b,c),return)
SDL_DYNAPI_PROC(float,SDL_GetAudioDeviceGain,(SDL_AudioDeviceID a),(a),return) SDL_DYNAPI_PROC(float,SDL_GetAudioDeviceGain,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_GetAudioDeviceName,(SDL_AudioDeviceID a),(a),return) SDL_DYNAPI_PROC(const char*,SDL_GetAudioDeviceName,(SDL_AudioDeviceID a),(a),return)
@ -220,6 +221,8 @@ SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_GetAudioStreamDevice,(SDL_AudioStream *a),
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioSpec *b, SDL_AudioSpec *c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_GetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioSpec *b, SDL_AudioSpec *c),(a,b,c),return)
SDL_DYNAPI_PROC(float,SDL_GetAudioStreamFrequencyRatio,(SDL_AudioStream *a),(a),return) SDL_DYNAPI_PROC(float,SDL_GetAudioStreamFrequencyRatio,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(float,SDL_GetAudioStreamGain,(SDL_AudioStream *a),(a),return) SDL_DYNAPI_PROC(float,SDL_GetAudioStreamGain,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(const int*,SDL_GetAudioStreamInputChannelMap,(SDL_AudioStream *a, int *b),(a,b),return)
SDL_DYNAPI_PROC(const int*,SDL_GetAudioStreamOutputChannelMap,(SDL_AudioStream *a, int *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetAudioStreamProperties,(SDL_AudioStream *a),(a),return) SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetAudioStreamProperties,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamQueued,(SDL_AudioStream *a),(a),return) SDL_DYNAPI_PROC(int,SDL_GetAudioStreamQueued,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(char*,SDL_GetBasePath,(void),(),return) SDL_DYNAPI_PROC(char*,SDL_GetBasePath,(void),(),return)
@ -727,6 +730,8 @@ SDL_DYNAPI_PROC(int,SDL_SetAudioStreamFormat,(SDL_AudioStream *a, const SDL_Audi
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamFrequencyRatio,(SDL_AudioStream *a, float b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_SetAudioStreamFrequencyRatio,(SDL_AudioStream *a, float b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamGain,(SDL_AudioStream *a, float b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_SetAudioStreamGain,(SDL_AudioStream *a, float b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamGetCallback,(SDL_AudioStream *a, SDL_AudioStreamCallback b, void *c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_SetAudioStreamGetCallback,(SDL_AudioStream *a, SDL_AudioStreamCallback b, void *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamInputChannelMap,(SDL_AudioStream *a, const int *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamOutputChannelMap,(SDL_AudioStream *a, const int *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamPutCallback,(SDL_AudioStream *a, SDL_AudioStreamCallback b, void *c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_SetAudioStreamPutCallback,(SDL_AudioStream *a, SDL_AudioStreamCallback b, void *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SetBooleanProperty,(SDL_PropertiesID a, const char *b, SDL_bool c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_SetBooleanProperty,(SDL_PropertiesID a, const char *b, SDL_bool c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SetClipboardData,(SDL_ClipboardDataCallback a, SDL_ClipboardCleanupCallback b, void *c, const char **d, size_t e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(int,SDL_SetClipboardData,(SDL_ClipboardDataCallback a, SDL_ClipboardCleanupCallback b, void *c, const char **d, size_t e),(a,b,c,d,e),return)

View file

@ -114,6 +114,7 @@ int SDL_AppInit(void **appstate, int argc, char *argv[])
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create audio stream: %s\n", SDL_GetError()); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create audio stream: %s\n", SDL_GetError());
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }
SDL_ResumeAudioStreamDevice(stream); SDL_ResumeAudioStreamDevice(stream);
return SDL_APP_CONTINUE; return SDL_APP_CONTINUE;

View file

@ -1167,7 +1167,7 @@ static AVCodecContext *OpenAudioStream(AVFormatContext *ic, int stream, const AV
return NULL; return NULL;
} }
SDL_AudioSpec spec = { SDL_AUDIO_F32, codecpar->ch_layout.nb_channels, codecpar->sample_rate, SDL_FALSE }; SDL_AudioSpec spec = { SDL_AUDIO_F32, codecpar->ch_layout.nb_channels, codecpar->sample_rate };
audio = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, NULL, NULL); audio = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, NULL, NULL);
if (audio) { if (audio) {
SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(audio)); SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(audio));
@ -1240,7 +1240,7 @@ static void InterleaveAudio(AVFrame *frame, const SDL_AudioSpec *spec)
static void HandleAudioFrame(AVFrame *frame) static void HandleAudioFrame(AVFrame *frame)
{ {
if (audio) { if (audio) {
SDL_AudioSpec spec = { GetAudioFormat(frame->format), frame->ch_layout.nb_channels, frame->sample_rate, SDL_FALSE }; SDL_AudioSpec spec = { GetAudioFormat(frame->format), frame->ch_layout.nb_channels, frame->sample_rate };
SDL_SetAudioStreamFormat(audio, &spec, NULL); SDL_SetAudioStreamFormat(audio, &spec, NULL);
if (frame->ch_layout.nb_channels > 1 && IsPlanarAudioFormat(frame->format)) { if (frame->ch_layout.nb_channels > 1 && IsPlanarAudioFormat(frame->format)) {