diff --git a/res/anuli_common.svg b/res/anuli_common.svg index ee85678b..51333b90 100644 --- a/res/anuli_common.svg +++ b/res/anuli_common.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/res/anuli_faceplate.svg b/res/anuli_faceplate.svg index 7660ea62..f26c51e4 100644 --- a/res/anuli_faceplate.svg +++ b/res/anuli_faceplate.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/res/anuli_faceplate_plumbago.svg b/res/anuli_faceplate_plumbago.svg index 1ed1bb0a..578e53e0 100644 --- a/res/anuli_faceplate_plumbago.svg +++ b/res/anuli_faceplate_plumbago.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/res/components/BananutBlackPoly.svg b/res/components/BananutBlackPoly.svg new file mode 100644 index 00000000..4c77479e --- /dev/null +++ b/res/components/BananutBlackPoly.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/anuli.cpp b/src/anuli.cpp index bda4b5a0..f680a749 100644 --- a/src/anuli.cpp +++ b/src/anuli.cpp @@ -66,6 +66,7 @@ struct Anuli : SanguineModule { INPUT_STRUM, INPUT_PITCH, INPUT_IN, + INPUT_MODE, INPUTS_COUNT }; @@ -77,7 +78,7 @@ struct Anuli : SanguineModule { enum LightIds { ENUMS(LIGHT_POLYPHONY, 2), - ENUMS(LIGHT_RESONATOR, 3), + ENUMS(LIGHT_RESONATOR, 16 * 3), ENUMS(LIGHT_FX, 2), LIGHTS_COUNT }; @@ -101,9 +102,9 @@ struct Anuli : SanguineModule { int strummingFlagCounter = 0; int strummingFlagInterval = 0; - rings::ResonatorModel resonatorModel = rings::RESONATOR_MODEL_MODAL; + rings::ResonatorModel resonatorModel[PORT_MAX_CHANNELS]; rings::ResonatorModel fxModel = rings::RESONATOR_MODEL_MODAL; - bool bEasterEgg = false; + bool bEasterEgg[PORT_MAX_CHANNELS] = {}; std::string displayText = ""; @@ -139,6 +140,8 @@ struct Anuli : SanguineModule { configInput(INPUT_PITCH, "Pitch (1V/oct)"); configInput(INPUT_IN, "Audio"); + configInput(INPUT_MODE, "Mode"); + configOutput(OUTPUT_ODD, "Odd"); configOutput(OUTPUT_EVEN, "Even"); @@ -152,6 +155,8 @@ struct Anuli : SanguineModule { part[i].Init(reverbBuffer[i]); memset(&stringSynth[i], 0, sizeof(rings::StringSynthPart)); stringSynth[i].Init(reverbBuffer[i]); + + resonatorModel[i] = rings::RESONATOR_MODEL_MODAL; } clockDivider.setDivision(kDividerFrequency); @@ -160,22 +165,9 @@ struct Anuli : SanguineModule { void process(const ProcessArgs& args) override { const float sampleTime = kDividerFrequency * args.sampleTime; - bool dividerTurn = clockDivider.process(); + bool bDividerTurn = clockDivider.process(); - if (dividerTurn) { - if (params[PARAM_MODE].getValue() < 6) { - bEasterEgg = false; - resonatorModel = rings::ResonatorModel(params[PARAM_MODE].getValue()); - } - else { - bEasterEgg = true; - } - displayText = anuliModeLabels[params[PARAM_MODE].getValue()]; - - polyphonyMode = params[PARAM_POLYPHONY].getValue(); - - fxModel = rings::ResonatorModel(params[PARAM_FX].getValue()); - } + uint8_t disastrousCount = 0; channelCount = std::max(std::max(std::max(inputs[INPUT_STRUM].getChannels(), inputs[INPUT_PITCH].getChannels()), inputs[INPUT_IN].getChannels()), 1); @@ -183,6 +175,24 @@ struct Anuli : SanguineModule { outputs[OUTPUT_EVEN].setChannels(channelCount); for (int channel = 0; channel < channelCount; channel++) { + int modeNum = 0; + + if (!inputs[INPUT_MODE].isConnected()) { + modeNum = int(params[PARAM_MODE].getValue()); + } + else { + modeNum = clamp(int(inputs[INPUT_MODE].getVoltage(channel)), 0, 6); + } + + if (modeNum < 6) { + bEasterEgg[channel] = false; + resonatorModel[channel] = rings::ResonatorModel(modeNum); + } + else { + bEasterEgg[channel] = true; + ++disastrousCount; + } + // TODO // "Normalized to a pulse/burst generator that reacts to note changes on the V/OCT input." // Get input @@ -212,10 +222,10 @@ struct Anuli : SanguineModule { if (part[channel].polyphony() != polyphonyMode) part[channel].set_polyphony(polyphonyMode); // Model - if (bEasterEgg) + if (bEasterEgg[channel]) stringSynth[channel].set_fx(rings::FxType(fxModel)); else - part[channel].set_model(resonatorModel); + part[channel].set_model(resonatorModel[channel]); // Patch rings::Patch patch; @@ -259,7 +269,7 @@ struct Anuli : SanguineModule { // Process audio float out[24]; float aux[24]; - if (bEasterEgg) { + if (bEasterEgg[channel]) { strummer[channel].Process(NULL, 24, &performanceState); stringSynth[channel].Process(performanceState, patch, in, out, aux, 24); } @@ -300,67 +310,87 @@ struct Anuli : SanguineModule { } } - if (dividerTurn) { + if (bDividerTurn) { + displayText = anuliModeLabels[resonatorModel[0]]; + uint8_t pulseWidthModulationCounter = getSystemTimeMs() & 15; uint8_t triangle = (getSystemTimeMs() >> 5) & 31; triangle = triangle < 16 ? triangle : 31 - triangle; - if (!bEasterEgg) { - if (resonatorModel < 3) { - lights[LIGHT_RESONATOR + 0].setBrightnessSmooth(resonatorModel >= 1 ? 1.f : 0.f, sampleTime); - lights[LIGHT_RESONATOR + 1].setBrightnessSmooth(resonatorModel <= 1 ? 1.f : 0.f, sampleTime); - lights[LIGHT_RESONATOR + 2].setBrightnessSmooth(0.f, sampleTime); + polyphonyMode = params[PARAM_POLYPHONY].getValue(); + + fxModel = rings::ResonatorModel(params[PARAM_FX].getValue()); + + for (int channel = 0; channel < PORT_MAX_CHANNELS; channel++) { + int currentLight = LIGHT_RESONATOR + channel * 3; + if (channel < channelCount) { + if (!bEasterEgg[channel]) { + if (resonatorModel[channel] < 3) { + lights[currentLight + 0].setBrightnessSmooth(resonatorModel[channel] >= 1 ? 1.f : 0.f, sampleTime); + lights[currentLight + 1].setBrightnessSmooth(resonatorModel[channel] <= 1 ? 1.f : 0.f, sampleTime); + lights[currentLight + 2].setBrightnessSmooth(0.f, sampleTime); + } + else { + lights[currentLight + 0].setBrightnessSmooth((resonatorModel[channel] >= 4 && pulseWidthModulationCounter < triangle) ? 1.f : 0.f, sampleTime); + lights[currentLight + 1].setBrightnessSmooth((resonatorModel[channel] <= 4 && pulseWidthModulationCounter < triangle) ? 1.f : 0.f, sampleTime); + lights[currentLight + 2].setBrightnessSmooth(0.f, sampleTime); + } + } + else { + lights[currentLight + 0].setBrightnessSmooth(0.f, sampleTime); + lights[currentLight + 1].setBrightnessSmooth(0.f, sampleTime); + lights[currentLight + 2].setBrightnessSmooth(pulseWidthModulationCounter < triangle ? 1.f : 0.f, sampleTime); + } } else { - lights[LIGHT_RESONATOR + 0].setBrightnessSmooth((resonatorModel >= 4 && pulseWidthModulationCounter < triangle) ? 1.f : 0.f, sampleTime); - lights[LIGHT_RESONATOR + 1].setBrightnessSmooth((resonatorModel <= 4 && pulseWidthModulationCounter < triangle) ? 1.f : 0.f, sampleTime); - lights[LIGHT_RESONATOR + 2].setBrightnessSmooth(0.f, sampleTime); + lights[currentLight + 0].setBrightnessSmooth(0.f, sampleTime); + lights[currentLight + 1].setBrightnessSmooth(0.f, sampleTime); + lights[currentLight + 2].setBrightnessSmooth(0.f, sampleTime); } - lights[LIGHT_FX + 0].setBrightnessSmooth(0.f, sampleTime); - lights[LIGHT_FX + 1].setBrightnessSmooth(0.f, sampleTime); - } - else { - lights[LIGHT_RESONATOR + 0].setBrightnessSmooth(0.f, sampleTime); - lights[LIGHT_RESONATOR + 1].setBrightnessSmooth(0.f, sampleTime); - lights[LIGHT_RESONATOR + 2].setBrightnessSmooth(pulseWidthModulationCounter < triangle ? 1.f : 0.f, sampleTime); - if (fxModel < 3) { - lights[LIGHT_FX + 0].setBrightnessSmooth(fxModel <= 1 ? 1.f : 0.f, sampleTime); - lights[LIGHT_FX + 1].setBrightnessSmooth(fxModel >= 1 ? 1.f : 0.f, sampleTime); + if (disastrousCount < 1) { + lights[LIGHT_FX + 0].setBrightnessSmooth(0.f, sampleTime); + lights[LIGHT_FX + 1].setBrightnessSmooth(0.f, sampleTime); } else { - lights[LIGHT_FX + 0].setBrightnessSmooth((fxModel <= 4 && pulseWidthModulationCounter < triangle) ? 1.f : 0.f, sampleTime); - lights[LIGHT_FX + 1].setBrightnessSmooth((fxModel >= 4 && pulseWidthModulationCounter < triangle) ? 1.f : 0.f, sampleTime); + if (fxModel < 3) { + lights[LIGHT_FX + 0].setBrightnessSmooth(fxModel <= 1 ? 1.f : 0.f, sampleTime); + lights[LIGHT_FX + 1].setBrightnessSmooth(fxModel >= 1 ? 1.f : 0.f, sampleTime); + } + else { + lights[LIGHT_FX + 0].setBrightnessSmooth((fxModel <= 4 && pulseWidthModulationCounter < triangle) ? 1.f : 0.f, sampleTime); + lights[LIGHT_FX + 1].setBrightnessSmooth((fxModel >= 4 && pulseWidthModulationCounter < triangle) ? 1.f : 0.f, sampleTime); + } } - } - if (polyphonyMode != 3) { - lights[LIGHT_POLYPHONY + 0].setBrightness(polyphonyMode <= 2 ? 1.f : 0.f); - lights[LIGHT_POLYPHONY + 1].setBrightness(polyphonyMode >= 2 ? 1.f : 0.f); - } - else { - lights[LIGHT_POLYPHONY + 0].setBrightness(1.f); - lights[LIGHT_POLYPHONY + 1].setBrightness(pulseWidthModulationCounter < triangle ? 1.f : 0.f); - } + if (polyphonyMode != 3) { + lights[LIGHT_POLYPHONY + 0].setBrightness(polyphonyMode <= 2 ? 1.f : 0.f); + lights[LIGHT_POLYPHONY + 1].setBrightness(polyphonyMode >= 2 ? 1.f : 0.f); + } + else { + lights[LIGHT_POLYPHONY + 0].setBrightness(1.f); + lights[LIGHT_POLYPHONY + 1].setBrightness(pulseWidthModulationCounter < triangle ? 1.f : 0.f); + } - ++strummingFlagInterval; - if (strummingFlagCounter) { - --strummingFlagCounter; - lights[LIGHT_POLYPHONY + 0].setBrightness(0.f); - lights[LIGHT_POLYPHONY + 1].setBrightness(0.f); + ++strummingFlagInterval; + if (strummingFlagCounter) { + --strummingFlagCounter; + lights[LIGHT_POLYPHONY + 0].setBrightness(0.f); + lights[LIGHT_POLYPHONY + 1].setBrightness(0.f); + } } } } void setMode(int modeNum) { if (modeNum < 6) { - bEasterEgg = false; - resonatorModel = rings::ResonatorModel(modeNum); + bEasterEgg[0] = false; + resonatorModel[0] = rings::ResonatorModel(modeNum); params[PARAM_MODE].setValue(modeNum); } else { - bEasterEgg = true; + bEasterEgg[0] = true; params[PARAM_MODE].setValue(modeNum); } } @@ -389,7 +419,23 @@ struct AnuliWidget : SanguineModuleWidget { addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); - addChild(createLightCentered>(millimetersToPixelsVec(9.021, 17.317), module, Anuli::LIGHT_RESONATOR)); + const float xDelta = 3.71f; + const int lightIdOffset = 8; + + float currentXA = 23.989f; + float currentXB = 56.725f; + + for (int i = 0; i < 8; i++) { + addChild(createLightCentered>(millimetersToPixelsVec(currentXA, 14.973), + module, Anuli::LIGHT_RESONATOR + i * 3)); + addChild(createLightCentered>(millimetersToPixelsVec(currentXB, 14.973), + module, Anuli::LIGHT_RESONATOR + ((i + lightIdOffset) * 3))); + currentXA += xDelta; + currentXB += xDelta; + } + + + addInput(createInputCentered(millimetersToPixelsVec(9.021, 22.087), module, Anuli::INPUT_MODE)); FramebufferWidget* anuliFrambuffer = new FramebufferWidget(); addChild(anuliFrambuffer); diff --git a/src/sanguinecomponents.cpp b/src/sanguinecomponents.cpp index 3213239f..1db48a17 100644 --- a/src/sanguinecomponents.cpp +++ b/src/sanguinecomponents.cpp @@ -11,6 +11,10 @@ BananutBlack::BananutBlack() { setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/BananutBlack.svg"))); } +BananutBlackPoly::BananutBlackPoly() { + setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/BananutBlackPoly.svg"))); +} + BananutBlue::BananutBlue() { setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/BananutBlue.svg"))); } diff --git a/src/sanguinecomponents.hpp b/src/sanguinecomponents.hpp index b52fb7b9..9b1127a9 100644 --- a/src/sanguinecomponents.hpp +++ b/src/sanguinecomponents.hpp @@ -13,6 +13,10 @@ struct BananutBlack : app::SvgPort { BananutBlack(); }; +struct BananutBlackPoly : app::SvgPort { + BananutBlackPoly(); +}; + struct BananutBlue : app::SvgPort { BananutBlue(); };