Browse Source

Initial commit from cschol PR

pull/120/head
hemmer 3 years ago
parent
commit
9ae62cbb38
4 changed files with 709 additions and 1 deletions
  1. +10
    -0
      plugin.json
  2. +697
    -0
      src/Peaks.cpp
  3. +1
    -0
      src/plugin.cpp
  4. +1
    -1
      src/plugin.hpp

+ 10
- 0
plugin.json View File

@@ -253,6 +253,16 @@
"Low-pass gate",
"Polyphonic"
]
},
{
"slug": "Peaks",
"name": "Peaks",
"description": "Based on Mutable Instruments Peaks",
"tags": [
"Oscillator",
"LFO",
"Hardware clone"
]
}
]
}

+ 697
- 0
src/Peaks.cpp View File

@@ -0,0 +1,697 @@
#include <string>
#include <chrono>

#include "dsp/digital.hpp"
#include "dsp/samplerate.hpp"
#include "dsp/ringbuffer.hpp"

#include "peaks/processors.h"

#include "AudibleInstruments.hpp"


enum SwitchIndex {
SWITCH_TWIN_MODE,
SWITCH_FUNCTION,
SWITCH_GATE_TRIG_1,
SWITCH_GATE_TRIG_2
};

enum EditMode {
EDIT_MODE_TWIN,
EDIT_MODE_SPLIT,
EDIT_MODE_FIRST,
EDIT_MODE_SECOND,
EDIT_MODE_LAST
};

enum Function {
FUNCTION_ENVELOPE,
FUNCTION_LFO,
FUNCTION_TAP_LFO,
FUNCTION_DRUM_GENERATOR,
FUNCTION_MINI_SEQUENCER,
FUNCTION_PULSE_SHAPER,
FUNCTION_PULSE_RANDOMIZER,
FUNCTION_FM_DRUM_GENERATOR,
FUNCTION_LAST,
FUNCTION_FIRST_ALTERNATE_FUNCTION = FUNCTION_MINI_SEQUENCER
};

struct Settings {
uint8_t edit_mode;
uint8_t function[2];
uint8_t pot_value[8];
bool snap_mode;
};


static const size_t kNumBlocks = 2;
static const size_t kNumChannels = 2;
static const size_t kBlockSize = 4;
static const int32_t kLongPressDuration = 600;
static const uint8_t kNumAdcChannels = 4;
static const uint16_t kAdcThresholdUnlocked = 1 << (16 - 10); // 10 bits
static const uint16_t kAdcThresholdLocked = 1 << (16 - 8); // 8 bits


struct Peaks : Module {
enum ParamIds {
KNOB_1_PARAM,
KNOB_2_PARAM,
KNOB_3_PARAM,
KNOB_4_PARAM,
BUTTON_1_PARAM,
BUTTON_2_PARAM,
TRIG_1_PARAM,
TRIG_2_PARAM,
NUM_PARAMS
};
enum InputIds {
GATE_1_INPUT,
GATE_2_INPUT,
NUM_INPUTS
};
enum OutputIds {
OUT_1_OUTPUT,
OUT_2_OUTPUT,
NUM_OUTPUTS
};
enum LightIds {
TRIG_1_LIGHT,
TRIG_2_LIGHT,
TWIN_MODE_LIGHT,
FUNC_1_LIGHT,
FUNC_2_LIGHT,
FUNC_3_LIGHT,
FUNC_4_LIGHT,
NUM_LIGHTS
};

static const peaks::ProcessorFunction function_table_[FUNCTION_LAST][2];

EditMode edit_mode_ = EDIT_MODE_TWIN;
Function function_[2] = {FUNCTION_ENVELOPE, FUNCTION_ENVELOPE};
Settings settings_;

uint8_t pot_value_[8] = {0, 0, 0, 0, 0, 0, 0, 0};

bool snap_mode_ = false;
bool snapped_[4] = {false, false, false, false};

int32_t adc_lp_[kNumAdcChannels] = {0, 0, 0, 0};
int32_t adc_value_[kNumAdcChannels] = {0, 0, 0, 0};
int32_t adc_threshold_[kNumAdcChannels] = {0, 0, 0, 0};
long long press_time_[2] = {0, 0};

peaks::Processors processors[2];

int16_t output[kBlockSize];
int16_t brightness[kNumChannels] = {0, 0};

SchmittTrigger switches_[2];

peaks::GateFlags gate_flags[2] = {0, 0};

SampleRateConverter<2> outputSrc;
DoubleRingBuffer<Frame<2>, 256> outputBuffer;

bool initNumberStation = false;

struct Block {
peaks::GateFlags input[kNumChannels][kBlockSize];
uint16_t output[kNumChannels][kBlockSize];
};

struct Slice {
Block* block;
size_t frame_index;
};

Block block_[kNumBlocks];
size_t io_frame_ = 0;
size_t io_block_ = 0;
size_t render_block_ = kNumBlocks / 2;


Peaks() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
settings_.edit_mode = EDIT_MODE_TWIN;
settings_.function[0] = FUNCTION_ENVELOPE;
settings_.function[1] = FUNCTION_ENVELOPE;
settings_.snap_mode = false;
std::fill(&settings_.pot_value[0], &settings_.pot_value[8], 0);

memset(&processors[0], 0, sizeof(processors[0]));
memset(&processors[1], 0, sizeof(processors[1]));
processors[0].Init(0);
processors[1].Init(1);
}

