From 5966b1f1a782319ef3dc1c7b74ad3ceb90343477 Mon Sep 17 00:00:00 2001 From: Paul Walker Date: Sun, 12 Sep 2021 20:13:53 -0400 Subject: [PATCH] Add a Latent / Small Block mode to FX Processor In FruityLoops (for now) use a latent block mode. Closes #4518 --- src/surge_effects_bank/SurgeFXProcessor.cpp | 130 ++++++++++++++++---- src/surge_effects_bank/SurgeFXProcessor.h | 7 ++ 2 files changed, 111 insertions(+), 26 deletions(-) diff --git a/src/surge_effects_bank/SurgeFXProcessor.cpp b/src/surge_effects_bank/SurgeFXProcessor.cpp index 377ac240f1a..834b07e6a7f 100644 --- a/src/surge_effects_bank/SurgeFXProcessor.cpp +++ b/src/surge_effects_bank/SurgeFXProcessor.cpp @@ -19,6 +19,9 @@ SurgefxAudioProcessor::SurgefxAudioProcessor() .withOutput("Output", juce::AudioChannelSet::stereo(), true) .withInput("Sidechain", juce::AudioChannelSet::stereo(), true)) { + nonLatentBlockMode = !juce::PluginHostType().isFruityLoops(); + setLatencySamples(nonLatentBlockMode ? 0 : BLOCK_SIZE); + effectNum = fxt_delay; storage.reset(new SurgeStorage()); storage->userPrefOverrides[Surge::Storage::HighPrecisionReadouts] = std::make_pair(0, ""); @@ -104,6 +107,7 @@ void SurgefxAudioProcessor::changeProgramName(int index, const juce::String &new void SurgefxAudioProcessor::prepareToPlay(double sr, int samplesPerBlock) { storage->setSamplerate(sr); + setLatencySamples(nonLatentBlockMode ? 0 : BLOCK_SIZE); } void SurgefxAudioProcessor::releaseResources() @@ -130,6 +134,8 @@ void SurgefxAudioProcessor::processBlock(juce::AudioBuffer &buffer, juce::ScopedNoDenormals noDenormals; + setLatencySamples(nonLatentBlockMode ? 0 : BLOCK_SIZE); + if (surge_effect->checkHasInvalidatedUI()) { resetFxParams(true); @@ -168,46 +174,116 @@ void SurgefxAudioProcessor::processBlock(juce::AudioBuffer &buffer, audio_thread_surge_effect = surge_effect; } - for (int outPos = 0; outPos < buffer.getNumSamples() && !resettingFx; outPos += BLOCK_SIZE) + if (nonLatentBlockMode) { - auto outL = mainInputOutput.getWritePointer(0, outPos); - auto outR = mainInputOutput.getWritePointer(1, outPos); - auto sideChainBus = getBus(true, 1); - if (effectNum == fxt_vocoder && sideChainBus && sideChainBus->isEnabled()) + + for (int outPos = 0; outPos < buffer.getNumSamples() && !resettingFx; outPos += BLOCK_SIZE) { - auto sideL = sideChainInput.getReadPointer(0, outPos); - auto sideR = sideChainInput.getReadPointer(1, outPos); + auto outL = mainInputOutput.getWritePointer(0, outPos); + auto outR = mainInputOutput.getWritePointer(1, outPos); - memcpy(storage->audio_in_nonOS[0], sideL, BLOCK_SIZE * sizeof(float)); - memcpy(storage->audio_in_nonOS[1], sideR, BLOCK_SIZE * sizeof(float)); - } + if (effectNum == fxt_vocoder && sideChainBus && sideChainBus->isEnabled()) + { + auto sideL = sideChainInput.getReadPointer(0, outPos); + auto sideR = sideChainInput.getReadPointer(1, outPos); - for (int i = 0; i < n_fx_params; ++i) - { - fxstorage->p[fx_param_remap[i]].set_value_f01(*fxParams[i]); - paramFeatureOntoParam(&(fxstorage->p[fx_param_remap[i]]), *(fxParamFeatures[i])); + memcpy(storage->audio_in_nonOS[0], sideL, BLOCK_SIZE * sizeof(float)); + memcpy(storage->audio_in_nonOS[1], sideR, BLOCK_SIZE * sizeof(float)); + } + + for (int i = 0; i < n_fx_params; ++i) + { + fxstorage->p[fx_param_remap[i]].set_value_f01(*fxParams[i]); + paramFeatureOntoParam(&(fxstorage->p[fx_param_remap[i]]), *(fxParamFeatures[i])); + } + copyGlobaldataSubset(storage_id_start, storage_id_end); + + if (is_aligned(outL, 16) && is_aligned(outR, 16)) + { + audio_thread_surge_effect->process(outL, outR); + } + else + { + float bufferL alignas(16)[BLOCK_SIZE], bufferR alignas(16)[BLOCK_SIZE]; + + auto inL = mainInputOutput.getReadPointer(0, outPos); + auto inR = mainInputOutput.getReadPointer(1, outPos); + + memcpy(bufferL, inL, BLOCK_SIZE * sizeof(float)); + memcpy(bufferR, inR, BLOCK_SIZE * sizeof(float)); + + audio_thread_surge_effect->process(bufferL, bufferR); + + memcpy(outL, bufferL, BLOCK_SIZE * sizeof(float)); + memcpy(outR, bufferR, BLOCK_SIZE * sizeof(float)); + } } - copyGlobaldataSubset(storage_id_start, storage_id_end); + } + else + { + auto outL = mainInputOutput.getWritePointer(0, 0); + auto outR = mainInputOutput.getWritePointer(1, 0); + + auto inL = mainInputOutput.getReadPointer(0, 0); + auto inR = mainInputOutput.getReadPointer(1, 0); + + const float *sideL = nullptr, *sideR = nullptr; + + auto sideChainBus = getBus(true, 1); - if (is_aligned(outL, 16) && is_aligned(outR, 16)) + if (effectNum == fxt_vocoder && sideChainBus && sideChainBus->isEnabled()) { - audio_thread_surge_effect->process(outL, outR); + sideL = sideChainInput.getReadPointer(0, 0); + sideR = sideChainInput.getReadPointer(1, 0); } - else + + for (int smp = 0; smp < buffer.getNumSamples(); smp++) { - float bufferL alignas(16)[BLOCK_SIZE], bufferR alignas(16)[BLOCK_SIZE]; + input_buffer[0][input_position] = inL[smp]; + input_buffer[1][input_position] = inR[smp]; + if (effectNum == fxt_vocoder && sideL && sideR) + { + sidechain_buffer[0][input_position] = sideL[smp]; + sidechain_buffer[1][input_position] = sideR[smp]; + } + else + { + sidechain_buffer[0][input_position] = 0.f; + sidechain_buffer[1][input_position] = 0.f; + } + input_position++; - auto inL = mainInputOutput.getReadPointer(0, outPos); - auto inR = mainInputOutput.getReadPointer(1, outPos); + if (input_position == BLOCK_SIZE) + { + memcpy(storage->audio_in_nonOS[0], sidechain_buffer[0], BLOCK_SIZE * sizeof(float)); + memcpy(storage->audio_in_nonOS[1], sidechain_buffer[1], BLOCK_SIZE * sizeof(float)); - memcpy(bufferL, inL, BLOCK_SIZE * sizeof(float)); - memcpy(bufferR, inR, BLOCK_SIZE * sizeof(float)); + for (int i = 0; i < n_fx_params; ++i) + { + fxstorage->p[fx_param_remap[i]].set_value_f01(*fxParams[i]); + paramFeatureOntoParam(&(fxstorage->p[fx_param_remap[i]]), + *(fxParamFeatures[i])); + } + copyGlobaldataSubset(storage_id_start, storage_id_end); - audio_thread_surge_effect->process(bufferL, bufferR); + audio_thread_surge_effect->process(input_buffer[0], input_buffer[1]); + memcpy(output_buffer, input_buffer, 2 * BLOCK_SIZE * sizeof(float)); + input_position = 0; + output_position = 0; + } - memcpy(outL, bufferL, BLOCK_SIZE * sizeof(float)); - memcpy(outR, bufferR, BLOCK_SIZE * sizeof(float)); + if (output_position >= 0 && output_position < BLOCK_SIZE) // that < shoudl never happen + { + outL[smp] = output_buffer[0][output_position]; + outR[smp] = output_buffer[1][output_position]; + output_position++; + } + else + { + outL[smp] = 0; + outR[smp] = 0; + } } } } @@ -340,6 +416,8 @@ void SurgefxAudioProcessor::reorderSurgeParams() void SurgefxAudioProcessor::resetFxType(int type, bool updateJuceParams) { resettingFx = true; + input_position = 0; + output_position = -1; effectNum = type; fxstorage->type.val.i = effectNum; diff --git a/src/surge_effects_bank/SurgeFXProcessor.h b/src/surge_effects_bank/SurgeFXProcessor.h index 5979948e581..4c9b80782cb 100644 --- a/src/surge_effects_bank/SurgeFXProcessor.h +++ b/src/surge_effects_bank/SurgeFXProcessor.h @@ -31,6 +31,13 @@ class SurgefxAudioProcessor : public juce::AudioProcessor, SurgefxAudioProcessor(); ~SurgefxAudioProcessor(); + float input_buffer alignas(16)[2][BLOCK_SIZE]; + float sidechain_buffer alignas(16)[2][BLOCK_SIZE]; + float output_buffer alignas(16)[2][BLOCK_SIZE]; + int input_position{0}; + int output_position{-1}; + + bool nonLatentBlockMode{true}; //============================================================================== void prepareToPlay(double sampleRate, int samplesPerBlock) override; void releaseResources() override;