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.

400 lines
14KB

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