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.
This commit is contained in:
bjorn 2021-04-02 23:32:11 -06:00
parent 69b5c51388
commit 4469fc99e8
1 changed files with 36 additions and 22 deletions

View File

@ -92,7 +92,7 @@ static void onPlayback(ma_device* device, void* out, const void* in, uint32_t co
do { do {
float* dst = count >= BUFFER_SIZE ? output : state.leftovers; 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) { if (dst == state.leftovers) {
memset(dst, 0, sizeof(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 // 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 // - No converter: just read frames into raw (it has enough space for BUFFER_SIZE frames).
uint64_t frameLimit = sizeof(raw) / lovrSoundGetChannelCount(source->sound) / sizeof(float); // - Converter: keep reading as many frames as possible/needed into raw and convert into aux.
uint32_t framesToConvert = BUFFER_SIZE; // - If EOF is reached, rewind and continue for looping sources, otherwise pad end with zero.
uint32_t framesConverted = 0; buf = source->converter ? aux : raw;
while (framesToConvert > 0) { float* cursor = buf; // Edge of processed frames
src = raw; uint32_t channelsOut = lovrSourceUsesSpatializer(source) ? 1 : 2; // If spatializer isn't converting to stereo, converter must do it
uint64_t framesToRead = source->converter ? MIN(ma_data_converter_get_required_input_frame_count(source->converter, framesToConvert), frameLimit) : framesToConvert; uint32_t framesRemaining = BUFFER_SIZE;
uint64_t framesRead = lovrSoundRead(source->sound, source->offset, framesToRead, src); uint32_t framesProcessed = 0;
ma_uint64 framesIn = framesRead; while (framesRemaining > 0) {
ma_uint64 framesOut = framesToConvert; 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 (framesRead == 0) {
if (source->looping) { if (source->looping) {
@ -128,32 +137,37 @@ static void onPlayback(ma_device* device, void* out, const void* in, uint32_t co
} else { } else {
source->offset = 0; source->offset = 0;
source->playing = false; source->playing = false;
src = source->converter ? aux : raw; memset(cursor, 0, framesRemaining * channelsOut * sizeof(float));
memset(src + framesConverted * channels, 0, framesToConvert * channels * sizeof(float));
break; break;
} }
} else {
source->offset += framesRead;
} }
if (source->converter) { if (source->converter) {
ma_data_converter_process_pcm_frames(source->converter, src, &framesIn, aux + framesConverted * channels, &framesOut); ma_uint64 framesIn = framesRead;
src = aux; 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 // Spatialize
if (lovrSourceUsesSpatializer(source)) { if (lovrSourceUsesSpatializer(source)) {
state.spatializer->apply(source, src, mix, BUFFER_SIZE, BUFFER_SIZE); state.spatializer->apply(source, buf, mix, BUFFER_SIZE, BUFFER_SIZE);
src = mix; buf = mix;
} }
// Mix // Mix
float volume = source->volume; float volume = source->volume;
for (uint32_t i = 0; i < OUTPUT_CHANNELS * BUFFER_SIZE; i++) { for (uint32_t i = 0; i < OUTPUT_CHANNELS * BUFFER_SIZE; i++) {
dst[i] += src[i] * volume; dst[i] += buf[i] * volume;
} }
} }