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.

414 lines
14KB

  1. #include "plugin.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. dsp::SampleRateConverter<2> inputSrc;
  47. dsp::SampleRateConverter<2> outputSrc;
  48. dsp::DoubleRingBuffer<dsp::Frame<2>, 256> inputBuffer;
  49. dsp::DoubleRingBuffer<dsp::Frame<2>, 256> outputBuffer;
  50. uint8_t* block_mem;
  51. uint8_t* block_ccm;
  52. clouds::GranularProcessor* processor;
  53. bool triggered = false;
  54. dsp::SchmittTrigger freezeTrigger;
  55. bool freeze = false;
  56. dsp::SchmittTrigger blendTrigger;
  57. int blendMode = 0;
  58. clouds::PlaybackMode playback;
  59. int quality = 0;
  60. Clouds() {
  61. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  62. configParam(POSITION_PARAM, 0.0, 1.0, 0.5, "Grain position");
  63. configParam(SIZE_PARAM, 0.0, 1.0, 0.5, "Grain size");
  64. configParam(PITCH_PARAM, -2.0, 2.0, 0.0, "Grain pitch");
  65. configParam(IN_GAIN_PARAM, 0.0, 1.0, 0.5, "Audio input gain");
  66. configParam(DENSITY_PARAM, 0.0, 1.0, 0.5, "Grain density");
  67. configParam(TEXTURE_PARAM, 0.0, 1.0, 0.5, "Grain texture");
  68. configParam(BLEND_PARAM, 0.0, 1.0, 0.5, "Dry/wet");
  69. configParam(SPREAD_PARAM, 0.0, 1.0, 0.0, "Stereo spread");
  70. configParam(FEEDBACK_PARAM, 0.0, 1.0, 0.0, "Feedback amount");
  71. configParam(REVERB_PARAM, 0.0, 1.0, 0.0, "Reverb amount");
  72. configButton(FREEZE_PARAM, "Freeze");
  73. configButton(MODE_PARAM, "Mode");
  74. configButton(LOAD_PARAM, "Load/save");
  75. configInput(FREEZE_INPUT, "Freeze");
  76. configInput(TRIG_INPUT, "Trigger");
  77. configInput(POSITION_INPUT, "Position");
  78. configInput(SIZE_INPUT, "Size");
  79. configInput(PITCH_INPUT, "Pitch (1V/oct)");
  80. configInput(BLEND_INPUT, "Blend");
  81. configInput(IN_L_INPUT, "Left");
  82. configInput(IN_R_INPUT, "Right");
  83. configInput(DENSITY_INPUT, "Density");
  84. configInput(TEXTURE_INPUT, "Texture");
  85. configOutput(OUT_L_OUTPUT, "Left");
  86. configOutput(OUT_R_OUTPUT, "Right");
  87. configBypass(IN_L_INPUT, OUT_L_OUTPUT);
  88. configBypass(IN_R_INPUT, OUT_R_OUTPUT);
  89. const int memLen = 118784;
  90. const int ccmLen = 65536 - 128;
  91. block_mem = new uint8_t[memLen]();
  92. block_ccm = new uint8_t[ccmLen]();
  93. processor = new clouds::GranularProcessor();
  94. memset(processor, 0, sizeof(*processor));
  95. processor->Init(block_mem, memLen, block_ccm, ccmLen);
  96. onReset();
  97. }
  98. ~Clouds() {
  99. delete processor;
  100. delete[] block_mem;
  101. delete[] block_ccm;
  102. }
  103. void process(const ProcessArgs& args) override {
  104. // Get input
  105. dsp::Frame<2> inputFrame = {};
  106. if (!inputBuffer.full()) {
  107. inputFrame.samples[0] = inputs[IN_L_INPUT].getVoltage() * params[IN_GAIN_PARAM].getValue() / 5.0;
  108. inputFrame.samples[1] = inputs[IN_R_INPUT].isConnected() ? inputs[IN_R_INPUT].getVoltage() * params[IN_GAIN_PARAM].getValue() / 5.0 : inputFrame.samples[0];
  109. inputBuffer.push(inputFrame);
  110. }
  111. if (freezeTrigger.process(params[FREEZE_PARAM].getValue())) {
  112. freeze ^= true;
  113. }
  114. if (blendTrigger.process(params[MODE_PARAM].getValue())) {
  115. blendMode = (blendMode + 1) % 4;
  116. }
  117. // Trigger
  118. if (inputs[TRIG_INPUT].getVoltage() >= 1.0) {
  119. triggered = true;
  120. }
  121. // Render frames
  122. if (outputBuffer.empty()) {
  123. clouds::ShortFrame input[32] = {};
  124. // Convert input buffer
  125. {
  126. inputSrc.setRates(args.sampleRate, 32000);
  127. dsp::Frame<2> inputFrames[32];
  128. int inLen = inputBuffer.size();
  129. int outLen = 32;
  130. inputSrc.process(inputBuffer.startData(), &inLen, inputFrames, &outLen);
  131. inputBuffer.startIncr(inLen);
  132. // 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.
  133. for (int i = 0; i < outLen; i++) {
  134. input[i].l = clamp(inputFrames[i].samples[0] * 32767.0f, -32768.0f, 32767.0f);
  135. input[i].r = clamp(inputFrames[i].samples[1] * 32767.0f, -32768.0f, 32767.0f);
  136. }
  137. }
  138. // Set up processor
  139. processor->set_playback_mode(playback);
  140. processor->set_quality(quality);
  141. processor->Prepare();
  142. clouds::Parameters* p = processor->mutable_parameters();
  143. p->trigger = triggered;
  144. p->gate = triggered;
  145. p->freeze = freeze || (inputs[FREEZE_INPUT].getVoltage() >= 1.0);
  146. p->position = clamp(params[POSITION_PARAM].getValue() + inputs[POSITION_INPUT].getVoltage() / 5.0f, 0.0f, 1.0f);
  147. p->size = clamp(params[SIZE_PARAM].getValue() + inputs[SIZE_INPUT].getVoltage() / 5.0f, 0.0f, 1.0f);
  148. p->pitch = clamp((params[PITCH_PARAM].getValue() + inputs[PITCH_INPUT].getVoltage()) * 12.0f, -48.0f, 48.0f);
  149. p->density = clamp(params[DENSITY_PARAM].getValue() + inputs[DENSITY_INPUT].getVoltage() / 5.0f, 0.0f, 1.0f);
  150. p->texture = clamp(params[TEXTURE_PARAM].getValue() + inputs[TEXTURE_INPUT].getVoltage() / 5.0f, 0.0f, 1.0f);
  151. p->dry_wet = params[BLEND_PARAM].getValue();
  152. p->stereo_spread = params[SPREAD_PARAM].getValue();
  153. p->feedback = params[FEEDBACK_PARAM].getValue();
  154. // TODO
  155. // Why doesn't dry audio get reverbed?
  156. p->reverb = params[REVERB_PARAM].getValue();
  157. float blend = inputs[BLEND_INPUT].getVoltage() / 5.0f;
  158. switch (blendMode) {
  159. case 0:
  160. p->dry_wet += blend;
  161. p->dry_wet = clamp(p->dry_wet, 0.0f, 1.0f);
  162. break;
  163. case 1:
  164. p->stereo_spread += blend;
  165. p->stereo_spread = clamp(p->stereo_spread, 0.0f, 1.0f);
  166. break;
  167. case 2:
  168. p->feedback += blend;
  169. p->feedback = clamp(p->feedback, 0.0f, 1.0f);
  170. break;
  171. case 3:
  172. p->reverb += blend;
  173. p->reverb = clamp(p->reverb, 0.0f, 1.0f);
  174. break;
  175. }
  176. clouds::ShortFrame output[32];
  177. processor->Process(input, output, 32);
  178. // Convert output buffer
  179. {
  180. dsp::Frame<2> outputFrames[32];
  181. for (int i = 0; i < 32; i++) {
  182. outputFrames[i].samples[0] = output[i].l / 32768.0;
  183. outputFrames[i].samples[1] = output[i].r / 32768.0;
  184. }
  185. outputSrc.setRates(32000, args.sampleRate);
  186. int inLen = 32;
  187. int outLen = outputBuffer.capacity();
  188. outputSrc.process(outputFrames, &inLen, outputBuffer.endData(), &outLen);
  189. outputBuffer.endIncr(outLen);
  190. }
  191. triggered = false;
  192. }
  193. // Set output
  194. dsp::Frame<2> outputFrame = {};
  195. if (!outputBuffer.empty()) {
  196. outputFrame = outputBuffer.shift();
  197. outputs[OUT_L_OUTPUT].setVoltage(5.0 * outputFrame.samples[0]);
  198. outputs[OUT_R_OUTPUT].setVoltage(5.0 * outputFrame.samples[1]);
  199. }
  200. // Lights
  201. clouds::Parameters* p = processor->mutable_parameters();
  202. dsp::VuMeter vuMeter;
  203. vuMeter.dBInterval = 6.0;
  204. dsp::Frame<2> lightFrame = p->freeze ? outputFrame : inputFrame;
  205. vuMeter.setValue(fmaxf(fabsf(lightFrame.samples[0]), fabsf(lightFrame.samples[1])));
  206. lights[FREEZE_LIGHT].setBrightness(p->freeze ? 0.75 : 0.0);
  207. lights[MIX_GREEN_LIGHT].setSmoothBrightness(vuMeter.getBrightness(3), args.sampleTime);
  208. lights[PAN_GREEN_LIGHT].setSmoothBrightness(vuMeter.getBrightness(2), args.sampleTime);
  209. lights[FEEDBACK_GREEN_LIGHT].setSmoothBrightness(vuMeter.getBrightness(1), args.sampleTime);
  210. lights[REVERB_GREEN_LIGHT].setBrightness(0.0);
  211. lights[MIX_RED_LIGHT].setBrightness(0.0);
  212. lights[PAN_RED_LIGHT].setBrightness(0.0);
  213. lights[FEEDBACK_RED_LIGHT].setSmoothBrightness(vuMeter.getBrightness(1), args.sampleTime);
  214. lights[REVERB_RED_LIGHT].setSmoothBrightness(vuMeter.getBrightness(0), args.sampleTime);
  215. }
  216. void onReset() override {
  217. freeze = false;
  218. blendMode = 0;
  219. playback = clouds::PLAYBACK_MODE_GRANULAR;
  220. quality = 0;
  221. }
  222. json_t* dataToJson() override {
  223. json_t* rootJ = json_object();
  224. json_object_set_new(rootJ, "playback", json_integer((int) playback));
  225. json_object_set_new(rootJ, "quality", json_integer(quality));
  226. json_object_set_new(rootJ, "blendMode", json_integer(blendMode));
  227. return rootJ;
  228. }
  229. void dataFromJson(json_t* rootJ) override {
  230. json_t* playbackJ = json_object_get(rootJ, "playback");
  231. if (playbackJ) {
  232. playback = (clouds::PlaybackMode) json_integer_value(playbackJ);
  233. }
  234. json_t* qualityJ = json_object_get(rootJ, "quality");
  235. if (qualityJ) {
  236. quality = json_integer_value(qualityJ);
  237. }
  238. json_t* blendModeJ = json_object_get(rootJ, "blendMode");
  239. if (blendModeJ) {
  240. blendMode = json_integer_value(blendModeJ);
  241. }
  242. }
  243. };
  244. struct FreezeLight : YellowLight {
  245. FreezeLight() {
  246. box.size = Vec(28 - 6, 28 - 6);
  247. bgColor = color::BLACK_TRANSPARENT;
  248. }
  249. };
  250. struct CloudsWidget : ModuleWidget {
  251. ParamWidget* blendParam;
  252. ParamWidget* spreadParam;
  253. ParamWidget* feedbackParam;
  254. ParamWidget* reverbParam;
  255. CloudsWidget(Clouds* module) {
  256. setModule(module);
  257. setPanel(Svg::load(asset::plugin(pluginInstance, "res/Clouds.svg")));
  258. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  259. addChild(createWidget<ScrewSilver>(Vec(240, 0)));
  260. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  261. addChild(createWidget<ScrewSilver>(Vec(240, 365)));
  262. addParam(createParam<Rogan3PSRed>(Vec(27, 93), module, Clouds::POSITION_PARAM));
  263. addParam(createParam<Rogan3PSGreen>(Vec(108, 93), module, Clouds::SIZE_PARAM));
  264. addParam(createParam<Rogan3PSWhite>(Vec(190, 93), module, Clouds::PITCH_PARAM));
  265. addParam(createParam<Rogan1PSRed>(Vec(14, 180), module, Clouds::IN_GAIN_PARAM));
  266. addParam(createParam<Rogan1PSRed>(Vec(81, 180), module, Clouds::DENSITY_PARAM));
  267. addParam(createParam<Rogan1PSGreen>(Vec(146, 180), module, Clouds::TEXTURE_PARAM));
  268. blendParam = createParam<Rogan1PSWhite>(Vec(213, 180), module, Clouds::BLEND_PARAM);
  269. addParam(blendParam);
  270. spreadParam = createParam<Rogan1PSRed>(Vec(213, 180), module, Clouds::SPREAD_PARAM);
  271. spreadParam->hide();
  272. addParam(spreadParam);
  273. feedbackParam = createParam<Rogan1PSGreen>(Vec(213, 180), module, Clouds::FEEDBACK_PARAM);
  274. feedbackParam->hide();
  275. addParam(feedbackParam);
  276. reverbParam = createParam<Rogan1PSBlue>(Vec(213, 180), module, Clouds::REVERB_PARAM);
  277. reverbParam->hide();
  278. addParam(reverbParam);
  279. addParam(createParam<CKD6>(Vec(12, 43), module, Clouds::FREEZE_PARAM));
  280. addParam(createParam<TL1105>(Vec(211, 50), module, Clouds::MODE_PARAM));
  281. addParam(createParam<TL1105>(Vec(239, 50), module, Clouds::LOAD_PARAM));
  282. addInput(createInput<PJ301MPort>(Vec(15, 274), module, Clouds::FREEZE_INPUT));
  283. addInput(createInput<PJ301MPort>(Vec(58, 274), module, Clouds::TRIG_INPUT));
  284. addInput(createInput<PJ301MPort>(Vec(101, 274), module, Clouds::POSITION_INPUT));
  285. addInput(createInput<PJ301MPort>(Vec(144, 274), module, Clouds::SIZE_INPUT));
  286. addInput(createInput<PJ301MPort>(Vec(188, 274), module, Clouds::PITCH_INPUT));
  287. addInput(createInput<PJ301MPort>(Vec(230, 274), module, Clouds::BLEND_INPUT));
  288. addInput(createInput<PJ301MPort>(Vec(15, 317), module, Clouds::IN_L_INPUT));
  289. addInput(createInput<PJ301MPort>(Vec(58, 317), module, Clouds::IN_R_INPUT));
  290. addInput(createInput<PJ301MPort>(Vec(101, 317), module, Clouds::DENSITY_INPUT));
  291. addInput(createInput<PJ301MPort>(Vec(144, 317), module, Clouds::TEXTURE_INPUT));
  292. addOutput(createOutput<PJ301MPort>(Vec(188, 317), module, Clouds::OUT_L_OUTPUT));
  293. addOutput(createOutput<PJ301MPort>(Vec(230, 317), module, Clouds::OUT_R_OUTPUT));
  294. addChild(createLight<FreezeLight>(Vec(12 + 3, 43 + 3), module, Clouds::FREEZE_LIGHT));
  295. addChild(createLight<MediumLight<GreenRedLight>>(Vec(82.5, 53), module, Clouds::MIX_GREEN_LIGHT));
  296. addChild(createLight<MediumLight<GreenRedLight>>(Vec(114.5, 53), module, Clouds::PAN_GREEN_LIGHT));
  297. addChild(createLight<MediumLight<GreenRedLight>>(Vec(145.5, 53), module, Clouds::FEEDBACK_GREEN_LIGHT));
  298. addChild(createLight<MediumLight<GreenRedLight>>(Vec(177.5, 53), module, Clouds::REVERB_GREEN_LIGHT));
  299. }
  300. void step() override {
  301. Clouds* module = dynamic_cast<Clouds*>(this->module);
  302. if (module) {
  303. blendParam->visible = (module->blendMode == 0);
  304. spreadParam->visible = (module->blendMode == 1);
  305. feedbackParam->visible = (module->blendMode == 2);
  306. reverbParam->visible = (module->blendMode == 3);
  307. }
  308. ModuleWidget::step();
  309. }
  310. void appendContextMenu(Menu* menu) override {
  311. Clouds* module = dynamic_cast<Clouds*>(this->module);
  312. assert(module);
  313. menu->addChild(new MenuSeparator);
  314. menu->addChild(createMenuLabel("Blend knob"));
  315. static const std::vector<std::string> blendLabels = {
  316. "Wet/dry",
  317. "Spread",
  318. "Feedback",
  319. "Reverb",
  320. };
  321. for (int i = 0; i < (int) blendLabels.size(); i++) {
  322. menu->addChild(createCheckMenuItem(blendLabels[i], "",
  323. [=]() {return module->blendMode == i;},
  324. [=]() {module->blendMode = i;}
  325. ));
  326. }
  327. menu->addChild(new MenuSeparator);
  328. menu->addChild(createMenuLabel("Alternate mode"));
  329. static const std::vector<std::string> playbackLabels = {
  330. "Granular",
  331. "Pitch-shifter/time-stretcher",
  332. "Looping delay",
  333. "Spectral madness",
  334. };
  335. for (int i = 0; i < (int) playbackLabels.size(); i++) {
  336. menu->addChild(createCheckMenuItem(playbackLabels[i], "",
  337. [=]() {return module->playback == i;},
  338. [=]() {module->playback = (clouds::PlaybackMode) i;}
  339. ));
  340. }
  341. menu->addChild(new MenuSeparator);
  342. menu->addChild(createMenuLabel("Quality"));
  343. static const std::vector<std::string> qualityLabels = {
  344. "1s 32kHz 16-bit stereo",
  345. "2s 32kHz 16-bit mono",
  346. "4s 16kHz 8-bit µ-law stereo",
  347. "8s 16kHz 8-bit µ-law mono",
  348. };
  349. for (int i = 0; i < (int) qualityLabels.size(); i++) {
  350. menu->addChild(createCheckMenuItem(qualityLabels[i], "",
  351. [=]() {return module->quality == i;},
  352. [=]() {module->quality = i;}
  353. ));
  354. }
  355. }
  356. };
  357. Model* modelClouds = createModel<Clouds, CloudsWidget>("Clouds");