audio: Add channel remapping to SDL_AudioSpec and SDL_AudioStream.

Fixes #8367.
This commit is contained in:
Ryan C. Gordon 2024-07-03 03:19:00 -04:00
parent 0367f1af19
commit 16e7fdc4f2
12 changed files with 254 additions and 193 deletions

View file

@ -43,20 +43,41 @@
* 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.
* *
* For multi-channel audio, data is interleaved (one sample for each channel, * ## Channel layouts as SDL expects them
* then repeat). The SDL channel order is:
* *
* - Stereo: FL, FR * Abbreviations:
* - 2.1 surround: FL, FR, LFE *
* - Quad: FL, FR, BL, BR * - FRONT = single mono speaker
* - 4.1 surround: FL, FR, LFE, BL, BR * - FL = front left speaker
* - 5.1 surround: FL, FR, FC, LFE, SL, SR (last two can also be BL BR) * - FR = front right speaker
* - 6.1 surround: FL, FR, FC, LFE, BC, SL, SR * - FC = front center speaker
* - 7.1 surround: FL, FR, FC, LFE, BL, BR, SL, SR * - BL = back left speaker
* - BR = back right speaker
* - SR = surround right speaker
* - SL = surround left speaker
* - BC = back center speaker
* - LFE = low-frequency speaker
*
* These are listed in the order they are laid out in
* memory, so "FL, FR" means "the front left speaker is
* laid out in memory first, then the front right, then
* it repeats for the next audio frame".
*
* - 1 channel (mono) layout: FRONT
* - 2 channels (stereo) layout: FL, FR
* - 3 channels (2.1) layout: FL, FR, LFE
* - 4 channels (quad) layout: FL, FR, BL, BR
* - 5 channels (4.1) layout: FL, FR, LFE, BL, BR
* - 6 channels (5.1) layout: FL, FR, FC, LFE, BL, BR (last two can also be BL, BR)
* - 7 channels (6.1) layout: FL, FR, FC, LFE, BC, SL, SR
* - 8 channels (7.1) layout: FL, FR, FC, LFE, BL, BR, SL, SR
* *
* This is the same order as DirectSound expects, but applied to all * This is the same order as DirectSound expects, but applied to all
* 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
* to whatever is necessary, in other audio processing scenarios.
*/ */
#ifndef SDL_audio_h_ #ifndef SDL_audio_h_
@ -280,6 +301,18 @@ 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.
* *
@ -292,6 +325,8 @@ 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;
/** /**
@ -318,6 +353,7 @@ typedef struct SDL_AudioSpec
* when it doesn't have the complete buffer available. * when it doesn't have the complete buffer available.
* - It can handle incoming data in any variable size. * - It can handle incoming data in any variable size.
* - It can handle input/output format changes on the fly. * - It can handle input/output format changes on the fly.
* - It can remap audio channels between inputs and outputs.
* - You push data as you have it, and pull it when you need it * - You push data as you have it, and pull it when you need it
* - It can also function as a basic audio data queue even if you just have * - It can also function as a basic audio data queue even if you just have
* sound that needs to pass from one place to another. * sound that needs to pass from one place to another.

View file

@ -249,6 +249,16 @@ static void UpdateAudioStreamFormatsPhysical(SDL_AudioDevice *device)
} }
} }
SDL_bool SDL_AudioSpecsEqual(const SDL_AudioSpec *a, const SDL_AudioSpec *b)
{
if ((a->format != b->format) || (a->channels != b->channels) || (a->freq != b->freq) || (a->use_channel_map != b->use_channel_map)) {
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)) {
return SDL_FALSE;
}
return SDL_TRUE;
}
// Zombie device implementation... // Zombie device implementation...
@ -632,11 +642,13 @@ SDL_AudioDevice *SDL_AddAudioDevice(SDL_bool recording, const char *name, const
const int default_freq = recording ? DEFAULT_AUDIO_RECORDING_FREQUENCY : DEFAULT_AUDIO_PLAYBACK_FREQUENCY; const int default_freq = recording ? DEFAULT_AUDIO_RECORDING_FREQUENCY : DEFAULT_AUDIO_PLAYBACK_FREQUENCY;
SDL_AudioSpec spec; SDL_AudioSpec spec;
SDL_zero(spec);
if (!inspec) { if (!inspec) {
spec.format = default_format; spec.format = default_format;
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;
@ -1089,7 +1101,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(AUDIO_SPECS_EQUAL(stream->dst_spec, device->spec)); SDL_assert(SDL_AudioSpecsEqual(&stream->dst_spec, &device->spec));
const int br = SDL_AtomicGet(&logdev->paused) ? 0 : SDL_GetAudioStreamData(stream, device_buffer, buffer_size); const int br = SDL_AtomicGet(&logdev->paused) ? 0 : SDL_GetAudioStreamData(stream, device_buffer, buffer_size);
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.
@ -1106,9 +1118,8 @@ SDL_bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device)
SDL_assert(work_buffer_size <= device->work_buffer_size); SDL_assert(work_buffer_size <= device->work_buffer_size);
SDL_copyp(&outspec, &device->spec);
outspec.format = SDL_AUDIO_F32; outspec.format = SDL_AUDIO_F32;
outspec.channels = device->spec.channels;
outspec.freq = device->spec.freq;
SDL_memset(final_mix_buffer, '\0', work_buffer_size); // start with silence. SDL_memset(final_mix_buffer, '\0', work_buffer_size); // start with silence.
@ -1126,7 +1137,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(AUDIO_SPECS_EQUAL(stream->dst_spec, outspec)); SDL_assert(SDL_AudioSpecsEqual(&stream->dst_spec, &outspec));
/* 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.
@ -1150,8 +1161,8 @@ SDL_bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device)
if (((Uint8 *) final_mix_buffer) != device_buffer) { if (((Uint8 *) final_mix_buffer) != device_buffer) {
// !!! FIXME: we can't promise the device buf is aligned/padded for SIMD. // !!! FIXME: we can't promise the device buf is aligned/padded for SIMD.
//ConvertAudio(needed_samples * device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, device_buffer, device->spec.format, device->spec.channels, device->work_buffer); //ConvertAudio(needed_samples / device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, NULL, device_buffer, device->spec.format, device->spec.channels, NULL, NULL);
ConvertAudio(needed_samples / device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, device->work_buffer, device->spec.format, device->spec.channels, NULL); ConvertAudio(needed_samples / device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, NULL, device->work_buffer, device->spec.format, device->spec.channels, NULL, NULL);
SDL_memcpy(device_buffer, device->work_buffer, buffer_size); SDL_memcpy(device_buffer, device->work_buffer, buffer_size);
} }
} }
@ -1242,13 +1253,12 @@ SDL_bool SDL_RecordingAudioThreadIterate(SDL_AudioDevice *device)
if (logdev->postmix) { if (logdev->postmix) {
// move to float format. // move to float format.
SDL_AudioSpec outspec; SDL_AudioSpec outspec;
SDL_copyp(&outspec, &device->spec);
outspec.format = SDL_AUDIO_F32; outspec.format = SDL_AUDIO_F32;
outspec.channels = device->spec.channels;
outspec.freq = device->spec.freq;
output_buffer = device->postmix_buffer; output_buffer = device->postmix_buffer;
const int frames = br / SDL_AUDIO_FRAMESIZE(device->spec); const int frames = br / SDL_AUDIO_FRAMESIZE(device->spec);
br = frames * SDL_AUDIO_FRAMESIZE(outspec); br = frames * SDL_AUDIO_FRAMESIZE(outspec);
ConvertAudio(frames, device->work_buffer, device->spec.format, outspec.channels, device->postmix_buffer, SDL_AUDIO_F32, outspec.channels, NULL); ConvertAudio(frames, device->work_buffer, device->spec.format, outspec.channels, NULL, device->postmix_buffer, SDL_AUDIO_F32, outspec.channels, NULL, NULL);
logdev->postmix(logdev->postmix_userdata, &outspec, device->postmix_buffer, br); logdev->postmix(logdev->postmix_userdata, &outspec, device->postmix_buffer, br);
} }
@ -1606,6 +1616,7 @@ 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.
@ -2155,7 +2166,7 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device)
} }
if (needs_migration) { if (needs_migration) {
const SDL_bool spec_changed = !AUDIO_SPECS_EQUAL(current_default_device->spec, new_default_device->spec); const SDL_bool spec_changed = !SDL_AudioSpecsEqual(&current_default_device->spec, &new_default_device->spec);
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;
@ -2235,7 +2246,7 @@ 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 (AUDIO_SPECS_EQUAL(device->spec, *newspec) && new_sample_frames == device->sample_frames) { if (SDL_AudioSpecsEqual(&device->spec, newspec) && (new_sample_frames == device->sample_frames)) {
return 0; // we're already in that format. return 0; // we're already in that format.
} }

View file

@ -29,39 +29,6 @@
#define SDL_INT_MAX ((int)(~0u>>1)) #define SDL_INT_MAX ((int)(~0u>>1))
#endif #endif
/*
* CHANNEL LAYOUTS AS SDL EXPECTS THEM:
*
* (Even if the platform expects something else later, that
* SDL will swizzle between the app and the platform).
*
* Abbreviations:
* - FRONT=single mono speaker
* - FL=front left speaker
* - FR=front right speaker
* - FC=front center speaker
* - BL=back left speaker
* - BR=back right speaker
* - SR=surround right speaker
* - SL=surround left speaker
* - BC=back center speaker
* - LFE=low-frequency speaker
*
* These are listed in the order they are laid out in
* memory, so "FL+FR" means "the front left speaker is
* laid out in memory first, then the front right, then
* it repeats for the next audio frame".
*
* 1 channel (mono) layout: FRONT
* 2 channels (stereo) layout: FL+FR
* 3 channels (2.1) layout: FL+FR+LFE
* 4 channels (quad) layout: FL+FR+BL+BR
* 5 channels (4.1) layout: FL+FR+LFE+BL+BR
* 6 channels (5.1) layout: FL+FR+FC+LFE+BL+BR
* 7 channels (6.1) layout: FL+FR+FC+LFE+BC+SL+SR
* 8 channels (7.1) layout: FL+FR+FC+LFE+BL+BR+SL+SR
*/
#ifdef SDL_SSE3_INTRINSICS #ifdef SDL_SSE3_INTRINSICS
// Convert from stereo to mono. Average left and right. // Convert from stereo to mono. Average left and right.
static void SDL_TARGETING("sse3") SDL_ConvertStereoToMono_SSE3(float *dst, const float *src, int num_frames) static void SDL_TARGETING("sse3") SDL_ConvertStereoToMono_SSE3(float *dst, const float *src, int num_frames)
@ -157,6 +124,68 @@ 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)
{
if (map) {
for (int i = 0; i < channels; i++) {
if (map[i] >= ((Uint8) channels)) {
return SDL_TRUE;
}
}
}
return SDL_FALSE;
}
SDL_bool SDL_ChannelMapIsDefault(const Uint8 *map, int channels)
{
if (map) {
for (int i = 0; i < channels; i++) {
if (map[i] != i) {
return SDL_FALSE;
}
}
}
return SDL_TRUE;
}
// 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)
{
#define CHANNEL_SWIZZLE(bits) { \
Uint##bits *tdst = (Uint##bits *) dst; /* treat as UintX; we only care about moving bits and not the type here. */ \
const Uint##bits *tsrc = (const Uint##bits *) src; \
if (src != dst) { /* don't need to copy to a temporary frame first. */ \
for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \
for (int ch = 0; ch < channels; ch++) { \
tdst[ch] = tsrc[map[ch]]; \
} \
} \
} else { \
Uint##bits tmp[SDL_MAX_CHANNEL_MAP_SIZE]; \
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]; \
} \
} \
} \
}
switch (bitsize) {
case 8: CHANNEL_SWIZZLE(8); break;
case 16: CHANNEL_SWIZZLE(16); break;
case 32: CHANNEL_SWIZZLE(32); break;
// we don't currently have int64 or double audio datatypes, so no `case 64` for now.
default: SDL_assert(!"Unsupported audio datatype size"); break;
}
#undef CHANNEL_SWIZZLE
}
// This does type and channel conversions _but not resampling_ (resampling happens in SDL_AudioStream). // This does type and channel conversions _but not resampling_ (resampling happens in SDL_AudioStream).
// This does not check parameter validity, (beyond asserts), it expects you did that already! // This does not check parameter validity, (beyond asserts), it expects you did that already!
// All of this has to function as if src==dst==scratch (conversion in-place), but as a convenience // All of this has to function as if src==dst==scratch (conversion in-place), but as a convenience
@ -164,8 +193,10 @@ static SDL_bool SDL_IsSupportedChannelCount(const int channels)
// //
// The scratch buffer must be able to store `num_frames * CalculateMaxSampleFrameSize(src_format, src_channels, dst_format, dst_channels)` bytes. // The scratch buffer must be able to store `num_frames * CalculateMaxSampleFrameSize(src_format, src_channels, dst_format, dst_channels)` bytes.
// If the scratch buffer is NULL, this restriction applies to the output buffer instead. // If the scratch buffer is NULL, this restriction applies to the output buffer instead.
void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_format, int src_channels, void ConvertAudio(int num_frames,
void *dst, SDL_AudioFormat dst_format, int dst_channels, void* scratch) 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,
void* scratch)
{ {
SDL_assert(src != NULL); SDL_assert(src != NULL);
SDL_assert(dst != NULL); SDL_assert(dst != NULL);
@ -188,11 +219,13 @@ void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_format, i
const int dst_sample_frame_size = (dst_bitsize / 8) * dst_channels; const int dst_sample_frame_size = (dst_bitsize / 8) * dst_channels;
/* Type conversion goes like this now: /* Type conversion goes like this now:
- swizzle through source channel map to "standard" layout.
- byteswap to CPU native format first if necessary. - byteswap to CPU native format first if necessary.
- convert to native Float32 if necessary. - convert to native Float32 if necessary.
- change channel count if necessary. - change channel count if necessary.
- convert to final data format. - convert to final data format.
- byteswap back to foreign format if necessary. - byteswap back to foreign format if necessary.
- swizzle through dest channel map from "standard" layout.
The expectation is we can process data faster in float32 The expectation is we can process data faster in float32
(possibly with SIMD), and making several passes over the same (possibly with SIMD), and making several passes over the same
@ -201,11 +234,20 @@ void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_format, i
(script-generated) custom converters for every data type and (script-generated) custom converters for every data type and
it was a bloat on SDL compile times and final library size. */ it was a bloat on SDL compile times and final library size. */
// swizzle input to "standard" format if necessary.
if (src_map) {
void* buf = scratch ? scratch : dst; // use scratch if available, since it has to be big enough to hold src, unless it's NULL, then dst has to be.
SwizzleAudio(num_frames, buf, src, src_channels, src_map, src_bitsize);
src = buf;
}
// see if we can skip float conversion entirely. // see if we can skip float conversion entirely.
if (src_channels == dst_channels) { if (src_channels == dst_channels) {
if (src_format == dst_format) { if (src_format == dst_format) {
// nothing to do, we're already in the right format, just copy it over if necessary. // nothing to do, we're already in the right format, just copy it over if necessary.
if (src != dst) { if (dst_map) {
SwizzleAudio(num_frames, dst, src, dst_channels, dst_map, dst_bitsize);
} else if (src != dst) {
SDL_memcpy(dst, src, num_frames * dst_sample_frame_size); SDL_memcpy(dst, src, num_frames * dst_sample_frame_size);
} }
return; return;
@ -213,7 +255,11 @@ void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_format, i
// just a byteswap needed? // just a byteswap needed?
if ((src_format ^ dst_format) == SDL_AUDIO_MASK_BIG_ENDIAN) { if ((src_format ^ dst_format) == SDL_AUDIO_MASK_BIG_ENDIAN) {
ConvertAudioSwapEndian(dst, src, num_frames * src_channels, src_bitsize); if (dst_map) { // do this first, in case we duplicate channels, we can avoid an extra copy if src != dst.
SwizzleAudio(num_frames, dst, src, dst_channels, dst_map, dst_bitsize);
src = dst;
}
ConvertAudioSwapEndian(dst, src, num_frames * dst_channels, dst_bitsize);
return; // all done. return; // all done.
} }
} }
@ -275,6 +321,10 @@ void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_format, i
} }
SDL_assert(src == dst); // if we got here, we _had_ to have done _something_. Otherwise, we should have memcpy'd! SDL_assert(src == dst); // if we got here, we _had_ to have done _something_. Otherwise, we should have memcpy'd!
if (dst_map) {
SwizzleAudio(num_frames, dst, src, dst_channels, dst_map, dst_bitsize);
}
} }
// Calculate the largest frame size needed to convert between the two formats. // Calculate the largest frame size needed to convert between the two formats.
@ -304,7 +354,7 @@ static Sint64 GetAudioStreamResampleRate(SDL_AudioStream* stream, int src_freq,
static int UpdateAudioStreamInputSpec(SDL_AudioStream *stream, const SDL_AudioSpec *spec) static int UpdateAudioStreamInputSpec(SDL_AudioStream *stream, const SDL_AudioSpec *spec)
{ {
if (AUDIO_SPECS_EQUAL(stream->input_spec, *spec)) { if (SDL_AudioSpecsEqual(&stream->input_spec, spec)) {
return 0; return 0;
} }
@ -451,6 +501,8 @@ 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");
} }
} }
@ -465,6 +517,8 @@ 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");
} }
} }
@ -481,12 +535,21 @@ int SDL_SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_s
if (src_spec) { if (src_spec) {
SDL_copyp(&stream->src_spec, 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 (dst_spec) { if (dst_spec) {
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;
@ -766,6 +829,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 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);
@ -789,7 +853,7 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
} }
} }
if (SDL_ReadFromAudioQueue(stream->queue, buf, dst_format, dst_channels, 0, output_frames, 0, work_buffer) != buf) { if (SDL_ReadFromAudioQueue(stream->queue, buf, dst_format, dst_channels, dst_map, 0, output_frames, 0, work_buffer) != buf) {
return SDL_SetError("Not enough data in queue"); return SDL_SetError("Not enough data in queue");
} }
@ -854,8 +918,9 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
return -1; return -1;
} }
// (dst channel map is NULL because we'll do the final swizzle on ConvertAudio after resample.)
const Uint8* input_buffer = SDL_ReadFromAudioQueue(stream->queue, const Uint8* input_buffer = SDL_ReadFromAudioQueue(stream->queue,
NULL, resample_format, resample_channels, NULL, resample_format, resample_channels, NULL,
padding_frames, input_frames, padding_frames, work_buffer); padding_frames, input_frames, padding_frames, work_buffer);
if (!input_buffer) { if (!input_buffer) {
@ -872,8 +937,8 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
(float*) resample_buffer, output_frames, (float*) resample_buffer, output_frames,
resample_rate, &stream->resample_offset); resample_rate, &stream->resample_offset);
// Convert to the final format, if necessary // Convert to the final format, if necessary (src channel map is NULL because SDL_ReadFromAudioQueue already handled this).
ConvertAudio(output_frames, resample_buffer, resample_format, resample_channels, buf, dst_format, dst_channels, work_buffer); ConvertAudio(output_frames, resample_buffer, resample_format, resample_channels, NULL, buf, dst_format, dst_channels, dst_map, work_buffer);
return 0; return 0;
} }

