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.

333 lines
11KB

  1. #include "FrozenWasteland.hpp"
  2. #include "dsp/decimator.hpp"
  3. #include "dsp/digital.hpp"
  4. #include "filters/biquad.h"
  5. using namespace std;
  6. #define BANDS 16
  7. namespace rack_plugin_FrozenWasteland {
  8. struct MrBlueSky : Module {
  9. enum ParamIds {
  10. BG_PARAM,
  11. ATTACK_PARAM = BG_PARAM + BANDS,
  12. DECAY_PARAM,
  13. CARRIER_Q_PARAM,
  14. MOD_Q_PARAM,
  15. BAND_OFFSET_PARAM,
  16. GMOD_PARAM,
  17. GCARR_PARAM,
  18. G_PARAM,
  19. SHAPE_PARAM,
  20. ATTACK_CV_ATTENUVERTER_PARAM,
  21. DECAY_CV_ATTENUVERTER_PARAM,
  22. CARRIER_Q_CV_ATTENUVERTER_PARAM,
  23. MODIFER_Q_CV_ATTENUVERTER_PARAM,
  24. SHIFT_BAND_OFFSET_CV_ATTENUVERTER_PARAM,
  25. NUM_PARAMS
  26. };
  27. enum InputIds {
  28. CARRIER_IN,
  29. IN_MOD = CARRIER_IN + BANDS,
  30. IN_CARR,
  31. ATTACK_INPUT,
  32. DECAY_INPUT,
  33. CARRIER_Q_INPUT,
  34. MOD_Q_INPUT,
  35. SHIFT_BAND_OFFSET_LEFT_INPUT,
  36. SHIFT_BAND_OFFSET_RIGHT_INPUT,
  37. SHIFT_BAND_OFFSET_INPUT,
  38. NUM_INPUTS
  39. };
  40. enum OutputIds {
  41. MOD_OUT,
  42. OUT = MOD_OUT + BANDS,
  43. NUM_OUTPUTS
  44. };
  45. enum LightIds {
  46. LEARN_LIGHT,
  47. NUM_LIGHTS
  48. };
  49. Biquad* iFilter[2*BANDS];
  50. Biquad* cFilter[2*BANDS];
  51. float mem[BANDS] = {0};
  52. float freq[BANDS] = {125,185,270,350,430,530,630,780,950,1150,1380,1680,2070,2780,3800,6400};
  53. float peaks[BANDS] = {0};
  54. float lastCarrierQ = 0;
  55. float lastModQ = 0;
  56. int bandOffset = 0;
  57. int shiftIndex = 0;
  58. int lastBandOffset = 0;
  59. SchmittTrigger shiftLeftTrigger,shiftRightTrigger;
  60. MrBlueSky() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  61. for(int i=0; i<2*BANDS; i++) {
  62. iFilter[i] = new Biquad(bq_type_bandpass, freq[i%BANDS] / engineGetSampleRate(), 5, 6);
  63. cFilter[i] = new Biquad(bq_type_bandpass, freq[i%BANDS] / engineGetSampleRate(), 5, 6);
  64. };
  65. }
  66. void step() override;
  67. void reset() override {
  68. bandOffset =0;
  69. }
  70. };
  71. void MrBlueSky::step() {
  72. // Band Offset Processing
  73. bandOffset = params[BAND_OFFSET_PARAM].value;
  74. if(inputs[SHIFT_BAND_OFFSET_INPUT].active) {
  75. bandOffset += inputs[SHIFT_BAND_OFFSET_INPUT].value * params[SHIFT_BAND_OFFSET_CV_ATTENUVERTER_PARAM].value;
  76. }
  77. if(bandOffset != lastBandOffset) {
  78. shiftIndex = 0;
  79. lastBandOffset = bandOffset;
  80. }
  81. if(inputs[SHIFT_BAND_OFFSET_LEFT_INPUT].active) {
  82. if (shiftLeftTrigger.process(inputs[SHIFT_BAND_OFFSET_LEFT_INPUT].value)) {
  83. shiftIndex -= 1;
  84. if(shiftIndex <= -BANDS) {
  85. shiftIndex = BANDS -1;
  86. }
  87. }
  88. }
  89. if(inputs[SHIFT_BAND_OFFSET_RIGHT_INPUT].active) {
  90. if (shiftRightTrigger.process(inputs[SHIFT_BAND_OFFSET_RIGHT_INPUT].value)) {
  91. shiftIndex += 1;
  92. if(shiftIndex >= BANDS) {
  93. shiftIndex = (-BANDS) + 1;
  94. }
  95. }
  96. }
  97. bandOffset +=shiftIndex;
  98. //Hack until I can do int clamping
  99. if(bandOffset <= -BANDS) {
  100. bandOffset += (BANDS*2) - 1;
  101. }
  102. if(bandOffset >= BANDS) {
  103. bandOffset -= (BANDS*2) + 1;
  104. }
  105. //So some vocoding!
  106. float inM = inputs[IN_MOD].value/5;
  107. float inC = inputs[IN_CARR].value/5;
  108. const float slewMin = 0.001;
  109. const float slewMax = 500.0;
  110. const float shapeScale = 1/10.0;
  111. const float qEpsilon = 0.1;
  112. float attack = params[ATTACK_PARAM].value;
  113. float decay = params[DECAY_PARAM].value;
  114. if(inputs[ATTACK_INPUT].active) {
  115. attack += clamp(inputs[ATTACK_INPUT].value * params[ATTACK_CV_ATTENUVERTER_PARAM].value / 20.0f,-0.25f,.25f);
  116. }
  117. if(inputs[DECAY_INPUT].active) {
  118. decay += clamp(inputs[DECAY_INPUT].value * params[DECAY_CV_ATTENUVERTER_PARAM].value / 20.0f,-0.25f,.25f);
  119. }
  120. float slewAttack = slewMax * powf(slewMin / slewMax, attack);
  121. float slewDecay = slewMax * powf(slewMin / slewMax, decay);
  122. float out = 0.0;
  123. //Check Mod Q
  124. float currentQ = params[MOD_Q_PARAM].value;
  125. if(inputs[MOD_Q_PARAM].active) {
  126. currentQ += inputs[MOD_Q_INPUT].value * params[MODIFER_Q_CV_ATTENUVERTER_PARAM].value;
  127. }
  128. currentQ = clamp(currentQ,1.0f,15.0f);
  129. if (abs(currentQ - lastModQ) >= qEpsilon ) {
  130. for(int i=0; i<2*BANDS; i++) {
  131. iFilter[i]->setQ(currentQ);
  132. }
  133. lastModQ = currentQ;
  134. }
  135. //Check Carrier Q
  136. currentQ = params[CARRIER_Q_PARAM].value;
  137. if(inputs[CARRIER_Q_INPUT].active) {
  138. currentQ += inputs[CARRIER_Q_INPUT].value * params[CARRIER_Q_CV_ATTENUVERTER_PARAM].value;
  139. }
  140. currentQ = clamp(currentQ,1.0f,15.0f);
  141. if (abs(currentQ - lastCarrierQ) >= qEpsilon ) {
  142. for(int i=0; i<2*BANDS; i++) {
  143. cFilter[i]->setQ(currentQ);
  144. }
  145. lastCarrierQ = currentQ;
  146. }
  147. //First process all the modifier bands
  148. for(int i=0; i<BANDS; i++) {
  149. float coeff = mem[i];
  150. float peak = abs(iFilter[i+BANDS]->process(iFilter[i]->process(inM*params[GMOD_PARAM].value)));
  151. if (peak>coeff) {
  152. coeff += slewAttack * shapeScale * (peak - coeff) / engineGetSampleRate();
  153. if (coeff > peak)
  154. coeff = peak;
  155. }
  156. else if (peak < coeff) {
  157. coeff -= slewDecay * shapeScale * (coeff - peak) / engineGetSampleRate();
  158. if (coeff < peak)
  159. coeff = peak;
  160. }
  161. peaks[i]=peak;
  162. mem[i]=coeff;
  163. outputs[MOD_OUT+i].value = coeff * 5.0;
  164. }
  165. //Then process carrier bands. Mod bands are normalled to their matched carrier band unless an insert
  166. for(int i=0; i<BANDS; i++) {
  167. float coeff;
  168. if(inputs[(CARRIER_IN+i+bandOffset) % BANDS].active) {
  169. coeff = inputs[CARRIER_IN+i+bandOffset].value / 5.0;
  170. } else {
  171. coeff = mem[(i+bandOffset) % BANDS];
  172. }
  173. float bandOut = cFilter[i+BANDS]->process(cFilter[i]->process(inC*params[GCARR_PARAM].value)) * coeff * params[BG_PARAM+i].value;
  174. out += bandOut;
  175. }
  176. outputs[OUT].value = out * 5 * params[G_PARAM].value;
  177. }
  178. struct MrBlueSkyBandDisplay : TransparentWidget {
  179. MrBlueSky *module;
  180. std::shared_ptr<Font> font;
  181. MrBlueSkyBandDisplay() {
  182. font = Font::load(assetPlugin(plugin, "res/fonts/Sudo.ttf"));
  183. }
  184. void draw(NVGcontext *vg) override {
  185. nvgFontSize(vg, 14);
  186. nvgFontFaceId(vg, font->handle);
  187. nvgStrokeWidth(vg, 2);
  188. nvgTextLetterSpacing(vg, -2);
  189. nvgTextAlign(vg, NVG_ALIGN_CENTER);
  190. //static const int portX0[4] = {20, 63, 106, 149};
  191. for (int i=0; i<BANDS; i++) {
  192. char fVal[10];
  193. snprintf(fVal, sizeof(fVal), "%1i", (int)module->freq[i]);
  194. nvgFillColor(vg,nvgRGBA(rescale(clamp(module->peaks[i],0.0f,1.0f),0,1,0,255), 0, 0, 255));
  195. nvgText(vg, 56 + 33*i, 30, fVal, NULL);
  196. }
  197. }
  198. };
  199. struct BandOffsetDisplay : TransparentWidget {
  200. MrBlueSky *module;
  201. int frame = 0;
  202. std::shared_ptr<Font> font;
  203. BandOffsetDisplay() {
  204. font = Font::load(assetPlugin(plugin, "res/fonts/01 Digit.ttf"));
  205. }
  206. void drawDuration(NVGcontext *vg, Vec pos, float bandOffset) {
  207. nvgFontSize(vg, 20);
  208. nvgFontFaceId(vg, font->handle);
  209. nvgTextLetterSpacing(vg, -2);
  210. nvgFillColor(vg, nvgRGBA(0x00, 0xff, 0x00, 0xff));
  211. char text[128];
  212. snprintf(text, sizeof(text), " % 2.0f", bandOffset);
  213. nvgText(vg, pos.x + 22, pos.y, text, NULL);
  214. }
  215. void draw(NVGcontext *vg) override {
  216. drawDuration(vg, Vec(0, box.size.y - 150), module->bandOffset);
  217. }
  218. };
  219. struct MrBlueSkyWidget : ModuleWidget {
  220. MrBlueSkyWidget(MrBlueSky *module);
  221. };
  222. MrBlueSkyWidget::MrBlueSkyWidget(MrBlueSky *module) : ModuleWidget(module) {
  223. box.size = Vec(RACK_GRID_WIDTH*39, RACK_GRID_HEIGHT);
  224. {
  225. SVGPanel *panel = new SVGPanel();
  226. panel->box.size = box.size;
  227. panel->setBackground(SVG::load(assetPlugin(plugin, "res/MrBlueSky.svg")));
  228. addChild(panel);
  229. }
  230. MrBlueSkyBandDisplay *bandDisplay = new MrBlueSkyBandDisplay();
  231. bandDisplay->module = module;
  232. bandDisplay->box.pos = Vec(12, 12);
  233. bandDisplay->box.size = Vec(700, 70);
  234. addChild(bandDisplay);
  235. {
  236. BandOffsetDisplay *offsetDisplay = new BandOffsetDisplay();
  237. offsetDisplay->module = module;
  238. offsetDisplay->box.pos = Vec(435, 200);
  239. offsetDisplay->box.size = Vec(box.size.x, 150);
  240. addChild(offsetDisplay);
  241. }
  242. for (int i = 0; i < BANDS; i++) {
  243. addParam( ParamWidget::create<RoundBlackKnob>(Vec(53 + 33*i, 120), module, MrBlueSky::BG_PARAM + i, 0, 2, 1));
  244. }
  245. addParam(ParamWidget::create<RoundBlackKnob>(Vec(34, 177), module, MrBlueSky::ATTACK_PARAM, 0.0, 0.25, 0.0));
  246. addParam(ParamWidget::create<RoundBlackKnob>(Vec(116, 177), module, MrBlueSky::DECAY_PARAM, 0.0, 0.25, 0.0));
  247. addParam(ParamWidget::create<RoundBlackKnob>(Vec(198, 177), module, MrBlueSky::CARRIER_Q_PARAM, 1.0, 15.0, 5.0));
  248. addParam(ParamWidget::create<RoundBlackKnob>(Vec(280, 177), module, MrBlueSky::MOD_Q_PARAM, 1.0, 15.0, 5.0));
  249. addParam(ParamWidget::create<RoundBlackKnob>(Vec(392, 177), module, MrBlueSky::BAND_OFFSET_PARAM, -15.5, 15.5, 0.0));
  250. addParam(ParamWidget::create<RoundBlackKnob>(Vec(40, 284), module, MrBlueSky::GMOD_PARAM, 1, 10, 5));
  251. addParam(ParamWidget::create<RoundBlackKnob>(Vec(120, 284), module, MrBlueSky::GCARR_PARAM, 1, 10, 5));
  252. addParam(ParamWidget::create<RoundBlackKnob>(Vec(207, 284), module, MrBlueSky::G_PARAM, 1, 10, 5));
  253. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(37, 238), module, MrBlueSky::ATTACK_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0));
  254. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(119, 238), module, MrBlueSky::DECAY_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0));
  255. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(202, 238), module, MrBlueSky::CARRIER_Q_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0));
  256. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(284, 238), module, MrBlueSky::MODIFER_Q_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0));
  257. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(395, 238), module, MrBlueSky::SHIFT_BAND_OFFSET_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0));
  258. for (int i = 0; i < BANDS; i++) {
  259. addInput(Port::create<PJ301MPort>(Vec(56 + 33*i, 85), Port::INPUT, module, MrBlueSky::CARRIER_IN + i));
  260. }
  261. addInput(Port::create<PJ301MPort>(Vec(42, 330), Port::INPUT, module, MrBlueSky::IN_MOD));
  262. addInput(Port::create<PJ301MPort>(Vec(122, 330), Port::INPUT, module, MrBlueSky::IN_CARR));
  263. addInput(Port::create<PJ301MPort>(Vec(36, 209), Port::INPUT, module, MrBlueSky::ATTACK_INPUT));
  264. addInput(Port::create<PJ301MPort>(Vec(118, 209), Port::INPUT, module, MrBlueSky::DECAY_INPUT));
  265. addInput(Port::create<PJ301MPort>(Vec(201, 209), Port::INPUT, module, MrBlueSky::CARRIER_Q_INPUT));
  266. addInput(Port::create<PJ301MPort>(Vec(283, 209), Port::INPUT, module, MrBlueSky::MOD_Q_INPUT));
  267. addInput(Port::create<PJ301MPort>(Vec(362, 184), Port::INPUT, module, MrBlueSky::SHIFT_BAND_OFFSET_LEFT_INPUT));
  268. addInput(Port::create<PJ301MPort>(Vec(425, 184), Port::INPUT, module, MrBlueSky::SHIFT_BAND_OFFSET_RIGHT_INPUT));
  269. addInput(Port::create<PJ301MPort>(Vec(394, 209), Port::INPUT, module, MrBlueSky::SHIFT_BAND_OFFSET_INPUT));
  270. for (int i = 0; i < BANDS; i++) {
  271. addOutput(Port::create<PJ301MPort>(Vec(56 + 33*i, 45), Port::OUTPUT, module, MrBlueSky::MOD_OUT + i));
  272. }
  273. addOutput(Port::create<PJ301MPort>(Vec(210, 330), Port::OUTPUT, module, MrBlueSky::OUT));
  274. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH - 12, 0)));
  275. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, 0)));
  276. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH - 12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  277. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  278. }
  279. } // namespace rack_plugin_FrozenWasteland
  280. using namespace rack_plugin_FrozenWasteland;
  281. RACK_PLUGIN_MODEL_INIT(FrozenWasteland, MrBlueSky) {
  282. Model *modelMrBlueSky = Model::create<MrBlueSky, MrBlueSkyWidget>("Frozen Wasteland", "MrBlueSky", "Mr. Blue Sky", EFFECT_TAG);
  283. return modelMrBlueSky;
  284. }