commit af21b244ae6bd5ebc05e5b8c3ff009890e4d0eed Author: Andrew Belt Date: Mon Dec 26 01:43:08 2016 -0500 Initial commit diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..1883563 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,7 @@ +Copyright (c) 2016 Andrew Belt + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fa5943d --- /dev/null +++ b/Makefile @@ -0,0 +1,80 @@ + +ARCH ?= linux +CXXFLAGS = -MMD -fPIC -g -Wall -std=c++11 -O3 -ffast-math -DTEST \ + -I./src -I../../include -I./eurorack \ + -fasm \ + -finline \ + -fshort-enums + +LDFLAGS = + +SOURCES = $(wildcard src/*.cpp) \ + eurorack/stmlib/utils/random.cc \ + eurorack/stmlib/dsp/atan.cc \ + eurorack/stmlib/dsp/units.cc \ + eurorack/braids/macro_oscillator.cc \ + eurorack/braids/analog_oscillator.cc \ + eurorack/braids/digital_oscillator.cc \ + eurorack/braids/quantizer.cc \ + eurorack/braids/resources.cc \ + eurorack/clouds/dsp/correlator.cc \ + eurorack/clouds/dsp/granular_processor.cc \ + eurorack/clouds/dsp/mu_law.cc \ + eurorack/clouds/dsp/pvoc/frame_transformation.cc \ + eurorack/clouds/dsp/pvoc/phase_vocoder.cc \ + eurorack/clouds/dsp/pvoc/stft.cc \ + eurorack/clouds/resources.cc \ + eurorack/elements/dsp/exciter.cc \ + eurorack/elements/dsp/ominous_voice.cc \ + eurorack/elements/dsp/resonator.cc \ + eurorack/elements/dsp/tube.cc \ + eurorack/elements/dsp/multistage_envelope.cc \ + eurorack/elements/dsp/part.cc \ + eurorack/elements/dsp/string.cc \ + eurorack/elements/dsp/voice.cc \ + eurorack/elements/resources.cc \ + eurorack/rings/dsp/fm_voice.cc \ + eurorack/rings/dsp/part.cc \ + eurorack/rings/dsp/string_synth_part.cc \ + eurorack/rings/dsp/string.cc \ + eurorack/rings/dsp/resonator.cc \ + eurorack/rings/resources.cc \ + eurorack/tides/generator.cc \ + eurorack/tides/resources.cc \ + eurorack/warps/dsp/modulator.cc \ + eurorack/warps/dsp/oscillator.cc \ + eurorack/warps/dsp/vocoder.cc \ + eurorack/warps/dsp/filter_bank.cc \ + eurorack/warps/resources.cc + + +# Linux +ifeq ($(ARCH), linux) +CC = gcc +CXX = g++ +CXXFLAGS += -Wno-unused-local-typedefs +LDFLAGS += -shared +TARGET = plugin.so +endif + +# Apple +ifeq ($(ARCH), apple) +CC = clang +CXX = clang++ +CXXFLAGS += -stdlib=libc++ +LDFLAGS += -stdlib=libc++ -shared -undefined dynamic_lookup +TARGET = plugin.dylib +endif + +# Windows +ifeq ($(ARCH), windows) +CC = x86_64-w64-mingw32-gcc +CXX = x86_64-w64-mingw32-g++ +CXXFLAGS += -D_USE_MATH_DEFINES -Wno-unused-local-typedefs +SOURCES += +LDFLAGS += -shared -L../../ -lRack +TARGET = plugin.dll +endif + + +include ../../Makefile.inc diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..0c5b824 --- /dev/null +++ b/README.txt @@ -0,0 +1,46 @@ + +# Audible Instruments + +Ports of [Mutable Instruments](http://mutable-instruments.net/modules) modules to Virtuoso Rack + + +## TODO + +Percentages are progress amounts, X means won't port + +[90%] Braids (Macro Oscillator) +- sample rate converter +[X] Edges (Chiptune Generator) +- GPL, will not port +[70%] Elements (Modal Synthesizer) +- fix weird audio stability bug +- sample rate converter +[100%] Tides (Tidal Modulator) +[X] Sheep (Wavetable Oscillator) +- based on Tides +- cannot find source, will not port +[0%] Peaks +[X] Ripples +- analog, will not port unless I buy/borrow one or someone contributes a technical model +[X] Shelves +- analog, will not port unless I buy/borrow one or someone contributes a technical model +[10%] Streams +- analog, but it's possible to fake the filter with schematic analysis +[70%] Clouds (Texture Synthesizer) +- edit buttons and lights +[100%] Warps (Meta Modulator) +[90%] Rings (Resonator) +- performance issue with first mode +[X] Grids (Drum Sequencer) +- GPL, will not port +[0%] Frames +[X] Yarns +- functionality covered by the MIDI Interface from core, will not port +[X] Ears +- a microphone, will not port +[100%] Links (Multiples) +[100%] Kinks (Utilities) +[100%] Shades (Mixer) +[100%] Branches (Bernoulli Gate) +[100%] Blinds (Quad VC-polarizer) +[100%] Veils (Quad VCA) diff --git a/src/AudibleInstruments.cpp b/src/AudibleInstruments.cpp new file mode 100644 index 0000000..2ff686d --- /dev/null +++ b/src/AudibleInstruments.cpp @@ -0,0 +1,48 @@ +#include "AudibleInstruments.hpp" + + +void ValueLight::step() { + float v = getf(value); + if (v > 0) { + color = nvgHSL(0.36, 0.7, 0.7); + color.a = v; // May be larger than 1 + } + else if (v < 0) { + color = nvgHSL(0.0, 0.7, 0.7); + color.a = -v; + } + else { + color = nvgRGBAf(1.0, 1.0, 1.0, 0.0); + } +} + + +void ModeLight::step() { + int mode = (int) roundf(getf(value)); + switch (mode) { + case 0: color = nvgHSL(0.36, 0.7, 0.7); break; + case 1: color = nvgHSL(0.18, 0.7, 0.7); break; + case 2: color = nvgHSL(0.0, 0.7, 0.7); break; + default: color = nvgRGBAf(0.0, 0.0, 0.0, 0.0); break; + } +} + + + +Plugin *init() { + Plugin *plugin = createPlugin("Audible Instruments", "Audible Instruments"); + createModel(plugin, "Braids", "Macro Oscillator"); + createModel(plugin, "Elements", "Modal Synthesizer"); + createModel(plugin, "Tides", "Tidal Modulator"); + // createModel(plugin, "Streams", "Dual Dynamics Gate"); + createModel(plugin, "Clouds", "Texture Synthesizer"); + createModel(plugin, "Warps", "Meta Modulator"); + createModel(plugin, "Rings", "Resonator"); + createModel(plugin, "Links", "Multiples"); + createModel(plugin, "Kinks", "Utilities"); + createModel(plugin, "Shades", "Mixer"); + createModel(plugin, "Branches", "Bernoulli Gate"); + createModel(plugin, "Blinds", "Quad VC-polarizer"); + createModel(plugin, "Veils", "Quad VCA"); + return plugin; +} diff --git a/src/AudibleInstruments.hpp b/src/AudibleInstruments.hpp new file mode 100644 index 0000000..4239257 --- /dev/null +++ b/src/AudibleInstruments.hpp @@ -0,0 +1,275 @@ +#include "Rack.hpp" + + +using namespace rack; + +//////////////////// +// helpers +//////////////////// + +struct AudiblePanel : ModulePanel { + AudiblePanel() { + backgroundColor = nvgRGBf(0.85, 0.85, 0.85); + } +}; + +//////////////////// +// knobs +//////////////////// + +struct AudibleKnob : Knob { + AudibleKnob() { + minIndex = 44; + maxIndex = -46; + spriteCount = 120; + } +}; + +struct HugeGlowKnob : AudibleKnob { + HugeGlowKnob() { + box.size = Vec(91, 91); + spriteOffset = Vec(-10, -9); + spriteSize = Vec(110, 110); + spriteFilename = "plugins/AudibleInstruments/res/knob_huge_glow.png"; + minIndex = 44+5; + maxIndex = -46-5; + } +}; + +struct LargeKnob : AudibleKnob { + LargeKnob() { + box.size = Vec(52, 52); + spriteOffset = Vec(-5, -4); + spriteSize = Vec(65, 66); + } +}; + +struct LargeWhiteKnob : LargeKnob { + LargeWhiteKnob() { + spriteFilename = "plugins/AudibleInstruments/res/knob_large_white.png"; + } +}; + +struct LargeRedKnob : LargeKnob { + LargeRedKnob() { + spriteFilename = "plugins/AudibleInstruments/res/knob_large_red.png"; + } +}; + +struct LargeGreenKnob : LargeKnob { + LargeGreenKnob() { + spriteFilename = "plugins/AudibleInstruments/res/knob_large_green.png"; + } +}; + +struct MediumKnob : AudibleKnob { + MediumKnob() { + box.size = Vec(44, 44); + spriteOffset = Vec(-5, -4); + spriteSize = Vec(57, 58); + } +}; + +struct MediumWhiteKnob : MediumKnob { + MediumWhiteKnob() { + spriteFilename = "plugins/AudibleInstruments/res/knob_medium_white.png"; + } +}; + +struct MediumRedKnob : MediumKnob { + MediumRedKnob() { + spriteFilename = "plugins/AudibleInstruments/res/knob_medium_red.png"; + } +}; + +struct MediumGreenKnob : MediumKnob { + MediumGreenKnob() { + spriteFilename = "plugins/AudibleInstruments/res/knob_medium_green.png"; + } +}; + +struct SmallKnob : AudibleKnob { + SmallKnob() { + box.size = Vec(40, 40); + spriteOffset = Vec(-5, -4); + spriteSize = Vec(53, 54); + } +}; + +struct SmallWhiteKnob : SmallKnob { + SmallWhiteKnob() { + spriteFilename = "plugins/AudibleInstruments/res/knob_small_white.png"; + } +}; + +struct SmallRedKnob : SmallKnob { + SmallRedKnob() { + spriteFilename = "plugins/AudibleInstruments/res/knob_small_red.png"; + } +}; + +struct SmallGreenKnob : SmallKnob { + SmallGreenKnob() { + spriteFilename = "plugins/AudibleInstruments/res/knob_small_green.png"; + } +}; + +struct TinyBlackKnob : AudibleKnob { + TinyBlackKnob() { + box.size = Vec(18, 18); + spriteOffset = Vec(-5, -3); + spriteSize = Vec(31, 30); + spriteFilename = "plugins/AudibleInstruments/res/knob_tiny_black.png"; + } +}; + +//////////////////// +// switches +//////////////////// + +struct LargeSwitch : virtual Switch { + LargeSwitch() { + box.size = Vec(27, 27); + spriteOffset = Vec(-3, -2); + spriteSize = Vec(36, 36); + spriteFilename = "plugins/AudibleInstruments/res/button_large_black.png"; + } +}; + +struct MediumSwitch : virtual Switch { + MediumSwitch() { + box.size = Vec(15, 15); + spriteOffset = Vec(-4, -2); + spriteSize = Vec(25, 25); + spriteFilename = "plugins/AudibleInstruments/res/button_medium_black.png"; + } +}; + +struct LargeMomentarySwitch : LargeSwitch, MomentarySwitch {}; +struct MediumMomentarySwitch : MediumSwitch, MomentarySwitch {}; +struct LargeToggleSwitch : LargeSwitch, ToggleSwitch {}; +struct MediumToggleSwitch : MediumSwitch, ToggleSwitch {}; + +struct SlideSwitch : Switch { + SlideSwitch() { + box.size = Vec(11, 21); + spriteOffset = Vec(-1, -1); + spriteSize = Vec(12, 22); + spriteFilename = "plugins/AudibleInstruments/res/slide_switch.png"; + } + + void step() { + index = (value == minValue) ? 0 : 1; + } + + void onDragDrop(Widget *origin) { + if (origin != this) + return; + + if (value == minValue) { + setValue(maxValue); + } + else { + setValue(minValue); + } + } +}; + +//////////////////// +// lights +//////////////////// + +struct ValueLight : virtual Light { + float *value = NULL; + void step(); +}; + +struct ModeLight : ValueLight { + void step(); +}; + +struct SmallLight : virtual Light { + SmallLight() { + box.size = Vec(7, 7); + spriteOffset = Vec(-16, -16); + spriteSize = Vec(38, 38); + spriteFilename = "plugins/AudibleInstruments/res/light_small.png"; + } +}; + +struct MediumLight : virtual Light { + MediumLight() { + box.size = Vec(14, 14); + spriteOffset = Vec(-16, -15); + spriteSize = Vec(45, 45); + spriteFilename = "plugins/AudibleInstruments/res/light_medium.png"; + } +}; + +struct SmallValueLight : SmallLight, ValueLight {}; +struct MediumValueLight : MediumLight, ValueLight {}; +struct SmallModeLight : SmallLight, ModeLight {}; + +template +ValueLight *createValueLight(Vec pos, float *value) { + ValueLight *light = new TLight(); + light->box.pos = pos; + light->value = value; + return light; +} + +//////////////////// +// module widgets +//////////////////// + +struct BraidsWidget : ModuleWidget { + BraidsWidget(); +}; + +struct ElementsWidget : ModuleWidget { + ElementsWidget(); +}; + +struct TidesWidget : ModuleWidget { + TidesWidget(); +}; + +struct StreamsWidget : ModuleWidget { + StreamsWidget(); +}; + +struct CloudsWidget : ModuleWidget { + CloudsWidget(); +}; + +struct WarpsWidget : ModuleWidget { + WarpsWidget(); +}; + +struct RingsWidget : ModuleWidget { + RingsWidget(); +}; + +struct LinksWidget : ModuleWidget { + LinksWidget(); +}; + +struct KinksWidget : ModuleWidget { + KinksWidget(); +}; + +struct ShadesWidget : ModuleWidget { + ShadesWidget(); +}; + +struct BranchesWidget : ModuleWidget { + BranchesWidget(); +}; + +struct BlindsWidget : ModuleWidget { + BlindsWidget(); +}; + +struct VeilsWidget : ModuleWidget { + VeilsWidget(); +}; diff --git a/src/Blinds.cpp b/src/Blinds.cpp new file mode 100644 index 0000000..3d9ef8e --- /dev/null +++ b/src/Blinds.cpp @@ -0,0 +1,137 @@ +#include "AudibleInstruments.hpp" +#include + + +struct Blinds : Module { + enum ParamIds { + GAIN1_PARAM, + GAIN2_PARAM, + GAIN3_PARAM, + GAIN4_PARAM, + MOD1_PARAM, + MOD2_PARAM, + MOD3_PARAM, + MOD4_PARAM, + NUM_PARAMS + }; + enum InputIds { + IN1_INPUT, + IN2_INPUT, + IN3_INPUT, + IN4_INPUT, + CV1_INPUT, + CV2_INPUT, + CV3_INPUT, + CV4_INPUT, + NUM_INPUTS + }; + enum OutputIds { + OUT1_OUTPUT, + OUT2_OUTPUT, + OUT3_OUTPUT, + OUT4_OUTPUT, + NUM_OUTPUTS + }; + + float lights[4] = {}; + float gainLights[4] = {}; + + Blinds(); + void step(); +}; + + +Blinds::Blinds() { + params.resize(NUM_PARAMS); + inputs.resize(NUM_INPUTS); + outputs.resize(NUM_OUTPUTS); +} + +static float getChannelOutput(const float *in, float gain, const float *cv, float mod, float *light) { + gain += mod * getf(cv) / 5.0; + *light = gain * 5.0; + return gain * getf(in, 5.0); +} + +void Blinds::step() { + float out = 0.0; + out += getChannelOutput(inputs[IN1_INPUT], params[GAIN1_PARAM], inputs[CV1_INPUT], params[MOD1_PARAM], &gainLights[0]); + lights[0] = out; + if (outputs[OUT1_OUTPUT]) { + *outputs[OUT1_OUTPUT] = out; + out = 0.0; + } + + out += getChannelOutput(inputs[IN2_INPUT], params[GAIN2_PARAM], inputs[CV2_INPUT], params[MOD2_PARAM], &gainLights[1]); + lights[1] = out; + if (outputs[OUT2_OUTPUT]) { + *outputs[OUT2_OUTPUT] = out; + out = 0.0; + } + + out += getChannelOutput(inputs[IN3_INPUT], params[GAIN3_PARAM], inputs[CV3_INPUT], params[MOD3_PARAM], &gainLights[2]); + lights[2] = out; + if (outputs[OUT3_OUTPUT]) { + *outputs[OUT3_OUTPUT] = out; + out = 0.0; + } + + out += getChannelOutput(inputs[IN4_INPUT], params[GAIN4_PARAM], inputs[CV4_INPUT], params[MOD4_PARAM], &gainLights[3]); + lights[3] = out; + if (outputs[OUT4_OUTPUT]) { + *outputs[OUT4_OUTPUT] = out; + } +} + + +BlindsWidget::BlindsWidget() : ModuleWidget(new Blinds()) { + box.size = Vec(15*12, 380); + + { + AudiblePanel *panel = new AudiblePanel(); + panel->imageFilename = "plugins/AudibleInstruments/res/Blinds.png"; + panel->box.size = box.size; + addChild(panel); + } + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(150, 0))); + addChild(createScrew(Vec(15, 365))); + addChild(createScrew(Vec(150, 365))); + + addParam(createParam(Vec(8, 52), module, Blinds::GAIN1_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(8, 131), module, Blinds::GAIN2_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(8, 210), module, Blinds::GAIN3_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(8, 288), module, Blinds::GAIN4_PARAM, -1.0, 1.0, 0.0)); + + addParam(createParam(Vec(72, 63), module, Blinds::MOD1_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(72, 142), module, Blinds::MOD2_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(72, 221), module, Blinds::MOD3_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(72, 300), module, Blinds::MOD4_PARAM, -1.0, 1.0, 0.0)); + + addInput(createInput(Vec(112, 43), module, Blinds::IN1_INPUT)); + addInput(createInput(Vec(112, 122), module, Blinds::IN2_INPUT)); + addInput(createInput(Vec(112, 200), module, Blinds::IN3_INPUT)); + addInput(createInput(Vec(112, 279), module, Blinds::IN4_INPUT)); + + addInput(createInput(Vec(112, 82), module, Blinds::CV1_INPUT)); + addInput(createInput(Vec(112, 161), module, Blinds::CV2_INPUT)); + addInput(createInput(Vec(112, 240), module, Blinds::CV3_INPUT)); + addInput(createInput(Vec(112, 318), module, Blinds::CV4_INPUT)); + + addOutput(createOutput(Vec(146, 43), module, Blinds::OUT1_OUTPUT)); + addOutput(createOutput(Vec(146, 122), module, Blinds::OUT2_OUTPUT)); + addOutput(createOutput(Vec(146, 200), module, Blinds::OUT3_OUTPUT)); + addOutput(createOutput(Vec(146, 279), module, Blinds::OUT4_OUTPUT)); + + Blinds *blinds = dynamic_cast(module); + addChild(createValueLight(Vec(149, 86), &blinds->lights[0])); + addChild(createValueLight(Vec(149, 165), &blinds->lights[1])); + addChild(createValueLight(Vec(149, 244), &blinds->lights[2])); + addChild(createValueLight(Vec(149, 323), &blinds->lights[3])); + + addChild(createValueLight(Vec(78, 97), &blinds->gainLights[0])); + addChild(createValueLight(Vec(78, 176), &blinds->gainLights[1])); + addChild(createValueLight(Vec(78, 255), &blinds->gainLights[2])); + addChild(createValueLight(Vec(78, 334), &blinds->gainLights[3])); +} diff --git a/src/Braids.cpp b/src/Braids.cpp new file mode 100644 index 0000000..f8201f5 --- /dev/null +++ b/src/Braids.cpp @@ -0,0 +1,197 @@ +#include "AudibleInstruments.hpp" +#include +#include "braids/macro_oscillator.h" + + +struct Braids : Module { + enum ParamIds { + FINE_PARAM, + COARSE_PARAM, + FM_PARAM, + TIMBRE_PARAM, + MODULATION_PARAM, + COLOR_PARAM, + SHAPE_PARAM, + NUM_PARAMS + }; + enum InputIds { + TRIG_INPUT, + PITCH_INPUT, + FM_INPUT, + TIMBRE_INPUT, + COLOR_INPUT, + NUM_INPUTS + }; + enum OutputIds { + OUT_OUTPUT, + NUM_OUTPUTS + }; + + braids::MacroOscillator *osc; + int bufferFrame = 0; + int16_t buffer[24] = {}; + bool lastTrig = false; + + Braids(); + ~Braids(); + void step(); + void setShape(int shape); +}; + + +Braids::Braids() { + params.resize(NUM_PARAMS); + inputs.resize(NUM_INPUTS); + outputs.resize(NUM_OUTPUTS); + + osc = new braids::MacroOscillator(); + memset(osc, 0, sizeof(*osc)); + osc->Init(); +} + +Braids::~Braids() { + delete osc; +} + +void Braids::step() { + // TODO Sample rate convert from 96000Hz + setf(outputs[OUT_OUTPUT], 5.0*(buffer[bufferFrame] / 32768.0)); + + // Trigger + bool trig = getf(inputs[TRIG_INPUT]) >= 1.0; + if (!lastTrig && trig) { + osc->Strike(); + } + lastTrig = trig; + + if (++bufferFrame >= 24) { + bufferFrame = 0; + // Set shape + int shape = roundf(params[SHAPE_PARAM]); + osc->set_shape((braids::MacroOscillatorShape) shape); + + // Set timbre/modulation + float timbre = params[TIMBRE_PARAM] + params[MODULATION_PARAM] * getf(inputs[TIMBRE_INPUT]) / 5.0; + float modulation = params[COLOR_PARAM] + getf(inputs[COLOR_INPUT]) / 5.0; + int16_t param1 = mapf(clampf(timbre, 0.0, 1.0), 0.0, 1.0, 0, INT16_MAX); + int16_t param2 = mapf(clampf(modulation, 0.0, 1.0), 0.0, 1.0, 0, INT16_MAX); + osc->set_parameters(param1, param2); + + // Set pitch + float pitch = getf(inputs[PITCH_INPUT]) + params[COARSE_PARAM] + params[FINE_PARAM] / 12.0 + params[FM_PARAM] * getf(inputs[FM_INPUT]); + int16_t p = clampf((pitch * 12.0 + 60) * 128, 0, INT16_MAX); + osc->set_pitch(p); + + uint8_t sync_buffer[24] = {}; + osc->Render(sync_buffer, buffer, 24); + } +} + + +static const char *algo_values[] = { + "CSAW", + "/\\-_", + "//-_", + "FOLD", + "uuuu", + "SYN-", + "SYN/", + "//x3", + "-_x3", + "/\\x3", + "SIx3", + "RING", + "////", + "//uu", + "TOY*", + "ZLPF", + "ZPKF", + "ZBPF", + "ZHPF", + "VOSM", + "VOWL", + "VFOF", + "HARM", + "FM ", + "FBFM", + "WTFM", + "PLUK", + "BOWD", + "BLOW", + "FLUT", + "BELL", + "DRUM", + "KICK", + "CYMB", + "SNAR", + "WTBL", + "WMAP", + "WLIN", + "WTx4", + "NOIS", + "TWNQ", + "CLKN", + "CLOU", + "PRTC", + "QPSK", + " ", +}; + +struct BraidsDisplay : TransparentWidget { + float *value; + void draw(NVGcontext *vg) { + int shape = roundf(getf(value)); + + int font = loadFont("plugins/AudibleInstruments/res/hdad-segment14-1.002/Segment14.ttf"); + nvgFontSize(vg, 36); + nvgFontFaceId(vg, font); + nvgTextLetterSpacing(vg, 2.5); + + NVGcolor color = nvgRGB(0xaf, 0xd2, 0x2c); + nvgFillColor(vg, nvgTransRGBA(color, 16)); + nvgText(vg, box.pos.x, box.pos.y, "~~~~", NULL); + nvgFillColor(vg, color); + nvgText(vg, box.pos.x, box.pos.y, algo_values[shape], NULL); + } +}; + + +BraidsWidget::BraidsWidget() : ModuleWidget(new Braids()) { + box.size = Vec(15*16, 380); + + { + AudiblePanel *panel = new AudiblePanel(); + panel->imageFilename = "plugins/AudibleInstruments/res/Braids.png"; + panel->box.size = box.size; + addChild(panel); + } + + { + BraidsDisplay *display = new BraidsDisplay(); + display->box.pos = Vec(24, 101); + display->value = &module->params[Braids::SHAPE_PARAM]; + addChild(display); + } + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(210, 0))); + addChild(createScrew(Vec(15, 365))); + addChild(createScrew(Vec(210, 365))); + + addParam(createParam(Vec(187-10, 71-11), module, Braids::SHAPE_PARAM, 0.0, braids::MACRO_OSC_SHAPE_LAST-2, 0.0)); + + addParam(createParam(Vec(30-10, 150-11), module, Braids::FINE_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(108-10, 150-11), module, Braids::COARSE_PARAM, -2.0, 2.0, 0.0)); + addParam(createParam(Vec(187-10, 150-11), module, Braids::FM_PARAM, -1.0, 1.0, 0.0)); + + addParam(createParam(Vec(30-10, 229-11), module, Braids::TIMBRE_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(108-10, 229-11), module, Braids::MODULATION_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(187-10, 229-11), module, Braids::COLOR_PARAM, 0.0, 1.0, 0.5)); + + addInput(createInput(Vec(12, 318), module, Braids::TRIG_INPUT)); + addInput(createInput(Vec(50, 318), module, Braids::PITCH_INPUT)); + addInput(createInput(Vec(87, 318), module, Braids::FM_INPUT)); + addInput(createInput(Vec(125, 318), module, Braids::TIMBRE_INPUT)); + addInput(createInput(Vec(162, 318), module, Braids::COLOR_INPUT)); + addOutput(createOutput(Vec(207, 318), module, Braids::OUT_OUTPUT)); +} diff --git a/src/Branches.cpp b/src/Branches.cpp new file mode 100644 index 0000000..ac44786 --- /dev/null +++ b/src/Branches.cpp @@ -0,0 +1,106 @@ +#include "AudibleInstruments.hpp" + + +struct Branches : Module { + enum ParamIds { + THRESHOLD1_PARAM, + THRESHOLD2_PARAM, + MODE1_PARAM, + MODE2_PARAM, + NUM_PARAMS + }; + enum InputIds { + IN1_INPUT, + P1_INPUT, + IN2_INPUT, + P2_INPUT, + NUM_INPUTS + }; + enum OutputIds { + OUT1A_OUTPUT, + OUT1B_OUTPUT, + OUT2A_OUTPUT, + OUT2B_OUTPUT, + NUM_OUTPUTS + }; + + bool lastGate[2] = {}; + bool outcome[2] = {}; + float light[2] = {}; + + Branches(); + void step(); + float getOutput(int outputId); +}; + + +Branches::Branches() { + params.resize(NUM_PARAMS); + inputs.resize(NUM_INPUTS); + outputs.resize(NUM_OUTPUTS); +} + +static void computeChannel(const float *in, const float *p, float threshold, float mode, bool *lastGate, bool *outcome, float *out1, float *out2, float *light) { + float out = getf(in); + bool gate = (out >= 1.0); + if (gate && !*lastGate) { + // trigger + std::uniform_real_distribution dist(0.0, 1.0); + bool toss = (dist(rng) < threshold + getf(p)); + if (mode < 0.5) { + // direct mode + *outcome = toss; + } + else { + // toggle mode + *outcome = *outcome != toss; + } + } + *lastGate = gate; + *light = *outcome ? out : -out; + + if (out1) { + *out1 = *outcome ? 0.0 : out; + } + if (out2) { + *out2 = *outcome ? out : 0.0; + } +} + +void Branches::step() { + computeChannel(inputs[IN1_INPUT], inputs[P1_INPUT], params[THRESHOLD1_PARAM], params[MODE1_PARAM], &lastGate[0], &outcome[0], outputs[OUT1A_OUTPUT], outputs[OUT1B_OUTPUT], &light[0]); + computeChannel(inputs[IN2_INPUT], inputs[P2_INPUT], params[THRESHOLD2_PARAM], params[MODE2_PARAM], &lastGate[1], &outcome[1], outputs[OUT2A_OUTPUT], outputs[OUT2B_OUTPUT], &light[1]); +} + + +BranchesWidget::BranchesWidget() : ModuleWidget(new Branches()) { + box.size = Vec(15*6, 380); + + { + AudiblePanel *panel = new AudiblePanel(); + panel->imageFilename = "plugins/AudibleInstruments/res/Branches.png"; + panel->box.size = box.size; + addChild(panel); + } + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(15, 365))); + + addParam(createParam(Vec(44-20, 84-20), module, Branches::THRESHOLD1_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(69, 58), module, Branches::MODE1_PARAM, 0.0, 1.0, 0.0)); + addInput(createInput(Vec(11, 125-1), module, Branches::IN1_INPUT)); + addInput(createInput(Vec(58, 125-1), module, Branches::P1_INPUT)); + addOutput(createOutput(Vec(11, 163-1), module, Branches::OUT1A_OUTPUT)); + addOutput(createOutput(Vec(58, 163-1), module, Branches::OUT1B_OUTPUT)); + + addParam(createParam(Vec(44-20, 240-20), module, Branches::THRESHOLD2_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(69, 214), module, Branches::MODE2_PARAM, 0.0, 1.0, 0.0)); + addInput(createInput(Vec(11, 281-1), module, Branches::IN2_INPUT)); + addInput(createInput(Vec(58, 281-1), module, Branches::P2_INPUT)); + addOutput(createOutput(Vec(11, 319-1), module, Branches::OUT2A_OUTPUT)); + addOutput(createOutput(Vec(58, 319-1), module, Branches::OUT2B_OUTPUT)); + + Branches *branches = dynamic_cast(module); + addChild(createValueLight(Vec(42, 170), &branches->light[0])); + addChild(createValueLight(Vec(42, 326), &branches->light[1])); +} diff --git a/src/Clouds.cpp b/src/Clouds.cpp new file mode 100644 index 0000000..ecf728d --- /dev/null +++ b/src/Clouds.cpp @@ -0,0 +1,168 @@ +#include "AudibleInstruments.hpp" +#include +#include "clouds/dsp/granular_processor.h" + + +struct Clouds : Module { + enum ParamIds { + POSITION_PARAM, + SIZE_PARAM, + PITCH_PARAM, + IN_GAIN_PARAM, + DENSITY_PARAM, + TEXTURE_PARAM, + BLEND_PARAM, + NUM_PARAMS + }; + enum InputIds { + FREEZE_INPUT, + TRIG_INPUT, + POSITION_INPUT, + SIZE_INPUT, + PITCH_INPUT, + BLEND_INPUT, + IN_L_INPUT, + IN_R_INPUT, + DENSITY_INPUT, + TEXTURE_INPUT, + NUM_INPUTS + }; + enum OutputIds { + OUT_L_OUTPUT, + OUT_R_OUTPUT, + NUM_OUTPUTS + }; + + uint8_t *block_mem; + uint8_t *block_ccm; + clouds::GranularProcessor *processor; + int bufferFrame = 0; + float inL[32] = {}; + float inR[32] = {}; + float outL[32] = {}; + float outR[32] = {}; + bool triggered = false; + + Clouds(); + ~Clouds(); + void step(); +}; + + +Clouds::Clouds() { + params.resize(NUM_PARAMS); + inputs.resize(NUM_INPUTS); + outputs.resize(NUM_OUTPUTS); + + const int memLen = 118784; + const int ccmLen = 65536 - 128; + block_mem = new uint8_t[memLen]; + memset(block_mem, 0, memLen); + block_ccm = new uint8_t[ccmLen]; + memset(block_ccm, 0, ccmLen); + processor = new clouds::GranularProcessor(); + memset(processor, 0, sizeof(*processor)); + + processor->Init(block_mem, memLen, block_ccm, ccmLen); +} + +Clouds::~Clouds() { + delete processor; + delete[] block_mem; + delete[] block_ccm; +} + +void Clouds::step() { + // TODO Sample rate conversion from 32000 Hz + inL[bufferFrame] = getf(inputs[IN_L_INPUT]); + inR[bufferFrame] = getf(inputs[IN_R_INPUT]); + setf(outputs[OUT_L_OUTPUT], outL[bufferFrame]); + setf(outputs[OUT_R_OUTPUT], outR[bufferFrame]); + + // Trigger + if (getf(inputs[TRIG_INPUT]) >= 1.0) { + triggered = true; + } + + if (++bufferFrame >= 32) { + bufferFrame = 0; + processor->set_num_channels(2); + processor->set_low_fidelity(false); + processor->set_playback_mode(clouds::PLAYBACK_MODE_GRANULAR); + processor->Prepare(); + + clouds::Parameters* p = processor->mutable_parameters(); + p->trigger = triggered; + p->freeze = (getf(inputs[FREEZE_INPUT]) >= 1.0); + p->position = clampf(params[POSITION_PARAM] + getf(inputs[POSITION_INPUT]) / 5.0, 0.0, 1.0); + p->size = clampf(params[SIZE_PARAM] + getf(inputs[SIZE_INPUT]) / 5.0, 0.0, 1.0); + p->pitch = clampf((params[PITCH_PARAM] + getf(inputs[PITCH_INPUT])) * 12.0, -48.0, 48.0); + p->density = clampf(params[DENSITY_PARAM] + getf(inputs[DENSITY_INPUT]) / 5.0, 0.0, 1.0); + p->texture = clampf(params[TEXTURE_PARAM] + getf(inputs[TEXTURE_INPUT]) / 5.0, 0.0, 1.0); + float blend = clampf(params[BLEND_PARAM] + getf(inputs[BLEND_INPUT]) / 5.0, 0.0, 1.0); + p->dry_wet = blend; + p->stereo_spread = 0.0f; + p->feedback = 0.0f; + p->reverb = 0.0f; + + clouds::ShortFrame input[32]; + clouds::ShortFrame output[32]; + for (int j = 0; j < 32; j++) { + input[j].l = clampf(inL[j] * params[IN_GAIN_PARAM] / 5.0, -1.0, 1.0) * 32767; + input[j].r = clampf(inR[j] * params[IN_GAIN_PARAM] / 5.0, -1.0, 1.0) * 32767; + } + + processor->Process(input, output, 32); + + for (int j = 0; j < 32; j++) { + outL[j] = (float)output[j].l / 32767 * 5.0; + outR[j] = (float)output[j].r / 32767 * 5.0; + } + + triggered = false; + } +} + + +CloudsWidget::CloudsWidget() : ModuleWidget(new Clouds()) { + box.size = Vec(15*18, 380); + + { + AudiblePanel *panel = new AudiblePanel(); + panel->imageFilename = "plugins/AudibleInstruments/res/Clouds.png"; + panel->box.size = box.size; + addChild(panel); + } + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(240, 0))); + addChild(createScrew(Vec(15, 365))); + addChild(createScrew(Vec(240, 365))); + + // TODO + // addParam(createParam(Vec(211, 51), module, Clouds::POSITION_PARAM, 0.0, 1.0, 0.5)); + // addParam(createParam(Vec(239, 51), module, Clouds::POSITION_PARAM, 0.0, 1.0, 0.5)); + + addParam(createParam(Vec(42-14, 108-14), module, Clouds::POSITION_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(123-14, 108-14), module, Clouds::SIZE_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(205-14, 108-14), module, Clouds::PITCH_PARAM, -2.0, 2.0, 0.0)); + + addParam(createParam(Vec(25-10, 191-10), module, Clouds::IN_GAIN_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(92-10, 191-10), module, Clouds::DENSITY_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(157-10, 191-10), module, Clouds::TEXTURE_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(224-10, 191-10), module, Clouds::BLEND_PARAM, 0.0, 1.0, 0.5)); + + addInput(createInput(Vec(17, 275), module, Clouds::FREEZE_INPUT)); + addInput(createInput(Vec(60, 275), module, Clouds::TRIG_INPUT)); + addInput(createInput(Vec(103, 275), module, Clouds::POSITION_INPUT)); + addInput(createInput(Vec(146, 275), module, Clouds::SIZE_INPUT)); + addInput(createInput(Vec(190, 275), module, Clouds::PITCH_INPUT)); + addInput(createInput(Vec(233, 275), module, Clouds::BLEND_INPUT)); + + addInput(createInput(Vec(17, 318), module, Clouds::IN_L_INPUT)); + addInput(createInput(Vec(60, 318), module, Clouds::IN_R_INPUT)); + addInput(createInput(Vec(103, 318), module, Clouds::DENSITY_INPUT)); + addInput(createInput(Vec(146, 318), module, Clouds::TEXTURE_INPUT)); + addOutput(createOutput(Vec(190, 318), module, Clouds::OUT_L_OUTPUT)); + addOutput(createOutput(Vec(233, 318), module, Clouds::OUT_R_OUTPUT)); +} diff --git a/src/Elements.cpp b/src/Elements.cpp new file mode 100644 index 0000000..c28f5c2 --- /dev/null +++ b/src/Elements.cpp @@ -0,0 +1,222 @@ +#include "AudibleInstruments.hpp" +#include +#include "elements/dsp/part.h" + + +struct Elements : Module { + enum ParamIds { + CONTOUR_PARAM, + BOW_PARAM, + BLOW_PARAM, + STRIKE_PARAM, + COARSE_PARAM, + FINE_PARAM, + FM_PARAM, + + FLOW_PARAM, + MALLET_PARAM, + GEOMETRY_PARAM, + BRIGHTNESS_PARAM, + + BOW_TIMBRE_PARAM, + BLOW_TIMBRE_PARAM, + STRIKE_TIMBRE_PARAM, + DAMPING_PARAM, + POSITION_PARAM, + SPACE_PARAM, + + BOW_TIMBRE_MOD_PARAM, + FLOW_MOD_PARAM, + BLOW_TIMBRE_MOD_PARAM, + MALLET_MOD_PARAM, + STRIKE_TIMBRE_MOD_PARAM, + DAMPING_MOD_PARAM, + GEOMETRY_MOD_PARAM, + POSITION_MOD_PARAM, + BRIGHTNESS_MOD_PARAM, + SPACE_MOD_PARAM, + + PLAY_PARAM, + NUM_PARAMS + }; + enum InputIds { + NOTE_INPUT, + FM_INPUT, + GATE_INPUT, + STRENGTH_INPUT, + BLOW_INPUT, + STRIKE_INPUT, + + BOW_TIMBRE_MOD_INPUT, + FLOW_MOD_INPUT, + BLOW_TIMBRE_MOD_INPUT, + MALLET_MOD_INPUT, + STRIKE_TIMBRE_MOD_INPUT, + DAMPING_MOD_INPUT, + GEOMETRY_MOD_INPUT, + POSITION_MOD_INPUT, + BRIGHTNESS_MOD_INPUT, + SPACE_MOD_INPUT, + NUM_INPUTS + }; + enum OutputIds { + AUX_OUTPUT, + MAIN_OUTPUT, + NUM_OUTPUTS + }; + + uint16_t reverb_buffer[32768] = {}; + elements::Part *part; + int bufferFrame = 0; + float blow[16] = {}; + float strike[16] = {}; + float main[16] = {}; + float aux[16] = {}; + float lights[2] = {}; + + Elements(); + ~Elements(); + void step(); +}; + + +Elements::Elements() { + params.resize(NUM_PARAMS); + inputs.resize(NUM_INPUTS); + outputs.resize(NUM_OUTPUTS); + + part = new elements::Part(); + // In the Mutable Instruments code, Part doesn't initialize itself, so zero it here. + memset(part, 0, sizeof(*part)); + part->Init(reverb_buffer); + // Just some random numbers + uint32_t seed[3] = {1, 2, 3}; + part->Seed(seed, 3); +} + +Elements::~Elements() { + delete part; +} + +void Elements::step() { + // TODO Sample rate convert from 32000Hz + blow[bufferFrame] = getf(inputs[BLOW_INPUT]); + strike[bufferFrame] = getf(inputs[STRIKE_INPUT]); + setf(outputs[AUX_OUTPUT], 5.0*aux[bufferFrame]); + setf(outputs[MAIN_OUTPUT], 5.0*main[bufferFrame]); + + if (++bufferFrame >= 16) { + bufferFrame = 0; + // Set patch from parameters + elements::Patch* p = part->mutable_patch(); + p->exciter_envelope_shape = params[CONTOUR_PARAM]; + p->exciter_bow_level = params[BOW_PARAM]; + p->exciter_blow_level = params[BLOW_PARAM]; + p->exciter_strike_level = params[STRIKE_PARAM]; + + #define BIND(_p, _m, _i) clampf(params[_p] + 3.3*quadraticBipolar(params[_m])*getf(inputs[_i])/5.0, 0.0, 0.9995) + + p->exciter_bow_timbre = BIND(BOW_TIMBRE_PARAM, BOW_TIMBRE_MOD_PARAM, BOW_TIMBRE_MOD_INPUT); + p->exciter_blow_meta = BIND(FLOW_PARAM, FLOW_MOD_PARAM, FLOW_MOD_INPUT); + p->exciter_blow_timbre = BIND(BLOW_TIMBRE_PARAM, BLOW_TIMBRE_MOD_PARAM, BLOW_TIMBRE_MOD_INPUT); + p->exciter_strike_meta = BIND(MALLET_PARAM, MALLET_MOD_PARAM, MALLET_MOD_INPUT); + p->exciter_strike_timbre = BIND(STRIKE_TIMBRE_PARAM, STRIKE_TIMBRE_MOD_PARAM, STRIKE_TIMBRE_MOD_INPUT); + p->resonator_geometry = BIND(GEOMETRY_PARAM, GEOMETRY_MOD_PARAM, GEOMETRY_MOD_INPUT); + p->resonator_brightness = BIND(BRIGHTNESS_PARAM, BRIGHTNESS_MOD_PARAM, BRIGHTNESS_MOD_INPUT); + p->resonator_damping = BIND(DAMPING_PARAM, DAMPING_MOD_PARAM, DAMPING_MOD_INPUT); + p->resonator_position = BIND(POSITION_PARAM, POSITION_MOD_PARAM, POSITION_MOD_INPUT); + p->space = clampf(params[SPACE_PARAM] + params[SPACE_MOD_PARAM]*getf(inputs[SPACE_MOD_INPUT])/5.0, 0.0, 2.0); + + // Get performance inputs + elements::PerformanceState performance; + performance.note = 12.0*getf(inputs[NOTE_INPUT]) + roundf(params[COARSE_PARAM]) + 3.3*quadraticBipolar(params[FINE_PARAM]) + 69.0; + performance.modulation = 3.3*quarticBipolar(params[FM_PARAM]) * 49.5 * getf(inputs[FM_INPUT])/5.0; + performance.gate = params[PLAY_PARAM] >= 1.0 || getf(inputs[GATE_INPUT]) >= 1.0; + performance.strength = clampf(1.0 - getf(inputs[STRENGTH_INPUT])/5.0, 0.0, 1.0); + + // Generate audio + part->Process(performance, blow, strike, main, aux, 16); + + // Set lights + lights[0] = part->exciter_level(); + lights[1] = part->resonator_level(); + } +} + + +ElementsWidget::ElementsWidget() : ModuleWidget(new Elements()) { + box.size = Vec(15*34, 380); + + { + AudiblePanel *panel = new AudiblePanel(); + panel->imageFilename = "plugins/AudibleInstruments/res/Elements.png"; + panel->box.size = box.size; + addChild(panel); + } + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(480, 0))); + addChild(createScrew(Vec(15, 365))); + addChild(createScrew(Vec(480, 365))); + + addParam(createParam(Vec(49-20, 63-20), module, Elements::CONTOUR_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(120-20, 63-20), module, Elements::BOW_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(190-20, 63-20), module, Elements::BLOW_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(260-20, 63-20), module, Elements::STRIKE_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(331-20, 63-20), module, Elements::COARSE_PARAM, -30.0, 30.0, 0.0)); + addParam(createParam(Vec(402-20, 63-20), module, Elements::FINE_PARAM, -2.0, 2.0, 0.0)); + addParam(createParam(Vec(472-20, 63-20), module, Elements::FM_PARAM, -1.0, 1.0, 0.0)); + + addParam(createParam(Vec(142-26, 143-26), module, Elements::FLOW_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(239-26, 143-26), module, Elements::MALLET_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(353-26, 143-26), module, Elements::GEOMETRY_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(450-26, 143-26), module, Elements::BRIGHTNESS_PARAM, 0.0, 1.0, 0.5)); + + addParam(createParam(Vec(120-20, 223-20), module, Elements::BOW_TIMBRE_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(191-20, 223-20), module, Elements::BLOW_TIMBRE_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(260-20, 223-20), module, Elements::STRIKE_TIMBRE_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(331-20, 223-20), module, Elements::DAMPING_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(401-20, 223-20), module, Elements::POSITION_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(472-20, 223-20), module, Elements::SPACE_PARAM, 0.0, 2.0, 0.0)); + + addParam(createParam(Vec(113-9, 283-9), module, Elements::BOW_TIMBRE_MOD_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(152-9, 283-9), module, Elements::FLOW_MOD_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(190-9, 283-9), module, Elements::BLOW_TIMBRE_MOD_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(229-9, 283-9), module, Elements::MALLET_MOD_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(267-9, 283-9), module, Elements::STRIKE_TIMBRE_MOD_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(325-9, 283-9), module, Elements::DAMPING_MOD_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(363-9, 283-9), module, Elements::GEOMETRY_MOD_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(402-9, 283-9), module, Elements::POSITION_MOD_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(440-9, 283-9), module, Elements::BRIGHTNESS_MOD_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(479-9, 283-9), module, Elements::SPACE_MOD_PARAM, -2.0, 2.0, 0.0)); + + addInput(createInput(Vec(32-10, 190-10), module, Elements::NOTE_INPUT)); + addInput(createInput(Vec(68-10, 190-10), module, Elements::FM_INPUT)); + + addInput(createInput(Vec(32-10, 236-10), module, Elements::GATE_INPUT)); + addInput(createInput(Vec(68-10, 236-10), module, Elements::STRENGTH_INPUT)); + + addInput(createInput(Vec(32-10, 282-10), module, Elements::BLOW_INPUT)); + addInput(createInput(Vec(68-10, 282-10), module, Elements::STRIKE_INPUT)); + + addOutput(createOutput(Vec(32-10, 328-10), module, Elements::AUX_OUTPUT)); + addOutput(createOutput(Vec(68-10, 328-10), module, Elements::MAIN_OUTPUT)); + + addInput(createInput(Vec(113-10, 328-10), module, Elements::BOW_TIMBRE_MOD_INPUT)); + addInput(createInput(Vec(152-10, 328-10), module, Elements::FLOW_MOD_INPUT)); + addInput(createInput(Vec(190-10, 328-10), module, Elements::BLOW_TIMBRE_MOD_INPUT)); + addInput(createInput(Vec(229-10, 328-10), module, Elements::MALLET_MOD_INPUT)); + addInput(createInput(Vec(267-10, 328-10), module, Elements::STRIKE_TIMBRE_MOD_INPUT)); + addInput(createInput(Vec(325-10, 328-10), module, Elements::DAMPING_MOD_INPUT)); + addInput(createInput(Vec(363-10, 328-10), module, Elements::GEOMETRY_MOD_INPUT)); + addInput(createInput(Vec(402-10, 328-10), module, Elements::POSITION_MOD_INPUT)); + addInput(createInput(Vec(440-10, 328-10), module, Elements::BRIGHTNESS_MOD_INPUT)); + addInput(createInput(Vec(479-10, 328-10), module, Elements::SPACE_MOD_INPUT)); + // addInputPort(createInputPort(Vec(479-10, 328-10), module, Elements::SPACE_MOD_INPUT)); + + addParam(createParam(Vec(36, 116), module, Elements::PLAY_PARAM, 0.0, 2.0, 0.0)); + + Elements *elements = dynamic_cast(module); + addChild(createValueLight(Vec(184, 168), &elements->lights[0])); + addChild(createValueLight(Vec(395, 168), &elements->lights[1])); +} diff --git a/src/Kinks.cpp b/src/Kinks.cpp new file mode 100644 index 0000000..f24c30a --- /dev/null +++ b/src/Kinks.cpp @@ -0,0 +1,121 @@ +#include "AudibleInstruments.hpp" + + +// If the trigger input is rising at a rate of at least DTRIG, the S&H will be triggered +#define DTRIG 5000.0 + + +struct Kinks : Module { + enum ParamIds { + NUM_PARAMS + }; + enum InputIds { + SIGN_INPUT, + LOGIC_A_INPUT, + LOGIC_B_INPUT, + SH_INPUT, + TRIG_INPUT, + NUM_INPUTS + }; + enum OutputIds { + INVERT_OUTPUT, + HALF_RECTIFY_OUTPUT, + FULL_RECTIFY_OUTPUT, + MAX_OUTPUT, + MIN_OUTPUT, + NOISE_OUTPUT, + SH_OUTPUT, + NUM_OUTPUTS + }; + + std::normal_distribution dist; + float lastTrig = 0.0; + float sample = 0.0; + float lights[3] = {}; + + Kinks(); + void step(); +}; + + +Kinks::Kinks() : dist(0.0, 1.0) { + params.resize(NUM_PARAMS); + inputs.resize(NUM_INPUTS); + outputs.resize(NUM_OUTPUTS); +} + +void Kinks::step() { + // Gaussian noise generator + float noise = 2.0 * dist(rng); + + // S&H + float trig = getf(inputs[TRIG_INPUT]); + float dtrig = (trig - lastTrig) * SAMPLE_RATE; + if (dtrig > DTRIG) { + sample = getf(inputs[SH_INPUT], noise); + } + lastTrig = trig; + + // lights + lights[0] = getf(inputs[SIGN_INPUT]); + lights[1] = getf(inputs[LOGIC_A_INPUT]) + getf(inputs[LOGIC_B_INPUT]); + lights[2] = sample; + + // outputs + if (outputs[INVERT_OUTPUT]) { + *outputs[INVERT_OUTPUT] = -getf(inputs[SIGN_INPUT]); + } + if (outputs[HALF_RECTIFY_OUTPUT]) { + *outputs[HALF_RECTIFY_OUTPUT] = fmaxf(0.0, getf(inputs[SIGN_INPUT])); + } + if (outputs[FULL_RECTIFY_OUTPUT]) { + *outputs[FULL_RECTIFY_OUTPUT] = fabsf(getf(inputs[SIGN_INPUT])); + } + if (outputs[MAX_OUTPUT]) { + *outputs[MAX_OUTPUT] = fmaxf(getf(inputs[LOGIC_A_INPUT]), getf(inputs[LOGIC_B_INPUT])); + } + if (outputs[MIN_OUTPUT]) { + *outputs[MIN_OUTPUT] = fminf(getf(inputs[LOGIC_A_INPUT]), getf(inputs[LOGIC_B_INPUT])); + } + if (outputs[NOISE_OUTPUT]) { + *outputs[NOISE_OUTPUT] = noise; + } + if (outputs[SH_OUTPUT]) { + *outputs[SH_OUTPUT] = sample; + } +} + + +KinksWidget::KinksWidget() : ModuleWidget(new Kinks()) { + box.size = Vec(15*4, 380); + + { + AudiblePanel *panel = new AudiblePanel(); + panel->imageFilename = "plugins/AudibleInstruments/res/Kinks.png"; + panel->box.size = box.size; + addChild(panel); + } + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(15, 365))); + + addInput(createInput(Vec(6-1, 78-1), module, Kinks::SIGN_INPUT)); + addOutput(createOutput(Vec(34-1, 78-1), module, Kinks::INVERT_OUTPUT)); + addOutput(createOutput(Vec(6-1, 116-1), module, Kinks::HALF_RECTIFY_OUTPUT)); + addOutput(createOutput(Vec(34-1, 116-1), module, Kinks::FULL_RECTIFY_OUTPUT)); + + addInput(createInput(Vec(6-1, 180-1), module, Kinks::LOGIC_A_INPUT)); + addInput(createInput(Vec(34-1, 180-1), module, Kinks::LOGIC_B_INPUT)); + addOutput(createOutput(Vec(6-1, 217-1), module, Kinks::MAX_OUTPUT)); + addOutput(createOutput(Vec(34-1, 217-1), module, Kinks::MIN_OUTPUT)); + + addInput(createInput(Vec(6-1, 281-1), module, Kinks::SH_INPUT)); + addInput(createInput(Vec(34-1, 281-1), module, Kinks::TRIG_INPUT)); + addOutput(createOutput(Vec(6-1, 319-1), module, Kinks::NOISE_OUTPUT)); + addOutput(createOutput(Vec(34-1, 319-1), module, Kinks::SH_OUTPUT)); + + Kinks *kinks = dynamic_cast(module); + addChild(createValueLight(Vec(12, 61), &kinks->lights[0])); + addChild(createValueLight(Vec(12, 164), &kinks->lights[1])); + addChild(createValueLight(Vec(12, 264), &kinks->lights[2])); +} diff --git a/src/Links.cpp b/src/Links.cpp new file mode 100644 index 0000000..c1b5564 --- /dev/null +++ b/src/Links.cpp @@ -0,0 +1,89 @@ +#include "AudibleInstruments.hpp" + + +struct Links : Module { + enum ParamIds { + NUM_PARAMS + }; + enum InputIds { + A1_INPUT, + B1_INPUT, + B2_INPUT, + C1_INPUT, + C2_INPUT, + C3_INPUT, + NUM_INPUTS + }; + enum OutputIds { + A1_OUTPUT, + A2_OUTPUT, + A3_OUTPUT, + B1_OUTPUT, + B2_OUTPUT, + C1_OUTPUT, + NUM_OUTPUTS + }; + + float lights[3] = {}; + Links(); + void step(); +}; + + +Links::Links() { + params.resize(NUM_PARAMS); + inputs.resize(NUM_INPUTS); + outputs.resize(NUM_OUTPUTS); +} + +void Links::step() { + float in1 = getf(inputs[A1_INPUT]); + float in2 = getf(inputs[B1_INPUT]) + getf(inputs[B2_INPUT]); + float in3 = getf(inputs[C1_INPUT]) + getf(inputs[C2_INPUT]) + getf(inputs[C3_INPUT]); + + setf(outputs[A1_OUTPUT], in1); + setf(outputs[A2_OUTPUT], in1); + setf(outputs[A3_OUTPUT], in1); + setf(outputs[B1_OUTPUT], in2); + setf(outputs[B2_OUTPUT], in2); + setf(outputs[C1_OUTPUT], in3); + + lights[0] = in1; + lights[1] = in2; + lights[2] = in3; +} + + +LinksWidget::LinksWidget() : ModuleWidget(new Links()) { + box.size = Vec(15*4, 380); + + { + AudiblePanel *panel = new AudiblePanel(); + panel->imageFilename = "plugins/AudibleInstruments/res/Links.png"; + panel->box.size = box.size; + addChild(panel); + } + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(15, 365))); + + addInput(createInput(Vec(6-1, 78-1), module, Links::A1_INPUT)); + addOutput(createOutput(Vec(34-1, 78-1), module, Links::A1_OUTPUT)); + addOutput(createOutput(Vec(6-1, 116-1), module, Links::A2_OUTPUT)); + addOutput(createOutput(Vec(34-1, 116-1), module, Links::A3_OUTPUT)); + + addInput(createInput(Vec(6-1, 180-1), module, Links::B1_INPUT)); + addInput(createInput(Vec(34-1, 180-1), module, Links::B2_INPUT)); + addOutput(createOutput(Vec(6-1, 217-1), module, Links::B1_OUTPUT)); + addOutput(createOutput(Vec(34-1, 217-1), module, Links::B2_OUTPUT)); + + addInput(createInput(Vec(6-1, 281-1), module, Links::C1_INPUT)); + addInput(createInput(Vec(34-1, 281-1), module, Links::C2_INPUT)); + addInput(createInput(Vec(6-1, 319-1), module, Links::C3_INPUT)); + addOutput(createOutput(Vec(34-1, 319-1), module, Links::C1_OUTPUT)); + + Links *links = dynamic_cast(module); + addChild(createValueLight(Vec(26, 61), &links->lights[0])); + addChild(createValueLight(Vec(26, 164), &links->lights[1])); + addChild(createValueLight(Vec(26, 264), &links->lights[2])); +} diff --git a/src/Rings.cpp b/src/Rings.cpp new file mode 100644 index 0000000..ff8c31f --- /dev/null +++ b/src/Rings.cpp @@ -0,0 +1,206 @@ +#include "AudibleInstruments.hpp" +#include +#include "rings/dsp/part.h" +#include "rings/dsp/strummer.h" +#include "rings/dsp/string_synth_part.h" + + +struct Rings : Module { + enum ParamIds { + POLYPHONY_PARAM, + RESONATOR_PARAM, + + FREQUENCY_PARAM, + STRUCTURE_PARAM, + BRIGHTNESS_PARAM, + DAMPING_PARAM, + POSITION_PARAM, + + BRIGHTNESS_MOD_PARAM, + FREQUENCY_MOD_PARAM, + DAMPING_MOD_PARAM, + STRUCTURE_MOD_PARAM, + POSITION_MOD_PARAM, + NUM_PARAMS + }; + enum InputIds { + BRIGHTNESS_MOD_INPUT, + FREQUENCY_MOD_INPUT, + DAMPING_MOD_INPUT, + STRUCTURE_MOD_INPUT, + POSITION_MOD_INPUT, + + STRUM_INPUT, + PITCH_INPUT, + IN_INPUT, + NUM_INPUTS + }; + enum OutputIds { + ODD_OUTPUT, + EVEN_OUTPUT, + NUM_OUTPUTS + }; + + uint16_t reverb_buffer[32768] = {}; + int bufferFrame = 0; + float in[24] = {}; + float out[24] = {}; + float aux[24] = {}; + rings::Part part; + rings::StringSynthPart string_synth; + rings::Strummer strummer; + bool strum = false; + bool lastStrum = false; + + Rings(); + ~Rings(); + void step(); +}; + + +Rings::Rings() { + params.resize(NUM_PARAMS); + inputs.resize(NUM_INPUTS); + outputs.resize(NUM_OUTPUTS); + + memset(&strummer, 0, sizeof(strummer)); + memset(&part, 0, sizeof(part)); + memset(&string_synth, 0, sizeof(string_synth)); + + strummer.Init(0.01, 44100.0 / 24); + part.Init(reverb_buffer); + string_synth.Init(reverb_buffer); +} + +Rings::~Rings() { +} + +void Rings::step() { + // TODO Sample rate conversion from 48000 Hz + // TODO + // "Normalized to a pulse/burst generator that reacts to note changes on the V/OCT input." + in[bufferFrame] = clampf(getf(inputs[IN_INPUT])/5.0, -1.0, 1.0); + // "Note that you need to insert a jack into each output to split the signals: when only one jack is inserted, both signals are mixed together." + if (outputs[ODD_OUTPUT] && outputs[EVEN_OUTPUT]) { + setf(outputs[ODD_OUTPUT], clampf(out[bufferFrame], -1.0, 1.0)*5.0); + setf(outputs[EVEN_OUTPUT], clampf(aux[bufferFrame], -1.0, 1.0)*5.0); + } + else { + float v = clampf(out[bufferFrame] + aux[bufferFrame], -1.0, 1.0)*5.0; + setf(outputs[ODD_OUTPUT], v); + setf(outputs[EVEN_OUTPUT], v); + } + + if (!strum) { + strum = getf(inputs[STRUM_INPUT]) >= 1.0; + } + + if (++bufferFrame >= 24) { + bufferFrame = 0; + + // modes + int polyphony = 1; + switch ((int) roundf(params[POLYPHONY_PARAM])) { + case 1: polyphony = 2; break; + case 2: polyphony = 4; break; + } + if (polyphony != part.polyphony()) { + part.set_polyphony(polyphony); + } + + int model = (int) roundf(params[RESONATOR_PARAM]); + if (0 <= model && model < rings::RESONATOR_MODEL_LAST) { + part.set_model((rings::ResonatorModel) model); + } + + // Patch + rings::Patch patch; + float structure = params[STRUCTURE_PARAM] + 3.3*quadraticBipolar(params[STRUCTURE_MOD_PARAM])*getf(inputs[STRUCTURE_MOD_INPUT])/5.0; + patch.structure = clampf(structure, 0.0, 0.9995); + patch.brightness = clampf(params[BRIGHTNESS_PARAM] + 3.3*quadraticBipolar(params[BRIGHTNESS_MOD_PARAM])*getf(inputs[BRIGHTNESS_MOD_INPUT])/5.0, 0.0, 1.0); + patch.damping = clampf(params[DAMPING_PARAM] + 3.3*quadraticBipolar(params[DAMPING_MOD_PARAM])*getf(inputs[DAMPING_MOD_INPUT])/5.0, 0.0, 0.9995); + patch.position = clampf(params[POSITION_PARAM] + 3.3*quadraticBipolar(params[POSITION_MOD_PARAM])*getf(inputs[POSITION_MOD_INPUT])/5.0, 0.0, 0.9995); + + // Performance + rings::PerformanceState performance_state; + performance_state.note = 12.0*getf(inputs[PITCH_INPUT], 1/12.0); + float transpose = params[FREQUENCY_PARAM]; + // Quantize transpose if pitch input is connected + if (inputs[PITCH_INPUT]) { + transpose = roundf(transpose); + } + performance_state.tonic = 12.0 + clampf(transpose, 0, 60.0); + performance_state.fm = clampf(48.0 * 3.3*quarticBipolar(params[FREQUENCY_MOD_PARAM]) * getf(inputs[FREQUENCY_MOD_INPUT], 1.0)/5.0, -48.0, 48.0); + + performance_state.internal_exciter = !inputs[IN_INPUT]; + performance_state.internal_strum = !inputs[STRUM_INPUT]; + performance_state.internal_note = !inputs[PITCH_INPUT]; + + // TODO + // "Normalized to a step detector on the V/OCT input and a transient detector on the IN input." + performance_state.strum = strum && !lastStrum; + lastStrum = strum; + strum = false; + + performance_state.chord = clampf(roundf(structure * (rings::kNumChords - 1)), 0, rings::kNumChords - 1); + + // Process audio + if (0) { + // strummer.Process(NULL, 24, &performance_state); + // string_synth.Process(performance_state, patch, in, out, aux, 24); + } + else { + strummer.Process(in, 24, &performance_state); + part.Process(performance_state, patch, in, out, aux, 24); + } + } +} + + +RingsWidget::RingsWidget() : ModuleWidget(new Rings()) { + box.size = Vec(15*14, 380); + + { + AudiblePanel *panel = new AudiblePanel(); + panel->imageFilename = "plugins/AudibleInstruments/res/Rings.png"; + panel->box.size = box.size; + addChild(panel); + } + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(180, 0))); + addChild(createScrew(Vec(15, 365))); + addChild(createScrew(Vec(180, 365))); + + addParam(createParam(Vec(14, 40), module, Rings::POLYPHONY_PARAM, 0.0, 2.0, 0.0)); + addParam(createParam(Vec(179, 40), module, Rings::RESONATOR_PARAM, 0.0, 2.0, 0.0)); + + addParam(createParam(Vec(30, 73), module, Rings::FREQUENCY_PARAM, 0.0, 60.0, 30.0)); + addParam(createParam(Vec(127, 73), module, Rings::STRUCTURE_PARAM, 0.0, 1.0, 0.5)); + + addParam(createParam(Vec(14, 159), module, Rings::BRIGHTNESS_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(84, 159), module, Rings::DAMPING_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(155, 159), module, Rings::POSITION_PARAM, 0.0, 1.0, 0.5)); + + addParam(createParam(Vec(19, 229), module, Rings::BRIGHTNESS_MOD_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(57, 229), module, Rings::FREQUENCY_MOD_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(95, 229), module, Rings::DAMPING_MOD_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(134, 229), module, Rings::STRUCTURE_MOD_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(172, 229), module, Rings::POSITION_MOD_PARAM, -1.0, 1.0, 0.0)); + + addInput(createInput(Vec(18, 275), module, Rings::BRIGHTNESS_MOD_INPUT)); + addInput(createInput(Vec(56, 275), module, Rings::FREQUENCY_MOD_INPUT)); + addInput(createInput(Vec(95, 275), module, Rings::DAMPING_MOD_INPUT)); + addInput(createInput(Vec(133, 275), module, Rings::STRUCTURE_MOD_INPUT)); + addInput(createInput(Vec(171, 275), module, Rings::POSITION_MOD_INPUT)); + + addInput(createInput(Vec(18, 318), module, Rings::STRUM_INPUT)); + addInput(createInput(Vec(56, 318), module, Rings::PITCH_INPUT)); + addInput(createInput(Vec(95, 318), module, Rings::IN_INPUT)); + addOutput(createOutput(Vec(133, 318), module, Rings::ODD_OUTPUT)); + addOutput(createOutput(Vec(171, 318), module, Rings::EVEN_OUTPUT)); + + Rings *rings = dynamic_cast(module); + addChild(createValueLight(Vec(39, 45), &rings->params[Rings::POLYPHONY_PARAM])); + addChild(createValueLight(Vec(164, 45), &rings->params[Rings::RESONATOR_PARAM])); +} diff --git a/src/Shades.cpp b/src/Shades.cpp new file mode 100644 index 0000000..ecd9757 --- /dev/null +++ b/src/Shades.cpp @@ -0,0 +1,111 @@ +#include "AudibleInstruments.hpp" +#include + + +struct Shades : Module { + enum ParamIds { + GAIN1_PARAM, + GAIN2_PARAM, + GAIN3_PARAM, + MODE1_PARAM, + MODE2_PARAM, + MODE3_PARAM, + NUM_PARAMS + }; + enum InputIds { + IN1_INPUT, + IN2_INPUT, + IN3_INPUT, + NUM_INPUTS + }; + enum OutputIds { + OUT1_OUTPUT, + OUT2_OUTPUT, + OUT3_OUTPUT, + NUM_OUTPUTS + }; + + float lights[3] = {}; + + Shades(); + void step(); +}; + + +Shades::Shades() { + params.resize(NUM_PARAMS); + inputs.resize(NUM_INPUTS); + outputs.resize(NUM_OUTPUTS); +} + +static float getChannelOutput(const float *in, float gain, float mode) { + float out = getf(in, 5.0); + if (mode < 0.5) { + // attenuverter + out *= 2.0*gain - 1.0; + } + else { + // attenuator + out *= gain; + } + return out; +} + +void Shades::step() { + float out = 0.0; + out += getChannelOutput(inputs[IN1_INPUT], params[GAIN1_PARAM], params[MODE1_PARAM]); + lights[0] = out; + if (outputs[OUT1_OUTPUT]) { + *outputs[OUT1_OUTPUT] = out; + out = 0.0; + } + + out += getChannelOutput(inputs[IN2_INPUT], params[GAIN2_PARAM], params[MODE2_PARAM]); + lights[1] = out; + if (outputs[OUT2_OUTPUT]) { + *outputs[OUT2_OUTPUT] = out; + out = 0.0; + } + + out += getChannelOutput(inputs[IN3_INPUT], params[GAIN3_PARAM], params[MODE3_PARAM]); + lights[2] = out; + if (outputs[OUT3_OUTPUT]) { + *outputs[OUT3_OUTPUT] = out; + } +} + + +ShadesWidget::ShadesWidget() : ModuleWidget(new Shades()) { + box.size = Vec(15*6, 380); + + { + AudiblePanel *panel = new AudiblePanel(); + panel->imageFilename = "plugins/AudibleInstruments/res/Shades.png"; + panel->box.size = box.size; + addChild(panel); + } + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(15, 365))); + + addParam(createParam(Vec(40, 41), module, Shades::GAIN1_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(40, 107), module, Shades::GAIN2_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(40, 173), module, Shades::GAIN3_PARAM, 0.0, 1.0, 0.5)); + + addParam(createParam(Vec(11, 52), module, Shades::MODE1_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(Vec(11, 118), module, Shades::MODE2_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(Vec(11, 184), module, Shades::MODE3_PARAM, 0.0, 1.0, 0.0)); + + addInput(createInput(Vec(11, 247), module, Shades::IN1_INPUT)); + addInput(createInput(Vec(11, 283), module, Shades::IN2_INPUT)); + addInput(createInput(Vec(11, 319), module, Shades::IN3_INPUT)); + + addOutput(createOutput(Vec(58, 247), module, Shades::OUT1_OUTPUT)); + addOutput(createOutput(Vec(58, 283), module, Shades::OUT2_OUTPUT)); + addOutput(createOutput(Vec(58, 319), module, Shades::OUT3_OUTPUT)); + + Shades *shades = dynamic_cast(module); + addChild(createValueLight(Vec(42, 256), &shades->lights[0])); + addChild(createValueLight(Vec(42, 292), &shades->lights[1])); + addChild(createValueLight(Vec(42, 328), &shades->lights[2])); +} diff --git a/src/Streams.cpp b/src/Streams.cpp new file mode 100644 index 0000000..341a319 --- /dev/null +++ b/src/Streams.cpp @@ -0,0 +1,83 @@ +#include +#include "AudibleInstruments.hpp" + + +struct Streams : Module { + enum ParamIds { + SHAPE1_PARAM, + MOD1_PARAM, + LEVEL1_PARAM, + RESPONSE1_PARAM, + + SHAPE2_PARAM, + MOD2_PARAM, + LEVEL2_PARAM, + RESPONSE2_PARAM, + NUM_PARAMS + }; + enum InputIds { + EXCITE1_INPUT, + IN1_INPUT, + LEVEL1_INPUT, + + EXCITE2_INPUT, + IN2_INPUT, + LEVEL2_INPUT, + NUM_INPUTS + }; + enum OutputIds { + OUT1_OUTPUT, + OUT2_OUTPUT, + NUM_OUTPUTS + }; + + Streams(); + void step(); +}; + + +Streams::Streams() { + params.resize(NUM_PARAMS); + inputs.resize(NUM_INPUTS); + outputs.resize(NUM_OUTPUTS); +} + +void Streams::step() { +} + + +StreamsWidget::StreamsWidget() : ModuleWidget(new Streams()) { + box.size = Vec(15*12, 380); + + { + AudiblePanel *panel = new AudiblePanel(); + panel->imageFilename = "plugins/AudibleInstruments/res/Streams.png"; + panel->box.size = box.size; + addChild(panel); + } + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(150, 0))); + addChild(createScrew(Vec(15, 365))); + addChild(createScrew(Vec(150, 365))); + + // addParam(createParam(Vec(30, 53), module, Streams::ALGORITHM_PARAM, 0.0, 8.0, 0.0)); + + // addParam(createParam(Vec(95, 173), module, Streams::TIMBRE_PARAM, 0.0, 1.0, 0.5)); + // addParam(createParam(Vec(17, 182), module, Streams::STATE_PARAM, 0.0, 3.0, 0.0)); + // addParam(createParam(Vec(15, 214), module, Streams::LEVEL1_PARAM, 0.0, 1.0, 1.0)); + // addParam(createParam(Vec(53, 214), module, Streams::LEVEL2_PARAM, 0.0, 1.0, 1.0)); + + // addInput(createInput(Vec(11, 275), module, Streams::LEVEL1_INPUT)); + // addInput(createInput(Vec(47, 275), module, Streams::LEVEL2_INPUT)); + // addInput(createInput(Vec(83, 275), module, Streams::ALGORITHM_INPUT)); + // addInput(createInput(Vec(119, 275), module, Streams::TIMBRE_INPUT)); + + // addInput(createInput(Vec(11, 318), module, Streams::CARRIER_INPUT)); + // addInput(createInput(Vec(47, 318), module, Streams::MODULATOR_INPUT)); + // addOutput(createOutput(Vec(83, 318), module, Streams::MODULATOR_OUTPUT)); + // addOutput(createOutput(Vec(119, 318), module, Streams::AUX_OUTPUT)); + + // Streams *streams = dynamic_cast(module); + // addChild(createValueLight(Vec(21, 168), &streams->lights[0])); +} diff --git a/src/Tides.cpp b/src/Tides.cpp new file mode 100644 index 0000000..854f860 --- /dev/null +++ b/src/Tides.cpp @@ -0,0 +1,187 @@ +#include "AudibleInstruments.hpp" +#include +#include "tides/generator.h" + + +struct Tides : Module { + enum ParamIds { + MODE_PARAM, + RANGE_PARAM, + + FREQUENCY_PARAM, + FM_PARAM, + + SHAPE_PARAM, + SLOPE_PARAM, + SMOOTHNESS_PARAM, + NUM_PARAMS + }; + enum InputIds { + SHAPE_INPUT, + SLOPE_INPUT, + SMOOTHNESS_INPUT, + + TRIG_INPUT, + FREEZE_INPUT, + PITCH_INPUT, + FM_INPUT, + LEVEL_INPUT, + + CLOCK_INPUT, + NUM_INPUTS + }; + enum OutputIds { + HIGH_OUTPUT, + LOW_OUTPUT, + UNI_OUTPUT, + BI_OUTPUT, + NUM_OUTPUTS + }; + + tides::Generator generator; + float lights[3] = {}; + int frame = 0; + uint8_t lastGate; + + Tides(); + void step(); +}; + + +Tides::Tides() { + params.resize(NUM_PARAMS); + inputs.resize(NUM_INPUTS); + outputs.resize(NUM_OUTPUTS); + + memset(&generator, 0, sizeof(generator)); + generator.Init(); + generator.set_range(tides::GENERATOR_RANGE_LOW); + generator.set_mode(tides::GENERATOR_MODE_AD); + generator.set_sync(false); +} + +void Tides::step() { + if (++frame >= 16) { + frame = 0; + + // Mode and range + tides::GeneratorMode mode = (tides::GeneratorMode) roundf(1.0 - params[MODE_PARAM]); + if (mode != generator.mode()) { + generator.set_mode(mode); + } + lights[0] = -params[MODE_PARAM]; + + tides::GeneratorRange range = (tides::GeneratorRange) roundf(1.0 - params[RANGE_PARAM]); + if (range != generator.range()) { + generator.set_range(range); + } + lights[2] = -params[RANGE_PARAM]; + + // Pitch + float pitch = params[FREQUENCY_PARAM]; + pitch += 12.0*getf(inputs[PITCH_INPUT]); + pitch += params[FM_PARAM] * getf(inputs[FM_INPUT], 0.1) / 5.0; + pitch += 60.0; + generator.set_pitch(clampf(pitch*0x80, -0x8000, 0x7fff)); + + // Slope, smoothness, pitch + int16_t shape = clampf(params[SHAPE_PARAM] + getf(inputs[SHAPE_INPUT]) / 5.0, -1.0, 1.0) * 0x7fff; + int16_t slope = clampf(params[SLOPE_PARAM] + getf(inputs[SLOPE_INPUT]) / 5.0, -1.0, 1.0) * 0x7fff; + int16_t smoothness = clampf(params[SMOOTHNESS_PARAM] + getf(inputs[SMOOTHNESS_INPUT]) / 5.0, -1.0, 1.0) * 0x7fff; + generator.set_shape(shape); + generator.set_slope(slope); + generator.set_smoothness(smoothness); + + // Sync + // Slight deviation from spec here. + // Instead of toggling sync by holding the range button, just enable it if the clock port is plugged in. + generator.set_sync(inputs[CLOCK_INPUT]); + + // Generator + generator.Process(); + } + + // Level + uint16_t level = clampf(getf(inputs[LEVEL_INPUT], 8.0) / 8.0, 0.0, 1.0) * 0xffff; + if (level < 32) + level = 0; + + uint8_t gate = 0; + if (getf(inputs[FREEZE_INPUT]) >= 0.7) + gate |= tides::CONTROL_FREEZE; + if (getf(inputs[TRIG_INPUT]) >= 0.7) + gate |= tides::CONTROL_GATE; + if (getf(inputs[CLOCK_INPUT]) >= 0.7) + gate |= tides::CONTROL_CLOCK; + if (!(lastGate & tides::CONTROL_CLOCK) && (gate & tides::CONTROL_CLOCK)) + gate |= tides::CONTROL_GATE_RISING; + if (!(lastGate & tides::CONTROL_GATE) && (gate & tides::CONTROL_GATE)) + gate |= tides::CONTROL_GATE_RISING; + if ((lastGate & tides::CONTROL_GATE) && !(gate & tides::CONTROL_GATE)) + gate |= tides::CONTROL_GATE_FALLING; + lastGate = gate; + + const tides::GeneratorSample& sample = generator.Process(gate); + uint32_t uni = sample.unipolar; + int32_t bi = sample.bipolar; + + uni = uni * level >> 16; + bi = -bi * level >> 16; + float unif = mapf(uni, 0, 0xffff, 0.0, 8.0); + float bif = mapf(bi, -0x8000, 0x7fff, -5.0, 5.0); + + setf(outputs[HIGH_OUTPUT], sample.flags & tides::FLAG_END_OF_ATTACK ? 0.0 : 5.0); + setf(outputs[LOW_OUTPUT], sample.flags & tides::FLAG_END_OF_RELEASE ? 0.0 : 5.0); + setf(outputs[UNI_OUTPUT], unif); + setf(outputs[BI_OUTPUT], bif); + + lights[1] = sample.flags & tides::FLAG_END_OF_ATTACK ? -unif : unif; +} + + +TidesWidget::TidesWidget() : ModuleWidget(new Tides()) { + box.size = Vec(15*14, 380); + + { + AudiblePanel *panel = new AudiblePanel(); + panel->imageFilename = "plugins/AudibleInstruments/res/Tides.png"; + panel->box.size = box.size; + addChild(panel); + } + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(180, 0))); + addChild(createScrew(Vec(15, 365))); + addChild(createScrew(Vec(180, 365))); + + addParam(createParam(Vec(19, 52), module, Tides::MODE_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(19, 93), module, Tides::RANGE_PARAM, -1.0, 1.0, 0.0)); + + addParam(createParam(Vec(79, 60), module, Tides::FREQUENCY_PARAM, -48.0, 48.0, 0.0)); + addParam(createParam(Vec(156, 66), module, Tides::FM_PARAM, -12.0, 12.0, 0.0)); + + addParam(createParam(Vec(13, 155), module, Tides::SHAPE_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(85, 155), module, Tides::SLOPE_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(156, 155), module, Tides::SMOOTHNESS_PARAM, -1.0, 1.0, 0.0)); + + addInput(createInput(Vec(23, 221), module, Tides::SHAPE_INPUT)); + addInput(createInput(Vec(95, 221), module, Tides::SLOPE_INPUT)); + addInput(createInput(Vec(166, 221), module, Tides::SMOOTHNESS_INPUT)); + + addInput(createInput(Vec(23, 275), module, Tides::TRIG_INPUT)); + addInput(createInput(Vec(59, 275), module, Tides::FREEZE_INPUT)); + addInput(createInput(Vec(95, 275), module, Tides::PITCH_INPUT)); + addInput(createInput(Vec(130, 275), module, Tides::FM_INPUT)); + addInput(createInput(Vec(166, 275), module, Tides::LEVEL_INPUT)); + + addInput(createInput(Vec(23, 318), module, Tides::CLOCK_INPUT)); + addOutput(createOutput(Vec(59, 318), module, Tides::HIGH_OUTPUT)); + addOutput(createOutput(Vec(95, 318), module, Tides::LOW_OUTPUT)); + addOutput(createOutput(Vec(130, 318), module, Tides::UNI_OUTPUT)); + addOutput(createOutput(Vec(166, 318), module, Tides::BI_OUTPUT)); + + Tides *tides = dynamic_cast(module); + addChild(createValueLight(Vec(58, 63), &tides->lights[0])); + addChild(createValueLight(Vec(58, 84), &tides->lights[1])); + addChild(createValueLight(Vec(58, 105), &tides->lights[2])); +} diff --git a/src/Veils.cpp b/src/Veils.cpp new file mode 100644 index 0000000..44d891e --- /dev/null +++ b/src/Veils.cpp @@ -0,0 +1,141 @@ +#include "AudibleInstruments.hpp" +#include + + +struct Veils : Module { + enum ParamIds { + GAIN1_PARAM, + GAIN2_PARAM, + GAIN3_PARAM, + GAIN4_PARAM, + RESPONSE1_PARAM, + RESPONSE2_PARAM, + RESPONSE3_PARAM, + RESPONSE4_PARAM, + NUM_PARAMS + }; + enum InputIds { + IN1_INPUT, + IN2_INPUT, + IN3_INPUT, + IN4_INPUT, + CV1_INPUT, + CV2_INPUT, + CV3_INPUT, + CV4_INPUT, + NUM_INPUTS + }; + enum OutputIds { + OUT1_OUTPUT, + OUT2_OUTPUT, + OUT3_OUTPUT, + OUT4_OUTPUT, + NUM_OUTPUTS + }; + + float lights[4] = {}; + + Veils(); + void step(); +}; + + +Veils::Veils() { + params.resize(NUM_PARAMS); + inputs.resize(NUM_INPUTS); + outputs.resize(NUM_OUTPUTS); +} + +static float getChannelOutput(float *in, float gain, float *cv, float response) { + float out = getf(in) * gain; + if (out == 0.0) + return 0.0; + + if (cv) { + float linear = fmaxf(getf(cv) / 5.0, 0.0); + if (linear == 0.0) + return 0.0; + const float ex = 200.0; + float exponential = (powf(ex, linear) - 1.0) / (ex - 1.0); + out *= crossf(exponential, linear, response); + } + return out; +} + +void Veils::step() { + float out = 0.0; + out += getChannelOutput(inputs[IN1_INPUT], params[GAIN1_PARAM], inputs[CV1_INPUT], params[RESPONSE1_PARAM]); + lights[0] = out; + if (outputs[OUT1_OUTPUT]) { + *outputs[OUT1_OUTPUT] = out; + out = 0.0; + } + + out += getChannelOutput(inputs[IN2_INPUT], params[GAIN2_PARAM], inputs[CV2_INPUT], params[RESPONSE2_PARAM]); + lights[1] = out; + if (outputs[OUT2_OUTPUT]) { + *outputs[OUT2_OUTPUT] = out; + out = 0.0; + } + + out += getChannelOutput(inputs[IN3_INPUT], params[GAIN3_PARAM], inputs[CV3_INPUT], params[RESPONSE3_PARAM]); + lights[2] = out; + if (outputs[OUT3_OUTPUT]) { + *outputs[OUT3_OUTPUT] = out; + out = 0.0; + } + + out += getChannelOutput(inputs[IN4_INPUT], params[GAIN4_PARAM], inputs[CV4_INPUT], params[RESPONSE4_PARAM]); + lights[3] = out; + if (outputs[OUT4_OUTPUT]) { + *outputs[OUT4_OUTPUT] = out; + } +} + + +VeilsWidget::VeilsWidget() : ModuleWidget(new Veils()) { + box.size = Vec(15*12, 380); + + { + AudiblePanel *panel = new AudiblePanel(); + panel->imageFilename = "plugins/AudibleInstruments/res/Veils.png"; + panel->box.size = box.size; + addChild(panel); + } + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(150, 0))); + addChild(createScrew(Vec(15, 365))); + addChild(createScrew(Vec(150, 365))); + + addParam(createParam(Vec(8, 52), module, Veils::GAIN1_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(8, 131), module, Veils::GAIN2_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(8, 210), module, Veils::GAIN3_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(8, 288), module, Veils::GAIN4_PARAM, 0.0, 1.0, 0.5)); + + addParam(createParam(Vec(72, 56), module, Veils::RESPONSE1_PARAM, 0.0, 1.0, 1.0)); + addParam(createParam(Vec(72, 135), module, Veils::RESPONSE2_PARAM, 0.0, 1.0, 1.0)); + addParam(createParam(Vec(72, 214), module, Veils::RESPONSE3_PARAM, 0.0, 1.0, 1.0)); + addParam(createParam(Vec(72, 292), module, Veils::RESPONSE4_PARAM, 0.0, 1.0, 1.0)); + + addInput(createInput(Vec(112, 43), module, Veils::IN1_INPUT)); + addInput(createInput(Vec(112, 122), module, Veils::IN2_INPUT)); + addInput(createInput(Vec(112, 200), module, Veils::IN3_INPUT)); + addInput(createInput(Vec(112, 279), module, Veils::IN4_INPUT)); + + addInput(createInput(Vec(112, 82), module, Veils::CV1_INPUT)); + addInput(createInput(Vec(112, 161), module, Veils::CV2_INPUT)); + addInput(createInput(Vec(112, 240), module, Veils::CV3_INPUT)); + addInput(createInput(Vec(112, 318), module, Veils::CV4_INPUT)); + + addOutput(createOutput(Vec(146, 43), module, Veils::OUT1_OUTPUT)); + addOutput(createOutput(Vec(146, 122), module, Veils::OUT2_OUTPUT)); + addOutput(createOutput(Vec(146, 200), module, Veils::OUT3_OUTPUT)); + addOutput(createOutput(Vec(146, 279), module, Veils::OUT4_OUTPUT)); + + Veils *veils = dynamic_cast(module); + addChild(createValueLight(Vec(149, 86), &veils->lights[0])); + addChild(createValueLight(Vec(149, 165), &veils->lights[1])); + addChild(createValueLight(Vec(149, 244), &veils->lights[2])); + addChild(createValueLight(Vec(149, 323), &veils->lights[3])); +} diff --git a/src/Warps.cpp b/src/Warps.cpp new file mode 100644 index 0000000..af56079 --- /dev/null +++ b/src/Warps.cpp @@ -0,0 +1,112 @@ +#include +#include "AudibleInstruments.hpp" +#include "warps/dsp/modulator.h" + + +struct Warps : Module { + enum ParamIds { + ALGORITHM_PARAM, + TIMBRE_PARAM, + STATE_PARAM, + LEVEL1_PARAM, + LEVEL2_PARAM, + NUM_PARAMS + }; + enum InputIds { + LEVEL1_INPUT, + LEVEL2_INPUT, + ALGORITHM_INPUT, + TIMBRE_INPUT, + CARRIER_INPUT, + MODULATOR_INPUT, + NUM_INPUTS + }; + enum OutputIds { + MODULATOR_OUTPUT, + AUX_OUTPUT, + NUM_OUTPUTS + }; + + int frame = 0; + warps::Modulator modulator; + warps::ShortFrame inputFrames[60] = {}; + warps::ShortFrame outputFrames[60] = {}; + float lights[1] = {}; + + Warps(); + void step(); +}; + + +Warps::Warps() { + params.resize(NUM_PARAMS); + inputs.resize(NUM_INPUTS); + outputs.resize(NUM_OUTPUTS); + + memset(&modulator, 0, sizeof(modulator)); + modulator.Init(96000.0f); +} + +void Warps::step() { + if (++frame >= 60) { + frame = 0; + + warps::Parameters *p = modulator.mutable_parameters(); + p->channel_drive[0] = clampf(params[LEVEL1_PARAM] + getf(inputs[LEVEL1_INPUT]) / 5.0, 0.0, 1.0); + p->channel_drive[1] = clampf(params[LEVEL2_PARAM] + getf(inputs[LEVEL2_INPUT]) / 5.0, 0.0, 1.0); + p->modulation_algorithm = clampf(params[ALGORITHM_PARAM] / 8.0 + getf(inputs[ALGORITHM_PARAM]) / 5.0, 0.0, 1.0); + p->modulation_parameter = clampf(params[TIMBRE_PARAM] + getf(inputs[TIMBRE_INPUT]) / 5.0, 0.0, 1.0); + + p->frequency_shift_pot = params[ALGORITHM_PARAM] / 8.0; + p->frequency_shift_cv = clampf(getf(inputs[ALGORITHM_INPUT]) / 5.0, -1.0, 1.0); + p->phase_shift = p->modulation_algorithm; + p->note = 60.0 * params[LEVEL1_PARAM] + 12.0 * getf(inputs[LEVEL1_INPUT], 2.0) + 12.0; + float state = roundf(params[STATE_PARAM]); + p->carrier_shape = (int32_t)state; + lights[0] = state - 1.0; + + modulator.Process(inputFrames, outputFrames, 60); + } + + inputFrames[frame].l = clampf(getf(inputs[CARRIER_INPUT]) / 16.0 * 0x8000, -0x8000, 0x7fff); + inputFrames[frame].r = clampf(getf(inputs[MODULATOR_INPUT]) / 16.0 * 0x8000, -0x8000, 0x7fff); + setf(outputs[MODULATOR_OUTPUT], (float)outputFrames[frame].l / 0x8000 * 5.0); + setf(outputs[AUX_OUTPUT], (float)outputFrames[frame].r / 0x8000 * 5.0); +} + + +WarpsWidget::WarpsWidget() : ModuleWidget(new Warps()) { + box.size = Vec(15*10, 380); + + { + AudiblePanel *panel = new AudiblePanel(); + panel->imageFilename = "plugins/AudibleInstruments/res/Warps.png"; + panel->box.size = box.size; + addChild(panel); + } + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(120, 0))); + addChild(createScrew(Vec(15, 365))); + addChild(createScrew(Vec(120, 365))); + + addParam(createParam(Vec(30, 53), module, Warps::ALGORITHM_PARAM, 0.0, 8.0, 0.0)); + + addParam(createParam(Vec(95, 173), module, Warps::TIMBRE_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(17, 182), module, Warps::STATE_PARAM, 0.0, 3.0, 0.0)); + addParam(createParam(Vec(15, 214), module, Warps::LEVEL1_PARAM, 0.0, 1.0, 1.0)); + addParam(createParam(Vec(53, 214), module, Warps::LEVEL2_PARAM, 0.0, 1.0, 1.0)); + + addInput(createInput(Vec(11, 275), module, Warps::LEVEL1_INPUT)); + addInput(createInput(Vec(47, 275), module, Warps::LEVEL2_INPUT)); + addInput(createInput(Vec(83, 275), module, Warps::ALGORITHM_INPUT)); + addInput(createInput(Vec(119, 275), module, Warps::TIMBRE_INPUT)); + + addInput(createInput(Vec(11, 318), module, Warps::CARRIER_INPUT)); + addInput(createInput(Vec(47, 318), module, Warps::MODULATOR_INPUT)); + addOutput(createOutput(Vec(83, 318), module, Warps::MODULATOR_OUTPUT)); + addOutput(createOutput(Vec(119, 318), module, Warps::AUX_OUTPUT)); + + Warps *warps = dynamic_cast(module); + addChild(createValueLight(Vec(21, 168), &warps->lights[0])); +}