void onReset() override {
init();
}

void init() {
std::fill(&pot_value_[0], &pot_value_[8], 0);
std::fill(&press_time_[0], &press_time_[1], 0);
std::fill(&brightness[0], &brightness[1], 0);
std::fill(&adc_lp_[0], &adc_lp_[kNumAdcChannels], 0);
std::fill(&adc_value_[0], &adc_value_[kNumAdcChannels], 0);
std::fill(&adc_threshold_[0], &adc_threshold_[kNumAdcChannels], 0);
std::fill(&snapped_[0], &snapped_[kNumAdcChannels], false);

edit_mode_ = static_cast<EditMode>(settings_.edit_mode);
function_[0] = static_cast<Function>(settings_.function[0]);
function_[1] = static_cast<Function>(settings_.function[1]);
std::copy(&settings_.pot_value[0], &settings_.pot_value[8], &pot_value_[0]);

if (edit_mode_ == EDIT_MODE_FIRST || edit_mode_ == EDIT_MODE_SECOND) {
lockPots();
for (uint8_t i = 0; i < 4; ++i) {
processors[0].set_parameter(
i,
static_cast<uint16_t>(pot_value_[i]) << 8);
processors[1].set_parameter(
i,
static_cast<uint16_t>(pot_value_[i + 4]) << 8);
}
}

snap_mode_ = settings_.snap_mode;

changeControlMode();
setFunction(0, function_[0]);
setFunction(1, function_[1]);
}

json_t* toJson() override {

saveState();

json_t* rootJ = json_object();

json_object_set_new(rootJ, "edit_mode", json_integer((int)settings_.edit_mode));
json_object_set_new(rootJ, "fcn_channel_1", json_integer((int)settings_.function[0]));
json_object_set_new(rootJ, "fcn_channel_2", json_integer((int)settings_.function[1]));

json_t* potValuesJ = json_array();
for (int p : pot_value_) {
json_t* pJ = json_integer(p);
json_array_append_new(potValuesJ, pJ);
}
json_object_set_new(rootJ, "pot_values", potValuesJ);

json_object_set_new(rootJ, "snap_mode", json_boolean(settings_.snap_mode));

return rootJ;
}

