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.

321 lines
11KB

  1. #include "AudibleInstruments.hpp"
  2. #include "dsp/samplerate.hpp"
  3. #include "dsp/ringbuffer.hpp"
  4. #include "dsp/functions.hpp"
  5. #include "dsp/digital.hpp"
  6. #include "plaits/dsp/voice.h"
  7. struct Plaits : Module {
  8. enum ParamIds {
  9. MODEL1_PARAM,
  10. MODEL2_PARAM,
  11. FREQ_PARAM,
  12. HARMONICS_PARAM,
  13. TIMBRE_PARAM,
  14. MORPH_PARAM,
  15. TIMBRE_CV_PARAM,
  16. FREQ_CV_PARAM,
  17. MORPH_CV_PARAM,
  18. NUM_PARAMS
  19. };
  20. enum InputIds {
  21. ENGINE_INPUT,
  22. TIMBRE_INPUT,
  23. FREQ_INPUT,
  24. MORPH_INPUT,
  25. HARMONICS_INPUT,
  26. TRIGGER_INPUT,
  27. LEVEL_INPUT,
  28. NOTE_INPUT,
  29. NUM_INPUTS
  30. };
  31. enum OutputIds {
  32. OUT_OUTPUT,
  33. AUX_OUTPUT,
  34. NUM_OUTPUTS
  35. };
  36. enum LightIds {
  37. ENUMS(MODEL_LIGHT, 8 * 2),
  38. NUM_LIGHTS
  39. };
  40. plaits::Voice voice;
  41. plaits::Patch patch;
  42. plaits::Modulations modulations;
  43. char shared_buffer[16384];
  44. float triPhase = 0.f;
  45. SampleRateConverter<2> outputSrc;
  46. DoubleRingBuffer<Frame<2>, 256> outputBuffer;
  47. bool lowCpu = false;
  48. bool lpg = false;
  49. SchmittTrigger model1Trigger;
  50. SchmittTrigger model2Trigger;
  51. Plaits() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  52. memset(shared_buffer, 0, sizeof(shared_buffer));
  53. stmlib::BufferAllocator allocator(shared_buffer, sizeof(shared_buffer));
  54. voice.Init(&allocator);
  55. memset(&patch, 0, sizeof(patch));
  56. memset(&modulations, 0, sizeof(modulations));
  57. onReset();
  58. }
  59. void onReset() override {
  60. patch.engine = 0;
  61. patch.lpg_colour = 0.5f;
  62. patch.decay = 0.5f;
  63. }
  64. void onRandomize() override {
  65. patch.engine = randomu32() % 16;
  66. }
  67. json_t *toJson() override {
  68. json_t *rootJ = json_object();
  69. json_object_set_new(rootJ, "lowCpu", json_boolean(lowCpu));
  70. json_object_set_new(rootJ, "model", json_integer(patch.engine));
  71. json_object_set_new(rootJ, "lpgColor", json_real(patch.lpg_colour));
  72. json_object_set_new(rootJ, "decay", json_real(patch.decay));
  73. return rootJ;
  74. }
  75. void fromJson(json_t *rootJ) override {
  76. json_t *lowCpuJ = json_object_get(rootJ, "lowCpu");
  77. if (lowCpuJ)
  78. lowCpu = json_boolean_value(lowCpuJ);
  79. json_t *modelJ = json_object_get(rootJ, "model");
  80. if (modelJ)
  81. patch.engine = json_integer_value(modelJ);
  82. json_t *lpgColorJ = json_object_get(rootJ, "lpgColor");
  83. if (lpgColorJ)
  84. patch.lpg_colour = json_number_value(lpgColorJ);
  85. json_t *decayJ = json_object_get(rootJ, "decay");
  86. if (decayJ)
  87. patch.decay = json_number_value(decayJ);
  88. }
  89. void step() override {
  90. if (outputBuffer.empty()) {
  91. const int blockSize = 12;
  92. // Model buttons
  93. if (model1Trigger.process(params[MODEL1_PARAM].value)) {
  94. if (patch.engine >= 8) {
  95. patch.engine -= 8;
  96. }
  97. else {
  98. patch.engine = (patch.engine + 1) % 8;
  99. }
  100. }
  101. if (model2Trigger.process(params[MODEL2_PARAM].value)) {
  102. if (patch.engine < 8) {
  103. patch.engine += 8;
  104. }
  105. else {
  106. patch.engine = (patch.engine + 1) % 8 + 8;
  107. }
  108. }
  109. // Model lights
  110. int activeEngine = voice.active_engine();
  111. triPhase += 2.f * engineGetSampleTime() * blockSize;
  112. if (triPhase >= 1.f)
  113. triPhase -= 1.f;
  114. float tri = (triPhase < 0.5f) ? triPhase * 2.f : (1.f - triPhase) * 2.f;
  115. for (int i = 0; i < 8; i++) {
  116. lights[MODEL_LIGHT + 2*i + 0].setBrightness((activeEngine == i) ? 1.f : (patch.engine == i) ? tri : 0.f);
  117. lights[MODEL_LIGHT + 2*i + 1].setBrightness((activeEngine == i + 8) ? 1.f : (patch.engine == i + 8) ? tri : 0.f);
  118. }
  119. // Calculate pitch for lowCpu mode if needed
  120. float pitch = params[FREQ_PARAM].value;
  121. if (lowCpu)
  122. pitch += log2f(48000.f * engineGetSampleTime());
  123. // Update patch
  124. patch.note = 60.f + pitch * 12.f;
  125. patch.harmonics = params[HARMONICS_PARAM].value;
  126. if (!lpg) {
  127. patch.timbre = params[TIMBRE_PARAM].value;
  128. patch.morph = params[MORPH_PARAM].value;
  129. }
  130. else {
  131. patch.lpg_colour = params[TIMBRE_PARAM].value;
  132. patch.decay = params[MORPH_PARAM].value;
  133. }
  134. patch.frequency_modulation_amount = params[FREQ_CV_PARAM].value;
  135. patch.timbre_modulation_amount = params[TIMBRE_CV_PARAM].value;
  136. patch.morph_modulation_amount = params[MORPH_CV_PARAM].value;
  137. // Update modulations
  138. modulations.engine = inputs[ENGINE_INPUT].value / 5.f;
  139. modulations.note = inputs[NOTE_INPUT].value * 12.f;
  140. modulations.frequency = inputs[FREQ_INPUT].value * 6.f;
  141. modulations.harmonics = inputs[HARMONICS_INPUT].value / 5.f;
  142. modulations.timbre = inputs[TIMBRE_INPUT].value / 8.f;
  143. modulations.morph = inputs[MORPH_INPUT].value / 8.f;
  144. // Triggers at around 0.7 V
  145. modulations.trigger = inputs[TRIGGER_INPUT].value / 3.f;
  146. modulations.level = inputs[LEVEL_INPUT].value / 8.f;
  147. modulations.frequency_patched = inputs[FREQ_INPUT].active;
  148. modulations.timbre_patched = inputs[TIMBRE_INPUT].active;
  149. modulations.morph_patched = inputs[MORPH_INPUT].active;
  150. modulations.trigger_patched = inputs[TRIGGER_INPUT].active;
  151. modulations.level_patched = inputs[LEVEL_INPUT].active;
  152. // Render frames
  153. plaits::Voice::Frame output[blockSize];
  154. voice.Render(patch, modulations, output, blockSize);
  155. // Convert output to frames
  156. Frame<2> outputFrames[blockSize];
  157. for (int i = 0; i < blockSize; i++) {
  158. outputFrames[i].samples[0] = output[i].out / 32768.f;
  159. outputFrames[i].samples[1] = output[i].aux / 32768.f;
  160. }
  161. // Convert output
  162. if (lowCpu) {
  163. int len = min(outputBuffer.capacity(), blockSize);
  164. memcpy(outputBuffer.endData(), outputFrames, len * sizeof(Frame<2>));
  165. outputBuffer.endIncr(len);
  166. }
  167. else {
  168. outputSrc.setRates(48000, engineGetSampleRate());
  169. int inLen = blockSize;
  170. int outLen = outputBuffer.capacity();
  171. outputSrc.process(outputFrames, &inLen, outputBuffer.endData(), &outLen);
  172. outputBuffer.endIncr(outLen);
  173. }
  174. }
  175. // Set output
  176. if (!outputBuffer.empty()) {
  177. Frame<2> outputFrame = outputBuffer.shift();
  178. // Inverting op-amp on outputs
  179. outputs[OUT_OUTPUT].value = -outputFrame.samples[0] * 5.f;
  180. outputs[AUX_OUTPUT].value = -outputFrame.samples[1] * 5.f;
  181. }
  182. }
  183. };
  184. static const std::string modelLabels[16] = {
  185. "Pair of classic waveforms",
  186. "Waveshaping oscillator",
  187. "Two operator FM",
  188. "Granular formant oscillator",
  189. "Harmonic oscillator",
  190. "Wavetable oscillator",
  191. "Chords",
  192. "Vowel and speech synthesis",
  193. "Granular cloud",
  194. "Filtered noise",
  195. "Particle noise",
  196. "Inharmonic string modeling",
  197. "Modal resonator",
  198. "Analog bass drum",
  199. "Analog snare drum",
  200. "Analog hi-hat",
  201. };
  202. struct PlaitsWidget : ModuleWidget {
  203. PlaitsWidget(Plaits *module) : ModuleWidget(module) {
  204. setPanel(SVG::load(assetPlugin(plugin, "res/Plaits.svg")));
  205. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  206. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  207. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  208. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  209. addParam(ParamWidget::create<TL1105>(mm2px(Vec(23.32685, 14.6539)), module, Plaits::MODEL1_PARAM, 0.0, 1.0, 0.0));
  210. addParam(ParamWidget::create<TL1105>(mm2px(Vec(32.22764, 14.6539)), module, Plaits::MODEL2_PARAM, 0.0, 1.0, 0.0));
  211. addParam(ParamWidget::create<Rogan3PSWhite>(mm2px(Vec(3.1577, 20.21088)), module, Plaits::FREQ_PARAM, -4.0, 4.0, 0.0));
  212. addParam(ParamWidget::create<Rogan3PSWhite>(mm2px(Vec(39.3327, 20.21088)), module, Plaits::HARMONICS_PARAM, 0.0, 1.0, 0.5));
  213. addParam(ParamWidget::create<Rogan1PSWhite>(mm2px(Vec(4.04171, 49.6562)), module, Plaits::TIMBRE_PARAM, 0.0, 1.0, 0.5));
  214. addParam(ParamWidget::create<Rogan1PSWhite>(mm2px(Vec(42.71716, 49.6562)), module, Plaits::MORPH_PARAM, 0.0, 1.0, 0.5));
  215. addParam(ParamWidget::create<Trimpot>(mm2px(Vec(7.88712, 77.60705)), module, Plaits::TIMBRE_CV_PARAM, -1.0, 1.0, 0.0));
  216. addParam(ParamWidget::create<Trimpot>(mm2px(Vec(27.2245, 77.60705)), module, Plaits::FREQ_CV_PARAM, -1.0, 1.0, 0.0));
  217. addParam(ParamWidget::create<Trimpot>(mm2px(Vec(46.56189, 77.60705)), module, Plaits::MORPH_CV_PARAM, -1.0, 1.0, 0.0));
  218. addInput(Port::create<PJ301MPort>(mm2px(Vec(3.31381, 92.48067)), Port::INPUT, module, Plaits::ENGINE_INPUT));
  219. addInput(Port::create<PJ301MPort>(mm2px(Vec(14.75983, 92.48067)), Port::INPUT, module, Plaits::TIMBRE_INPUT));
  220. addInput(Port::create<PJ301MPort>(mm2px(Vec(26.20655, 92.48067)), Port::INPUT, module, Plaits::FREQ_INPUT));
  221. addInput(Port::create<PJ301MPort>(mm2px(Vec(37.65257, 92.48067)), Port::INPUT, module, Plaits::MORPH_INPUT));
  222. addInput(Port::create<PJ301MPort>(mm2px(Vec(49.0986, 92.48067)), Port::INPUT, module, Plaits::HARMONICS_INPUT));
  223. addInput(Port::create<PJ301MPort>(mm2px(Vec(3.31381, 107.08103)), Port::INPUT, module, Plaits::TRIGGER_INPUT));
  224. addInput(Port::create<PJ301MPort>(mm2px(Vec(14.75983, 107.08103)), Port::INPUT, module, Plaits::LEVEL_INPUT));
  225. addInput(Port::create<PJ301MPort>(mm2px(Vec(26.20655, 107.08103)), Port::INPUT, module, Plaits::NOTE_INPUT));
  226. addOutput(Port::create<PJ301MPort>(mm2px(Vec(37.65257, 107.08103)), Port::OUTPUT, module, Plaits::OUT_OUTPUT));
  227. addOutput(Port::create<PJ301MPort>(mm2px(Vec(49.0986, 107.08103)), Port::OUTPUT, module, Plaits::AUX_OUTPUT));
  228. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(mm2px(Vec(28.79498, 23.31649)), module, Plaits::MODEL_LIGHT + 0 * 2));
  229. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(mm2px(Vec(28.79498, 28.71704)), module, Plaits::MODEL_LIGHT + 1 * 2));
  230. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(mm2px(Vec(28.79498, 34.1162)), module, Plaits::MODEL_LIGHT + 2 * 2));
  231. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(mm2px(Vec(28.79498, 39.51675)), module, Plaits::MODEL_LIGHT + 3 * 2));
  232. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(mm2px(Vec(28.79498, 44.91731)), module, Plaits::MODEL_LIGHT + 4 * 2));
  233. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(mm2px(Vec(28.79498, 50.31785)), module, Plaits::MODEL_LIGHT + 5 * 2));
  234. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(mm2px(Vec(28.79498, 55.71771)), module, Plaits::MODEL_LIGHT + 6 * 2));
  235. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(mm2px(Vec(28.79498, 61.11827)), module, Plaits::MODEL_LIGHT + 7 * 2));
  236. }
  237. void appendContextMenu(Menu *menu) override {
  238. Plaits *module = dynamic_cast<Plaits*>(this->module);
  239. struct PlaitsLowCpuItem : MenuItem {
  240. Plaits *module;
  241. void onAction(EventAction &e) override {
  242. module->lowCpu ^= true;
  243. }
  244. };
  245. struct PlaitsLPGItem : MenuItem {
  246. Plaits *module;
  247. void onAction(EventAction &e) override {
  248. module->lpg ^= true;
  249. }
  250. };
  251. struct PlaitsModelItem : MenuItem {
  252. Plaits *module;
  253. int model;
  254. void onAction(EventAction &e) override {
  255. module->patch.engine = model;
  256. }
  257. };
  258. menu->addChild(MenuEntry::create());
  259. PlaitsLowCpuItem *lowCpuItem = MenuItem::create<PlaitsLowCpuItem>("Low CPU", CHECKMARK(module->lowCpu));
  260. lowCpuItem->module = module;
  261. menu->addChild(lowCpuItem);
  262. PlaitsLPGItem *lpgItem = MenuItem::create<PlaitsLPGItem>("Edit LPG response/decay", CHECKMARK(module->lpg));
  263. lpgItem->module = module;
  264. menu->addChild(lpgItem);
  265. menu->addChild(new MenuEntry());
  266. menu->addChild(MenuLabel::create("Models"));
  267. for (int i = 0; i < 16; i++) {
  268. PlaitsModelItem *modelItem = MenuItem::create<PlaitsModelItem>(modelLabels[i], CHECKMARK(module->patch.engine == i));
  269. modelItem->module = module;
  270. modelItem->model = i;
  271. menu->addChild(modelItem);
  272. }
  273. }
  274. };
  275. Model *modelPlaits = Model::create<Plaits, PlaitsWidget>("Audible Instruments", "Plaits", "Macro Oscillator 2", OSCILLATOR_TAG, WAVESHAPER_TAG);