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.

457 lines
16KB

  1. #include "SubmarineFree.hpp"
  2. #include "dsp/functions.hpp"
  3. namespace rack_plugin_SubmarineFree {
  4. struct PO_Util {
  5. static constexpr float deg0 = 0.0f;
  6. static constexpr float deg30 = M_PI / 6.0f;
  7. static constexpr float deg45 = M_PI / 4.0f;
  8. static constexpr float deg60 = M_PI / 3.0f;
  9. static constexpr float deg90 = M_PI / 2.0f;
  10. static constexpr float deg120 = 2.0f * M_PI / 3.0f;
  11. static constexpr float deg135 = 3.0f * M_PI / 4.0f;
  12. static constexpr float deg150 = 5.0f * M_PI / 6.0f;
  13. static constexpr float ph0 = 0.0f;
  14. static constexpr float ph30 = 1.0f / 12.0f;
  15. static constexpr float ph45 = 0.125f;
  16. static constexpr float ph60 = 1.0f / 6.0f;
  17. static constexpr float ph90 = 0.25f;
  18. static constexpr float ph120 = 1.0f / 3.0f;
  19. static constexpr float ph135 = 0.375f;
  20. static constexpr float ph150 = 5.0f / 12.0f;
  21. static constexpr float ph180 = 0.5f;
  22. static constexpr float ph210 = 7.0f / 12.0f;
  23. static constexpr float ph225 = 0.625;
  24. static constexpr float ph240 = 2.0f / 3.0f;
  25. static constexpr float ph270 = 0.75f;
  26. static constexpr float ph300 = 5.0f / 6.0f;
  27. static constexpr float ph315 = 0.875f;
  28. static constexpr float ph330 = 11.0f / 12.0f;
  29. float sin(float phase);
  30. float tri(float phase);
  31. float saw(float phase);
  32. float sqr(float phase);
  33. float rsn(float phase);
  34. };
  35. float PO_Util::sin(float phase) {
  36. return 5.0f * sinf(phase);
  37. }
  38. float PO_Util::tri(float phase) {
  39. phase -= floor(phase);
  40. if (phase < 0.25f)
  41. return 20.0f * phase;
  42. if (phase < 0.75f)
  43. return 20.0f * (0.5f - phase);
  44. return 20.0f * (phase - 1.0f);
  45. }
  46. float PO_Util::saw(float phase) {
  47. phase -= floor(phase);
  48. if (phase < 0.5f)
  49. return 10.0f * phase;
  50. return 10.0f * (phase - 1.0f);
  51. }
  52. float PO_Util::sqr(float phase) {
  53. phase -= floor(phase);
  54. return (phase < 0.5f)?5.0f:-5.0f;
  55. }
  56. float PO_Util::rsn(float phase) {
  57. return 10.0f * fabs(sinf(phase)) - 5.0f;
  58. }
  59. struct PO_101 : Module, PO_Util {
  60. enum ParamIds {
  61. PARAM_TUNE,
  62. PARAM_FINE,
  63. PARAM_WAVE,
  64. PARAM_PHASE_1,
  65. PARAM_PHASE_2,
  66. PARAM_PHASE_3,
  67. PARAM_PHASE_4,
  68. NUM_PARAMS
  69. };
  70. enum InputIds {
  71. INPUT_NOTE_CV,
  72. INPUT_PHASE_1,
  73. INPUT_PHASE_2,
  74. INPUT_PHASE_3,
  75. INPUT_PHASE_4,
  76. NUM_INPUTS
  77. };
  78. enum OutputIds {
  79. OUTPUT_1,
  80. OUTPUT_2,
  81. OUTPUT_3,
  82. OUTPUT_4,
  83. OUTPUT_5,
  84. OUTPUT_6,
  85. OUTPUT_7,
  86. OUTPUT_8,
  87. OUTPUT_9,
  88. OUTPUT_10,
  89. OUTPUT_11,
  90. OUTPUT_12,
  91. OUTPUT_13,
  92. OUTPUT_14,
  93. OUTPUT_15,
  94. OUTPUT_16,
  95. OUTPUT_17,
  96. OUTPUT_18,
  97. OUTPUT_19,
  98. OUTPUT_20,
  99. NUM_OUTPUTS
  100. };
  101. enum LightIds {
  102. NUM_LIGHTS
  103. };
  104. PO_101() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  105. void step() override;
  106. void sin(float phase);
  107. void tri(float phase);
  108. void saw(float phase);
  109. void sqr(float phase);
  110. void rsn(float phase);
  111. float phase = 0.0f;
  112. float baseFreq = 261.626f;
  113. };
  114. void PO_101::sin(float phase) {
  115. phase *= (2 * M_PI);
  116. outputs[OUTPUT_9].value = -(outputs[OUTPUT_1].value = PO_Util::sin(phase + deg0));
  117. outputs[OUTPUT_10].value = -(outputs[OUTPUT_2].value = PO_Util::sin(phase + deg30));
  118. outputs[OUTPUT_11].value = -(outputs[OUTPUT_3].value = PO_Util::sin(phase + deg45));
  119. outputs[OUTPUT_12].value = -(outputs[OUTPUT_4].value = PO_Util::sin(phase + deg60));
  120. outputs[OUTPUT_13].value = -(outputs[OUTPUT_5].value = PO_Util::sin(phase + deg90));
  121. outputs[OUTPUT_14].value = -(outputs[OUTPUT_6].value = PO_Util::sin(phase + deg120));
  122. outputs[OUTPUT_15].value = -(outputs[OUTPUT_7].value = PO_Util::sin(phase + deg135));
  123. outputs[OUTPUT_16].value = -(outputs[OUTPUT_8].value = PO_Util::sin(phase + deg150));
  124. for (int i = 0; i < 4; i++) {
  125. if (outputs[OUTPUT_17 + i].active) {
  126. float offset = params[PARAM_PHASE_1 + i].value;
  127. if (inputs[INPUT_PHASE_1 + i].active)
  128. offset += inputs[INPUT_PHASE_1 + i].value * 0.4f;
  129. offset *= 2 * M_PI;
  130. outputs[OUTPUT_17 + i].value = PO_Util::sin(phase + offset);
  131. }
  132. }
  133. }
  134. void PO_101::tri(float phase) {
  135. outputs[OUTPUT_9].value = -(outputs[OUTPUT_1].value = PO_Util::tri(phase + ph0));
  136. outputs[OUTPUT_10].value = -(outputs[OUTPUT_2].value = PO_Util::tri(phase + ph30));
  137. outputs[OUTPUT_11].value = -(outputs[OUTPUT_3].value = PO_Util::tri(phase + ph45));
  138. outputs[OUTPUT_12].value = -(outputs[OUTPUT_4].value = PO_Util::tri(phase + ph60));
  139. outputs[OUTPUT_13].value = -(outputs[OUTPUT_5].value = PO_Util::tri(phase + ph90));
  140. outputs[OUTPUT_14].value = -(outputs[OUTPUT_6].value = PO_Util::tri(phase + ph120));
  141. outputs[OUTPUT_15].value = -(outputs[OUTPUT_7].value = PO_Util::tri(phase + ph135));
  142. outputs[OUTPUT_16].value = -(outputs[OUTPUT_8].value = PO_Util::tri(phase + ph150));
  143. for (int i = 0; i < 4; i++) {
  144. if (outputs[OUTPUT_17 + i].active) {
  145. float offset = params[PARAM_PHASE_1 + i].value;
  146. if (inputs[INPUT_PHASE_1 + i].active)
  147. offset += inputs[INPUT_PHASE_1 + i].value * 0.4f;
  148. outputs[OUTPUT_17 + i].value = PO_Util::tri(phase + offset);
  149. }
  150. }
  151. }
  152. void PO_101::saw(float phase) {
  153. outputs[OUTPUT_1].value = PO_Util::saw(phase + ph0);
  154. outputs[OUTPUT_2].value = PO_Util::saw(phase + ph30);
  155. outputs[OUTPUT_3].value = PO_Util::saw(phase + ph45);
  156. outputs[OUTPUT_4].value = PO_Util::saw(phase + ph60);
  157. outputs[OUTPUT_5].value = PO_Util::saw(phase + ph90);
  158. outputs[OUTPUT_6].value = PO_Util::saw(phase + ph120);
  159. outputs[OUTPUT_7].value = PO_Util::saw(phase + ph135);
  160. outputs[OUTPUT_8].value = PO_Util::saw(phase + ph150);
  161. outputs[OUTPUT_9].value = PO_Util::saw(phase + ph180);
  162. outputs[OUTPUT_10].value = PO_Util::saw(phase + ph210);
  163. outputs[OUTPUT_11].value = PO_Util::saw(phase + ph225);
  164. outputs[OUTPUT_12].value = PO_Util::saw(phase + ph240);
  165. outputs[OUTPUT_13].value = PO_Util::saw(phase + ph270);
  166. outputs[OUTPUT_14].value = PO_Util::saw(phase + ph300);
  167. outputs[OUTPUT_15].value = PO_Util::saw(phase + ph315);
  168. outputs[OUTPUT_16].value = PO_Util::saw(phase + ph330);
  169. for (int i = 0; i < 4; i++) {
  170. if (outputs[OUTPUT_17 + i].active) {
  171. float offset = params[PARAM_PHASE_1 + i].value;
  172. if (inputs[INPUT_PHASE_1 + i].active)
  173. offset += inputs[INPUT_PHASE_1 + i].value * 0.4f;
  174. outputs[OUTPUT_17 + i].value = PO_Util::saw(phase + offset);
  175. }
  176. }
  177. }
  178. void PO_101::sqr(float phase) {
  179. outputs[OUTPUT_9].value = -(outputs[OUTPUT_1].value = PO_Util::sqr(phase + ph0));
  180. outputs[OUTPUT_10].value = -(outputs[OUTPUT_2].value = PO_Util::sqr(phase + ph30));
  181. outputs[OUTPUT_11].value = -(outputs[OUTPUT_3].value = PO_Util::sqr(phase + ph45));
  182. outputs[OUTPUT_12].value = -(outputs[OUTPUT_4].value = PO_Util::sqr(phase + ph60));
  183. outputs[OUTPUT_13].value = -(outputs[OUTPUT_5].value = PO_Util::sqr(phase + ph90));
  184. outputs[OUTPUT_14].value = -(outputs[OUTPUT_6].value = PO_Util::sqr(phase + ph120));
  185. outputs[OUTPUT_15].value = -(outputs[OUTPUT_7].value = PO_Util::sqr(phase + ph135));
  186. outputs[OUTPUT_16].value = -(outputs[OUTPUT_8].value = PO_Util::sqr(phase + ph150));
  187. for (int i = 0; i < 4; i++) {
  188. if (outputs[OUTPUT_17 + i].active) {
  189. float offset = params[PARAM_PHASE_1 + i].value;
  190. if (inputs[INPUT_PHASE_1 + i].active)
  191. offset += inputs[INPUT_PHASE_1 + i].value * 0.4f;
  192. outputs[OUTPUT_17 + i].value = PO_Util::sqr(phase + offset);
  193. }
  194. }
  195. }
  196. void PO_101::rsn(float phase) {
  197. phase *= (2 * M_PI);
  198. outputs[OUTPUT_9].value = (outputs[OUTPUT_1].value = PO_Util::rsn(phase + deg0));
  199. outputs[OUTPUT_10].value = (outputs[OUTPUT_2].value = PO_Util::rsn(phase + deg30));
  200. outputs[OUTPUT_11].value = (outputs[OUTPUT_3].value = PO_Util::rsn(phase + deg45));
  201. outputs[OUTPUT_12].value = (outputs[OUTPUT_4].value = PO_Util::rsn(phase + deg60));
  202. outputs[OUTPUT_13].value = (outputs[OUTPUT_5].value = PO_Util::rsn(phase + deg90));
  203. outputs[OUTPUT_14].value = (outputs[OUTPUT_6].value = PO_Util::rsn(phase + deg120));
  204. outputs[OUTPUT_15].value = (outputs[OUTPUT_7].value = PO_Util::rsn(phase + deg135));
  205. outputs[OUTPUT_16].value = (outputs[OUTPUT_8].value = PO_Util::rsn(phase + deg150));
  206. for (int i = 0; i < 4; i++) {
  207. if (outputs[OUTPUT_17 + i].active) {
  208. float offset = params[PARAM_PHASE_1 + i].value;
  209. if (inputs[INPUT_PHASE_1 + i].active)
  210. offset += inputs[INPUT_PHASE_1 + i].value * 0.4f;
  211. offset *= 2 * M_PI;
  212. outputs[OUTPUT_17 + i].value = PO_Util::rsn(phase + offset);
  213. }
  214. }
  215. }
  216. void PO_101::step() {
  217. float freq = baseFreq * powf(2.0f, (params[PARAM_TUNE].value + 3.0f * quadraticBipolar(params[PARAM_FINE].value)) / 12.0f + (inputs[INPUT_NOTE_CV].active?inputs[INPUT_NOTE_CV].value:0.0f));
  218. float deltaTime = freq / engineGetSampleRate();
  219. phase += deltaTime;
  220. double intPart;
  221. phase = modf(phase, &intPart);
  222. {
  223. float waveShape = clamp(params[PARAM_WAVE].value, 0.0f, 4.0f);
  224. if (waveShape < 0.5f)
  225. sin(phase);
  226. else if (waveShape < 1.5f)
  227. tri(phase);
  228. else if (waveShape < 2.5f)
  229. saw(phase);
  230. else if (waveShape < 3.5f)
  231. sqr(phase);
  232. else
  233. rsn(phase);
  234. }
  235. }
  236. struct PO_204 : Module, PO_Util {
  237. enum ParamIds {
  238. PARAM_TUNE,
  239. PARAM_FINE,
  240. PARAM_WAVE_1,
  241. PARAM_WAVE_2,
  242. PARAM_WAVE_3,
  243. PARAM_WAVE_4,
  244. PARAM_PHASE_1,
  245. PARAM_PHASE_2,
  246. PARAM_PHASE_3,
  247. PARAM_PHASE_4,
  248. PARAM_MULT_1,
  249. PARAM_MULT_2,
  250. PARAM_MULT_3,
  251. PARAM_MULT_4,
  252. NUM_PARAMS
  253. };
  254. enum InputIds {
  255. INPUT_TUNE,
  256. INPUT_WAVE_1,
  257. INPUT_WAVE_2,
  258. INPUT_WAVE_3,
  259. INPUT_WAVE_4,
  260. INPUT_PHASE_1,
  261. INPUT_PHASE_2,
  262. INPUT_PHASE_3,
  263. INPUT_PHASE_4,
  264. NUM_INPUTS
  265. };
  266. enum OutputIds {
  267. OUTPUT_1,
  268. OUTPUT_2,
  269. OUTPUT_3,
  270. OUTPUT_4,
  271. NUM_OUTPUTS
  272. };
  273. enum LightIds {
  274. NUM_LIGHTS
  275. };
  276. PO_204() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  277. void step() override;
  278. float phase = 0.0f;
  279. float baseFreq = 261.626f;
  280. };
  281. void PO_204::step() {
  282. float freq = baseFreq * powf(2.0f, (params[PARAM_TUNE].value + 3.0f * quadraticBipolar(params[PARAM_FINE].value)) / 12.0f + (inputs[INPUT_TUNE].active?inputs[INPUT_TUNE].value:0.0f));
  283. float deltaTime = freq / engineGetSampleRate();
  284. phase += deltaTime;
  285. double intPart;
  286. phase = modf(phase, &intPart);
  287. for (int i = 0; i < 4; i++) {
  288. if (outputs[OUTPUT_1 + i].active) {
  289. float offset = phase + params[PARAM_PHASE_1 + i].value;
  290. if (inputs[INPUT_PHASE_1 + i].active)
  291. offset += inputs[INPUT_PHASE_1 + i].value * 0.4f;
  292. offset *= params[PARAM_MULT_1 + i].value;
  293. float wave = params[PARAM_WAVE_1 + i].value + (inputs[INPUT_WAVE_1 + i].active?inputs[INPUT_WAVE_1 + i].value:0.0f);
  294. double waveSection;
  295. wave = modf(clamp(wave, 0.0f, 10.0f), &waveSection);
  296. float w1 = 0.0f;
  297. float w2 = 0.0f;
  298. switch ((int)waveSection) {
  299. case 0:
  300. w1 = PO_Util::sin(offset * 2 * M_PI);
  301. w2 = PO_Util::saw(offset);
  302. break;
  303. case 1:
  304. w1 = PO_Util::saw(offset);
  305. w2 = PO_Util::rsn(offset * 2 * M_PI);
  306. break;
  307. case 2:
  308. w1 = PO_Util::rsn(offset * 2 * M_PI);
  309. w2 = PO_Util::tri(offset);
  310. break;
  311. case 3:
  312. w1 = PO_Util::tri(offset);
  313. w2 = PO_Util::sqr(offset);
  314. break;
  315. case 4:
  316. w1 = PO_Util::sqr(offset);
  317. w2 = PO_Util::sin(offset * 2 * M_PI);
  318. break;
  319. case 5:
  320. w1 = PO_Util::sin(offset * 2 * M_PI);
  321. w2 = PO_Util::tri(offset);
  322. break;
  323. case 6:
  324. w1 = PO_Util::tri(offset);
  325. w2 = PO_Util::saw(offset);
  326. break;
  327. case 7:
  328. w1 = PO_Util::saw(offset);
  329. w2 = PO_Util::sqr(offset);
  330. break;
  331. case 8:
  332. w1 = PO_Util::sqr(offset);
  333. w2 = PO_Util::rsn(offset * 2 * M_PI);
  334. break;
  335. case 9:
  336. w1 = PO_Util::rsn(offset * 2 * M_PI);
  337. w2 = PO_Util::sin(offset * 2 * M_PI);
  338. break;
  339. default:
  340. w2 = w1 = PO_Util::sin(offset * 2 * M_PI);
  341. break;
  342. }
  343. outputs[OUTPUT_1 + i].value = w1 * (1.0f - wave) + w2 * wave;
  344. }
  345. }
  346. }
  347. struct PO_Layout : ModuleWidget {
  348. PO_Layout(PO_101 *module) : ModuleWidget(module) {}
  349. void Layout() {
  350. addParam(ParamWidget::create<sub_knob_med>(Vec(66, 39), module, PO_101::PARAM_FINE, -1.0f, +1.0f, 0.0f));
  351. addParam(ParamWidget::create<sub_knob_med_snap_narrow>(Vec(121, 39), module, PO_101::PARAM_WAVE, 0.0f, +4.0f, 0.0f));
  352. addInput(Port::create<sub_port>(Vec(45,19), Port::INPUT, module, PO_101::INPUT_NOTE_CV));
  353. addOutput(Port::create<sub_port>(Vec(77.5,100), Port::OUTPUT, module, PO_101::OUTPUT_1));
  354. addOutput(Port::create<sub_port>(Vec(110,109), Port::OUTPUT, module, PO_101::OUTPUT_2));
  355. addOutput(Port::create<sub_port>(Vec(142.5,100), Port::OUTPUT, module, PO_101::OUTPUT_3));
  356. addOutput(Port::create<sub_port>(Vec(133.5,132.5), Port::OUTPUT, module, PO_101::OUTPUT_4));
  357. addOutput(Port::create<sub_port>(Vec(142.5,165), Port::OUTPUT, module, PO_101::OUTPUT_5));
  358. addOutput(Port::create<sub_port>(Vec(133.5,197.5), Port::OUTPUT, module, PO_101::OUTPUT_6));
  359. addOutput(Port::create<sub_port>(Vec(142.5,230), Port::OUTPUT, module, PO_101::OUTPUT_7));
  360. addOutput(Port::create<sub_port>(Vec(110,221), Port::OUTPUT, module, PO_101::OUTPUT_8));
  361. addOutput(Port::create<sub_port>(Vec(77.5,230), Port::OUTPUT, module, PO_101::OUTPUT_9));
  362. addOutput(Port::create<sub_port>(Vec(45,221), Port::OUTPUT, module, PO_101::OUTPUT_10));
  363. addOutput(Port::create<sub_port>(Vec(12.5,230), Port::OUTPUT, module, PO_101::OUTPUT_11));
  364. addOutput(Port::create<sub_port>(Vec(21.5,197.5), Port::OUTPUT, module, PO_101::OUTPUT_12));
  365. addOutput(Port::create<sub_port>(Vec(12.5,165), Port::OUTPUT, module, PO_101::OUTPUT_13));
  366. addOutput(Port::create<sub_port>(Vec(21.5,132.5), Port::OUTPUT, module, PO_101::OUTPUT_14));
  367. addOutput(Port::create<sub_port>(Vec(12.5,100), Port::OUTPUT, module, PO_101::OUTPUT_15));
  368. addOutput(Port::create<sub_port>(Vec(45,109), Port::OUTPUT, module, PO_101::OUTPUT_16));
  369. for (int i = 0; i < 4; i++) {
  370. addInput(Port::create<sub_port>(Vec(10 + 45 * i,260), Port::INPUT, module, PO_101::INPUT_PHASE_1 + i));
  371. addParam(ParamWidget::create<sub_knob_med>(Vec(3.5 + 45 * i, 290), module, PO_101::PARAM_PHASE_1 + i, -1.0f, +1.0f, 0.0f));
  372. addOutput(Port::create<sub_port>(Vec(10 + 45 * i,333), Port::OUTPUT, module, PO_101::OUTPUT_17 + i));
  373. }
  374. }
  375. };
  376. struct PO101 : PO_Layout {
  377. PO101(PO_101 *module) : PO_Layout(module) {
  378. setPanel(SVG::load(assetPlugin(plugin, "res/PO-101.svg")));
  379. addParam(ParamWidget::create<sub_knob_med>(Vec(11, 39), module, PO_101::PARAM_TUNE, -54.0f, +54.0f, 0.0f));
  380. Layout();
  381. }
  382. };
  383. struct PO102 : PO_Layout {
  384. PO102(PO_101 *module) : PO_Layout(module) {
  385. setPanel(SVG::load(assetPlugin(plugin, "res/PO-102.svg")));
  386. addParam(ParamWidget::create<sub_knob_med>(Vec(11, 39), module, PO_101::PARAM_TUNE, -96.0f, 72.0f, -12.0f));
  387. module->baseFreq = 1.0f;
  388. Layout();
  389. }
  390. };
  391. struct PO204 : ModuleWidget {
  392. PO204(PO_204 *module) : ModuleWidget(module) {
  393. setPanel(SVG::load(assetPlugin(plugin, "res/PO-204.svg")));
  394. addParam(ParamWidget::create<sub_knob_med>(Vec(60, 19), module, PO_204::PARAM_TUNE, -54.0f, +54.0f, 0.0f));
  395. addParam(ParamWidget::create<sub_knob_med>(Vec(105, 19), module, PO_204::PARAM_FINE, -1.0f, +1.0f, 0.0f));
  396. addInput(Port::create<sub_port>(Vec(17.5, 25.5), Port::INPUT, module, PO_204::INPUT_TUNE));
  397. for (int i = 0; i < 4; i++) {
  398. addParam(ParamWidget::create<sub_knob_small>(Vec(5, 89 + 70 * i), module, PO_204::PARAM_WAVE_1 + i, 0.0f, 10.0f, 5.0f));
  399. addParam(ParamWidget::create<sub_knob_small>(Vec(45, 89 + 70 * i), module, PO_204::PARAM_PHASE_1 + i, -1.0f, +1.0f, 0.0f));
  400. addParam(ParamWidget::create<sub_knob_small_snap>(Vec(85, 89 + 70 * i), module, PO_204::PARAM_MULT_1 + i, 1.0f, 16.0f, 1.0f));
  401. addInput(Port::create<sub_port>(Vec(4.5, 125 + 70 * i), Port::INPUT, module, PO_204::INPUT_WAVE_1 + i));
  402. addInput(Port::create<sub_port>(Vec(44.5, 125 + 70 * i), Port::INPUT, module, PO_204::INPUT_PHASE_1 + i));
  403. addOutput(Port::create<sub_port>(Vec(120.5, 125 + 70 * i), Port::OUTPUT, module, PO_204::OUTPUT_1 + i));
  404. }
  405. }
  406. };
  407. } // namespace rack_plugin_SubmarineFree
  408. using namespace rack_plugin_SubmarineFree;
  409. RACK_PLUGIN_MODEL_INIT(SubmarineFree, PO101) {
  410. Model *modelPO101 = Model::create<PO_101, PO101>("SubmarineFree", "PO-101", "PO-101 Phased VCO", OSCILLATOR_TAG, MULTIPLE_TAG, DIGITAL_TAG);
  411. return modelPO101;
  412. }
  413. RACK_PLUGIN_MODEL_INIT(SubmarineFree, PO102) {
  414. Model *modelPO102 = Model::create<PO_101, PO102>("SubmarineFree", "PO-102", "PO-102 Phased LFO", OSCILLATOR_TAG, MULTIPLE_TAG, DIGITAL_TAG);
  415. return modelPO102;
  416. }
  417. RACK_PLUGIN_MODEL_INIT(SubmarineFree, PO204) {
  418. Model *modelPO204 = Model::create<PO_204, PO204>("SubmarineFree", "PO-204", "PO-204 Phase Modulation Engine", OSCILLATOR_TAG, QUAD_TAG, DIGITAL_TAG);
  419. return modelPO204;
  420. }