View file

@ -23,8 +23,6 @@
#include "SDL_audioqueue.h" #include "SDL_audioqueue.h"
#include "SDL_sysaudio.h" #include "SDL_sysaudio.h"
#define AUDIO_SPECS_EQUAL(x, y) (((x).format == (y).format) && ((x).channels == (y).channels) && ((x).freq == (y).freq))
typedef struct SDL_MemoryPool SDL_MemoryPool; typedef struct SDL_MemoryPool SDL_MemoryPool;
struct SDL_MemoryPool struct SDL_MemoryPool
@ -285,7 +283,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 (!AUDIO_SPECS_EQUAL(tail->spec, track->spec)) { if (!SDL_AudioSpecsEqual(&tail->spec, &track->spec)) {
FlushAudioTrack(tail); FlushAudioTrack(tail);
} }
@ -319,7 +317,7 @@ 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 (!AUDIO_SPECS_EQUAL(track->spec, *spec)) { if (!SDL_AudioSpecsEqual(&track->spec, spec)) {
FlushAudioTrack(track); FlushAudioTrack(track);
} }
} else { } else {
@ -514,7 +512,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, Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_map,
int past_frames, int present_frames, int future_frames, int past_frames, int present_frames, int future_frames,
Uint8 *scratch) Uint8 *scratch)
{ {
@ -526,6 +524,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;
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;
@ -553,7 +552,7 @@ const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue,
// Do we still need to copy/convert the data? // Do we still need to copy/convert the data?
if (dst) { if (dst) {
ConvertAudio(past_frames + present_frames + future_frames, ptr, ConvertAudio(past_frames + present_frames + future_frames, ptr,
src_format, src_channels, dst, dst_format, dst_channels, scratch); src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch);
ptr = dst; ptr = dst;
} }
@ -571,19 +570,19 @@ const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue,
Uint8 *ptr = dst; Uint8 *ptr = dst;
if (src_past_bytes) { if (src_past_bytes) {
ConvertAudio(past_frames, PeekIntoAudioQueuePast(queue, scratch, src_past_bytes), src_format, src_channels, dst, dst_format, dst_channels, scratch); ConvertAudio(past_frames, PeekIntoAudioQueuePast(queue, scratch, src_past_bytes), src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch);
dst += dst_past_bytes; dst += dst_past_bytes;
scratch += dst_past_bytes; scratch += dst_past_bytes;
} }
if (src_present_bytes) { if (src_present_bytes) {
ConvertAudio(present_frames, ReadFromAudioQueue(queue, scratch, src_present_bytes), src_format, src_channels, dst, dst_format, dst_channels, scratch); ConvertAudio(present_frames, ReadFromAudioQueue(queue, scratch, src_present_bytes), src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch);
dst += dst_present_bytes; dst += dst_present_bytes;
scratch += dst_present_bytes; scratch += dst_present_bytes;
} }
if (src_future_bytes) { if (src_future_bytes) {
ConvertAudio(future_frames, PeekIntoAudioQueueFuture(queue, scratch, src_future_bytes), src_format, src_channels, dst, dst_format, dst_channels, scratch); ConvertAudio(future_frames, PeekIntoAudioQueueFuture(queue, scratch, src_future_bytes), src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch);
dst += dst_future_bytes; dst += dst_future_bytes;
scratch += dst_future_bytes; scratch += dst_future_bytes;
} }

View file

@ -67,7 +67,7 @@ void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue);
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, 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, Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_map,
int past_frames, int present_frames, int future_frames, int past_frames, int present_frames, int future_frames,
Uint8 *scratch); Uint8 *scratch);