void fromJson(json_t* rootJ) override {
json_t* editModeJ = json_object_get(rootJ, "edit_mode");
if (editModeJ) {
settings_.edit_mode = static_cast<EditMode>(json_integer_value(editModeJ));
}

json_t* fcnChannel1J = json_object_get(rootJ, "fcn_channel_1");
if (fcnChannel1J) {
settings_.function[0] = static_cast<Function>(json_integer_value(fcnChannel1J));
}

json_t* fcnChannel2J = json_object_get(rootJ, "fcn_channel_2");
if (fcnChannel2J) {
settings_.function[1] = static_cast<Function>(json_integer_value(fcnChannel2J));
}

json_t* snapModeJ = json_object_get(rootJ, "snap_mode");
if (snapModeJ) {
settings_.snap_mode = json_boolean_value(snapModeJ);
}

json_t* potValuesJ = json_object_get(rootJ, "pot_values");
size_t potValueId;
json_t* pJ;
json_array_foreach(potValuesJ, potValueId, pJ) {
if (potValueId < sizeof(pot_value_) / sizeof(pot_value_)[0]) {
settings_.pot_value[potValueId] = json_integer_value(pJ);
}
}

// Update module internal state from settings.
init();
}

void step() override {
poll();
pollPots();

// Initialize "secret" number station mode.
if (initNumberStation) {
processors[0].set_function(peaks::PROCESSOR_FUNCTION_NUMBER_STATION);
processors[1].set_function(peaks::PROCESSOR_FUNCTION_NUMBER_STATION);
initNumberStation = false;
}

if (outputBuffer.empty()) {

while (render_block_ != io_block_) {
process(&block_[render_block_], kBlockSize);
render_block_ = (render_block_ + 1) % kNumBlocks;
}

uint32_t external_gate_inputs = 0;
external_gate_inputs |= (inputs[GATE_1_INPUT].value ? 1 : 0);
external_gate_inputs |= (inputs[GATE_2_INPUT].value ? 2 : 0);

uint32_t buttons = 0;
buttons |= (params[TRIG_1_PARAM].value ? 1 : 0);
buttons |= (params[TRIG_2_PARAM].value ? 2 : 0);

uint32_t gate_inputs = external_gate_inputs | buttons;

// Prepare sample rate conversion.
// Peaks is sampling at 48kHZ.
outputSrc.setRates(48000, engineGetSampleRate());
int inLen = kBlockSize;
int outLen = outputBuffer.capacity();
Frame<2> f[kBlockSize];

// Process an entire block of data from the IOBuffer.
for (size_t k = 0; k < kBlockSize; ++k) {

Slice slice = NextSlice(1);

for (size_t i = 0; i < kNumChannels; ++i) {
gate_flags[i] = peaks::ExtractGateFlags(
gate_flags[i],
gate_inputs & (1 << i));

f[k].samples[i] = slice.block->output[i][slice.frame_index];
}

// A hack to make channel 1 aware of what's going on in channel 2. Used to
// reset the sequencer.
slice.block->input[0][slice.frame_index] = gate_flags[0] | (gate_flags[1] << 4) | (buttons & 8 ? peaks::GATE_FLAG_FROM_BUTTON : 0);

slice.block->input[1][slice.frame_index] = gate_flags[1] | (buttons & 2 ? peaks::GATE_FLAG_FROM_BUTTON : 0);
}

outputSrc.process(f, &inLen, outputBuffer.endData(), &outLen);
outputBuffer.endIncr(outLen);
}

// Update outputs.
if (!outputBuffer.empty()) {
Frame<2> f = outputBuffer.shift();

// Peaks manual says output spec is 0..8V for envelopes and 10Vpp for audio/CV.
// TODO Check the output values against an actual device.
outputs[OUT_1_OUTPUT].value = rescale(static_cast<float>(f.samples[0]), 0.0f, 65535.f, -8.0f, 8.0f);
outputs[OUT_2_OUTPUT].value = rescale(static_cast<float>(f.samples[1]), 0.0f, 65535.f, -8.0f, 8.0f);
}
}

inline Slice NextSlice(size_t size) {
Slice s;
s.block = &block_[io_block_];
s.frame_index = io_frame_;
io_frame_ += size;
if (io_frame_ >= kBlockSize) {
io_frame_ -= kBlockSize;
io_block_ = (io_block_ + 1) % kNumBlocks;
}
return s;
}

inline Function function() const {
return edit_mode_ == EDIT_MODE_SECOND ? function_[1] : function_[0];
}

inline void set_led_brightness(int channel, int16_t value) {
brightness[channel] = value;
}

