diff --git a/.travis.yml b/.travis.yml index 7bfde22..d14e567 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,7 +45,7 @@ env: install: - mkdir -p "${HOME}"/Rack - pushd "${HOME}"/Rack -- curl -o Rack-SDK.zip https://vcvrack.com/downloads/Rack-SDK-1.1.4.zip +- curl -o Rack-SDK.zip https://vcvrack.com/downloads/Rack-SDK-1.1.6.zip - unzip Rack-SDK.zip - popd script: diff --git a/README.md b/README.md index 12b16b4..000788f 100755 --- a/README.md +++ b/README.md @@ -4,36 +4,25 @@ This is a work in progress collection for Rack version 1. ![modules screenshot](./images/admiral.png) -## Watches - Switched Multiples -![modules screenshot](./images/watches.png) +## Divisions - Clock Divider and Polyrhythm Generator -Watches is a passive multi-connector similar to a classic multiple. -What's different is that each socket has a 3-position switch to -connect the jack to one of three internal buses. +![divisions screenshot](./images/divisions.png) -All **inputs on a bus are added together and sent to the output -jacks** connected to that bus. +Divisions is four clock dividers and two auxiliary inputs that act as +trigger sources. Each trigger can be routed to one or both of the available +buses, and the slider controls the gate length. -The top section has three inputs and two outputs, the bottom section -is the opposite and has two inputs and three outputs. - -Normally Watches is separated in a top and bottom section that act -independently. When the `+` button is red the top three buses are -connected to their bottom counterparts and the module act as one. - -The **neutral switch position** connect jacks to the middle bus. -The middle bus is special and acts differently according to the `2:3` -switch: - - On `2` only two buses are active, the middle bus mutes. - - On `3` the middle bus is independent in each section. - - In between the middle bus is shared between the two sections even - when `+` is not active and they otherwise act independently. +The module has three outputs towards the bottom that will be high when their +selected buses are high. +The four clock dividers can be controlled by CV (0 - 10V) and if a jack is +connected the knobs will act as attenuators to limit the range of the clock +division. ## Shifts - Hybrid Bernoulli Mixer -![modules screenshot](./images/shifts.png) +![shifts screenshot](./images/shifts.png) Shifts is a hybrid mixer and Bernoulli gate. Each knob mixes linearly between two things as long as the knob is in the top part. When going @@ -70,7 +59,7 @@ knob is in the Bernoulli zone. This can act like **a mixer** or a ## Tables - Quad Sequencer -![modules screenshot](./images/tables.png) +![tables screenshot](./images/tables.png) Tables is four sequencers in one module. Inspired by the Metropolis and the Ryk M-185, Tables focus is more on the rhythm and randomness @@ -151,3 +140,30 @@ the bottom `mod` button. - `@ @`: **reverse** iterates over all stages from the bottom to the top then cycles starting from the bottom again. + + +## Watches - Switched Multiples + +![watches screenshot](./images/watches.png) + +Watches is a passive multi-connector similar to a classic multiple. +What's different is that each socket has a 3-position switch to +connect the jack to one of three internal buses. + +All **inputs on a bus are added together and sent to the output +jacks** connected to that bus. + +The top section has three inputs and two outputs, the bottom section +is the opposite and has two inputs and three outputs. + +Normally Watches is separated in a top and bottom section that act +independently. When the `+` button is red the top three buses are +connected to their bottom counterparts and the module act as one. + +The **neutral switch position** connect jacks to the middle bus. +The middle bus is special and acts differently according to the `2:3` +switch: + - On `2` only two buses are active, the middle bus mutes. + - On `3` the middle bus is independent in each section. + - In between the middle bus is shared between the two sections even + when `+` is not active and they otherwise act independently. diff --git a/images/admiral.png b/images/admiral.png index 43125f5..af3f2c3 100644 Binary files a/images/admiral.png and b/images/admiral.png differ diff --git a/images/divisions.png b/images/divisions.png new file mode 100644 index 0000000..4fb2118 Binary files /dev/null and b/images/divisions.png differ diff --git a/plugin.json b/plugin.json index cc436a7..0b5df76 100644 --- a/plugin.json +++ b/plugin.json @@ -1,7 +1,7 @@ { "slug": "Admiral", "name": "Admiral", - "version": "1.0.0", + "version": "1.1.0", "license": "MIT", "brand": "Admiral", "author": "Wannes Rombouts", @@ -10,7 +10,7 @@ "pluginUrl": "https://github.com/wapiflapi/admiral", "manualUrl": "https://github.com/wapiflapi/admiral", "sourceUrl": "https://github.com/wapiflapi/admiral", - "donateUrl": "", + "donateUrl": "https://paypal.me/wapiflapi", "modules": [ { "slug": "Watches", @@ -20,7 +20,8 @@ "Dual", "Multiple", "Switch", - "Utility" + "Utility", + "Polyphonic" ] }, { @@ -42,6 +43,16 @@ "Quad", "Sequencer" ] + }, + { + "slug": "Divisions", + "name": "Divisions", + "description": "Clock divider and polyrhythm generator.", + "tags": [ + "Switch", + "Clock modulator", + "Sequencer" + ] } ] } diff --git a/res/Divisions.svg b/res/Divisions.svg new file mode 100644 index 0000000..00577bb --- /dev/null +++ b/res/Divisions.svg @@ -0,0 +1,2603 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/Tables.svg b/res/Tables.svg index 80d6fb0..617187b 100644 --- a/res/Tables.svg +++ b/res/Tables.svg @@ -179,12 +179,12 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="2.8" - inkscape:cx="50.385554" - inkscape:cy="261.54956" + inkscape:cx="16.278411" + inkscape:cy="204.4067" inkscape:document-units="mm" - inkscape:current-layer="layer2" + inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="3208" + inkscape:window-width="2174" inkscape:window-height="1877" inkscape:window-x="170" inkscape:window-y="145" @@ -198,7 +198,7 @@ image/svg+xml - + @@ -285,6 +285,11 @@ id="path2735-2" d="m 0.0010725,276.28386 c 0.2967486,-0.0162 0.5946855,0.0162 0.8907104,0.0414 0.6788371,0.0652 1.3579336,0.12683 2.0363438,0.19806 1.0951493,0.11517 2.1878294,0.25782 3.2801818,0.40447 1.4071476,0.19809 2.8156355,0.3824 4.2230145,0.57814 1.596039,0.22581 3.192887,0.44407 4.791792,0.64001 1.785388,0.22084 3.57127,0.43568 5.357835,0.64292 1.87169,0.20573 3.740769,0.44196 5.611306,0.66111 1.790669,0.20382 3.579129,0.43292 5.36854,0.6512 1.602683,0.18496 3.205874,0.3635 4.809911,0.53155 1.374999,0.14213 2.750949,0.27043 4.127525,0.38949 1.222574,0.10558 2.446975,0.17627 3.670858,0.25633 1.11673,0.0747 2.23438,0.12864 3.352124,0.17745 0,0 -2.861834,-13.0743 -2.861834,-13.0743 v 0 c -1.106818,-0.032 -2.213416,-0.0739 -3.319255,-0.13832 -1.215629,-0.0679 -2.431277,-0.13584 -3.645919,-0.22557 -1.365512,-0.0966 -2.730816,-0.19808 -4.094982,-0.31864 -1.592678,-0.13945 -3.185177,-0.28318 -4.775243,-0.45977 -1.785792,-0.1957 -3.571063,-0.3979 -5.356449,-0.59859 -1.870744,-0.21746 -3.740885,-0.44199 -5.610924,-0.66762 -1.788054,-0.21647 -3.573424,-0.46041 -5.35881,-0.70518 -1.598572,-0.22312 -3.195639,-0.46085 -4.7915559,-0.70881 -1.4119173,-0.21604 -2.8253617,-0.41886 -4.2346236,-0.65818 -1.1086009,-0.18666 -2.2175394,-0.37017 -3.3274896,-0.54546 -0.6916368,-0.11168 -1.3833882,-0.22208 -2.074973,-0.3341 -0.3350335,-0.0537 -0.6703042,-0.10494 -1.0056587,-0.15559 0,0 2.9375841,13.41751 2.9375841,13.41751 z" inkscape:connector-curvature="0" /> + engine->getSampleTime())) { + lights[GATE_LIGHT].setBrightness(1.0); + } else { + lights[GATE_LIGHT].setBrightness(0.0); + } + + for (int bus = 0; bus < 2; ++bus) { + bus_high[bus] = bus_generator[bus].process(APP->engine->getSampleTime()); + bus_light_high[bus] = bus_light_generator[bus].process(APP->engine->getSampleTime()); + bus_triggered[bus] = false; + } + + float gate_mod = ( + inputs[GATE_INPUT].isConnected() + ? inputs[GATE_INPUT].getVoltage() / 10.0f + : 1.0); + + float gatelength = std::max( + 1e-3f, // Minimum trigger length. + params[GATE_PARAM].getValue() * gate_mod); + + float lightgatelength = std::max( + 1.f / 25, // Minimum visible light length. + gatelength); + + for (int i = 0; i < 6; ++i) { + + bool trigger = false; + + if (i < 2) { + trigger = aux_trigger[i].process(inputs[AUX_INPUT + i].getVoltage()); + } else { + + float div_mod = std::abs( + inputs[DIV_INPUT + (i - 2)].isConnected() + ? inputs[DIV_INPUT + (i - 2)].getVoltage() / 10.0f + : 1.0); + + unsigned int limit = params[DIV_PARAM + (i - 2)].getValue(); + unsigned int selected = limit * div_mod; + unsigned int current = conf.counter % (selected + 1); + + for (unsigned int li = 0; li < 16; ++li) { + lights[DIV_LIGHT + (i - 2) * 16 + li].setBrightness( + li == current ? 1.0 : + li == limit ? 0.8 : + li >= selected && li < limit ? 0.3 : + 0.0); + } + + if (clk && current == selected) { + trigger = true; + } + + } + + if (trigger) { + light_generator[i].reset(); + light_generator[i].trigger(lightgatelength); + } + + bool light_high = light_generator[i].process(APP->engine->getSampleTime()); + + for (int bus = 0; bus < 2; ++bus) { + + if (bus_triggers[i * 2 + bus].process( + params[BUS_PARAM + i * 2 + bus].getValue())) { + conf.bus_select[i * 2 + bus] ^= true; + } + + lights[BUS_LIGHT + i * 2 + bus].setBrightness( + conf.bus_select[i * 2 + bus] + ? (light_high ? 1.0 : 0.3) + : 0.0); + + if (trigger && conf.bus_select[i * 2 + bus]) { + bus_triggered[bus] = true; + bus_generator[bus].reset(); + bus_generator[bus].trigger(gatelength); + bus_light_generator[bus].reset(); + bus_light_generator[bus].trigger(lightgatelength); + } + + } + + } + + for (int i = 0; i < 3; ++i) { + bool high = false; + + for (int bus = 0; bus < 2; ++bus) { + + if (bus_triggers[(6 + i) * 2 + bus].process( + params[BUS_PARAM + (6 + i) * 2 + bus].getValue())) { + conf.bus_select[(6 + i) * 2 + bus] ^= true; + } + + lights[BUS_LIGHT + (6 + i) * 2 + bus].setBrightness( + conf.bus_select[(6 + i) * 2 + bus] + ? (bus_light_high[bus] ? 1.0 : 0.3) + : 0.0); + + if (conf.bus_select[(6 + i) * 2 + bus]) { + if (bus_high[bus] && bus_triggered[bus]) { + // This means it has triggered but it was + // already high. Since we actually use this + // it's important! + warning_generator.reset(); + warning_generator.trigger(lightgatelength); + } + + high |= bus_high[bus]; + } + + } + + outputs[MIX_OUTPUT + i].setVoltage(high ? 10.0 : 0.0); + } + + + } + +}; + +struct DivisionsWidget : ModuleWidget { + DivisionsWidget(Divisions* module) { + setModule(module); + setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Divisions.svg"))); + ADD_SCREWS(); + + int height = 33; + int hinc = 37; + int winc = 35; + + for (int i = 0; i < 9; ++i) { + + int width = 22; + + if (i == 0) { + addInput(createInputCentered( + Vec(width, height), module, + Divisions::CLK_INPUT)); + } else if (i == 1) { + addInput(createInputCentered( + Vec(width, height), module, + Divisions::RST_INPUT)); + } else if (i < 6) { + addInput(createInputCentered( + Vec(width, height), module, + Divisions::DIV_INPUT + i - 2)); + } else if (i == 6) { + addInput(createInputCentered( + Vec(width, height), module, + Divisions::GATE_INPUT)); + } else if (i == 7) { + addParam(createLightParamCentered>( + Vec(width, height + hinc / 2.f), module, + Divisions::GATE_PARAM, + Divisions::GATE_LIGHT)); + } + + width += winc; + + if (i < 2) { + addInput(createInputCentered( + Vec(width, height), module, + Divisions::AUX_INPUT + i)); + } else if (i < 6) { + + for (int ii = 0; ii < 16; ++ii) { + float r = 15.0; + + float a = 0.72; + float b = 0.14; + + float h = r * std::cos(2 * M_PI * (a * (float) ii / 15.0 + b)); + float w = r * std::sin(2 * M_PI * (a * (float) ii / 15.0 + b)); + + addChild(createLightCentered>( + Vec(width - w, height + h), module, + Divisions::DIV_LIGHT + (i - 2) * 16 + ii)); + + } + + addParam(createParamCentered( + Vec(width, height), module, + Divisions::DIV_PARAM + i - 2)); + + } else { + addOutput(createOutputCentered( + Vec(width, height), module, + Divisions::MIX_OUTPUT + i - 6)); + } + + width += winc; + + addParam(createParamCentered( + Vec(width, height), module, + Divisions::BUS_PARAM + 2 * i)); + addChild(createLightCentered>( + Vec(width, height), module, + Divisions::BUS_LIGHT + 2 * i)); + + width += winc; + + addParam(createParamCentered( + Vec(width, height), module, + Divisions::BUS_PARAM + 2 * i + 1)); + addChild(createLightCentered>( + Vec(width, height), module, + Divisions::BUS_LIGHT + 2 * i + 1)); + + height += hinc; + + } + + } +}; + + +Model *modelDivisions = createModel("Divisions"); diff --git a/src/Tables.cpp b/src/Tables.cpp index f176080..25d6247 100644 --- a/src/Tables.cpp +++ b/src/Tables.cpp @@ -85,13 +85,46 @@ struct Tables : Module { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); for (int i = 0; i < 8; ++i) { - configParam(MOD_PARAM + i, 0.f, 1.f, 0.f, string::f("Mode %d", i+1)); - configParam(PAT_PARAM + i, 0.f, 1.f, 0.f, string::f("Pattern %d", i+1)); + configParam(MOD_PARAM + i, 0.f, 1.f, 0.f, + string::f( + "◯◯ wait / pause\n" + "⬤◯ first / one\n" + "◯⬤ all / each\n" + "⬤⬤ join / continuous\n" + "Change Mode - step %d", i + 1)); + configParam(PAT_PARAM + i, 0.f, 1.f, 0.f, + string::f("Change Pattern - step %d", i + 1)); } - configParam(SELECT_PARAM, 0.f, 1.f, 1.f, "Select"); - configParam(SELECT_PARAM, 0.f, 1.f, 1.f, "Sequence Mode"); + configParam(SELECT_PARAM, 0.f, 1.f, 1.f, + "Select Sequencer"); + configParam(ORDER_PARAM, 0.f, 1.f, 1.f, + "◯◯ forward\n" + "⬤◯ random\n" + "◯⬤ brownian\n" + "⬤⬤ reverse\n" + "Change Mode" + ); + + onReset(); + } + void onReset() override { + memset(&conf, 0, sizeof conf); + for (int c = 0; c < 4; c++) { + conf.channels[c].reset = true; + } + } + + void onRandomize() override { + onReset(); + for (int c = 0; c < 4; c++) { + conf.channels[c].order = (int) (4.f * random::uniform()); + for (int i = 0; i < 8; i++) { + conf.channels[c].steps[i].mode = (int) (4.f * random::uniform()); + conf.channels[c].steps[i].pattern = (((int) (16.f * std::pow(random::uniform(), 3))) + 1) % 16; + } + } } json_t *dataToJson() override { @@ -203,7 +236,6 @@ struct Tables : Module { } } - bool channelClock(int c) { if (conf.channels[c].reset) { @@ -426,11 +458,7 @@ struct TablesWidget : ModuleWidget { TablesWidget(Tables *module) { setModule(module); setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Tables.svg"))); - - addChild(createWidget(Vec(0, 0))); - addChild(createWidget(Vec(box.size.x - 1 * RACK_GRID_WIDTH, 0))); - addChild(createWidget(Vec(0, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); - addChild(createWidget(Vec(box.size.x - 1 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + ADD_SCREWS(); int height = 35; int hinc = 27; @@ -440,13 +468,21 @@ struct TablesWidget : ModuleWidget { int width = 19; - addParam(createParamCentered(Vec(width, height), module, Tables::MOD_PARAM + i)); - addChild(createLightCentered>(Vec(width, height), module, Tables::MOD_LIGHT + i)); + addParam(createParamCentered( + Vec(width, height), module, + Tables::MOD_PARAM + i)); + addChild(createLightCentered>( + Vec(width, height), module, + Tables::MOD_LIGHT + i)); width += winc; - addParam(createParamCentered(Vec(54, height), module, Tables::PAT_PARAM + i)); - addChild(createLightCentered>(Vec(width, height), module, Tables::PAT_LIGHT + i)); + addParam(createParamCentered( + Vec(54, height), module, + Tables::PAT_PARAM + i)); + addChild(createLightCentered>( + Vec(width, height), module, + Tables::PAT_LIGHT + i)); width += winc; @@ -454,7 +490,9 @@ struct TablesWidget : ModuleWidget { width -= ledwidth / 2; for (int j = 0; j < 4; ++j) { - addChild(createLightCentered>(Vec(width, height), module, Tables::STEP_LIGHT + i * 4 + j)); + addChild(createLightCentered>( + Vec(width, height), module, + Tables::STEP_LIGHT + i * 4 + j)); width += ledwidth; } @@ -464,21 +502,33 @@ struct TablesWidget : ModuleWidget { int width = 19; - addParam(createParamCentered(Vec(width, height), module, Tables::ORDER_PARAM)); - addChild(createLightCentered>(Vec(width, height), module, Tables::ORDER_LIGHT)); + addParam(createParamCentered( + Vec(width, height), module, + Tables::ORDER_PARAM)); + addChild(createLightCentered>( + Vec(width, height), module, + Tables::ORDER_LIGHT)); width += winc; - addParam(createParamCentered(Vec(width, height), module, Tables::SELECT_PARAM)); - addChild(createLightCentered>(Vec(width, height), module, Tables::SELECT_LIGHT)); + addParam(createParamCentered( + Vec(width, height), module, + Tables::SELECT_PARAM)); + addChild(createLightCentered>( + Vec(width, height), module, + Tables::SELECT_LIGHT)); width += winc; - addInput(createInputCentered(Vec(width, height), module, Tables::RESET_INPUT)); + addInput(createInputCentered( + Vec(width, height), module, + Tables::RESET_INPUT)); width += winc; - addInput(createInputCentered(Vec(width, height), module, Tables::CLOCK_INPUT)); + addInput(createInputCentered( + Vec(width, height), module, + Tables::CLOCK_INPUT)); height += hinc; @@ -487,7 +537,9 @@ struct TablesWidget : ModuleWidget { width = 35; for (int j = 0; j < 4; ++j) { - addChild(createLightCentered>(Vec(width, height), module, Tables::CHANNEL_LIGHT + j)); + addChild(createLightCentered>( + Vec(width, height), module, + Tables::CHANNEL_LIGHT + j)); width += winc; } @@ -495,7 +547,9 @@ struct TablesWidget : ModuleWidget { width = 19; for (int j = 0; j < 4; ++j) { - addOutput(createOutputCentered(Vec(width, height), module, Tables::GATE_OUTPUT + j)); + addOutput(createOutputCentered( + Vec(width, height), module, + Tables::GATE_OUTPUT + j)); width += winc; } @@ -503,13 +557,12 @@ struct TablesWidget : ModuleWidget { width = 19; for (int j = 0; j < 4; ++j) { - addOutput(createOutputCentered(Vec(width, height), module, Tables::TRIG_OUTPUT + j)); + addOutput(createOutputCentered( + Vec(width, height), module, + Tables::TRIG_OUTPUT + j)); width += winc; } - - - } }; diff --git a/src/Watches.cpp b/src/Watches.cpp index 53c2771..1f10a40 100644 --- a/src/Watches.cpp +++ b/src/Watches.cpp @@ -1,5 +1,7 @@ #include "plugin.hpp" +using namespace simd; + struct Watches : Module { enum ParamIds { @@ -64,47 +66,81 @@ struct Watches : Module { float buscnt = params[BUSCNT_PARAM].getValue(); for (int bus = 0; bus < 3; ++bus) { - float voltage = float(bus); - float w1 = 0.f; - - if (params[W11_PARAM].getValue() == voltage) - w1 += inputs[W11_INPUT].getVoltage(); - if (params[W12_PARAM].getValue() == voltage) - w1 += inputs[W12_INPUT].getVoltage(); - if (params[W13_PARAM].getValue() == voltage) - w1 += inputs[W13_INPUT].getVoltage(); - - float w2 = 0.f; - - if (params[W21_PARAM].getValue() == voltage) - w2 += inputs[W21_INPUT].getVoltage(); - if (params[W22_PARAM].getValue() == voltage) - w2 += inputs[W22_INPUT].getVoltage(); - - if (bus == 1 && buscnt == 2.0) { - // Middle bus needs to mute. - w1 = w2 = 0.0; + float bus_select_voltage = float(bus); + + int top_channel_count = 0, bottom_channel_count = 0; + + for (int channel = 0; channel < 16; channel++) { + float top, bottom; + +# define check_channel(DEST, COUNT, PARAM, INPUT) { \ + if (inputs[INPUT].getChannels() > channel \ + && params[PARAM].getValue() == bus_select_voltage) { \ + COUNT = channel + 1; \ + DEST += inputs[INPUT].getVoltage(channel); \ + } \ + } + + top = 0.f; + check_channel(top, top_channel_count, W11_PARAM, W11_INPUT); + check_channel(top, top_channel_count, W12_PARAM, W12_INPUT); + check_channel(top, top_channel_count, W13_PARAM, W13_INPUT); + + bottom = 0.f; + check_channel(bottom, bottom_channel_count, W21_PARAM, W21_INPUT); + check_channel(bottom, bottom_channel_count, W22_PARAM, W22_INPUT); + + if (top_channel_count <= channel && bottom_channel_count <= channel) { + break; // No point in continuing. + } + + if (bus == 1 && buscnt == 2.0) { + // Middle bus needs to mute because we only want two buses. + top = bottom = 0.0; + } + + if (join || (bus == 1 && buscnt == 1.0)) { + // We are joining, or middle bus is crossing sections. + top = bottom = top + bottom; + } + + if (params[W14_PARAM].getValue() == bus_select_voltage) + outputs[W14_OUTPUT].setVoltage(top, channel); + if (params[W15_PARAM].getValue() == bus_select_voltage) + outputs[W15_OUTPUT].setVoltage(top, channel); + + if (params[W23_PARAM].getValue() == bus_select_voltage) + outputs[W23_OUTPUT].setVoltage(bottom, channel); + if (params[W24_PARAM].getValue() == bus_select_voltage) + outputs[W24_OUTPUT].setVoltage(bottom, channel); + if (params[W25_PARAM].getValue() == bus_select_voltage) + outputs[W25_OUTPUT].setVoltage(bottom, channel); + } if (join || (bus == 1 && buscnt == 1.0)) { // We are joining, or middle bus is crossing sections. - w1 = w2 = w1 + w2; + // Both halves will have max number of channels. + if (top_channel_count > bottom_channel_count) + bottom_channel_count = top_channel_count; + else + top_channel_count = bottom_channel_count; } - if (params[W14_PARAM].getValue() == voltage) - outputs[W14_OUTPUT].setVoltage(w1); - if (params[W15_PARAM].getValue() == voltage) - outputs[W15_OUTPUT].setVoltage(w1); + if (params[W14_PARAM].getValue() == bus_select_voltage) + outputs[W14_OUTPUT].setChannels(top_channel_count); + if (params[W15_PARAM].getValue() == bus_select_voltage) + outputs[W15_OUTPUT].setChannels(top_channel_count); - if (params[W23_PARAM].getValue() == voltage) - outputs[W23_OUTPUT].setVoltage(w2); - if (params[W24_PARAM].getValue() == voltage) - outputs[W24_OUTPUT].setVoltage(w2); - if (params[W25_PARAM].getValue() == voltage) - outputs[W25_OUTPUT].setVoltage(w2); + if (params[W23_PARAM].getValue() == bus_select_voltage) + outputs[W23_OUTPUT].setChannels(bottom_channel_count); + if (params[W24_PARAM].getValue() == bus_select_voltage) + outputs[W24_OUTPUT].setChannels(bottom_channel_count); + if (params[W25_PARAM].getValue() == bus_select_voltage) + outputs[W25_OUTPUT].setChannels(bottom_channel_count); - } + } if (joinTrigger.process(params[JOIN_PARAM].getValue() > 0.f)) { join ^= true; diff --git a/src/plugin.cpp b/src/plugin.cpp index dae885e..4cd3367 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -11,6 +11,7 @@ void init(Plugin *p) { p->addModel(modelWatches); p->addModel(modelShifts); p->addModel(modelTables); + p->addModel(modelDivisions); // 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 35ed70a..3eaf5f4 100644 --- a/src/plugin.hpp +++ b/src/plugin.hpp @@ -10,6 +10,16 @@ extern Plugin *pluginInstance; extern Model *modelWatches; extern Model *modelShifts; extern Model *modelTables; +extern Model *modelDivisions; + + +#define ADD_SCREW(X, Y) addChild(createWidget(Vec((X), (Y)))) +#define ADD_SCREWS() do { \ + ADD_SCREW(0, 0); \ + ADD_SCREW(box.size.x - 1 * RACK_GRID_WIDTH, 0); \ + ADD_SCREW(0, RACK_GRID_HEIGHT - RACK_GRID_WIDTH); \ + ADD_SCREW(box.size.x - 1 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH); \ + } while (0); template