View file

@ -48,8 +48,6 @@
#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 AUDIO_SPECS_EQUAL(x, y) (((x).format == (y).format) && ((x).channels == (y).channels) && ((x).freq == (y).freq))
typedef struct SDL_AudioDevice SDL_AudioDevice; typedef struct SDL_AudioDevice SDL_AudioDevice;
typedef struct SDL_LogicalAudioDevice SDL_LogicalAudioDevice; typedef struct SDL_LogicalAudioDevice SDL_LogicalAudioDevice;
@ -113,9 +111,19 @@ 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_ChannelMapIsBogus(const Uint8 *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, const void *src, SDL_AudioFormat src_format, int src_channels, extern void ConvertAudio(int num_frames,
void *dst, SDL_AudioFormat dst_format, int dst_channels, void* scratch); 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,
void* scratch);
// 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);
// 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);

View file

@ -242,108 +242,22 @@ static const char *get_audio_device(void *handle, const int channels)
return dev->name; return dev->name;
} }
// !!! FIXME: is there a channel swizzler in alsalib instead? // Swizzle channels to match SDL defaults.
// These are swizzles _from_ SDL's layouts to what ALSA wants.
// 5.1 swizzle:
// 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"
#define SWIZ6(T) \ static const Uint8 swizzle_alsa_channels_6[6] = { 0, 1, 4, 5, 2, 3 };
static void swizzle_alsa_channels_6_##T(void *buffer, const Uint32 bufferlen) \
{ \
T *ptr = (T *)buffer; \
Uint32 i; \
for (i = 0; i < bufferlen; i++, ptr += 6) { \
T tmp; \
tmp = ptr[2]; \
ptr[2] = ptr[4]; \
ptr[4] = tmp; \
tmp = ptr[3]; \
ptr[3] = ptr[5]; \
ptr[5] = tmp; \
} \
}
// !!! FIXME: is there a channel swizzler in alsalib instead?
// !!! FIXME: this screams for a SIMD shuffle operation.
// 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"
#define SWIZ8(T) \ static const Uint8 swizzle_alsa_channels_8[8] = { 0, 1, 6, 7, 2, 3, 4, 5 };
static void swizzle_alsa_channels_8_##T(void *buffer, const Uint32 bufferlen) \
{ \
T *ptr = (T *)buffer; \
Uint32 i; \
for (i = 0; i < bufferlen; i++, ptr += 6) { \
const T center = ptr[2]; \
const T subwoofer = ptr[3]; \
const T side_left = ptr[4]; \
const T side_right = ptr[5]; \
const T rear_left = ptr[6]; \
const T rear_right = ptr[7]; \
ptr[2] = rear_left; \
ptr[3] = rear_right; \
ptr[4] = center; \
ptr[5] = subwoofer; \
ptr[6] = side_left; \
ptr[7] = side_right; \
} \
}
#define CHANNEL_SWIZZLE(x) \
x(Uint64) \
x(Uint32) \
x(Uint16) \
x(Uint8)
CHANNEL_SWIZZLE(SWIZ6)
CHANNEL_SWIZZLE(SWIZ8)
#undef CHANNEL_SWIZZLE
#undef SWIZ6
#undef SWIZ8
// Called right before feeding device->hidden->mixbuf to the hardware. Swizzle
// channels from Windows/Mac order to the format alsalib will want.
static void swizzle_alsa_channels(SDL_AudioDevice *device, void *buffer, Uint32 bufferlen)
{
switch (device->spec.channels) {
#define CHANSWIZ(chans) \
case chans: \
switch ((device->spec.format & (0xFF))) { \
case 8: \
swizzle_alsa_channels_##chans##_Uint8(buffer, bufferlen); \
break; \
case 16: \
swizzle_alsa_channels_##chans##_Uint16(buffer, bufferlen); \
break; \
case 32: \
swizzle_alsa_channels_##chans##_Uint32(buffer, bufferlen); \
break; \
case 64: \
swizzle_alsa_channels_##chans##_Uint64(buffer, bufferlen); \
break; \
default: \
SDL_assert(!"unhandled bitsize"); \
break; \
} \
return;
CHANSWIZ(6);
CHANSWIZ(8);
#undef CHANSWIZ
default:
break;
}
}
#ifdef SND_CHMAP_API_VERSION
// Some devices have the right channel map, no swizzling necessary
static void no_swizzle(SDL_AudioDevice *device, void *buffer, Uint32 bufferlen)
{
}
#endif // SND_CHMAP_API_VERSION
// This function waits until it is possible to write a full sound buffer // This function waits until it is possible to write a full sound buffer
static int ALSA_WaitDevice(SDL_AudioDevice *device) static int ALSA_WaitDevice(SDL_AudioDevice *device)
@ -380,8 +294,6 @@ static int ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buf
const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec); const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec);
snd_pcm_uframes_t frames_left = (snd_pcm_uframes_t) (buflen / frame_size); snd_pcm_uframes_t frames_left = (snd_pcm_uframes_t) (buflen / frame_size);
device->hidden->swizzle_func(device, sample_buf, frames_left);
while ((frames_left > 0) && !SDL_AtomicGet(&device->shutdown)) { while ((frames_left > 0) && !SDL_AtomicGet(&device->shutdown)) {
const int rc = ALSA_snd_pcm_writei(device->hidden->pcm_handle, sample_buf, frames_left); const int rc = ALSA_snd_pcm_writei(device->hidden->pcm_handle, sample_buf, frames_left);
//SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA PLAYDEVICE: WROTE %d of %d bytes", (rc >= 0) ? ((int) (rc * frame_size)) : rc, (int) (frames_left * frame_size)); //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA PLAYDEVICE: WROTE %d of %d bytes", (rc >= 0) ? ((int) (rc * frame_size)) : rc, (int) (frames_left * frame_size));
@ -447,8 +359,6 @@ static int ALSA_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen)
return -1; return -1;
} }
return 0; // go back to WaitDevice and try again. return 0; // go back to WaitDevice and try again.
} else if (rc > 0) {
device->hidden->swizzle_func(device, buffer, total_frames - rc);
} }
//SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA: recorded %d bytes", rc * frame_size); //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA: recorded %d bytes", rc * frame_size);
@ -611,23 +521,6 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device)
} }
device->spec.format = test_format; device->spec.format = test_format;
// Validate number of channels and determine if swizzling is necessary.
// Assume original swizzling, until proven otherwise.
device->hidden->swizzle_func = swizzle_alsa_channels;
#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 (SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0 ||
SDL_strcmp("FL FR FC LFE SL SR", chmap_str) == 0) {
device->hidden->swizzle_func = no_swizzle;
}
}
free(chmap); // This should NOT be SDL_free()
}
#endif // SND_CHMAP_API_VERSION
// Set the number of channels // Set the number of channels
status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams, status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
device->spec.channels); device->spec.channels);
@ -640,6 +533,33 @@ 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.
// Assume original swizzling, until proven otherwise.
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);
} 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);
}
#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;
}
}
free(chmap); // This should NOT be SDL_free()
}
#endif // SND_CHMAP_API_VERSION
// 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

