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 6f903c4164f4360019907de9db5f0e07b22fc863
parent 11c66c24a10c52f60cd58258fcf97e5ced439b48
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Thu, 30 Dec 2021 22:16:52 +0100

rework bank numbers to use strong typed enum to prevent -1/+1 problem at compile time

Diffstat:
Msource/jucePlugin/PluginEditor.cpp | 11++++++-----
Msource/jucePlugin/VirusController.cpp | 37++++++++++++++++++++++++++-----------
Msource/jucePlugin/VirusController.h | 15++++++++++-----
Msource/jucePlugin/VirusParameter.h | 4++--
Msource/virusLib/CMakeLists.txt | 2+-
Msource/virusLib/microcontroller.cpp | 105++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msource/virusLib/microcontroller.h | 8++++----
Asource/virusLib/microcontrollerTypes.cpp | 26++++++++++++++++++++++++++
Msource/virusLib/microcontrollerTypes.h | 24++++++++++++++++++++++--
Msource/virusTestConsole/virusTestConsole.cpp | 2+-
10 files changed, 154 insertions(+), 80 deletions(-)

diff --git a/source/jucePlugin/PluginEditor.cpp b/source/jucePlugin/PluginEditor.cpp @@ -48,19 +48,20 @@ AudioPluginAudioProcessorEditor::AudioPluginAudioProcessorEditor(AudioPluginAudi loadFile(); }; - for (auto pt = 0; pt < 16; pt++) + for (uint8_t pt = 0; pt < 16; pt++) { m_partSelectors[pt].onClick = [this, pt]() { juce::PopupMenu selector; - for(auto b=0; b<processorRef.getController().getBankCount(); ++b) + for(uint8_t b=0; b<processorRef.getController().getBankCount(); ++b) { - auto bank = processorRef.getController().getSinglePresetNames(b); + const auto bank = virusLib::fromArrayIndex(b); + auto presetNames = processorRef.getController().getSinglePresetNames(bank); juce::PopupMenu p; - for (auto i = 0; i < 128; i++) + for (uint8_t i = 0; i < 128; i++) { - p.addItem(bank[i], [this, b, i, pt] { processorRef.getController().setCurrentPartPreset(pt, b, i); }); + p.addItem(presetNames[i], [this, bank, i, pt] { processorRef.getController().setCurrentPartPreset(pt, bank, i); }); } std::stringstream bankName; bankName << "Bank " << static_cast<char>('A' + b); diff --git a/source/jucePlugin/VirusController.cpp b/source/jucePlugin/VirusController.cpp @@ -180,8 +180,16 @@ namespace Virus */ } - juce::StringArray Controller::getSinglePresetNames(int bank) const + juce::StringArray Controller::getSinglePresetNames(virusLib::BankNumber _bank) const { + if (_bank == virusLib::BankNumber::EditBuffer) + { + jassertfalse; + return {}; + } + + const auto bank = virusLib::toArrayIndex(_bank); + if (bank >= m_singles.size() || bank < 0) { jassertfalse; @@ -212,9 +220,17 @@ namespace Virus return juce::String(text); } - void Controller::setCurrentPartPreset(uint8_t part, uint8_t bank, uint8_t prg) + void Controller::setCurrentPartPreset(uint8_t part, virusLib::BankNumber _bank, uint8_t prg) { - if (bank < 0 || bank >= m_singles.size() || prg < 0 || prg > 127) + if(_bank == virusLib::BankNumber::EditBuffer || prg < 0 || prg > 127) + { + jassertfalse; + return; + } + + const auto bank = virusLib::toArrayIndex(_bank); + + if (bank >= m_singles.size()) { jassertfalse; return; @@ -227,11 +243,11 @@ namespace Virus patch.push_back(preset.data[i]); sendSysEx(constructMessage(patch)); sendSysEx(constructMessage({MessageType::REQUEST_ARRANGEMENT})); - m_currentBank[part] = bank; + m_currentBank[part] = _bank; m_currentProgram[part] = prg; } - uint8_t Controller::getCurrentPartBank(uint8_t part) { return m_currentBank[part]; } + virusLib::BankNumber Controller::getCurrentPartBank(uint8_t part) { return m_currentBank[part]; } uint8_t Controller::getCurrentPartProgram(uint8_t part) { return m_currentProgram[part]; } void Controller::parseSingle(const SysEx &msg) { @@ -243,7 +259,7 @@ namespace Virus assert(hasChecksum || dataSize == expectedDataSize); SinglePatch patch; - patch.bankNumber = msg[kHeaderWithMsgCodeLen]; + patch.bankNumber = virusLib::fromMidiByte(msg[kHeaderWithMsgCodeLen]); patch.progNumber = msg[kHeaderWithMsgCodeLen + 1]; [[maybe_unused]] const auto dataSum = copyData(msg, kHeaderWithMsgCodeLen + 2, patch.data); @@ -251,16 +267,15 @@ namespace Virus { const auto checksum = msg[msg.size() - 2]; const auto deviceId = msg[5]; - [[maybe_unused]] const auto expectedSum = - (deviceId + 0x10 + patch.bankNumber + patch.progNumber + dataSum) & 0x7f; + [[maybe_unused]] const auto expectedSum = (deviceId + 0x10 + virusLib::toMidiByte(patch.bankNumber) + patch.progNumber + dataSum) & 0x7f; assert(expectedSum == checksum); } - if (patch.bankNumber == 0) + if (patch.bankNumber == virusLib::BankNumber::EditBuffer) { // virus sends also the single buffer not matter what's the mode. // instead of keeping both, we 'encapsulate' this into first channel. // the logic to maintain this is done by listening the global single/multi param. - if (isMultiMode() && patch.progNumber == 0x40) + if (isMultiMode() && patch.progNumber == virusLib::SINGLE) return; else if (!isMultiMode() && patch.progNumber == 0x0) return; @@ -274,7 +289,7 @@ namespace Virus } } else - m_singles[patch.bankNumber - 1][patch.progNumber] = patch; + m_singles[virusLib::toArrayIndex(patch.bankNumber)][patch.progNumber] = patch; const auto namePos = kHeaderWithMsgCodeLen + 2 + 128 + 112; assert(namePos < msg.size()); diff --git a/source/jucePlugin/VirusController.h b/source/jucePlugin/VirusController.h @@ -4,6 +4,11 @@ #include "../synthLib/plugin.h" #include "VirusParameter.h" +namespace virusLib +{ + enum class BankNumber : uint8_t; +} + class AudioPluginAudioProcessor; namespace Virus @@ -29,12 +34,12 @@ namespace Virus juce::Value *getParam(uint8_t ch, uint8_t bank, uint8_t paramIndex); // bank - 0-1 (AB) - juce::StringArray getSinglePresetNames(int bank) const; + juce::StringArray getSinglePresetNames(virusLib::BankNumber bank) const; juce::StringArray getMultiPresetsName() const; bool isMultiMode() { return getParam(0, 2, 0x7a)->getValue(); } // part 0 - 15 (ignored when single! 0x40...) - void setCurrentPartPreset(uint8_t part, uint8_t bank, uint8_t prg); - uint8_t getCurrentPartBank(uint8_t part); + void setCurrentPartPreset(uint8_t part, virusLib::BankNumber bank, uint8_t prg); + virusLib::BankNumber getCurrentPartBank(uint8_t part); uint8_t getCurrentPartProgram(uint8_t part); juce::String getCurrentPartPresetName(uint8_t part); uint32_t getBankCount() const { return static_cast<uint32_t>(m_singles.size()); } @@ -53,7 +58,7 @@ namespace Virus struct SinglePatch { - uint8_t bankNumber; + virusLib::BankNumber bankNumber; uint8_t progNumber; uint8_t data[kDataSizeInBytes]; }; @@ -103,7 +108,7 @@ namespace Virus juce::CriticalSection m_eventQueueLock; std::vector<synthLib::SMidiEvent> m_virusOut; unsigned char m_deviceId; - uint8_t m_currentBank[16]; + virusLib::BankNumber m_currentBank[16]; uint8_t m_currentProgram[16]; }; }; // namespace Virus diff --git a/source/jucePlugin/VirusParameter.h b/source/jucePlugin/VirusParameter.h @@ -69,8 +69,8 @@ namespace Virus { // brute force but this should be O(1) of 128... for (auto i = 0; i < 128; i++) - if (m_desc.valueToTextFunction(i, m_desc) == text) - return convertTo0to1(i); + if (m_desc.valueToTextFunction(static_cast<float>(i), m_desc) == text) + return convertTo0to1(static_cast<float>(i)); } return convertTo0to1(text.getFloatValue()); } diff --git a/source/virusLib/CMakeLists.txt b/source/virusLib/CMakeLists.txt @@ -8,7 +8,7 @@ set(SOURCES midiOutParser.cpp midiOutParser.h romfile.cpp romfile.h microcontroller.cpp microcontroller.h - microcontrollerTypes.h + microcontrollerTypes.cpp microcontrollerTypes.h ) target_sources(virusLib PRIVATE ${SOURCES}) diff --git a/source/virusLib/microcontroller.cpp b/source/virusLib/microcontroller.cpp @@ -96,7 +96,7 @@ void Microcontroller::createDefaultState() sendControlCommand(PLAY_MODE, g_defaultPlayMode); if constexpr (g_defaultPlayMode == PlayModeSingle) - writeSingle(0, SINGLE, m_singleEditBuffer); + writeSingle(BankNumber::EditBuffer, SINGLE, m_singleEditBuffer); else loadMulti(0, m_multiEditBuffer); } @@ -199,7 +199,7 @@ bool Microcontroller::sendMIDI(const SMidiEvent& _ev, bool cancelIfFull/* = fals if(getSingle(m_currentBank, _ev.b, single)) { m_currentSingle = _ev.b; - return writeSingle(0, SINGLE, single); + return writeSingle(BankNumber::EditBuffer, SINGLE, single); } } else @@ -262,7 +262,7 @@ bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, bool _cancelI response.push_back(deviceId); }; - auto buildPresetResponse = [&](const uint8_t _type, const uint8_t _bank, const uint8_t _program, const TPreset& _dump) + auto buildPresetResponse = [&](const uint8_t _type, const BankNumber _bank, const uint8_t _program, const TPreset& _dump) { SMidiEvent ev; ev.source = _source; @@ -272,7 +272,7 @@ bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, bool _cancelI buildResponseHeader(ev); response.push_back(_type); - response.push_back(_bank); + response.push_back(toMidiByte(_bank)); response.push_back(_program); for(const auto value : _dump) @@ -293,7 +293,7 @@ bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, bool _cancelI _responses.emplace_back(std::move(ev)); }; - auto buildSingleResponse = [&](const uint8_t _bank, const uint8_t _program) + auto buildSingleResponse = [&](const BankNumber _bank, const uint8_t _program) { TPreset dump; const auto res = requestSingle(_bank, _program, dump); @@ -301,7 +301,7 @@ bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, bool _cancelI buildPresetResponse(DUMP_SINGLE, _bank, _program, dump); }; - auto buildMultiResponse = [&](const uint8_t _bank, const uint8_t _program) + auto buildMultiResponse = [&](const BankNumber _bank, const uint8_t _program) { TPreset dump; const auto res = requestMulti(_bank, _program, dump); @@ -309,12 +309,17 @@ bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, bool _cancelI buildPresetResponse(DUMP_MULTI, _bank, _program, dump); }; - auto buildSingleBankResponse = [&](const uint8_t _bank) + auto buildSingleBankResponse = [&](const BankNumber _bank) { - if(_bank > 0 && _bank <= m_singles.size()) + if (_bank == BankNumber::EditBuffer) + return; + + const auto bankIndex = toArrayIndex(_bank); + + if(bankIndex < m_singles.size()) { // eat this, host, whoever you are. 128 single packets - for(uint8_t i=0; i<m_singles[_bank-1].size(); ++i) + for(uint8_t i=0; i<m_singles[bankIndex].size(); ++i) { TPreset data; const auto res = requestSingle(_bank, i, data); @@ -323,9 +328,9 @@ bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, bool _cancelI } }; - auto buildMultiBankResponse = [&](const uint8_t _bank) + auto buildMultiBankResponse = [&](const BankNumber _bank) { - if(_bank == 1) + if(_bank == BankNumber::A) { // eat this, host, whoever you are. 128 multi packets for(uint8_t i=0; i<g_presetsPerBank; ++i) @@ -363,9 +368,9 @@ bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, bool _cancelI auto buildTotalResponse = [&]() { buildGlobalResponses(); - buildSingleBankResponse(1); - buildSingleBankResponse(2); - buildMultiBankResponse(1); + buildSingleBankResponse(BankNumber::A); + buildSingleBankResponse(BankNumber::B); + buildMultiBankResponse(BankNumber::A); }; auto buildArrangementResponse = [&]() @@ -375,22 +380,22 @@ bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, bool _cancelI const bool isMultiMode = m_globalSettings[PLAY_MODE] == PlayModeMulti; if(isMultiMode) - buildSingleResponse(0, SINGLE); + buildSingleResponse(BankNumber::EditBuffer, SINGLE); - buildMultiResponse(0, 0); + buildMultiResponse(BankNumber::EditBuffer, 0); for(uint8_t p=0; p<16; ++p) - buildPresetResponse(DUMP_SINGLE, 0, p, m_singleEditBuffers[p]); + buildPresetResponse(DUMP_SINGLE, BankNumber::EditBuffer, p, m_singleEditBuffers[p]); if(!isMultiMode) - buildSingleResponse(0, SINGLE); + buildSingleResponse(BankNumber::EditBuffer, SINGLE); }; auto buildControllerDumpResponse = [&](uint8_t _part) { TPreset _dump, _multi; - const auto res = requestSingle(0, _part, _dump); - const auto resm = requestMulti(0, 0, _multi); + const auto res = requestSingle(BankNumber::EditBuffer, _part, _dump); + const auto resm = requestMulti(BankNumber::EditBuffer, 0, _multi); const uint8_t channel = _part == SINGLE ? m_globalSettings[GLOBAL_CHANNEL] : _multi[static_cast<size_t>(MD_PART_MIDI_CHANNEL) + _part]; for (const auto cc : g_pageA) { @@ -417,33 +422,33 @@ bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, bool _cancelI { case DUMP_SINGLE: { - const uint8_t bank = _data[7]; + const auto bank = fromMidiByte(_data[7]); const uint8_t program = _data[8]; - LOG("Received Single dump, Bank " << (int)bank << ", program " << (int)program); + LOG("Received Single dump, Bank " << (int)toMidiByte(bank) << ", program " << (int)program); TPreset dump; std::copy_n(_data.data() + g_sysexPresetHeaderSize, dump.size(), dump.begin()); return writeSingle(bank, program, dump); } case DUMP_MULTI: { - const uint8_t bank = _data[7]; + const auto bank = fromMidiByte(_data[7]); const uint8_t program = _data[8]; - LOG("Received Multi dump, Bank " << (int)bank << ", program " << (int)program); + LOG("Received Multi dump, Bank " << (int)toMidiByte(bank) << ", program " << (int)program); TPreset dump; std::copy_n(_data.data() + g_sysexPresetHeaderSize, dump.size(), dump.begin()); return writeMulti(bank, program, dump); } case REQUEST_SINGLE: { - const uint8_t bank = _data[7]; + const auto bank = fromMidiByte(_data[7]); const uint8_t program = _data[8]; - LOG("Request Single, Bank " << (int)bank << ", program " << (int)program); + LOG("Request Single, Bank " << (int)toMidiByte(bank) << ", program " << (int)program); buildSingleResponse(bank, program); break; } case REQUEST_MULTI: { - const uint8_t bank = _data[7]; + const auto bank = fromMidiByte(_data[7]); const uint8_t program = _data[8]; LOG("Request Multi, Bank " << (int)bank << ", program " << (int)program); buildMultiResponse(bank, program); @@ -451,19 +456,19 @@ bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, bool _cancelI } case REQUEST_BANK_SINGLE: { - const uint8_t bank = _data[7]; + const auto bank = fromMidiByte(_data[7]); buildSingleBankResponse(bank); break; } case REQUEST_BANK_MULTI: { - const uint8_t bank = _data[7]; + const auto bank = fromMidiByte(_data[7]); buildMultiBankResponse(bank); break; } case REQUEST_CONTROLLER_DUMP: { - const uint8_t part = _data[8]; + const auto part = _data[8]; if (part < 16 || part == SINGLE) buildControllerDumpResponse(part); break; @@ -506,15 +511,15 @@ bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, bool _cancelI m_globalSettings[PLAY_MODE] = playMode; LOG("Switch to Single mode"); - return writeSingle(0, SINGLE, m_singleEditBuffer); + return writeSingle(BankNumber::EditBuffer, SINGLE, m_singleEditBuffer); } case PlayModeMultiSingle: case PlayModeMulti: { m_globalSettings[PLAY_MODE] = PlayModeMulti; - writeMulti(0, 0, m_multiEditBuffer); + writeMulti(BankNumber::EditBuffer, 0, m_multiEditBuffer); for(uint8_t i=0; i<16; ++i) - writeSingle(0, i, m_singleEditBuffers[i]); + writeSingle(BankNumber::EditBuffer, i, m_singleEditBuffers[i]); return true; } default: @@ -610,25 +615,25 @@ void Microcontroller::waitUntilReady() const } } -bool Microcontroller::requestMulti(uint8_t _bank, uint8_t _program, TPreset& _data) const +bool Microcontroller::requestMulti(BankNumber _bank, uint8_t _program, TPreset& _data) const { - if (_bank == 0) + if (_bank == BankNumber::EditBuffer) { // Use multi-edit buffer _data = m_multiEditBuffer; return true; } - if (_bank != 1) + if (_bank != BankNumber::A) return false; // Load from flash return m_rom.getMulti(_program, _data); } -bool Microcontroller::requestSingle(uint8_t _bank, uint8_t _program, TPreset& _data) const +bool Microcontroller::requestSingle(BankNumber _bank, uint8_t _program, TPreset& _data) const { - if (_bank == 0) + if (_bank == BankNumber::EditBuffer) { // Use single-edit buffer if(_program == SINGLE) @@ -640,20 +645,22 @@ bool Microcontroller::requestSingle(uint8_t _bank, uint8_t _program, TPreset& _d } // Load from flash - return getSingle(_bank - 1, _program, _data); + return getSingle(toArrayIndex(_bank), _program, _data); } -bool Microcontroller::writeSingle(uint8_t _bank, uint8_t _program, const TPreset& _data) +bool Microcontroller::writeSingle(BankNumber _bank, uint8_t _program, const TPreset& _data) { - if (_bank > 0) + if (_bank != BankNumber::EditBuffer) { - if(_bank >= m_singles.size() || _bank >= g_singleRamBankCount) + const auto bank = toArrayIndex(_bank); + + if(bank >= m_singles.size() || bank >= g_singleRamBankCount) return true; // out of range - if(_program >= m_singles[_bank].size()) + if(_program >= m_singles[bank].size()) return true; // out of range - m_singles[_bank][_program] = _data; + m_singles[bank][_program] = _data; return true; } @@ -669,11 +676,11 @@ bool Microcontroller::writeSingle(uint8_t _bank, uint8_t _program, const TPreset return sendPreset(_program, presetToDSPWords(_data), false); } -bool Microcontroller::writeMulti(uint8_t _bank, uint8_t _program, const TPreset& _data) +bool Microcontroller::writeMulti(BankNumber _bank, uint8_t _program, const TPreset& _data) { - if (_bank != 0) + if (_bank != BankNumber::EditBuffer) { - LOG("We do not support writing to flash, attempt to write multi to bank " << _bank << ", program " << _program); + LOG("We do not support writing to RAM or ROM, attempt to write multi to bank " << static_cast<int>(toMidiByte(_bank)) << ", program " << static_cast<int>(_program)); return true; } @@ -704,7 +711,7 @@ bool Microcontroller::partProgramChange(const uint8_t _part, const uint8_t _valu if(getSingle(bank, _value, single)) { m_multiEditBuffer[MD_PART_PROGRAM_NUMBER + _part] = _value; - return writeSingle(0, _part, single); + return writeSingle(BankNumber::EditBuffer, _part, single); } return true; @@ -722,7 +729,7 @@ bool Microcontroller::multiProgramChange(uint8_t _value) bool Microcontroller::loadMulti(uint8_t _program, const TPreset& _multi) { - if(!writeMulti(0, _program, _multi)) + if(!writeMulti(BankNumber::EditBuffer, _program, _multi)) return false; for (uint8_t p = 0; p < 16; ++p) diff --git a/source/virusLib/microcontroller.h b/source/virusLib/microcontroller.h @@ -25,10 +25,10 @@ public: bool sendMIDI(const synthLib::SMidiEvent& _ev, bool cancelIfFull = false); bool sendSysex(const std::vector<uint8_t>& _data, bool _cancelIfFull, std::vector<synthLib::SMidiEvent>& _responses, synthLib::MidiEventSource _source); - bool writeSingle(uint8_t _bank, uint8_t _program, const TPreset& _data); - bool writeMulti(uint8_t _bank, uint8_t _program, const TPreset& _data); - bool requestMulti(uint8_t _bank, uint8_t _program, TPreset& _data) const; - bool requestSingle(uint8_t _bank, uint8_t _program, TPreset& _data) const; + bool writeSingle(BankNumber _bank, uint8_t _program, const TPreset& _data); + bool writeMulti(BankNumber _bank, uint8_t _program, const TPreset& _data); + bool requestMulti(BankNumber _bank, uint8_t _program, TPreset& _data) const; + bool requestSingle(BankNumber _bank, uint8_t _program, TPreset& _data) const; void sendInitControlCommands(); diff --git a/source/virusLib/microcontrollerTypes.cpp b/source/virusLib/microcontrollerTypes.cpp @@ -0,0 +1,26 @@ +#include "microcontrollerTypes.h" + +#include <cassert> + +namespace virusLib +{ + uint8_t toMidiByte(BankNumber _bank) + { + return static_cast<uint8_t>(_bank); + } + BankNumber fromMidiByte(uint8_t _byte) + { + return static_cast<BankNumber>(_byte); + } + uint32_t toArrayIndex(BankNumber _bank) + { + const auto bank = static_cast<uint8_t>(_bank); + assert(bank > 0); + return bank - 1; + } + + BankNumber fromArrayIndex(uint8_t _bank) + { + return static_cast<BankNumber>(_bank + 1); + } +} diff --git a/source/virusLib/microcontrollerTypes.h b/source/virusLib/microcontrollerTypes.h @@ -1,5 +1,7 @@ #pragma once +#include <cstdint> + namespace virusLib { enum SysexMessageType : uint8_t @@ -158,4 +160,23 @@ namespace virusLib }; static constexpr uint8_t OMNI_DEVICE_ID = 0x10; -} -\ No newline at end of file + + enum class BankNumber : uint8_t + { + EditBuffer, + A, + B, + C, + D, + E, + F, + G, + H, + Count + }; + + uint8_t toMidiByte(BankNumber _bank); + BankNumber fromMidiByte(uint8_t _byte); + uint32_t toArrayIndex(BankNumber _bank); + BankNumber fromArrayIndex(uint8_t _bank); +} diff --git a/source/virusTestConsole/virusTestConsole.cpp b/source/virusTestConsole/virusTestConsole.cpp @@ -51,7 +51,7 @@ void audioCallback(dsp56k::Audio* audio) break; case 64: LOG("Sending Preset"); - microcontroller->writeSingle(0, SINGLE, preset); + microcontroller->writeSingle(BankNumber::EditBuffer, SINGLE, preset); break; case 128: LOG("Sending Note On");