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 754aa7fda42a6e99cdf7f447996ebb2bf045f79a
parent 13c8960f3b786cd83c6f8a249b2379e390739ade
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Thu, 30 Dec 2021 22:48:03 +0100

Merge branch 'dsp56300' into dsp56300_ui

Diffstat:
Msource/jucePlugin/PluginEditor.cpp | 111++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msource/jucePlugin/PluginProcessor.cpp | 2++
Msource/jucePlugin/VirusController.cpp | 42+++++++++++++++++++++++++++++++-----------
Msource/jucePlugin/VirusController.h | 18++++++++++++------
Msource/jucePlugin/VirusParameter.h | 4++--
Msource/virusLib/CMakeLists.txt | 2+-
Msource/virusLib/microcontroller.cpp | 121++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msource/virusLib/microcontroller.h | 10+++++-----
Asource/virusLib/microcontrollerTypes.cpp | 26++++++++++++++++++++++++++
Msource/virusLib/microcontrollerTypes.h | 24++++++++++++++++++++++--
Msource/virusTestConsole/virusTestConsole.cpp | 2+-
11 files changed, 228 insertions(+), 134 deletions(-)

diff --git a/source/jucePlugin/PluginEditor.cpp b/source/jucePlugin/PluginEditor.cpp @@ -51,19 +51,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); @@ -280,63 +281,70 @@ void AudioPluginAudioProcessorEditor::loadFile() { m_previousPath.isEmpty() ? juce::File::getSpecialLocation(juce::File::currentApplicationFile).getParentDirectory() : m_previousPath, "*.syx,*.mid,*.midi", true); - const bool result = chooser.browseForFileToOpen(); - if (result) + + if (!chooser.browseForFileToOpen()) + return; + bool sentData = false; + const auto result = chooser.getResult(); + m_previousPath = result.getParentDirectory().getFullPathName(); + const auto ext = result.getFileExtension().toLowerCase(); + if (ext == ".syx") { - const auto result = chooser.getResult(); - m_previousPath = result.getParentDirectory().getFullPathName(); - const auto ext = result.getFileExtension().toLowerCase(); - if (ext == ".syx") + juce::MemoryBlock data; + result.loadFileAsData(data); + for (auto it = data.begin(); it != data.end(); it += 267) { - juce::MemoryBlock data; - result.loadFileAsData(data); - for (auto it = data.begin(); it != data.end(); it += 267) + if ((it + 267) < data.end()) { - if ((it + 267) < data.end()) - { - processorRef.getController().parseMessage(Virus::SysEx(it, it + 267)); - } + processorRef.getController().sendSysEx(Virus::SysEx(it, it + 267)); + sentData = true; } - m_btLoadFile.setButtonText("Loaded"); } - else if (ext == ".mid" || ext == ".midi") + m_btLoadFile.setButtonText("Loaded"); + } + else if (ext == ".mid" || ext == ".midi") + { + juce::MemoryBlock data; + if (!result.loadFileAsData(data)) { - juce::MemoryBlock data; - if (!result.loadFileAsData(data)) - { - return; - } - const uint8_t *ptr = (uint8_t *)data.getData(); - const auto end = ptr + data.getSize(); + return; + } + const uint8_t *ptr = (uint8_t *)data.getData(); + const auto end = ptr + data.getSize(); - for (auto it = ptr; it < end; it += 1) + for (auto it = ptr; it < end; it += 1) + { + if ((uint8_t)*it == (uint8_t)0xf0 && (it+267) < end) { - if ((uint8_t)*it == (uint8_t)0xf0 && (it+267) < end) + if ((uint8_t) *(it + 1) == (uint8_t)0x00) { - if ((uint8_t) *(it + 1) == (uint8_t)0x00) - { - auto syx = Virus::SysEx(it, it + 267); - syx[7] = 0x01; // force to bank a - syx[266] = 0xf7; - processorRef.getController().parseMessage(syx); - - it += 266; - } - else // some midi files have two bytes after the 0xf0 + auto syx = Virus::SysEx(it, it + 267); + syx[7] = 0x01; // force to bank a + syx[266] = 0xf7; + + processorRef.getController().sendSysEx(syx); + + it += 266; + } + else // some midi files have two bytes after the 0xf0 + { + auto syx = Virus::SysEx(); + syx.push_back(0xf0); + for (auto i = it + 3; i < it + 3 + 266; i++) { - auto syx = Virus::SysEx(); - syx.push_back(0xf0); - for (auto i = it + 3; i < it + 3 + 266; i++) - { - syx.push_back((uint8_t)*i); - } - syx[7] = 0x01; // force to bank a - syx[266] = 0xf7; - processorRef.getController().parseMessage(syx); - it += 266; + syx.push_back((uint8_t)*i); } + syx[7] = 0x01; // force to bank a + syx[266] = 0xf7; + processorRef.getController().sendSysEx(syx); + it += 266; } + + sentData = true; } } - } -} -\ No newline at end of file + } + + if (sentData) + processorRef.getController().onStateLoaded(); +} diff --git a/source/jucePlugin/PluginProcessor.cpp b/source/jucePlugin/PluginProcessor.cpp @@ -398,6 +398,8 @@ void AudioPluginAudioProcessor::setState(const void* _data, size_t _sizeInBytes) state.resize(_sizeInBytes); memcpy(&state[0], _data, _sizeInBytes); m_plugin.setState(state); + if (m_controller) + m_controller->onStateLoaded(); } //============================================================================== diff --git a/source/jucePlugin/VirusController.cpp b/source/jucePlugin/VirusController.cpp @@ -216,8 +216,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; @@ -248,9 +256,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; @@ -263,11 +279,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) { @@ -279,7 +295,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); @@ -287,16 +303,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; @@ -310,7 +325,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()); @@ -1679,6 +1694,11 @@ namespace Virus m_processor.addMidiEvent(ev); } + void Controller::onStateLoaded() + { + sendSysEx(constructMessage({ MessageType::REQUEST_TOTAL })); + } + std::vector<uint8_t> Controller::constructMessage(SysEx msg) { const uint8_t start[] = {0xf0, 0x00, 0x20, 0x33, 0x01, static_cast<uint8_t>(m_deviceId)}; 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 @@ -399,18 +404,19 @@ namespace Virus Parameter* getParameter(ParameterType _param); // bank - 0-1 (AB) - juce::StringArray getSinglePresetNames(int bank) const; + juce::StringArray getSinglePresetNames(virusLib::BankNumber bank) const; juce::StringArray getMultiPresetsName() const; bool isMultiMode() { return getParamValue(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()); } void parseMessage(const SysEx &); void sendSysEx(const SysEx &); - private: + void onStateLoaded(); + private: void timerCallback() override; static constexpr size_t kDataSizeInBytes = 256; // same for multi and single @@ -423,7 +429,7 @@ namespace Virus struct SinglePatch { - uint8_t bankNumber; + virusLib::BankNumber bankNumber; uint8_t progNumber; uint8_t data[kDataSizeInBytes]; }; @@ -475,7 +481,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 @@ -68,8 +68,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); } @@ -196,10 +196,10 @@ bool Microcontroller::sendMIDI(const SMidiEvent& _ev, bool cancelIfFull/* = fals if(singleMode) { - if(getSingle(m_currentBank, _ev.b, single)) + if(getSingle(fromArrayIndex(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: @@ -588,11 +593,17 @@ std::vector<TWord> Microcontroller::presetToDSPWords(const TPreset& _preset) return preset; } -bool Microcontroller::getSingle(uint32_t _bank, uint32_t _preset, TPreset& _result) const +bool Microcontroller::getSingle(BankNumber _bank, uint32_t _preset, TPreset& _result) const { - if(_bank >= m_singles.size()) + if (_bank == BankNumber::EditBuffer) return false; - const auto& s = m_singles[_bank]; + + const auto bank = toArrayIndex(_bank); + + if(bank >= m_singles.size()) + return false; + + const auto& s = m_singles[bank]; if(_preset >= s.size()) return false; @@ -610,25 +621,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 +651,22 @@ bool Microcontroller::requestSingle(uint8_t _bank, uint8_t _program, TPreset& _d } // Load from flash - return getSingle(_bank - 1, _program, _data); + return getSingle(_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 +682,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; } @@ -699,12 +712,12 @@ bool Microcontroller::partProgramChange(const uint8_t _part, const uint8_t _valu { TPreset single; - const auto bank = m_multiEditBuffer[MD_PART_BANK_NUMBER + _part]; + const auto bank = fromMidiByte(m_multiEditBuffer[MD_PART_BANK_NUMBER + _part]); 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 +735,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(); @@ -51,7 +51,7 @@ private: void waitUntilReady() const; void waitUntilBufferEmpty() const; static std::vector<dsp56k::TWord> presetToDSPWords(const TPreset& _preset); - bool getSingle(uint32_t _bank, uint32_t _preset, TPreset& _result) const; + bool getSingle(BankNumber _bank, uint32_t _preset, TPreset& _result) const; bool partBankSelect(uint8_t _part, uint8_t _value, bool _immediatelySelectSingle); bool partProgramChange(uint8_t _part, uint8_t _value); 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");