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.

244 lines
9.5KB

  1. #include "LabSeven.hpp"
  2. #include "LabSeven_3340_VCO.h"
  3. #include <time.h>
  4. #include <fstream>
  5. using namespace std;
  6. //TODO:
  7. //fine tune vco parameters to match my own synth
  8. namespace rack_plugin_LabSeven {
  9. struct LS3340VCO : Module
  10. {
  11. enum ParamIds
  12. {
  13. PARAM_MOD,
  14. PARAM_RANGE,
  15. PARAM_PULSEWIDTH,
  16. PARAM_PWMSOURCE,
  17. PARAM_VOLSQUARE,
  18. PARAM_VOLSAW,
  19. PARAM_VOLTRIANGLE,
  20. PARAM_VOLSUBOSC,
  21. PARAM_SUBOSCRATIO,
  22. PARAM_VOLNOISE,
  23. NUM_PARAMS
  24. };
  25. enum InputIds
  26. {
  27. IN_PITCH,
  28. IN_MOD,
  29. IN_RANGE,
  30. IN_LFO,
  31. IN_ENV,
  32. IN_SUBOSCSELECT,
  33. NUM_INPUTS
  34. };
  35. enum OutputIds
  36. {
  37. OUT_SQUARE,
  38. OUT_SAW,
  39. OUT_SUB,
  40. OUT_TRIANGLE,
  41. OUT_MIX,
  42. OUT_NOISE,
  43. NUM_OUTPUTS
  44. };
  45. enum LightIds
  46. {
  47. NUM_LIGHTS
  48. };
  49. //VCO instance and VCO output frame
  50. LabSeven::LS3340::TLS3340VCO vco;
  51. LabSeven::LS3340::TLS3340VCOFrame nextFrame;
  52. LS3340VCO() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  53. void step() override;
  54. float sampleTimeCurrent = 0.0;
  55. float sampleRateCurrent = 0.0;
  56. double pitch,maxPitch,rangeFactor;
  57. // For more advanced Module features, read Rack's engine.hpp header file
  58. // - toJson, fromJson: serialization of internal data
  59. // - onSampleRateChange: event triggered by a change of sample rate
  60. // - onReset, onRandomize, onCreate, onDelete: implements special behavior when user clicks these from the context menu
  61. };
  62. void LS3340VCO::step()
  63. {
  64. //update external sample rate if neccessary
  65. if (sampleTimeCurrent != engineGetSampleTime())
  66. {
  67. sampleTimeCurrent = engineGetSampleTime();
  68. sampleRateCurrent = 1.0/sampleTimeCurrent;
  69. vco.setSamplerateExternal(sampleRateCurrent);
  70. maxPitch = sampleRateCurrent*0.45;
  71. if (maxPitch > 40000) maxPitch = 40000; //high value so that suboscillator can go up to 10kHz
  72. }
  73. //get pitch and pitch mod input
  74. pitch = inputs[IN_PITCH].value;
  75. pitch += pow(2,2.25*0.2*inputs[IN_MOD].value * params[PARAM_MOD].value);
  76. //set rangeFactor
  77. rangeFactor = params[PARAM_RANGE].value;
  78. switch ((int)rangeFactor)
  79. {
  80. case 0: rangeFactor = 0.5; break;
  81. case 1: rangeFactor = 1.0; break;
  82. case 2: rangeFactor = 2.0; break;
  83. case 3: rangeFactor = 4.0; break;
  84. default: rangeFactor = 1.0;
  85. }
  86. //range modulation
  87. if (inputs[IN_RANGE].active)
  88. {
  89. int rangeSelect = (int)round(inputs[IN_RANGE].value*0.6);
  90. switch (rangeSelect)
  91. {
  92. case -3: rangeFactor /= 8.0; break;
  93. case -2: rangeFactor /= 4.0; break;
  94. case -1: rangeFactor /= 2.0; break;
  95. case 0: break; //no change
  96. case 1: rangeFactor *= 2.0; break;
  97. case 2: rangeFactor *= 4.0; break;
  98. case 3: rangeFactor *= 8.0; break;
  99. }
  100. if (rangeFactor > 16.0) rangeFactor = 16.0;
  101. }
  102. //set pitch
  103. //TODO: Clean up this paragraph!!!
  104. pitch = 261.626f * pow(2.0, pitch) * rangeFactor;
  105. pitch = clamp(pitch, 0.01f, maxPitch);
  106. //simulate the jitter observed in the hardware synth
  107. //use values > 0.02 for dirtier sound
  108. pitch *= 1.0+0.02*((double) rand() / (RAND_MAX)-0.5);
  109. vco.setFrequency(pitch);
  110. //update suboscillator
  111. switch((int)inputs[IN_SUBOSCSELECT].value)
  112. {
  113. case 1: vco.setSuboscillatorMode(0); break;
  114. case 2: vco.setSuboscillatorMode(1); break;
  115. case 3: vco.setSuboscillatorMode(2); break;
  116. default: vco.setSuboscillatorMode((unsigned short)params[PARAM_SUBOSCRATIO].value);
  117. }
  118. //pulse width modulation
  119. switch ((int)params[PARAM_PWMSOURCE].value)
  120. {
  121. //LFO PWM requires values between -0.4 and 0.4; SH does PWM between 10% and 90% pulse width
  122. case 2: vco.setPwmCoefficient(-2.0*params[PARAM_PULSEWIDTH].value*0.4*0.2*inputs[IN_LFO].value); break; //bipolar, -5V - +5V
  123. case 1: vco.setPwmCoefficient(-0.8*params[PARAM_PULSEWIDTH].value); break;
  124. case 0: vco.setPwmCoefficient(-2.0*params[PARAM_PULSEWIDTH].value*0.4*0.1*inputs[IN_ENV].value); break; //unipolar, 0V - +10v
  125. default: vco.setPwmCoefficient(-0.8*params[PARAM_PULSEWIDTH].value);
  126. }
  127. //get next frame and put it out
  128. double scaling = 8.0;
  129. //TODO: PROPER (FREQUENCY DEPENDENT) AMPLITUDE SCALING FOR SAW AND TRIANGLE
  130. //TODO: PWM FOR TRIANGLE
  131. //calculate next frame
  132. if (this->sampleRateCurrent != 192000)
  133. {
  134. //TODO: Add a 'standard/high' quality switch to GUI
  135. //and choose interpolation method accordingly
  136. if (true)
  137. {
  138. vco.getNextFrameAtExternalSampleRateSinc(&nextFrame);
  139. }
  140. else
  141. {
  142. //currently next neighbour interpolation, not used!
  143. //TODO: Add quality switch (low/medium/high) to select
  144. //nn (has its own sound), cubic or sinc interpolation
  145. vco.getNextFrameAtExternalSampleRateCubic(&nextFrame);
  146. }
  147. }
  148. else //no interpolation required if internal sample rate == external sample rate == 192kHz
  149. {
  150. vco.getNextBlock(&nextFrame,1);
  151. }
  152. //TODO: Activate/deactivate interpolation if outs are not active
  153. outputs[OUT_SQUARE].value = scaling * nextFrame.square;
  154. outputs[OUT_SAW].value = scaling * nextFrame.sawtooth;
  155. outputs[OUT_SUB].value = scaling * nextFrame.subosc;
  156. outputs[OUT_TRIANGLE].value = scaling * nextFrame.triangle;
  157. outputs[OUT_NOISE].value = 6.0* nextFrame.noise;
  158. outputs[OUT_MIX].value = 0.4*(outputs[OUT_SQUARE].value * params[PARAM_VOLSQUARE].value +
  159. outputs[OUT_SAW].value * params[PARAM_VOLSAW].value +
  160. outputs[OUT_SUB].value * params[PARAM_VOLSUBOSC].value +
  161. outputs[OUT_TRIANGLE].value * params[PARAM_VOLTRIANGLE].value +
  162. outputs[OUT_NOISE].value * params[PARAM_VOLNOISE].value);
  163. }
  164. struct LS3340VCOWidget : ModuleWidget {
  165. LS3340VCOWidget(LS3340VCO *module) : ModuleWidget(module)
  166. {
  167. srand(time(0));
  168. //BACKGROUND
  169. setPanel(SVG::load(assetPlugin(plugin, "res/LabSeven_3340_VCO.svg")));
  170. //SCREWS
  171. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  172. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  173. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  174. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  175. //INPUTS
  176. addInput(Port::create<LabSeven_Port>(Vec(114, 380-24-231+2), Port::INPUT, module, LS3340VCO::IN_PITCH));
  177. addInput(Port::create<LabSeven_Port>(Vec( 75, 380-24-231+2), Port::INPUT, module, LS3340VCO::IN_MOD));
  178. addInput(Port::create<LabSeven_Port>(Vec(114, 380-24-276+2), Port::INPUT, module, LS3340VCO::IN_RANGE));
  179. addInput(Port::create<LabSeven_Port>(Vec(219, 380-24-284+2), Port::INPUT, module, LS3340VCO::IN_LFO));
  180. addInput(Port::create<LabSeven_Port>(Vec(219, 380-24-214+2), Port::INPUT, module, LS3340VCO::IN_ENV));
  181. addInput(Port::create<LabSeven_Port>(Vec(153, 380-24- 32+2), Port::INPUT, module, LS3340VCO::IN_SUBOSCSELECT));
  182. //VCO SECTION
  183. addParam(ParamWidget::create<LabSeven_3340_FaderRedLargeRed>(Vec(28-4, 380-24-272), module, LS3340VCO::PARAM_MOD, 0.0f, 1.0f, 0.0f));
  184. addParam(ParamWidget::create<LabSeven_3340_KnobLargeRange>(Vec(69, 380-36-266), module, LS3340VCO::PARAM_RANGE, 0.0, 3.0, 1.0));
  185. addParam(ParamWidget::create<LabSeven_3340_FaderRedLargeRed>(Vec(164-4, 380-24-272), module, LS3340VCO::PARAM_PULSEWIDTH, 0.0f, 0.5f, 0.0f));
  186. addParam(ParamWidget::create<LabSeven_3340_FaderRedLargeYellow3Stage>(Vec(201-4, 380-40-234), module, LS3340VCO::PARAM_PWMSOURCE, 0.0f, 2.0f, 1.0f));
  187. //SOURCE MIXER SECTION
  188. addParam(ParamWidget::create<LabSeven_3340_FaderRedLargeGreen>(Vec( 28-4, 380-20-4-125), module, LS3340VCO::PARAM_VOLSQUARE, 0.0f, 1.0f, 0.0f));
  189. addParam(ParamWidget::create<LabSeven_3340_FaderRedLargeGreen>(Vec( 59-4, 380-20-4-125), module, LS3340VCO::PARAM_VOLSAW, 0.0f, 1.0f, 0.0f));
  190. addParam(ParamWidget::create<LabSeven_3340_FaderRedLargeGreen>(Vec( 90-4, 380-20-4-125), module, LS3340VCO::PARAM_VOLTRIANGLE, 0.0f, 1.0f, 0.0f));
  191. addParam(ParamWidget::create<LabSeven_3340_FaderRedLargeGreen>(Vec(121-4, 380-20-4-125), module, LS3340VCO::PARAM_VOLSUBOSC, 0.0f, 1.0f, 0.0f));
  192. addParam(ParamWidget::create<LabSeven_3340_FaderRedLargeYellow3Stage>(Vec(158-4-1, 380-40-88), module, LS3340VCO::PARAM_SUBOSCRATIO, 2.0f, 0.0f, 2.0f));
  193. addParam(ParamWidget::create<LabSeven_3340_FaderRedLargeGreen>(Vec(213-4, 380-20-4-125), module, LS3340VCO::PARAM_VOLNOISE, 0.0f, 1.0f, 0.0f));
  194. //OUTPUTS
  195. addOutput(Port::create<LabSeven_Port>(Vec( 24, 380-30-24), Port::OUTPUT, module, LS3340VCO::OUT_SQUARE));
  196. addOutput(Port::create<LabSeven_Port>(Vec( 55, 380-30-24), Port::OUTPUT, module, LS3340VCO::OUT_SAW));
  197. addOutput(Port::create<LabSeven_Port>(Vec(117, 380-30-24), Port::OUTPUT, module, LS3340VCO::OUT_SUB));
  198. addOutput(Port::create<LabSeven_Port>(Vec( 86, 380-30-24), Port::OUTPUT, module, LS3340VCO::OUT_TRIANGLE));
  199. addOutput(Port::create<LabSeven_Port>(Vec(181, 380-30-24), Port::OUTPUT, module, LS3340VCO::OUT_MIX));
  200. addOutput(Port::create<LabSeven_Port>(Vec(208, 380-30-24), Port::OUTPUT, module, LS3340VCO::OUT_NOISE));
  201. }
  202. };
  203. } // namespace rack_plugin_LabSeven
  204. using namespace rack_plugin_LabSeven;
  205. RACK_PLUGIN_MODEL_INIT(LabSeven, LS3340VCO) {
  206. Model *modelLS3340VCO = Model::create<LS3340VCO, LS3340VCOWidget>("LabSeven", "LS3340VCO", "LS3340-VCO", OSCILLATOR_TAG);
  207. return modelLS3340VCO;
  208. }