inline void process(Block* block, size_t size) {
for (size_t i = 0; i < kNumChannels; ++i) {
processors[i].Process(block->input[i], output, size);
set_led_brightness(i, output[0]);
for (size_t j = 0; j < size; ++j) {
// From calibration_data.h, shifting signed to unsigned values.
int32_t shifted_value = 32767 + static_cast<int32_t>(output[j]);
CONSTRAIN(shifted_value, 0, 65535);
block->output[i][j] = static_cast<uint16_t>(shifted_value);
}
}
}

void changeControlMode();
void setFunction(uint8_t index, Function f);
void onPotChanged(uint16_t id, uint16_t value);
void onSwitchReleased(uint16_t id, uint16_t data);
void saveState();
void lockPots();
void poll();
void pollPots();
void refreshLeds();

long long getSystemTimeMs();
};

const peaks::ProcessorFunction Peaks::function_table_[FUNCTION_LAST][2] = {
{ peaks::PROCESSOR_FUNCTION_ENVELOPE, peaks::PROCESSOR_FUNCTION_ENVELOPE },
{ peaks::PROCESSOR_FUNCTION_LFO, peaks::PROCESSOR_FUNCTION_LFO },
{ peaks::PROCESSOR_FUNCTION_TAP_LFO, peaks::PROCESSOR_FUNCTION_TAP_LFO },
{ peaks::PROCESSOR_FUNCTION_BASS_DRUM, peaks::PROCESSOR_FUNCTION_SNARE_DRUM },

{ peaks::PROCESSOR_FUNCTION_MINI_SEQUENCER, peaks::PROCESSOR_FUNCTION_MINI_SEQUENCER },
{ peaks::PROCESSOR_FUNCTION_PULSE_SHAPER, peaks::PROCESSOR_FUNCTION_PULSE_SHAPER },
{ peaks::PROCESSOR_FUNCTION_PULSE_RANDOMIZER, peaks::PROCESSOR_FUNCTION_PULSE_RANDOMIZER },
{ peaks::PROCESSOR_FUNCTION_FM_DRUM, peaks::PROCESSOR_FUNCTION_FM_DRUM },
};


void Peaks::changeControlMode() {
uint16_t parameters[4];
for (int i = 0; i < 4; ++i) {
parameters[i] = adc_value_[i];
}

if (edit_mode_ == EDIT_MODE_SPLIT) {
processors[0].CopyParameters(&parameters[0], 2);
processors[1].CopyParameters(&parameters[2], 2);
processors[0].set_control_mode(peaks::CONTROL_MODE_HALF);
processors[1].set_control_mode(peaks::CONTROL_MODE_HALF);
}
else if (edit_mode_ == EDIT_MODE_TWIN) {
processors[0].CopyParameters(&parameters[0], 4);
processors[1].CopyParameters(&parameters[0], 4);
processors[0].set_control_mode(peaks::CONTROL_MODE_FULL);
processors[1].set_control_mode(peaks::CONTROL_MODE_FULL);
}
else {
processors[0].set_control_mode(peaks::CONTROL_MODE_FULL);
processors[1].set_control_mode(peaks::CONTROL_MODE_FULL);
}
}

void Peaks::setFunction(uint8_t index, Function f) {
if (edit_mode_ == EDIT_MODE_SPLIT || edit_mode_ == EDIT_MODE_TWIN) {
function_[0] = function_[1] = f;
processors[0].set_function(function_table_[f][0]);
processors[1].set_function(function_table_[f][1]);
}
else {
function_[index] = f;
processors[index].set_function(function_table_[f][index]);
}
}

