#include "sync3.hpp"
#include "via-module.hpp"
#include "polyblamp.hpp"
#include "osdialog.h"
#define SYNC3_OVERSAMPLE_AMOUNT 24
#define SYNC3_OVERSAMPLE_QUALITY 6
struct Sync3 : Via {
struct IRatioQuantity;
struct IIRatioQuantity;
struct IIIRatioQuantity;
struct IButtonQuantity;
struct RatioButtonQuantity;
struct IIButtonQuantity;
struct CVButtonQuantity;
struct IIIButtonQuantity;
Sync3() : Via(), virtualModule(asset::plugin(pluginInstance, "res/original.sync3")) {
virtualIO = &virtualModule;
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
configParam(KNOB1_PARAM, 0, 4095.0, 2048.0, "I ratio");
configParam(KNOB2_PARAM, 0, 4095.0, 2048.0, "II ratio");
configParam(KNOB3_PARAM, 0, 4095.0, 2048.0, "III ratio");
configParam(B_PARAM, -1.0, 1.0, -1.0, "Oscillator III level", "V");
configParam(CV2AMT_PARAM, 0, 1.0, 1.0, "II CV scale");
configParam(A_PARAM, -5.0, 5.0, 5.0, "Oscillator II level", "V");
configParam(CV3AMT_PARAM, 0, 1.0, 1.0, "III CV scale");
configParam(BUTTON1_PARAM, 0.0, 1.0, 0.0, "Oscillator I shape");
configParam(BUTTON2_PARAM, 0.0, 1.0, 0.0, "Ratio set");
configParam(BUTTON3_PARAM, 0.0, 1.0, 0.0, "Oscillator II shape");
configParam(BUTTON4_PARAM, 0.0, 1.0, 0.0, "CV mapping");
configParam(BUTTON5_PARAM, 0.0, 1.0, 0.0, "Ratio set");
configParam(BUTTON6_PARAM, 0.0, 1.0, 0.0, "Oscillator III shape");
configParam(TRIGBUTTON_PARAM, 0.0, 5.0, 0.0, "Tap tempo");
configInput(A_INPUT, "Oscillator II level");
configInput(B_INPUT, "Oscillator III level");
configInput(CV1_INPUT, "I ratio");
configInput(CV2_INPUT, "II or II+III ratio");
configInput(CV3_INPUT, "III ratio or PM");
configInput(MAIN_LOGIC_INPUT, "Sync");
configInput(AUX_LOGIC_INPUT, "Hold II and III level inputs");
configOutput(MAIN_OUTPUT, "II + III");
configOutput(LOGICA_OUTPUT, "Ratio change gate");
configOutput(AUX_DAC_OUTPUT, "I");
configOutput(AUX_LOGIC_OUTPUT, "Divided by two gate");
onSampleRateChange();
}
void process(const ProcessArgs &args) override;
ViaSync3 virtualModule;
void onSampleRateChange() override {
float sampleRate = APP->engine->getSampleRate();
ledDecay = 1.0/sampleRate;
if (sampleRate == 44100.0) {
divideAmount = 1;
} else if (sampleRate == 48000.0) {
divideAmount = 1;
} else if (sampleRate == 88200.0) {
divideAmount = 2;
} else if (sampleRate == 96000.0) {
divideAmount = 2;
} else if (sampleRate == 176400.0) {
divideAmount = 4;
} else if (sampleRate == 192000.0) {
divideAmount = 4;
} else if (sampleRate == 352800.0) {
divideAmount = 8;
} else if (sampleRate == 384000.0) {
divideAmount = 8;
} else if (sampleRate == 705600.0) {
divideAmount = 16;
} else if (sampleRate == 768000.0) {
divideAmount = 16;
}
}
json_t *dataToJson() override {
json_t *rootJ = json_object();
json_object_set_new(rootJ, "osc_modes", json_integer(virtualModule.sync3UI.modeStateBuffer));
json_object_set_new(rootJ, "scale_file", json_string(scalePath.c_str()));
return rootJ;
}
void dataFromJson(json_t *rootJ) override {
json_t *modesJ = json_object_get(rootJ, "osc_modes");
if (modesJ) {
virtualModule.sync3UI.modeStateBuffer = json_integer_value(modesJ);
virtualModule.sync3UI.loadFromEEPROM(0);
virtualModule.sync3UI.recallModuleState();
virtualModule.sync3UI.defaultEnterMenuCallback();
}
json_t *pathJ = json_object_get(rootJ, "scale_file");
if (pathJ) {
scalePath = json_string_value(pathJ);
virtualModule.readScalesFromFile(scalePath);
}
}
std::string scalePath = asset::plugin(pluginInstance, "res/original.sync3");
int32_t optimize = 0;
float lastDac1Phase = 0;
float lastDac2Phase = 0;
float lastDac3Phase = 0;
dsp::MinBlepGenerator dac1MinBlep;
dsp::MinBlepGenerator dac2MinBlep;
dsp::MinBlepGenerator dac3MinBlep;
PolyBlampGenerator dac1PolyBlamp;
PolyBlampGenerator dac2PolyBlamp;
PolyBlampGenerator dac3PolyBlamp;
float blampDelay1[3];
float blampDelay2[3];
float blampDelay3[3];
void updateAudioRateEconomy(void) {
acquireCVs();
processLogicInputs();
float dac1Sample = (float) virtualIO->outputs.dac1Samples[23];
float dac2Sample = (float) virtualIO->outputs.dac2Samples[23];
float dac3Sample = (float) virtualIO->outputs.dac3Samples[23];
int32_t inc1 = (virtualModule.increment3 + virtualModule.phaseModIncrement2) * 24;
int32_t inc2 = (virtualModule.increment4 + virtualModule.phaseModIncrement2) * 24;
int32_t inc3 = virtualModule.increment2 * 24;
if (virtualModule.sync3UI.button1Mode == 0) {
int32_t crossingDirection = crossed0(lastDac3Phase, inc3);
if (crossingDirection) {
float deltaPhase = (4294967296.f * (crossingDirection == 1) - (float)lastDac3Phase) / (float) inc3;
dac3MinBlep.insertDiscontinuity(deltaPhase - 1.0f, -4095.0 * (float) crossingDirection);
}
dac3Sample += dac3MinBlep.process();
} else if (virtualModule.sync3UI.button1Mode == 1) {
int32_t crossingDirection = crossed0(lastDac3Phase, inc3);
if (crossingDirection) {
float deltaPhase = (4294967296.f * (crossingDirection == 1) - (float)lastDac3Phase) / (float) inc3;
dac3MinBlep.insertDiscontinuity(deltaPhase - 1.0f, -4095.0 * (float) crossingDirection);
}
crossingDirection = crossed2(lastDac3Phase, inc3);
if (crossingDirection) {
float deltaPhase = (2147483648.f - lastDac3Phase) / (float) inc3;
dac3MinBlep.insertDiscontinuity(deltaPhase - 1.0f, 4095.0 * (float) crossingDirection);
}
dac3Sample += dac3MinBlep.process();
} else if (virtualModule.sync3UI.button1Mode == 2) {
int32_t crossingDirection = crossed0(lastDac3Phase, inc3);
if (crossingDirection) {
float deltaPhase = (4294967296.f * (crossingDirection == 1) - lastDac3Phase) / (float) inc3;
dac3PolyBlamp.insertDiscontinuity(deltaPhase - 1.0f, 4095.0 * 4.0f * ((float) abs(inc3) / 4294967296.f));
}
crossingDirection = crossed2(lastDac3Phase, inc3);
if (crossingDirection) {
float deltaPhase = (2147483648.f - lastDac3Phase) / (float) inc3;
dac3PolyBlamp.insertDiscontinuity(deltaPhase - 1.0f, -4095.0 * 4.0f * ((float) abs(inc3) / 4294967296.f));
}
float dac3Output = blampDelay3[0];
blampDelay3[0] = blampDelay3[1];
blampDelay3[1] = dac3Sample;
dac3Sample = dac3Output + dac3PolyBlamp.process();
}
if (virtualModule.sync3UI.button3Mode == 0) {
int32_t crossingDirection = crossed0(lastDac1Phase, inc1);
if (crossingDirection) {
float deltaPhase = (4294967296.f * (crossingDirection == 1) - (float)lastDac1Phase) / (float) inc1;
dac1MinBlep.insertDiscontinuity(deltaPhase - 1.0f, 4095.0 * (float) crossingDirection);
}
dac1Sample += dac1MinBlep.process();
} else if (virtualModule.sync3UI.button3Mode == 1) {
int32_t crossingDirection = crossed0(lastDac1Phase, inc1);
if (crossingDirection) {
float deltaPhase = (4294967296.f * (crossingDirection == 1) - (float)lastDac1Phase) / (float) inc1;
dac1MinBlep.insertDiscontinuity(deltaPhase - 1.0f, -4095.0 * (float) crossingDirection);
}
crossingDirection = crossed2(lastDac1Phase, inc1);
if (crossingDirection) {
float deltaPhase = (2147483648.f - lastDac1Phase) / (float) inc1;
dac1MinBlep.insertDiscontinuity(deltaPhase - 1.0f, 4095.0 * (float) crossingDirection);
}
dac1Sample += dac1MinBlep.process();
} else if (virtualModule.sync3UI.button3Mode == 2) {
int32_t crossingDirection = crossed0(lastDac1Phase, inc1);
if (crossingDirection) {
float deltaPhase = (4294967296.f * (crossingDirection == 1) - lastDac1Phase) / (float) inc1;
dac1PolyBlamp.insertDiscontinuity(deltaPhase - 1.0f, 4095.0 * 4.0f * ((float) abs(inc1) / 4294967296.f));
}
crossingDirection = crossed2(lastDac1Phase, inc1);
if (crossingDirection) {
float deltaPhase = (2147483648.f - lastDac1Phase) / (float) inc1;
dac1PolyBlamp.insertDiscontinuity(deltaPhase - 1.0f, -4095.0 * 4.0f * ((float) abs(inc1) / 4294967296.f));
}
float dac1Output = blampDelay1[0];
blampDelay1[0] = blampDelay1[1];
blampDelay1[1] = dac1Sample;
dac1Sample = dac1Output + dac1PolyBlamp.process();
}
if (virtualModule.sync3UI.button6Mode == 0) {
int32_t crossingDirection = crossed0(lastDac2Phase, inc2);
if (crossingDirection) {
float deltaPhase = (4294967296.f * (crossingDirection == 1) - (float)lastDac2Phase) / (float) inc2;
dac2MinBlep.insertDiscontinuity(deltaPhase - 1.0f, 4095.0 * (float) crossingDirection);
}
dac1Sample += dac1MinBlep.process();
} else if (virtualModule.sync3UI.button6Mode == 1) {
int32_t crossingDirection = crossed0(lastDac2Phase, inc2);
if (crossingDirection) {
float deltaPhase = (4294967296.f * (crossingDirection == 1) - (float)lastDac2Phase) / (float) inc2;
dac2MinBlep.insertDiscontinuity(deltaPhase - 1.0f, -4095.0 * (float) crossingDirection);
}
crossingDirection = crossed2(lastDac1Phase, inc2);
if (crossingDirection) {
float deltaPhase = (2147483648.f - lastDac2Phase) / (float) inc2;
dac2MinBlep.insertDiscontinuity(deltaPhase - 1.0f, 4095.0 * (float) crossingDirection);
}
dac1Sample += dac1MinBlep.process();
} else if (virtualModule.sync3UI.button6Mode == 2) {
int32_t crossingDirection = crossed0(lastDac2Phase, inc2);
if (crossingDirection) {
float deltaPhase = (4294967296.f * (crossingDirection == 1) - lastDac2Phase) / (float) inc2;
dac2PolyBlamp.insertDiscontinuity(deltaPhase - 1.0f, 4095.0 * 4.0f * ((float) abs(inc2) / 4294967296.f));
}
crossingDirection = crossed2(lastDac2Phase, inc2);
if (crossingDirection) {
float deltaPhase = (2147483648.f - lastDac2Phase) / (float) inc2;
dac2PolyBlamp.insertDiscontinuity(deltaPhase - 1.0f, -4095.0 * 4.0f * ((float) abs(inc2) / 4294967296.f));
}
float dac2Output = blampDelay2[0];
blampDelay2[0] = blampDelay2[1];
blampDelay2[1] = dac2Sample;
dac2Sample = dac2Output + dac2PolyBlamp.process();
}
lastDac1Phase = virtualModule.phase3;
lastDac2Phase = virtualModule.phase4;
lastDac3Phase = virtualModule.phase2;
virtualIO->halfTransferCallback();
// "model" the circuit
// A and B inputs with normalled reference voltages
float aIn = inputs[A_INPUT].isConnected() ? inputs[A_INPUT].getVoltage() : params[A_PARAM].getValue();
float bIn = inputs[B_INPUT].isConnected() ? inputs[B_INPUT].getVoltage() : 5.0;
bIn *= params[B_PARAM].getValue();
// sample and holds
// get a new sample on the rising edge at the sh control output
if (virtualIO->shAState > shALast) {
aSample = aIn;
}
if (virtualIO->shBState > shBLast) {
bSample = bIn;
}
shALast = virtualIO->shAState;
shBLast = virtualIO->shBState;
// either use the sample or track depending on the sh control output
aIn = virtualIO->shAState ? aSample : aIn;
bIn = virtualIO->shBState ? bSample : bIn;
// VCA/mixing stage
// normalize 12 bits to 0-1
outputs[MAIN_OUTPUT].setVoltage(bIn*(dac2Sample/4095.0) + aIn*(dac1Sample/4095.0));
outputs[AUX_DAC_OUTPUT].setVoltage((dac3Sample/4095.0 - .5) * -10.666666666);
outputs[LOGICA_OUTPUT].setVoltage(virtualIO->logicAState * 5.0);
outputs[AUX_LOGIC_OUTPUT].setVoltage(virtualIO->auxLogicState * 5.0);
updateLEDs();
clockDivider = 0;
}
};
void Sync3::process(const ProcessArgs &args) {
clockDivider++;
if (clockDivider >= divideAmount) {
// update the "slow IO" (not audio rate) every 16 samples
slowIOPrescaler++;
if (slowIOPrescaler == 16) {
slowIOPrescaler = 0;
updateSlowIO();
virtualModule.slowConversionCallback();
virtualModule.ui_dispatch(SENSOR_EVENT_SIG);
virtualModule.sync3UI.incrementTimer();
processTriggerButton();
}
if (!optimize) {
updateAudioRateEconomy();
} else {
updateAudioRate();
}
virtualModule.advanceMeasurementTimer();
virtualModule.advanceTimer1();
virtualModule.advanceTimer2();
}
}
struct Sync3Widget : ModuleWidget {
Sync3Widget(Sync3 *module) {
box.size = Vec(12 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/sync3.svg")));
setModule(module);
addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addParam(createParam(Vec(9.022 + .753, 30.90), module, Sync3::KNOB1_PARAM));
addParam(createParam(Vec(68.53 + .753, 30.90), module, Sync3::KNOB2_PARAM));
addParam(createParam(Vec(68.53 + .753, 169.89), module, Sync3::KNOB3_PARAM));
addParam(createParam(Vec(9.022 + .753, 169.89), module, Sync3::B_PARAM));
addParam(createParam(Vec(128.04 + .753, 30.90), module, Sync3::CV2AMT_PARAM));
addParam(createParam(Vec(128.04 + .753, 100.4), module, Sync3::A_PARAM));
addParam(createParam(Vec(128.04 + .753, 169.89), module, Sync3::CV3AMT_PARAM));
addParam(createParam(Vec(10.5 + .753, 83), module, Sync3::BUTTON1_PARAM));
addParam(createParam(Vec(47 + .753, 90), module, Sync3::BUTTON2_PARAM));
addParam(createParam(Vec(83 + .753, 83), module, Sync3::BUTTON3_PARAM));
addParam(createParam(Vec(10.5 + .753, 133), module, Sync3::BUTTON4_PARAM));
addParam(createParam(Vec(47 + .753, 129.5), module, Sync3::BUTTON5_PARAM));
addParam(createParam(Vec(83 + .753, 133), module, Sync3::BUTTON6_PARAM));
addParam(createParam(Vec(132.7 + .753, 320), module, Sync3::TRIGBUTTON_PARAM));
addInput(createInput(Vec(8.07 + 1.053, 241.12), module, Sync3::A_INPUT));
addInput(createInput(Vec(8.07 + 1.053, 282.62), module, Sync3::B_INPUT));
addInput(createInput(Vec(8.07 + 1.053, 324.02), module, Sync3::MAIN_LOGIC_INPUT));
addInput(createInput(Vec(45.75 + 1.053, 241.12), module, Sync3::CV1_INPUT));
addInput(createInput(Vec(45.75 + 1.053, 282.62), module, Sync3::CV2_INPUT));
addInput(createInput(Vec(45.75 + 1.053, 324.02), module, Sync3::CV3_INPUT));
addInput(createInput(Vec(135 + 1.053, 282.62), module, Sync3::AUX_LOGIC_INPUT));
addOutput(createOutput(Vec(83.68 + 1.053, 241.12), module, Sync3::LOGICA_OUTPUT));
addOutput(createOutput(Vec(83.68 + 1.053, 282.62), module, Sync3::AUX_DAC_OUTPUT));
addOutput(createOutput(Vec(83.68 + 1.053, 324.02), module, Sync3::MAIN_OUTPUT));
addOutput(createOutput(Vec(135 + 1.053, 241.12), module, Sync3::AUX_LOGIC_OUTPUT));
addChild(createLight(Vec(35.9 + .753, 268.5), module, Sync3::LED1_LIGHT));
addChild(createLight(Vec(73.8 + .753, 268.5), module, Sync3::LED2_LIGHT));
addChild(createLight(Vec(35.9 + .753, 309.8), module, Sync3::LED3_LIGHT));
addChild(createLight(Vec(73.8 + .753, 309.8), module, Sync3::LED4_LIGHT));
addChild(createLight(Vec(54.8 + .753, 179.6), module, Sync3::OUTPUT_GREEN_LIGHT));
addChild(createLight(Vec(59 + .753, 221), module, Sync3::RED_LIGHT));
};
void appendContextMenu(Menu *menu) override {
Sync3 *module = dynamic_cast(this->module);
struct PresetRecallItem : MenuItem {
Sync3 *module;
int preset;
void onAction(const event::Action &e) override {
module->virtualModule.sync3UI.modeStateBuffer = preset;
module->virtualModule.sync3UI.loadFromEEPROM(0);
module->virtualModule.sync3UI.recallModuleState();
}
};
struct OptimizationHandler : MenuItem {
Sync3 *module;
int32_t optimization;
void onAction(const event::Action &e) override {
module->optimize = optimization;
}
};
struct ScaleSetHandler : MenuItem {
Sync3 *module;
void onAction(const event::Action &e) override {
char* pathC = osdialog_file(OSDIALOG_OPEN, NULL, NULL, NULL);
if (!pathC) {
// Fail silently
return;
}
DEFER({
std::free(pathC);
});
module->virtualModule.readScalesFromFile(pathC);
module->scalePath = pathC;
}
};
menu->addChild(new MenuEntry);
menu->addChild(createMenuLabel("CPU Mode"));
const std::string optimization[] = {
"Optimized",
"Direct Port",
};
for (int i = 0; i < (int) LENGTHOF(optimization); i++) {
OptimizationHandler *menuItem = createMenuItem(optimization[i], CHECKMARK(module->optimize == i));
menuItem->module = module;
menuItem->optimization = i;
menu->addChild(menuItem);
}
ScaleSetHandler *menuItem = createMenuItem("Select Scale Set File");
menuItem->module = module;
menu->addChild(menuItem);
}
};
Model *modelSync3 = createModel("SYNC3");
// Tooltip definitions
struct Sync3::IRatioQuantity : ViaKnobQuantity {
std::string getDisplayValueString(void) override {
Sync3 * sync3Module = dynamic_cast(this->module);
return string::f("%d", sync3Module->virtualModule.numerator1Alt) + "/" +
string::f("%d", sync3Module->virtualModule.denominator1Select);
}
};
struct Sync3::IIRatioQuantity : ViaKnobQuantity {
std::string getDisplayValueString(void) override {
Sync3 * sync3Module = dynamic_cast(this->module);
return string::f("%i", sync3Module->virtualModule.numerator2Alt) + "/" +
string::f("%i", sync3Module->virtualModule.denominator2Select);
}
};
struct Sync3::IIIRatioQuantity : ViaKnobQuantity {
std::string getDisplayValueString(void) override {
Sync3 * sync3Module = dynamic_cast(this->module);
return string::f("%i", sync3Module->virtualModule.numerator3Alt) + "/" +
string::f("%i", sync3Module->virtualModule.denominator3Select);
}
};
struct Sync3::IButtonQuantity : ViaButtonQuantity {
std::string buttonModes[3] = {"Saw", "Square", "Triangle"};
IButtonQuantity() {
for (int i = 0; i < 3; i++) {
modes[i] = buttonModes[i];
}
}
int getModeEnumeration(void) override {
Sync3 * sync3Module = dynamic_cast(this->module);
return sync3Module->virtualModule.sync3UI.button1Mode;
}
void setMode(int mode) override {
Sync3 * sync3Module = dynamic_cast(this->module);
sync3Module->virtualModule.sync3UI.button1Mode = mode;
sync3Module->virtualModule.sync3UI.storeMode(sync3Module->virtualModule.sync3UI.button1Mode, BUTTON1_MASK, BUTTON1_SHIFT);
sync3Module->virtualModule.handleButton1ModeChange(mode);
}
};
struct Sync3::RatioButtonQuantity : ViaButtonQuantity {
std::string buttonModes[8] = {"Rhythms", "Integers", "Open Intervals", "Circle of Fifths", "Major Arp", "Minor Arp", "Microtonal", "Bohlen-Pierce (Spooky)"};
RatioButtonQuantity() {
for (int i = 0; i < 8; i++) {
modes[i] = buttonModes[i];
}
}
int getModeEnumeration(void) override {
Sync3 * sync3Module = dynamic_cast(this->module);
return sync3Module->virtualModule.sync3UI.button2Mode;
}
void setMode(int mode) override {
Sync3 * sync3Module = dynamic_cast(this->module);
sync3Module->virtualModule.sync3UI.button2Mode = mode;
sync3Module->virtualModule.sync3UI.storeMode(sync3Module->virtualModule.sync3UI.button2Mode, BUTTON2_MASK, BUTTON2_SHIFT);
sync3Module->virtualModule.handleButton2ModeChange(mode);
}
};
struct Sync3::IIButtonQuantity : ViaButtonQuantity {
std::string buttonModes[3] = {"Saw", "Square", "Triangle"};
IIButtonQuantity() {
for (int i = 0; i < 3; i++) {
modes[i] = buttonModes[i];
}
}
int getModeEnumeration(void) override {
Sync3 * sync3Module = dynamic_cast(this->module);
return sync3Module->virtualModule.sync3UI.button3Mode;
}
void setMode(int mode) override {
Sync3 * sync3Module = dynamic_cast(this->module);
sync3Module->virtualModule.sync3UI.button3Mode = mode;
sync3Module->virtualModule.sync3UI.storeMode(sync3Module->virtualModule.sync3UI.button3Mode, BUTTON3_MASK, BUTTON3_SHIFT);
sync3Module->virtualModule.handleButton3ModeChange(mode);
}
};
struct Sync3::CVButtonQuantity : ViaButtonQuantity {
std::string buttonModes[2] = {"Independent Ratios", "II + III and Phase Modulation"};
CVButtonQuantity() {
for (int i = 0; i < 2; i++) {
modes[i] = buttonModes[i];
}
}
int getModeEnumeration(void) override {
Sync3 * sync3Module = dynamic_cast(this->module);
return sync3Module->virtualModule.sync3UI.button4Mode;
}
void setMode(int mode) override {
Sync3 * sync3Module = dynamic_cast(this->module);
sync3Module->virtualModule.sync3UI.button4Mode = mode;
sync3Module->virtualModule.sync3UI.storeMode(sync3Module->virtualModule.sync3UI.button4Mode, BUTTON4_MASK, BUTTON4_SHIFT);
sync3Module->virtualModule.handleButton4ModeChange(mode);
}
};
struct Sync3::IIIButtonQuantity : ViaButtonQuantity {
std::string buttonModes[3] = {"Saw", "Square", "Triangle"};
IIIButtonQuantity() {
for (int i = 0; i < 3; i++) {
modes[i] = buttonModes[i];
}
}
int getModeEnumeration(void) override {
Sync3 * sync3Module = dynamic_cast(this->module);
return sync3Module->virtualModule.sync3UI.button6Mode;
}
void setMode(int mode) override {
Sync3 * sync3Module = dynamic_cast(this->module);
sync3Module->virtualModule.sync3UI.button6Mode = mode;
sync3Module->virtualModule.sync3UI.storeMode(sync3Module->virtualModule.sync3UI.button6Mode, BUTTON6_MASK, BUTTON6_SHIFT);
sync3Module->virtualModule.handleButton6ModeChange(mode);
}
};