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 544f663b311d1451acaf72849a48235386a1518d
parent 02ab7d99f28fe3931a34a916bb66e29b9956edd5
Author: 790 <790@users.noreply.github.com>
Date:   Sun, 16 Jan 2022 22:18:06 +0000

add scale. fix crashes. better reverb/delay controls

Diffstat:
Msource/jucePlugin/CMakeLists.txt | 2++
Msource/jucePlugin/PluginEditor.cpp | 2+-
Msource/jucePlugin/VirusController.cpp | 16++++++++++------
Msource/jucePlugin/VirusParameterBinding.cpp | 1+
Asource/jucePlugin/assets/panels/bg_fxdelay_481x234.png | 0
Asource/jucePlugin/assets/panels/bg_fxreverb_481x234.png | 0
Msource/jucePlugin/ui/VirusEditor.cpp | 4++--
Msource/jucePlugin/ui/Virus_FxEditor.cpp | 112++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msource/jucePlugin/ui/Virus_FxEditor.h | 41++++++++++++++++++++++++-----------------
Msource/jucePlugin/ui/Virus_Parts.cpp | 12+++++++-----
Msource/virusLib/microcontroller.cpp | 4++--
11 files changed, 129 insertions(+), 65 deletions(-)

diff --git a/source/jucePlugin/CMakeLists.txt b/source/jucePlugin/CMakeLists.txt @@ -70,6 +70,8 @@ juce_add_binary_data(jucePlugin_BinaryData "assets/panels/bg_fx_1018x620.png" "assets/panels/bg_lfo_1018x620.png" "assets/panels/bg_osc_1018x620.png" + "assets/panels/bg_fxreverb_481x234.png" + "assets/panels/bg_fxdelay_481x234.png" "assets/buttons/GLOBAL_btn_arp_settings_141x26.png" "assets/buttons/GLOBAL_btn_effects_141x26.png" "assets/buttons/GLOBAL_btn_lfo_matrix_141x26.png" diff --git a/source/jucePlugin/PluginEditor.cpp b/source/jucePlugin/PluginEditor.cpp @@ -15,7 +15,7 @@ AudioPluginAudioProcessorEditor::AudioPluginAudioProcessorEditor(AudioPluginAudi const auto config = processorRef.getController().getConfig(); auto scale = config->getIntValue("scale", 100); m_virusEditor->setTopLeftPosition(0, 0); - m_scale.setBounds(8,8,64,24); + m_scale.setBounds(0,0,52,24); m_scale.addItem("50%", 50); m_scale.addItem("75%", 75); m_scale.addItem("100%", 100); diff --git a/source/jucePlugin/VirusController.cpp b/source/jucePlugin/VirusController.cpp @@ -348,8 +348,12 @@ namespace Virus const auto ch = patch.progNumber == 0x40 ? 0 : patch.progNumber; for (auto i = 0; i < kDataSizeInBytes; i++) { - if (auto *p = findSynthParam(ch, i > bankSize ? 0x71 : 0x70, i % bankSize)) - p->setValueFromSynth(patch.data[i], true); + if (auto *p = findSynthParam(ch, i > bankSize ? 0x71 : 0x70, i % bankSize)) { + if((p->getDescription().classFlags & Parameter::MULTI_OR_SINGLE) && isMultiMode()) + continue; + else + p->setValueFromSynth(patch.data[i], true); + } } if (onProgramChange) { onProgramChange(); @@ -391,9 +395,9 @@ namespace Virus if (auto* p = findSynthParam(pt, virusLib::PAGE_B, virusLib::CLOCK_TEMPO)) { p->setValueFromSynth(patch.data[virusLib::MD_CLOCK_TEMPO], true); } - if (auto* p = findSynthParam(pt, virusLib::PAGE_A, virusLib::EFFECT_SEND)) { +/* if (auto* p = findSynthParam(pt, virusLib::PAGE_A, virusLib::EFFECT_SEND)) { p->setValueFromSynth(patch.data[virusLib::MD_PART_EFFECT_SEND], true); - } + }*/ m_currentBank[pt] = (virusLib::BankNumber)(patch.data[virusLib::MD_PART_BANK_NUMBER + pt]+1); m_currentProgram[pt] = patch.data[virusLib::MD_PART_PROGRAM_NUMBER + pt]; } @@ -1566,7 +1570,7 @@ namespace Virus {Parameter::Page::A, Parameter::Class::SOUNDBANK_A, 108, "Chorus Delay", {0,127}, {},{}, true, false, false}, {Parameter::Page::A, Parameter::Class::SOUNDBANK_A, 109, "Chorus Feedback", {0,127}, paramTo7bitSigned, textTo7bitSigned, true, false, false, true}, {Parameter::Page::A, Parameter::Class::SOUNDBANK_A, 110, "Chorus Lfo Shape", {0,67}, numToLfoShape, {}, true, true, false}, - {Parameter::Page::A, Parameter::Class::SOUNDBANK_A, 112, "Delay/Reverb Mode", {0,26}, numToDelayReverbMode, {}, true, true, false}, + {Parameter::Page::A, Parameter::Class::SOUNDBANK_A|Parameter::Class::MULTI_OR_SINGLE, 112, "Delay/Reverb Mode", {0,26}, numToDelayReverbMode, {}, true, true, false}, {Parameter::Page::A, Parameter::Class::SOUNDBANK_A|Parameter::Class::MULTI_OR_SINGLE, 113, "Effect Send", {0,127}, {},{}, true, false, false}, {Parameter::Page::A, Parameter::Class::SOUNDBANK_A|Parameter::Class::MULTI_OR_SINGLE|Parameter::Class::NON_PART_SENSITIVE, 114, "Delay Time", {0,127}, {},{}, true, false, false}, {Parameter::Page::A, Parameter::Class::SOUNDBANK_A|Parameter::Class::MULTI_OR_SINGLE|Parameter::Class::NON_PART_SENSITIVE, 115, "Delay Feedback", {0,127}, {},{}, true, false, false}, @@ -1669,7 +1673,7 @@ namespace Virus {Parameter::Page::B, Parameter::Class::SOUNDBANK_B|Parameter::Class::VIRUS_C, 101, "Distortion Intensity", {0,127}, {},{}, true, false, false}, {Parameter::Page::B, Parameter::Class::SOUNDBANK_B|Parameter::Class::VIRUS_C, 103, "Assign 4 Source", {0,27}, numToModMatrixSource,{}, true, true, false}, {Parameter::Page::B, Parameter::Class::SOUNDBANK_B|Parameter::Class::VIRUS_C, 104, "Assign 4 Destination", {0,122}, numToModMatrixDest, {}, true, true, false}, - {Parameter::Page::B, Parameter::Class::SOUNDBANK_B|Parameter::Class::VIRUS_C, 105, "Assign 4 Amount", {0,127}, {},{}, true, false, false}, + {Parameter::Page::B, Parameter::Class::SOUNDBANK_B|Parameter::Class::VIRUS_C, 105, "Assign 4 Amount", {0,127}, {},{}, true, false, false, true}, {Parameter::Page::B, Parameter::Class::SOUNDBANK_B|Parameter::Class::VIRUS_C, 106, "Assign 5 Source", {0,27}, numToModMatrixSource, {}, true, true, false}, {Parameter::Page::B, Parameter::Class::SOUNDBANK_B|Parameter::Class::VIRUS_C, 107, "Assign 5 Destination", {0,122}, numToModMatrixDest, {}, true, true, false}, {Parameter::Page::B, Parameter::Class::SOUNDBANK_B|Parameter::Class::VIRUS_C, 108, "Assign 5 Amount", {0,127}, paramTo7bitSigned, textTo7bitSigned, true, false, false, true}, diff --git a/source/jucePlugin/VirusParameterBinding.cpp b/source/jucePlugin/VirusParameterBinding.cpp @@ -2,6 +2,7 @@ #include "VirusParameter.h" #include "PluginProcessor.h" class Parameter; + VirusParameterBinding::~VirusParameterBinding() { clearBindings(); diff --git a/source/jucePlugin/assets/panels/bg_fxdelay_481x234.png b/source/jucePlugin/assets/panels/bg_fxdelay_481x234.png Binary files differ. diff --git a/source/jucePlugin/assets/panels/bg_fxreverb_481x234.png b/source/jucePlugin/assets/panels/bg_fxreverb_481x234.png Binary files differ. diff --git a/source/jucePlugin/ui/VirusEditor.cpp b/source/jucePlugin/ui/VirusEditor.cpp @@ -38,7 +38,7 @@ VirusEditor::VirusEditor(VirusParameterBinding &_parameterBinding, AudioPluginAu addAndMakeVisible (m_mainButtons); m_arpEditor = std::make_unique<ArpEditor>(_parameterBinding); - m_fxEditor = std::make_unique<FxEditor>(_parameterBinding); + m_fxEditor = std::make_unique<FxEditor>(_parameterBinding, m_controller); m_lfoEditor = std::make_unique<LfoEditor>(_parameterBinding); m_oscEditor = std::make_unique<OscEditor>(_parameterBinding); m_patchBrowser = std::make_unique<PatchBrowser>(_parameterBinding, m_controller); @@ -387,7 +387,7 @@ void VirusEditor::recreateControls() m_lfoEditor = std::make_unique<LfoEditor>(m_parameterBinding); addChildComponent(m_lfoEditor.get()); - m_fxEditor = std::make_unique<FxEditor>(m_parameterBinding); + m_fxEditor = std::make_unique<FxEditor>(m_parameterBinding, m_controller); addChildComponent(m_fxEditor.get()); m_arpEditor = std::make_unique<ArpEditor>(m_parameterBinding); diff --git a/source/jucePlugin/ui/Virus_FxEditor.cpp b/source/jucePlugin/ui/Virus_FxEditor.cpp @@ -6,12 +6,13 @@ using namespace juce; constexpr auto comboBoxWidth = 84; -FxEditor::FxEditor(VirusParameterBinding &_parameterBinding) : +FxEditor::FxEditor(VirusParameterBinding &_parameterBinding, Virus::Controller& _controller) : m_dist(_parameterBinding), m_analogBoost(_parameterBinding), m_phaser(_parameterBinding), m_chorus(_parameterBinding), m_eq(_parameterBinding), m_envFollow(_parameterBinding), m_punch(_parameterBinding), - m_delayReverb(_parameterBinding), m_vocoder(_parameterBinding) + m_vocoder(_parameterBinding), m_fxMode("FX Mode"), m_parameterBinding(_parameterBinding) { setupBackground(*this, m_background, BinaryData::bg_fx_1018x620_png, BinaryData::bg_fx_1018x620_pngSize); + setupRotary(*this, m_fxSend); setBounds(m_background->getDrawableBounds().toNearestIntEdges()); m_dist.setBounds(23, 28, 273, 116); @@ -28,12 +29,60 @@ FxEditor::FxEditor(VirusParameterBinding &_parameterBinding) : addAndMakeVisible(m_envFollow); m_punch.setBounds(m_envFollow.getRight() + 2, m_envFollow.getY(), 174, 103); addAndMakeVisible(m_punch); - m_delayReverb.setBounds(m_phaser.getRight() + 2, m_dist.getY(), 481, m_phaser.getHeight() * 2); - addAndMakeVisible(m_delayReverb); - m_vocoder.setBounds(m_delayReverb.getBounds().withY(m_delayReverb.getBottom() + 2).withHeight(304)); + + m_delay = std::make_unique<Delay>(_parameterBinding); + m_reverb = std::make_unique<Reverb>(_parameterBinding); + m_delay->setBounds(m_phaser.getRight() + 2, m_dist.getY(), 481, m_phaser.getHeight() * 2); + m_reverb->setBounds(m_phaser.getRight() + 2, m_dist.getY(), 481, m_phaser.getHeight() * 2); + + addChildComponent(m_delay.get()); + m_delay->setVisible(true); + + addChildComponent(m_reverb.get()); + + m_vocoder.setBounds(m_delay->getBounds().withY(m_delay->getBottom() + 2).withHeight(304)); addAndMakeVisible(m_vocoder); -} + m_fxMode.setBounds(m_reverb->getX()+18, m_reverb->getY()+42, comboBoxWidth, comboBoxHeight); + addAndMakeVisible(m_fxMode); + m_fxMode.setAlwaysOnTop(true); + //m_fxSend.getProperties().set(Virus::LookAndFeel::KnobStyleProp, Virus::LookAndFeel::KnobStyle::GENERIC_RED); + m_fxSend.setBounds(m_reverb->getX()+376, m_phaser.getY() - 2, knobSize, knobSize); + addAndMakeVisible(m_fxSend); + m_fxSend.setAlwaysOnTop(true); + _parameterBinding.bind(m_fxSend, Virus::Param_EffectSend); + _parameterBinding.bind(m_fxMode, Virus::Param_DelayReverbMode, 0); + auto p = _controller.getParameter(Virus::Param_DelayReverbMode, 0); + if (p) { + const auto val = (int)p->getValueObject().getValueSource().getValue(); + if (val > 1 && val < 5) { + m_fxMode.setSelectedId(val + 1, juce::dontSendNotification); + const bool isReverb = (val > 1 && val < 5); + m_reverb->setVisible(isReverb); + m_delay->setVisible(!isReverb); + } + p->onValueChanged = nullptr; + p->onValueChanged = [this, p]() { + rebind(); + const auto value = (int)p->getValueObject().getValueSource().getValue(); + m_fxMode.setSelectedId(value + 1, juce::dontSendNotification); + const bool isReverb = (value > 1 && value < 5); + m_reverb->setVisible(isReverb); + m_delay->setVisible(!isReverb); + + }; + } +} +void FxEditor::rebind() { + removeChildComponent(m_delay.get()); + m_delay = std::make_unique<FxEditor::Delay>(m_parameterBinding); + addChildComponent(m_delay.get()); + removeChildComponent(m_reverb.get()); + m_reverb = std::make_unique<FxEditor::Reverb>(m_parameterBinding); + addChildComponent(m_reverb.get()); + m_delay->setBounds(m_phaser.getRight() + 2, m_dist.getY(), 481, m_phaser.getHeight() * 2); + m_reverb->setBounds(m_phaser.getRight() + 2, m_dist.getY(), 481, m_phaser.getHeight() * 2); +} FxEditor::Distortion::Distortion(VirusParameterBinding &_parameterBinding) { setupRotary(*this, m_intensity); @@ -149,8 +198,9 @@ FxEditor::Punch::Punch(VirusParameterBinding &_parameterBinding) _parameterBinding.bind(m_amount, Virus::Param_PunchIntensity); } -FxEditor::DelayAndReverb::DelayAndReverb(VirusParameterBinding &_parameterBinding) : m_sync(_parameterBinding) +FxEditor::Delay::Delay(VirusParameterBinding &_parameterBinding) { + setupBackground(*this, m_background, BinaryData::bg_fxdelay_481x234_png, BinaryData::bg_fxdelay_481x234_pngSize); constexpr auto y = 18; for (auto *s : {&m_time, &m_rate, &m_depth, &m_color, &m_feedback}) setupRotary(*this, *s); @@ -160,44 +210,42 @@ FxEditor::DelayAndReverb::DelayAndReverb(VirusParameterBinding &_parameterBindin m_color.setBounds(m_depth.getRight() - 3, y, knobSize, knobSize); m_feedback.setBounds(m_color.getRight() - 3, y, knobSize, knobSize); - addAndMakeVisible(m_fxMode); - m_fxMode.setBounds(18, 42, comboBoxWidth, comboBoxHeight); - m_sync.setBounds(0, 116 + 2, 481, 116); - addAndMakeVisible(m_sync); + addAndMakeVisible(m_clock); + m_clock.setBounds(18, 116+2+22, comboBoxWidth, comboBoxHeight); + addAndMakeVisible(m_lfoShape); + m_lfoShape.setBounds(m_clock.getBounds().getRight() + 26, 116+2+22, comboBoxWidth, comboBoxHeight); + + _parameterBinding.bind(m_clock, Virus::Param_DelayClock, 0); + _parameterBinding.bind(m_lfoShape, Virus::Param_DelayLfoShape, 0); _parameterBinding.bind(m_time, Virus::Param_DelayTime, 0); _parameterBinding.bind(m_rate, Virus::Param_DelayRateReverbDecayTime, 0); _parameterBinding.bind(m_depth, Virus::Param_DelayDepthReverbRoomSize, 0); _parameterBinding.bind(m_color, Virus::Param_DelayColor, 0); _parameterBinding.bind(m_feedback, Virus::Param_DelayFeedback, 0); - _parameterBinding.bind(m_fxMode, Virus::Param_DelayReverbMode, 0); } -FxEditor::DelayAndReverb::Sync::Sync(VirusParameterBinding &_parameterBinding) +FxEditor::Reverb::Reverb(VirusParameterBinding &_parameterBinding) { - setupRotary(*this, m_mix); - setupRotary(*this, m_damping); - m_mix.getProperties().set(Virus::LookAndFeel::KnobStyleProp, Virus::LookAndFeel::KnobStyle::GENERIC_RED); - m_mix.setBounds(376, -2, knobSize, knobSize); - - addAndMakeVisible(m_clock); - m_clock.setBounds(18, 22, comboBoxWidth, comboBoxHeight); - addAndMakeVisible(m_lfoShape); - m_lfoShape.setBounds(m_clock.getBounds().getRight() + 26, 22, comboBoxWidth, comboBoxHeight); - + setupBackground(*this, m_background, BinaryData::bg_fxreverb_481x234_png, BinaryData::bg_fxreverb_481x234_pngSize); + constexpr auto y = 18; + for (auto *s : {&m_time, &m_rate, &m_damping, &m_color, &m_feedback}) + setupRotary(*this, *s); + m_time.setBounds(118, 18, knobSize, knobSize); + m_rate.setBounds(m_time.getRight() - 8, y, knobSize, knobSize); + m_damping.setBounds(m_rate.getRight() - 8, y, knobSize, knobSize); + m_color.setBounds(m_damping.getRight() - 3, y, knobSize, knobSize); + m_feedback.setBounds(m_color.getRight() - 3, y, knobSize, knobSize); + m_reverbMode.setBounds(18, 116+2+22, comboBoxWidth, comboBoxHeight); addAndMakeVisible(m_reverbMode); - m_reverbMode.setBounds(m_lfoShape.getBounds().getRight() + 26, 22, comboBoxWidth, comboBoxHeight); - - m_damping.getProperties().set(Virus::LookAndFeel::KnobStyleProp, Virus::LookAndFeel::KnobStyle::GENERIC_RED); - m_damping.setBounds(m_reverbMode.getBounds().getRight() + 2, -2, knobSize, knobSize); - addAndMakeVisible(m_damping); - _parameterBinding.bind(m_mix, Virus::Param_EffectSend); - _parameterBinding.bind(m_clock, Virus::Param_DelayClock, 0); - _parameterBinding.bind(m_lfoShape, Virus::Param_DelayLfoShape, 0); - _parameterBinding.bind(m_damping, Virus::Param_DelayLfoShape, 0); _parameterBinding.bind(m_reverbMode, Virus::Param_DelayDepthReverbRoomSize, 0); + _parameterBinding.bind(m_time, Virus::Param_DelayTime, 0); + _parameterBinding.bind(m_rate, Virus::Param_DelayRateReverbDecayTime, 0); + _parameterBinding.bind(m_damping, Virus::Param_DelayLfoShape, 0); + _parameterBinding.bind(m_color, Virus::Param_DelayColor, 0); + _parameterBinding.bind(m_feedback, Virus::Param_DelayFeedback, 0); } FxEditor::Vocoder::Vocoder(VirusParameterBinding &_parameterBinding) : diff --git a/source/jucePlugin/ui/Virus_FxEditor.h b/source/jucePlugin/ui/Virus_FxEditor.h @@ -2,14 +2,14 @@ #include "../PluginProcessor.h" #include "Virus_Buttons.h" - +#include "../VirusController.h" class VirusParameterBinding; class FxEditor : public juce::Component { public: - FxEditor(VirusParameterBinding& _parameterBinding); - + FxEditor(VirusParameterBinding& _parameterBinding, Virus::Controller& _controller); + void rebind(); private: struct Distortion : juce::Component { @@ -75,26 +75,29 @@ private: juce::Slider m_amount; } m_punch; - struct DelayAndReverb : juce::Component + struct Delay : juce::Component { - DelayAndReverb(VirusParameterBinding &_parameterBinding); + Delay(VirusParameterBinding &_parameterBinding); + std::unique_ptr<juce::Drawable> m_background; juce::Slider m_time; juce::Slider m_rate; juce::Slider m_depth; juce::Slider m_color; juce::Slider m_feedback; - juce::ComboBox m_fxMode; - - struct Sync : juce::Component - { - Sync(VirusParameterBinding &_parameterBinding); - juce::Slider m_mix; - juce::ComboBox m_clock, m_lfoShape; - juce::ComboBox m_reverbMode; - juce::Slider m_damping; - } m_sync; - } m_delayReverb; + juce::ComboBox m_clock, m_lfoShape; + }; + struct Reverb : juce::Component + { + Reverb(VirusParameterBinding &_parameterBinding); + std::unique_ptr<juce::Drawable> m_background; + juce::Slider m_time; + juce::Slider m_rate; + juce::Slider m_damping; + juce::Slider m_color; + juce::Slider m_feedback; + juce::ComboBox m_reverbMode; + }; struct Vocoder : juce::Component { Vocoder(VirusParameterBinding &_parameterBinding); @@ -123,6 +126,10 @@ private: juce::ComboBox m_modInput; } m_modulator; } m_vocoder; - + juce::ComboBox m_fxMode; + juce::Slider m_fxSend; + std::unique_ptr<Delay> m_delay; + std::unique_ptr<Reverb> m_reverb; + VirusParameterBinding &m_parameterBinding; std::unique_ptr<juce::Drawable> m_background; }; diff --git a/source/jucePlugin/ui/Virus_Parts.cpp b/source/jucePlugin/ui/Virus_Parts.cpp @@ -107,7 +107,7 @@ Parts::Parts(VirusParameterBinding & _parameterBinding, Virus::Controller& _cont addAndMakeVisible(m_btMultiMode); //addAndMakeVisible(m_btMultiSingleMode); m_btSingleMode.setTopLeftPosition(102, 756); - m_btSingleMode.setSize(70, 30); + m_btSingleMode.setSize(103, 30); m_btSingleMode.setColour(TextButton::ColourIds::textColourOnId, juce::Colours::white); m_btSingleMode.setColour(TextButton::ColourIds::textColourOffId, juce::Colours::grey); @@ -119,8 +119,8 @@ Parts::Parts(VirusParameterBinding & _parameterBinding, Virus::Controller& _cont 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)); + //m_btMultiSingleMode.setBounds(m_btSingleMode.getBounds().translated(m_btSingleMode.getWidth()+4, 0)); + m_btMultiMode.setBounds(m_btSingleMode.getBounds().translated(m_btSingleMode.getWidth()+4, 0)); } Parts::~Parts() { } @@ -172,5 +172,8 @@ void Parts::changePart(uint8_t _part) void Parts::setPlayMode(uint8_t _mode) { m_controller.getParameter(Virus::Param_PlayMode)->setValue(_mode); + if (_mode == virusLib::PlayModeSingle && m_controller.getCurrentPart() != 0) { + changePart(0); + } getParentComponent()->postCommandMessage(VirusEditor::Commands::Rebind); -} -\ No newline at end of file +} diff --git a/source/virusLib/microcontroller.cpp b/source/virusLib/microcontroller.cpp @@ -904,10 +904,10 @@ 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 (_part == PAGE_C && _param >= PART_MIDI_CHANNEL && _param <= PART_OUTPUT_SELECT) { + if (_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) { + else if (_param == CLOCK_TEMPO) { m_multiEditBuffer[MD_CLOCK_TEMPO] = _value; } }