@ -789,6 +789,7 @@ static SDL_AudioFormat PulseFormatToSDLFormat(pa_sample_format_t format)
static void AddPulseAudioDevice(const SDL_bool recording, const char *description, const char *name, const uint32_t index, const pa_sample_spec *sample_spec) static void AddPulseAudioDevice(const SDL_bool recording, const char *description, const char *name, const uint32_t index, const pa_sample_spec *sample_spec)
{ {
SDL_AudioSpec spec; SDL_AudioSpec spec;
SDL_zero(spec);
spec.format = PulseFormatToSDLFormat(sample_spec->format); spec.format = PulseFormatToSDLFormat(sample_spec->format);
spec.channels = sample_spec->channels; spec.channels = sample_spec->channels;
spec.freq = sample_spec->rate; spec.freq = sample_spec->rate;

View file

@ -363,6 +363,7 @@ static void QSA_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevic
if (status == EOK) { if (status == EOK) {
SDL_AudioSpec spec; SDL_AudioSpec spec;
SDL_zero(spec);
SDL_AudioSpec *pspec = &spec; SDL_AudioSpec *pspec = &spec;
snd_pcm_channel_setup_t csetup; snd_pcm_channel_setup_t csetup;
SDL_zero(csetup); SDL_zero(csetup);

View file

@ -109,6 +109,7 @@ static void queue_audio()
int retval = 0; int retval = 0;
SDL_AudioSpec new_spec; SDL_AudioSpec new_spec;
SDL_zero(new_spec);
new_spec.format = spec.format; new_spec.format = spec.format;
new_spec.channels = (int) sliders[2].value; new_spec.channels = (int) sliders[2].value;
new_spec.freq = (int) sliders[1].value; new_spec.freq = (int) sliders[1].value;

View file

@ -181,7 +181,7 @@ static int audio_initOpenCloseQuitAudio(void *arg)
SDLTest_AssertCheck(result == 0, "Validate result value; expected: 0 got: %d", result); SDLTest_AssertCheck(result == 0, "Validate result value; expected: 0 got: %d", result);
/* Set spec */ /* Set spec */
SDL_memset(&desired, 0, sizeof(desired)); SDL_zero(desired);
switch (j) { switch (j) {
case 0: case 0:
/* Set standard desired spec */ /* Set standard desired spec */
@ -272,7 +272,7 @@ static int audio_pauseUnpauseAudio(void *arg)
SDLTest_AssertCheck(result == 0, "Validate result value; expected: 0 got: %d", result); SDLTest_AssertCheck(result == 0, "Validate result value; expected: 0 got: %d", result);
/* Set spec */ /* Set spec */
SDL_memset(&desired, 0, sizeof(desired)); SDL_zero(desired);
switch (j) { switch (j) {
case 0: case 0:
/* Set standard desired spec */ /* Set standard desired spec */
@ -496,6 +496,9 @@ static int audio_buildAudioStream(void *arg)
SDL_AudioSpec spec2; SDL_AudioSpec spec2;
int i, ii, j, jj, k, kk; int i, ii, j, jj, k, kk;
SDL_zero(spec1);
SDL_zero(spec2);
/* Call Quit */ /* Call Quit */
SDL_QuitSubSystem(SDL_INIT_AUDIO); SDL_QuitSubSystem(SDL_INIT_AUDIO);
SDLTest_AssertPass("Call to SDL_QuitSubSystem(SDL_INIT_AUDIO)"); SDLTest_AssertPass("Call to SDL_QuitSubSystem(SDL_INIT_AUDIO)");
@ -567,6 +570,9 @@ static int audio_buildAudioStreamNegative(void *arg)
int i; int i;
char message[256]; char message[256];
SDL_zero(spec1);
SDL_zero(spec2);
/* Valid format */ /* Valid format */
spec1.format = SDL_AUDIO_S8; spec1.format = SDL_AUDIO_S8;
spec1.channels = 1; spec1.channels = 1;
@ -678,6 +684,9 @@ static int audio_convertAudio(void *arg)
char message[128]; char message[128];
int i, ii, j, jj, k, kk; int i, ii, j, jj, k, kk;
SDL_zero(spec1);
SDL_zero(spec2);
/* Iterate over bitmask that determines which parameters are modified in the conversion */ /* Iterate over bitmask that determines which parameters are modified in the conversion */
for (c = 1; c < 8; c++) { for (c = 1; c < 8; c++) {
SDL_strlcpy(message, "Changing:", 128); SDL_strlcpy(message, "Changing:", 128);
@ -995,6 +1004,9 @@ static int audio_resampleLoss(void *arg)
double sum_squared_value = 0; double sum_squared_value = 0;
double signal_to_noise = 0; double signal_to_noise = 0;
SDL_zero(tmpspec1);
SDL_zero(tmpspec2);
SDLTest_AssertPass("Test resampling of %i s %i Hz %f phase sine wave from sampling rate of %i Hz to %i Hz", SDLTest_AssertPass("Test resampling of %i s %i Hz %f phase sine wave from sampling rate of %i Hz to %i Hz",
spec->time, spec->freq, spec->phase, spec->rate_in, spec->rate_out); spec->time, spec->freq, spec->phase, spec->rate_in, spec->rate_out);
@ -1147,6 +1159,9 @@ static int audio_convertAccuracy(void *arg)
int tmp_len, dst_len; int tmp_len, dst_len;
int ret; int ret;
SDL_zero(src_spec);
SDL_zero(tmp_spec);
SDL_AudioFormat format = formats[i]; SDL_AudioFormat format = formats[i];
const char* format_name = format_names[i]; const char* format_name = format_names[i];
@ -1238,6 +1253,10 @@ static int audio_formatChange(void *arg)
double target_signal_to_noise = 75.0; double target_signal_to_noise = 75.0;
int sine_freq = 500; int sine_freq = 500;
SDL_zero(spec1);
SDL_zero(spec2);
SDL_zero(spec3);
spec1.format = SDL_AUDIO_F32; spec1.format = SDL_AUDIO_F32;
spec1.channels = 1; spec1.channels = 1;
spec1.freq = 20000; spec1.freq = 20000;

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_AudioSpec spec = { SDL_AUDIO_F32, codecpar->ch_layout.nb_channels, codecpar->sample_rate, SDL_FALSE };
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_AudioSpec spec = { GetAudioFormat(frame->format), frame->ch_layout.nb_channels, frame->sample_rate, SDL_FALSE };
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)) {