#include "AudibleInstruments.hpp" #include "dsp/digital.hpp" // #include "stages/chain_state.h" #include "stages/segment_generator.h" #include "stages/oscillator.h" // Must match io_buffer.h static const int NUM_CHANNELS = 6; static const int BLOCK_SIZE = 8; static const int MAX_MODULES = 6; struct SineOscillator { float phase = 0.f; float step(float offset) { // Implement a simple sine oscillator float deltaTime = engineGetSampleTime(); float freq = 0.5f; // Accumulate the phase phase += freq * deltaTime; if (phase >= 1.0f) phase -= 1.0f; // Compute the sine output float sine = sinf(2.0f * M_PI * (phase + offset)); return sine; } }; struct LongPressButton { enum Events { NO_PRESS, SHORT_PRESS, LONG_PRESS }; float pressedTime = 0.f; BooleanTrigger trigger; Events step(Param& param) { auto result = NO_PRESS; bool pressed = param.value > 0.f; if (pressed && pressedTime >= 0.f) { pressedTime += engineGetSampleTime(); if (pressedTime >= 1.f) { pressedTime = -1.f; result = LONG_PRESS; } } // Check if released if (trigger.process(!pressed)) { if (pressedTime >= 0.f) { result = SHORT_PRESS; } pressedTime = 0.f; } return result; } }; struct GroupBuilder { GroupBuilder() { for (size_t i = 0; i < NUM_CHANNELS*MAX_MODULES; i += 1) { groupSize[i] = 0; } } size_t groupSize[NUM_CHANNELS*MAX_MODULES]; bool isPatched = false; bool buildGroups(std::vector *inputs, size_t first, size_t count) { bool changed = false; isPatched = false; size_t activeGroup = 0; for (int i = count-1; i >= 0; i -= 1) { auto patched = (*inputs)[first + i].active; activeGroup += 1; if (!patched) { changed = changed || groupSize[i] != 0; groupSize[i] = 0; } else if (patched) { isPatched = true; changed = changed || groupSize[i] != activeGroup; groupSize[i] = activeGroup; activeGroup = 0; } } return changed; } int groupForSegment(int segment) { int group = 0; int currentGroupSize = 0; for (int i = 0; i < NUM_CHANNELS * MAX_MODULES; i += 1) { if (currentGroupSize <= 0) { currentGroupSize = max(1, groupSize[i]); group = i; } if (segment == i) { return group; } currentGroupSize -= 1; } return segment; } }; struct Stages : Module { enum ParamIds { ENUMS(SHAPE_PARAMS, NUM_CHANNELS*MAX_MODULES), ENUMS(TYPE_PARAMS, NUM_CHANNELS*MAX_MODULES), ENUMS(LEVEL_PARAMS, NUM_CHANNELS*MAX_MODULES), NUM_PARAMS }; enum InputIds { ENUMS(LEVEL_INPUTS, NUM_CHANNELS*MAX_MODULES), ENUMS(GATE_INPUTS, NUM_CHANNELS*MAX_MODULES), NUM_INPUTS }; enum OutputIds { ENUMS(ENVELOPE_OUTPUTS, NUM_CHANNELS*MAX_MODULES), NUM_OUTPUTS }; enum LightIds { ENUMS(TYPE_LIGHTS, NUM_CHANNELS*2*MAX_MODULES), ENUMS(ENVELOPE_LIGHTS, NUM_CHANNELS*MAX_MODULES), NUM_LIGHTS }; int numModules = 1; stages::segment::Configuration configurations[NUM_CHANNELS*MAX_MODULES]; bool configuration_changed[NUM_CHANNELS*MAX_MODULES]; stages::SegmentGenerator segment_generator[NUM_CHANNELS*MAX_MODULES]; SineOscillator oscillator[NUM_CHANNELS*MAX_MODULES]; bool abloop; // stages::ChainState chain_state; // stages::Settings settings; // Buttons LongPressButton typeButtons[NUM_CHANNELS*MAX_MODULES]; // Buffers float envelopeBuffer[NUM_CHANNELS*MAX_MODULES][BLOCK_SIZE] = {}; stmlib::GateFlags last_gate_flags[NUM_CHANNELS*MAX_MODULES] = {}; stmlib::GateFlags gate_flags[NUM_CHANNELS*MAX_MODULES][BLOCK_SIZE] = {}; int blockIndex = 0; GroupBuilder groupBuilder; Stages() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { // chain_state.Init(NULL, NULL); onReset(); } void onReset() override { stages::kSampleRate = engineGetSampleRate(); abloop = false; for (size_t i = 0; i < NUM_CHANNELS*MAX_MODULES; ++i) { segment_generator[i].Init(); configurations[i].type = stages::segment::TYPE_RAMP; configurations[i].loop = false; configuration_changed[i] = true; } } json_t *toJson() override { json_t *rootJ = json_object(); json_object_set_new(rootJ, "numModules", json_integer(numModules)); json_t *configurationsJ = json_array(); for (int i = 0; i < NUM_CHANNELS*MAX_MODULES; i++) { json_t *configurationJ = json_object(); json_object_set_new(configurationJ, "type", json_integer(configurations[i].type)); json_object_set_new(configurationJ, "loop", json_boolean(configurations[i].loop)); json_array_insert_new(configurationsJ, i, configurationJ); } json_object_set_new(rootJ, "configurations", configurationsJ); return rootJ; } void fromJson(json_t *rootJ) override { json_t *numModulesJ = json_object_get(rootJ, "numModules"); if (numModulesJ) numModules = json_integer_value(numModulesJ); json_t *configurationsJ = json_object_get(rootJ, "configurations"); for (int i = 0; i < NUM_CHANNELS*MAX_MODULES; i++) { json_t *configurationJ = json_array_get(configurationsJ, i); if (configurationJ) { json_t *typeJ = json_object_get(configurationJ, "type"); if (typeJ) configurations[i].type = (stages::segment::Type) json_integer_value(typeJ); json_t *loopJ = json_object_get(configurationJ, "loop"); if (loopJ) configurations[i].loop = json_boolean_value(loopJ); } } } void onSampleRateChange() override { stages::kSampleRate = engineGetSampleRate(); for (int i = 0; i < NUM_CHANNELS*MAX_MODULES; i++) { segment_generator[i].InitRamps(); } } void stepBlock() { // Get parameters float primaries[NUM_CHANNELS*numModules]; float secondaries[NUM_CHANNELS*numModules]; for (int i = 0; i < NUM_CHANNELS*numModules; i++) { primaries[i] = clamp(params[LEVEL_PARAMS + i].value + inputs[LEVEL_INPUTS + i].value / 8.f, 0.f, 1.f); secondaries[i] = params[SHAPE_PARAMS + i].value; } // See if the group associations have changed since the last group auto groups_changed = groupBuilder.buildGroups(&inputs, GATE_INPUTS, NUM_CHANNELS * numModules); // Process block stages::SegmentGenerator::Output out[BLOCK_SIZE] = {}; for (int i = 0; i < NUM_CHANNELS*numModules;) { // Check if the config needs applying to the segment generator for this group bool segment_changed = groups_changed; int numberOfLoops = 0; for (int j = 0; j < max(1, groupBuilder.groupSize[i]); j += 1) { numberOfLoops += configurations[i + j].loop ? 1 : 0; segment_changed |= configuration_changed[i + j]; configuration_changed[i + j] = false; } if (segment_changed) { if (numberOfLoops > 2) { for (int j = 0; j < max(1, groupBuilder.groupSize[i]); j += 1) { configurations[i + j].loop = false; } } segment_generator[i].Configure(groupBuilder.groupSize[i] > 0, &configurations[i], max(1, groupBuilder.groupSize[i])); } // Set the segment parameters on the generator we're about to process for (int j = 0; j < segment_generator[i].num_segments(); j += 1) { segment_generator[i].set_segment_parameters(j, primaries[i + j], secondaries[i + j]); } segment_generator[i].Process(gate_flags[i], out, BLOCK_SIZE); // Set the outputs for the active segment in each output sample // All outputs also go to the first segment for (int j = 0; j < BLOCK_SIZE; j++) { for (int k = 1; k < segment_generator[i].num_segments(); k += 1) { envelopeBuffer[i + k][j] = k == out[j].segment ? 1- out[j].phase : 0.f; } envelopeBuffer[i][j] = out[j].value; } i += segment_generator[i].num_segments(); } } void toggleMode(int i) { configurations[i].type = (stages::segment::Type) ((configurations[i].type + 1) % 3); configuration_changed[i] = true; } void toggleLoop(int i) { configuration_changed[i] = true; configurations[i].loop = !configurations[i].loop; // ensure that we're the only loop item in the group if (configurations[i].loop) { int group = groupBuilder.groupForSegment(i); // See how many loop items we have int loopitems = 0; for (size_t j = 0; j < groupBuilder.groupSize[group]; j += 1) { loopitems += configurations[group + j].loop ? 1 : 0; } // If we've got too many loop items, clear down to the one loop if ((abloop && loopitems > 2) || (!abloop && loopitems > 1)) { for (size_t j = 0; j < groupBuilder.groupSize[group]; j += 1) { configurations[group + j].loop = (group + (int)j) == i; } loopitems = 1; } // Turn abloop off if we've got 2 or more loops if (loopitems >= 2) { abloop = false; } } } void step() override { // Buttons for (int i = 0; i < NUM_CHANNELS * numModules; i++) { switch (typeButtons[i].step(params[TYPE_PARAMS + i])) { default: case LongPressButton::NO_PRESS: break; case LongPressButton::SHORT_PRESS: toggleMode(i); break; case LongPressButton::LONG_PRESS: toggleLoop(i); break; } } // Input for (int i = 0; i < NUM_CHANNELS * numModules; i++) { bool gate = (inputs[GATE_INPUTS + i].value >= 1.7f); last_gate_flags[i] = stmlib::ExtractGateFlags(last_gate_flags[i], gate); gate_flags[i][blockIndex] = last_gate_flags[i]; } // Process block if (++blockIndex >= BLOCK_SIZE) { blockIndex = 0; stepBlock(); } // Output int currentGroupSize = 0; int loopcount = 0; for (int i = 0; i < NUM_CHANNELS * numModules; i++) { float envelope = envelopeBuffer[i][blockIndex]; outputs[ENVELOPE_OUTPUTS + i].value = envelope * 8.f; lights[ENVELOPE_LIGHTS + i].setBrightnessSmooth(envelope); if (currentGroupSize <= 0) { currentGroupSize = max(1, groupBuilder.groupSize[i]); loopcount = 0; } currentGroupSize -= 1; loopcount += configurations[i].loop ? 1 : 0; auto flashlevel = 1.f; if (!configurations[i].loop) { oscillator[i].step(0.f); // move the oscillator on to keep the lights in sync } else if (configurations[i].loop && loopcount == 1) { flashlevel = abs(oscillator[i].step(0.f)); } else { flashlevel = abs(oscillator[i].step(0.25f)); } lights[TYPE_LIGHTS + i*2 + 0].setBrightness((configurations[i].type == 0 || configurations[i].type == 1) * flashlevel); lights[TYPE_LIGHTS + i*2 + 1].setBrightness((configurations[i].type == 1 || configurations[i].type == 2) * flashlevel); } } }; struct StagesWidget : ModuleWidget { Stages* module; int lastNumModules = 1; Widget* topLeftScrew; Widget* topRightScrew; Widget* bottomLeftScrew; Widget* bottomRightScrew; StagesWidget(Stages *module) : ModuleWidget(module) { this->module = module; setPanel(SVG::load(assetPlugin(plugin, "res/Stages.svg"))); this->box.size.x = 210 * module->numModules; this->panel->box.size = this->box.size; topLeftScrew = Widget::create(Vec(RACK_GRID_WIDTH, 0)); topRightScrew = Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)); bottomLeftScrew = Widget::create(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)); bottomRightScrew = Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)); addChild(topLeftScrew); addChild(topRightScrew); addChild(bottomLeftScrew); addChild(bottomRightScrew); for(auto i = 0; i < MAX_MODULES; i += 1) { float offset = 71.12 * i; addParam(ParamWidget::create(mm2px(Vec(offset + 3.72965, 13.98158)), module, Stages::SHAPE_PARAMS + 0 + (NUM_CHANNELS*i), 0.0, 1.0, 0.5)); addParam(ParamWidget::create(mm2px(Vec(offset + 15.17012, 13.98158)), module, Stages::SHAPE_PARAMS + 1 + (NUM_CHANNELS*i), 0.0, 1.0, 0.5)); addParam(ParamWidget::create(mm2px(Vec(offset + 26.6099, 13.98158)), module, Stages::SHAPE_PARAMS + 2 + (NUM_CHANNELS*i), 0.0, 1.0, 0.5)); addParam(ParamWidget::create(mm2px(Vec(offset + 38.07174, 13.98158)), module, Stages::SHAPE_PARAMS + 3 + (NUM_CHANNELS*i), 0.0, 1.0, 0.5)); addParam(ParamWidget::create(mm2px(Vec(offset + 49.51152, 13.98158)), module, Stages::SHAPE_PARAMS + 4 + (NUM_CHANNELS*i), 0.0, 1.0, 0.5)); addParam(ParamWidget::create(mm2px(Vec(offset + 60.95199, 13.98158)), module, Stages::SHAPE_PARAMS + 5 + (NUM_CHANNELS*i), 0.0, 1.0, 0.5)); addParam(ParamWidget::create(mm2px(Vec(offset + 4.17259, 32.37248)), module, Stages::TYPE_PARAMS + 0 + (NUM_CHANNELS*i), 0.0, 1.0, 0.0)); addParam(ParamWidget::create(mm2px(Vec(offset + 15.61237, 32.37248)), module, Stages::TYPE_PARAMS + 1 + (NUM_CHANNELS*i), 0.0, 1.0, 0.0)); addParam(ParamWidget::create(mm2px(Vec(offset + 27.05284, 32.37248)), module, Stages::TYPE_PARAMS + 2 + (NUM_CHANNELS*i), 0.0, 1.0, 0.0)); addParam(ParamWidget::create(mm2px(Vec(offset + 38.51399, 32.37248)), module, Stages::TYPE_PARAMS + 3 + (NUM_CHANNELS*i), 0.0, 1.0, 0.0)); addParam(ParamWidget::create(mm2px(Vec(offset + 49.95446, 32.37248)), module, Stages::TYPE_PARAMS + 4 + (NUM_CHANNELS*i), 0.0, 1.0, 0.0)); addParam(ParamWidget::create(mm2px(Vec(offset + 61.39424, 32.37248)), module, Stages::TYPE_PARAMS + 5 + (NUM_CHANNELS*i), 0.0, 1.0, 0.0)); addParam(ParamWidget::create(mm2px(Vec(offset + 3.36193, 43.06508)), module, Stages::LEVEL_PARAMS + 0 + (NUM_CHANNELS*i), 0.0, 1.0, 1.0)); addParam(ParamWidget::create(mm2px(Vec(offset + 14.81619, 43.06508)), module, Stages::LEVEL_PARAMS + 1 + (NUM_CHANNELS*i), 0.0, 1.0, 1.0)); addParam(ParamWidget::create(mm2px(Vec(offset + 26.26975, 43.06508)), module, Stages::LEVEL_PARAMS + 2 + (NUM_CHANNELS*i), 0.0, 1.0, 1.0)); addParam(ParamWidget::create(mm2px(Vec(offset + 37.70265, 43.06508)), module, Stages::LEVEL_PARAMS + 3 + (NUM_CHANNELS*i), 0.0, 1.0, 1.0)); addParam(ParamWidget::create(mm2px(Vec(offset + 49.15759, 43.06508)), module, Stages::LEVEL_PARAMS + 4 + (NUM_CHANNELS*i), 0.0, 1.0, 1.0)); addParam(ParamWidget::create(mm2px(Vec(offset + 60.61184, 43.06508)), module, Stages::LEVEL_PARAMS + 5 + (NUM_CHANNELS*i), 0.0, 1.0, 1.0)); addInput(Port::create(mm2px(Vec(offset + 2.70756, 77.75277)), Port::INPUT, module, Stages::LEVEL_INPUTS + 0 + (NUM_CHANNELS*i))); addInput(Port::create(mm2px(Vec(offset + 14.14734, 77.75277)), Port::INPUT, module, Stages::LEVEL_INPUTS + 1 + (NUM_CHANNELS*i))); addInput(Port::create(mm2px(Vec(offset + 25.58781, 77.75277)), Port::INPUT, module, Stages::LEVEL_INPUTS + 2 + (NUM_CHANNELS*i))); addInput(Port::create(mm2px(Vec(offset + 37.04896, 77.75277)), Port::INPUT, module, Stages::LEVEL_INPUTS + 3 + (NUM_CHANNELS*i))); addInput(Port::create(mm2px(Vec(offset + 48.48943, 77.75277)), Port::INPUT, module, Stages::LEVEL_INPUTS + 4 + (NUM_CHANNELS*i))); addInput(Port::create(mm2px(Vec(offset + 59.92921, 77.75277)), Port::INPUT, module, Stages::LEVEL_INPUTS + 5 + (NUM_CHANNELS*i))); addInput(Port::create(mm2px(Vec(offset + 2.70756, 92.35239)), Port::INPUT, module, Stages::GATE_INPUTS + 0 + (NUM_CHANNELS*i))); addInput(Port::create(mm2px(Vec(offset + 14.14734, 92.35239)), Port::INPUT, module, Stages::GATE_INPUTS + 1 + (NUM_CHANNELS*i))); addInput(Port::create(mm2px(Vec(offset + 25.58781, 92.35239)), Port::INPUT, module, Stages::GATE_INPUTS + 2 + (NUM_CHANNELS*i))); addInput(Port::create(mm2px(Vec(offset + 37.04896, 92.35239)), Port::INPUT, module, Stages::GATE_INPUTS + 3 + (NUM_CHANNELS*i))); addInput(Port::create(mm2px(Vec(offset + 48.48943, 92.35239)), Port::INPUT, module, Stages::GATE_INPUTS + 4 + (NUM_CHANNELS*i))); addInput(Port::create(mm2px(Vec(offset + 59.92921, 92.35239)), Port::INPUT, module, Stages::GATE_INPUTS + 5 + (NUM_CHANNELS*i))); addOutput(Port::create(mm2px(Vec(offset + 2.70756, 106.95203)), Port::OUTPUT, module, Stages::ENVELOPE_OUTPUTS + 0 + (NUM_CHANNELS*i))); addOutput(Port::create(mm2px(Vec(offset + 14.14734, 106.95203)), Port::OUTPUT, module, Stages::ENVELOPE_OUTPUTS + 1 + (NUM_CHANNELS*i))); addOutput(Port::create(mm2px(Vec(offset + 25.58781, 106.95203)), Port::OUTPUT, module, Stages::ENVELOPE_OUTPUTS + 2 + (NUM_CHANNELS*i))); addOutput(Port::create(mm2px(Vec(offset + 37.04896, 106.95203)), Port::OUTPUT, module, Stages::ENVELOPE_OUTPUTS + 3 + (NUM_CHANNELS*i))); addOutput(Port::create(mm2px(Vec(offset + 48.48943, 106.95203)), Port::OUTPUT, module, Stages::ENVELOPE_OUTPUTS + 4 + (NUM_CHANNELS*i))); addOutput(Port::create(mm2px(Vec(offset + 59.92921, 106.95203)), Port::OUTPUT, module, Stages::ENVELOPE_OUTPUTS + 5 + (NUM_CHANNELS*i))); addChild(ModuleLightWidget::create>(mm2px(Vec(offset + 5.27737, 26.74447)), module, Stages::TYPE_LIGHTS + (0*2) + (NUM_CHANNELS*2*i))); addChild(ModuleLightWidget::create>(mm2px(Vec(offset + 16.73784, 26.74447)), module, Stages::TYPE_LIGHTS + (1*2) + (NUM_CHANNELS*2*i))); addChild(ModuleLightWidget::create>(mm2px(Vec(offset + 28.1783, 26.74447)), module, Stages::TYPE_LIGHTS + (2*2) + (NUM_CHANNELS*2*i))); addChild(ModuleLightWidget::create>(mm2px(Vec(offset + 39.61877, 26.74447)), module, Stages::TYPE_LIGHTS + (3*2) + (NUM_CHANNELS*2*i))); addChild(ModuleLightWidget::create>(mm2px(Vec(offset + 51.07923, 26.74447)), module, Stages::TYPE_LIGHTS + (4*2) + (NUM_CHANNELS*2*i))); addChild(ModuleLightWidget::create>(mm2px(Vec(offset + 62.51971, 26.74447)), module, Stages::TYPE_LIGHTS + (5*2) + (NUM_CHANNELS*2*i))); addChild(ModuleLightWidget::create>(mm2px(Vec(offset + 2.29462, 103.19253)), module, Stages::ENVELOPE_LIGHTS + 0 + (NUM_CHANNELS*i))); addChild(ModuleLightWidget::create>(mm2px(Vec(offset + 13.73509, 103.19253)), module, Stages::ENVELOPE_LIGHTS + 1 + (NUM_CHANNELS*i))); addChild(ModuleLightWidget::create>(mm2px(Vec(offset + 25.17556, 103.19253)), module, Stages::ENVELOPE_LIGHTS + 2 + (NUM_CHANNELS*i))); addChild(ModuleLightWidget::create>(mm2px(Vec(offset + 36.63671, 103.19253)), module, Stages::ENVELOPE_LIGHTS + 3 + (NUM_CHANNELS*i))); addChild(ModuleLightWidget::create>(mm2px(Vec(offset + 48.07649, 103.19253)), module, Stages::ENVELOPE_LIGHTS + 4 + (NUM_CHANNELS*i))); addChild(ModuleLightWidget::create>(mm2px(Vec(offset + 59.51696, 103.19253)), module, Stages::ENVELOPE_LIGHTS + 5 + (NUM_CHANNELS*i))); } } void step() override { if (module->numModules != lastNumModules) { resize(module->numModules); // Remove wires which no longer have a port for (int i = 0; i < inputs.size(); i++) { if (inputs[i]->portId >= Stages::LEVEL_INPUTS + (module->numModules * NUM_CHANNELS) && inputs[i]->portId < Stages::LEVEL_INPUTS + (NUM_CHANNELS * MAX_MODULES)) { gRackWidget->wireContainer->removeAllWires(inputs[i]); } if (inputs[i]->portId >= Stages::GATE_INPUTS + (module->numModules * NUM_CHANNELS) && inputs[i]->portId < Stages::GATE_INPUTS + (NUM_CHANNELS * MAX_MODULES)) { gRackWidget->wireContainer->removeAllWires(inputs[i]); } } for (int i = 0; i < outputs.size(); i++) { if (outputs[i]->portId >= Stages::ENVELOPE_OUTPUTS + (module->numModules * NUM_CHANNELS) && outputs[i]->portId < Stages::ENVELOPE_OUTPUTS + (NUM_CHANNELS * MAX_MODULES)) { gRackWidget->wireContainer->removeAllWires(outputs[i]); } } } ModuleWidget::step(); } void resize(int width) { lastNumModules = width; this->box.size.x = width * 210; this->panel->box.size = this->box.size; topRightScrew->box.pos.x = box.size.x - 2 * RACK_GRID_WIDTH; bottomRightScrew->box.pos.x = box.size.x - 2 * RACK_GRID_WIDTH; } void appendContextMenu(Menu *menu) override { Stages *module = dynamic_cast(this->module); struct ABLoopItem : MenuItem { Stages *module; void onAction(EventAction &e) override { module->abloop = true; } }; menu->addChild(MenuEntry::create()); ABLoopItem *abloopItem = MenuItem::create("Set A/B Loop", CHECKMARK(module->abloop)); abloopItem->module = module; menu->addChild(abloopItem); struct ExpandoItem : MenuItem { Stages *module; int width = 1; void onAction(EventAction &e) override { module->numModules = width; } }; struct ModuleLinkingItem : MenuItem { Stages* module; Menu *createChildMenu() override { Menu *menu = new Menu(); for (auto i = 1; i <= MAX_MODULES; i += 1) { ExpandoItem *expandoItem = MenuItem::create(stringf("%dx", i), CHECKMARK(module->numModules == i)); expandoItem->module = module; expandoItem->width = i; menu->addChild(expandoItem); } return menu; } }; menu->addChild(MenuEntry::create()); ModuleLinkingItem *moduleLinkingItem = MenuItem::create("Linked Modules"); moduleLinkingItem->module = module; menu->addChild(moduleLinkingItem); } }; Model *modelStages = Model::create("Audible Instruments", "Stages", "Segment Generator", FUNCTION_GENERATOR_TAG, ENVELOPE_GENERATOR_TAG);