void Peaks::onSwitchReleased(uint16_t id, uint16_t data) {
switch (id) {
case SWITCH_TWIN_MODE:
if (data > kLongPressDuration) {
edit_mode_ = static_cast<EditMode>(
(edit_mode_ + EDIT_MODE_FIRST) % EDIT_MODE_LAST);
function_[0] = function_[1];
processors[0].set_function(function_table_[function_[0]][0]);
processors[1].set_function(function_table_[function_[0]][1]);
lockPots();
}
else {
if (edit_mode_ <= EDIT_MODE_SPLIT) {
edit_mode_ = static_cast<EditMode>(EDIT_MODE_SPLIT - edit_mode_);
}
else {
edit_mode_ = static_cast<EditMode>(EDIT_MODE_SECOND - (edit_mode_ & 1));
lockPots();
}
}
changeControlMode();
saveState();
break;

case SWITCH_FUNCTION: {
Function f = function();
if (data > kLongPressDuration) {
f = static_cast<Function>((f + FUNCTION_FIRST_ALTERNATE_FUNCTION) % FUNCTION_LAST);
}
else {
if (f <= FUNCTION_DRUM_GENERATOR) {
f = static_cast<Function>((f + 1) & 3);
}
else {
f = static_cast<Function>(((f + 1) & 3) + FUNCTION_FIRST_ALTERNATE_FUNCTION);
}
}
setFunction(edit_mode_ - EDIT_MODE_FIRST, f);
saveState();
}
break;

case SWITCH_GATE_TRIG_1:
// no-op
break;

case SWITCH_GATE_TRIG_2:
// no-op
break;
}
}

void Peaks::lockPots() {
std::fill(
&adc_threshold_[0],
&adc_threshold_[kNumAdcChannels],
kAdcThresholdLocked);
std::fill(&snapped_[0], &snapped_[kNumAdcChannels], false);
}

void Peaks::pollPots() {
for (uint8_t i = 0; i < kNumAdcChannels; ++i) {
adc_lp_[i] = (int32_t(params[KNOB_1_PARAM + i].value) + adc_lp_[i] * 7) >> 3;
int32_t value = adc_lp_[i];
int32_t current_value = adc_value_[i];
if (value >= current_value + adc_threshold_[i] ||
value <= current_value - adc_threshold_[i] ||
!adc_threshold_[i]) {
onPotChanged(i, value);
adc_value_[i] = value;
adc_threshold_[i] = kAdcThresholdUnlocked;
}
}
}

void Peaks::onPotChanged(uint16_t id, uint16_t value) {
switch (edit_mode_) {
case EDIT_MODE_TWIN:
processors[0].set_parameter(id, value);
processors[1].set_parameter(id, value);
pot_value_[id] = value >> 8;
break;

case EDIT_MODE_SPLIT:
if (id < 2) {
processors[0].set_parameter(id, value);
}
else {
processors[1].set_parameter(id - 2, value);
}
pot_value_[id] = value >> 8;
break;

case EDIT_MODE_FIRST:
case EDIT_MODE_SECOND: {
uint8_t index = id + (edit_mode_ - EDIT_MODE_FIRST) * 4;
peaks::Processors* p = &processors[edit_mode_ - EDIT_MODE_FIRST];

int16_t delta = static_cast<int16_t>(pot_value_[index]) - \
static_cast<int16_t>(value >> 8);
if (delta < 0) {
delta = -delta;
}

if (!snap_mode_ || snapped_[id] || delta <= 2) {
p->set_parameter(id, value);
pot_value_[index] = value >> 8;
snapped_[id] = true;
}
}
break;

case EDIT_MODE_LAST:
break;
}
}

long long Peaks::getSystemTimeMs() {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()
).count();
}

void Peaks::poll() {
for (uint8_t i = 0; i < 2; ++i) {
if (switches_[i].process(params[BUTTON_1_PARAM + i].value)) {
press_time_[i] = getSystemTimeMs();
}

if (switches_[i].isHigh() && press_time_[i] != 0) {
int32_t pressed_time = getSystemTimeMs() - press_time_[i];
if (pressed_time > kLongPressDuration) {
onSwitchReleased(SWITCH_TWIN_MODE + i, pressed_time);
press_time_[i] = 0; // Inhibit next release event
}
}
if (!switches_[i].isHigh() && press_time_[i] != 0) {
int32_t delta = getSystemTimeMs() - press_time_[i] + 1;
onSwitchReleased(SWITCH_TWIN_MODE + i, delta);
press_time_[i] = 0; // Not in original code!
}
}

refreshLeds();
}

