|
|
@@ -2,6 +2,8 @@ |
|
|
|
#include "AudibleInstruments.hpp" |
|
|
|
#include "dsp.hpp" |
|
|
|
#include "braids/macro_oscillator.h" |
|
|
|
#include "braids/vco_jitter_source.h" |
|
|
|
#include "braids/signature_waveshaper.h" |
|
|
|
|
|
|
|
|
|
|
|
struct Braids : Module { |
|
|
@@ -28,15 +30,42 @@ struct Braids : Module { |
|
|
|
NUM_OUTPUTS |
|
|
|
}; |
|
|
|
|
|
|
|
braids::MacroOscillator *osc; |
|
|
|
braids::MacroOscillator osc; |
|
|
|
braids::SettingsData settings; |
|
|
|
braids::VcoJitterSource jitter_source; |
|
|
|
braids::SignatureWaveshaper ws; |
|
|
|
|
|
|
|
SampleRateConverter<1> src; |
|
|
|
DoubleRingBuffer<Frame<1>, 256> outputBuffer; |
|
|
|
bool lastTrig = false; |
|
|
|
|
|
|
|
Braids(); |
|
|
|
~Braids(); |
|
|
|
void step(); |
|
|
|
void setShape(int shape); |
|
|
|
|
|
|
|
json_t *toJson() { |
|
|
|
json_t *rootJ = json_object(); |
|
|
|
json_t *settingsJ = json_array(); |
|
|
|
uint8_t *settingsArray = &settings.shape; |
|
|
|
for (int i = 0; i < 20; i++) { |
|
|
|
json_t *settingJ = json_integer(settingsArray[i]); |
|
|
|
json_array_insert_new(settingsJ, i, settingJ); |
|
|
|
} |
|
|
|
json_object_set_new(rootJ, "settings", settingsJ); |
|
|
|
return rootJ; |
|
|
|
} |
|
|
|
|
|
|
|
void fromJson(json_t *rootJ) { |
|
|
|
json_t *settingsJ = json_object_get(rootJ, "settings"); |
|
|
|
if (settingsJ) { |
|
|
|
uint8_t *settingsArray = &settings.shape; |
|
|
|
for (int i = 0; i < 20; i++) { |
|
|
|
json_t *settingJ = json_array_get(settingsJ, i); |
|
|
|
if (settingJ) |
|
|
|
settingsArray[i] = json_integer_value(settingJ); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@@ -45,46 +74,72 @@ Braids::Braids() { |
|
|
|
inputs.resize(NUM_INPUTS); |
|
|
|
outputs.resize(NUM_OUTPUTS); |
|
|
|
|
|
|
|
osc = new braids::MacroOscillator(); |
|
|
|
memset(osc, 0, sizeof(*osc)); |
|
|
|
osc->Init(); |
|
|
|
} |
|
|
|
|
|
|
|
Braids::~Braids() { |
|
|
|
delete osc; |
|
|
|
memset(&osc, 0, sizeof(osc)); |
|
|
|
osc.Init(); |
|
|
|
memset(&jitter_source, 0, sizeof(jitter_source)); |
|
|
|
jitter_source.Init(); |
|
|
|
memset(&ws, 0, sizeof(ws)); |
|
|
|
ws.Init(0x0000); |
|
|
|
memset(&settings, 0, sizeof(settings)); |
|
|
|
|
|
|
|
// List of supported settings |
|
|
|
settings.meta_modulation = 0; |
|
|
|
settings.vco_drift = 0; |
|
|
|
settings.signature = 255; |
|
|
|
} |
|
|
|
|
|
|
|
void Braids::step() { |
|
|
|
// Trigger |
|
|
|
bool trig = getf(inputs[TRIG_INPUT]) >= 1.0; |
|
|
|
if (!lastTrig && trig) { |
|
|
|
osc->Strike(); |
|
|
|
osc.Strike(); |
|
|
|
} |
|
|
|
lastTrig = trig; |
|
|
|
|
|
|
|
// Render frames |
|
|
|
if (outputBuffer.empty()) { |
|
|
|
float fm = params[FM_PARAM] * getf(inputs[FM_INPUT]); |
|
|
|
|
|
|
|
// Set shape |
|
|
|
int shape = roundf(params[SHAPE_PARAM]); |
|
|
|
osc->set_shape((braids::MacroOscillatorShape) shape); |
|
|
|
int shape = roundf(params[SHAPE_PARAM] * braids::MACRO_OSC_SHAPE_LAST_ACCESSIBLE_FROM_META); |
|
|
|
if (settings.meta_modulation) { |
|
|
|
shape += roundf(fm / 10.0 * braids::MACRO_OSC_SHAPE_LAST_ACCESSIBLE_FROM_META); |
|
|
|
} |
|
|
|
settings.shape = clampi(shape, 0, braids::MACRO_OSC_SHAPE_LAST_ACCESSIBLE_FROM_META); |
|
|
|
|
|
|
|
// Setup oscillator from settings |
|
|
|
osc.set_shape((braids::MacroOscillatorShape) settings.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 = rescalef(clampf(timbre, 0.0, 1.0), 0.0, 1.0, 0, INT16_MAX); |
|
|
|
int16_t param2 = rescalef(clampf(modulation, 0.0, 1.0), 0.0, 1.0, 0, INT16_MAX); |
|
|
|
osc->set_parameters(param1, param2); |
|
|
|
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); |
|
|
|
float pitchV = getf(inputs[PITCH_INPUT]) + params[COARSE_PARAM] + params[FINE_PARAM] / 12.0; |
|
|
|
if (!settings.meta_modulation) |
|
|
|
pitchV += fm; |
|
|
|
int32_t pitch = (pitchV * 12.0 + 60) * 128; |
|
|
|
pitch += jitter_source.Render(settings.vco_drift); |
|
|
|
pitch = clampi(pitch, 0, 16383); |
|
|
|
osc.set_pitch(pitch); |
|
|
|
|
|
|
|
// TODO: add a sync input buffer (must be sample rate converted) |
|
|
|
uint8_t sync_buffer[24] = {}; |
|
|
|
|
|
|
|
int16_t render_buffer[24]; |
|
|
|
osc->Render(sync_buffer, render_buffer, 24); |
|
|
|
osc.Render(sync_buffer, render_buffer, 24); |
|
|
|
|
|
|
|
// Signature waveshaping, decimation (not yet supported), and bit reduction (not yet supported) |
|
|
|
uint16_t signature = settings.signature * settings.signature * 4095; |
|
|
|
for (size_t i = 0; i < 24; i++) { |
|
|
|
const int16_t bit_mask = 0xffff; |
|
|
|
int16_t sample = render_buffer[i] & bit_mask; |
|
|
|
int16_t warped = ws.Transform(sample); |
|
|
|
render_buffer[i] = stmlib::Mix(sample, warped, signature); |
|
|
|
} |
|
|
|
|
|
|
|
// Sample rate convert |
|
|
|
Frame<1> in[24]; |
|
|
@@ -159,7 +214,7 @@ static const char *algo_values[] = { |
|
|
|
}; |
|
|
|
|
|
|
|
struct BraidsDisplay : TransparentWidget { |
|
|
|
float *value; |
|
|
|
Braids *module; |
|
|
|
std::shared_ptr<Font> font; |
|
|
|
|
|
|
|
BraidsDisplay() { |
|
|
@@ -167,7 +222,7 @@ struct BraidsDisplay : TransparentWidget { |
|
|
|
} |
|
|
|
|
|
|
|
void draw(NVGcontext *vg) { |
|
|
|
int shape = roundf(getf(value)); |
|
|
|
int shape = module->settings.shape; |
|
|
|
|
|
|
|
// Background |
|
|
|
NVGcolor backgroundColor = nvgRGB(0x38, 0x38, 0x38); |
|
|
@@ -210,7 +265,7 @@ BraidsWidget::BraidsWidget() { |
|
|
|
BraidsDisplay *display = new BraidsDisplay(); |
|
|
|
display->box.pos = Vec(14, 53); |
|
|
|
display->box.size = Vec(148, 56); |
|
|
|
display->value = &module->params[Braids::SHAPE_PARAM]; |
|
|
|
display->module = module; |
|
|
|
addChild(display); |
|
|
|
} |
|
|
|
|
|
|
@@ -219,7 +274,7 @@ BraidsWidget::BraidsWidget() { |
|
|
|
addChild(createScrew<ScrewSilver>(Vec(15, 365))); |
|
|
|
addChild(createScrew<ScrewSilver>(Vec(210, 365))); |
|
|
|
|
|
|
|
addParam(createParam<Rogan2SGray>(Vec(177, 60), module, Braids::SHAPE_PARAM, 0.0, braids::MACRO_OSC_SHAPE_LAST-2, 0.0)); |
|
|
|
addParam(createParam<Rogan2SGray>(Vec(177, 60), module, Braids::SHAPE_PARAM, 0.0, 1.0, 0.0)); |
|
|
|
|
|
|
|
addParam(createParam<Rogan2PSWhite>(Vec(20, 139), module, Braids::FINE_PARAM, -1.0, 1.0, 0.0)); |
|
|
|
addParam(createParam<Rogan2PSWhite>(Vec(98, 139), module, Braids::COARSE_PARAM, -2.0, 2.0, 0.0)); |
|
|
@@ -236,3 +291,49 @@ BraidsWidget::BraidsWidget() { |
|
|
|
addInput(createInput<PJ3410Port>(Vec(157, 313), module, Braids::COLOR_INPUT)); |
|
|
|
addOutput(createOutput<PJ3410Port>(Vec(202, 313), module, Braids::OUT_OUTPUT)); |
|
|
|
} |
|
|
|
|
|
|
|
struct BraidsSettingItem : MenuItem { |
|
|
|
uint8_t *setting = NULL; |
|
|
|
uint8_t offValue = 0; |
|
|
|
uint8_t onValue = 1; |
|
|
|
void onAction() { |
|
|
|
// Toggle setting |
|
|
|
*setting = (*setting == onValue) ? offValue : onValue; |
|
|
|
} |
|
|
|
void step() { |
|
|
|
rightText = (*setting == onValue) ? "✔" : ""; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Menu *BraidsWidget::createContextMenu() { |
|
|
|
Menu *menu = ModuleWidget::createContextMenu(); |
|
|
|
|
|
|
|
MenuLabel *spacerLabel = new MenuLabel(); |
|
|
|
menu->pushChild(spacerLabel); |
|
|
|
|
|
|
|
MenuLabel *optionsLabel = new MenuLabel(); |
|
|
|
optionsLabel->text = "Options"; |
|
|
|
menu->pushChild(optionsLabel); |
|
|
|
|
|
|
|
Braids *braids = dynamic_cast<Braids*>(module); |
|
|
|
assert(braids); |
|
|
|
|
|
|
|
BraidsSettingItem *metaItem = new BraidsSettingItem(); |
|
|
|
metaItem->text = "META"; |
|
|
|
metaItem->setting = &braids->settings.meta_modulation; |
|
|
|
menu->pushChild(metaItem); |
|
|
|
|
|
|
|
BraidsSettingItem *drftItem = new BraidsSettingItem(); |
|
|
|
drftItem->text = "DRFT"; |
|
|
|
drftItem->setting = &braids->settings.vco_drift; |
|
|
|
drftItem->onValue = 4; |
|
|
|
menu->pushChild(drftItem); |
|
|
|
|
|
|
|
BraidsSettingItem *signItem = new BraidsSettingItem(); |
|
|
|
signItem->text = "SIGN"; |
|
|
|
signItem->setting = &braids->settings.signature; |
|
|
|
signItem->onValue = 4; |
|
|
|
menu->pushChild(signItem); |
|
|
|
|
|
|
|
return menu; |
|
|
|
} |