gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

commit 4eadf87f607cc26ece3da8e56abc12421cad19a9
parent 7798f8b3d7bcbf24daa45596b666cf4410e1c84e
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Fri, 25 Oct 2024 00:13:04 +0200

correctly apply latency when plugin is bypassed

Diffstat:
Mdoc/changelog.txt | 3+++
Msource/jucePluginLib/CMakeLists.txt | 1+
Asource/jucePluginLib/bypassBuffer.cpp | 5+++++
Asource/jucePluginLib/bypassBuffer.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msource/jucePluginLib/processor.cpp | 31+++++++++++++++++++++++++++++++
Msource/jucePluginLib/processor.h | 3+++
6 files changed, 97 insertions(+), 0 deletions(-)

diff --git a/doc/changelog.txt b/doc/changelog.txt @@ -30,6 +30,9 @@ Framework: For backwards compatibility, it is also possible to copy ROMs next to the plugins as it was before +- [Fix] FX plugins didn't apply latency when being processed as bypassed, causing the + signal to appear early + NodalRed2x: - [Imp] Added indicator around knobs to show the current VMMap parameter value diff --git a/source/jucePluginLib/CMakeLists.txt b/source/jucePluginLib/CMakeLists.txt @@ -7,6 +7,7 @@ file(TOUCH_NOCREATE ${CMAKE_CURRENT_LIST_DIR}/version.h.in) file(TOUCH_NOCREATE ${CMAKE_CURRENT_LIST_DIR}/versionDateTime.h.in) set(SOURCES + bypassBuffer.cpp bypassBuffer.h createVersionDateTime.cmake clipboard.cpp clipboard.h controller.cpp controller.h diff --git a/source/jucePluginLib/bypassBuffer.cpp b/source/jucePluginLib/bypassBuffer.cpp @@ -0,0 +1,5 @@ +#include "bypassBuffer.h" + +namespace pluginLib +{ +} diff --git a/source/jucePluginLib/bypassBuffer.h b/source/jucePluginLib/bypassBuffer.h @@ -0,0 +1,54 @@ +#pragma once + +#include <vector> + +#include "dsp56kEmu/ringbuffer.h" + +namespace pluginLib +{ + class BypassBuffer + { + public: + using ChannelBuffer = dsp56k::RingBuffer<float, 32768, false, true>; + + void write(const float* _data, uint32_t _channel, uint32_t _samples, uint32_t _latency) + { + if(_channel >= m_channels.size()) + { + m_channels.resize(_channel + 1); + m_latency.resize(_channel + 1); + } + + auto& ch = m_channels[_channel]; + auto& latency = m_latency[_channel]; + + while(_latency > latency) + { + ++latency; + ch.push_back(0.0f); + } + + while(_latency < latency) + { + --latency; + ch.pop_front(); + } + + for(uint32_t i=0; i<_samples; ++i) + ch.push_back(_data[i]); + } + + void read(float* _output, uint32_t _channel, const uint32_t _samples) + { + if(_channel >= m_channels.size()) + return; + + for(uint32_t i=0; i<_samples; ++i) + _output[i] = m_channels[_channel].pop_front(); + } + + private: + std::vector<ChannelBuffer> m_channels; + std::vector<uint32_t> m_latency; + }; +} diff --git a/source/jucePluginLib/processor.cpp b/source/jucePluginLib/processor.cpp @@ -583,6 +583,37 @@ namespace pluginLib } } + void Processor::processBlockBypassed(juce::AudioBuffer<float>& _buffer, juce::MidiBuffer& _midiMessages) + { + if(getProperties().isSynth || getTotalNumInputChannels() <= 0) + { + _buffer.clear(0, _buffer.getNumSamples()); + return; + } + + const auto sampleCount = static_cast<uint32_t>(_buffer.getNumSamples()); + const auto outCount = static_cast<uint32_t>(getTotalNumOutputChannels()); + const auto inCount = static_cast<uint32_t>(getTotalNumInputChannels()); + + uint32_t inCh = 0; + + for(uint32_t outCh=0; outCh<outCount; ++outCh) + { + auto* input = _buffer.getReadPointer(static_cast<int>(inCh)); + auto* output = _buffer.getWritePointer(static_cast<int>(outCh)); + + m_bypassBuffer.write(input, outCh, sampleCount, getLatencySamples()); + m_bypassBuffer.read(output, outCh, sampleCount); + + ++inCh; + + if(inCh >= inCount) + inCh = 0; + } + +// AudioProcessor::processBlockBypassed(_buffer, _midiMessages); + } + #if !SYNTHLIB_DEMO_MODE void Processor::setState(const void* _data, const size_t _sizeInBytes) { diff --git a/source/jucePluginLib/processor.h b/source/jucePluginLib/processor.h @@ -3,6 +3,7 @@ #include <juce_audio_processors/juce_audio_processors.h> #include <juce_audio_devices/juce_audio_devices.h> +#include "bypassBuffer.h" #include "controller.h" #include "midiports.h" @@ -137,6 +138,7 @@ namespace pluginLib bool producesMidi() const override; bool isMidiEffect() const override; void processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages) override; + void processBlockBypassed(juce::AudioBuffer<float>& _buffer, juce::MidiBuffer& _midiMessages) override; #if !SYNTHLIB_DEMO_MODE void setState(const void *_data, size_t _sizeInBytes); @@ -172,5 +174,6 @@ namespace pluginLib float m_preferredDeviceSamplerate = 0.0f; float m_hostSamplerate = 0.0f; MidiPorts m_midiPorts; + BypassBuffer m_bypassBuffer; }; }