You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

350 lines
9.8KB

  1. #include "plugin.hpp"
  2. #include "braids/macro_oscillator.h"
  3. #include "braids/vco_jitter_source.h"
  4. #include "braids/signature_waveshaper.h"
  5. struct Braids : Module {
  6. enum ParamIds {
  7. FINE_PARAM,
  8. COARSE_PARAM,
  9. FM_PARAM,
  10. TIMBRE_PARAM,
  11. MODULATION_PARAM,
  12. COLOR_PARAM,
  13. SHAPE_PARAM,
  14. NUM_PARAMS
  15. };
  16. enum InputIds {
  17. TRIG_INPUT,
  18. PITCH_INPUT,
  19. FM_INPUT,
  20. TIMBRE_INPUT,
  21. COLOR_INPUT,
  22. NUM_INPUTS
  23. };
  24. enum OutputIds {
  25. OUT_OUTPUT,
  26. NUM_OUTPUTS
  27. };
  28. braids::MacroOscillator osc;
  29. braids::SettingsData settings;
  30. braids::VcoJitterSource jitter_source;
  31. braids::SignatureWaveshaper ws;
  32. dsp::SampleRateConverter<1> src;
  33. dsp::DoubleRingBuffer<dsp::Frame<1>, 256> outputBuffer;
  34. bool lastTrig = false;
  35. bool lowCpu = false;
  36. Braids() {
  37. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS);
  38. configParam(SHAPE_PARAM, 0.0, 1.0, 0.0, "Model");
  39. configParam(FINE_PARAM, -1.0, 1.0, 0.0, "Fine frequency adjustment");
  40. configParam(COARSE_PARAM, -5.0, 3.0, -1.0, "Coarse frequency adjustment");
  41. configParam(FM_PARAM, -1.0, 1.0, 0.0, "FM");
  42. configParam(TIMBRE_PARAM, 0.0, 1.0, 0.5, "Timbre");
  43. configParam(MODULATION_PARAM, -1.0, 1.0, 0.0, "Modulation");
  44. configParam(COLOR_PARAM, 0.0, 1.0, 0.5, "Color");
  45. memset(&osc, 0, sizeof(osc));
  46. osc.Init();
  47. memset(&jitter_source, 0, sizeof(jitter_source));
  48. jitter_source.Init();
  49. memset(&ws, 0, sizeof(ws));
  50. ws.Init(0x0000);
  51. memset(&settings, 0, sizeof(settings));
  52. // List of supported settings
  53. settings.meta_modulation = 0;
  54. settings.vco_drift = 0;
  55. settings.signature = 0;
  56. }
  57. void process(const ProcessArgs &args) override {
  58. // Trigger
  59. bool trig = inputs[TRIG_INPUT].getVoltage() >= 1.0;
  60. if (!lastTrig && trig) {
  61. osc.Strike();
  62. }
  63. lastTrig = trig;
  64. // Render frames
  65. if (outputBuffer.empty()) {
  66. float fm = params[FM_PARAM].getValue() * inputs[FM_INPUT].getVoltage();
  67. // Set shape
  68. int shape = roundf(params[SHAPE_PARAM].getValue() * braids::MACRO_OSC_SHAPE_LAST_ACCESSIBLE_FROM_META);
  69. if (settings.meta_modulation) {
  70. shape += roundf(fm / 10.0 * braids::MACRO_OSC_SHAPE_LAST_ACCESSIBLE_FROM_META);
  71. }
  72. settings.shape = clamp(shape, 0, braids::MACRO_OSC_SHAPE_LAST_ACCESSIBLE_FROM_META);
  73. // Setup oscillator from settings
  74. osc.set_shape((braids::MacroOscillatorShape) settings.shape);
  75. // Set timbre/modulation
  76. float timbre = params[TIMBRE_PARAM].getValue() + params[MODULATION_PARAM].getValue() * inputs[TIMBRE_INPUT].getVoltage() / 5.0;
  77. float modulation = params[COLOR_PARAM].getValue() + inputs[COLOR_INPUT].getVoltage() / 5.0;
  78. int16_t param1 = rescale(clamp(timbre, 0.0f, 1.0f), 0.0f, 1.0f, 0, INT16_MAX);
  79. int16_t param2 = rescale(clamp(modulation, 0.0f, 1.0f), 0.0f, 1.0f, 0, INT16_MAX);
  80. osc.set_parameters(param1, param2);
  81. // Set pitch
  82. float pitchV = inputs[PITCH_INPUT].getVoltage() + params[COARSE_PARAM].getValue() + params[FINE_PARAM].getValue() / 12.0;
  83. if (!settings.meta_modulation)
  84. pitchV += fm;
  85. if (lowCpu)
  86. pitchV += log2f(96000.f * args.sampleTime);
  87. int32_t pitch = (pitchV * 12.0 + 60) * 128;
  88. pitch += jitter_source.Render(settings.vco_drift);
  89. pitch = clamp(pitch, 0, 16383);
  90. osc.set_pitch(pitch);
  91. // TODO: add a sync input buffer (must be sample rate converted)
  92. uint8_t sync_buffer[24] = {};
  93. int16_t render_buffer[24];
  94. osc.Render(sync_buffer, render_buffer, 24);
  95. // Signature waveshaping, decimation (not yet supported), and bit reduction (not yet supported)
  96. uint16_t signature = settings.signature * settings.signature * 4095;
  97. for (size_t i = 0; i < 24; i++) {
  98. const int16_t bit_mask = 0xffff;
  99. int16_t sample = render_buffer[i] & bit_mask;
  100. int16_t warped = ws.Transform(sample);
  101. render_buffer[i] = stmlib::Mix(sample, warped, signature);
  102. }
  103. if (lowCpu) {
  104. for (int i = 0; i < 24; i++) {
  105. dsp::Frame<1> f;
  106. f.samples[0] = render_buffer[i] / 32768.0;
  107. outputBuffer.push(f);
  108. }
  109. }
  110. else {
  111. // Sample rate convert
  112. dsp::Frame<1> in[24];
  113. for (int i = 0; i < 24; i++) {
  114. in[i].samples[0] = render_buffer[i] / 32768.0;
  115. }
  116. src.setRates(96000, args.sampleRate);
  117. int inLen = 24;
  118. int outLen = outputBuffer.capacity();
  119. src.process(in, &inLen, outputBuffer.endData(), &outLen);
  120. outputBuffer.endIncr(outLen);
  121. }
  122. }
  123. // Output
  124. if (!outputBuffer.empty()) {
  125. dsp::Frame<1> f = outputBuffer.shift();
  126. outputs[OUT_OUTPUT].setVoltage(5.0 * f.samples[0]);
  127. }
  128. }
  129. json_t *dataToJson() override {
  130. json_t *rootJ = json_object();
  131. json_t *settingsJ = json_array();
  132. uint8_t *settingsArray = &settings.shape;
  133. for (int i = 0; i < 20; i++) {
  134. json_t *settingJ = json_integer(settingsArray[i]);
  135. json_array_insert_new(settingsJ, i, settingJ);
  136. }
  137. json_object_set_new(rootJ, "settings", settingsJ);
  138. json_t *lowCpuJ = json_boolean(lowCpu);
  139. json_object_set_new(rootJ, "lowCpu", lowCpuJ);
  140. return rootJ;
  141. }
  142. void dataFromJson(json_t *rootJ) override {
  143. json_t *settingsJ = json_object_get(rootJ, "settings");
  144. if (settingsJ) {
  145. uint8_t *settingsArray = &settings.shape;
  146. for (int i = 0; i < 20; i++) {
  147. json_t *settingJ = json_array_get(settingsJ, i);
  148. if (settingJ)
  149. settingsArray[i] = json_integer_value(settingJ);
  150. }
  151. }
  152. json_t *lowCpuJ = json_object_get(rootJ, "lowCpu");
  153. if (lowCpuJ) {
  154. lowCpu = json_boolean_value(lowCpuJ);
  155. }
  156. }
  157. };
  158. static const char *algo_values[] = {
  159. "CSAW",
  160. "/\\-_",
  161. "//-_",
  162. "FOLD",
  163. "uuuu",
  164. "SUB-",
  165. "SUB/",
  166. "SYN-",
  167. "SYN/",
  168. "//x3",
  169. "-_x3",
  170. "/\\x3",
  171. "SIx3",
  172. "RING",
  173. "////",
  174. "//uu",
  175. "TOY*",
  176. "ZLPF",
  177. "ZPKF",
  178. "ZBPF",
  179. "ZHPF",
  180. "VOSM",
  181. "VOWL",
  182. "VFOF",
  183. "HARM",
  184. "FM ",
  185. "FBFM",
  186. "WTFM",
  187. "PLUK",
  188. "BOWD",
  189. "BLOW",
  190. "FLUT",
  191. "BELL",
  192. "DRUM",
  193. "KICK",
  194. "CYMB",
  195. "SNAR",
  196. "WTBL",
  197. "WMAP",
  198. "WLIN",
  199. "WTx4",
  200. "NOIS",
  201. "TWNQ",
  202. "CLKN",
  203. "CLOU",
  204. "PRTC",
  205. "QPSK",
  206. " ",
  207. };
  208. struct BraidsDisplay : TransparentWidget {
  209. Braids *module;
  210. std::shared_ptr<Font> font;
  211. BraidsDisplay() {
  212. font = APP->window->loadFont(asset::plugin(pluginInstance, "res/hdad-segment14-1.002/Segment14.ttf"));
  213. }
  214. void draw(const DrawArgs &args) override {
  215. int shape = module ? module->settings.shape : 0;
  216. // Background
  217. NVGcolor backgroundColor = nvgRGB(0x38, 0x38, 0x38);
  218. NVGcolor borderColor = nvgRGB(0x10, 0x10, 0x10);
  219. nvgBeginPath(args.vg);
  220. nvgRoundedRect(args.vg, 0.0, 0.0, box.size.x, box.size.y, 5.0);
  221. nvgFillColor(args.vg, backgroundColor);
  222. nvgFill(args.vg);
  223. nvgStrokeWidth(args.vg, 1.0);
  224. nvgStrokeColor(args.vg, borderColor);
  225. nvgStroke(args.vg);
  226. nvgFontSize(args.vg, 36);
  227. nvgFontFaceId(args.vg, font->handle);
  228. nvgTextLetterSpacing(args.vg, 2.5);
  229. Vec textPos = Vec(10, 48);
  230. NVGcolor textColor = nvgRGB(0xaf, 0xd2, 0x2c);
  231. nvgFillColor(args.vg, nvgTransRGBA(textColor, 16));
  232. nvgText(args.vg, textPos.x, textPos.y, "~~~~", NULL);
  233. nvgFillColor(args.vg, textColor);
  234. nvgText(args.vg, textPos.x, textPos.y, algo_values[shape], NULL);
  235. }
  236. };
  237. struct BraidsSettingItem : MenuItem {
  238. uint8_t *setting = NULL;
  239. uint8_t offValue = 0;
  240. uint8_t onValue = 1;
  241. void onAction(const event::Action &e) override {
  242. // Toggle setting
  243. *setting = (*setting == onValue) ? offValue : onValue;
  244. }
  245. void step() override {
  246. rightText = (*setting == onValue) ? "✔" : "";
  247. MenuItem::step();
  248. }
  249. };
  250. struct BraidsLowCpuItem : MenuItem {
  251. Braids *braids;
  252. void onAction(const event::Action &e) override {
  253. braids->lowCpu = !braids->lowCpu;
  254. }
  255. void step() override {
  256. rightText = (braids->lowCpu) ? "✔" : "";
  257. MenuItem::step();
  258. }
  259. };
  260. struct BraidsWidget : ModuleWidget {
  261. BraidsWidget(Braids *module) {
  262. setModule(module);
  263. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Braids.svg")));
  264. {
  265. BraidsDisplay *display = new BraidsDisplay();
  266. display->box.pos = Vec(14, 53);
  267. display->box.size = Vec(148, 56);
  268. display->module = module;
  269. addChild(display);
  270. }
  271. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  272. addChild(createWidget<ScrewSilver>(Vec(210, 0)));
  273. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  274. addChild(createWidget<ScrewSilver>(Vec(210, 365)));
  275. addParam(createParam<Rogan2SGray>(Vec(176, 59), module, Braids::SHAPE_PARAM));
  276. addParam(createParam<Rogan2PSWhite>(Vec(19, 138), module, Braids::FINE_PARAM));
  277. addParam(createParam<Rogan2PSWhite>(Vec(97, 138), module, Braids::COARSE_PARAM));
  278. addParam(createParam<Rogan2PSWhite>(Vec(176, 138), module, Braids::FM_PARAM));
  279. addParam(createParam<Rogan2PSGreen>(Vec(19, 217), module, Braids::TIMBRE_PARAM));
  280. addParam(createParam<Rogan2PSGreen>(Vec(97, 217), module, Braids::MODULATION_PARAM));
  281. addParam(createParam<Rogan2PSRed>(Vec(176, 217), module, Braids::COLOR_PARAM));
  282. addInput(createInput<PJ301MPort>(Vec(10, 316), module, Braids::TRIG_INPUT));
  283. addInput(createInput<PJ301MPort>(Vec(47, 316), module, Braids::PITCH_INPUT));
  284. addInput(createInput<PJ301MPort>(Vec(84, 316), module, Braids::FM_INPUT));
  285. addInput(createInput<PJ301MPort>(Vec(122, 316), module, Braids::TIMBRE_INPUT));
  286. addInput(createInput<PJ301MPort>(Vec(160, 316), module, Braids::COLOR_INPUT));
  287. addOutput(createOutput<PJ301MPort>(Vec(205, 316), module, Braids::OUT_OUTPUT));
  288. }
  289. void appendContextMenu(Menu *menu) override {
  290. Braids *braids = dynamic_cast<Braids*>(module);
  291. assert(braids);
  292. menu->addChild(new MenuSeparator);
  293. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Options"));
  294. menu->addChild(construct<BraidsSettingItem>(&MenuItem::text, "META", &BraidsSettingItem::setting, &braids->settings.meta_modulation));
  295. menu->addChild(construct<BraidsSettingItem>(&MenuItem::text, "DRFT", &BraidsSettingItem::setting, &braids->settings.vco_drift, &BraidsSettingItem::onValue, 4));
  296. menu->addChild(construct<BraidsSettingItem>(&MenuItem::text, "SIGN", &BraidsSettingItem::setting, &braids->settings.signature, &BraidsSettingItem::onValue, 4));
  297. menu->addChild(construct<BraidsLowCpuItem>(&MenuItem::text, "Low CPU", &BraidsLowCpuItem::braids, braids));
  298. }
  299. };
  300. Model *modelBraids = createModel<Braids, BraidsWidget>("Braids");