From 9b24182542cbb1e086f84c5e66e886a599e3e179 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 30 Oct 2019 13:26:26 -0400 Subject: [PATCH] Allow audio interfaces with no input to be used. Fix sample rate conversion. --- include/dsp/resampler.hpp | 4 +- src/core/AudioInterface.cpp | 96 +++++++++++++++++++++++-------------- 2 files changed, 63 insertions(+), 37 deletions(-) diff --git a/include/dsp/resampler.hpp b/include/dsp/resampler.hpp index 27c244d7..40cebd86 100644 --- a/include/dsp/resampler.hpp +++ b/include/dsp/resampler.hpp @@ -80,8 +80,8 @@ struct SampleRateConverter { assert(outFrames); if (st) { // Resample each channel at a time - spx_uint32_t inLen; - spx_uint32_t outLen; + spx_uint32_t inLen = 0; + spx_uint32_t outLen = 0; for (int i = 0; i < channels; i++) { inLen = *inFrames; outLen = *outFrames; diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index 20595fb9..0139adb5 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -31,8 +31,8 @@ struct AudioInterface : Module, audio::Port { NUM_LIGHTS }; - dsp::DoubleRingBuffer, 8192> inputBuffer; - dsp::DoubleRingBuffer, 8192> outputBuffer; + dsp::DoubleRingBuffer, 32768> inputBuffer; + dsp::DoubleRingBuffer, 32768> outputBuffer; dsp::SampleRateConverter inputSrc; dsp::SampleRateConverter outputSrc; @@ -53,6 +53,11 @@ struct AudioInterface : Module, audio::Port { setDeviceId(-1, 0); } + void onSampleRateChange(const SampleRateChangeEvent& e) override { + inputBuffer.clear(); + outputBuffer.clear(); + } + void process(const ProcessArgs& args) override { // Get inputs if (!inputBuffer.full()) { @@ -108,52 +113,73 @@ struct AudioInterface : Module, audio::Port { std::memset(output, 0, sizeof(float) * numOutputs * frames); // Initialize sample rate converters - outputSrc.setRates((int) sampleRate, (int) APP->engine->getSampleRate()); + int engineSampleRate = (int) APP->engine->getSampleRate(); + outputSrc.setRates((int) sampleRate, engineSampleRate); outputSrc.setChannels(numInputs); - inputSrc.setRates((int) APP->engine->getSampleRate(), (int) sampleRate); + inputSrc.setRates(engineSampleRate, (int) sampleRate); inputSrc.setChannels(numOutputs); - // audio input -> module output - dsp::Frame inputAudioBuffer[frames]; - std::memset(inputAudioBuffer, 0, sizeof(inputAudioBuffer)); - for (int i = 0; i < frames; i++) { - for (int j = 0; j < std::min(numInputs, NUM_AUDIO_OUTPUTS); j++) { - inputAudioBuffer[i].samples[j] = input[i * numInputs + j]; + int engineFrames = 0; + + if (numInputs > 0) { + // audio input -> module output + dsp::Frame inputAudioBuffer[frames]; + std::memset(inputAudioBuffer, 0, sizeof(inputAudioBuffer)); + for (int i = 0; i < frames; i++) { + for (int j = 0; j < std::min(numInputs, NUM_AUDIO_OUTPUTS); j++) { + inputAudioBuffer[i].samples[j] = input[i * numInputs + j]; + } } + int inputAudioFrames = frames; + int outputFrames = outputBuffer.capacity(); + outputSrc.process(inputAudioBuffer, &inputAudioFrames, outputBuffer.endData(), &outputFrames); + outputBuffer.endIncr(outputFrames); + engineFrames = outputBuffer.size(); } - int inputAudioFrames = frames; - int outputFrames = outputBuffer.capacity(); - outputSrc.process(inputAudioBuffer, &inputAudioFrames, outputBuffer.endData(), &outputFrames); - outputBuffer.endIncr(outputFrames); - - // Step engine estimated number of steps - if (APP->engine->getPrimaryModule() == this) { - APP->engine->step(outputFrames); + else { + // Upper bound on number of frames so that `outputAudioFrames >= frames` at the end of this method. + double ratio = (double) engineSampleRate / sampleRate; + engineFrames = std::ceil(frames * ratio - inputBuffer.size()); + engineFrames = std::max(engineFrames, 0); + } + + // Step engine to consume the entire output buffer + if (APP->engine->getPrimaryModule() == this && engineFrames > 0) { + APP->engine->step(engineFrames); } - // module input -> audio output - dsp::Frame outputAudioBuffer[frames]; - int inputFrames = inputBuffer.size(); - int outputAudioFrames = frames; - inputSrc.process(inputBuffer.startData(), &inputFrames, outputAudioBuffer, &outputAudioFrames); - inputBuffer.startIncr(inputFrames); - for (int i = 0; i < frames; i++) { - for (int j = 0; j < std::min(numOutputs, NUM_AUDIO_INPUTS); j++) { - output[i * numOutputs + j] = outputAudioBuffer[i].samples[j]; + if (numOutputs > 0) { + // module input -> audio output + dsp::Frame outputAudioBuffer[frames]; + int inputFrames = inputBuffer.size(); + int outputAudioFrames = frames; + inputSrc.process(inputBuffer.startData(), &inputFrames, outputAudioBuffer, &outputAudioFrames); + inputBuffer.startIncr(inputFrames); + for (int i = 0; i < outputAudioFrames; i++) { + for (int j = 0; j < std::min(numOutputs, NUM_AUDIO_INPUTS); j++) { + output[i * numOutputs + j] = outputAudioBuffer[i].samples[j]; + } } } - DEBUG("%p %d: frames %d, %d -> %d outputBuffer %d, %d -> %d inputBuffer %d", - this, APP->engine->getPrimaryModule() == this, frames, - inputAudioFrames, outputFrames, outputBuffer.size(), - inputFrames, outputAudioFrames, inputBuffer.size()); + // If we're left with too many output samples, flush the buffer. + // if (inputBuffer.size() >= 2.f * ) { + // inputBuffer.clear(); + // } + + // DEBUG("%p %d: frames %d\toutputBuffer %d inputBuffer %d engineFrames %d\t", + // this, APP->engine->getPrimaryModule() == this, frames, + // outputBuffer.size(), inputBuffer.size(), engineFrames); + } + + void onOpenStream() override { + inputBuffer.clear(); + outputBuffer.clear(); } void onCloseStream() override { - // Reset outputs - for (int i = 0; i < NUM_AUDIO_OUTPUTS; i++) { - outputs[AUDIO_OUTPUTS + i].setVoltage(0.f); - } + inputBuffer.clear(); + outputBuffer.clear(); } void onChannelsChange() override {