computerscare-vcv-modules

computerscare modules for VCV Rack
Log | Files | Refs

commit 4cb36f6f57b1794a02b1d4b26f9f7cc7cc5d3abb
parent aa6c94da6cfc37dccdd76b7168469a7a3eda3cd2
Author: Adam <[email protected]>
Date:   Mon,  3 Jun 2024 11:43:19 -0500

Merge pull request #87 from freddyz/82-suggestion-toly-pools-rotate-through-output-channels-instead-of-all-16

82 suggestion toly pools rotate through output channels instead of all 16
Diffstat:
MCHANGELOG.MD | 6++++++
Mdoc/poly-utilities.md | 5+++++
Mplugin.json | 13++++++++++++-
Msrc/Computerscare.cpp | 2++
Msrc/Computerscare.hpp | 2++
Asrc/ComputerscareTolyPools-v2.cpp | 265+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 292 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.MD b/CHANGELOG.MD @@ -1,3 +1,9 @@ +# 2.1.1 +Toly Pools +- Make a new module with new slug, Toly Pools v2 with better rotation options and behavior +- New Toly Pools v2 has Auto output channels mode by default, and positive/negative rotation +- Deprecate the old Toly Pools. It will still work in old patches, but won't show up in browser + # 2.1.0 Father & Son Patch Sequencer - changed font to one with license suitable for cardinal diff --git a/doc/poly-utilities.md b/doc/poly-utilities.md @@ -27,6 +27,11 @@ Sequentially output the individual channel voltages of a polyphonic signal. Con * CV output of the input channel count (1 - 16 channels is linearly mapped to 0 - 10 volts) * Knob and CV for setting output channel count (0 - 10 volts linearly mapped to 1-16 output channels) * Knob and CV for rotating the polyphonic signal (0 - 10 volts sets rotation of 0-15 channels. For example: rotation of "1" will move input channel 2 -> output channel 1, input channel 3->output channel 2, ... input channel 16 -> output channel 1) +* The default output polyphony setting is "A": Automatic which will set the output polyphony equal to the input polyphony +* Different rotation modes via context menu: +- "Repeat Input Channels": If the output polyphony is set higher than input polyphony (channel count), the input channels will be repeated to fill the output +- "Rotate Through Maximum of Output, Input Channels": If the output polyphony is higher than the input polyphony, all input channels will be used and the remainder will be filled with 0v +- "Rotate Through 16 Channels (Legacy)": The input signal will be padded to 16 channels polyphony with 0v always. This is the "old" Toly Pools behavior, and results in a lot of 0v signals. ### Poly Channels Knob diff --git a/plugin.json b/plugin.json @@ -1,6 +1,6 @@ { "slug": "computerscare", - "version": "2.1.0", + "version": "2.1.1", "name": "computerscare", "brand": "computerscare", "author": "computerscare", @@ -86,10 +86,21 @@ "slug": "computerscare-toly-pools", "name": "Toly Pools", "description": + "Polyphonic toolset including rotator, number-of-channels selector. Deprecated: Replaced by Toly Pools v2.", + "tags": ["Polyphonic", "Utility", "Attenuator"], + "manualUrl": + "https://github.com/freddyz/computerscare-vcv-modules/blob/master/doc/poly-utilities.md#toly-pools", + "hidden": true + }, + { + "slug": "computerscare-toly-pools-v2", + "name": "Toly Pools v2", + "description": "Polyphonic toolset including rotator, number-of-channels selector", "tags": ["Polyphonic", "Utility", "Attenuator"], "manualUrl": "https://github.com/freddyz/computerscare-vcv-modules/blob/master/doc/poly-utilities.md#toly-pools" + }, { diff --git a/src/Computerscare.cpp b/src/Computerscare.cpp @@ -25,4 +25,6 @@ void init(Plugin *p) { p->addModel(modelComputerscareGolyPenerator); p->addModel(modelComputerscareMolyPatrix); p->addModel(modelComputerscareHorseADoodleDoo); + + p->addModel(modelComputerscareTolyPoolsV2); } diff --git a/src/Computerscare.hpp b/src/Computerscare.hpp @@ -35,6 +35,8 @@ extern Model *modelComputerscareMolyPatrix; extern Model *modelComputerscareHorseADoodleDoo; extern Model *modelComputerscareDrolyPaw; +extern Model *modelComputerscareTolyPoolsV2; + static const NVGcolor COLOR_COMPUTERSCARE_LIGHT_GREEN = nvgRGB(0xC0, 0xE7, 0xDE); static const NVGcolor COLOR_COMPUTERSCARE_GREEN = nvgRGB(0x24, 0xc9, 0xa6); static const NVGcolor COLOR_COMPUTERSCARE_RED = nvgRGB(0xC4, 0x34, 0x21); diff --git a/src/ComputerscareTolyPools-v2.cpp b/src/ComputerscareTolyPools-v2.cpp @@ -0,0 +1,265 @@ +#include "Computerscare.hpp" +#include "dtpulse.hpp" + +struct ComputerscareTolyPoolsV2; + +/* +Input: + +first rotate +knob, CV + +numChannels select (auto) +knob,cv + + +input: +0123456789abcdef + +want: +3456 + +rotate 4,clip 4 + +*/ + + + + +struct ComputerscareTolyPoolsV2 : Module { + int counter = 83910; + int numOutputChannelsControlValue = 0; + int numOutputChannels = 1; + int rotation = 0; + int numInputChannels = 1; + + int rotationModeEnum=0; + + ComputerscareSVGPanel* panelRef; + enum ParamIds { + ROTATE_KNOB, + NUM_CHANNELS_KNOB, + AUTO_CHANNELS_SWITCH, + NUM_PARAMS + + }; + enum InputIds { + POLY_INPUT, + ROTATE_CV, + NUM_CHANNELS_CV, + NUM_INPUTS + }; + enum OutputIds { + POLY_OUTPUT, + NUM_CHANNELS_OUTPUT, + NUM_OUTPUTS + }; + enum LightIds { + NUM_LIGHTS + }; + + + ComputerscareTolyPoolsV2() { + + config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); + + configParam(ROTATE_KNOB, -16.f, 16.f, 0.f, "Rotate", " channels"); + + configParam<AutoParamQuantity>(NUM_CHANNELS_KNOB, 0.f, 16.f, 0.f, "Number of Output Channels"); + + configInput(POLY_INPUT, "Main"); + configInput(ROTATE_CV, "Rotation CV"); + configInput(NUM_CHANNELS_CV, "Number of Channels CV"); + + configOutput(POLY_OUTPUT, "Main"); + configOutput(NUM_CHANNELS_OUTPUT, "Number of Input Channels"); + } + void process(const ProcessArgs &args) override { + counter++; + if (counter > 982) { + counter = 0; + numOutputChannelsControlValue = params[NUM_CHANNELS_KNOB].getValue(); + rotation = params[ROTATE_KNOB].getValue(); + numInputChannels = inputs[POLY_INPUT].getChannels(); + } + if (inputs[NUM_CHANNELS_CV].isConnected()) { + numOutputChannelsControlValue = mapVoltageToChannelCount(inputs[NUM_CHANNELS_CV].getVoltage(0)); + } + if (inputs[ROTATE_CV].isConnected()) { + rotation = mapVoltageToChannelCount(inputs[ROTATE_CV].getVoltage(0)); + } + + if(numOutputChannelsControlValue == 0) { + numOutputChannels = numInputChannels; + } else { + numOutputChannels = numOutputChannelsControlValue; + } + outputs[POLY_OUTPUT].setChannels(numOutputChannels); + outputs[NUM_CHANNELS_OUTPUT].setVoltage(mapChannelCountToVoltage(numInputChannels)); + + int rotationBase = 16; + + if(rotationModeEnum == 0) { + rotationBase = numInputChannels; + } else if(rotationModeEnum == 1) { + rotationBase = std::max(numOutputChannels,numInputChannels); + } else if(rotationModeEnum == 2) { + rotationBase = 16; + } + + + + for (int i = 0; i < numOutputChannels; i++) { + outputs[POLY_OUTPUT].setVoltage(inputs[POLY_INPUT].getVoltage((i + rotation + rotationBase*16) % rotationBase), i); + } + } + + json_t *dataToJson() override { + json_t *rootJ = json_object(); + json_object_set_new(rootJ, "rotationModeEnum", json_integer(rotationModeEnum)); + return rootJ; + } + + void dataFromJson(json_t *rootJ) override { + json_t *rotationModeJ = json_object_get(rootJ, "rotationModeEnum"); + + if (rotationModeJ) { + rotationModeEnum = json_integer_value(rotationModeJ); + } + } + +}; +struct PoolsSmallDisplayV2 : SmallLetterDisplay +{ + ComputerscareTolyPoolsV2 *module; + int ch; + int type = 0; + PoolsSmallDisplayV2(int someType) + { + type = someType; + SmallLetterDisplay(); + }; + void draw(const DrawArgs &args) + { + //this->setNumDivisionsString(); + if (module) + { + + if (type == 0) { + if(module->numOutputChannelsControlValue == 0) { + value = "A"; //Automatic - output channels match input channels + } else { + value = std::to_string(module->numOutputChannelsControlValue); + } + + } + else if (type == 1) { + value = std::to_string(module->rotation); + } + else if (type == 2) { + value = std::to_string(module->numInputChannels); + } + + } + else { + value = std::to_string((random::u32() % 16) + 1); + } + SmallLetterDisplay::draw(args); + } + +}; + +struct ComputerscareTolyPoolsWidgetV2 : ModuleWidget { + ComputerscareTolyPoolsWidgetV2(ComputerscareTolyPoolsV2 *module) { + + setModule(module); + box.size = Vec(4 * 15, 380); + { + ComputerscareSVGPanel *panel = new ComputerscareSVGPanel(); + panel->box.size = box.size; + panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ComputerscareTolyPoolsPanel.svg"))); + + addChild(panel); + + } + + + addInput(createInput<InPort>(Vec(1 , 50), module, ComputerscareTolyPoolsV2::POLY_INPUT)); + poolsSmallDisplay = new PoolsSmallDisplayV2(2); + poolsSmallDisplay->box.size = Vec(14, 20); + poolsSmallDisplay->box.pos = Vec(-3 , 80); + poolsSmallDisplay->fontSize = 22; + poolsSmallDisplay->textAlign = 18; + poolsSmallDisplay->breakRowWidth = 20; + poolsSmallDisplay->module = module; + addChild(poolsSmallDisplay); + + + addLabeledKnob("Num Output Channels", 10, 156, module, ComputerscareTolyPoolsV2::NUM_CHANNELS_KNOB, -14, -24, 0); + addInput(createInput<InPort>(Vec(10, 186), module, ComputerscareTolyPoolsV2::NUM_CHANNELS_CV)); + + addLabeledKnob("Rotation", 10, 256, module, ComputerscareTolyPoolsV2::ROTATE_KNOB, -13, -5, 1); + addInput(createInput<InPort>(Vec(10, 286), module, ComputerscareTolyPoolsV2::ROTATE_CV)); + + + addOutput(createOutput<OutPort>(Vec(28, 30), module, ComputerscareTolyPoolsV2::POLY_OUTPUT)); + + addOutput(createOutput<PointingUpPentagonPort>(Vec(31, 76), module, ComputerscareTolyPoolsV2::NUM_CHANNELS_OUTPUT)); + } + void addLabeledKnob(std::string label, int x, int y, ComputerscareTolyPoolsV2 *module, int index, float labelDx, float labelDy, int type) { + + poolsSmallDisplay = new PoolsSmallDisplayV2(type); + poolsSmallDisplay->box.size = Vec(30, 20); + poolsSmallDisplay->box.pos = Vec(x - 7.5 , y + 1.f); + poolsSmallDisplay->fontSize = 22; + poolsSmallDisplay->textAlign = 18; + poolsSmallDisplay->textColor = COLOR_COMPUTERSCARE_LIGHT_GREEN; + poolsSmallDisplay->breakRowWidth = 30; + poolsSmallDisplay->module = module; + + + outputChannelLabel = new SmallLetterDisplay(); + outputChannelLabel->box.size = Vec(5, 5); + outputChannelLabel->box.pos = Vec(x + labelDx, y - 12 + labelDy); + outputChannelLabel->fontSize = 15; + outputChannelLabel->textAlign = 1; + outputChannelLabel->breakRowWidth = 55; + + outputChannelLabel->value = label; + + addParam(createParam<MediumDotSnapKnob>(Vec(x, y), module, index)); + addChild(poolsSmallDisplay); + + } + + void appendContextMenu(Menu *menu) override; + + + PoolsSmallDisplayV2* poolsSmallDisplay; + SmallLetterDisplay* outputChannelLabel; +}; +struct PoolsModeItem : MenuItem { + ComputerscareTolyPoolsV2 *pools; + int modeEnum; + void onAction(const event::Action &e) override { + pools->rotationModeEnum = modeEnum; + } + void step() override { + rightText = CHECKMARK(pools->rotationModeEnum == modeEnum); + MenuItem::step(); + } +}; + + void ComputerscareTolyPoolsWidgetV2::appendContextMenu(Menu* menu) { + ComputerscareTolyPoolsV2* pools = dynamic_cast<ComputerscareTolyPoolsV2*>(this->module); + menu->addChild(construct<MenuLabel>(&MenuLabel::text, "")); + + menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Rotation Mode")); + menu->addChild(construct<PoolsModeItem>(&MenuItem::text, "Repeat Input Channels", &PoolsModeItem::pools, pools, &PoolsModeItem::modeEnum, 0)); + menu->addChild(construct<PoolsModeItem>(&MenuItem::text, "Rotate Through Maximum of Output, Input Channels", &PoolsModeItem::pools, pools, &PoolsModeItem::modeEnum, 1)); + menu->addChild(construct<PoolsModeItem>(&MenuItem::text, "Rotate Through 16 Channels (Legacy)", &PoolsModeItem::pools, pools, &PoolsModeItem::modeEnum, 2)); + + } + + +Model *modelComputerscareTolyPoolsV2 = createModel<ComputerscareTolyPoolsV2, ComputerscareTolyPoolsWidgetV2>("computerscare-toly-pools-v2");