@@ -320,6 +320,7 @@ struct BraidsSettingItem : MenuItem { | |||||
} | } | ||||
void step() override { | void step() override { | ||||
rightText = (*setting == onValue) ? "✔" : ""; | rightText = (*setting == onValue) ? "✔" : ""; | ||||
MenuItem::step(); | |||||
} | } | ||||
}; | }; | ||||
@@ -330,6 +331,7 @@ struct BraidsLowCpuItem : MenuItem { | |||||
} | } | ||||
void step() override { | void step() override { | ||||
rightText = (braids->lowCpu) ? "✔" : ""; | rightText = (braids->lowCpu) ? "✔" : ""; | ||||
MenuItem::step(); | |||||
} | } | ||||
}; | }; | ||||
@@ -121,6 +121,7 @@ BranchesWidget::BranchesWidget() { | |||||
addChild(createLight<SmallLight<GreenRedLight>>(Vec(40, 325), module, Branches::STATE2_POS_LIGHT)); | addChild(createLight<SmallLight<GreenRedLight>>(Vec(40, 325), module, Branches::STATE2_POS_LIGHT)); | ||||
} | } | ||||
struct BranchesModeItem : MenuItem { | struct BranchesModeItem : MenuItem { | ||||
Branches *branches; | Branches *branches; | ||||
int channel; | int channel; | ||||
@@ -129,9 +130,11 @@ struct BranchesModeItem : MenuItem { | |||||
} | } | ||||
void step() override { | void step() override { | ||||
rightText = branches->mode[channel] ? "Toggle" : "Latch"; | rightText = branches->mode[channel] ? "Toggle" : "Latch"; | ||||
MenuItem::step(); | |||||
} | } | ||||
}; | }; | ||||
Menu *BranchesWidget::createContextMenu() { | Menu *BranchesWidget::createContextMenu() { | ||||
Menu *menu = ModuleWidget::createContextMenu(); | Menu *menu = ModuleWidget::createContextMenu(); | ||||
@@ -139,6 +142,7 @@ Menu *BranchesWidget::createContextMenu() { | |||||
assert(branches); | assert(branches); | ||||
menu->pushChild(construct<MenuLabel>()); | menu->pushChild(construct<MenuLabel>()); | ||||
menu->pushChild(construct<MenuLabel>(&MenuEntry::text, "Channels")); | menu->pushChild(construct<MenuLabel>(&MenuEntry::text, "Channels")); | ||||
menu->pushChild(construct<BranchesModeItem>(&MenuEntry::text, "Channel 1 mode", &BranchesModeItem::branches, branches, &BranchesModeItem::channel, 0)); | menu->pushChild(construct<BranchesModeItem>(&MenuEntry::text, "Channel 1 mode", &BranchesModeItem::branches, branches, &BranchesModeItem::channel, 0)); | ||||
menu->pushChild(construct<BranchesModeItem>(&MenuEntry::text, "Channel 2 mode", &BranchesModeItem::branches, branches, &BranchesModeItem::channel, 1)); | menu->pushChild(construct<BranchesModeItem>(&MenuEntry::text, "Channel 2 mode", &BranchesModeItem::branches, branches, &BranchesModeItem::channel, 1)); | ||||
@@ -3,6 +3,7 @@ | |||||
#include "dsp/samplerate.hpp" | #include "dsp/samplerate.hpp" | ||||
#include "dsp/ringbuffer.hpp" | #include "dsp/ringbuffer.hpp" | ||||
#include "dsp/digital.hpp" | #include "dsp/digital.hpp" | ||||
#include "dsp/vumeter.hpp" | |||||
#include "clouds/dsp/granular_processor.h" | #include "clouds/dsp/granular_processor.h" | ||||
@@ -63,8 +64,11 @@ struct Clouds : Module { | |||||
SchmittTrigger freezeTrigger; | SchmittTrigger freezeTrigger; | ||||
bool freeze = false; | bool freeze = false; | ||||
SchmittTrigger modeTrigger; | |||||
int modeIndex = 0; | |||||
SchmittTrigger blendTrigger; | |||||
int blendIndex = 0; | |||||
clouds::PlaybackMode playback; | |||||
int quality = 0; | |||||
Clouds(); | Clouds(); | ||||
~Clouds(); | ~Clouds(); | ||||
@@ -72,7 +76,30 @@ struct Clouds : Module { | |||||
void reset() override { | void reset() override { | ||||
freeze = false; | freeze = false; | ||||
modeIndex = 0; | |||||
blendIndex = 0; | |||||
playback = clouds::PLAYBACK_MODE_GRANULAR; | |||||
quality = 0; | |||||
} | |||||
json_t *toJson() override { | |||||
json_t *rootJ = json_object(); | |||||
json_object_set_new(rootJ, "playback", json_integer((int) playback)); | |||||
json_object_set_new(rootJ, "quality", json_integer(quality)); | |||||
return rootJ; | |||||
} | |||||
void fromJson(json_t *rootJ) override { | |||||
json_t *playbackJ = json_object_get(rootJ, "playback"); | |||||
if (playbackJ) { | |||||
playback = (clouds::PlaybackMode) json_integer_value(playbackJ); | |||||
} | |||||
json_t *qualityJ = json_object_get(rootJ, "quality"); | |||||
if (qualityJ) { | |||||
quality = json_integer_value(qualityJ); | |||||
} | |||||
} | } | ||||
}; | }; | ||||
@@ -86,6 +113,7 @@ Clouds::Clouds() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { | |||||
memset(processor, 0, sizeof(*processor)); | memset(processor, 0, sizeof(*processor)); | ||||
processor->Init(block_mem, memLen, block_ccm, ccmLen); | processor->Init(block_mem, memLen, block_ccm, ccmLen); | ||||
reset(); | |||||
} | } | ||||
Clouds::~Clouds() { | Clouds::~Clouds() { | ||||
@@ -96,8 +124,8 @@ Clouds::~Clouds() { | |||||
void Clouds::step() { | void Clouds::step() { | ||||
// Get input | // Get input | ||||
Frame<2> inputFrame = {}; | |||||
if (!inputBuffer.full()) { | if (!inputBuffer.full()) { | ||||
Frame<2> inputFrame; | |||||
inputFrame.samples[0] = inputs[IN_L_INPUT].value * params[IN_GAIN_PARAM].value / 5.0; | inputFrame.samples[0] = inputs[IN_L_INPUT].value * params[IN_GAIN_PARAM].value / 5.0; | ||||
inputFrame.samples[1] = inputs[IN_R_INPUT].active ? inputs[IN_R_INPUT].value * params[IN_GAIN_PARAM].value / 5.0 : inputFrame.samples[0]; | inputFrame.samples[1] = inputs[IN_R_INPUT].active ? inputs[IN_R_INPUT].value * params[IN_GAIN_PARAM].value / 5.0 : inputFrame.samples[0]; | ||||
inputBuffer.push(inputFrame); | inputBuffer.push(inputFrame); | ||||
@@ -106,8 +134,8 @@ void Clouds::step() { | |||||
if (freezeTrigger.process(params[FREEZE_PARAM].value)) { | if (freezeTrigger.process(params[FREEZE_PARAM].value)) { | ||||
freeze ^= true; | freeze ^= true; | ||||
} | } | ||||
if (modeTrigger.process(params[MODE_PARAM].value)) { | |||||
modeIndex = (modeIndex + 1) % 4; | |||||
if (blendTrigger.process(params[MODE_PARAM].value)) { | |||||
blendIndex = (blendIndex + 1) % 4; | |||||
} | } | ||||
// Trigger | // Trigger | ||||
@@ -135,16 +163,14 @@ void Clouds::step() { | |||||
} | } | ||||
// Set up processor | // Set up processor | ||||
processor->set_num_channels(2); | |||||
processor->set_low_fidelity(false); | |||||
// TODO Support the other modes | |||||
processor->set_playback_mode(clouds::PLAYBACK_MODE_GRANULAR); | |||||
processor->set_playback_mode(playback); | |||||
processor->set_quality(quality); | |||||
processor->Prepare(); | processor->Prepare(); | ||||
clouds::Parameters* p = processor->mutable_parameters(); | |||||
clouds::Parameters *p = processor->mutable_parameters(); | |||||
p->trigger = triggered; | p->trigger = triggered; | ||||
p->gate = triggered; | p->gate = triggered; | ||||
p->freeze = freeze; | |||||
p->freeze = freeze || (inputs[FREEZE_INPUT].value >= 1.0); | |||||
p->position = clampf(params[POSITION_PARAM].value + inputs[POSITION_INPUT].value / 5.0, 0.0, 1.0); | p->position = clampf(params[POSITION_PARAM].value + inputs[POSITION_INPUT].value / 5.0, 0.0, 1.0); | ||||
p->size = clampf(params[SIZE_PARAM].value + inputs[SIZE_INPUT].value / 5.0, 0.0, 1.0); | p->size = clampf(params[SIZE_PARAM].value + inputs[SIZE_INPUT].value / 5.0, 0.0, 1.0); | ||||
p->pitch = clampf((params[PITCH_PARAM].value + inputs[PITCH_INPUT].value) * 12.0, -48.0, 48.0); | p->pitch = clampf((params[PITCH_PARAM].value + inputs[PITCH_INPUT].value) * 12.0, -48.0, 48.0); | ||||
@@ -153,6 +179,8 @@ void Clouds::step() { | |||||
p->dry_wet = clampf(params[BLEND_PARAM].value + inputs[BLEND_INPUT].value / 5.0, 0.0, 1.0); | p->dry_wet = clampf(params[BLEND_PARAM].value + inputs[BLEND_INPUT].value / 5.0, 0.0, 1.0); | ||||
p->stereo_spread = params[SPREAD_PARAM].value; | p->stereo_spread = params[SPREAD_PARAM].value; | ||||
p->feedback = params[FEEDBACK_PARAM].value; | p->feedback = params[FEEDBACK_PARAM].value; | ||||
// TODO | |||||
// Why doesn't dry audio get reverbed? | |||||
p->reverb = params[REVERB_PARAM].value; | p->reverb = params[REVERB_PARAM].value; | ||||
clouds::ShortFrame output[32]; | clouds::ShortFrame output[32]; | ||||
@@ -174,22 +202,31 @@ void Clouds::step() { | |||||
} | } | ||||
triggered = false; | triggered = false; | ||||
// Lights | |||||
lights[FREEZE_LIGHT].value = freeze ? 1.0 : 0.0; | |||||
// TODO | |||||
lights[MIX_GREEN_LIGHT].value = 1.0; | |||||
lights[PAN_GREEN_LIGHT].value = 1.0; | |||||
lights[FEEDBACK_GREEN_LIGHT].value = 1.0; | |||||
lights[REVERB_GREEN_LIGHT].value = 1.0; | |||||
} | } | ||||
// Set output | // Set output | ||||
Frame<2> outputFrame = {}; | |||||
if (!outputBuffer.empty()) { | if (!outputBuffer.empty()) { | ||||
Frame<2> outputFrame = outputBuffer.shift(); | |||||
outputFrame = outputBuffer.shift(); | |||||
outputs[OUT_L_OUTPUT].value = 5.0 * outputFrame.samples[0]; | outputs[OUT_L_OUTPUT].value = 5.0 * outputFrame.samples[0]; | ||||
outputs[OUT_R_OUTPUT].value = 5.0 * outputFrame.samples[1]; | outputs[OUT_R_OUTPUT].value = 5.0 * outputFrame.samples[1]; | ||||
} | } | ||||
// Lights | |||||
clouds::Parameters *p = processor->mutable_parameters(); | |||||
VUMeter vuMeter; | |||||
vuMeter.dBInterval = 6.0; | |||||
Frame<2> lightFrame = p->freeze ? outputFrame : inputFrame; | |||||
vuMeter.setValue(fmaxf(fabsf(lightFrame.samples[0]), fabsf(lightFrame.samples[1]))); | |||||
lights[FREEZE_LIGHT].setBrightness(p->freeze ? 0.75 : 0.0); | |||||
lights[MIX_GREEN_LIGHT].setBrightnessSmooth(vuMeter.getBrightness(3)); | |||||
lights[PAN_GREEN_LIGHT].setBrightnessSmooth(vuMeter.getBrightness(2)); | |||||
lights[FEEDBACK_GREEN_LIGHT].setBrightnessSmooth(vuMeter.getBrightness(1)); | |||||
lights[REVERB_GREEN_LIGHT].setBrightness(0.0); | |||||
lights[MIX_RED_LIGHT].setBrightness(0.0); | |||||
lights[PAN_RED_LIGHT].setBrightness(0.0); | |||||
lights[FEEDBACK_RED_LIGHT].setBrightnessSmooth(vuMeter.getBrightness(1)); | |||||
lights[REVERB_RED_LIGHT].setBrightnessSmooth(vuMeter.getBrightness(0)); | |||||
} | } | ||||
@@ -260,37 +297,78 @@ CloudsWidget::CloudsWidget() { | |||||
void CloudsWidget::step() { | void CloudsWidget::step() { | ||||
Clouds *module = dynamic_cast<Clouds*>(this->module); | Clouds *module = dynamic_cast<Clouds*>(this->module); | ||||
blendParam->visible = (module->modeIndex == 0); | |||||
spreadParam->visible = (module->modeIndex == 1); | |||||
feedbackParam->visible = (module->modeIndex == 2); | |||||
reverbParam->visible = (module->modeIndex == 3); | |||||
blendParam->visible = (module->blendIndex == 0); | |||||
spreadParam->visible = (module->blendIndex == 1); | |||||
feedbackParam->visible = (module->blendIndex == 2); | |||||
reverbParam->visible = (module->blendIndex == 3); | |||||
ModuleWidget::step(); | ModuleWidget::step(); | ||||
} | } | ||||
struct CloudsModeItem : MenuItem { | |||||
struct CloudsBlendItem : MenuItem { | |||||
Clouds *module; | |||||
int blendIndex; | |||||
void onAction(EventAction &e) override { | |||||
module->blendIndex = blendIndex; | |||||
} | |||||
void step() override { | |||||
rightText = (module->blendIndex == blendIndex) ? "✔" : ""; | |||||
MenuItem::step(); | |||||
} | |||||
}; | |||||
struct CloudsPlaybackItem : MenuItem { | |||||
Clouds *module; | |||||
clouds::PlaybackMode playback; | |||||
void onAction(EventAction &e) override { | |||||
module->playback = playback; | |||||
} | |||||
void step() override { | |||||
rightText = (module->playback == playback) ? "✔" : ""; | |||||
MenuItem::step(); | |||||
} | |||||
}; | |||||
struct CloudsQualityItem : MenuItem { | |||||
Clouds *module; | Clouds *module; | ||||
int modeIndex; | |||||
int quality; | |||||
void onAction(EventAction &e) override { | void onAction(EventAction &e) override { | ||||
module->modeIndex = modeIndex; | |||||
module->quality = quality; | |||||
} | } | ||||
void step() override { | void step() override { | ||||
rightText = (module->modeIndex == modeIndex) ? "✔" : ""; | |||||
rightText = (module->quality == quality) ? "✔" : ""; | |||||
MenuItem::step(); | |||||
} | } | ||||
}; | }; | ||||
Menu *CloudsWidget::createContextMenu() { | Menu *CloudsWidget::createContextMenu() { | ||||
Menu *menu = ModuleWidget::createContextMenu(); | Menu *menu = ModuleWidget::createContextMenu(); | ||||
Clouds *module = dynamic_cast<Clouds*>(this->module); | Clouds *module = dynamic_cast<Clouds*>(this->module); | ||||
menu->pushChild(construct<MenuLabel>()); | menu->pushChild(construct<MenuLabel>()); | ||||
menu->pushChild(construct<MenuLabel>(&MenuEntry::text, "Blend knob")); | menu->pushChild(construct<MenuLabel>(&MenuEntry::text, "Blend knob")); | ||||
menu->pushChild(construct<CloudsModeItem>(&MenuEntry::text, "Wet/dry", &CloudsModeItem::module, module, &CloudsModeItem::modeIndex, 0)); | |||||
menu->pushChild(construct<CloudsModeItem>(&MenuEntry::text, "Spread", &CloudsModeItem::module, module, &CloudsModeItem::modeIndex, 1)); | |||||
menu->pushChild(construct<CloudsModeItem>(&MenuEntry::text, "Feedback", &CloudsModeItem::module, module, &CloudsModeItem::modeIndex, 2)); | |||||
menu->pushChild(construct<CloudsModeItem>(&MenuEntry::text, "Reverb", &CloudsModeItem::module, module, &CloudsModeItem::modeIndex, 3)); | |||||
menu->pushChild(construct<CloudsBlendItem>(&MenuEntry::text, "Wet/dry", &CloudsBlendItem::module, module, &CloudsBlendItem::blendIndex, 0)); | |||||
menu->pushChild(construct<CloudsBlendItem>(&MenuEntry::text, "Spread", &CloudsBlendItem::module, module, &CloudsBlendItem::blendIndex, 1)); | |||||
menu->pushChild(construct<CloudsBlendItem>(&MenuEntry::text, "Feedback", &CloudsBlendItem::module, module, &CloudsBlendItem::blendIndex, 2)); | |||||
menu->pushChild(construct<CloudsBlendItem>(&MenuEntry::text, "Reverb", &CloudsBlendItem::module, module, &CloudsBlendItem::blendIndex, 3)); | |||||
menu->pushChild(construct<MenuLabel>()); | |||||
menu->pushChild(construct<MenuLabel>(&MenuEntry::text, "Alternative mode")); | |||||
menu->pushChild(construct<CloudsPlaybackItem>(&MenuEntry::text, "Granular", &CloudsPlaybackItem::module, module, &CloudsPlaybackItem::playback, clouds::PLAYBACK_MODE_GRANULAR)); | |||||
menu->pushChild(construct<CloudsPlaybackItem>(&MenuEntry::text, "Pitch-shifter/time-stretcher", &CloudsPlaybackItem::module, module, &CloudsPlaybackItem::playback, clouds::PLAYBACK_MODE_STRETCH)); | |||||
menu->pushChild(construct<CloudsPlaybackItem>(&MenuEntry::text, "Looping delay", &CloudsPlaybackItem::module, module, &CloudsPlaybackItem::playback, clouds::PLAYBACK_MODE_LOOPING_DELAY)); | |||||
menu->pushChild(construct<CloudsPlaybackItem>(&MenuEntry::text, "Spectral madness", &CloudsPlaybackItem::module, module, &CloudsPlaybackItem::playback, clouds::PLAYBACK_MODE_SPECTRAL)); | |||||
menu->pushChild(construct<MenuLabel>()); | |||||
menu->pushChild(construct<MenuLabel>(&MenuEntry::text, "Quality")); | |||||
menu->pushChild(construct<CloudsQualityItem>(&MenuEntry::text, "1s 32kHz 16-bit stereo", &CloudsQualityItem::module, module, &CloudsQualityItem::quality, 0)); | |||||
menu->pushChild(construct<CloudsQualityItem>(&MenuEntry::text, "2s 32kHz 16-bit mono", &CloudsQualityItem::module, module, &CloudsQualityItem::quality, 1)); | |||||
menu->pushChild(construct<CloudsQualityItem>(&MenuEntry::text, "4s 16kHz 8-bit µ-law stereo", &CloudsQualityItem::module, module, &CloudsQualityItem::quality, 2)); | |||||
menu->pushChild(construct<CloudsQualityItem>(&MenuEntry::text, "8s 16kHz 8-bit µ-law mono", &CloudsQualityItem::module, module, &CloudsQualityItem::quality, 3)); | |||||
return menu; | return menu; | ||||
} | } |
@@ -199,7 +199,7 @@ void Elements::step() { | |||||
} | } | ||||
// Set lights | // Set lights | ||||
lights[GATE_LIGHT].setBrightness(performance.gate ? 0.5 : 0.0); | |||||
lights[GATE_LIGHT].setBrightness(performance.gate ? 0.75 : 0.0); | |||||
lights[EXCITER_LIGHT].setBrightness(part->exciter_level()); | lights[EXCITER_LIGHT].setBrightness(part->exciter_level()); | ||||
lights[RESONATOR_LIGHT].setBrightness(part->resonator_level()); | lights[RESONATOR_LIGHT].setBrightness(part->resonator_level()); | ||||
} | } | ||||
@@ -286,10 +286,14 @@ ElementsWidget::ElementsWidget() { | |||||
addParam(createParam<CKD6>(Vec(36, 116), module, Elements::PLAY_PARAM, 0.0, 1.0, 0.0)); | addParam(createParam<CKD6>(Vec(36, 116), module, Elements::PLAY_PARAM, 0.0, 1.0, 0.0)); | ||||
ModuleLightWidget *gateLight = createLight<YellowLight>(Vec(36+3, 116+3), module, Elements::GATE_LIGHT); | |||||
gateLight->bgColor = COLOR_BLACK_TRANSPARENT; | |||||
gateLight->box.size = Vec(28-6, 28-6); | |||||
addChild(gateLight); | |||||
struct GateLight : YellowLight { | |||||
GateLight() { | |||||
box.size = Vec(28-6, 28-6); | |||||
bgColor = COLOR_BLACK_TRANSPARENT; | |||||
} | |||||
}; | |||||
addChild(createLight<GateLight>(Vec(36+3, 116+3), module, Elements::GATE_LIGHT)); | |||||
addChild(createLight<MediumLight<GreenLight>>(Vec(184, 165), module, Elements::EXCITER_LIGHT)); | addChild(createLight<MediumLight<GreenLight>>(Vec(184, 165), module, Elements::EXCITER_LIGHT)); | ||||
addChild(createLight<MediumLight<RedLight>>(Vec(395, 165), module, Elements::RESONATOR_LIGHT)); | addChild(createLight<MediumLight<RedLight>>(Vec(395, 165), module, Elements::RESONATOR_LIGHT)); | ||||
} | } | ||||
@@ -302,6 +306,7 @@ struct ElementsModalItem : MenuItem { | |||||
} | } | ||||
void step() override { | void step() override { | ||||
rightText = (elements->getModel() == model) ? "✔" : ""; | rightText = (elements->getModel() == model) ? "✔" : ""; | ||||
MenuItem::step(); | |||||
} | } | ||||
}; | }; | ||||
@@ -311,7 +316,7 @@ Menu *ElementsWidget::createContextMenu() { | |||||
Elements *elements = dynamic_cast<Elements*>(module); | Elements *elements = dynamic_cast<Elements*>(module); | ||||
assert(elements); | assert(elements); | ||||
menu->pushChild(construct<MenuLabel>()); | |||||
menu->pushChild(construct<MenuEntry>()); | |||||
menu->pushChild(construct<MenuLabel>(&MenuEntry::text, "Alternative models")); | menu->pushChild(construct<MenuLabel>(&MenuEntry::text, "Alternative models")); | ||||
menu->pushChild(construct<ElementsModalItem>(&MenuEntry::text, "Original", &ElementsModalItem::elements, elements, &ElementsModalItem::model, 0)); | menu->pushChild(construct<ElementsModalItem>(&MenuEntry::text, "Original", &ElementsModalItem::elements, elements, &ElementsModalItem::model, 0)); | ||||
menu->pushChild(construct<ElementsModalItem>(&MenuEntry::text, "Non-linear string", &ElementsModalItem::elements, elements, &ElementsModalItem::model, 1)); | menu->pushChild(construct<ElementsModalItem>(&MenuEntry::text, "Non-linear string", &ElementsModalItem::elements, elements, &ElementsModalItem::model, 1)); | ||||
@@ -50,7 +50,6 @@ struct Frames : Module { | |||||
SchmittTrigger addTrigger; | SchmittTrigger addTrigger; | ||||
SchmittTrigger delTrigger; | SchmittTrigger delTrigger; | ||||
bool clearKeyframes = false; | |||||
Frames(); | Frames(); | ||||
void step() override; | void step() override; | ||||
@@ -71,7 +70,14 @@ struct Frames : Module { | |||||
} | } | ||||
json_object_set_new(rootJ, "keyframes", keyframesJ); | json_object_set_new(rootJ, "keyframes", keyframesJ); | ||||
// TODO Channel settings | |||||
json_t *channelsJ = json_array(); | |||||
for (int i = 0; i < 4; i++) { | |||||
json_t *channelJ = json_object(); | |||||
json_object_set_new(channelJ, "curve", json_integer((int) keyframer.mutable_settings(i)->easing_curve)); | |||||
json_object_set_new(channelJ, "response", json_integer(keyframer.mutable_settings(i)->response)); | |||||
json_array_append_new(channelsJ, channelJ); | |||||
} | |||||
json_object_set_new(rootJ, "channels", channelsJ); | |||||
return rootJ; | return rootJ; | ||||
} | } | ||||
@@ -95,7 +101,18 @@ struct Frames : Module { | |||||
} | } | ||||
} | } | ||||
// TODO Channel settings | |||||
json_t *channelsJ = json_object_get(rootJ, "channels"); | |||||
if (channelsJ) { | |||||
for (int i = 0; i < 4; i++) { | |||||
json_t *channelJ = json_array_get(channelsJ, i); | |||||
if (channelJ) { | |||||
json_t *curveJ = json_object_get(channelJ, "curve"); | |||||
keyframer.mutable_settings(i)->easing_curve = (frames::EasingCurve) json_integer_value(curveJ); | |||||
json_t *responseJ = json_object_get(channelJ, "response"); | |||||
keyframer.mutable_settings(i)->response = json_integer_value(responseJ); | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
void reset() override { | void reset() override { | ||||
@@ -323,9 +340,11 @@ struct FramesCurveItem : MenuItem { | |||||
} | } | ||||
void step() override { | void step() override { | ||||
rightText = (frames->keyframer.mutable_settings(channel)->easing_curve == curve) ? "✔" : ""; | rightText = (frames->keyframer.mutable_settings(channel)->easing_curve == curve) ? "✔" : ""; | ||||
MenuItem::step(); | |||||
} | } | ||||
}; | }; | ||||
struct FramesResponseItem : MenuItem { | struct FramesResponseItem : MenuItem { | ||||
Frames *frames; | Frames *frames; | ||||
uint8_t channel; | uint8_t channel; | ||||
@@ -335,9 +354,11 @@ struct FramesResponseItem : MenuItem { | |||||
} | } | ||||
void step() override { | void step() override { | ||||
rightText = (frames->keyframer.mutable_settings(channel)->response = response) ? "✔" : ""; | rightText = (frames->keyframer.mutable_settings(channel)->response = response) ? "✔" : ""; | ||||
MenuItem::step(); | |||||
} | } | ||||
}; | }; | ||||
struct FramesChannelSettingsItem : MenuItem { | struct FramesChannelSettingsItem : MenuItem { | ||||
Frames *frames; | Frames *frames; | ||||
uint8_t channel; | uint8_t channel; | ||||
@@ -361,6 +382,7 @@ struct FramesChannelSettingsItem : MenuItem { | |||||
} | } | ||||
}; | }; | ||||
struct FramesClearItem : MenuItem { | struct FramesClearItem : MenuItem { | ||||
Frames *frames; | Frames *frames; | ||||
void onAction(EventAction &e) override { | void onAction(EventAction &e) override { | ||||
@@ -368,6 +390,7 @@ struct FramesClearItem : MenuItem { | |||||
} | } | ||||
}; | }; | ||||
struct FramesModeItem : MenuItem { | struct FramesModeItem : MenuItem { | ||||
Frames *frames; | Frames *frames; | ||||
bool poly_lfo_mode; | bool poly_lfo_mode; | ||||
@@ -376,6 +399,7 @@ struct FramesModeItem : MenuItem { | |||||
} | } | ||||
void step() override { | void step() override { | ||||
rightText = (frames->poly_lfo_mode == poly_lfo_mode) ? "✔" : ""; | rightText = (frames->poly_lfo_mode == poly_lfo_mode) ? "✔" : ""; | ||||
MenuItem::step(); | |||||
} | } | ||||
}; | }; | ||||
@@ -60,11 +60,11 @@ struct Rings : Module { | |||||
rings::Strummer strummer; | rings::Strummer strummer; | ||||
bool strum = false; | bool strum = false; | ||||
bool lastStrum = false; | bool lastStrum = false; | ||||
SchmittTrigger polyphonyTrigger; | SchmittTrigger polyphonyTrigger; | ||||
SchmittTrigger modelTrigger; | SchmittTrigger modelTrigger; | ||||
int polyphonyMode = 0; | int polyphonyMode = 0; | ||||
int model = 0; | |||||
rings::ResonatorModel model = rings::RESONATOR_MODEL_MODAL; | |||||
bool easterEgg = false; | bool easterEgg = false; | ||||
Rings(); | Rings(); | ||||
@@ -74,7 +74,7 @@ struct Rings : Module { | |||||
json_t *rootJ = json_object(); | json_t *rootJ = json_object(); | ||||
json_object_set_new(rootJ, "polyphony", json_integer(polyphonyMode)); | json_object_set_new(rootJ, "polyphony", json_integer(polyphonyMode)); | ||||
json_object_set_new(rootJ, "model", json_integer(model)); | |||||
json_object_set_new(rootJ, "model", json_integer((int) model)); | |||||
json_object_set_new(rootJ, "easterEgg", json_boolean(easterEgg)); | json_object_set_new(rootJ, "easterEgg", json_boolean(easterEgg)); | ||||
return rootJ; | return rootJ; | ||||
@@ -88,7 +88,7 @@ struct Rings : Module { | |||||
json_t *modelJ = json_object_get(rootJ, "model"); | json_t *modelJ = json_object_get(rootJ, "model"); | ||||
if (modelJ) { | if (modelJ) { | ||||
model = json_integer_value(modelJ); | |||||
model = (rings::ResonatorModel) json_integer_value(modelJ); | |||||
} | } | ||||
json_t *easterEggJ = json_object_get(rootJ, "easterEgg"); | json_t *easterEggJ = json_object_get(rootJ, "easterEgg"); | ||||
@@ -99,12 +99,12 @@ struct Rings : Module { | |||||
void reset() override { | void reset() override { | ||||
polyphonyMode = 0; | polyphonyMode = 0; | ||||
model = 0; | |||||
model = rings::RESONATOR_MODEL_MODAL; | |||||
} | } | ||||
void randomize() override { | void randomize() override { | ||||
polyphonyMode = randomu32() % 3; | polyphonyMode = randomu32() % 3; | ||||
model = randomu32() % 3; | |||||
model = (rings::ResonatorModel) (randomu32() % 3); | |||||
} | } | ||||
}; | }; | ||||
@@ -144,10 +144,11 @@ void Rings::step() { | |||||
lights[POLYPHONY_RED_LIGHT].value = (polyphonyMode == 1 || polyphonyMode == 2) ? 1.0 : 0.0; | lights[POLYPHONY_RED_LIGHT].value = (polyphonyMode == 1 || polyphonyMode == 2) ? 1.0 : 0.0; | ||||
if (modelTrigger.process(params[RESONATOR_PARAM].value)) { | if (modelTrigger.process(params[RESONATOR_PARAM].value)) { | ||||
model = (model + 1) % 3; | |||||
model = (rings::ResonatorModel) ((model + 1) % 3); | |||||
} | } | ||||
lights[RESONATOR_GREEN_LIGHT].value = (model == 0 || model == 1) ? 1.0 : 0.0; | |||||
lights[RESONATOR_RED_LIGHT].value = (model == 1 || model == 2) ? 1.0 : 0.0; | |||||
int modelColor = model % 3; | |||||
lights[RESONATOR_GREEN_LIGHT].value = (modelColor == 0 || modelColor == 1) ? 1.0 : 0.0; | |||||
lights[RESONATOR_RED_LIGHT].value = (modelColor == 1 || modelColor == 2) ? 1.0 : 0.0; | |||||
// Render frames | // Render frames | ||||
if (outputBuffer.empty()) { | if (outputBuffer.empty()) { | ||||
@@ -161,11 +162,15 @@ void Rings::step() { | |||||
inputBuffer.startIncr(inLen); | inputBuffer.startIncr(inLen); | ||||
} | } | ||||
// Polyphony / model | |||||
int polyphony = 1<<polyphonyMode; | |||||
// Polyphony | |||||
int polyphony = 1 << polyphonyMode; | |||||
if (part.polyphony() != polyphony) | if (part.polyphony() != polyphony) | ||||
part.set_polyphony(polyphony); | part.set_polyphony(polyphony); | ||||
part.set_model((rings::ResonatorModel)model); | |||||
// Model | |||||
if (easterEgg) | |||||
string_synth.set_fx((rings::FxType) model); | |||||
else | |||||
part.set_model(model); | |||||
// Patch | // Patch | ||||
rings::Patch patch; | rings::Patch patch; | ||||
@@ -288,11 +293,23 @@ RingsWidget::RingsWidget() { | |||||
addOutput(createOutput<PJ301MPort>(Vec(131, 316), module, Rings::ODD_OUTPUT)); | addOutput(createOutput<PJ301MPort>(Vec(131, 316), module, Rings::ODD_OUTPUT)); | ||||
addOutput(createOutput<PJ301MPort>(Vec(169, 316), module, Rings::EVEN_OUTPUT)); | addOutput(createOutput<PJ301MPort>(Vec(169, 316), module, Rings::EVEN_OUTPUT)); | ||||
addChild(createLight<SmallLight<GreenRedLight>>(Vec(38, 43.8), module, Rings::POLYPHONY_GREEN_LIGHT)); | |||||
addChild(createLight<SmallLight<GreenRedLight>>(Vec(163, 43.8), module, Rings::RESONATOR_GREEN_LIGHT)); | |||||
addChild(createLight<MediumLight<GreenRedLight>>(Vec(37, 43), module, Rings::POLYPHONY_GREEN_LIGHT)); | |||||
addChild(createLight<MediumLight<GreenRedLight>>(Vec(162, 43), module, Rings::RESONATOR_GREEN_LIGHT)); | |||||
} | } | ||||
struct RingsModelItem : MenuItem { | |||||
Rings *rings; | |||||
rings::ResonatorModel model; | |||||
void onAction(EventAction &e) override { | |||||
rings->model = model; | |||||
} | |||||
void step() override { | |||||
rightText = (rings->model == model) ? "✔" : ""; | |||||
MenuItem::step(); | |||||
} | |||||
}; | |||||
struct RingsEasterEggItem : MenuItem { | struct RingsEasterEggItem : MenuItem { | ||||
Rings *rings; | Rings *rings; | ||||
void onAction(EventAction &e) override { | void onAction(EventAction &e) override { | ||||
@@ -300,6 +317,7 @@ struct RingsEasterEggItem : MenuItem { | |||||
} | } | ||||
void step() override { | void step() override { | ||||
rightText = (rings->easterEgg) ? "✔" : ""; | rightText = (rings->easterEgg) ? "✔" : ""; | ||||
MenuItem::step(); | |||||
} | } | ||||
}; | }; | ||||
@@ -309,7 +327,16 @@ Menu *RingsWidget::createContextMenu() { | |||||
Rings *rings = dynamic_cast<Rings*>(module); | Rings *rings = dynamic_cast<Rings*>(module); | ||||
assert(rings); | assert(rings); | ||||
menu->pushChild(construct<MenuLabel>()); | |||||
menu->pushChild(construct<MenuEntry>()); | |||||
menu->pushChild(construct<MenuLabel>(&MenuLabel::text, "Resonator")); | |||||
menu->pushChild(construct<RingsModelItem>(&MenuEntry::text, "Modal resonator", &RingsModelItem::rings, rings, &RingsModelItem::model, rings::RESONATOR_MODEL_MODAL)); | |||||
menu->pushChild(construct<RingsModelItem>(&MenuEntry::text, "Sympathetic strings", &RingsModelItem::rings, rings, &RingsModelItem::model, rings::RESONATOR_MODEL_SYMPATHETIC_STRING)); | |||||
menu->pushChild(construct<RingsModelItem>(&MenuEntry::text, "Modulated/inharmonic string", &RingsModelItem::rings, rings, &RingsModelItem::model, rings::RESONATOR_MODEL_STRING)); | |||||
menu->pushChild(construct<RingsModelItem>(&MenuEntry::text, "FM voice", &RingsModelItem::rings, rings, &RingsModelItem::model, rings::RESONATOR_MODEL_FM_VOICE)); | |||||
menu->pushChild(construct<RingsModelItem>(&MenuEntry::text, "Quantized sympathetic strings", &RingsModelItem::rings, rings, &RingsModelItem::model, rings::RESONATOR_MODEL_SYMPATHETIC_STRING_QUANTIZED)); | |||||
menu->pushChild(construct<RingsModelItem>(&MenuEntry::text, "Reverb string", &RingsModelItem::rings, rings, &RingsModelItem::model, rings::RESONATOR_MODEL_STRING_AND_REVERB)); | |||||
menu->pushChild(construct<MenuEntry>()); | |||||
menu->pushChild(construct<RingsEasterEggItem>(&MenuEntry::text, "Disastrous Peace", &RingsEasterEggItem::rings, rings)); | menu->pushChild(construct<RingsEasterEggItem>(&MenuEntry::text, "Disastrous Peace", &RingsEasterEggItem::rings, rings)); | ||||
return menu; | return menu; |
@@ -56,6 +56,17 @@ struct Tides : Module { | |||||
Tides(); | Tides(); | ||||
void step() override; | void step() override; | ||||
void reset() override { | |||||
generator.set_range(tides::GENERATOR_RANGE_MEDIUM); | |||||
generator.set_mode(tides::GENERATOR_MODE_LOOPING); | |||||
} | |||||
void randomize() override { | |||||
generator.set_range((tides::GeneratorRange) (randomu32() % 3)); | |||||
generator.set_mode((tides::GeneratorMode) (randomu32() % 3)); | |||||
} | |||||
json_t *toJson() override { | json_t *toJson() override { | ||||
json_t *rootJ = json_object(); | json_t *rootJ = json_object(); | ||||
@@ -76,16 +87,6 @@ struct Tides : Module { | |||||
generator.set_range((tides::GeneratorRange) json_integer_value(rangeJ)); | generator.set_range((tides::GeneratorRange) json_integer_value(rangeJ)); | ||||
} | } | ||||
} | } | ||||
void reset() override { | |||||
generator.set_range(tides::GENERATOR_RANGE_MEDIUM); | |||||
generator.set_mode(tides::GENERATOR_MODE_LOOPING); | |||||
} | |||||
void randomize() override { | |||||
generator.set_range((tides::GeneratorRange) (randomu32() % 3)); | |||||
generator.set_mode((tides::GeneratorMode) (randomu32() % 3)); | |||||
} | |||||
}; | }; | ||||
@@ -227,9 +228,9 @@ TidesWidget::TidesWidget() { | |||||
addOutput(createOutput<PJ301MPort>(Vec(128, 316), module, Tides::UNI_OUTPUT)); | addOutput(createOutput<PJ301MPort>(Vec(128, 316), module, Tides::UNI_OUTPUT)); | ||||
addOutput(createOutput<PJ301MPort>(Vec(164, 316), module, Tides::BI_OUTPUT)); | addOutput(createOutput<PJ301MPort>(Vec(164, 316), module, Tides::BI_OUTPUT)); | ||||
addChild(createLight<SmallLight<GreenRedLight>>(Vec(57, 62), module, Tides::MODE_GREEN_LIGHT)); | |||||
addChild(createLight<SmallLight<GreenRedLight>>(Vec(57, 83), module, Tides::PHASE_GREEN_LIGHT)); | |||||
addChild(createLight<SmallLight<GreenRedLight>>(Vec(57, 103), module, Tides::RANGE_GREEN_LIGHT)); | |||||
addChild(createLight<MediumLight<GreenRedLight>>(Vec(56, 61), module, Tides::MODE_GREEN_LIGHT)); | |||||
addChild(createLight<MediumLight<GreenRedLight>>(Vec(56, 82), module, Tides::PHASE_GREEN_LIGHT)); | |||||
addChild(createLight<MediumLight<GreenRedLight>>(Vec(56, 102), module, Tides::RANGE_GREEN_LIGHT)); | |||||
} | } | ||||