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 9a80373293be6f1f5b0f7128f434ec3ac6ee73c9
parent 44d86a798f73fc1618a304f1aa93d180ca18ddd7
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Sun, 15 May 2022 23:57:03 +0200

use generic midi packets to send midi data from editor to emu

Diffstat:
Msource/jucePlugin/VirusController.cpp | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msource/jucePlugin/VirusController.h | 20++++++++++++++++----
Msource/jucePlugin/ui3/PatchBrowser.cpp | 7+++----
Msource/jucePluginLib/controller.cpp | 23+++++++++++++++++++++++
Msource/jucePluginLib/controller.h | 3+++
Msource/jucePluginLib/midipacket.cpp | 2+-
Msource/jucePluginLib/midipacket.h | 2+-
Msource/jucePluginLib/parameterdescriptions.cpp | 6++++++
Msource/jucePluginLib/parameterdescriptions.h | 2++
9 files changed, 135 insertions(+), 27 deletions(-)

diff --git a/source/jucePlugin/VirusController.cpp b/source/jucePlugin/VirusController.cpp @@ -31,8 +31,8 @@ namespace Virus { parameter->onValueChanged = [this] { const uint8_t prg = isMultiMode() ? 0x0 : virusLib::SINGLE; - sendSysEx(constructMessage({ MessageType::REQUEST_SINGLE, 0x0, prg })); - sendSysEx(constructMessage({ MessageType::REQUEST_MULTI, 0x0, prg })); + requestSingle(0, prg); + requestMulti(0, prg); if (onMsgDone) { @@ -40,11 +40,12 @@ namespace Virus } }; } - sendSysEx(constructMessage({MessageType::REQUEST_TOTAL})); - sendSysEx(constructMessage({MessageType::REQUEST_ARRANGEMENT})); + requestTotal(); + requestArrangement(); for(uint8_t i=3; i<=8; ++i) - sendSysEx(constructMessage({MessageType::REQUEST_BANK_SINGLE, i})); + requestSingleBank(i); + startTimer(5); } @@ -232,10 +233,10 @@ namespace Virus const uint8_t pt = isMultiMode() ? _part : virusLib::SINGLE; - sendSysEx(constructMessage({ MessageType::PARAM_CHANGE_C, pt, virusLib::PART_BANK_SELECT, virusLib::toMidiByte(_bank) })); - sendSysEx(constructMessage({ MessageType::PARAM_CHANGE_C, pt, virusLib::PART_PROGRAM_CHANGE, _prg })); + sendParameterChange(MessageType::PARAM_CHANGE_C, pt, virusLib::PART_BANK_SELECT, virusLib::toMidiByte(_bank)); + sendParameterChange(MessageType::PARAM_CHANGE_C, pt, virusLib::PART_PROGRAM_CHANGE, _prg); - sendSysEx(constructMessage({MessageType::REQUEST_SINGLE, 0x0, pt})); + requestSingle(0x0, pt); m_currentBank[_part] = _bank; m_currentProgram[_part] = _prg; @@ -297,7 +298,7 @@ namespace Virus constexpr auto namePos = kHeaderWithMsgCodeLen + 2 + 128 + 112; assert(namePos < msg.size()); - auto progName = parseAsciiText(msg, namePos); + const auto progName = parseAsciiText(msg, namePos); DBG(progName); } @@ -412,16 +413,65 @@ namespace Virus void Controller::onStateLoaded() const { - sendSysEx(constructMessage({ MessageType::REQUEST_TOTAL })); - sendSysEx(constructMessage({ MessageType::REQUEST_ARRANGEMENT })); + requestTotal(); + requestArrangement(); } - std::vector<uint8_t> Controller::constructMessage(SysEx msg) const + bool Controller::requestProgram(uint8_t _bank, uint8_t _program, bool _multi) const + { + std::map<pluginLib::MidiDataType, uint8_t> data; + + data.insert(std::make_pair(pluginLib::MidiDataType::Bank, _bank)); + data.insert(std::make_pair(pluginLib::MidiDataType::Program, _program)); + + return sendSysEx(_multi ? "requestmulti" : "requestsingle", data); + } + + bool Controller::requestSingle(uint8_t _bank, uint8_t _program) const + { + return requestProgram(_bank, _program, false); + } + + bool Controller::requestMulti(uint8_t _bank, uint8_t _program) const + { + return requestProgram(_bank, _program, true); + } + + bool Controller::requestSingleBank(uint8_t _bank) const + { + std::map<pluginLib::MidiDataType, uint8_t> data; + data.insert(std::make_pair(pluginLib::MidiDataType::Bank, _bank)); + + return sendSysEx("requestsinglebank", data); + } + + bool Controller::requestTotal() const + { + return sendSysEx("requesttotal"); + } + + bool Controller::requestArrangement() const + { + return sendSysEx("requestarrangement"); + } + + bool Controller::sendSysEx(const std::string& _packetType) const { - const uint8_t start[] = {0xf0, 0x00, 0x20, 0x33, 0x01, static_cast<uint8_t>(m_deviceId)}; - msg.insert(msg.begin(), std::begin(start), std::end(start)); - msg.push_back(0xf7); - return msg; + std::map<pluginLib::MidiDataType, uint8_t> params; + return sendSysEx(_packetType, params); + } + + bool Controller::sendSysEx(const std::string& _packetType, std::map<pluginLib::MidiDataType, uint8_t>& _params) const + { + std::vector<uint8_t> sysex; + + _params.insert(std::make_pair(pluginLib::MidiDataType::DeviceId, m_deviceId)); + + if(!createMidiDataFromPacket(sysex, _packetType, _params)) + return false; + + sendSysEx(sysex); + return true; } void Controller::timerCallback() @@ -452,7 +502,20 @@ namespace Virus void Controller::sendParameterChange(const pluginLib::Parameter& _parameter, uint8_t _value) { const auto& desc = _parameter.getDescription(); - sendSysEx(constructMessage({static_cast<uint8_t>(desc.page), _parameter.getPart(), desc.index, _value})); + + sendParameterChange(desc.page, _parameter.getPart(), desc.index, _value); + } + + bool Controller::sendParameterChange(uint8_t _page, uint8_t _part, uint8_t _index, uint8_t _value) const + { + std::map<pluginLib::MidiDataType, uint8_t> data; + + data.insert(std::make_pair(pluginLib::MidiDataType::Page, _page)); + data.insert(std::make_pair(pluginLib::MidiDataType::Part, _part)); + data.insert(std::make_pair(pluginLib::MidiDataType::ParameterIndex, _index)); + data.insert(std::make_pair(pluginLib::MidiDataType::ParameterValue, _value)); + + return sendSysEx("parameterchange", data); } pluginLib::Parameter* Controller::createParameter(pluginLib::Controller& _controller, const pluginLib::Description& _desc, uint8_t _part, int _uid) diff --git a/source/jucePlugin/VirusController.h b/source/jucePlugin/VirusController.h @@ -44,8 +44,6 @@ namespace Virus // this is called by the plug-in on audio thread! void dispatchVirusOut(const std::vector<synthLib::SMidiEvent> &); - void sendParameterChange(const pluginLib::Parameter& _parameter, uint8_t _value) override; - pluginLib::Parameter* createParameter(pluginLib::Controller& _controller, const pluginLib::Description& _desc, uint8_t _part, int _uid) override; static void printMessage(const SysEx &); @@ -83,9 +81,23 @@ namespace Virus juce::PropertiesFile* getConfig() { return m_config; } std::function<void()> onProgramChange = {}; std::function<void()> onMsgDone = {}; - std::vector<uint8_t> constructMessage(SysEx msg) const; - uint8_t getDeviceId() const { return m_deviceId; } + bool requestProgram(uint8_t _bank, uint8_t _program, bool _multi) const; + bool requestSingle(uint8_t _bank, uint8_t _program) const; + bool requestMulti(uint8_t _bank, uint8_t _program) const; + + bool requestSingleBank(uint8_t _bank) const; + + bool requestTotal() const; + bool requestArrangement() const; + + void sendParameterChange(const pluginLib::Parameter& _parameter, uint8_t _value) override; + bool sendParameterChange(uint8_t _page, uint8_t _part, uint8_t _index, uint8_t _value) const; + + bool sendSysEx(const std::string& _packetType) const; + bool sendSysEx(const std::string& _packetType, std::map<pluginLib::MidiDataType, uint8_t>& _params) const; + + uint8_t getDeviceId() const { return m_deviceId; } private: void timerCallback() override; diff --git a/source/jucePlugin/ui3/PatchBrowser.cpp b/source/jucePlugin/ui3/PatchBrowser.cpp @@ -343,15 +343,14 @@ namespace genericVirusUI return; // force to edit buffer - const auto part = m_controller.isMultiMode() ? m_controller.getCurrentPart() : static_cast<uint8_t>(virusLib::ProgramType::SINGLE); + const auto program = m_controller.isMultiMode() ? m_controller.getCurrentPart() : static_cast<uint8_t>(virusLib::ProgramType::SINGLE); auto sysex = m_filteredPatches[idx].sysex; sysex[7] = toMidiByte(virusLib::BankNumber::EditBuffer); - sysex[8] = part; + sysex[8] = program; m_controller.sendSysEx(sysex); - - m_controller.sendSysEx(m_controller.constructMessage({ virusLib::REQUEST_SINGLE, 0x0, part })); + m_controller.requestSingle(0x0, program); } void PatchBrowser::cellDoubleClicked(int rowNumber, int columnId, const MouseEvent&) diff --git a/source/jucePluginLib/controller.cpp b/source/jucePluginLib/controller.cpp @@ -1,5 +1,7 @@ #include "controller.h" +#include <cassert> + #include "parameter.h" namespace pluginLib @@ -156,4 +158,25 @@ namespace pluginLib uint32_t index; return m_descriptions.getIndexByName(index, _name) ? index : InvalidParameterIndex; } + + const MidiPacket* Controller::getMidiPacket(const std::string& _name) const + { + return m_descriptions.getMidiPacket(_name); + } + + bool Controller::createMidiDataFromPacket(std::vector<uint8_t>& _sysex, const std::string& _packetName, const std::map<MidiDataType, uint8_t>& _params) const + { + const auto* m = getMidiPacket(_packetName); + assert(m && "midi packet not found"); + if(!m) + return false; + + if(!m->create(_sysex, _params)) + { + assert(false && "failed to create midi packet"); + _sysex.clear(); + return false; + } + return true; + } } diff --git a/source/jucePluginLib/controller.h b/source/jucePluginLib/controller.h @@ -22,6 +22,9 @@ namespace pluginLib uint32_t getParameterIndexByName(const std::string& _name) const; + const MidiPacket* getMidiPacket(const std::string& _name) const; + bool createMidiDataFromPacket(std::vector<uint8_t>& _sysex, const std::string& _packetName, const std::map<MidiDataType, uint8_t>& _params) const; + protected: virtual Parameter* createParameter(Controller& _controller, const Description& _desc, uint8_t _part, int _uid) = 0; void registerParams(juce::AudioProcessor& _processor); diff --git a/source/jucePluginLib/midipacket.cpp b/source/jucePluginLib/midipacket.cpp @@ -4,7 +4,7 @@ namespace pluginLib { - bool MidiPacket::create(std::vector<uint8_t>& _dst, std::map<MidiDataType, uint8_t>& _data) const + bool MidiPacket::create(std::vector<uint8_t>& _dst, const std::map<MidiDataType, uint8_t>& _data) const { _dst.reserve(size()); diff --git a/source/jucePluginLib/midipacket.h b/source/jucePluginLib/midipacket.h @@ -41,7 +41,7 @@ namespace pluginLib const std::vector<MidiByte>& bytes() { return m_bytes; } size_t size() const { return m_bytes.size(); } - bool create(std::vector<uint8_t>& _dst, std::map<MidiDataType, uint8_t>& _data) const; + bool create(std::vector<uint8_t>& _dst, const std::map<MidiDataType, uint8_t>& _data) const; private: std::vector<MidiByte> m_bytes; diff --git a/source/jucePluginLib/parameterdescriptions.cpp b/source/jucePluginLib/parameterdescriptions.cpp @@ -12,6 +12,12 @@ namespace pluginLib LOG(err); } + const MidiPacket* ParameterDescriptions::getMidiPacket(const std::string& _name) const + { + const auto it = m_midiPackets.find(_name); + return it == m_midiPackets.end() ? nullptr : &it->second; + } + std::string ParameterDescriptions::removeComments(std::string _json) { auto removeBlock = [&](const std::string& _begin, const std::string& _end) diff --git a/source/jucePluginLib/parameterdescriptions.h b/source/jucePluginLib/parameterdescriptions.h @@ -19,6 +19,8 @@ namespace pluginLib return m_descriptions; } + const MidiPacket* getMidiPacket(const std::string& _name) const; + static std::string removeComments(std::string _json); bool getIndexByName(uint32_t& _index, const std::string& _name) const;