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
* 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:
*
@ -76,7 +81,7 @@
* platforms; SDL will swizzle the channels as necessary if a platform expects
* 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.
*/
@ -301,18 +306,6 @@ typedef Uint32 SDL_AudioDeviceID;
*/
#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.
*
@ -325,8 +318,6 @@ typedef struct SDL_AudioSpec
SDL_AudioFormat format; /**< Audio data format */
int channels; /**< Number of channels: 1 mono, 2 stereo, etc */
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;
/**
@ -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);
/**
* 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.
@ -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);
/**
* 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.
@ -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
* 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
* with `SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(stream));`
* with `SDL_ResumeAudioStreamDevice(stream);`
*
* 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.
*
* \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);

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)
{
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_LockMutex(stream->lock);
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_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;
} 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_TRUE;
@ -533,6 +544,7 @@ static void DestroyPhysicalAudioDevice(SDL_AudioDevice *device)
SDL_DestroyMutex(device->lock);
SDL_DestroyCondition(device->close_cond);
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_free(device);
}
@ -648,7 +660,6 @@ SDL_AudioDevice *SDL_AddAudioDevice(SDL_bool recording, const char *name, const
spec.channels = default_channels;
spec.freq = default_freq;
} 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.channels = (inspec->channels != 0) ? inspec->channels : default_channels;
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;
// 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);
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) {
// 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
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;
}
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
// so the device thread can terminate but also not have two things
// 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.freq = SDL_max(device->default_spec.freq, spec.freq);
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);
SDL_UpdatedAudioDeviceFormat(device); // start this off sane.
@ -1906,6 +1935,7 @@ int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int
if (logdev->postmix) {
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);
@ -2208,7 +2238,8 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device)
}
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;
for (SDL_LogicalAudioDevice *logdev = current_default_device->logical_devices; logdev; 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;
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.
}

View File

@ -124,11 +124,12 @@ static SDL_bool SDL_IsSupportedChannelCount(const int channels)
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++) {
if (map[i] >= ((Uint8) channels)) {
const int mapping = chmap[i];
if ((mapping < 0) || (mapping >= channels)) {
return SDL_TRUE;
}
}
@ -136,11 +137,11 @@ SDL_bool SDL_ChannelMapIsBogus(const Uint8 *map, int channels)
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++) {
if (map[i] != i) {
if (chmap[i] != i) {
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.
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) { \
Uint##bits *tdst = (Uint##bits *) dst; /* treat as UintX; we only care about moving bits and not the type here. */ \
@ -161,16 +162,18 @@ static void SwizzleAudio(const int num_frames, void *dst, const void *src, int c
} \
} \
} else { \
Uint##bits tmp[SDL_MAX_CHANNEL_MAP_SIZE]; \
SDL_zeroa(tmp); \
SDL_assert(SDL_arraysize(tmp) >= channels); \
for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \
for (int ch = 0; ch < channels; ch++) { \
tmp[ch] = tsrc[map[ch]]; \
} \
for (int ch = 0; ch < channels; ch++) { \
tdst[ch] = tmp[ch]; \
SDL_bool isstack; \
Uint##bits *tmp = (Uint##bits *) SDL_small_alloc(int, channels, &isstack); /* !!! FIXME: allocate this when setting the channel map instead. */ \
if (tmp) { \
for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \
for (int ch = 0; ch < channels; ch++) { \
tmp[ch] = tsrc[map[ch]]; \
} \
for (int ch = 0; ch < channels; 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.
// Strictly speaking, this is also a "conversion". :)
void ConvertAudio(int num_frames,
const void *src, SDL_AudioFormat src_format, int src_channels, const Uint8 *src_map,
void *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_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 int *dst_map,
void* scratch, float gain)
{
SDL_assert(src != NULL);
@ -374,9 +377,9 @@ static Sint64 GetAudioStreamResampleRate(SDL_AudioStream* stream, int src_freq,
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;
}
@ -384,6 +387,14 @@ static int UpdateAudioStreamInputSpec(SDL_AudioStream *stream, const SDL_AudioSp
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);
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");
} else if (src_spec->freq > max_freq) {
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");
} else if (dst_spec->freq > max_freq) {
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) {
SDL_copyp(&stream->src_spec, src_spec);
if (src_spec->use_channel_map && SDL_ChannelMapIsDefault(src_spec->channel_map, src_spec->channels)) {
stream->src_spec.use_channel_map = SDL_FALSE; // turn off the channel map, as this is just unnecessary work.
if (src_spec->channels != stream->src_spec.channels) {
SDL_FreeLater(stream->src_chmap); // this pointer is handed to the app during SDL_GetAudioStreamInputChannelMap
stream->src_chmap = NULL;
}
SDL_copyp(&stream->src_spec, src_spec);
}
if (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.
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);
}
// !!! 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);
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)
{
if (!stream) {
@ -676,7 +770,7 @@ static int PutAudioStreamBuffer(SDL_AudioStream *stream, const void *buf, int le
SDL_AudioTrack* track = NULL;
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) {
SDL_UnlockMutex(stream->lock);
@ -691,7 +785,7 @@ static int PutAudioStreamBuffer(SDL_AudioStream *stream, const void *buf, int le
if (track) {
SDL_AddTrackToAudioQueue(stream->queue, track);
} 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) {
@ -782,16 +876,21 @@ static Uint8 *EnsureAudioStreamWorkBufferSize(SDL_AudioStream *stream, size_t ne
}
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_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) {
SDL_copyp(out_spec, &spec);
}
if (out_chmap) {
*out_chmap = chmap;
}
// There is infinite audio available, whether or not we are resampling
if (queued_bytes == SDL_SIZE_MAX) {
*inout_resample_offset = 0;
@ -839,7 +938,7 @@ static Sint64 GetAudioStreamAvailableFrames(SDL_AudioStream* stream, Sint64* out
Sint64 output_frames = 0;
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
if (output_frames >= SDL_MAX_SINT32) {
@ -855,7 +954,7 @@ static Sint64 GetAudioStreamAvailableFrames(SDL_AudioStream* stream, Sint64* out
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);
@ -866,7 +965,7 @@ static Sint64 GetAudioStreamHead(SDL_AudioStream* stream, SDL_AudioSpec* out_spe
}
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!
@ -881,7 +980,7 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
const SDL_AudioFormat dst_format = dst_spec->format;
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 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) {
// Audio is processed a track at a time.
SDL_AudioSpec input_spec;
int *input_chmap;
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 (flushed) {
SDL_PopAudioQueueHead(stream->queue);
SDL_zero(stream->input_spec);
stream->resample_offset = 0;
stream->input_chmap = NULL;
continue;
}
// There are no frames available, but the track hasn't been flushed, so more might be added later.
break;
}
if (UpdateAudioStreamInputSpec(stream, &input_spec) != 0) {
if (UpdateAudioStreamInputSpec(stream, &input_spec, input_chmap) != 0) {
total = total ? total : -1;
break;
}
@ -1160,6 +1261,7 @@ int SDL_ClearAudioStream(SDL_AudioStream *stream)
SDL_ClearAudioQueue(stream->queue);
SDL_zero(stream->input_spec);
stream->input_chmap = NULL;
stream->resample_offset = 0;
SDL_UnlockMutex(stream->lock);

View File

@ -36,6 +36,7 @@ struct SDL_MemoryPool
struct SDL_AudioTrack
{
SDL_AudioSpec spec;
int *chmap;
SDL_bool flushed;
SDL_AudioTrack *next;
@ -46,6 +47,8 @@ struct SDL_AudioTrack
size_t head;
size_t tail;
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
@ -226,7 +229,7 @@ void SDL_PopAudioQueueHead(SDL_AudioQueue *queue)
}
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,
SDL_ReleaseAudioBufferCallback callback, void *userdata)
{
@ -237,6 +240,13 @@ SDL_AudioTrack *SDL_CreateAudioTrack(
}
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);
track->userdata = userdata;
@ -256,7 +266,7 @@ static void SDLCALL FreeChunkedAudioBuffer(void *userdata, const void *buf, int
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);
@ -267,7 +277,7 @@ static SDL_AudioTrack *CreateChunkedAudioTrack(SDL_AudioQueue *queue, const SDL_
size_t capacity = queue->chunk_pool.block_size;
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) {
FreeMemoryPoolBlock(&queue->chunk_pool, chunk);
@ -283,7 +293,7 @@ void SDL_AddTrackToAudioQueue(SDL_AudioQueue *queue, SDL_AudioTrack *track)
if (tail) {
// 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);
}
@ -308,7 +318,7 @@ static size_t WriteToAudioTrack(SDL_AudioTrack *track, const Uint8 *data, size_t
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) {
return 0;
@ -317,12 +327,12 @@ int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, cons
SDL_AudioTrack *track = queue->tail;
if (track) {
if (!SDL_AudioSpecsEqual(&track->spec, spec)) {
if (!SDL_AudioSpecsEqual(&track->spec, spec, track->chmap, chmap)) {
FlushAudioTrack(track);
}
} else {
SDL_assert(!queue->head);
track = CreateChunkedAudioTrack(queue, spec);
track = CreateChunkedAudioTrack(queue, spec, chmap);
if (!track) {
return -1;
@ -333,7 +343,7 @@ int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, cons
}
for (;;) {
size_t written = WriteToAudioTrack(track, data, len);
const size_t written = WriteToAudioTrack(track, data, len);
data += written;
len -= written;
@ -341,7 +351,7 @@ int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, cons
break;
}
SDL_AudioTrack *new_track = CreateChunkedAudioTrack(queue, spec);
SDL_AudioTrack *new_track = CreateChunkedAudioTrack(queue, spec, chmap);
if (!new_track) {
return -1;
@ -360,12 +370,13 @@ void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue)
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_assert(iter != NULL);
SDL_copyp(out_spec, &iter->spec);
*out_chmap = iter->chmap;
SDL_bool flushed = SDL_FALSE;
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,
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,
Uint8 *scratch, float gain)
{
@ -524,7 +535,7 @@ const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue,
SDL_AudioFormat src_format = track->spec.format;
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 dst_frame_size = SDL_AUDIO_BYTESIZE(dst_format) * dst_channels;
@ -597,9 +608,10 @@ size_t SDL_GetAudioQueueQueued(SDL_AudioQueue *queue)
while (iter) {
SDL_AudioSpec src_spec;
int *src_chmap;
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) {
total = SDL_SIZE_MAX;

View File

@ -48,11 +48,11 @@ void SDL_PopAudioQueueHead(SDL_AudioQueue *queue);
// Write data to the end of queue
// 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
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);
// 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
// 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,
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,
Uint8 *scratch, float gain);

View File

@ -48,6 +48,8 @@
#define DEFAULT_AUDIO_RECORDING_CHANNELS 1
#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_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 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_ChannelMapIsBogus(const Uint8 *map, int channels);
extern SDL_bool SDL_ChannelMapIsDefault(const int *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!
extern void ConvertAudio(int num_frames,
const void *src, SDL_AudioFormat src_format, int src_channels, const Uint8 *src_map,
void *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_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 int *dst_map,
void* scratch, float gain);
// 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.
extern SDL_bool SDL_AudioSpecsEqual(const SDL_AudioSpec *a, const SDL_AudioSpec *b);
// Using SDL_memcmp directly isn't safe, since potential padding might not be initialized.
// 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.
extern void OnAudioStreamCreated(SDL_AudioStream *stream);
@ -197,12 +203,16 @@ struct SDL_AudioStream
SDL_AudioSpec src_spec;
SDL_AudioSpec dst_spec;
int *src_chmap;
int *dst_chmap;
float freq_ratio;
float gain;
struct SDL_AudioQueue* queue;
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;
Uint8 *work_buffer; // used for scratch space during data conversion/resampling.
@ -288,6 +298,8 @@ struct SDL_AudioDevice
SDL_AudioSpec spec;
int buffer_size;
int *chmap;
// The device's default audio specification
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
// "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"
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:
// 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
// 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,33 +533,41 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device)
device->spec.channels = channels;
}
// Validate number of channels and determine if swizzling is necessary.
// Assume original swizzling, until proven otherwise.
const int *swizmap = NULL;
if (channels == 6) {
device->spec.use_channel_map = SDL_TRUE;
SDL_memcpy(device->spec.channel_map, swizzle_alsa_channels_6, sizeof (device->spec.channel_map[0]) * channels);
swizmap = swizzle_alsa_channels_6;
} else if (channels == 8) {
device->spec.use_channel_map = SDL_TRUE;
SDL_memcpy(device->spec.channel_map, swizzle_alsa_channels_8, sizeof (device->spec.channel_map[0]) * channels);
swizmap = swizzle_alsa_channels_8;
}
#ifdef SND_CHMAP_API_VERSION
snd_pcm_chmap_t *chmap = ALSA_snd_pcm_get_chmap(pcm_handle);
if (chmap) {
char chmap_str[64];
if (ALSA_snd_pcm_chmap_print(chmap, sizeof(chmap_str), chmap_str) > 0) {
if ( (channels == 6) &&
((SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0) ||
(SDL_strcmp("FL FR FC LFE SL SR", chmap_str) == 0)) ) {
device->spec.use_channel_map = SDL_FALSE;
} else if ((channels == 8) && (SDL_strcmp("FL FR FC LFE SL SR RL RR", chmap_str) == 0)) {
device->spec.use_channel_map = SDL_FALSE;
if (swizmap) {
snd_pcm_chmap_t *chmap = ALSA_snd_pcm_get_chmap(pcm_handle);
if (chmap) {
char chmap_str[64];
if (ALSA_snd_pcm_chmap_print(chmap, sizeof(chmap_str), chmap_str) > 0) {
if ( (channels == 6) &&
((SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0) ||
(SDL_strcmp("FL FR FC LFE SL SR", chmap_str) == 0)) ) {
swizmap = NULL;
} else if ((channels == 8) && (SDL_strcmp("FL FR FC LFE SL SR RL RR", chmap_str) == 0)) {
swizmap = NULL;
}
}
free(chmap); // This should NOT be SDL_free()
}
free(chmap); // This should NOT be SDL_free()
}
#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
unsigned int rate = device->spec.freq;
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_GetAssertionHandler;
SDL_GetAssertionReport;
SDL_GetAudioDeviceChannelMap;
SDL_GetAudioDeviceFormat;
SDL_GetAudioDeviceGain;
SDL_GetAudioDeviceName;
@ -177,6 +178,8 @@ SDL3_0.0.0 {
SDL_GetAudioStreamFormat;
SDL_GetAudioStreamFrequencyRatio;
SDL_GetAudioStreamGain;
SDL_GetAudioStreamInputChannelMap;
SDL_GetAudioStreamOutputChannelMap;
SDL_GetAudioStreamProperties;
SDL_GetAudioStreamQueued;
SDL_GetBasePath;
@ -693,6 +696,8 @@ SDL3_0.0.0 {
SDL_SetAudioStreamFrequencyRatio;
SDL_SetAudioStreamGain;
SDL_SetAudioStreamGetCallback;
SDL_SetAudioStreamInputChannelMap;
SDL_SetAudioStreamOutputChannelMap;
SDL_SetAudioStreamPutCallback;
SDL_SetBooleanProperty;
SDL_SetClipboardData;

View File

@ -189,6 +189,7 @@
#define SDL_GetAndroidSDKVersion SDL_GetAndroidSDKVersion_REAL
#define SDL_GetAssertionHandler SDL_GetAssertionHandler_REAL
#define SDL_GetAssertionReport SDL_GetAssertionReport_REAL
#define SDL_GetAudioDeviceChannelMap SDL_GetAudioDeviceChannelMap_REAL
#define SDL_GetAudioDeviceFormat SDL_GetAudioDeviceFormat_REAL
#define SDL_GetAudioDeviceGain SDL_GetAudioDeviceGain_REAL
#define SDL_GetAudioDeviceName SDL_GetAudioDeviceName_REAL
@ -201,6 +202,8 @@
#define SDL_GetAudioStreamFormat SDL_GetAudioStreamFormat_REAL
#define SDL_GetAudioStreamFrequencyRatio SDL_GetAudioStreamFrequencyRatio_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_GetAudioStreamQueued SDL_GetAudioStreamQueued_REAL
#define SDL_GetBasePath SDL_GetBasePath_REAL
@ -717,6 +720,8 @@
#define SDL_SetAudioStreamFrequencyRatio SDL_SetAudioStreamFrequencyRatio_REAL
#define SDL_SetAudioStreamGain SDL_SetAudioStreamGain_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_SetBooleanProperty SDL_SetBooleanProperty_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(SDL_AssertionHandler,SDL_GetAssertionHandler,(void **a),(a),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(float,SDL_GetAudioDeviceGain,(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(float,SDL_GetAudioStreamFrequencyRatio,(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(int,SDL_GetAudioStreamQueued,(SDL_AudioStream *a),(a),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_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_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_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)

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());
return SDL_APP_FAILURE;
}
SDL_ResumeAudioStreamDevice(stream);
return SDL_APP_CONTINUE;

View File

@ -1167,7 +1167,7 @@ static AVCodecContext *OpenAudioStream(AVFormatContext *ic, int stream, const AV
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);
if (audio) {
SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(audio));
@ -1240,7 +1240,7 @@ static void InterleaveAudio(AVFrame *frame, const SDL_AudioSpec *spec)
static void HandleAudioFrame(AVFrame *frame)
{
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);
if (frame->ch_layout.nb_channels > 1 && IsPlanarAudioFormat(frame->format)) {