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.

281 lines
6.5KB

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