void Peaks::saveState() {
settings_.edit_mode = edit_mode_;
settings_.function[0] = function_[0];
settings_.function[1] = function_[1];
std::copy(&pot_value_[0], &pot_value_[8], &settings_.pot_value[0]);
settings_.snap_mode = snap_mode_;
}

void Peaks::refreshLeds() {
uint8_t flash = (getSystemTimeMs() >> 7) & 7;
switch (edit_mode_) {
case EDIT_MODE_FIRST:
lights[TWIN_MODE_LIGHT].value = (flash == 1) ? 1.0f : 0.0f;
break;
case EDIT_MODE_SECOND:
lights[TWIN_MODE_LIGHT].value = (flash == 1 || flash == 3) ? 1.0f : 0.0f;
break;
default:
lights[TWIN_MODE_LIGHT].value = (edit_mode_ & 1) ? 1.0f : 0.0f;
break;
}
if ((getSystemTimeMs() & 256) && function() >= FUNCTION_FIRST_ALTERNATE_FUNCTION) {
for (size_t i = 0; i < 4; ++i) {
lights[FUNC_1_LIGHT + i].value = 0.0f;
}
}
else {
for (size_t i = 0; i < 4; ++i) {
lights[FUNC_1_LIGHT + i].value = ((function() & 3) == i) ? 1.0f : 0.0f;
}
}

uint8_t b[2];
for (uint8_t i = 0; i < 2; ++i) {
switch (function_[i]) {
case FUNCTION_DRUM_GENERATOR:
case FUNCTION_FM_DRUM_GENERATOR:
b[i] = (int16_t) abs(brightness[i]) >> 8;
b[i] = b[i] >= 255 ? 255 : b[i];
break;
case FUNCTION_LFO:
case FUNCTION_TAP_LFO:
case FUNCTION_MINI_SEQUENCER: {
int32_t brightnessVal = int32_t(brightness[i]) * 409 >> 8;
brightnessVal += 32768;
brightnessVal >>= 8;
CONSTRAIN(brightnessVal, 0, 255);
b[i] = brightnessVal;
}
break;
default:
b[i] = brightness[i] >> 7;
break;
}
}

if (processors[0].function() == peaks::PROCESSOR_FUNCTION_NUMBER_STATION) {
uint8_t pattern = processors[0].number_station().digit()
^ processors[1].number_station().digit();
for (size_t i = 0; i < 4; ++i) {
lights[FUNC_1_LIGHT + i].value = (pattern & 1) ? 1.0f : 0.0f;
pattern = pattern >> 1;
}
b[0] = processors[0].number_station().gate() ? 255 : 0;
b[1] = processors[1].number_station().gate() ? 255 : 0;
}

lights[TRIG_1_LIGHT].value = rescale(static_cast<float>(b[0]), 0.0f, 255.0f, 0.0f, 1.0f);
lights[TRIG_2_LIGHT].value = rescale(static_cast<float>(b[1]), 0.0f, 255.0f, 0.0f, 1.0f);
}


struct PeaksWidget : ModuleWidget {
PeaksWidget(Peaks* module) : ModuleWidget(module) {
setPanel(SVG::load(assetPlugin(plugin, "res/Peaks.svg")));

addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
addChild(Widget::create<ScrewSilver>(Vec(15, 365)));

addParam(ParamWidget::create<TL1105>(Vec(8.5, 52), module, Peaks::BUTTON_1_PARAM, 0.0f, 1.0f, 0.0f));
addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(11.88, 74), module, Peaks::TWIN_MODE_LIGHT));
addParam(ParamWidget::create<TL1105>(Vec(8.5, 89), module, Peaks::BUTTON_2_PARAM, 0.0f, 1.0f, 0.0f));
addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(11.88, 111), module, Peaks::FUNC_1_LIGHT));
addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(11.88, 126.75), module, Peaks::FUNC_2_LIGHT));
addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(11.88, 142.5), module, Peaks::FUNC_3_LIGHT));
addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(11.88, 158), module, Peaks::FUNC_4_LIGHT));

