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.

296 lines
10.0KB

  1. #include "FMOp.hpp"
  2. #include "dsp/pitch.hpp"
  3. #define LINEAR_LEVEL "linearLevel"
  4. void FMOp::onReset() {
  5. _steps = modulationSteps;
  6. _envelope.reset();
  7. _gateTrigger.reset();
  8. }
  9. void FMOp::onSampleRateChange() {
  10. _steps = modulationSteps;
  11. float sampleRate = engineGetSampleRate();
  12. _envelope.setSampleRate(sampleRate);
  13. _phasor.setSampleRate(sampleRate);
  14. _decimator.setParams(sampleRate, oversample);
  15. _maxFrequency = 0.475f * sampleRate;
  16. _feedbackSL.setParams(sampleRate, 5.0f, 1.0f);
  17. _depthSL.setParams(sampleRate, 5.0f, 1.0f);
  18. _levelSL.setParams(sampleRate, 10.0f, 1.0f);
  19. _sustainSL.setParams(sampleRate, 1.0f, 1.0f);
  20. }
  21. json_t* FMOp::toJson() {
  22. json_t* root = json_object();
  23. json_object_set_new(root, LINEAR_LEVEL, json_boolean(_linearLevel));
  24. return root;
  25. }
  26. void FMOp::fromJson(json_t* root) {
  27. json_t* ll = json_object_get(root, LINEAR_LEVEL);
  28. if (ll) {
  29. _linearLevel = json_is_true(ll);
  30. }
  31. }
  32. void FMOp::step() {
  33. if (!outputs[AUDIO_OUTPUT].active) {
  34. lights[ENV_TO_LEVEL_LIGHT].value = params[ENV_TO_LEVEL_PARAM].value > 0.5f;
  35. lights[ENV_TO_FEEDBACK_LIGHT].value = params[ENV_TO_FEEDBACK_PARAM].value > 0.5f;
  36. lights[ENV_TO_DEPTH_LIGHT].value = params[ENV_TO_DEPTH_PARAM].value > 0.5f;
  37. return;
  38. }
  39. lights[ENV_TO_LEVEL_LIGHT].value = _levelEnvelopeOn;
  40. lights[ENV_TO_FEEDBACK_LIGHT].value = _feedbackEnvelopeOn;
  41. lights[ENV_TO_DEPTH_LIGHT].value = _depthEnvelopeOn;
  42. float pitchIn = 0.0f;
  43. if (inputs[PITCH_INPUT].active) {
  44. pitchIn = inputs[PITCH_INPUT].value;
  45. }
  46. float gateIn = 0.0f;
  47. if (inputs[GATE_INPUT].active) {
  48. gateIn = inputs[GATE_INPUT].value;
  49. }
  50. ++_steps;
  51. if (_steps >= modulationSteps) {
  52. _steps = 0;
  53. float ratio = params[RATIO_PARAM].value;
  54. if (ratio < 0.0f) {
  55. ratio = std::max(1.0f + ratio, 0.01f);
  56. }
  57. else {
  58. ratio *= 9.0f;
  59. ratio += 1.0f;
  60. }
  61. float frequency = pitchIn;
  62. frequency += params[FINE_PARAM].value / 12.0f;
  63. frequency = cvToFrequency(frequency);
  64. frequency *= ratio;
  65. frequency = clamp(frequency, -_maxFrequency, _maxFrequency);
  66. _phasor.setFrequency(frequency / (float)oversample);
  67. bool levelEnvelopeOn = params[ENV_TO_LEVEL_PARAM].value > 0.5f;
  68. bool feedbackEnvelopeOn = params[ENV_TO_FEEDBACK_PARAM].value > 0.5f;
  69. bool depthEnvelopeOn = params[ENV_TO_DEPTH_PARAM].value > 0.5f;
  70. if (_levelEnvelopeOn != levelEnvelopeOn || _feedbackEnvelopeOn != feedbackEnvelopeOn || _depthEnvelopeOn != depthEnvelopeOn) {
  71. _levelEnvelopeOn = levelEnvelopeOn;
  72. _feedbackEnvelopeOn = feedbackEnvelopeOn;
  73. _depthEnvelopeOn = depthEnvelopeOn;
  74. bool envelopeOn = _levelEnvelopeOn || _feedbackEnvelopeOn || _depthEnvelopeOn;
  75. if (envelopeOn && !_envelopeOn) {
  76. _envelope.reset();
  77. }
  78. _envelopeOn = envelopeOn;
  79. }
  80. if (_envelopeOn) {
  81. float sustain = params[SUSTAIN_PARAM].value;
  82. if (inputs[SUSTAIN_INPUT].active) {
  83. sustain *= clamp(inputs[SUSTAIN_INPUT].value / 10.0f, 0.0f, 1.0f);
  84. }
  85. _envelope.setAttack(powf(params[ATTACK_PARAM].value, 2.0f) * 10.f);
  86. _envelope.setDecay(powf(params[DECAY_PARAM].value, 2.0f) * 10.f);
  87. _envelope.setSustain(_sustainSL.next(sustain));
  88. _envelope.setRelease(powf(params[RELEASE_PARAM].value, 2.0f) * 10.f);
  89. }
  90. _feedback = params[FEEDBACK_PARAM].value;
  91. if (inputs[FEEDBACK_INPUT].active) {
  92. _feedback *= clamp(inputs[FEEDBACK_INPUT].value / 10.0f, 0.0f, 1.0f);
  93. }
  94. _depth = params[DEPTH_PARAM].value;
  95. if (inputs[DEPTH_INPUT].active) {
  96. _depth *= clamp(inputs[DEPTH_INPUT].value / 10.0f, 0.0f, 1.0f);
  97. }
  98. _level = params[LEVEL_PARAM].value;
  99. if (inputs[LEVEL_INPUT].active) {
  100. _level *= clamp(inputs[LEVEL_INPUT].value / 10.0f, 0.0f, 1.0f);
  101. }
  102. }
  103. float envelope = 0.0f;
  104. if (_envelopeOn) {
  105. _gateTrigger.process(gateIn);
  106. _envelope.setGate(_gateTrigger.isHigh());
  107. envelope = _envelope.next();
  108. }
  109. float feedback = _feedbackSL.next(_feedback);
  110. if (_feedbackEnvelopeOn) {
  111. feedback *= envelope;
  112. }
  113. bool feedbackOn = feedback > 0.001f;
  114. float out = _levelSL.next(_level);
  115. if (_levelEnvelopeOn) {
  116. out *= envelope;
  117. }
  118. float offset = 0.0f;
  119. if (feedbackOn) {
  120. offset = feedback * _feedbackDelayedSample;
  121. }
  122. if (inputs[FM_INPUT].active) {
  123. float depth = _depthSL.next(_depth);
  124. if (_depthEnvelopeOn) {
  125. depth *= envelope;
  126. }
  127. offset += inputs[FM_INPUT].value * depth * 2.0f;
  128. }
  129. float sample = 0.0f;
  130. if (out > 0.0001f) {
  131. Phasor::phase_delta_t o = Phasor::radiansToPhase(offset);
  132. if (feedbackOn) {
  133. if (_oversampleMix < 1.0f) {
  134. _oversampleMix += oversampleMixIncrement;
  135. }
  136. }
  137. else if (_oversampleMix > 0.0f) {
  138. _oversampleMix -= oversampleMixIncrement;
  139. }
  140. if (_oversampleMix > 0.0f) {
  141. for (int i = 0; i < oversample; ++i) {
  142. _phasor.advancePhase();
  143. _buffer[i] = _sineTable.nextFromPhasor(_phasor, o);
  144. }
  145. sample = _oversampleMix * _decimator.next(_buffer);
  146. }
  147. else {
  148. _phasor.advancePhase(oversample);
  149. }
  150. if (_oversampleMix < 1.0f) {
  151. sample += (1.0f - _oversampleMix) * _sineTable.nextFromPhasor(_phasor, o);
  152. }
  153. if (_linearLevel) {
  154. sample *= out;
  155. }
  156. else {
  157. out = (1.0f - out) * Amplifier::minDecibels;
  158. _amplifier.setLevel(out);
  159. sample = _amplifier.next(sample);
  160. }
  161. }
  162. else {
  163. _phasor.advancePhase(oversample);
  164. }
  165. outputs[AUDIO_OUTPUT].value = _feedbackDelayedSample = amplitude * sample;
  166. }
  167. struct LinearLevelMenuItem : MenuItem {
  168. FMOp* _module;
  169. LinearLevelMenuItem(FMOp* module, const char* label)
  170. : _module(module)
  171. {
  172. this->text = label;
  173. }
  174. void onAction(EventAction &e) override {
  175. _module->_linearLevel = !_module->_linearLevel;
  176. }
  177. void step() override {
  178. rightText = _module->_linearLevel ? "✔" : "";
  179. }
  180. };
  181. struct FMOpWidget : ModuleWidget {
  182. static constexpr int hp = 10;
  183. FMOpWidget(FMOp* module) : ModuleWidget(module) {
  184. box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
  185. {
  186. SVGPanel *panel = new SVGPanel();
  187. panel->box.size = box.size;
  188. panel->setBackground(SVG::load(assetPlugin(plugin, "res/FMOp.svg")));
  189. addChild(panel);
  190. }
  191. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  192. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 0)));
  193. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  194. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 365)));
  195. // generated by svg_widgets.rb
  196. auto ratioParamPosition = Vec(30.0, 45.0);
  197. auto fineParamPosition = Vec(112.0, 57.0);
  198. auto attackParamPosition = Vec(107.0, 94.0);
  199. auto decayParamPosition = Vec(107.0, 139.0);
  200. auto sustainParamPosition = Vec(107.0, 184.0);
  201. auto releaseParamPosition = Vec(107.0, 229.0);
  202. auto depthParamPosition = Vec(36.0, 106.0);
  203. auto envToDepthParamPosition = Vec(59.0, 139.7);
  204. auto feedbackParamPosition = Vec(36.0, 162.0);
  205. auto envToFeedbackParamPosition = Vec(59.0, 195.7);
  206. auto levelParamPosition = Vec(36.0, 218.0);
  207. auto envToLevelParamPosition = Vec(59.0, 251.7);
  208. auto depthInputPosition = Vec(15.0, 274.0);
  209. auto feedbackInputPosition = Vec(47.0, 274.0);
  210. auto levelInputPosition = Vec(79.0, 274.0);
  211. auto sustainInputPosition = Vec(111.0, 274.0);
  212. auto pitchInputPosition = Vec(15.0, 318.0);
  213. auto fmInputPosition = Vec(47.0, 318.0);
  214. auto gateInputPosition = Vec(79.0, 318.0);
  215. auto audioOutputPosition = Vec(111.0, 318.0);
  216. auto envToDepthLightPosition = Vec(30.0, 141.0);
  217. auto envToFeedbackLightPosition = Vec(30.0, 197.0);
  218. auto envToLevelLightPosition = Vec(30.0, 253.0);
  219. // end generated by svg_widgets.rb
  220. addParam(ParamWidget::create<Knob38>(ratioParamPosition, module, FMOp::RATIO_PARAM, -1.0, 1.0, 0.0));
  221. addParam(ParamWidget::create<Knob16>(fineParamPosition, module, FMOp::FINE_PARAM, -1.0, 1.0, 0.0));
  222. addParam(ParamWidget::create<Knob26>(attackParamPosition, module, FMOp::ATTACK_PARAM, 0.0, 1.0, 0.12));
  223. addParam(ParamWidget::create<Knob26>(decayParamPosition, module, FMOp::DECAY_PARAM, 0.0, 1.0, 0.31623));
  224. addParam(ParamWidget::create<Knob26>(sustainParamPosition, module, FMOp::SUSTAIN_PARAM, 0.0, 1.0, 1.0));
  225. addParam(ParamWidget::create<Knob26>(releaseParamPosition, module, FMOp::RELEASE_PARAM, 0.0, 1.0, 0.31623));
  226. addParam(ParamWidget::create<Knob26>(depthParamPosition, module, FMOp::DEPTH_PARAM, 0.0, 1.0, 0.0));
  227. addParam(ParamWidget::create<Knob26>(feedbackParamPosition, module, FMOp::FEEDBACK_PARAM, 0.0, 1.0, 0.0));
  228. addParam(ParamWidget::create<Knob26>(levelParamPosition, module, FMOp::LEVEL_PARAM, 0.0, 1.0, 1.0));
  229. addParam(ParamWidget::create<StatefulButton9>(envToLevelParamPosition, module, FMOp::ENV_TO_LEVEL_PARAM, 0.0, 1.0, 0.0));
  230. addParam(ParamWidget::create<StatefulButton9>(envToFeedbackParamPosition, module, FMOp::ENV_TO_FEEDBACK_PARAM, 0.0, 1.0, 0.0));
  231. addParam(ParamWidget::create<StatefulButton9>(envToDepthParamPosition, module, FMOp::ENV_TO_DEPTH_PARAM, 0.0, 1.0, 0.0));
  232. addInput(Port::create<Port24>(sustainInputPosition, Port::INPUT, module, FMOp::SUSTAIN_INPUT));
  233. addInput(Port::create<Port24>(depthInputPosition, Port::INPUT, module, FMOp::DEPTH_INPUT));
  234. addInput(Port::create<Port24>(feedbackInputPosition, Port::INPUT, module, FMOp::FEEDBACK_INPUT));
  235. addInput(Port::create<Port24>(levelInputPosition, Port::INPUT, module, FMOp::LEVEL_INPUT));
  236. addInput(Port::create<Port24>(pitchInputPosition, Port::INPUT, module, FMOp::PITCH_INPUT));
  237. addInput(Port::create<Port24>(gateInputPosition, Port::INPUT, module, FMOp::GATE_INPUT));
  238. addInput(Port::create<Port24>(fmInputPosition, Port::INPUT, module, FMOp::FM_INPUT));
  239. addOutput(Port::create<Port24>(audioOutputPosition, Port::OUTPUT, module, FMOp::AUDIO_OUTPUT));
  240. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(envToLevelLightPosition, module, FMOp::ENV_TO_LEVEL_LIGHT));
  241. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(envToFeedbackLightPosition, module, FMOp::ENV_TO_FEEDBACK_LIGHT));
  242. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(envToDepthLightPosition, module, FMOp::ENV_TO_DEPTH_LIGHT));
  243. }
  244. void appendContextMenu(Menu* menu) override {
  245. FMOp* fmop = dynamic_cast<FMOp*>(module);
  246. assert(fmop);
  247. menu->addChild(new MenuLabel());
  248. menu->addChild(new LinearLevelMenuItem(fmop, "Linear level response"));
  249. }
  250. };
  251. RACK_PLUGIN_MODEL_INIT(Bogaudio, FMOp) {
  252. Model *modelFMOp = createModel<FMOp, FMOpWidget>("Bogaudio-FMOp", "FM-OP", "FM oscillator", OSCILLATOR_TAG, SYNTH_VOICE_TAG);
  253. return modelFMOp;
  254. }