From 4469fc99e811a3861720b095b787cc68a3045202 Mon Sep 17 00:00:00 2001 From: bjorn Date: Fri, 2 Apr 2021 23:32:11 -0600 Subject: [PATCH] Fix mixer bug; Refactor mixer; - Sources without converters always read into the beginning of the raw buffer, overwriting previous frames if the source was rewound due to looping. This resulted in an audible click whenever the source was rewound. - After looping, Sources without converters would try to read too many frames -- they would read a full buffer instead of only the necessary number of frames. --- src/modules/audio/audio.c | 58 ++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/src/modules/audio/audio.c b/src/modules/audio/audio.c index 97ffbbd0..d6be50ce 100644 --- a/src/modules/audio/audio.c +++ b/src/modules/audio/audio.c @@ -92,7 +92,7 @@ static void onPlayback(ma_device* device, void* out, const void* in, uint32_t co do { float* dst = count >= BUFFER_SIZE ? output : state.leftovers; - float* src = NULL; // The "current" buffer (used for fast paths) + float* buf = NULL; // The "current" buffer (used for fast paths) if (dst == state.leftovers) { memset(dst, 0, sizeof(state.leftovers)); @@ -110,16 +110,25 @@ static void onPlayback(ma_device* device, void* out, const void* in, uint32_t co } // Read and convert raw frames until there's BUFFER_SIZE converted frames - uint32_t channels = lovrSourceUsesSpatializer(source) ? 1 : 2; // If spatializer isn't converting to stereo, converter must do it - uint64_t frameLimit = sizeof(raw) / lovrSoundGetChannelCount(source->sound) / sizeof(float); - uint32_t framesToConvert = BUFFER_SIZE; - uint32_t framesConverted = 0; - while (framesToConvert > 0) { - src = raw; - uint64_t framesToRead = source->converter ? MIN(ma_data_converter_get_required_input_frame_count(source->converter, framesToConvert), frameLimit) : framesToConvert; - uint64_t framesRead = lovrSoundRead(source->sound, source->offset, framesToRead, src); - ma_uint64 framesIn = framesRead; - ma_uint64 framesOut = framesToConvert; + // - No converter: just read frames into raw (it has enough space for BUFFER_SIZE frames). + // - Converter: keep reading as many frames as possible/needed into raw and convert into aux. + // - If EOF is reached, rewind and continue for looping sources, otherwise pad end with zero. + buf = source->converter ? aux : raw; + float* cursor = buf; // Edge of processed frames + uint32_t channelsOut = lovrSourceUsesSpatializer(source) ? 1 : 2; // If spatializer isn't converting to stereo, converter must do it + uint32_t framesRemaining = BUFFER_SIZE; + uint32_t framesProcessed = 0; + while (framesRemaining > 0) { + uint32_t framesRead; + + if (source->converter) { + uint32_t channelsIn = lovrSoundGetChannelCount(source->sound); + uint32_t capacity = sizeof(raw) / (channelsIn * sizeof(float)); + uint32_t chunk = MIN(ma_data_converter_get_required_input_frame_count(source->converter, framesRemaining), capacity); + framesRead = lovrSoundRead(source->sound, source->offset, chunk, raw); + } else { + framesRead = lovrSoundRead(source->sound, source->offset, framesRemaining, cursor); + } if (framesRead == 0) { if (source->looping) { @@ -128,32 +137,37 @@ static void onPlayback(ma_device* device, void* out, const void* in, uint32_t co } else { source->offset = 0; source->playing = false; - src = source->converter ? aux : raw; - memset(src + framesConverted * channels, 0, framesToConvert * channels * sizeof(float)); + memset(cursor, 0, framesRemaining * channelsOut * sizeof(float)); break; } + } else { + source->offset += framesRead; } if (source->converter) { - ma_data_converter_process_pcm_frames(source->converter, src, &framesIn, aux + framesConverted * channels, &framesOut); - src = aux; + ma_uint64 framesIn = framesRead; + ma_uint64 framesOut = framesRemaining; + ma_data_converter_process_pcm_frames(source->converter, raw, &framesIn, cursor, &framesOut); + cursor += framesOut * channelsOut; + framesProcessed += framesOut; + framesRemaining -= framesOut; + } else { + cursor += framesRead * channelsOut; + framesProcessed += framesRead; + framesRemaining -= framesRead; } - - framesToConvert -= framesOut; - framesConverted += framesOut; - source->offset += framesRead; } // Spatialize if (lovrSourceUsesSpatializer(source)) { - state.spatializer->apply(source, src, mix, BUFFER_SIZE, BUFFER_SIZE); - src = mix; + state.spatializer->apply(source, buf, mix, BUFFER_SIZE, BUFFER_SIZE); + buf = mix; } // Mix float volume = source->volume; for (uint32_t i = 0; i < OUTPUT_CHANNELS * BUFFER_SIZE; i++) { - dst[i] += src[i] * volume; + dst[i] += buf[i] * volume; } }