addParam(ParamWidget::create<Rogan1PSWhite>(Vec(61, 51), module, Peaks::KNOB_1_PARAM, 0.0f, 65535.0f, 16384.0f));
addParam(ParamWidget::create<Rogan1PSWhite>(Vec(61, 115), module, Peaks::KNOB_2_PARAM, 0.0f, 65535.0f, 16384.0f));
addParam(ParamWidget::create<Rogan1PSWhite>(Vec(61, 179), module, Peaks::KNOB_3_PARAM, 0.0f, 65535.0f, 32678.0f));
addParam(ParamWidget::create<Rogan1PSWhite>(Vec(61, 244), module, Peaks::KNOB_4_PARAM, 0.0f, 65535.0f, 32678.0f));

addParam(ParamWidget::create<LEDBezel>(Vec(11, 188), module, Peaks::TRIG_1_PARAM, 0.0f, 1.0f, 0.0f));
addParam(ParamWidget::create<LEDBezel>(Vec(11, 273), module, Peaks::TRIG_2_PARAM, 0.0f, 1.0f, 0.0f));
addChild(ModuleLightWidget::create<LEDBezelLight<GreenLight>>(Vec(11, 188).plus(mm2px(Vec(0.75, 0.75))), module, Peaks::TRIG_1_LIGHT));
addChild(ModuleLightWidget::create<LEDBezelLight<GreenLight>>(Vec(11, 273).plus(mm2px(Vec(0.75, 0.75))), module, Peaks::TRIG_2_LIGHT));

addInput(Port::create<PJ301MPort>(Vec(10, 230), Port::INPUT, module, Peaks::GATE_1_INPUT));
addInput(Port::create<PJ301MPort>(Vec(10, 315), Port::INPUT, module, Peaks::GATE_2_INPUT));

addOutput(Port::create<PJ301MPort>(Vec(53, 315), Port::OUTPUT, module, Peaks::OUT_1_OUTPUT));
addOutput(Port::create<PJ301MPort>(Vec(86, 315), Port::OUTPUT, module, Peaks::OUT_2_OUTPUT));
}

Menu* createContextMenu() override {
Menu* menu = ModuleWidget::createContextMenu();
Peaks* peaks = dynamic_cast<Peaks*>(this->module);

struct SnapModeItem : MenuItem {
Peaks* peaks;
void onAction(EventAction& e) override {
peaks->snap_mode_ = !peaks->snap_mode_;
}
void step() override {
rightText = (peaks->snap_mode_) ? "✔" : "";
MenuItem::step();
}
};

struct NumberStationItem : MenuItem {
Peaks* peaks;
void onAction(EventAction& e) override {
peaks->initNumberStation = true;
}
void step() override {
rightText = (peaks->processors[0].function() == peaks::PROCESSOR_FUNCTION_NUMBER_STATION) ? "✔" : "";
MenuItem::step();
}
};

menu->addChild(construct<MenuLabel>());
menu->addChild(construct<SnapModeItem>(&SnapModeItem::text, "Snap Mode", &SnapModeItem::peaks, peaks));

menu->addChild(construct<MenuLabel>());
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Secret Modes"));
menu->addChild(construct<NumberStationItem>(&NumberStationItem::text, "Number Station", &NumberStationItem::peaks, peaks));

return menu;
}
};



Model* modelPeaks = Model::create<Peaks, PeaksWidget>("Audible Instruments", "Peaks", "Percussive Synthesizer", UTILITY_TAG, LFO_TAG, DRUM_TAG);

+ 1
- 0
src/plugin.cpp View File

@@ -26,4 +26,5 @@ void init(rack::Plugin* p) {
p->addModel(modelRipples);
p->addModel(modelShelves);
p->addModel(modelStreams);
p->addModel(modelPeaks);
}

+ 1
- 1
src/plugin.hpp View File

@@ -26,7 +26,7 @@ extern Model* modelMarbles;
extern Model* modelRipples;
extern Model* modelShelves;
extern Model* modelStreams;
extern Model *modelPeaks;

template <typename Base>
struct Rogan6PSLight : Base {


Loading…
Cancel
Save