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.

369 lines
15KB

  1. #include "XCO.hpp"
  2. #include "dsp/pitch.hpp"
  3. void XCO::onReset() {
  4. _syncTrigger.reset();
  5. _modulationStep = modulationSteps;
  6. }
  7. void XCO::onSampleRateChange() {
  8. setSampleRate(engineGetSampleRate());
  9. _modulationStep = modulationSteps;
  10. }
  11. void XCO::step() {
  12. lights[SLOW_LIGHT].value = _slowMode = params[SLOW_PARAM].value > 0.5f;
  13. _fmLinearMode = params[FM_TYPE_PARAM].value < 0.5f;
  14. if (!(
  15. outputs[MIX_OUTPUT].active ||
  16. outputs[SQUARE_OUTPUT].active ||
  17. outputs[SAW_OUTPUT].active ||
  18. outputs[TRIANGLE_OUTPUT].active ||
  19. outputs[SINE_OUTPUT].active
  20. )) {
  21. return;
  22. }
  23. ++_modulationStep;
  24. if (_modulationStep >= modulationSteps) {
  25. _modulationStep = 0;
  26. _baseVOct = params[FREQUENCY_PARAM].value;
  27. _baseVOct += params[FINE_PARAM].value / 12.0f;;
  28. if (inputs[PITCH_INPUT].active) {
  29. _baseVOct += clamp(inputs[PITCH_INPUT].value, -5.0f, 5.0f);
  30. }
  31. if (_slowMode) {
  32. _baseVOct -= 7.0f;
  33. }
  34. _baseHz = cvToFrequency(_baseVOct);
  35. float pw = params[SQUARE_PW_PARAM].value;
  36. if (inputs[SQUARE_PW_INPUT].active) {
  37. pw *= clamp(inputs[SQUARE_PW_INPUT].value / 5.0f, -1.0f, 1.0f);
  38. }
  39. pw *= 1.0f - 2.0f * _square.minPulseWidth;
  40. pw *= 0.5f;
  41. pw += 0.5f;
  42. _square.setPulseWidth(_squarePulseWidthSL.next(pw));
  43. float saturation = params[SAW_SATURATION_PARAM].value;
  44. if (inputs[SAW_SATURATION_INPUT].active) {
  45. saturation *= clamp(inputs[SAW_SATURATION_INPUT].value / 10.0f, 0.0f, 1.0f);
  46. }
  47. _saw.setSaturation(_sawSaturationSL.next(saturation) * 10.f);
  48. float tsw = params[TRIANGLE_SAMPLE_PARAM].value * Phasor::maxSampleWidth;
  49. if (inputs[TRIANGLE_SAMPLE_INPUT].active) {
  50. tsw *= clamp(inputs[TRIANGLE_SAMPLE_INPUT].value / 10.0f, 0.0f, 1.0f);
  51. }
  52. _triangleSampleWidth = _triangleSampleWidthSL.next(tsw);
  53. _triangle.setSampleWidth(_triangleSampleWidth);
  54. float sfb = params[SINE_FEEDBACK_PARAM].value;
  55. if (inputs[SINE_FEEDBACK_INPUT].active) {
  56. sfb *= clamp(inputs[SINE_FEEDBACK_INPUT].value / 10.0f, 0.0f, 1.0f);
  57. }
  58. _sineFeedback = _sineFeedbackSL.next(sfb);
  59. _fmDepth = params[FM_DEPTH_PARAM].value;
  60. if (inputs[FM_DEPTH_INPUT].active) {
  61. _fmDepth *= clamp(inputs[FM_DEPTH_INPUT].value / 10.0f, 0.0f, 1.0f);
  62. }
  63. _squarePhaseOffset = phaseOffset(params[SQUARE_PHASE_PARAM], inputs[SQUARE_PHASE_INPUT]);
  64. _sawPhaseOffset = phaseOffset(params[SAW_PHASE_PARAM], inputs[SAW_PHASE_INPUT]);
  65. _trianglePhaseOffset = phaseOffset(params[TRIANGLE_PHASE_PARAM], inputs[TRIANGLE_PHASE_INPUT]);
  66. _sinePhaseOffset = phaseOffset(params[SINE_PHASE_PARAM], inputs[SINE_PHASE_INPUT]);
  67. _squareMix = level(params[SQUARE_MIX_PARAM], inputs[SQUARE_MIX_INPUT]);
  68. _sawMix = level(params[SAW_MIX_PARAM], inputs[SAW_MIX_INPUT]);
  69. _triangleMix = level(params[TRIANGLE_MIX_PARAM], inputs[TRIANGLE_MIX_INPUT]);
  70. _sineMix = level(params[SINE_MIX_PARAM], inputs[SINE_MIX_INPUT]);
  71. }
  72. if (_syncTrigger.next(inputs[SYNC_INPUT].value)) {
  73. _phasor.resetPhase();
  74. }
  75. float frequency = _baseHz;
  76. Phasor::phase_delta_t phaseOffset = 0;
  77. float fmd = _fmDepthSL.next(_fmDepth);
  78. if (inputs[FM_INPUT].active && fmd > 0.01f) {
  79. float fm = inputs[FM_INPUT].value * fmd;
  80. if (_fmLinearMode) {
  81. phaseOffset = Phasor::radiansToPhase(2.0f * fm);
  82. }
  83. else {
  84. frequency = cvToFrequency(_baseVOct + fm);
  85. }
  86. }
  87. setFrequency(frequency);
  88. const float oversampleWidth = 100.0f;
  89. float mix, oMix;
  90. if (frequency > _oversampleThreshold) {
  91. if (frequency > _oversampleThreshold + oversampleWidth) {
  92. mix = 0.0f;
  93. oMix = 1.0f;
  94. }
  95. else {
  96. oMix = (frequency - _oversampleThreshold) / oversampleWidth;
  97. mix = 1.0f - oMix;
  98. }
  99. }
  100. else {
  101. mix = 1.0f;
  102. oMix = 0.0f;
  103. }
  104. bool triangleSample = _triangleSampleWidth > 0.001f;
  105. bool squareActive = outputs[MIX_OUTPUT].active || outputs[SQUARE_OUTPUT].active;
  106. bool sawActive = outputs[MIX_OUTPUT].active || outputs[SAW_OUTPUT].active;
  107. bool triangleActive = outputs[MIX_OUTPUT].active || outputs[TRIANGLE_OUTPUT].active;
  108. bool sineActive = outputs[MIX_OUTPUT].active || outputs[SINE_OUTPUT].active;
  109. bool squareOversample = squareActive && oMix > 0.0f;
  110. bool sawOversample = sawActive && oMix > 0.0f;
  111. bool triangleOversample = triangleActive && (triangleSample || oMix > 0.0f);
  112. bool squareNormal = squareActive && mix > 0.0f;
  113. bool sawNormal = sawActive && mix > 0.0f;
  114. bool triangleNormal = triangleActive && !triangleSample && mix > 0.0f;
  115. float squareOut = 0.0f;
  116. float sawOut = 0.0f;
  117. float triangleOut = 0.0f;
  118. float sineOut = 0.0f;
  119. Phasor::phase_delta_t sineFeedbackOffset = 0;
  120. if (sineActive) {
  121. if (_sineFeedback > 0.001f) {
  122. sineFeedbackOffset = Phasor::radiansToPhase(_sineFeedback * _sineFeedbackDelayedSample);
  123. if (_sineOMix < 1.0f) {
  124. _sineOMix += sineOversampleMixIncrement;
  125. }
  126. }
  127. else if (_sineOMix > 0.0f) {
  128. _sineOMix -= sineOversampleMixIncrement;
  129. }
  130. }
  131. if (squareOversample || sawOversample || triangleOversample || _sineOMix > 0.0f) {
  132. for (int i = 0; i < oversample; ++i) {
  133. _phasor.advancePhase();
  134. if (squareOversample) {
  135. _squareBuffer[i] = _square.nextFromPhasor(_phasor, _squarePhaseOffset + phaseOffset);
  136. }
  137. if (sawOversample) {
  138. _sawBuffer[i] = _saw.nextFromPhasor(_phasor, _sawPhaseOffset + phaseOffset);
  139. }
  140. if (triangleOversample) {
  141. _triangleBuffer[i] = _triangle.nextFromPhasor(_phasor, _trianglePhaseOffset + phaseOffset);
  142. }
  143. if (_sineOMix > 0.0f) {
  144. _sineBuffer[i] = _sine.nextFromPhasor(_phasor, sineFeedbackOffset + _sinePhaseOffset + phaseOffset);
  145. }
  146. }
  147. if (squareOversample) {
  148. squareOut += oMix * amplitude * _squareDecimator.next(_squareBuffer);
  149. }
  150. if (sawOversample) {
  151. sawOut += oMix * amplitude * _sawDecimator.next(_sawBuffer);
  152. }
  153. if (triangleOversample) {
  154. triangleOut += amplitude * _triangleDecimator.next(_triangleBuffer);
  155. if (!triangleSample) {
  156. triangleOut *= oMix;
  157. }
  158. }
  159. if (_sineOMix > 0.0f) {
  160. sineOut += amplitude * _sineOMix * _sineDecimator.next(_sineBuffer);
  161. }
  162. }
  163. else {
  164. _phasor.advancePhase(oversample);
  165. }
  166. if (squareNormal) {
  167. squareOut += mix * amplitude * _square.nextFromPhasor(_phasor, _squarePhaseOffset + phaseOffset);
  168. }
  169. if (sawNormal) {
  170. sawOut += mix * amplitude * _saw.nextFromPhasor(_phasor, _sawPhaseOffset + phaseOffset);
  171. }
  172. if (triangleNormal) {
  173. triangleOut += mix * amplitude * _triangle.nextFromPhasor(_phasor, _trianglePhaseOffset + phaseOffset);
  174. }
  175. if (_sineOMix < 1.0f) {
  176. sineOut += amplitude * (1.0f - _sineOMix) * _sine.nextFromPhasor(_phasor, sineFeedbackOffset + _sinePhaseOffset + phaseOffset);
  177. }
  178. outputs[SQUARE_OUTPUT].value = squareOut;
  179. outputs[SAW_OUTPUT].value = sawOut;
  180. outputs[TRIANGLE_OUTPUT].value = triangleOut;
  181. outputs[SINE_OUTPUT].value = _sineFeedbackDelayedSample = sineOut;
  182. if (outputs[MIX_OUTPUT].active) {
  183. outputs[MIX_OUTPUT].value = _squareMixSL.next(_squareMix) * squareOut + _sawMixSL.next(_sawMix) * sawOut + _triangleMixSL.next(_triangleMix) * triangleOut + _sineMixSL.next(_sineMix) * sineOut;
  184. }
  185. }
  186. Phasor::phase_delta_t XCO::phaseOffset(Param& param, Input& input) {
  187. float v = param.value;
  188. if (input.active) {
  189. v *= clamp(input.value / 5.0f, -1.0f, 1.0f);
  190. }
  191. return -v * Phasor::maxPhase / 2.0f;
  192. }
  193. float XCO::level(Param& param, Input& input) {
  194. float v = param.value;
  195. if (input.active) {
  196. v *= clamp(input.value / 10.0f, 0.0f, 1.0f);
  197. }
  198. return v;
  199. }
  200. void XCO::setSampleRate(float sampleRate) {
  201. _oversampleThreshold = 0.06f * sampleRate;
  202. _phasor.setSampleRate(sampleRate);
  203. _square.setSampleRate(sampleRate);
  204. _saw.setSampleRate(sampleRate);
  205. _squareDecimator.setParams(sampleRate, oversample);
  206. _sawDecimator.setParams(sampleRate, oversample);
  207. _triangleDecimator.setParams(sampleRate, oversample);
  208. _sineDecimator.setParams(sampleRate, oversample);
  209. _fmDepthSL.setParams(sampleRate, 5.0f, 1.0f);
  210. _squarePulseWidthSL.setParams(sampleRate, 0.1f, 2.0f);
  211. _sawSaturationSL.setParams(sampleRate, 1.0f, 1.0f);
  212. _triangleSampleWidthSL.setParams(sampleRate, 0.1f, 1.0f);
  213. _sineFeedbackSL.setParams(sampleRate, 0.1f, 1.0f);
  214. _squareMixSL.setParams(sampleRate, 5.0f, 1.0f);
  215. _sawMixSL.setParams(sampleRate, 5.0f, 1.0f);
  216. _triangleMixSL.setParams(sampleRate, 5.0f, 1.0f);
  217. _sineMixSL.setParams(sampleRate, 5.0f, 1.0f);
  218. }
  219. void XCO::setFrequency(float frequency) {
  220. if (_frequency != frequency && frequency < 0.475f * _phasor._sampleRate) {
  221. _frequency = frequency;
  222. _phasor.setFrequency(_frequency / (float)oversample);
  223. _square.setFrequency(_frequency);
  224. _saw.setFrequency(_frequency);
  225. }
  226. }
  227. struct XCOWidget : ModuleWidget {
  228. static constexpr int hp = 20;
  229. XCOWidget(XCO* module) : ModuleWidget(module) {
  230. box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
  231. {
  232. SVGPanel *panel = new SVGPanel();
  233. panel->box.size = box.size;
  234. panel->setBackground(SVG::load(assetPlugin(plugin, "res/XCO.svg")));
  235. addChild(panel);
  236. }
  237. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  238. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 0)));
  239. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  240. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 365)));
  241. // generated by svg_widgets.rb
  242. auto frequencyParamPosition = Vec(40.0, 45.0);
  243. auto fineParamPosition = Vec(47.0, 153.0);
  244. auto slowParamPosition = Vec(121.0, 157.2);
  245. auto fmParamPosition = Vec(55.0, 194.0);
  246. auto fmTypeParamPosition = Vec(101.5, 256.5);
  247. auto squarePwParamPosition = Vec(147.0, 60.0);
  248. auto squarePhaseParamPosition = Vec(147.0, 148.0);
  249. auto squareMixParamPosition = Vec(147.0, 237.0);
  250. auto sawSaturationParamPosition = Vec(187.0, 60.0);
  251. auto sawPhaseParamPosition = Vec(187.0, 148.0);
  252. auto sawMixParamPosition = Vec(187.0, 237.0);
  253. auto triangleSampleParamPosition = Vec(227.0, 60.0);
  254. auto trianglePhaseParamPosition = Vec(227.0, 148.0);
  255. auto triangleMixParamPosition = Vec(227.0, 237.0);
  256. auto sineFeedbackParamPosition = Vec(267.0, 60.0);
  257. auto sinePhaseParamPosition = Vec(267.0, 148.0);
  258. auto sineMixParamPosition = Vec(267.0, 237.0);
  259. auto fmInputPosition = Vec(29.0, 251.0);
  260. auto fmDepthInputPosition = Vec(62.0, 251.0);
  261. auto squarePwInputPosition = Vec(143.0, 95.0);
  262. auto squarePhaseInputPosition = Vec(143.0, 183.0);
  263. auto squareMixInputPosition = Vec(143.0, 272.0);
  264. auto sawSaturationInputPosition = Vec(183.0, 95.0);
  265. auto sawPhaseInputPosition = Vec(183.0, 183.0);
  266. auto sawMixInputPosition = Vec(183.0, 272.0);
  267. auto triangleSampleInputPosition = Vec(223.0, 95.0);
  268. auto trianglePhaseInputPosition = Vec(223.0, 183.0);
  269. auto triangleMixInputPosition = Vec(223.0, 272.0);
  270. auto sineFeedbackInputPosition = Vec(263.0, 95.0);
  271. auto sinePhaseInputPosition = Vec(263.0, 183.0);
  272. auto sineMixInputPosition = Vec(263.0, 272.0);
  273. auto pitchInputPosition = Vec(17.0, 318.0);
  274. auto syncInputPosition = Vec(50.0, 318.0);
  275. auto squareOutputPosition = Vec(143.0, 318.0);
  276. auto sawOutputPosition = Vec(183.0, 318.0);
  277. auto triangleOutputPosition = Vec(223.0, 318.0);
  278. auto sineOutputPosition = Vec(263.0, 318.0);
  279. auto mixOutputPosition = Vec(103.0, 318.0);
  280. auto slowLightPosition = Vec(81.0, 158.5);
  281. // end generated by svg_widgets.rb
  282. addParam(ParamWidget::create<Knob68>(frequencyParamPosition, module, XCO::FREQUENCY_PARAM, -3.0, 6.0, 0.0));
  283. addParam(ParamWidget::create<Knob16>(fineParamPosition, module, XCO::FINE_PARAM, -1.0, 1.0, 0.0));
  284. addParam(ParamWidget::create<StatefulButton9>(slowParamPosition, module, XCO::SLOW_PARAM, 0.0, 1.0, 0.0));
  285. addParam(ParamWidget::create<Knob38>(fmParamPosition, module, XCO::FM_DEPTH_PARAM, 0.0, 1.0, 0.0));
  286. addParam(ParamWidget::create<SliderSwitch2State14>(fmTypeParamPosition, module, XCO::FM_TYPE_PARAM, 0.0, 1.0, 1.0));
  287. addParam(ParamWidget::create<Knob16>(squarePwParamPosition, module, XCO::SQUARE_PW_PARAM, -0.97, 0.97, 0.0));
  288. addParam(ParamWidget::create<Knob16>(squarePhaseParamPosition, module, XCO::SQUARE_PHASE_PARAM, -1.0, 1.0, 0.0));
  289. addParam(ParamWidget::create<Knob16>(squareMixParamPosition, module, XCO::SQUARE_MIX_PARAM, 0.0, 1.0, 1.0));
  290. addParam(ParamWidget::create<Knob16>(sawSaturationParamPosition, module, XCO::SAW_SATURATION_PARAM, 0.0, 1.0, 0.0));
  291. addParam(ParamWidget::create<Knob16>(sawPhaseParamPosition, module, XCO::SAW_PHASE_PARAM, -1.0, 1.0, 0.0));
  292. addParam(ParamWidget::create<Knob16>(sawMixParamPosition, module, XCO::SAW_MIX_PARAM, 0.0, 1.0, 1.0));
  293. addParam(ParamWidget::create<Knob16>(triangleSampleParamPosition, module, XCO::TRIANGLE_SAMPLE_PARAM, 0.0, 1.0, 0.0));
  294. addParam(ParamWidget::create<Knob16>(trianglePhaseParamPosition, module, XCO::TRIANGLE_PHASE_PARAM, -1.0, 1.0, 0.0));
  295. addParam(ParamWidget::create<Knob16>(triangleMixParamPosition, module, XCO::TRIANGLE_MIX_PARAM, 0.0, 1.0, 1.0));
  296. addParam(ParamWidget::create<Knob16>(sineFeedbackParamPosition, module, XCO::SINE_FEEDBACK_PARAM, 0.0, 1.0, 0.0));
  297. addParam(ParamWidget::create<Knob16>(sinePhaseParamPosition, module, XCO::SINE_PHASE_PARAM, -1.0, 1.0, 0.0));
  298. addParam(ParamWidget::create<Knob16>(sineMixParamPosition, module, XCO::SINE_MIX_PARAM, 0.0, 1.0, 1.0));
  299. addInput(Port::create<Port24>(fmInputPosition, Port::INPUT, module, XCO::FM_INPUT));
  300. addInput(Port::create<Port24>(fmDepthInputPosition, Port::INPUT, module, XCO::FM_DEPTH_INPUT));
  301. addInput(Port::create<Port24>(squarePwInputPosition, Port::INPUT, module, XCO::SQUARE_PW_INPUT));
  302. addInput(Port::create<Port24>(squarePhaseInputPosition, Port::INPUT, module, XCO::SQUARE_PHASE_INPUT));
  303. addInput(Port::create<Port24>(squareMixInputPosition, Port::INPUT, module, XCO::SQUARE_MIX_INPUT));
  304. addInput(Port::create<Port24>(sawSaturationInputPosition, Port::INPUT, module, XCO::SAW_SATURATION_INPUT));
  305. addInput(Port::create<Port24>(sawPhaseInputPosition, Port::INPUT, module, XCO::SAW_PHASE_INPUT));
  306. addInput(Port::create<Port24>(sawMixInputPosition, Port::INPUT, module, XCO::SAW_MIX_INPUT));
  307. addInput(Port::create<Port24>(triangleSampleInputPosition, Port::INPUT, module, XCO::TRIANGLE_SAMPLE_INPUT));
  308. addInput(Port::create<Port24>(trianglePhaseInputPosition, Port::INPUT, module, XCO::TRIANGLE_PHASE_INPUT));
  309. addInput(Port::create<Port24>(triangleMixInputPosition, Port::INPUT, module, XCO::TRIANGLE_MIX_INPUT));
  310. addInput(Port::create<Port24>(sineFeedbackInputPosition, Port::INPUT, module, XCO::SINE_FEEDBACK_INPUT));
  311. addInput(Port::create<Port24>(sinePhaseInputPosition, Port::INPUT, module, XCO::SINE_PHASE_INPUT));
  312. addInput(Port::create<Port24>(sineMixInputPosition, Port::INPUT, module, XCO::SINE_MIX_INPUT));
  313. addInput(Port::create<Port24>(pitchInputPosition, Port::INPUT, module, XCO::PITCH_INPUT));
  314. addInput(Port::create<Port24>(syncInputPosition, Port::INPUT, module, XCO::SYNC_INPUT));
  315. addOutput(Port::create<Port24>(squareOutputPosition, Port::OUTPUT, module, XCO::SQUARE_OUTPUT));
  316. addOutput(Port::create<Port24>(sawOutputPosition, Port::OUTPUT, module, XCO::SAW_OUTPUT));
  317. addOutput(Port::create<Port24>(triangleOutputPosition, Port::OUTPUT, module, XCO::TRIANGLE_OUTPUT));
  318. addOutput(Port::create<Port24>(sineOutputPosition, Port::OUTPUT, module, XCO::SINE_OUTPUT));
  319. addOutput(Port::create<Port24>(mixOutputPosition, Port::OUTPUT, module, XCO::MIX_OUTPUT));
  320. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(slowLightPosition, module, XCO::SLOW_LIGHT));
  321. }
  322. };
  323. RACK_PLUGIN_MODEL_INIT(Bogaudio, XCO) {
  324. Model *modelXCO = createModel<XCO, XCOWidget>("Bogaudio-XCO", "XCO", "oscillator with wave mixer", OSCILLATOR_TAG);
  325. return modelXCO;
  326. }