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 ec82e084001d976fc961294e99c9408d666babcd
parent 5a01097c2efbd094feaf7fadd03aa2abc3ca1634
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Thu,  7 Apr 2022 18:30:12 +0200

use separate values for input-to-output latency and midi-to-output latency

Diffstat:
Msource/jucePlugin/PluginProcessor.cpp | 6+++++-
Msource/synthLib/device.cpp | 8++++----
Msource/synthLib/device.h | 11+++++++----
Msource/synthLib/plugin.cpp | 20++++++++++++++------
Msource/synthLib/plugin.h | 6++++--
Msource/virusLib/device.cpp | 16++++++++++++----
Msource/virusLib/device.h | 3++-
7 files changed, 48 insertions(+), 22 deletions(-)

diff --git a/source/jucePlugin/PluginProcessor.cpp b/source/jucePlugin/PluginProcessor.cpp @@ -88,7 +88,11 @@ void AudioPluginAudioProcessor::prepareToPlay (double sampleRate, int samplesPer // initialisation that you need.. m_plugin.setSamplerate(static_cast<float>(sampleRate)); m_plugin.setBlockSize(samplesPerBlock); - setLatencySamples(m_plugin.getLatencySamples()); + + if constexpr(JucePlugin_IsSynth) + setLatencySamples(m_plugin.getLatencyMidiToOutput()); + else + setLatencySamples(m_plugin.getLatencyInputToOutput()); } void AudioPluginAudioProcessor::releaseResources() diff --git a/source/synthLib/device.cpp b/source/synthLib/device.cpp @@ -65,17 +65,17 @@ namespace synthLib for (const auto& ev : _midiIn) sendMidi(ev, _midiOut); - m_periph.getEsai().processAudioInterleaved(_inputs, _outputs, _size, 2, 2, m_latency); + m_periph.getEsai().processAudioInterleaved(_inputs, _outputs, _size, 2, 2, m_extraLatency); readMidiOut(_midiOut); } - void Device::setLatencySamples(const uint32_t _size) + void Device::setExtraLatencySamples(const uint32_t _size) { const uint32_t maxLatency = static_cast<uint32_t>(getPeriph().getEsai().getAudioInputs()[0].capacity()) >> 1; - m_latency = std::min(_size, maxLatency); + m_extraLatency = std::min(_size, maxLatency); - LOG("Latency set to " << m_latency << " samples at " << getSamplerate() << " Hz"); + LOG("Latency set to " << m_extraLatency << " samples at " << getSamplerate() << " Hz"); if(_size > maxLatency) { diff --git a/source/synthLib/device.h b/source/synthLib/device.h @@ -16,9 +16,12 @@ namespace synthLib Device(uint32_t _memorySize, uint32_t _externalMemStartAddress); virtual ~Device(); virtual void process(float** _inputs, float** _outputs, size_t _size, const std::vector<SMidiEvent>& _midiIn, std::vector<SMidiEvent>& _midiOut); - void setLatencySamples(uint32_t _size); - uint32_t getLatencySamples() const { return m_latency; } - virtual uint32_t getInternalLatencySamples() const { return 0; } + + void setExtraLatencySamples(uint32_t _size); + uint32_t getExtraLatencySamples() const { return m_extraLatency; } + + virtual uint32_t getInternalLatencyMidiToOutput() const { return 0; } + virtual uint32_t getInternalLatencyInputToOutput() const { return 0; } void startDSPThread(); @@ -50,6 +53,6 @@ namespace synthLib std::unique_ptr<dsp56k::DSPThread> m_dspThread; std::vector<SMidiEvent> m_midiIn; - uint32_t m_latency = 0; + uint32_t m_extraLatency = 0; }; } diff --git a/source/synthLib/plugin.cpp b/source/synthLib/plugin.cpp @@ -16,8 +16,9 @@ using namespace synthLib; namespace synthLib { constexpr uint8_t g_stateVersion = 1; + constexpr uint32_t g_extraLatencyBlocks = 1; - Plugin::Plugin(Device* _device) : m_device(_device), m_deviceLatency(0) + Plugin::Plugin(Device* _device) : m_device(_device) { m_resampler.setDeviceSamplerate(_device->getSamplerate()); } @@ -213,10 +214,11 @@ namespace synthLib if(m_blockSize <= 0 || m_hostSamplerate <= 0) return; - const auto latency = static_cast<uint32_t>(std::ceil(static_cast<float>(m_blockSize) * m_device->getSamplerate() * m_hostSamplerateInv)); - m_device->setLatencySamples(latency); + const auto latency = static_cast<uint32_t>(std::ceil(static_cast<float>(m_blockSize * g_extraLatencyBlocks) * m_device->getSamplerate() * m_hostSamplerateInv)); + m_device->setExtraLatencySamples(latency); - m_deviceLatency = static_cast<uint32_t>(m_device->getInternalLatencySamples() * m_hostSamplerate / m_device->getSamplerate()); + m_deviceLatencyMidiToOutput = static_cast<uint32_t>(static_cast<float>(m_device->getInternalLatencyMidiToOutput()) * m_hostSamplerate / m_device->getSamplerate()); + m_deviceLatencyInputToOutput = static_cast<uint32_t>(static_cast<float>(m_device->getInternalLatencyInputToOutput()) * m_hostSamplerate / m_device->getSamplerate()); } void Plugin::processMidiInEvents() @@ -273,9 +275,15 @@ namespace synthLib updateDeviceLatency(); } - uint32_t Plugin::getLatencySamples() const + uint32_t Plugin::getLatencyMidiToOutput() const { std::lock_guard lock(m_lock); - return m_blockSize + m_deviceLatency; + return m_blockSize * g_extraLatencyBlocks + m_deviceLatencyMidiToOutput; + } + + uint32_t Plugin::getLatencyInputToOutput() const + { + std::lock_guard lock(m_lock); + return m_blockSize * g_extraLatencyBlocks + m_deviceLatencyInputToOutput; } } diff --git a/source/synthLib/plugin.h b/source/synthLib/plugin.h @@ -23,7 +23,8 @@ namespace synthLib void setSamplerate(float _samplerate); void setBlockSize(uint32_t _blockSize); - uint32_t getLatencySamples() const; + uint32_t getLatencyMidiToOutput() const; + uint32_t getLatencyInputToOutput() const; void process(float** _inputs, float** _outputs, size_t _count, float _bpm, float _ppqPos, bool _isPlaying); void getMidiOut(std::vector<SMidiEvent>& _midiOut); @@ -61,7 +62,8 @@ namespace synthLib uint32_t m_blockSize = 0; - uint32_t m_deviceLatency; + uint32_t m_deviceLatencyMidiToOutput = 0; + uint32_t m_deviceLatencyInputToOutput = 0; // MIDI Clock bool m_isPlaying = false; diff --git a/source/virusLib/device.cpp b/source/virusLib/device.cpp @@ -58,11 +58,19 @@ namespace virusLib return m_syx.setState(_state, _type); } - uint32_t Device::getInternalLatencySamples() const + uint32_t Device::getInternalLatencyMidiToOutput() const { -// return 300; // hard to believe but this is what I figured out by measuring with the init patch + /* + * hard to believe but this is what I figured out by measuring with a customized init patch + * + * Note that this is an average value, midi latency drifts in a range of roughly +/- 61 samples + */ + return 384 + 28; + } - // more precise, measured by using an input init patch. Sent a click to the input and recorded both the input + uint32_t Device::getInternalLatencyInputToOutput() const + { + // Measured by using an input init patch. Sent a click to the input and recorded both the input // as direct signal plus the Virus output and checking the resulting latency in a wave editor return 384; } @@ -73,7 +81,7 @@ namespace virusLib { // LOG("MIDI: " << std::hex << (int)_ev.a << " " << (int)_ev.b << " " << (int)_ev.c); auto ev = _ev; - ev.offset += m_numSamplesProcessed + getLatencySamples(); + ev.offset += m_numSamplesProcessed + getExtraLatencySamples(); return m_syx.sendMIDI(ev, true); } diff --git a/source/virusLib/device.h b/source/virusLib/device.h @@ -22,7 +22,8 @@ namespace virusLib bool getState(std::vector<uint8_t>& _state, synthLib::StateType _type) override; bool setState(const std::vector<uint8_t>& _state, synthLib::StateType _type) override; - uint32_t getInternalLatencySamples() const override; + uint32_t getInternalLatencyMidiToOutput() const override; + uint32_t getInternalLatencyInputToOutput() const override; private: bool sendMidi(const synthLib::SMidiEvent& _ev, std::vector<synthLib::SMidiEvent>& _response) override;