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.

445 lines
14KB

  1. ////////////////////////////////////////////////
  2. ///
  3. // Dual digital oscillator
  4. // based on Fundamental OSC by Andrew Belt
  5. // still some fix to do;)
  6. //
  7. //
  8. //////////////////////////////////
  9. #include "dBiz.hpp"
  10. #include "dsp/decimator.hpp"
  11. #include "dsp/filter.hpp"
  12. //// Based on Fundamental OSC
  13. namespace rack_plugin_dBiz {
  14. extern float sawTable[2048];
  15. extern float triTable[2048];
  16. template <int OVERSAMPLE, int QUALITY>
  17. struct VoltageControlledOscillator {
  18. bool analog = false;
  19. bool soft = false;
  20. float lastSyncValue = 0.0;
  21. float phase = 0.0;
  22. float freq;
  23. float pw = 0.5;
  24. float pitch;
  25. bool syncEnabled = false;
  26. bool syncDirection = false;
  27. Decimator<OVERSAMPLE, QUALITY> sinDecimator;
  28. Decimator<OVERSAMPLE, QUALITY> triDecimator;
  29. Decimator<OVERSAMPLE, QUALITY> sawDecimator;
  30. Decimator<OVERSAMPLE, QUALITY> sqrDecimator;
  31. RCFilter sqrFilter;
  32. // For analog detuning effect
  33. float pitchSlew = 0.0;
  34. int pitchSlewIndex = 0;
  35. float sinBuffer[OVERSAMPLE] = {};
  36. float triBuffer[OVERSAMPLE] = {};
  37. float sawBuffer[OVERSAMPLE] = {};
  38. float sqrBuffer[OVERSAMPLE] = {};
  39. void setPitch(float pitchKnob, float pitchCv) {
  40. // Compute frequency
  41. pitch = pitchKnob;
  42. if (analog) {
  43. // Apply pitch slew
  44. const float pitchSlewAmount = 2.0;
  45. pitch += pitchSlew * pitchSlewAmount;
  46. }
  47. else {
  48. // Quantize coarse knob if digital mode
  49. pitch = roundf(pitch);
  50. }
  51. pitch += pitchCv;
  52. // Note C3
  53. freq = 261.626 * powf(2.0, pitch / 12.0);
  54. }
  55. void setPulseWidth(float pulseWidth) {
  56. const float pwMin = 0.01;
  57. pw = clamp(pulseWidth, pwMin, 1.0 - pwMin);
  58. }
  59. void process(float deltaTime, float syncValue) {
  60. if (analog) {
  61. // Adjust pitch slew
  62. if (++pitchSlewIndex > 32) {
  63. const float pitchSlewTau = 100.0; // Time constant for leaky integrator in seconds
  64. pitchSlew += (randomNormal() - pitchSlew / pitchSlewTau) / engineGetSampleRate();
  65. pitchSlewIndex = 0;
  66. }
  67. }
  68. // Advance phase
  69. float deltaPhase = clamp(freq * deltaTime, 1e-6, 0.5);
  70. // Detect sync
  71. int syncIndex = -1; // Index in the oversample loop where sync occurs [0, OVERSAMPLE)
  72. float syncCrossing = 0.0; // Offset that sync occurs [0.0, 1.0)
  73. if (syncEnabled) {
  74. syncValue -= 0.01;
  75. if (syncValue > 0.0 && lastSyncValue <= 0.0) {
  76. float deltaSync = syncValue - lastSyncValue;
  77. syncCrossing = 1.0 - syncValue / deltaSync;
  78. syncCrossing *= OVERSAMPLE;
  79. syncIndex = (int)syncCrossing;
  80. syncCrossing -= syncIndex;
  81. }
  82. lastSyncValue = syncValue;
  83. }
  84. if (syncDirection)
  85. deltaPhase *= -1.0;
  86. sqrFilter.setCutoff(40.0 * deltaTime);
  87. for (int i = 0; i < OVERSAMPLE; i++) {
  88. if (syncIndex == i) {
  89. if (soft) {
  90. syncDirection = !syncDirection;
  91. deltaPhase *= -1.0;
  92. }
  93. else {
  94. // phase = syncCrossing * deltaPhase / OVERSAMPLE;
  95. phase = 0.0;
  96. }
  97. }
  98. if (analog) {
  99. // Quadratic approximation of sine, slightly richer harmonics
  100. if (phase < 0.5f)
  101. sinBuffer[i] = 1.f - 16.f * powf(phase - 0.25f, 2);
  102. else
  103. sinBuffer[i] = -1.f + 16.f * powf(phase - 0.75f, 2);
  104. sinBuffer[i] *= 1.08f;
  105. }
  106. else {
  107. sinBuffer[i] = sinf(2.f*M_PI * phase);
  108. }
  109. if (analog) {
  110. triBuffer[i] = 1.25f * interpolateLinear(triTable, phase * 2047.f);
  111. }
  112. else {
  113. if (phase < 0.25f)
  114. triBuffer[i] = 4.f * phase;
  115. else if (phase < 0.75f)
  116. triBuffer[i] = 2.f - 4.f * phase;
  117. else
  118. triBuffer[i] = -4.f + 4.f * phase;
  119. }
  120. if (analog) {
  121. sawBuffer[i] = 1.66f * interpolateLinear(sawTable, phase * 2047.f);
  122. }
  123. else {
  124. if (phase < 0.5f)
  125. sawBuffer[i] = 2.f * phase;
  126. else
  127. sawBuffer[i] = -2.f + 2.f * phase;
  128. }
  129. sqrBuffer[i] = (phase < pw) ? 1.f : -1.f;
  130. if (analog) {
  131. // Simply filter here
  132. sqrFilter.process(sqrBuffer[i]);
  133. sqrBuffer[i] = 0.71f * sqrFilter.highpass();
  134. }
  135. // Advance phase
  136. phase += deltaPhase / OVERSAMPLE;
  137. phase = eucmod(phase, 1.0);
  138. }
  139. }
  140. float sin() {
  141. return sinDecimator.process(sinBuffer);
  142. }
  143. float tri() {
  144. return triDecimator.process(triBuffer);
  145. }
  146. float saw() {
  147. return sawDecimator.process(sawBuffer);
  148. }
  149. float sqr() {
  150. return sqrDecimator.process(sqrBuffer);
  151. }
  152. float light() {
  153. return sinf(2*M_PI * phase);
  154. }
  155. };
  156. struct DVCO : Module {
  157. enum ParamIds {
  158. MODE_A_PARAM,
  159. MODE_B_PARAM,
  160. SYNC_A_PARAM,
  161. SYNC_B_PARAM,
  162. FREQ_A_PARAM,
  163. FREQ_B_PARAM,
  164. FINE_A_PARAM,
  165. FINE_B_PARAM,
  166. FM_A_PARAM,
  167. FM_B_PARAM,
  168. PW_A_PARAM,
  169. PW_B_PARAM,
  170. PWM_A_PARAM,
  171. PWM_B_PARAM,
  172. WAVE_A_PARAM,
  173. WAVE_B_PARAM,
  174. LFO_A_MODE_PARAM,
  175. LFO_B_MODE_PARAM,
  176. OSC_SYNC_PARAM,
  177. NUM_PARAMS
  178. };
  179. enum InputIds {
  180. PITCH_A_INPUT,
  181. PITCH_B_INPUT,
  182. FM_A_INPUT,
  183. FM_B_INPUT,
  184. SYNC_A_INPUT,
  185. SYNC_B_INPUT,
  186. PW_A_INPUT,
  187. PW_B_INPUT,
  188. WAVE_A_INPUT,
  189. WAVE_B_INPUT,
  190. CARRIER_INPUT,
  191. MODULATOR_INPUT,
  192. NUM_INPUTS
  193. };
  194. enum OutputIds {
  195. OSC_A_OUTPUT,
  196. OSC_AN_OUTPUT,
  197. OSC_B_OUTPUT,
  198. OSC_BN_OUTPUT,
  199. RING_OUTPUT,
  200. SUM_OUTPUT,
  201. NUM_OUTPUTS
  202. };
  203. enum LightIds {
  204. NUM_LIGHTS
  205. };
  206. VoltageControlledOscillator<16, 16> oscillator_a;
  207. VoltageControlledOscillator<16, 16> oscillator_b;
  208. DVCO() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  209. void step() override;
  210. };
  211. void DVCO::step() {
  212. oscillator_a.analog = params[MODE_A_PARAM].value > 0.0;
  213. oscillator_a.soft = params[SYNC_A_PARAM].value <= 0.0;
  214. oscillator_b.analog = params[MODE_B_PARAM].value > 0.0;
  215. oscillator_b.soft = params[SYNC_B_PARAM].value <= 0.0;
  216. float carrier = inputs[CARRIER_INPUT].value / 5.0;
  217. float modulator = inputs[MODULATOR_INPUT].value / 5.0;
  218. float pitchFine_a = 3.0 * quadraticBipolar(params[FINE_A_PARAM].value);
  219. float pitchCv_a = 12.0 * inputs[PITCH_A_INPUT].value;
  220. float pitchFine_b = 3.0 * quadraticBipolar(params[FINE_B_PARAM].value);
  221. float pitchCv_b = 12.0 * inputs[PITCH_B_INPUT].value;
  222. if (inputs[FM_A_INPUT].active) {
  223. pitchCv_a += quadraticBipolar(params[FM_A_PARAM].value) * 12.0 * inputs[FM_A_INPUT].value;
  224. }
  225. if (inputs[FM_B_INPUT].active) {
  226. pitchCv_b += quadraticBipolar(params[FM_B_PARAM].value) * 12.0 * inputs[FM_B_INPUT].value;
  227. }
  228. else
  229. pitchCv_b += quadraticBipolar(params[FM_B_PARAM].value) * 12.0 * outputs[OSC_A_OUTPUT].value;
  230. if(params[LFO_A_MODE_PARAM].value==0.0){
  231. oscillator_a.setPitch(params[FREQ_A_PARAM].value, pitchFine_a + pitchCv_a);
  232. oscillator_a.freq=oscillator_a.freq/100;
  233. }
  234. else
  235. oscillator_a.setPitch(params[FREQ_A_PARAM].value, pitchFine_a + pitchCv_a);
  236. oscillator_a.setPulseWidth(params[PW_A_PARAM].value + params[PWM_A_PARAM].value * inputs[PW_A_INPUT].value / 10.0);
  237. oscillator_a.syncEnabled = inputs[SYNC_A_INPUT].active;
  238. oscillator_a.process(1.0 / engineGetSampleRate(), inputs[SYNC_A_INPUT].value);
  239. if(params[LFO_B_MODE_PARAM].value==0.0){
  240. oscillator_b.setPitch(params[FREQ_B_PARAM].value, pitchFine_b + pitchCv_b);
  241. oscillator_b.freq=oscillator_b.freq/100;
  242. }
  243. else
  244. oscillator_b.setPitch(params[FREQ_B_PARAM].value, pitchFine_b + pitchCv_b);
  245. oscillator_b.setPulseWidth(params[PW_B_PARAM].value + params[PWM_B_PARAM].value * inputs[PW_B_INPUT].value / 10.0);
  246. if(params[OSC_SYNC_PARAM].value==0.0){
  247. oscillator_b.syncEnabled = true;
  248. oscillator_b.process(1.0 / engineGetSampleRate(), outputs[OSC_A_OUTPUT].value);
  249. }
  250. else {
  251. oscillator_b.syncEnabled = inputs[SYNC_B_INPUT].active;
  252. oscillator_b.process(1.0 / engineGetSampleRate(), inputs[SYNC_B_INPUT].value);
  253. }
  254. float wave_a = clamp(params[WAVE_A_PARAM].value + inputs[WAVE_A_INPUT].value, 0.0f, 3.0f);
  255. float out_a;
  256. if (wave_a < 1.0)
  257. out_a = crossfade(oscillator_a.sin(), oscillator_a.tri(), wave_a);
  258. else if (wave_a < 2.0)
  259. out_a = crossfade(oscillator_a.tri(), oscillator_a.saw(), wave_a - 1.0);
  260. else
  261. out_a = crossfade(oscillator_a.saw(), oscillator_a.sqr(), wave_a - 2.0);
  262. float wave_b = clamp(params[WAVE_B_PARAM].value + inputs[WAVE_B_INPUT].value, 0.0f, 3.0f);
  263. float out_b;
  264. if (wave_b < 1.0)
  265. out_b = crossfade(oscillator_b.sin(), oscillator_b.tri(), wave_b);
  266. else if (wave_b < 2.0)
  267. out_b = crossfade(oscillator_b.tri(), oscillator_b.saw(), wave_b - 1.0);
  268. else
  269. out_b = crossfade(oscillator_b.saw(), oscillator_b.sqr(), wave_b - 2.0);
  270. if (inputs[CARRIER_INPUT].active && inputs[MODULATOR_INPUT].value == 0.0)
  271. {
  272. outputs[RING_OUTPUT].value=5.0*carrier*out_b;
  273. outputs[SUM_OUTPUT].value = 5.0 * (carrier + out_b);
  274. }
  275. else if (inputs[MODULATOR_INPUT].active && inputs[CARRIER_INPUT].value == 0.0)
  276. {
  277. outputs[RING_OUTPUT].value = 5.0 * out_a * modulator;
  278. outputs[SUM_OUTPUT].value = 5.0 * (out_a + modulator);
  279. }
  280. else if (inputs[MODULATOR_INPUT].active && inputs[CARRIER_INPUT].active)
  281. {
  282. outputs[RING_OUTPUT].value=5.0*carrier*modulator;
  283. outputs[SUM_OUTPUT].value=5.0*(carrier+modulator);
  284. }
  285. else
  286. {
  287. outputs[RING_OUTPUT].value=5.0*out_a*out_b;
  288. outputs[SUM_OUTPUT].value = 2.5*(out_a + out_b);
  289. }
  290. outputs[OSC_AN_OUTPUT].value = -5.0 * out_a;
  291. outputs[OSC_BN_OUTPUT].value = -5.0 * out_b;
  292. outputs[OSC_A_OUTPUT].value = 5.0 * out_a;
  293. outputs[OSC_B_OUTPUT].value = 5.0 * out_b;
  294. }
  295. struct DVCOWidget : ModuleWidget
  296. {
  297. DVCOWidget(DVCO *module) : ModuleWidget(module)
  298. {
  299. box.size = Vec(15*13, 380);
  300. {
  301. SVGPanel *panel = new SVGPanel();
  302. panel->box.size = box.size;
  303. panel->setBackground(SVG::load(assetPlugin(plugin, "res/DVCO.svg")));
  304. addChild(panel);
  305. }
  306. float mid = (15*13)/2;
  307. int jacks = 27;
  308. int knobs = 38;
  309. int border=10;
  310. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  311. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 0)));
  312. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  313. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 365)));
  314. ///////////////////////////////////////port//////////////////////////////////////////////////
  315. addParam(ParamWidget::create<CKSS>(Vec(mid/2-10, 20), module, DVCO::LFO_A_MODE_PARAM, 0.0, 1.0, 1.0));
  316. addParam(ParamWidget::create<CKSS>(Vec(mid-8, 20), module, DVCO::OSC_SYNC_PARAM, 0.0, 1.0, 1.0));
  317. addParam(ParamWidget::create<CKSS>(Vec(mid+(mid/2-5), 20), module,DVCO::LFO_B_MODE_PARAM, 0.0, 1.0, 1.0));
  318. addParam(ParamWidget::create<CKSS>(Vec(border, 260), module, DVCO::MODE_A_PARAM, 0.0, 1.0, 1.0));
  319. addParam(ParamWidget::create<CKSS>(Vec(border+jacks, 260), module, DVCO::SYNC_A_PARAM, 0.0, 1.0, 1.0));
  320. addParam(ParamWidget::create<CKSS>(Vec(box.size.x - 26, 260), module, DVCO::MODE_B_PARAM, 0.0, 1.0, 1.0));
  321. addParam(ParamWidget::create<CKSS>(Vec(box.size.x - jacks - 26, 260), module, DVCO::SYNC_B_PARAM, 0.0, 1.0, 1.0));
  322. ///////////////////////////////////////params////////////////////////////////////////
  323. addParam(ParamWidget::create<LRoundWhy>(Vec(10, 50), module, DVCO::FREQ_A_PARAM, -54.0, 54.0, 0.0));
  324. addParam(ParamWidget::create<RoundWhy>(Vec(55, 40), module, DVCO::FINE_A_PARAM, -1.5, 1.5, 0.0));
  325. addParam(ParamWidget::create<LRoundWhy>(Vec(box.size.x -45-10, 50), module, DVCO::FREQ_B_PARAM, -54.0, 54.0, 0.0));
  326. addParam(ParamWidget::create<RoundWhy>(Vec(box.size.x-95, 40), module, DVCO::FINE_B_PARAM, -1.5, 1.5, 0.0));
  327. addParam(ParamWidget::create<RoundAzz>(Vec(15, 110), module, DVCO::PW_A_PARAM, 0.0, 1.0, 0.5));
  328. addParam(ParamWidget::create<RoundWhy>(Vec(15+knobs+5, 100), module, DVCO::FM_A_PARAM, 0.0, 1.0, 0.0));
  329. addParam(ParamWidget::create<RoundAzz>(Vec(5, 160), module, DVCO::PWM_A_PARAM, 0.0, 1.0, 0.0));
  330. addParam(ParamWidget::create<RoundAzz>(Vec(box.size.x - knobs-15, 110), module, DVCO::PW_B_PARAM, 0.0, 1.0, 0.5));
  331. addParam(ParamWidget::create<RoundWhy>(Vec(box.size.x - (knobs*2)-15-5, 100), module, DVCO::FM_B_PARAM, 0.0, 1.0, 0.0));
  332. addParam(ParamWidget::create<RoundAzz>(Vec(box.size.x - knobs-5, 160), module, DVCO::PWM_B_PARAM, 0.0, 1.0, 0.0));
  333. addParam(ParamWidget::create<RoundRed>(Vec(15+knobs, 150), module, DVCO::WAVE_A_PARAM, 0.0, 3.0, 1.5));
  334. addParam(ParamWidget::create<RoundRed>(Vec(box.size.x - (knobs * 2) - 15 , 150), module, DVCO::WAVE_B_PARAM, 0.0, 3.0, 1.5));
  335. ////////////////////////////////jacks//////////////////////////////////////////////////////////
  336. addInput(Port::create<PJ301MIPort>(Vec(border, 290), Port::INPUT, module, DVCO::PITCH_A_INPUT));
  337. addInput(Port::create<PJ301MCPort>(Vec(border+jacks, 290), Port::INPUT, module, DVCO::FM_A_INPUT));
  338. addInput(Port::create<PJ301MIPort>(Vec(border + jacks*2, 290), Port::INPUT, module, DVCO::WAVE_A_INPUT));
  339. addInput(Port::create<PJ301MIPort>(Vec(border+jacks, 330), Port::INPUT, module, DVCO::SYNC_A_INPUT));
  340. addInput(Port::create<PJ301MIPort>(Vec(border, 330), Port::INPUT, module, DVCO::PW_A_INPUT));
  341. addInput(Port::create<PJ301MIPort>(Vec(box.size.x-8-jacks, 290), Port::INPUT, module, DVCO::PITCH_B_INPUT));
  342. addInput(Port::create<PJ301MCPort>(Vec(box.size.x-8-jacks*2, 290), Port::INPUT, module, DVCO::FM_B_INPUT));
  343. addInput(Port::create<PJ301MIPort>(Vec(box.size.x-8-jacks*3, 290), Port::INPUT, module, DVCO::WAVE_B_INPUT));
  344. addInput(Port::create<PJ301MIPort>(Vec(box.size.x-(jacks*2)-8, 330), Port::INPUT, module, DVCO::SYNC_B_INPUT));
  345. addInput(Port::create<PJ301MIPort>(Vec(box.size.x-jacks-8, 330), Port::INPUT, module, DVCO::PW_B_INPUT));
  346. addInput(Port::create<PJ301MOrPort>(Vec(mid-jacks-15,245), Port::INPUT, module, DVCO::CARRIER_INPUT));
  347. addInput(Port::create<PJ301MOrPort>(Vec(mid+3+10, 245), Port::INPUT, module, DVCO::MODULATOR_INPUT));
  348. //////////////////////////////OUTPUTS////////////////////////////////////////////////////////////////
  349. addOutput(Port::create<PJ301MOPort>(Vec(border, 225), Port::OUTPUT, module, DVCO::OSC_A_OUTPUT));
  350. addOutput(Port::create<PJ301MOPort>(Vec(border+jacks, 225), Port::OUTPUT, module, DVCO::OSC_AN_OUTPUT));
  351. addOutput(Port::create<PJ301MOPort>(Vec(box.size.x-jacks-8 , 225), Port::OUTPUT, module, DVCO::OSC_B_OUTPUT));
  352. addOutput(Port::create<PJ301MOPort>(Vec(box.size.x-jacks*2-8 , 225), Port::OUTPUT, module, DVCO::OSC_BN_OUTPUT));
  353. addOutput(Port::create<PJ301MOPort>(Vec(mid-14, 255), Port::OUTPUT, module, DVCO::SUM_OUTPUT));
  354. addOutput(Port::create<PJ301MOPort>(Vec(mid-14, 225), Port::OUTPUT, module, DVCO::RING_OUTPUT));
  355. }
  356. };
  357. } // namespace rack_plugin_dBiz
  358. using namespace rack_plugin_dBiz;
  359. RACK_PLUGIN_MODEL_INIT(dBiz, DVCO) {
  360. Model *modelDVCO = Model::create<DVCO, DVCOWidget>("dBiz", "DVCO", "DVCO", OSCILLATOR_TAG);
  361. return modelDVCO;
  362. }