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.

396 lines
14KB

  1. #include "AudibleInstruments.hpp"
  2. #include "clouds/dsp/granular_processor.h"
  3. struct Clouds : Module {
  4. enum ParamIds {
  5. FREEZE_PARAM,
  6. MODE_PARAM,
  7. LOAD_PARAM,
  8. POSITION_PARAM,
  9. SIZE_PARAM,
  10. PITCH_PARAM,
  11. IN_GAIN_PARAM,
  12. DENSITY_PARAM,
  13. TEXTURE_PARAM,
  14. BLEND_PARAM,
  15. SPREAD_PARAM,
  16. FEEDBACK_PARAM,
  17. REVERB_PARAM,
  18. NUM_PARAMS
  19. };
  20. enum InputIds {
  21. FREEZE_INPUT,
  22. TRIG_INPUT,
  23. POSITION_INPUT,
  24. SIZE_INPUT,
  25. PITCH_INPUT,
  26. BLEND_INPUT,
  27. IN_L_INPUT,
  28. IN_R_INPUT,
  29. DENSITY_INPUT,
  30. TEXTURE_INPUT,
  31. NUM_INPUTS
  32. };
  33. enum OutputIds {
  34. OUT_L_OUTPUT,
  35. OUT_R_OUTPUT,
  36. NUM_OUTPUTS
  37. };
  38. enum LightIds {
  39. FREEZE_LIGHT,
  40. MIX_GREEN_LIGHT, MIX_RED_LIGHT,
  41. PAN_GREEN_LIGHT, PAN_RED_LIGHT,
  42. FEEDBACK_GREEN_LIGHT, FEEDBACK_RED_LIGHT,
  43. REVERB_GREEN_LIGHT, REVERB_RED_LIGHT,
  44. NUM_LIGHTS
  45. };
  46. SampleRateConverter<2> inputSrc;
  47. SampleRateConverter<2> outputSrc;
  48. DoubleRingBuffer<Frame<2>, 256> inputBuffer;
  49. DoubleRingBuffer<Frame<2>, 256> outputBuffer;
  50. uint8_t *block_mem;
  51. uint8_t *block_ccm;
  52. clouds::GranularProcessor *processor;
  53. bool triggered = false;
  54. SchmittTrigger freezeTrigger;
  55. bool freeze = false;
  56. SchmittTrigger blendTrigger;
  57. int blendMode = 0;
  58. clouds::PlaybackMode playback;
  59. int quality = 0;
  60. Clouds();
  61. ~Clouds();
  62. void step() override;
  63. void onReset() override {
  64. freeze = false;
  65. blendMode = 0;
  66. playback = clouds::PLAYBACK_MODE_GRANULAR;
  67. quality = 0;
  68. }
  69. json_t *dataToJson() override {
  70. json_t *rootJ = json_object();
  71. json_object_set_new(rootJ, "playback", json_integer((int) playback));
  72. json_object_set_new(rootJ, "quality", json_integer(quality));
  73. json_object_set_new(rootJ, "blendMode", json_integer(blendMode));
  74. return rootJ;
  75. }
  76. void dataFromJson(json_t *rootJ) override {
  77. json_t *playbackJ = json_object_get(rootJ, "playback");
  78. if (playbackJ) {
  79. playback = (clouds::PlaybackMode) json_integer_value(playbackJ);
  80. }
  81. json_t *qualityJ = json_object_get(rootJ, "quality");
  82. if (qualityJ) {
  83. quality = json_integer_value(qualityJ);
  84. }
  85. json_t *blendModeJ = json_object_get(rootJ, "blendMode");
  86. if (blendModeJ) {
  87. blendMode = json_integer_value(blendModeJ);
  88. }
  89. }
  90. };
  91. Clouds::Clouds() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  92. const int memLen = 118784;
  93. const int ccmLen = 65536 - 128;
  94. block_mem = new uint8_t[memLen]();
  95. block_ccm = new uint8_t[ccmLen]();
  96. processor = new clouds::GranularProcessor();
  97. memset(processor, 0, sizeof(*processor));
  98. processor->Init(block_mem, memLen, block_ccm, ccmLen);
  99. onReset();
  100. }
  101. Clouds::~Clouds() {
  102. delete processor;
  103. delete[] block_mem;
  104. delete[] block_ccm;
  105. }
  106. void Clouds::step() {
  107. // Get input
  108. Frame<2> inputFrame = {};
  109. if (!inputBuffer.full()) {
  110. inputFrame.samples[0] = inputs[IN_L_INPUT].value * params[IN_GAIN_PARAM].value / 5.0;
  111. inputFrame.samples[1] = inputs[IN_R_INPUT].active ? inputs[IN_R_INPUT].value * params[IN_GAIN_PARAM].value / 5.0 : inputFrame.samples[0];
  112. inputBuffer.push(inputFrame);
  113. }
  114. if (freezeTrigger.process(params[FREEZE_PARAM].value)) {
  115. freeze ^= true;
  116. }
  117. if (blendTrigger.process(params[MODE_PARAM].value)) {
  118. blendMode = (blendMode + 1) % 4;
  119. }
  120. // Trigger
  121. if (inputs[TRIG_INPUT].value >= 1.0) {
  122. triggered = true;
  123. }
  124. // Render frames
  125. if (outputBuffer.empty()) {
  126. clouds::ShortFrame input[32] = {};
  127. // Convert input buffer
  128. {
  129. inputSrc.setRates(engineGetSampleRate(), 32000);
  130. Frame<2> inputFrames[32];
  131. int inLen = inputBuffer.size();
  132. int outLen = 32;
  133. inputSrc.process(inputBuffer.startData(), &inLen, inputFrames, &outLen);
  134. inputBuffer.startIncr(inLen);
  135. // We might not fill all of the input buffer if there is a deficiency, but this cannot be avoided due to imprecisions between the input and output SRC.
  136. for (int i = 0; i < outLen; i++) {
  137. input[i].l = clamp(inputFrames[i].samples[0] * 32767.0f, -32768.0f, 32767.0f);
  138. input[i].r = clamp(inputFrames[i].samples[1] * 32767.0f, -32768.0f, 32767.0f);
  139. }
  140. }
  141. // Set up processor
  142. processor->set_playback_mode(playback);
  143. processor->set_quality(quality);
  144. processor->Prepare();
  145. clouds::Parameters *p = processor->mutable_parameters();
  146. p->trigger = triggered;
  147. p->gate = triggered;
  148. p->freeze = freeze || (inputs[FREEZE_INPUT].value >= 1.0);
  149. p->position = clamp(params[POSITION_PARAM].value + inputs[POSITION_INPUT].value / 5.0f, 0.0f, 1.0f);
  150. p->size = clamp(params[SIZE_PARAM].value + inputs[SIZE_INPUT].value / 5.0f, 0.0f, 1.0f);
  151. p->pitch = clamp((params[PITCH_PARAM].value + inputs[PITCH_INPUT].value) * 12.0f, -48.0f, 48.0f);
  152. p->density = clamp(params[DENSITY_PARAM].value + inputs[DENSITY_INPUT].value / 5.0f, 0.0f, 1.0f);
  153. p->texture = clamp(params[TEXTURE_PARAM].value + inputs[TEXTURE_INPUT].value / 5.0f, 0.0f, 1.0f);
  154. p->dry_wet = params[BLEND_PARAM].value;
  155. p->stereo_spread = params[SPREAD_PARAM].value;
  156. p->feedback = params[FEEDBACK_PARAM].value;
  157. // TODO
  158. // Why doesn't dry audio get reverbed?
  159. p->reverb = params[REVERB_PARAM].value;
  160. float blend = inputs[BLEND_INPUT].value / 5.0f;
  161. switch (blendMode) {
  162. case 0:
  163. p->dry_wet += blend;
  164. p->dry_wet = clamp(p->dry_wet, 0.0f, 1.0f);
  165. break;
  166. case 1:
  167. p->stereo_spread += blend;
  168. p->stereo_spread = clamp(p->stereo_spread, 0.0f, 1.0f);
  169. break;
  170. case 2:
  171. p->feedback += blend;
  172. p->feedback = clamp(p->feedback, 0.0f, 1.0f);
  173. break;
  174. case 3:
  175. p->reverb += blend;
  176. p->reverb = clamp(p->reverb, 0.0f, 1.0f);
  177. break;
  178. }
  179. clouds::ShortFrame output[32];
  180. processor->Process(input, output, 32);
  181. // Convert output buffer
  182. {
  183. Frame<2> outputFrames[32];
  184. for (int i = 0; i < 32; i++) {
  185. outputFrames[i].samples[0] = output[i].l / 32768.0;
  186. outputFrames[i].samples[1] = output[i].r / 32768.0;
  187. }
  188. outputSrc.setRates(32000, engineGetSampleRate());
  189. int inLen = 32;
  190. int outLen = outputBuffer.capacity();
  191. outputSrc.process(outputFrames, &inLen, outputBuffer.endData(), &outLen);
  192. outputBuffer.endIncr(outLen);
  193. }
  194. triggered = false;
  195. }
  196. // Set output
  197. Frame<2> outputFrame = {};
  198. if (!outputBuffer.empty()) {
  199. outputFrame = outputBuffer.shift();
  200. outputs[OUT_L_OUTPUT].value = 5.0 * outputFrame.samples[0];
  201. outputs[OUT_R_OUTPUT].value = 5.0 * outputFrame.samples[1];
  202. }
  203. // Lights
  204. clouds::Parameters *p = processor->mutable_parameters();
  205. VUMeter vuMeter;
  206. vuMeter.dBInterval = 6.0;
  207. Frame<2> lightFrame = p->freeze ? outputFrame : inputFrame;
  208. vuMeter.setValue(fmaxf(fabsf(lightFrame.samples[0]), fabsf(lightFrame.samples[1])));
  209. lights[FREEZE_LIGHT].setBrightness(p->freeze ? 0.75 : 0.0);
  210. lights[MIX_GREEN_LIGHT].setBrightnessSmooth(vuMeter.getBrightness(3));
  211. lights[PAN_GREEN_LIGHT].setBrightnessSmooth(vuMeter.getBrightness(2));
  212. lights[FEEDBACK_GREEN_LIGHT].setBrightnessSmooth(vuMeter.getBrightness(1));
  213. lights[REVERB_GREEN_LIGHT].setBrightness(0.0);
  214. lights[MIX_RED_LIGHT].setBrightness(0.0);
  215. lights[PAN_RED_LIGHT].setBrightness(0.0);
  216. lights[FEEDBACK_RED_LIGHT].setBrightnessSmooth(vuMeter.getBrightness(1));
  217. lights[REVERB_RED_LIGHT].setBrightnessSmooth(vuMeter.getBrightness(0));
  218. }
  219. struct FreezeLight : YellowLight {
  220. FreezeLight() {
  221. box.size = Vec(28-6, 28-6);
  222. bgColor = COLOR_BLACK_TRANSPARENT;
  223. }
  224. };
  225. struct CloudsBlendItem : MenuItem {
  226. Clouds *module;
  227. int blendMode;
  228. void onAction(const event::Action &e) override {
  229. module->blendMode = blendMode;
  230. }
  231. void step() override {
  232. rightText = (module->blendMode == blendMode) ? "✔" : "";
  233. MenuItem::step();
  234. }
  235. };
  236. struct CloudsPlaybackItem : MenuItem {
  237. Clouds *module;
  238. clouds::PlaybackMode playback;
  239. void onAction(const event::Action &e) override {
  240. module->playback = playback;
  241. }
  242. void step() override {
  243. rightText = (module->playback == playback) ? "✔" : "";
  244. MenuItem::step();
  245. }
  246. };
  247. struct CloudsQualityItem : MenuItem {
  248. Clouds *module;
  249. int quality;
  250. void onAction(const event::Action &e) override {
  251. module->quality = quality;
  252. }
  253. void step() override {
  254. rightText = (module->quality == quality) ? "✔" : "";
  255. MenuItem::step();
  256. }
  257. };
  258. struct CloudsWidget : ModuleWidget {
  259. ParamWidget *blendParam;
  260. ParamWidget *spreadParam;
  261. ParamWidget *feedbackParam;
  262. ParamWidget *reverbParam;
  263. CloudsWidget(Clouds *module) : ModuleWidget(module) {
  264. setPanel(SVG::load(assetPlugin(pluginInstance, "res/Clouds.svg")));
  265. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  266. addChild(createWidget<ScrewSilver>(Vec(240, 0)));
  267. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  268. addChild(createWidget<ScrewSilver>(Vec(240, 365)));
  269. addParam(createParam<Rogan3PSRed>(Vec(27, 93), module, Clouds::POSITION_PARAM, 0.0, 1.0, 0.5));
  270. addParam(createParam<Rogan3PSGreen>(Vec(108, 93), module, Clouds::SIZE_PARAM, 0.0, 1.0, 0.5));
  271. addParam(createParam<Rogan3PSWhite>(Vec(190, 93), module, Clouds::PITCH_PARAM, -2.0, 2.0, 0.0));
  272. addParam(createParam<Rogan1PSRed>(Vec(14, 180), module, Clouds::IN_GAIN_PARAM, 0.0, 1.0, 0.5));
  273. addParam(createParam<Rogan1PSRed>(Vec(81, 180), module, Clouds::DENSITY_PARAM, 0.0, 1.0, 0.5));
  274. addParam(createParam<Rogan1PSGreen>(Vec(146, 180), module, Clouds::TEXTURE_PARAM, 0.0, 1.0, 0.5));
  275. blendParam = createParam<Rogan1PSWhite>(Vec(213, 180), module, Clouds::BLEND_PARAM, 0.0, 1.0, 0.5);
  276. addParam(blendParam);
  277. spreadParam = createParam<Rogan1PSRed>(Vec(213, 180), module, Clouds::SPREAD_PARAM, 0.0, 1.0, 0.0);
  278. addParam(spreadParam);
  279. feedbackParam = createParam<Rogan1PSGreen>(Vec(213, 180), module, Clouds::FEEDBACK_PARAM, 0.0, 1.0, 0.0);
  280. addParam(feedbackParam);
  281. reverbParam = createParam<Rogan1PSBlue>(Vec(213, 180), module, Clouds::REVERB_PARAM, 0.0, 1.0, 0.0);
  282. addParam(reverbParam);
  283. addParam(createParam<CKD6>(Vec(12, 43), module, Clouds::FREEZE_PARAM, 0.0, 1.0, 0.0));
  284. addParam(createParam<TL1105>(Vec(211, 50), module, Clouds::MODE_PARAM, 0.0, 1.0, 0.0));
  285. addParam(createParam<TL1105>(Vec(239, 50), module, Clouds::LOAD_PARAM, 0.0, 1.0, 0.0));
  286. addInput(createPort<PJ301MPort>(Vec(15, 274), PortWidget::INPUT, module, Clouds::FREEZE_INPUT));
  287. addInput(createPort<PJ301MPort>(Vec(58, 274), PortWidget::INPUT, module, Clouds::TRIG_INPUT));
  288. addInput(createPort<PJ301MPort>(Vec(101, 274), PortWidget::INPUT, module, Clouds::POSITION_INPUT));
  289. addInput(createPort<PJ301MPort>(Vec(144, 274), PortWidget::INPUT, module, Clouds::SIZE_INPUT));
  290. addInput(createPort<PJ301MPort>(Vec(188, 274), PortWidget::INPUT, module, Clouds::PITCH_INPUT));
  291. addInput(createPort<PJ301MPort>(Vec(230, 274), PortWidget::INPUT, module, Clouds::BLEND_INPUT));
  292. addInput(createPort<PJ301MPort>(Vec(15, 317), PortWidget::INPUT, module, Clouds::IN_L_INPUT));
  293. addInput(createPort<PJ301MPort>(Vec(58, 317), PortWidget::INPUT, module, Clouds::IN_R_INPUT));
  294. addInput(createPort<PJ301MPort>(Vec(101, 317), PortWidget::INPUT, module, Clouds::DENSITY_INPUT));
  295. addInput(createPort<PJ301MPort>(Vec(144, 317), PortWidget::INPUT, module, Clouds::TEXTURE_INPUT));
  296. addOutput(createPort<PJ301MPort>(Vec(188, 317), PortWidget::OUTPUT, module, Clouds::OUT_L_OUTPUT));
  297. addOutput(createPort<PJ301MPort>(Vec(230, 317), PortWidget::OUTPUT, module, Clouds::OUT_R_OUTPUT));
  298. addChild(createLight<FreezeLight>(Vec(12+3, 43+3), module, Clouds::FREEZE_LIGHT));
  299. addChild(createLight<MediumLight<GreenRedLight>>(Vec(82.5, 53), module, Clouds::MIX_GREEN_LIGHT));
  300. addChild(createLight<MediumLight<GreenRedLight>>(Vec(114.5, 53), module, Clouds::PAN_GREEN_LIGHT));
  301. addChild(createLight<MediumLight<GreenRedLight>>(Vec(145.5, 53), module, Clouds::FEEDBACK_GREEN_LIGHT));
  302. addChild(createLight<MediumLight<GreenRedLight>>(Vec(177.5, 53), module, Clouds::REVERB_GREEN_LIGHT));
  303. }
  304. void step() override {
  305. Clouds *module = dynamic_cast<Clouds*>(this->module);
  306. blendParam->visible = (module->blendMode == 0);
  307. spreadParam->visible = (module->blendMode == 1);
  308. feedbackParam->visible = (module->blendMode == 2);
  309. reverbParam->visible = (module->blendMode == 3);
  310. ModuleWidget::step();
  311. }
  312. void appendContextMenu(Menu *menu) override {
  313. Clouds *module = dynamic_cast<Clouds*>(this->module);
  314. assert(module);
  315. menu->addChild(construct<MenuLabel>());
  316. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Blend knob"));
  317. menu->addChild(construct<CloudsBlendItem>(&MenuItem::text, "Wet/dry", &CloudsBlendItem::module, module, &CloudsBlendItem::blendMode, 0));
  318. menu->addChild(construct<CloudsBlendItem>(&MenuItem::text, "Spread", &CloudsBlendItem::module, module, &CloudsBlendItem::blendMode, 1));
  319. menu->addChild(construct<CloudsBlendItem>(&MenuItem::text, "Feedback", &CloudsBlendItem::module, module, &CloudsBlendItem::blendMode, 2));
  320. menu->addChild(construct<CloudsBlendItem>(&MenuItem::text, "Reverb", &CloudsBlendItem::module, module, &CloudsBlendItem::blendMode, 3));
  321. menu->addChild(construct<MenuLabel>());
  322. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Alternative mode"));
  323. menu->addChild(construct<CloudsPlaybackItem>(&MenuItem::text, "Granular", &CloudsPlaybackItem::module, module, &CloudsPlaybackItem::playback, clouds::PLAYBACK_MODE_GRANULAR));
  324. menu->addChild(construct<CloudsPlaybackItem>(&MenuItem::text, "Pitch-shifter/time-stretcher", &CloudsPlaybackItem::module, module, &CloudsPlaybackItem::playback, clouds::PLAYBACK_MODE_STRETCH));
  325. menu->addChild(construct<CloudsPlaybackItem>(&MenuItem::text, "Looping delay", &CloudsPlaybackItem::module, module, &CloudsPlaybackItem::playback, clouds::PLAYBACK_MODE_LOOPING_DELAY));
  326. menu->addChild(construct<CloudsPlaybackItem>(&MenuItem::text, "Spectral madness", &CloudsPlaybackItem::module, module, &CloudsPlaybackItem::playback, clouds::PLAYBACK_MODE_SPECTRAL));
  327. menu->addChild(construct<MenuLabel>());
  328. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Quality"));
  329. menu->addChild(construct<CloudsQualityItem>(&MenuItem::text, "1s 32kHz 16-bit stereo", &CloudsQualityItem::module, module, &CloudsQualityItem::quality, 0));
  330. menu->addChild(construct<CloudsQualityItem>(&MenuItem::text, "2s 32kHz 16-bit mono", &CloudsQualityItem::module, module, &CloudsQualityItem::quality, 1));
  331. menu->addChild(construct<CloudsQualityItem>(&MenuItem::text, "4s 16kHz 8-bit µ-law stereo", &CloudsQualityItem::module, module, &CloudsQualityItem::quality, 2));
  332. menu->addChild(construct<CloudsQualityItem>(&MenuItem::text, "8s 16kHz 8-bit µ-law mono", &CloudsQualityItem::module, module, &CloudsQualityItem::quality, 3));
  333. }
  334. };
  335. Model *modelClouds = createModel<Clouds, CloudsWidget>("Clouds");