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 bd2b4277108ab74f2a858b8bd749bc6f0912b07c
parent 61af621d66f231b51077efca839cccfb1e4bbca7
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Mon, 19 Jul 2021 23:57:58 +0200

completely rework midi timing

Diffstat:
Msource/synthLib/device.cpp | 72+++++++++++-------------------------------------------------------------
Msource/synthLib/device.h | 1+
Msource/virusLib/device.cpp | 14+++++++++++++-
Msource/virusLib/device.h | 3+++
Msource/virusLib/microcontroller.cpp | 33++++++++++++++++++++++++++++++++-
Msource/virusLib/microcontroller.h | 21+++++++++++----------
6 files changed, 71 insertions(+), 73 deletions(-)

diff --git a/source/synthLib/device.cpp b/source/synthLib/device.cpp @@ -19,7 +19,12 @@ namespace synthLib auto* bufDSP = buf; auto* bufMem = bufDSP + alignedSize<DSP>(); auto* bufBuf = bufMem + alignedSize<Memory>(); - + + m_periph.getEsai().setCallback([this](Audio* _audio) + { + onAudioWritten(); + }, 0, 1); + m_memory = new (bufMem)Memory(m_memoryValidator, _memorySize, reinterpret_cast<TWord*>(bufBuf)); m_dsp = new (buf)DSP(*m_memory, &m_periph, &m_periph); @@ -57,67 +62,12 @@ namespace synthLib void Device::process(float** _inputs, float** _outputs, const size_t _size, const std::vector<SMidiEvent>& _midiIn, std::vector<SMidiEvent>& _midiOut) { - m_midiIn.insert(m_midiIn.end(), _midiIn.begin(), _midiIn.end()); + for(size_t i=0; i<_midiIn.size(); ++i) + sendMidi(_midiIn[i], _midiOut); - if(!m_midiIn.empty()) - { - size_t i = 0; - - uint32_t end = 0; - uint32_t begin = 0; - - bool sendMidiFailed = false; - - while(i < m_midiIn.size() && !sendMidiFailed) - { - end = m_midiIn[i].offset; - - const auto size = end - begin; - - m_periph.getEsai().processAudioInterleaved(_inputs, _outputs, size, 2, 2, m_latency); - readMidiOut(_midiOut); - - _inputs[0] += size; - _inputs[1] += size; - _outputs[0] += size; - _outputs[1] += size; - - for(size_t j=i; j<m_midiIn.size() && !sendMidiFailed; j++) - { - if(m_midiIn[j].offset <= static_cast<int>(end)) - { - if(!sendMidi(m_midiIn[j], _midiOut)) - { - if(j > 0) - { - m_midiIn.erase(m_midiIn.begin(), m_midiIn.begin() + j); - - for (auto& event : m_midiIn) - event.offset = 0; - } - sendMidiFailed = true; - } - ++i; - } - } - - begin = end; - } - - if(end < _size) - { - m_periph.getEsai().processAudioInterleaved(_inputs, _outputs, _size - end, 2, 2, m_latency); - readMidiOut(_midiOut); - } - - if(!sendMidiFailed) - m_midiIn.clear(); - } - else - { - m_periph.getEsai().processAudioInterleaved(_inputs, _outputs, _size, 2, 2, m_latency); - readMidiOut(_midiOut); - } + m_periph.getEsai().processAudioInterleaved(_inputs, _outputs, _size, 2, 2, m_latency); + + readMidiOut(_midiOut); } void Device::setLatencySamples(const uint32_t _size) diff --git a/source/synthLib/device.h b/source/synthLib/device.h @@ -29,6 +29,7 @@ namespace synthLib protected: virtual void readMidiOut(std::vector<SMidiEvent>& _midiOut) = 0; virtual bool sendMidi(const SMidiEvent& _ev, std::vector<SMidiEvent>& _response) = 0; + virtual void onAudioWritten() {} void dummyProcess(uint32_t _numSamples); diff --git a/source/virusLib/device.cpp b/source/virusLib/device.cpp @@ -42,6 +42,8 @@ namespace virusLib void Device::process(float** _inputs, float** _outputs, size_t _size, const std::vector<synthLib::SMidiEvent>& _midiIn, std::vector<synthLib::SMidiEvent>& _midiOut) { + m_numSamplesProcessed += static_cast<uint32_t>(_size); + synthLib::Device::process(_inputs, _outputs, _size, _midiIn, _midiOut); m_syx.process(_size); } @@ -61,7 +63,9 @@ namespace virusLib if(_ev.sysex.empty()) { // LOG("MIDI: " << std::hex << (int)_ev.a << " " << (int)_ev.b << " " << (int)_ev.c); - return m_syx.sendMIDI(_ev, true); + auto ev = _ev; + ev.offset += m_numSamplesProcessed; + return m_syx.sendMIDI(ev, true); } std::vector<synthLib::SMidiEvent> responses; @@ -87,4 +91,12 @@ namespace virusLib } } } + + void Device::onAudioWritten() + { + m_numSamplesWritten += 1; + + if(m_numSamplesWritten >= 0) + m_syx.sendPendingMidiEvents(m_numSamplesWritten >> 1); + } } diff --git a/source/virusLib/device.h b/source/virusLib/device.h @@ -25,9 +25,12 @@ namespace virusLib private: bool sendMidi(const synthLib::SMidiEvent& _ev, std::vector<synthLib::SMidiEvent>& _response) override; void readMidiOut(std::vector<synthLib::SMidiEvent>& _midiOut) override; + void onAudioWritten() override; ROMFile m_rom; Microcontroller m_syx; MidiOutParser m_midiOutParser; + uint32_t m_numSamplesWritten = 0; + uint32_t m_numSamplesProcessed = 0; }; } diff --git a/source/virusLib/microcontroller.cpp b/source/virusLib/microcontroller.cpp @@ -106,6 +106,8 @@ bool Microcontroller::needsToWaitForHostBits(char flag1, char flag2) const void Microcontroller::writeHostBitsWithWait(const char flag1, const char flag2) const { + std::lock_guard lock(m_mutex); + const int hsr=m_hdi08.readStatusRegister(); const int target=(flag1?1:0)|(flag2?2:0); if (((hsr>>3)&3)==target) return; @@ -115,6 +117,8 @@ void Microcontroller::writeHostBitsWithWait(const char flag1, const char flag2) bool Microcontroller::sendPreset(uint8_t program, const std::vector<TWord>& preset, bool isMulti) { + std::lock_guard lock(m_mutex); + if(m_hdi08.hasDataToSend() || needsToWaitForHostBits(0,1)) { m_pendingPresetWrites.emplace_back(SPendingPresetWrite{program, isMulti, preset}); @@ -139,7 +143,10 @@ void Microcontroller::sendControlCommand(const ControlCommand _command, const ui bool Microcontroller::send(const Page _page, const uint8_t _part, const uint8_t _param, const uint8_t _value, bool cancelIfFull/* = false*/) { + std::lock_guard lock(m_mutex); + waitUntilReady(); + if(cancelIfFull && needsToWaitForHostBits(0,1)) return false; writeHostBitsWithWait(0,1); @@ -206,7 +213,8 @@ bool Microcontroller::sendMIDI(const SMidiEvent& _ev, bool cancelIfFull/* = fals break; } - return sendMIDItoDSP(_ev.a,_ev.b,_ev.c, cancelIfFull); + m_pendingMidiEvents.push_back(_ev); + return true; } bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, bool _cancelIfFull, std::vector<SMidiEvent>& _responses) @@ -707,6 +715,8 @@ bool Microcontroller::loadMultiSingle(uint8_t _part, const TPreset& _multi) void Microcontroller::process(size_t _size) { + std::lock_guard lock(m_mutex); + if(!m_pendingPresetWrites.empty() && !m_hdi08.hasDataToSend()) { const auto preset = m_pendingPresetWrites.front(); @@ -779,6 +789,8 @@ bool Microcontroller::setState(const std::vector<unsigned char>& _state, const S bool Microcontroller::sendMIDItoDSP(uint8_t _a, uint8_t _b, uint8_t _c, bool cancelIfFull) { + std::lock_guard lock(m_mutex); + if(cancelIfFull && (needsToWaitForHostBits(1,1) || m_hdi08.dataRXFull())) return false; writeHostBitsWithWait(1,1); @@ -787,6 +799,25 @@ bool Microcontroller::sendMIDItoDSP(uint8_t _a, uint8_t _b, uint8_t _c, bool can return true; } +void Microcontroller::sendPendingMidiEvents(uint32_t _maxOffset) +{ + auto size = m_pendingMidiEvents.size(); + + if(!size) + return; + + while(!m_pendingMidiEvents.empty() && static_cast<uint32_t>(m_pendingMidiEvents.front().offset) <= _maxOffset) + { + const auto& ev = m_pendingMidiEvents.front(); + + if(!sendMIDItoDSP(ev.a,ev.b,ev.c, true)) + break; + + m_pendingMidiEvents.pop_front(); + --size; + } +} + void Microcontroller::applyToSingleEditBuffer(const Page _page, const uint8_t _part, const uint8_t _param, const uint8_t _value) { if(_part == SINGLE) diff --git a/source/virusLib/microcontroller.h b/source/virusLib/microcontroller.h @@ -6,13 +6,10 @@ #include "romfile.h" #include "../synthLib/deviceTypes.h" +#include "../synthLib/midiTypes.h" #include <list> - -namespace synthLib -{ - struct SMidiEvent; -} +#include <mutex> namespace virusLib { @@ -180,10 +177,7 @@ public: explicit Microcontroller(dsp56k::HDI08& hdi08, ROMFile& romFile); - bool sendPreset(uint8_t program, const std::vector<dsp56k::TWord>& preset, bool isMulti = false); - void sendControlCommand(ControlCommand command, uint8_t value); bool sendMIDI(const synthLib::SMidiEvent& _ev, bool cancelIfFull = false); - bool send(Page page, uint8_t part, uint8_t param, uint8_t value, bool cancelIfFull = false); bool sendSysex(const std::vector<uint8_t>& _data, bool _cancelIfFull, std::vector<synthLib::SMidiEvent>& _responses); bool writeSingle(uint8_t _bank, uint8_t _program, const TPreset& _data); @@ -191,7 +185,6 @@ public: bool requestMulti(uint8_t _bank, uint8_t _program, TPreset& _data) const; bool requestSingle(uint8_t _bank, uint8_t _program, TPreset& _data) const; - bool needsToWaitForHostBits(char flag1,char flag2) const; void sendInitControlCommands(); void createDefaultState(); @@ -202,7 +195,13 @@ public: bool sendMIDItoDSP(uint8_t _a, uint8_t _b, uint8_t _c, bool cancelIfFull); + void sendPendingMidiEvents(uint32_t _maxOffset); + private: + bool send(Page page, uint8_t part, uint8_t param, uint8_t value, bool cancelIfFull = false); + void sendControlCommand(ControlCommand command, uint8_t value); + bool sendPreset(uint8_t program, const std::vector<dsp56k::TWord>& preset, bool isMulti = false); + bool needsToWaitForHostBits(char flag1,char flag2) const; void writeHostBitsWithWait(char flag1,char flag2) const; void waitUntilReady() const; void waitUntilBufferEmpty() const; @@ -245,7 +244,9 @@ private: }; std::list<SPendingPresetWrite> m_pendingPresetWrites; - uint32_t m_presetWriteCount = 0; + + dsp56k::RingBuffer<synthLib::SMidiEvent, 1024, false> m_pendingMidiEvents; + mutable std::recursive_mutex m_mutex; }; }