diff --git a/Makefile b/Makefile index 63a0b6f..a1dd49f 100644 --- a/Makefile +++ b/Makefile @@ -47,12 +47,15 @@ SOURCES += src/HC-2/cc_map_widget.cpp SOURCES += src/HC-3/HC-3.cpp SOURCES += src/HC-3/HC-3-ui.cpp -SOURCES += src/HC-4/HC-4.cpp -SOURCES += src/HC-4/HC-4-ui.cpp +#SOURCES += src/HC-4/HC-4.cpp +#SOURCES += src/HC-4/HC-4-ui.cpp SOURCES += src/Pedals/Pedals.cpp SOURCES += src/Pedals/Pedals-ui.cpp +SOURCES += src/Round/Round.cpp +SOURCES += src/Round/Round-ui.cpp + DISTRIBUTABLES += res # DISTRIBUTABLES += presets # DISTRIBUTABLES += selections diff --git a/plugin.json b/plugin.json index b1d982e..8a8c1bd 100644 --- a/plugin.json +++ b/plugin.json @@ -32,10 +32,10 @@ "tags": [ "Expander" ] }, { - "slug": "pachde-hc-4", - "name": "HC-4", - "description": "Next HC One module", - "tags": [ "Expander" ] + "slug": "pachde-hc-round", + "name": "Round", + "description": "Rounding (HC-1 companion)", + "tags": [ "Controller", "MIDI", "Expander" ] }, { "slug": "pachde-hc-pedal-1", diff --git a/res/Pedal1.svg b/res/Pedal1.svg index 5aeb9a5..b888627 100644 --- a/res/Pedal1.svg +++ b/res/Pedal1.svg @@ -1,9 +1,11 @@ - + @@ -14,15 +16,4 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/res/Pedal2.svg b/res/Pedal2.svg index e72a46a..f7d4391 100644 --- a/res/Pedal2.svg +++ b/res/Pedal2.svg @@ -2,25 +2,15 @@ - + + - - - + + + - - - - - - - - - - - - - \ No newline at end of file + diff --git a/res/Round.svg b/res/Round.svg new file mode 100644 index 0000000..7a5d16b --- /dev/null +++ b/res/Round.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/HC-1/HC-1-draw.cpp b/src/HC-1/HC-1-draw.cpp index 217ba2f..2d95ae6 100644 --- a/src/HC-1/HC-1-draw.cpp +++ b/src/HC-1/HC-1-draw.cpp @@ -230,7 +230,7 @@ void Hc1ModuleWidget::drawPedalAssignment( void Hc1ModuleWidget::drawPedals(NVGcontext* vg, std::shared_ptr font, bool stockPedals) { - SetTextStyle(vg, font, RampGray(G_85), 10.f); + SetTextStyle(vg, font, GetStockColor(StockColor::Gold), 10.f); if (stockPedals) { drawPedalAssignment(vg, PRESET_RIGHT + 1.f, PRESET_BOTTOM - 19.f, '1', 64, 0); drawPedalAssignment(vg, PRESET_RIGHT + 1.f, PRESET_BOTTOM - 4.5f, '2', 66, 0); diff --git a/src/HC-1/HC-1-ui.cpp b/src/HC-1/HC-1-ui.cpp index e868b08..dad41ee 100644 --- a/src/HC-1/HC-1-ui.cpp +++ b/src/HC-1/HC-1-ui.cpp @@ -97,7 +97,7 @@ void Hc1ModuleWidget::createPresetPrevNext() }); } addChild(w); - addChild(createStaticTextLabel(Vec(Vec(RIGHT_COLUMN_BUTTONS - 7.f, y + 5.f)), 25.f, "-", TextAlignment::Center, 9.f, false)); + addChild(createStaticTextLabel(Vec(Vec(RIGHT_COLUMN_BUTTONS - 7.f, y + 5.f)), 25.f, "-", TextAlignment::Center, 10.f, true)); w = createWidgetCentered(Vec(RIGHT_COLUMN_BUTTONS + 7.f, y)); w->describe("Next preset\n Shift+Click by 10\n Ctrl+Click for device order"); @@ -111,10 +111,10 @@ void Hc1ModuleWidget::createPresetPrevNext() }); } addChild(w); - addChild(createStaticTextLabel(Vec(Vec(RIGHT_COLUMN_BUTTONS + 7.f, y + 5.f)), 25.f, "+", TextAlignment::Center, 9.f, false)); + addChild(createStaticTextLabel(Vec(Vec(RIGHT_COLUMN_BUTTONS + 7.f, y + 5.f)), 25.f, "+", TextAlignment::Center, 10.f, true)); y += 12.f; - addChild(createStaticTextLabel(Vec(Vec(RIGHT_COLUMN_BUTTONS, y)), 25.f, "Preset", TextAlignment::Center, 9.f, false)); + addChild(createStaticTextLabel(Vec(Vec(RIGHT_COLUMN_BUTTONS, y)), 25.f, "Preset", TextAlignment::Center, 10.f, true)); } void Hc1ModuleWidget::createPresetPaging() diff --git a/src/HC-2/HC-2-layout.hpp b/src/HC-2/HC-2-layout.hpp index 25c8aef..dfa8d61 100644 --- a/src/HC-2/HC-2-layout.hpp +++ b/src/HC-2/HC-2-layout.hpp @@ -10,26 +10,18 @@ constexpr const float KNOB_SPREAD = 54.25f; constexpr const float KNOB_COL1 = 35.f; constexpr const float KNOB_ROW = 44.f; -constexpr const float ROUND_BOX_TOP = 35.f; -constexpr const float ROUND_BOX_LEFT = 7.5f; -constexpr const float ROUND_BOX_WIDTH = 105.f; -constexpr const float ROUND_BOX_HALF = 110.f * .5f; constexpr const float REL_OFFSET = 20.f; constexpr const float REL_VOFFSET = 10.f; constexpr const float CV_COLUMN_OFFSET = 24.f; constexpr const float CV_ROW_OFFSET = 6.f; constexpr const float STATIC_LABEL_OFFSET = 29.5f; -constexpr const float ROUND_KNOB_ROW = 34.f; -constexpr const float ROUND_COL1 = ROUND_BOX_HALF - KNOB_RADIUS - 3.f * PAD; -constexpr const float ROUND_COL2 = ROUND_BOX_HALF + KNOB_RADIUS + 2.f * PAD; -constexpr const float ROUND_COL3 = ROUND_BOX_WIDTH - KNOB_RADIUS + PAD; -constexpr const float COMP_BOX_LEFT = ROUND_BOX_LEFT + ROUND_BOX_WIDTH + 7.5; -constexpr const float COMP_BOX_TOP = ROUND_BOX_TOP; +constexpr const float COMP_BOX_LEFT = 7.5; +constexpr const float COMP_BOX_TOP = 35.f; constexpr const float COMP_BOX_WIDTH = KNOB_SPREAD * 4.f; -constexpr const float TEQ_BOX_LEFT = ROUND_BOX_LEFT; -constexpr const float TEQ_BOX_TOP = ROUND_BOX_TOP + KNOB_BOX_HEIGHT + 7.5f; +constexpr const float TEQ_BOX_LEFT = 7.5f; +constexpr const float TEQ_BOX_TOP = COMP_BOX_TOP + KNOB_BOX_HEIGHT + 7.5f; constexpr const float TEQ_BOX_WIDTH = KNOB_SPREAD * 3.f; } \ No newline at end of file diff --git a/src/HC-2/HC-2-ui.cpp b/src/HC-2/HC-2-ui.cpp index eb28036..db10b5d 100644 --- a/src/HC-2/HC-2-ui.cpp +++ b/src/HC-2/HC-2-ui.cpp @@ -9,7 +9,6 @@ #include "../widgets/port.hpp" #include "../widgets/small_push.hpp" #include "../widgets/switch_4.hpp" -#include "tuning_ui.hpp" namespace pachde { @@ -28,55 +27,6 @@ inline uint8_t GetSmallParamValue(rack::app::ModuleWidget* w, int id, uint8_t de return U8(pq->getValue()); } -void Hc2ModuleWidget::createRoundingUI(float x, float y) -{ - // Rounding cluster - addChild(createHeaderWidget(x, y, ROUND_BOX_WIDTH, KNOB_BOX_HEIGHT)); - addChild(createStaticTextLabel(Vec(x + MORE_PAD, y + PAD), 40.f, "Rounding", TextAlignment::Left)); - - addParam(createParamCentered( - Vec(x + ROUND_COL2, y + PAD + PAD + HALF_KNOB), - module, Hc2P::P_ROUND_KIND)); - - addChild(createModKnob( - Vec( x + ROUND_COL1, y + ROUND_KNOB_ROW), - module, Hc2P::P_ROUND_RATE, Hc2I::IN_ROUND_RATE, Hc2P::P_ROUND_RATE_REL)); - addParam(createLightParamCentered>>( - Vec( x + ROUND_COL1 - REL_OFFSET, y + ROUND_KNOB_ROW - REL_VOFFSET), - module, Hc2P::P_ROUND_RATE_REL, Hc2L::L_ROUND_RATE_REL)); - addChild(createInputCentered( - Vec( x + ROUND_COL1 - CV_COLUMN_OFFSET, y + ROUND_KNOB_ROW + HALF_KNOB), - module, Hc2I::IN_ROUND_RATE)); - - auto p = createParamCentered(Vec( x + ROUND_COL2, y + ROUND_KNOB_ROW), module, Hc2P::P_ROUND_TUNING); - p->setImage(); - addChild(p); - - addParam(createLightParamCentered>>( - Vec( x + ROUND_COL3, y + ROUND_KNOB_ROW - REL_VOFFSET), - module, Hc2P::P_ROUND_INITIAL, Hc2L::L_ROUND_INITIAL)); - addChild(createInputCentered( - Vec( x + ROUND_COL3, y + ROUND_KNOB_ROW + HALF_KNOB), - module, Hc2I::IN_ROUND_INITIAL)); - - rounding_summary = createLazyDynamicTextLabel( - Vec(x + ROUND_BOX_HALF, y + KNOB_BOX_HEIGHT - 11.f), - Vec(100.f, 12.f), - [=]() { - bool initial = GetSmallParamValue(this, Hc2P::P_ROUND_INITIAL); - auto rk = describeRoundKindShort(static_cast(GetSmallParamValue(this, Hc2P::P_ROUND_KIND))); - auto rr = GetSmallParamValue(this,Hc2P::P_ROUND_RATE); - PackedTuning tuning = static_cast(GetSmallParamValue(this, Hc2P::P_ROUND_TUNING)); - auto ts = describeTuning(unpackTuning(tuning)); - return format_string("%s %s %d %s", - rk.c_str(), (initial ? "I" : "\u2022"), rr, ts.c_str()); - }, - 9.f, false, TextAlignment::Center, - GetStockColor(StockColor::Gold), true) - ; - addChild(rounding_summary); -} - void Hc2ModuleWidget::createCompressorUI(float x, float y) { addChild(createHeaderWidget(x, y, COMP_BOX_WIDTH, KNOB_BOX_HEIGHT)); @@ -158,7 +108,6 @@ Hc2ModuleWidget::Hc2ModuleWidget(Hc2Module * module) setPanel(createPanel(asset::plugin(pluginInstance, "res/HC-2.svg"))); addChild(partner_picker = createPartnerPicker(7.f, 14.f, 180.f, module ? &module->partner_binding : nullptr)); - createRoundingUI(ROUND_BOX_LEFT, ROUND_BOX_TOP); createCompressorUI(COMP_BOX_LEFT, COMP_BOX_TOP); createTiltEqUI(TEQ_BOX_LEFT, TEQ_BOX_TOP); @@ -167,16 +116,6 @@ Hc2ModuleWidget::Hc2ModuleWidget(Hc2Module * module) addChild(createCCMap(x, box.size.y - 24.f, true, CCMapChannel::Sixteen, this)); } -void Hc2ModuleWidget::onPresetChanged(const PresetChangedEvent& e) -{ - rounding_summary->modified(); -} - -void Hc2ModuleWidget::onRoundingChanged(const RoundingChangedEvent& e) -{ - rounding_summary->modified(); -} - void Hc2ModuleWidget::onDeviceChanged(const DeviceChangedEvent& e) { partner_picker->onDeviceChanged(e); diff --git a/src/HC-2/HC-2.cpp b/src/HC-2/HC-2.cpp index 8a107d6..df7081d 100644 --- a/src/HC-2/HC-2.cpp +++ b/src/HC-2/HC-2.cpp @@ -6,7 +6,6 @@ #include "../widgets/cc_param.hpp" #include "../widgets/components.hpp" #include "../widgets/pedal_param.hpp" -#include "tuning_ui.hpp" namespace pachde { @@ -16,21 +15,6 @@ Hc2Module::Hc2Module() config(Params::NUM_PARAMS, Inputs::NUM_INPUTS, Outputs::NUM_OUTPUTS, Lights::NUM_LIGHTS); - configInput(Inputs::IN_ROUND_RATE, "Round rate"); - - auto p = configCCParam(EMCC_RoundRate, false, this, Params::P_ROUND_RATE, Inputs::IN_ROUND_RATE, Params::P_ROUND_RATE_REL, Lights::L_ROUND_RATE_REL, 0.f, 127.f, 0.f, "Round rate"); - p->snapEnabled = true; - configSwitch(P_ROUND_RATE_REL, 0.f, 1.f, 0.f, "Round rate CV-relative", offon); - configSwitch(P_ROUND_INITIAL, 0.f, 1.f, 0.f, "Round initial", offon); - configInput(Inputs::IN_ROUND_INITIAL, "Round initial trigger"); - configSwitch(P_ROUND_KIND, 0.f, 3.f, 0.f, "Round type", { - "Normal", - "Release", - "Y (Full to none)", - "Inverse Y (None to full)" - }); - configTuningParam(this, P_ROUND_TUNING); - configCCParam(EMCC_CompressorThreshold, false, this, P_COMP_THRESHOLD, IN_COMP_THRESHOLD, P_COMP_THRESHOLD_REL, L_COMP_THRESHOLD_REL, 0.f, 127.f, 127.f, "Threshold", "%", 0.f, 100.f/127.f)->snapEnabled = true; configCCParam(EMCC_CompressorThreshold, false, this, P_COMP_ATTACK, IN_COMP_ATTACK, P_COMP_ATTACK_REL, L_COMP_ATTACK_REL, 0.f, 127.f, 64.f, "Attack", "%", 0.f, 100.f/127.f)->snapEnabled = true; configCCParam(EMCC_CompressorThreshold, false, this, P_COMP_RATIO, IN_COMP_RATIO, P_COMP_RATIO_REL, L_COMP_RATIO_REL, 0.f, 127.f, 64.f, "Ratio", "%", 0.f, 100.f/127.f)->snapEnabled = true; @@ -93,51 +77,6 @@ Hc1Module* Hc2Module::getPartner() return getPartnerImpl(this); } -void Hc2Module::onPresetChanged(const PresetChangedEvent& e) -{ - pullRounding(); - if (ui_event_sink) { - ui_event_sink->onPresetChanged(e); - } -} - -void Hc2Module::onRoundingChanged(const RoundingChangedEvent& e) -{ - bool changed = false; - auto old = rounding; - rounding = e.rounding; - - if (old.rate != rounding.rate) { - changed = true; - auto pq = dynamic_cast(getParamQuantity(Params::P_ROUND_RATE)); - pq->setValueSilent(rounding.rate); - } - - if (old.initial != rounding.initial) { - changed = true; - getParamQuantity(Params::P_ROUND_INITIAL)->setValue(1.f * rounding.initial); - getLight(Lights::L_ROUND_INITIAL).setBrightness(1.0f * rounding.initial); - } - - if (old.kind != rounding.kind) { - changed = true; - getParamQuantity(Params::P_ROUND_KIND)->setValue(static_cast(rounding.kind)); - } - if (old.tuning != rounding.tuning) { - changed = true; - getParamQuantity(Params::P_ROUND_TUNING)->setValue(packTuning(rounding.tuning)); - } - if (old.equal != rounding.equal) { - changed = true; - // $BUGBUG: why is this commented out? - //getParamQuantity(Params::P_ROUND_EQUAL)->setValue(rounding.equal); - } - - if (changed && ui_event_sink) { - ui_event_sink->onRoundingChanged(e); - } -} - void Hc2Module::onCompressorChanged(const CompressorChangedEvent &e) { bool changed = false; @@ -215,31 +154,6 @@ void Hc2Module::onDisconnect(const DisconnectEvent& e) } } -void Hc2Module::pullRounding(Hc1Module * partner) -{ - if (!partner) partner = getPartner(); - if (!partner) return; - rounding = partner->em.rounding; - getParamQuantity(Params::P_ROUND_KIND)->setValue(static_cast(rounding.kind)); - getParamQuantity(Params::P_ROUND_INITIAL)->setValue(1.f * rounding.initial); - getLight(Lights::L_ROUND_INITIAL).setBrightness(1.0f * rounding.initial); - dynamic_cast(getParamQuantity(Params::P_ROUND_RATE))->setValueSilent(rounding.rate); - getParamQuantity(Params::P_ROUND_TUNING)->setValue(packTuning(rounding.tuning)); - if (ui_event_sink) { - ui_event_sink->onRoundingChanged(RoundingChangedEvent{rounding}); - } -} - -void Hc2Module::pushRounding(Hc1Module * partner) -{ - if (!partner) partner = getPartner(); - if (!partner) return; - partner->em.rounding = rounding; - if (ui_event_sink) { - ui_event_sink->onRoundingChanged(RoundingChangedEvent{rounding}); - } -} - void Hc2Module::pullCompressor(Hc1Module *partner) { if (!partner) partner = getPartner(); @@ -313,50 +227,6 @@ void Hc2Module::processCV(int paramId) } } -void Hc2Module::processRoundingControls() -{ - { - auto pq = dynamic_cast(getParamQuantity(Params::P_ROUND_RATE)); - assert(pq); - auto rr = pq->valueToSend(); - if (pq->last_value != rr) { - rounding.rate = rr; - pushRounding(); - pq->syncValue(); - } - } - { - bool ri = getParamQuantity(Params::P_ROUND_INITIAL)->getValue() >= .5f; - getLight(Lights::L_ROUND_INITIAL).setBrightness(1.0f * rounding.initial); - if (rounding.initial != ri) { - rounding.initial = ri; - pushRounding(); - sendControlChange(EM_MasterChannel, EMCC_RoundInitial, ri * 127); - } - } - - { - RoundKind kind = static_cast(static_cast(getParamQuantity(P_ROUND_KIND)->getValue())); - if (kind != rounding.kind) { - rounding.kind = kind; - pushRounding(); - auto partner = getPartner(); - uint8_t rev = partner ? (partner->em.reverse_surface ? 1 : 0) : 0; - sendControlChange(EM_SettingsChannel, EMCC_Reverse_Rounding, (static_cast(kind) << 1) | rev); - } - } - - { - auto tq = dynamic_cast(getParamQuantity(P_ROUND_TUNING)); - Tuning tuning = tq->getTuning(); - if (tuning != rounding.tuning) { - rounding.tuning = tuning; - pushRounding(); - sendControlChange(EM_SettingsChannel, EMCC_TuningGrid, tuning); - } - } -} - void Hc2Module::processCompressorControls() { bool changed = false; @@ -426,7 +296,6 @@ void Hc2Module::processTiltEqControls() void Hc2Module::processControls() { if (!control_rate.process()) { return; } - processRoundingControls(); processCompressorControls(); processTiltEqControls(); } @@ -435,7 +304,6 @@ void Hc2Module::process(const ProcessArgs& args) { if (++check_cv > CV_INTERVAL) { check_cv = 0; - processCV(Params::P_ROUND_RATE); processCV(Params::P_COMP_THRESHOLD); processCV(Params::P_COMP_ATTACK); processCV(Params::P_COMP_RATIO); @@ -445,15 +313,6 @@ void Hc2Module::process(const ProcessArgs& args) processCV(Params::P_TEQ_MIX); } - if (getInput(Inputs::IN_ROUND_INITIAL).isConnected()) { - auto v = getInput(Inputs::IN_ROUND_INITIAL).getVoltage(); - if (round_initial_trigger.process(v, 0.1f, 5.f)) { - round_initial_trigger.reset(); - auto pq = getParamQuantity(Params::P_ROUND_INITIAL); - bool ri = !(pq->getValue() >= .5f); // toggle - pq->setValue(1.0f * ri); - } - } processControls(); getLight(Lights::L_COMPRESSOR).setBrightness(static_cast(compressor.mix)/127.f); getLight(Lights::L_TEQ).setBrightness(static_cast(tilt_eq.mix)/127.f); diff --git a/src/HC-2/HC-2.hpp b/src/HC-2/HC-2.hpp index 6ae2a6b..a0a5fc5 100644 --- a/src/HC-2/HC-2.hpp +++ b/src/HC-2/HC-2.hpp @@ -27,15 +27,6 @@ struct Hc2Module : Module, ISendMidi, IHandleHcEvents { enum Params { - P_ROUND_RATE, // 0..127 cc25 - P_ROUND_INITIAL, // 0..1 cc 28 - P_ROUND_KIND, // 0..3 ch16 cc61, but includes reverse surface bit - P_ROUND_TUNING, // 0..69 - P_ROUND_RATE_REL, - - P_PEDAL1, - P_PEDAL2, - P_COMP_THRESHOLD, P_COMP_ATTACK, P_COMP_RATIO, @@ -56,9 +47,6 @@ struct Hc2Module : Module, ISendMidi, IHandleHcEvents }; enum Inputs { - IN_ROUND_RATE, - IN_ROUND_INITIAL, - IN_COMP_THRESHOLD, IN_COMP_ATTACK, IN_COMP_RATIO, @@ -76,9 +64,6 @@ struct Hc2Module : Module, ISendMidi, IHandleHcEvents }; enum Lights { - L_ROUND_RATE_REL, - L_ROUND_INITIAL, - L_COMP_THRESHOLD_REL, L_COMP_ATTACK_REL, L_COMP_RATIO_REL, @@ -95,7 +80,6 @@ struct Hc2Module : Module, ISendMidi, IHandleHcEvents NUM_LIGHTS }; - Rounding rounding; Compressor compressor; TiltEq tilt_eq; @@ -113,8 +97,6 @@ struct Hc2Module : Module, ISendMidi, IHandleHcEvents explicit Hc2Module(); virtual ~Hc2Module(); - void pullRounding(Hc1Module * partner = nullptr); - void pushRounding(Hc1Module * partner = nullptr); void pullCompressor(Hc1Module * partner = nullptr); void pushCompressor(Hc1Module * partner = nullptr); void pullTiltEq(Hc1Module * partner = nullptr); @@ -145,9 +127,9 @@ struct Hc2Module : Module, ISendMidi, IHandleHcEvents bool readyToSend() override; // IHandleHcEvents - void onPresetChanged(const PresetChangedEvent& e) override; + //void onPresetChanged(const PresetChangedEvent& e) override; //void onPedalChanged(const PedalChangedEvent& e) override; - void onRoundingChanged(const RoundingChangedEvent& e) override; + //void onRoundingChanged(const RoundingChangedEvent& e) override; void onCompressorChanged(const CompressorChangedEvent& e) override; void onTiltEqChanged(const TiltEqChangedEvent& e) override; void onDeviceChanged(const DeviceChangedEvent& e) override; @@ -158,7 +140,6 @@ struct Hc2ModuleWidget : ModuleWidget, IHandleHcEvents { Hc2Module* my_module = nullptr; PartnerPicker* partner_picker = nullptr; - DynamicTextLabel* rounding_summary = nullptr; explicit Hc2ModuleWidget(Hc2Module * module); virtual ~Hc2ModuleWidget() { @@ -167,14 +148,13 @@ struct Hc2ModuleWidget : ModuleWidget, IHandleHcEvents // } } Hc1Module* getPartner(); - void createRoundingUI(float x, float y); void createCompressorUI(float x, float y); void createTiltEqUI(float x, float y); // IHandleHcEvents - void onPresetChanged(const PresetChangedEvent& e) override; + //void onPresetChanged(const PresetChangedEvent& e) override; //void onPedalChanged(const PedalChangedEvent& e) override; - void onRoundingChanged(const RoundingChangedEvent& e) override; + //void onRoundingChanged(const RoundingChangedEvent& e) override; void onDeviceChanged(const DeviceChangedEvent& e) override; void onDisconnect(const DisconnectEvent& e) override; diff --git a/src/Pedals/Pedals-ui.cpp b/src/Pedals/Pedals-ui.cpp index 87498a6..719310b 100644 --- a/src/Pedals/Pedals-ui.cpp +++ b/src/Pedals/Pedals-ui.cpp @@ -60,7 +60,7 @@ void PedalUICore::createUI() /// pedal function knob float x_center = box.size.x * .5f; - addChild(pedal_assign = createStaticTextLabel(Vec(x_center, 37.f), 60.f, "Sustain", TextAlignment::Center, 10.f, true)); + addChild(pedal_assign = createStaticTextLabel(Vec(x_center, 37.f), 60.f, "Sustain", TextAlignment::Center, 9.f, false, GetStockColor(StockColor::Gold))); addChild(createParamCentered(Vec(x_center, 64.f), module, PedalCore::P_PEDAL_ASSIGN)); // value slider diff --git a/src/Round/Round-ui.cpp b/src/Round/Round-ui.cpp new file mode 100644 index 0000000..f3c1a81 --- /dev/null +++ b/src/Round/Round-ui.cpp @@ -0,0 +1,127 @@ +#include "Round.hpp" +#include "../widgets/port.hpp" +#include "../widgets/cc_param.hpp" +#include "../widgets/switch_4.hpp" +#include "../colors.hpp" +#include "../misc.hpp" +#include "../text.hpp" +#include "tuning_ui.hpp" + +namespace pachde { + +using RP = RoundModule::Params; +using RI = RoundModule::Inputs; +using RL = RoundModule::Lights; + +constexpr const float REL_OFFSET = 20.f; +constexpr const float REL_VOFFSET = 10.f; +constexpr const float CV_COLUMN_OFFSET = 24.f; +constexpr const float CV_ROW_OFFSET = 6.f; +constexpr const float STATIC_LABEL_OFFSET = 29.5f; +constexpr const float KNOB_RADIUS = 12.f; +constexpr const float HALF_KNOB = KNOB_RADIUS *.5f; + +inline uint8_t GetSmallParamValue(rack::app::ModuleWidget* w, int id, uint8_t default_value = 0) { + auto p = w->getParam(id); + if (!p) return default_value; + auto pq = p->getParamQuantity(); + if (!pq) return default_value; + return U8(pq->getValue()); +} + +RoundModuleWidget::RoundModuleWidget(RoundModule * module) +: my_module(module) +{ + setModule(module); + if (module) { + my_module->ui_event_sink = this; + } + setPanel(createPanel(asset::plugin(pluginInstance, "res/Round.svg"))); + addChild(partner_picker = createPartnerPicker(7.f, 14.f, 180.f, module ? &module->partner_binding : nullptr)); + + float center = box.size.x * .5f; + + // Type + float y = 60.f; + addParam(createParamCentered( + Vec(center, y), + module, RP::P_ROUND_KIND)); + y += 10.f; + addChild(type_text = createLazyDynamicTextLabel( + Vec(center, y), + Vec(100.f, 12.f), + [=]() { return describeRoundKind(static_cast(GetSmallParamValue(this, RP::P_ROUND_KIND))); }, + 9.f, false, TextAlignment::Center,GetStockColor(StockColor::Gold), false)); + + // Tuning + y = 130.f; + auto p = createParamCentered(Vec(center, y), module, RP::P_ROUND_TUNING); + p->setImage(); + addChild(p); + y += KNOB_RADIUS + 4.f; + addChild(tuning_text = createLazyDynamicTextLabel( + Vec(center, y), + Vec(100.f, 12.f), + [=]() { + PackedTuning tuning = static_cast(GetSmallParamValue(this, RP::P_ROUND_TUNING)); + return describeTuning(unpackTuning(tuning)); + }, + 9.f, false, TextAlignment::Center,GetStockColor(StockColor::Gold), false)); + + // Initial + y = 190.f; + addParam(createLightParamCentered>>( + Vec(center, y), + module, RP::P_ROUND_INITIAL, RL::L_ROUND_INITIAL)); + y += 16.f; + addChild(createInputCentered( + Vec(center, y), + module, RI::IN_ROUND_INITIAL)); + + // Rate + y = 265.f; + addChild(createModKnob( + Vec( center, y), + module, RP::P_ROUND_RATE, RI::IN_ROUND_RATE, RP::P_ROUND_RATE_REL)); + y += 18.f; + addParam(createLightParamCentered>>( + Vec(center - 14.f, y), + module, RP::P_ROUND_RATE_REL, RL::L_ROUND_RATE_REL)); + y += KNOB_RADIUS; + addChild(createInputCentered( + Vec( center, y), + module, RI::IN_ROUND_RATE)); + +} + +Hc1Module* RoundModuleWidget::getPartner() +{ + if (!module) return nullptr; + return my_module->getPartner(); +} + + +void RoundModuleWidget::onPresetChanged(const PresetChangedEvent& e) +{ + tuning_text->modified(); + type_text->modified(); +} + +void RoundModuleWidget::onRoundingChanged(const RoundingChangedEvent &e) +{ + tuning_text->modified(); + type_text->modified(); +} + +void RoundModuleWidget::onDeviceChanged(const DeviceChangedEvent &e) +{ + partner_picker->onDeviceChanged(e); +} + +void RoundModuleWidget::onDisconnect(const DisconnectEvent& e) +{ + partner_picker->onDisconnect(e); +} + + +} \ No newline at end of file diff --git a/src/Round/Round.cpp b/src/Round/Round.cpp new file mode 100644 index 0000000..29e7cef --- /dev/null +++ b/src/Round/Round.cpp @@ -0,0 +1,260 @@ +#include "Round.hpp" +#include "../widgets/cc_param.hpp" +#include "tuning_ui.hpp" + +namespace pachde { + +RoundModule::RoundModule() +{ + std::vector offon = {"off", "on"}; + config(Params::NUM_PARAMS, Inputs::NUM_INPUTS, Outputs::NUM_OUTPUTS, Lights::NUM_LIGHTS); + + configInput(Inputs::IN_ROUND_RATE, "Round rate"); + + auto p = configCCParam(EMCC_RoundRate, false, this, Params::P_ROUND_RATE, Inputs::IN_ROUND_RATE, Params::P_ROUND_RATE_REL, Lights::L_ROUND_RATE_REL, 0.f, 127.f, 0.f, "Round rate"); + p->snapEnabled = true; + configSwitch(P_ROUND_RATE_REL, 0.f, 1.f, 0.f, "Round rate CV-relative", offon); + configSwitch(P_ROUND_INITIAL, 0.f, 1.f, 0.f, "Round initial", offon); + configInput(Inputs::IN_ROUND_INITIAL, "Round initial trigger"); + configSwitch(P_ROUND_KIND, 0.f, 3.f, 0.f, "Round type", { + "Normal", + "Release", + "Y (Full to none)", + "Inverse Y (None to full)" + }); + configTuningParam(this, P_ROUND_TUNING); +} + +RoundModule::~RoundModule() +{ + if (!partner_subscribed) return; + auto partner = partner_binding.getPartner(); + if (partner){ + partner->unsubscribeHcEvents(this); + partner_subscribed = false; + } +} + +json_t * RoundModule::dataToJson() +{ + auto root = json_object(); + json_object_set_new(root, "device", json_string(partner_binding.claim.c_str())); + return root; +} + +void RoundModule::dataFromJson(json_t *root) +{ + auto j = json_object_get(root, "device"); + if (j) { + partner_binding.setClaim(json_string_value(j)); + } +} + +Hc1Module* RoundModule::getPartner() +{ + return getPartnerImpl(this); +} + +// ISendMidi +void RoundModule::sendControlChange(uint8_t channel, uint8_t cc, uint8_t value) +{ + auto partner = getPartner(); + if (partner) { + partner->sendControlChange(channel, cc, value); + } +} +//void sendProgramChange(uint8_t channel, uint8_t program) {} +//void sendKeyPressure(uint8_t channel, uint8_t note, uint8_t pressure) {} +//void sendChannelPressure(uint8_t channel, uint8_t pressure) {} +//void sendPitchBend(uint8_t channel, uint8_t bend_lo, uint8_t bend_hi) {} +bool RoundModule::readyToSend() +{ + auto partner = getPartner(); + return partner && partner->ready(); +} + +void RoundModule::onPresetChanged(const PresetChangedEvent &e) +{ + pullRounding(); + if (ui_event_sink) { + ui_event_sink->onPresetChanged(e); + } +} + +void RoundModule::onRoundingChanged(const RoundingChangedEvent& e) +{ + bool changed = false; + auto old = rounding; + rounding = e.rounding; + + if (old.rate != rounding.rate) { + changed = true; + auto pq = dynamic_cast(getParamQuantity(Params::P_ROUND_RATE)); + pq->setValueSilent(rounding.rate); + } + + if (old.initial != rounding.initial) { + changed = true; + getParamQuantity(Params::P_ROUND_INITIAL)->setValue(1.f * rounding.initial); + getLight(Lights::L_ROUND_INITIAL).setBrightness(1.0f * rounding.initial); + } + + if (old.kind != rounding.kind) { + changed = true; + getParamQuantity(Params::P_ROUND_KIND)->setValue(static_cast(rounding.kind)); + } + if (old.tuning != rounding.tuning) { + changed = true; + getParamQuantity(Params::P_ROUND_TUNING)->setValue(packTuning(rounding.tuning)); + } + if (old.equal != rounding.equal) { + changed = true; + // $BUGBUG: why is this commented out? + //getParamQuantity(Params::P_ROUND_EQUAL)->setValue(rounding.equal); + } + + if (changed && ui_event_sink) { + ui_event_sink->onRoundingChanged(e); + } +} + +void RoundModule::onDeviceChanged(const DeviceChangedEvent& e) +{ + partner_binding.setClaim(e.device ? e.device->info.spec() : 0); + if (ui_event_sink) { + ui_event_sink->onDeviceChanged(e); + } +} + +void RoundModule::onDisconnect(const DisconnectEvent& e) +{ + partner_subscribed = false; + partner_binding.forgetModule(); + if (ui_event_sink) { + ui_event_sink->onDisconnect(e); + } +} +void RoundModule::pullRounding(Hc1Module * partner) +{ + if (!partner) partner = getPartner(); + if (!partner) return; + rounding = partner->em.rounding; + getParamQuantity(Params::P_ROUND_KIND)->setValue(static_cast(rounding.kind)); + getParamQuantity(Params::P_ROUND_INITIAL)->setValue(1.f * rounding.initial); + getLight(Lights::L_ROUND_INITIAL).setBrightness(1.0f * rounding.initial); + dynamic_cast(getParamQuantity(Params::P_ROUND_RATE))->setValueSilent(rounding.rate); + getParamQuantity(Params::P_ROUND_TUNING)->setValue(packTuning(rounding.tuning)); + if (ui_event_sink) { + ui_event_sink->onRoundingChanged(RoundingChangedEvent{rounding}); + } +} + +void RoundModule::processCV(int paramId) +{ + auto pq = dynamic_cast(getParamQuantity(paramId)); + if (!pq) return; + if (pq->inputId < 0) return; + + auto in = getInput(pq->inputId); + + bool relative = pq->relativeId >= 0 ? getParam(pq->relativeId).getValue() > .5f : false; + if (pq->lightId >= 0) { + getLight(pq->lightId).setBrightness((relative *.20f) + ((in.isConnected() && relative) *.80f)); + } + + if (in.isConnected()) { + auto v = in.getVoltage(); + if (relative) { + pq->setRelativeVoltage(v); + } else { + pq->offset = 0.f; + pq->setKnobVoltage(v); + } + } else { + pq->offset = 0.f; + } +} + +void RoundModule::pushRounding(Hc1Module * partner) +{ + if (!partner) partner = getPartner(); + if (!partner) return; + partner->em.rounding = rounding; + if (ui_event_sink) { + ui_event_sink->onRoundingChanged(RoundingChangedEvent{rounding}); + } +} + +void RoundModule::processRoundingControls() +{ + { + auto pq = dynamic_cast(getParamQuantity(Params::P_ROUND_RATE)); + assert(pq); + auto rr = pq->valueToSend(); + if (pq->last_value != rr) { + rounding.rate = rr; + pushRounding(); + pq->syncValue(); + } + } + { + bool ri = getParamQuantity(Params::P_ROUND_INITIAL)->getValue() >= .5f; + getLight(Lights::L_ROUND_INITIAL).setBrightness(1.0f * rounding.initial); + if (rounding.initial != ri) { + rounding.initial = ri; + pushRounding(); + sendControlChange(EM_MasterChannel, EMCC_RoundInitial, ri * 127); + } + } + + { + RoundKind kind = static_cast(static_cast(getParamQuantity(P_ROUND_KIND)->getValue())); + if (kind != rounding.kind) { + rounding.kind = kind; + pushRounding(); + auto partner = getPartner(); + uint8_t rev = partner ? (partner->em.reverse_surface ? 1 : 0) : 0; + sendControlChange(EM_SettingsChannel, EMCC_Reverse_Rounding, (static_cast(kind) << 1) | rev); + } + } + + { + auto tq = dynamic_cast(getParamQuantity(P_ROUND_TUNING)); + Tuning tuning = tq->getTuning(); + if (tuning != rounding.tuning) { + rounding.tuning = tuning; + pushRounding(); + sendControlChange(EM_SettingsChannel, EMCC_TuningGrid, tuning); + } + } +} + +void RoundModule::processControls() +{ + if (!control_rate.process()) { return; } + processRoundingControls(); +} + +void RoundModule::process(const ProcessArgs& args) +{ + if (++check_cv > CV_INTERVAL) { + check_cv = 0; + processCV(Params::P_ROUND_RATE); + } + + if (getInput(Inputs::IN_ROUND_INITIAL).isConnected()) { + auto v = getInput(Inputs::IN_ROUND_INITIAL).getVoltage(); + if (round_initial_trigger.process(v, 0.1f, 5.f)) { + round_initial_trigger.reset(); + auto pq = getParamQuantity(Params::P_ROUND_INITIAL); + bool ri = !(pq->getValue() >= .5f); // toggle + pq->setValue(1.0f * ri); + } + } + + processControls(); +} + +} + +Model *modelRound = createModel("pachde-hc-round"); \ No newline at end of file diff --git a/src/Round/Round.hpp b/src/Round/Round.hpp new file mode 100644 index 0000000..d8a2e3f --- /dev/null +++ b/src/Round/Round.hpp @@ -0,0 +1,121 @@ +#pragma once +#ifndef ROUND_HPP_INCLUDED +#define ROUND_HPP_INCLUDED +#include +#include "../hc_events.hpp" +#include "../module_broker.hpp" +#include "../plugin.hpp" +#include "../widgets/label_widget.hpp" +#include "../widgets/partner_picker.hpp" +#include "../widgets/symbol_widget.hpp" + +namespace pachde { + +using Symbol = SymbolWidget::Symbol; + + +struct RoundModule : Module, ISendMidi, IHandleHcEvents +{ + enum Params + { + P_ROUND_RATE, // 0..127 cc25 + P_ROUND_INITIAL, // 0..1 cc 28 + P_ROUND_KIND, // 0..3 ch16 cc61, but includes reverse surface bit + P_ROUND_TUNING, // 0..69 + P_ROUND_RATE_REL, + NUM_PARAMS, + }; + enum Inputs + { + IN_ROUND_RATE, + IN_ROUND_INITIAL, + NUM_INPUTS + }; + enum Outputs + { + NUM_OUTPUTS + }; + enum Lights + { + L_ROUND_RATE_REL, + L_ROUND_INITIAL, + NUM_LIGHTS + }; + + Rounding rounding; + + PartnerBinding partner_binding; + bool partner_subscribed = false; + + IHandleHcEvents * ui_event_sink = nullptr; + const int CV_INTERVAL = 128; + int check_cv = 0; + RateTrigger control_rate; + rack::dsp::SchmittTrigger round_initial_trigger; + + void pullRounding(Hc1Module * partner = nullptr); + void pushRounding(Hc1Module * partner = nullptr); + void processCV(int paramId); + void processRoundingControls(); + void processControls(); + + RoundModule(); + virtual ~RoundModule(); + Hc1Module * getPartner(); + + // ISendMidi + //virtual void sendNoteOn(uint8_t channel, uint8_t note, uint8_t velocity) {} + //virtual void sendNoteOff(uint8_t channel, uint8_t note, uint8_t velocity) {} + void sendControlChange(uint8_t channel, uint8_t cc, uint8_t value) override; + //void sendProgramChange(uint8_t channel, uint8_t program) {} + //void sendKeyPressure(uint8_t channel, uint8_t note, uint8_t pressure) {} + //void sendChannelPressure(uint8_t channel, uint8_t pressure) {} + //void sendPitchBend(uint8_t channel, uint8_t bend_lo, uint8_t bend_hi) {} + bool readyToSend() override; + + // IHandleHcEvents + void onPresetChanged(const PresetChangedEvent& e) override; + void onRoundingChanged(const RoundingChangedEvent& e) override; + // void onPedalChanged(const PedalChangedEvent& e) override; + void onDeviceChanged(const DeviceChangedEvent& e) override; + void onDisconnect(const DisconnectEvent& e) override; + // void onFavoritesFileChanged(const FavoritesFileChangedEvent& e) override; + + // Module + void onSampleRateChange(const SampleRateChangeEvent& e) override { + control_rate.onSampleRateChanged(e); + } + json_t *dataToJson() override; + void dataFromJson(json_t *root) override; + void process(const ProcessArgs& args) override; +}; + + +struct RoundModuleWidget : ModuleWidget, IHandleHcEvents +{ + RoundModule * my_module; + PartnerPicker* partner_picker = nullptr; + + DynamicTextLabel* tuning_text = nullptr; + DynamicTextLabel* type_text = nullptr; + + explicit RoundModuleWidget(RoundModule * module); + virtual ~RoundModuleWidget() { + if (my_module) { + my_module->ui_event_sink = nullptr; + } + } + + Hc1Module * getPartner(); + + // IHandleHcEvents + void onPresetChanged(const PresetChangedEvent& e) override; + void onRoundingChanged(const RoundingChangedEvent& e) override; + void onDeviceChanged(const DeviceChangedEvent& e) override; + void onDisconnect(const DisconnectEvent& e) override; + // void onFavoritesFileChanged(const FavoritesFileChangedEvent& e) override; + +}; + +} +#endif \ No newline at end of file diff --git a/src/HC-2/tuning_ui.hpp b/src/Round/tuning_ui.hpp similarity index 99% rename from src/HC-2/tuning_ui.hpp rename to src/Round/tuning_ui.hpp index 33feb80..54470cd 100644 --- a/src/HC-2/tuning_ui.hpp +++ b/src/Round/tuning_ui.hpp @@ -2,7 +2,6 @@ #ifndef TUNING_UI_HPP_INCLUDED #define TUNING_UI_HPP_INCLUDED #include -#include "HC-2.hpp" #include "../em.hpp" using namespace ::rack; namespace pachde { diff --git a/src/em_types/em_rounding.cpp b/src/em_types/em_rounding.cpp index 37ea810..eb5a11b 100644 --- a/src/em_types/em_rounding.cpp +++ b/src/em_types/em_rounding.cpp @@ -8,8 +8,8 @@ std::string describeRoundKind(RoundKind kind) switch (kind) { case RoundKind::Normal: return "Normal"; case RoundKind::Release: return "Release"; - case RoundKind::Y: return "Y (Full to none)"; - case RoundKind::InverseY: return "Inverse Y (None to full)"; + case RoundKind::Y: return "Y"; + case RoundKind::InverseY: return "Inverse Y"; } return "?"; } diff --git a/src/plugin.cpp b/src/plugin.cpp index 779a57e..8cd5f9e 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -8,9 +8,10 @@ void init(Plugin *p) p->addModel(modelHc1); p->addModel(modelHc2); p->addModel(modelHc3); - p->addModel(modelHc4); + //p->addModel(modelHc4); p->addModel(modelPedal1); p->addModel(modelPedal2); + p->addModel(modelRound); // Any other plugin initialization may go here. // As an alternative, consider lazy-loading assets and lookup tables when your module is created to reduce startup times of Rack. diff --git a/src/plugin.hpp b/src/plugin.hpp index cd31d25..1f4ed5f 100644 --- a/src/plugin.hpp +++ b/src/plugin.hpp @@ -6,7 +6,8 @@ extern Plugin* pluginInstance; extern Model* modelHc1; extern Model* modelHc2; extern Model* modelHc3; -extern Model* modelHc4; +//extern Model* modelHc4; extern Model* modelPedal1; extern Model* modelPedal2; +extern Model* modelRound;