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.

280 lines
6.8KB

  1. #pragma once
  2. #include "MinBLEPVCO.h"
  3. #include "ObjectCache.h"
  4. #include "dsp/functions.hpp" // rack math
  5. /**
  6. *
  7. */
  8. template <class TBase>
  9. class EV3 : public TBase
  10. {
  11. public:
  12. friend class TestMB;
  13. EV3(struct Module * module) : TBase(module)
  14. {
  15. init();
  16. }
  17. EV3() : TBase()
  18. {
  19. init();
  20. }
  21. enum class Waves
  22. {
  23. SIN,
  24. TRI,
  25. SAW,
  26. SQUARE,
  27. EVEN,
  28. NONE,
  29. END // just a marker
  30. };
  31. enum ParamIds
  32. {
  33. MIX1_PARAM,
  34. MIX2_PARAM,
  35. MIX3_PARAM,
  36. OCTAVE1_PARAM,
  37. SEMI1_PARAM,
  38. FINE1_PARAM,
  39. FM1_PARAM,
  40. SYNC1_PARAM,
  41. WAVE1_PARAM,
  42. PW1_PARAM,
  43. PWM1_PARAM,
  44. OCTAVE2_PARAM,
  45. SEMI2_PARAM,
  46. FINE2_PARAM,
  47. FM2_PARAM,
  48. SYNC2_PARAM,
  49. WAVE2_PARAM,
  50. PW2_PARAM,
  51. PWM2_PARAM,
  52. OCTAVE3_PARAM,
  53. SEMI3_PARAM,
  54. FINE3_PARAM,
  55. FM3_PARAM,
  56. SYNC3_PARAM,
  57. WAVE3_PARAM,
  58. PW3_PARAM,
  59. PWM3_PARAM,
  60. NUM_PARAMS
  61. };
  62. enum InputIds
  63. {
  64. CV1_INPUT,
  65. CV2_INPUT,
  66. CV3_INPUT,
  67. FM1_INPUT,
  68. FM2_INPUT,
  69. FM3_INPUT,
  70. PWM1_INPUT,
  71. PWM2_INPUT,
  72. PWM3_INPUT,
  73. NUM_INPUTS
  74. };
  75. enum OutputIds
  76. {
  77. MIX_OUTPUT,
  78. VCO1_OUTPUT,
  79. VCO2_OUTPUT,
  80. VCO3_OUTPUT,
  81. NUM_OUTPUTS
  82. };
  83. enum LightIds
  84. {
  85. NUM_LIGHTS
  86. };
  87. void step() override;
  88. private:
  89. void setSync();
  90. void processPitchInputs();
  91. void processPitchInputs(int osc);
  92. void processWaveforms();
  93. void stepVCOs();
  94. void init();
  95. void processPWInputs();
  96. void processPWInput(int osc);
  97. float getInput(int osc, InputIds in0, InputIds in1, InputIds in2);
  98. MinBLEPVCO vcos[3];
  99. float _freq[3];
  100. float _out[3];
  101. std::function<float(float)> expLookup =
  102. ObjectCache<float>::getExp2Ex();
  103. std::shared_ptr<LookupTableParams<float>> audioTaper =
  104. ObjectCache<float>::getAudioTaper();
  105. };
  106. template <class TBase>
  107. inline void EV3<TBase>::init()
  108. {
  109. for (int i = 0; i < 3; ++i) {
  110. vcos[i].setWaveform(MinBLEPVCO::Waveform::Saw);
  111. }
  112. vcos[0].setSyncCallback([this](float f) {
  113. if (TBase::params[SYNC2_PARAM].value > .5) {
  114. vcos[1].onMasterSync(f);
  115. }
  116. if (TBase::params[SYNC3_PARAM].value > .5) {
  117. vcos[2].onMasterSync(f);
  118. }
  119. });
  120. }
  121. template <class TBase>
  122. inline void EV3<TBase>::setSync()
  123. {
  124. vcos[0].setSyncEnabled(false);
  125. vcos[1].setSyncEnabled(TBase::params[SYNC2_PARAM].value > .5);
  126. vcos[2].setSyncEnabled(TBase::params[SYNC3_PARAM].value > .5);
  127. }
  128. template <class TBase>
  129. inline void EV3<TBase>::processWaveforms()
  130. {
  131. vcos[0].setWaveform((MinBLEPVCO::Waveform)(int)TBase::params[WAVE1_PARAM].value);
  132. vcos[1].setWaveform((MinBLEPVCO::Waveform)(int)TBase::params[WAVE2_PARAM].value);
  133. vcos[2].setWaveform((MinBLEPVCO::Waveform)(int)TBase::params[WAVE3_PARAM].value);
  134. }
  135. template <class TBase>
  136. float EV3<TBase>::getInput(int osc, InputIds in1, InputIds in2, InputIds in3)
  137. {
  138. const bool in2Connected = TBase::inputs[in2].active;
  139. const bool in3Connected = TBase::inputs[in3].active;
  140. InputIds id = in1;
  141. if ((osc == 1) && in2Connected) {
  142. id = in2;
  143. }
  144. if (osc == 2) {
  145. if (in3Connected) id = in3;
  146. else if (in2Connected) id = in2;
  147. }
  148. return TBase::inputs[id].value;
  149. }
  150. template <class TBase>
  151. void EV3<TBase>::processPWInput(int osc)
  152. {
  153. const float pwmInput = getInput(osc, PWM1_INPUT, PWM2_INPUT, PWM3_INPUT) / 5.f;
  154. const int delta = osc * (OCTAVE2_PARAM - OCTAVE1_PARAM);
  155. const float pwmTrim = TBase::params[PWM1_PARAM + delta].value;
  156. const float pwInit = TBase::params[PW1_PARAM + delta].value;
  157. float pw = pwInit + pwmInput * pwmTrim;
  158. const float minPw = 0.05f;
  159. pw = rack::rescale(std::clamp(pw, -1.0f, 1.0f), -1.0f, 1.0f, minPw, 1.0f - minPw);
  160. vcos[osc].setPulseWidth(pw);
  161. }
  162. template <class TBase>
  163. inline void EV3<TBase>::processPWInputs()
  164. {
  165. processPWInput(0);
  166. processPWInput(1);
  167. processPWInput(2);
  168. }
  169. template <class TBase>
  170. inline void EV3<TBase>::step()
  171. {
  172. setSync();
  173. processPitchInputs();
  174. processWaveforms();
  175. processPWInputs();
  176. stepVCOs();
  177. float mix = 0;
  178. for (int i = 0; i < 3; ++i) {
  179. const float knob = TBase::params[MIX1_PARAM + i].value;
  180. const float gain = LookupTable<float>::lookup(*audioTaper, knob, false);
  181. const float rawWaveform = vcos[i].getOutput();
  182. const float scaledWaveform = rawWaveform * gain;
  183. mix += scaledWaveform;
  184. _out[i] = scaledWaveform;
  185. TBase::outputs[VCO1_OUTPUT + i].value = rawWaveform;
  186. }
  187. TBase::outputs[MIX_OUTPUT].value = mix;
  188. }
  189. template <class TBase>
  190. inline void EV3<TBase>::stepVCOs()
  191. {
  192. for (int i = 0; i < 3; ++i) {
  193. vcos[i].step();
  194. }
  195. }
  196. template <class TBase>
  197. inline void EV3<TBase>::processPitchInputs()
  198. {
  199. float lastFM = 0;
  200. for (int osc = 0; osc < 3; ++osc) {
  201. assert(osc >= 0 && osc <= 2);
  202. const int delta = osc * (OCTAVE2_PARAM - OCTAVE1_PARAM);
  203. const float cv = getInput(osc, CV1_INPUT, CV2_INPUT, CV3_INPUT);
  204. const float finePitch = TBase::params[FINE1_PARAM + delta].value / 12.0f;
  205. const float semiPitch = TBase::params[SEMI1_PARAM + delta].value / 12.0f;
  206. // const float fm = getInput(osc, FM1_INPUT, FM2_INPUT, FM3_INPUT);
  207. float pitch = 1.0f + roundf(TBase::params[OCTAVE1_PARAM + delta].value) +
  208. semiPitch +
  209. finePitch;
  210. pitch += cv;
  211. float fmCombined = 0; // The final, scaled, value (post knob
  212. if (TBase::inputs[FM1_INPUT + osc].active) {
  213. const float fm = TBase::inputs[FM1_INPUT + osc].value;
  214. // const float fmKnob = TBase::params[FM1_PARAM + delta].value;
  215. //const float fmDepth = LookupTable<float>::lookup(*audioTaper, fmKnob, false);
  216. const float fmDepth = rack::quadraticBipolar(TBase::params[FM1_PARAM + delta].value);
  217. fmCombined = (fmDepth * fm);
  218. #if 0
  219. static float biggest = 0;
  220. if (fmCombined > biggest) {
  221. printf("CV =%f knob = %f depth=%f combined=%f\n", fm, fmKnob, fmDepth, fmCombined);
  222. fflush(stdout);
  223. biggest = fmCombined;
  224. }
  225. #endif
  226. // pitch += (fmDepth * fm * 12);
  227. } else {
  228. fmCombined = lastFM;
  229. }
  230. pitch += fmCombined;
  231. lastFM = fmCombined;
  232. const float q = float(log2(261.626)); // move up to pitch range of EvenVCO
  233. pitch += q;
  234. const float freq = expLookup(pitch);
  235. _freq[osc] = freq;
  236. vcos[osc].setNormalizedFreq(TBase::engineGetSampleTime() * freq,
  237. TBase::engineGetSampleTime());
  238. }
  239. }