commit 86dbbba430c784f80664acc3b19233239e25c98e
parent 69a035101fada3d50b2176d3156d521477a5f046
Author: 790 <790@users.noreply.github.com>
Date: Wed, 12 Jan 2022 01:04:06 +0000
move parts to separate component. sync clock to host
Diffstat:
12 files changed, 378 insertions(+), 204 deletions(-)
diff --git a/source/jucePlugin/PluginProcessor.cpp b/source/jucePlugin/PluginProcessor.cpp
@@ -191,9 +191,18 @@ void AudioPluginAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer,
juce::AudioPlayHead::CurrentPositionInfo pos{};
auto* playHead = getPlayHead();
- if(playHead)
+ if(playHead) {
playHead->getCurrentPosition(pos);
+ if(pos.bpm > 0) { // sync virus interal clock to host
+ const uint8_t bpmValue = juce::jmin(127, juce::jmax(0, (int)pos.bpm-63)); // clamp to virus range, 63-190
+ auto clockParam = getController().getParameter(Virus::Param_ClockTempo, 0);
+ if (clockParam != nullptr && (int)clockParam->getValueObject().getValue() != bpmValue) {
+ clockParam->getValueObject().setValue(bpmValue);
+ }
+ }
+ }
+
m_plugin.process(inputs, outputs, buffer.getNumSamples(), static_cast<float>(pos.bpm),
static_cast<float>(pos.ppqPosition), pos.isPlaying);
diff --git a/source/jucePlugin/VirusController.cpp b/source/jucePlugin/VirusController.cpp
@@ -27,7 +27,6 @@ namespace Virus
for(uint8_t i=3; i<=8; ++i)
sendSysEx(constructMessage({MessageType::REQUEST_BANK_SINGLE, i}));
-
startTimer(5);
}
@@ -377,6 +376,9 @@ namespace Virus
p->setValueFromSynth(patch.data[virusLib::MD_PART_MIDI_CHANNEL + (i*16) + pt], true);
}
}
+ if (auto* p = findSynthParam(pt, virusLib::PAGE_B, virusLib::CLOCK_TEMPO)) {
+ p->setValueFromSynth(patch.data[virusLib::MD_CLOCK_TEMPO], true);
+ }
}
}
if (hasChecksum)
@@ -1528,7 +1530,7 @@ namespace Virus
{Parameter::Page::B, Parameter::Class::SOUNDBANK_B, 11, "Lfo3 Destination", {0,5}, numToLfoDest, {}, true, true, false},
{Parameter::Page::B, Parameter::Class::SOUNDBANK_B, 12, "Osc Lfo3 Amount", {0,127}, {},{}, true, false, false},
{Parameter::Page::B, Parameter::Class::SOUNDBANK_B, 13, "Lfo3 Fade-In Time", {0,127}, {},{}, true, false, false},
- {Parameter::Page::B, Parameter::Class::SOUNDBANK_B, 16, "Clock Tempo", {0,127}, numToClockTempo, {}, true, false, false},
+ {Parameter::Page::B, Parameter::Class::SOUNDBANK_B|Parameter::Class::MULTI_OR_SINGLE|Parameter::Class::NON_PART_SENSITIVE, 16, "Clock Tempo", {0,127}, numToClockTempo, {}, true, false, false},
{Parameter::Page::B, Parameter::Class::SOUNDBANK_B, 17, "Arp Clock", {0,17}, numToMusicDivision, {}, true, true, false},
{Parameter::Page::B, Parameter::Class::SOUNDBANK_B, 18, "Lfo1 Clock", {0,21}, numToMusicDivision, {}, true, true, false},
{Parameter::Page::B, Parameter::Class::SOUNDBANK_B, 19, "Lfo2 Clock", {0,21}, numToMusicDivision, {}, true, true, false},
diff --git a/source/jucePlugin/VirusParameterBinding.cpp b/source/jucePlugin/VirusParameterBinding.cpp
@@ -67,7 +67,7 @@ void VirusParameterBinding::bind(juce::ComboBox& _combo, Virus::ParameterType _p
v->getValueObject().getValueSource().setValue(_combo.getSelectedId() - 1);
};
- v->onValueChanged = [this, &_combo, v]() { _combo.setSelectedId((int)v->getValueObject().getValueSource().getValue() + 1, juce::NotificationType::dontSendNotification); };
+ v->onValueChanged = [this, &_combo, v]() { _combo.setSelectedId((int)v->getValueObject().getValueSource().getValue() + 1, juce::dontSendNotification); };
m_bindings.add(v);
}
diff --git a/source/jucePlugin/ui/VirusEditor.cpp b/source/jucePlugin/ui/VirusEditor.cpp
@@ -6,6 +6,7 @@
#include "Virus_LfoEditor.h"
#include "Virus_OscEditor.h"
#include "Virus_PatchBrowser.h"
+#include "Virus_Parts.h"
#include "../VirusParameterBinding.h"
#include "../VirusController.h"
@@ -15,7 +16,7 @@ constexpr auto kPanelWidth = 1377;
constexpr auto kPanelHeight = 800;
VirusEditor::VirusEditor(VirusParameterBinding &_parameterBinding, AudioPluginAudioProcessor &_processorRef) :
- m_parameterBinding(_parameterBinding), processorRef(_processorRef), m_controller(processorRef.getController()), m_btSingleMode("Single\nMode"), m_btMultiSingleMode("Multi\nSingle"), m_btMultiMode("Multi\nMode"),
+ m_parameterBinding(_parameterBinding), processorRef(_processorRef), m_controller(processorRef.getController()),
m_controlLabel("ctrlLabel", "")
{
setLookAndFeel(&m_lookAndFeel);
@@ -31,6 +32,11 @@ VirusEditor::VirusEditor(VirusParameterBinding &_parameterBinding, AudioPluginAu
m_lfoEditor = std::make_unique<LfoEditor>(_parameterBinding);
m_oscEditor = std::make_unique<OscEditor>(_parameterBinding);
m_patchBrowser = std::make_unique<PatchBrowser>(_parameterBinding, m_controller);
+ m_partList = std::make_unique<Parts>(_parameterBinding, m_controller);
+
+ m_partList->setBounds(0,0, 338, kPanelHeight);
+ m_partList->setVisible(true);
+ addChildComponent(m_partList.get());
applyToSections([this](Component *s) { addChildComponent(s); });
@@ -48,121 +54,6 @@ VirusEditor::VirusEditor(VirusParameterBinding &_parameterBinding, AudioPluginAu
m_presetButtons.m_load.onClick = [this]() { loadFile(); };
m_presetButtons.m_save.onClick = [this]() { saveFile(); };
- for (auto pt = 0; pt < 16; pt++)
- {
- m_partLabels[pt].setBounds(34, 161 + pt * (36), 24, 36);
- m_partLabels[pt].setText(juce::String(pt + 1), juce::dontSendNotification);
- m_partLabels[pt].setColour(0, juce::Colours::white);
- m_partLabels[pt].setColour(1, juce::Colour(45, 24, 24));
- m_partLabels[pt].setJustificationType(Justification::centred);
- addAndMakeVisible(m_partLabels[pt]);
-
- m_partSelect[pt].setBounds(35, 161 + pt*(36), 36, 36);
- m_partSelect[pt].setButtonText(juce::String(pt));
- m_partSelect[pt].setRadioGroupId(kPartGroupId);
- m_partSelect[pt].setClickingTogglesState(true);
- m_partSelect[pt].onClick = [this, pt]() {
- this->changePart(pt);
- };
- addAndMakeVisible(m_partSelect[pt]);
-
- m_presetNames[pt].setBounds(80, 171 + pt * (36) - 2, 136, 16 + 4);
- m_presetNames[pt].setButtonText(m_controller.getCurrentPartPresetName(pt));
- m_presetNames[pt].setColour(juce::TextButton::ColourIds::textColourOnId, juce::Colour(255,113,128));
- m_presetNames[pt].setColour(juce::TextButton::ColourIds::textColourOffId, juce::Colour(255, 113, 128));
-
- m_presetNames[pt].onClick = [this, pt]() {
- juce::PopupMenu selector;
-
- for (uint8_t b = 0; b < m_controller.getBankCount(); ++b)
- {
- const auto bank = virusLib::fromArrayIndex(b);
- auto presetNames = m_controller.getSinglePresetNames(bank);
- juce::PopupMenu p;
- for (uint8_t j = 0; j < 128; j++)
- {
- const auto presetName = presetNames[j];
- p.addItem(presetNames[j], [this, bank, j, pt, presetName] {
- m_controller.setCurrentPartPreset(pt, bank, j);
- m_presetNames[pt].setButtonText(presetName);
- });
- }
- std::stringstream bankName;
- bankName << "Bank " << static_cast<char>('A' + b);
- selector.addSubMenu(std::string(bankName.str()), p);
- }
- selector.showMenu(juce::PopupMenu::Options());
- };
- addAndMakeVisible(m_presetNames[pt]);
-
- m_prevPatch[pt].setBounds(228, 173 + 36*pt - 2, 16, 14);
- m_nextPatch[pt].setBounds(247, 173 + 36*pt - 2, 16, 14);
- m_prevPatch[pt].setButtonText("<");
- m_nextPatch[pt].setButtonText(">");
- m_prevPatch[pt].setColour(juce::TextButton::ColourIds::textColourOnId, juce::Colour(255, 113, 128));
- m_nextPatch[pt].setColour(juce::TextButton::ColourIds::textColourOnId, juce::Colour(255, 113, 128));
- m_prevPatch[pt].setColour(juce::TextButton::ColourIds::textColourOffId, juce::Colour(255, 113, 128));
- m_nextPatch[pt].setColour(juce::TextButton::ColourIds::textColourOffId, juce::Colour(255, 113, 128));
- m_prevPatch[pt].onClick = [this, pt]() {
- m_controller.setCurrentPartPreset(
- pt, m_controller.getCurrentPartBank(pt),
- std::max(0, m_controller.getCurrentPartProgram(pt) - 1));
- };
- m_nextPatch[pt].onClick = [this, pt]() {
- m_controller.setCurrentPartPreset(
- pt, m_controller.getCurrentPartBank(pt),
- std::min(127, m_controller.getCurrentPartProgram(pt) + 1));
- };
- addAndMakeVisible(m_prevPatch[pt]);
- addAndMakeVisible(m_nextPatch[pt]);
-
- m_partVolumes[pt].setSliderStyle(juce::Slider::RotaryHorizontalVerticalDrag);
- m_partVolumes[pt].setTextBoxStyle(juce::Slider::NoTextBox, false, 0, 0);
- m_partVolumes[pt].setBounds(m_nextPatch[pt].getBounds().translated(m_nextPatch[pt].getWidth()+8, 0));
- m_partVolumes[pt].setSize(18,18);
- m_partVolumes[pt].getProperties().set(Virus::LookAndFeel::KnobStyleProp, Virus::LookAndFeel::KnobStyle::GENERIC_MULTI);
- _parameterBinding.bind(m_partVolumes[pt], Virus::Param_PartVolume, pt);
- addAndMakeVisible(m_partVolumes[pt]);
-
- m_partPans[pt].setSliderStyle(juce::Slider::RotaryHorizontalVerticalDrag);
- m_partPans[pt].setTextBoxStyle(juce::Slider::NoTextBox, false, 0, 0);
- m_partPans[pt].setBounds(m_partVolumes[pt].getBounds().translated(m_partVolumes[pt].getWidth()+4, 0));
- m_partPans[pt].setSize(18,18);
- m_partPans[pt].getProperties().set(Virus::LookAndFeel::KnobStyleProp, Virus::LookAndFeel::KnobStyle::GENERIC_MULTI);
- _parameterBinding.bind(m_partPans[pt], Virus::Param_Panorama, pt);
- addAndMakeVisible(m_partPans[pt]);
- }
- m_partSelect[m_controller.getCurrentPart()].setToggleState(true, NotificationType::sendNotification);
-
- m_btSingleMode.setRadioGroupId(0x3cf);
- m_btMultiMode.setRadioGroupId(0x3cf);
- m_btMultiSingleMode.setRadioGroupId(0x3cf);
- addAndMakeVisible(m_btSingleMode);
- addAndMakeVisible(m_btMultiMode);
- addAndMakeVisible(m_btMultiSingleMode);
- m_btSingleMode.setTopLeftPosition(102, 756);
- m_btSingleMode.setSize(70, 30);
- //m_btMultiMode.getToggleStateValue().referTo(*m_controller.getParamValue(Virus::Param_PlayMode));
- const auto isMulti = m_controller.getParamValue(Virus::Param_PlayMode)>0;
- m_btSingleMode.setClickingTogglesState(true);
- m_btMultiMode.setClickingTogglesState(true);
- m_btMultiSingleMode.setClickingTogglesState(true);
- m_btSingleMode.setToggleState(true, juce::sendNotificationAsync);
- //m_btMultiMode.setToggleState(isMulti, juce::dontSendNotification);
- //m_btMultiSingleMode.setToggleState(isMulti, juce::dontSendNotification);
- m_btSingleMode.setColour(TextButton::ColourIds::textColourOnId, juce::Colours::white);
- m_btSingleMode.setColour(TextButton::ColourIds::textColourOffId, juce::Colours::grey);
- m_btMultiMode.setColour(TextButton::ColourIds::textColourOnId, juce::Colours::white);
- m_btMultiMode.setColour(TextButton::ColourIds::textColourOffId, juce::Colours::grey);
- m_btMultiSingleMode.setColour(TextButton::ColourIds::textColourOnId, juce::Colours::white);
- m_btMultiSingleMode.setColour(TextButton::ColourIds::textColourOffId, juce::Colours::grey);
- m_btSingleMode.onClick = [this]() { setPlayMode(virusLib::PlayMode::PlayModeSingle); };
- m_btMultiSingleMode.onClick = [this]() { setPlayMode(virusLib::PlayMode::PlayModeMultiSingle); };
- m_btMultiMode.onClick = [this]() { setPlayMode(virusLib::PlayMode::PlayModeMulti); };
-
- m_btMultiSingleMode.setBounds(m_btSingleMode.getBounds().translated(m_btSingleMode.getWidth()+4, 0));
- m_btMultiMode.setBounds(m_btMultiSingleMode.getBounds().translated(m_btMultiSingleMode.getWidth()+4, 0));
-
juce::PropertiesFile::Options opts;
opts.applicationName = "DSP56300 Emulator";
opts.filenameSuffix = ".settings";
@@ -270,11 +161,6 @@ void VirusEditor::timerCallback()
for (auto pt = 0; pt < 16; pt++)
{
bool singlePartOrInMulti = pt == 0 || multiMode;
- m_presetNames[pt].setVisible(singlePartOrInMulti);
- m_prevPatch[pt].setVisible(singlePartOrInMulti);
- m_nextPatch[pt].setVisible(singlePartOrInMulti);
- if (singlePartOrInMulti)
- m_presetNames[pt].setButtonText(m_controller.getCurrentPartPresetName(pt));
if (pt == m_controller.getCurrentPart())
{
const auto patchName = m_controller.getCurrentPartPresetName(pt);
@@ -282,7 +168,6 @@ void VirusEditor::timerCallback()
m_patchName.setText(patchName, NotificationType::dontSendNotification);
}
}
- //m_partVolumes[pt].setValue(m_controller.getParameter(Virus::Param_PartVolume, pt)->getValue(), juce::dontSendNotification);
}
}
@@ -345,13 +230,6 @@ void VirusEditor::updateMidiOutput(int index)
m_cmbMidiOutput.setSelectedItemIndex(index + 1, juce::dontSendNotification);
m_lastOutputIndex = index;
}
-void VirusEditor::updatePartsPresetNames()
-{
- for (auto i = 0; i < 16; i++)
- {
- m_presetNames[i].setButtonText(m_controller.getCurrentPartPresetName(i));
- }
-}
void VirusEditor::applyToSections(std::function<void(Component *)> action)
{
for (auto *section : {static_cast<Component *>(m_arpEditor.get()), static_cast<Component *>(m_fxEditor.get()),
@@ -404,20 +282,14 @@ void VirusEditor::resized()
applyToSections([this](Component *s) { s->setTopLeftPosition(338, 133); });
}
-void VirusEditor::setPlayMode(uint8_t _mode) {
- m_controller.getParameter(Virus::Param_PlayMode)->setValue(_mode);
- changePart(0);
+void VirusEditor::handleCommandMessage(int commandId) {
+ if (commandId == Commands::Rebind) {
+ recreateControls();
+ }
}
-void VirusEditor::changePart(uint8_t _part)
+void VirusEditor::recreateControls()
{
- for (auto &p : m_partSelect)
- {
- p.setToggleState(false, juce::dontSendNotification);
- }
- m_partSelect[_part].setToggleState(true, juce::dontSendNotification);
- m_parameterBinding.setPart(_part);
-
removeChildComponent(m_oscEditor.get());
removeChildComponent(m_lfoEditor.get());
removeChildComponent(m_fxEditor.get());
@@ -530,7 +402,11 @@ void VirusEditor::loadFile()
{
if ((uint8_t)*it == (uint8_t)0xf0 && (it + 267) < end)
{
- if ((uint8_t) * (it + 1) == (uint8_t)0x00)
+ if ((uint8_t)*(it+1) == 0x00
+ && (uint8_t)*(it+2) == 0x20
+ && (uint8_t)*(it+3) == 0x33
+ && (uint8_t)*(it+4) == 0x01
+ && (uint8_t)*(it+6) == virusLib::DUMP_SINGLE)
{
auto syx = Virus::SysEx(it, it + 267);
syx[7] = 0x01; // force to bank a
@@ -540,7 +416,11 @@ void VirusEditor::loadFile()
it += 266;
}
- else // some midi files have two bytes after the 0xf0
+ else if((uint8_t)*(it+3) == 0x00
+ && (uint8_t)*(it+4) == 0x20
+ && (uint8_t)*(it+5) == 0x33
+ && (uint8_t)*(it+6) == 0x01
+ && (uint8_t)*(it+8) == virusLib::DUMP_SINGLE)// some midi files have two bytes after the 0xf0
{
auto syx = Virus::SysEx();
syx.push_back(0xf0);
diff --git a/source/jucePlugin/ui/VirusEditor.h b/source/jucePlugin/ui/VirusEditor.h
@@ -20,30 +20,25 @@ public:
VirusEditor(VirusParameterBinding &_parameterBinding, AudioPluginAudioProcessor &_processorRef);
~VirusEditor() override;
void resized() override;
- void changePart(uint8_t _part);
+ void recreateControls();
void updatePartsPresetNames();
void loadFile();
void saveFile();
- void setPlayMode(uint8_t _mode);
+ enum Commands {
+ None,
+ Rebind = 0x100
+ };
private:
void timerCallback() override;
+ void handleCommandMessage(int commandId) override;
void updateMidiInput(int index);
void updateMidiOutput(int index);
juce::Label m_version;
juce::Label m_patchName;
juce::Label m_controlLabel;
- Buttons::PartSelectButton m_partSelect[16];
- juce::Label m_partLabels[16];
- juce::TextButton m_presetNames[16];
- juce::TextButton m_nextPatch[16];
- juce::TextButton m_prevPatch[16];
- juce::Slider m_partVolumes[16];
- juce::Slider m_partPans[16];
- juce::TextButton m_btSingleMode;
- juce::TextButton m_btMultiSingleMode;
- juce::TextButton m_btMultiMode;
+
juce::ComboBox m_cmbMidiInput;
juce::ComboBox m_cmbMidiOutput;
juce::AudioDeviceManager deviceManager;
@@ -51,7 +46,6 @@ private:
int m_lastInputIndex = 0;
int m_lastOutputIndex = 0;
- static constexpr auto kPartGroupId = 0x3FBBC;
struct MainButtons : juce::Component, juce::Value::Listener
{
MainButtons();
diff --git a/source/jucePlugin/ui/Virus_ArpEditor.cpp b/source/jucePlugin/ui/Virus_ArpEditor.cpp
@@ -92,7 +92,7 @@ ArpEditor::Arpeggiator::Arpeggiator(VirusParameterBinding &_parameterBinding)
addAndMakeVisible(m_arpHold);
m_arpHold.setBounds(222, m_octaveRange.getY()+2, 28, 11);
- _parameterBinding.bind(m_globalTempo, Virus::Param_ClockTempo);
+ _parameterBinding.bind(m_globalTempo, Virus::Param_ClockTempo, 0);
_parameterBinding.bind(m_noteLength, Virus::Param_ArpNoteLength);
_parameterBinding.bind(m_noteSwing, Virus::Param_ArpSwing);
_parameterBinding.bind(m_mode, Virus::Param_ArpMode);
diff --git a/source/jucePlugin/ui/Virus_Parts.cpp b/source/jucePlugin/ui/Virus_Parts.cpp
@@ -1,11 +1,162 @@
-#include "Virus_Parts.h"
-#include "BinaryData.h"
-#include "Ui_Utils.h"
-#include "../VirusParameterBinding.h"
-
-using namespace juce;
-
-Parts::Parts(VirusParameterBinding & _parameterBinding, Virus::Controller& _controller) : m_parameterBinding(_parameterBinding), m_controller(_controller)
-{
-
+#include "Virus_Parts.h"
+#include "BinaryData.h"
+#include "Ui_Utils.h"
+#include "../VirusParameterBinding.h"
+#include "VirusEditor.h"
+using namespace juce;
+
+Parts::Parts(VirusParameterBinding & _parameterBinding, Virus::Controller& _controller) : m_parameterBinding(_parameterBinding), m_controller(_controller),
+ m_btSingleMode("Single\nMode"), m_btMultiSingleMode("Multi\nSingle"), m_btMultiMode("Multi\nMode")
+{
+ for (auto pt = 0; pt < 16; pt++)
+ {
+ m_partLabels[pt].setBounds(34, 161 + pt * (36), 24, 36);
+ m_partLabels[pt].setText(juce::String(pt + 1), juce::dontSendNotification);
+ m_partLabels[pt].setColour(0, juce::Colours::white);
+ m_partLabels[pt].setColour(1, juce::Colour(45, 24, 24));
+ m_partLabels[pt].setJustificationType(Justification::centred);
+ addAndMakeVisible(m_partLabels[pt]);
+
+ m_partSelect[pt].setBounds(35, 161 + pt*(36), 36, 36);
+ m_partSelect[pt].setButtonText(juce::String(pt));
+ m_partSelect[pt].setRadioGroupId(kPartGroupId);
+ m_partSelect[pt].setClickingTogglesState(true);
+ m_partSelect[pt].onClick = [this, pt]() {
+ this->changePart(pt);
+ };
+ addAndMakeVisible(m_partSelect[pt]);
+
+ m_presetNames[pt].setBounds(80, 171 + pt * (36) - 2, 136, 16 + 4);
+ m_presetNames[pt].setButtonText(m_controller.getCurrentPartPresetName(pt));
+ m_presetNames[pt].setColour(juce::TextButton::ColourIds::textColourOnId, juce::Colour(255,113,128));
+ m_presetNames[pt].setColour(juce::TextButton::ColourIds::textColourOffId, juce::Colour(255, 113, 128));
+
+ m_presetNames[pt].onClick = [this, pt]() {
+ juce::PopupMenu selector;
+
+ for (uint8_t b = 0; b < m_controller.getBankCount(); ++b)
+ {
+ const auto bank = virusLib::fromArrayIndex(b);
+ auto presetNames = m_controller.getSinglePresetNames(bank);
+ juce::PopupMenu p;
+ for (uint8_t j = 0; j < 128; j++)
+ {
+ const auto presetName = presetNames[j];
+ p.addItem(presetNames[j], [this, bank, j, pt, presetName] {
+ m_controller.setCurrentPartPreset(pt, bank, j);
+ m_presetNames[pt].setButtonText(presetName);
+ });
+ }
+ std::stringstream bankName;
+ bankName << "Bank " << static_cast<char>('A' + b);
+ selector.addSubMenu(std::string(bankName.str()), p);
+ }
+ selector.showMenu(juce::PopupMenu::Options());
+ };
+ addAndMakeVisible(m_presetNames[pt]);
+
+ m_prevPatch[pt].setBounds(228, 173 + 36*pt - 2, 16, 14);
+ m_nextPatch[pt].setBounds(247, 173 + 36*pt - 2, 16, 14);
+ m_prevPatch[pt].setButtonText("<");
+ m_nextPatch[pt].setButtonText(">");
+ m_prevPatch[pt].setColour(juce::TextButton::ColourIds::textColourOnId, juce::Colour(255, 113, 128));
+ m_nextPatch[pt].setColour(juce::TextButton::ColourIds::textColourOnId, juce::Colour(255, 113, 128));
+ m_prevPatch[pt].setColour(juce::TextButton::ColourIds::textColourOffId, juce::Colour(255, 113, 128));
+ m_nextPatch[pt].setColour(juce::TextButton::ColourIds::textColourOffId, juce::Colour(255, 113, 128));
+ m_prevPatch[pt].onClick = [this, pt]() {
+ m_controller.setCurrentPartPreset(
+ pt, m_controller.getCurrentPartBank(pt),
+ std::max(0, m_controller.getCurrentPartProgram(pt) - 1));
+ };
+ m_nextPatch[pt].onClick = [this, pt]() {
+ m_controller.setCurrentPartPreset(
+ pt, m_controller.getCurrentPartBank(pt),
+ std::min(127, m_controller.getCurrentPartProgram(pt) + 1));
+ };
+ addAndMakeVisible(m_prevPatch[pt]);
+ addAndMakeVisible(m_nextPatch[pt]);
+
+ m_partVolumes[pt].setSliderStyle(juce::Slider::RotaryHorizontalVerticalDrag);
+ m_partVolumes[pt].setTextBoxStyle(juce::Slider::NoTextBox, false, 0, 0);
+ m_partVolumes[pt].setBounds(m_nextPatch[pt].getBounds().translated(m_nextPatch[pt].getWidth()+8, 0));
+ m_partVolumes[pt].setSize(18,18);
+ m_partVolumes[pt].getProperties().set(Virus::LookAndFeel::KnobStyleProp, Virus::LookAndFeel::KnobStyle::GENERIC_MULTI);
+ m_partPans[pt].setTooltip("Part Volume");
+ _parameterBinding.bind(m_partVolumes[pt], Virus::Param_PartVolume, pt);
+ addAndMakeVisible(m_partVolumes[pt]);
+
+ m_partPans[pt].setSliderStyle(juce::Slider::RotaryHorizontalVerticalDrag);
+ m_partPans[pt].setTextBoxStyle(juce::Slider::NoTextBox, false, 0, 0);
+ m_partPans[pt].setBounds(m_partVolumes[pt].getBounds().translated(m_partVolumes[pt].getWidth()+4, 0));
+ m_partPans[pt].setSize(18,18);
+ m_partPans[pt].getProperties().set(Virus::LookAndFeel::KnobStyleProp, Virus::LookAndFeel::KnobStyle::GENERIC_MULTI);
+ m_partPans[pt].setTooltip("Part Pan");
+ _parameterBinding.bind(m_partPans[pt], Virus::Param_Panorama, pt);
+ addAndMakeVisible(m_partPans[pt]);
+ }
+ m_partSelect[m_controller.getCurrentPart()].setToggleState(true, NotificationType::sendNotification);
+
+ m_btSingleMode.setRadioGroupId(0x3cf);
+ m_btMultiMode.setRadioGroupId(0x3cf);
+ m_btMultiSingleMode.setRadioGroupId(0x3cf);
+ addAndMakeVisible(m_btSingleMode);
+ addAndMakeVisible(m_btMultiMode);
+ addAndMakeVisible(m_btMultiSingleMode);
+ m_btSingleMode.setTopLeftPosition(102, 756);
+ m_btSingleMode.setSize(70, 30);
+ //m_btMultiMode.getToggleStateValue().referTo(*m_controller.getParamValue(Virus::Param_PlayMode));
+ const auto isMulti = m_controller.getParamValue(Virus::Param_PlayMode)>0;
+ m_btSingleMode.setClickingTogglesState(true);
+ m_btMultiMode.setClickingTogglesState(true);
+ m_btMultiSingleMode.setClickingTogglesState(true);
+ m_btSingleMode.setToggleState(true, juce::sendNotificationAsync);
+ //m_btMultiMode.setToggleState(isMulti, juce::dontSendNotification);
+ //m_btMultiSingleMode.setToggleState(isMulti, juce::dontSendNotification);
+ m_btSingleMode.setColour(TextButton::ColourIds::textColourOnId, juce::Colours::white);
+ m_btSingleMode.setColour(TextButton::ColourIds::textColourOffId, juce::Colours::grey);
+ m_btMultiMode.setColour(TextButton::ColourIds::textColourOnId, juce::Colours::white);
+ m_btMultiMode.setColour(TextButton::ColourIds::textColourOffId, juce::Colours::grey);
+ m_btMultiSingleMode.setColour(TextButton::ColourIds::textColourOnId, juce::Colours::white);
+ m_btMultiSingleMode.setColour(TextButton::ColourIds::textColourOffId, juce::Colours::grey);
+ m_btSingleMode.onClick = [this]() { setPlayMode(virusLib::PlayMode::PlayModeSingle); };
+ m_btMultiSingleMode.onClick = [this]() { setPlayMode(virusLib::PlayMode::PlayModeMultiSingle); };
+ m_btMultiMode.onClick = [this]() { setPlayMode(virusLib::PlayMode::PlayModeMulti); };
+
+ m_btMultiSingleMode.setBounds(m_btSingleMode.getBounds().translated(m_btSingleMode.getWidth()+4, 0));
+ m_btMultiMode.setBounds(m_btMultiSingleMode.getBounds().translated(m_btMultiSingleMode.getWidth()+4, 0));
+
+ startTimerHz(5);
+ setSize(338, 800);
+}
+
+void Parts::changePart(uint8_t _part)
+{
+ for (auto &p : m_partSelect)
+ {
+ p.setToggleState(false, juce::dontSendNotification);
+ }
+ m_partSelect[_part].setToggleState(true, juce::dontSendNotification);
+ m_parameterBinding.setPart(_part);
+ getParentComponent()->postCommandMessage(VirusEditor::Commands::Rebind);
+}
+
+void Parts::setPlayMode(uint8_t _mode) {
+ m_controller.getParameter(Virus::Param_PlayMode)->setValue(_mode);
+ getParentComponent()->postCommandMessage(VirusEditor::Commands::Rebind);
+}
+
+void Parts::timerCallback()
+{
+ // ugly (polling!) way for refreshing presets names as this is temporary ui
+ const auto multiMode = m_controller.isMultiMode();
+ for (auto pt = 0; pt < 16; pt++)
+ {
+ bool singlePartOrInMulti = pt == 0 || multiMode;
+ m_presetNames[pt].setVisible(singlePartOrInMulti);
+ m_prevPatch[pt].setVisible(singlePartOrInMulti);
+ m_nextPatch[pt].setVisible(singlePartOrInMulti);
+ if (singlePartOrInMulti)
+ m_presetNames[pt].setButtonText(m_controller.getCurrentPartPresetName(pt));
+ }
+
}
\ No newline at end of file
diff --git a/source/jucePlugin/ui/Virus_Parts.h b/source/jucePlugin/ui/Virus_Parts.h
@@ -6,11 +6,26 @@
#include "../VirusController.h"
class VirusParameterBinding;
-class Parts : public juce::Component
+class Parts : public juce::Component, private juce::Timer
{
- public:
- Parts(VirusParameterBinding& _parameterBinding, Virus::Controller& _controller);
- private:
- Virus::Controller &m_controller;
- VirusParameterBinding &m_parameterBinding;
+ public:
+ Parts(VirusParameterBinding& _parameterBinding, Virus::Controller& _controller);
+ static constexpr auto kPartGroupId = 0x3FBBC;
+ private:
+ void changePart(uint8_t _part);
+ void setPlayMode(uint8_t _mode);
+ void timerCallback() override;
+ Virus::Controller &m_controller;
+ VirusParameterBinding &m_parameterBinding;
+
+ Buttons::PartSelectButton m_partSelect[16];
+ juce::Label m_partLabels[16];
+ juce::TextButton m_presetNames[16];
+ juce::TextButton m_nextPatch[16];
+ juce::TextButton m_prevPatch[16];
+ juce::Slider m_partVolumes[16];
+ juce::Slider m_partPans[16];
+ juce::TextButton m_btSingleMode;
+ juce::TextButton m_btMultiSingleMode;
+ juce::TextButton m_btMultiMode;
};
\ No newline at end of file
diff --git a/source/jucePlugin/ui/Virus_PatchBrowser.cpp b/source/jucePlugin/ui/Virus_PatchBrowser.cpp
@@ -34,11 +34,13 @@ PatchBrowser::PatchBrowser(VirusParameterBinding & _parameterBinding, Virus::Con
m_patchList.setBounds(m_bankList.getBounds().translated(m_bankList.getWidth(), 0));
- m_patchList.getHeader().addColumn("#", 0, 32);
- m_patchList.getHeader().addColumn("Name", 1, 150);
- m_patchList.getHeader().addColumn("Category1", 2, 100);
- m_patchList.getHeader().addColumn("Category2", 3, 100);
- m_patchList.getHeader().addColumn("Arp", 4, 32);
+ m_patchList.getHeader().addColumn("#", Columns::INDEX, 32);
+ m_patchList.getHeader().addColumn("Name", Columns::NAME, 140);
+ m_patchList.getHeader().addColumn("Category1", Columns::CAT1, 90);
+ m_patchList.getHeader().addColumn("Category2", Columns::CAT2, 90);
+ m_patchList.getHeader().addColumn("Arp", Columns::ARP, 32);
+ m_patchList.getHeader().addColumn("Uni", Columns::UNI, 32);
+ m_patchList.getHeader().addColumn("Ver", Columns::VER, 32);
addAndMakeVisible(m_bankList);
addAndMakeVisible(m_patchList);
@@ -58,18 +60,32 @@ void PatchBrowser::fileClicked(const juce::File &file, const juce::MouseEvent &e
m_patches.clear();
juce::MemoryBlock data;
- file.loadFileAsData(data);
- uint8_t index = 0;
+ if (!file.loadFileAsData(data)) {
+ return;
+ }
+ int index = 0;
for (auto it = data.begin(); it != data.end(); it += 267)
{
- if ((it + 267) <= data.end())
+ if ((uint8_t)*it == (uint8_t)0xf0
+ && (uint8_t)*(it+1) == (uint8_t)0x00
+ && (uint8_t)*(it+2) == (uint8_t)0x20
+ && (uint8_t)*(it+3) == (uint8_t)0x33
+ && (uint8_t)*(it+4) == (uint8_t)0x01
+ && (uint8_t)*(it+6) == (uint8_t)virusLib::DUMP_SINGLE)
{
Patch patch;
patch.progNumber = index;
+ if ((uint8_t)*(it + 266) != (uint8_t)0xf7) {
+ patch.model = VirusModel::TI;
+ }
+ else {
+ patch.model = VirusModel::B;
+ }
data.copyTo(patch.data, 267*index + 9, 256);
patch.name = parseAsciiText(patch.data, 128 + 112);
patch.category1 = patch.data[251];
patch.category2 = patch.data[252];
+ patch.unison = patch.data[97];
m_patches.add(patch);
index++;
}
@@ -93,9 +109,13 @@ void PatchBrowser::fileClicked(const juce::File &file, const juce::MouseEvent &e
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) // we don't check for sysex eof so we can load TI banks
{
- if ((uint8_t) * (it + 1) == (uint8_t)0x00)
+ if ((uint8_t) *(it+1) == (uint8_t)0x00
+ && (uint8_t)*(it+2) == 0x20
+ && (uint8_t)*(it+3) == 0x33
+ && (uint8_t)*(it+4) == 0x01
+ && (uint8_t)*(it+6) == virusLib::DUMP_SINGLE)
{
auto syx = Virus::SysEx(it, it + 267);
syx[7] = 0x01; // force to bank a
@@ -103,15 +123,26 @@ void PatchBrowser::fileClicked(const juce::File &file, const juce::MouseEvent &e
Patch patch;
patch.progNumber = index;
+ if ((uint8_t)*(it + 266) != (uint8_t)0xf7) {
+ patch.model = VirusModel::TI;
+ }
+ else {
+ patch.model = VirusModel::B;
+ }
std::copy(syx.begin() + 9, syx.end() - 2, patch.data);
patch.name = parseAsciiText(patch.data, 128 + 112);
patch.category1 = patch.data[251];
patch.category2 = patch.data[252];
+ patch.unison = patch.data[97];
m_patches.add(patch);
index++;
it += 266;
}
- else // some midi files have two bytes after the 0xf0
+ else if((uint8_t)*(it+3) == 0x00 // some midi files have two bytes after the 0xf0
+ && (uint8_t)*(it+4) == 0x20
+ && (uint8_t)*(it+5) == 0x33
+ && (uint8_t)*(it+6) == 0x01
+ && (uint8_t)*(it+8) == virusLib::DUMP_SINGLE)
{
auto syx = Virus::SysEx();
syx.push_back(0xf0);
@@ -123,11 +154,18 @@ void PatchBrowser::fileClicked(const juce::File &file, const juce::MouseEvent &e
syx[266] = 0xf7;
Patch patch;
+ if ((uint8_t)*(it + 2 + 266) != (uint8_t)0xf7) { // TI patches are 512 bytes
+ patch.model = VirusModel::TI;
+ }
+ else {
+ patch.model = VirusModel::B;
+ }
std::memcpy(patch.data, syx.data()+9, 256);
patch.progNumber = index;
patch.name = parseAsciiText(patch.data, 128 + 112);
patch.category1 = patch.data[251];
patch.category2 = patch.data[252];
+ patch.unison = patch.data[97];
m_patches.add(patch);
index++;
@@ -137,6 +175,7 @@ void PatchBrowser::fileClicked(const juce::File &file, const juce::MouseEvent &e
}
}
m_patchList.updateContent();
+ m_patchList.deselectAllRows();
m_patchList.repaint();
}
}
@@ -164,23 +203,32 @@ void PatchBrowser::paintCell(Graphics &g, int rowNumber, int columnId, int width
auto rowElement = m_patches[rowNumber];
//auto text = rowElement.name;
juce::String text = "";
- if (columnId == 0)
+ if (columnId == Columns::INDEX)
text = juce::String(rowElement.progNumber);
- else if (columnId == 1)
+ else if (columnId == Columns::NAME)
text = rowElement.name;
- else if (columnId == 2)
+ else if (columnId == Columns::CAT1)
text = categories[rowElement.category1];
- else if (columnId == 3)
+ else if (columnId == Columns::CAT2)
text = categories[rowElement.category2];
- else if (columnId == 4)
+ else if (columnId == Columns::ARP)
text = rowElement.data[129] != 0 ? "Y" : " ";
+ else if(columnId == Columns::UNI)
+ text = rowElement.unison == 0 ? " " : juce::String(rowElement.unison+1);
+ else if (columnId == Columns::VER) {
+ if(rowElement.model < ModelList.size())
+ text = ModelList[rowElement.model];
+ }
g.drawText(text, 2, 0, width - 4, height, juce::Justification::centredLeft, true); // [6]
g.setColour(getLookAndFeel().findColour(juce::ListBox::backgroundColourId));
g.fillRect(width - 1, 0, 1, height); // [7]
}
-void PatchBrowser::selectedRowsChanged(int lastRowSelected) {
+void PatchBrowser::selectedRowsChanged(int lastRowSelected) {
auto idx = m_patchList.getSelectedRow();
+ if (idx == -1) {
+ return;
+ }
uint8_t syxHeader[9] = {0xF0, 0x00, 0x20, 0x33, 0x01, 0x00, 0x10, 0x00, 0x00};
syxHeader[8] = m_controller.isMultiMode() ? m_controller.getCurrentPart() : virusLib::ProgramType::SINGLE; // set edit buffer
const uint8_t syxEof = 0xF7;
@@ -213,3 +261,52 @@ void PatchBrowser::cellDoubleClicked(int rowNumber, int columnId, const juce::Mo
selectedRowsChanged(0);
}
}
+
+class PatchBrowser::PatchBrowserSorter
+{
+public:
+ PatchBrowserSorter (int attributeToSortBy, bool forwards)
+ : attributeToSort (attributeToSortBy),
+ direction (forwards ? 1 : -1)
+ {}
+
+ int compareElements (Patch first, Patch second) const
+ {
+ if(attributeToSort == Columns::INDEX) {
+ return direction * (first.progNumber - second.progNumber);
+ }
+ else if (attributeToSort == Columns::NAME) {
+ return direction * first.name.compareIgnoreCase(second.name);
+ }
+ else if (attributeToSort == Columns::CAT1) {
+ return direction * (first.category1 - second.category1);
+ }
+ else if (attributeToSort == Columns::CAT2) {
+ return direction * (first.category2 - second.category2);
+ }
+ else if (attributeToSort == Columns::ARP) {
+ return direction * (first.data[129]- second.data[129]);
+ }
+ else if (attributeToSort == Columns::UNI) {
+ return direction * (first.unison - second.unison);
+ }
+ else if (attributeToSort == Columns::VER) {
+ return direction * (first.model - second.model);
+ }
+ return direction * (first.progNumber - second.progNumber);
+ }
+
+private:
+ int attributeToSort;
+ int direction;
+};
+
+void PatchBrowser::sortOrderChanged(int newSortColumnId, bool isForwards)
+{
+ if (newSortColumnId != 0)
+ {
+ PatchBrowser::PatchBrowserSorter sorter (newSortColumnId, isForwards);
+ m_patches.sort(sorter);
+ m_patchList.updateContent();
+ }
+}
+\ No newline at end of file
diff --git a/source/jucePlugin/ui/Virus_PatchBrowser.h b/source/jucePlugin/ui/Virus_PatchBrowser.h
@@ -5,6 +5,24 @@
#include <juce_gui_extra/juce_gui_extra.h>
#include "../VirusController.h"
class VirusParameterBinding;
+enum VirusModel {
+ A,
+ B,
+ C,
+ TI
+};
+
+const juce::Array<juce::String> ModelList = {"A","B","C","TI"};
+struct Patch
+{
+ int progNumber;
+ juce::String name;
+ uint8_t category1;
+ uint8_t category2;
+ uint8_t data[256];
+ VirusModel model;
+ uint8_t unison;
+};
class PatchBrowser : public juce::Component, juce::FileBrowserListener, juce::TableListBoxModel
{
@@ -15,15 +33,6 @@ public:
private:
VirusParameterBinding &m_parameterBinding;
Virus::Controller& m_controller;
- struct Patch
- {
- uint8_t progNumber;
- juce::String name;
- uint8_t category1;
- uint8_t category2;
- uint8_t data[256];
-
- };
template <typename T> juce::String parseAsciiText(const T &msg, const int start) const
{
char text[Virus::Controller::kNameLength + 1];
@@ -53,4 +62,15 @@ private:
virtual void selectedRowsChanged(int lastRowSelected) override;
virtual void cellDoubleClicked (int rowNumber, int columnId, const juce::MouseEvent &) override;
+ void sortOrderChanged(int newSortColumnId, bool isForwards) override;
+ class PatchBrowserSorter;
+ enum Columns {
+ INDEX = 1,
+ NAME = 2,
+ CAT1 = 3,
+ CAT2 = 4,
+ ARP = 5,
+ UNI = 6,
+ VER = 7
+ };
};
diff --git a/source/virusLib/microcontroller.cpp b/source/virusLib/microcontroller.cpp
@@ -492,7 +492,7 @@ bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, bool _cancelI
const auto param = _data[8];
const auto value = _data[9];
- if(page == PAGE_C)
+ if(page == PAGE_C || (page == PAGE_B && param == CLOCK_TEMPO))
{
applyToMultiEditBuffer(part, param, value);
@@ -904,9 +904,12 @@ void Microcontroller::applyToSingleEditBuffer(TPreset& _single, const Page _page
void Microcontroller::applyToMultiEditBuffer(const uint8_t _part, const uint8_t _param, const uint8_t _value)
{
// remap page C parameters into the multi edit buffer
- if (_param >= PART_MIDI_CHANNEL && _param <= PART_OUTPUT_SELECT) {
+ if (_part == PAGE_C && _param >= PART_MIDI_CHANNEL && _param <= PART_OUTPUT_SELECT) {
m_multiEditBuffer[MD_PART_MIDI_CHANNEL + ((_param-PART_MIDI_CHANNEL)*16) + _part] = _value;
}
+ else if (_part == PAGE_B && _param == CLOCK_TEMPO) {
+ m_multiEditBuffer[MD_CLOCK_TEMPO] = _value;
+ }
}
}
diff --git a/source/virusLib/microcontrollerTypes.h b/source/virusLib/microcontrollerTypes.h
@@ -38,6 +38,8 @@ namespace virusLib
MULTI_NAME_CHAR_8 = 13,
MULTI_NAME_CHAR_9 = 14,
+ CLOCK_TEMPO = 16, // this is actually in page B
+
MULTI_DELAY_OUTPUT_SELECT = 22,
UNK1a = 26,