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.

346 lines
8.3KB

  1. #pragma once
  2. #include "GateTrigger.h"
  3. #include "IIRDecimator.h"
  4. #include "NonUniformLookupTable.h"
  5. #include "ObjectCache.h"
  6. #include "StateVariable4PHP.h"
  7. class SawtoothDetuneCurve
  8. {
  9. public:
  10. /**
  11. * @param depth is "detune" knob. 0..1
  12. * returns a number such that freq = detuneFactor * initialFreq
  13. */
  14. float getDetuneFactor(float depth)
  15. {
  16. return NonUniformLookupTable<float>::lookup(table, depth);
  17. }
  18. SawtoothDetuneCurve()
  19. {
  20. // this data is pretty regular - could use uniform table
  21. using T = NonUniformLookupTable<float>;
  22. T::addPoint(table, 0, 0);
  23. T::addPoint(table, .0551f, .00967f);
  24. T::addPoint(table, .118f, .022f);
  25. T::addPoint(table, .181f, .04f);
  26. T::addPoint(table, .244f, .0467f);
  27. T::addPoint(table, .307f, .059f);
  28. T::addPoint(table, .37f, .0714f);
  29. T::addPoint(table, .433f, .0838f);
  30. T::addPoint(table, .496f, .0967f);
  31. T::addPoint(table, .559f, .121f);
  32. T::addPoint(table, .622f, .147f);
  33. T::addPoint(table, .748f, .243f);
  34. T::addPoint(table, .811f, .293f);
  35. T::addPoint(table, .874f, .343f);
  36. T::addPoint(table, .937f, .392f);
  37. T::addPoint(table, 1, 1);
  38. NonUniformLookupTable<float>::finalize(table);
  39. }
  40. private:
  41. NonUniformLookupTableParams<float> table;
  42. };
  43. /**
  44. * orig CPU = 39
  45. * sub sample => 16
  46. * beta1 => 16.1
  47. 17.7 if change pitch every 16 samples.
  48. * beta 3: fix instability
  49. * made semitone and fine ranges sane.
  50. */
  51. template <class TBase>
  52. class Super : public TBase
  53. {
  54. public:
  55. Super(struct Module * module) : TBase(module), gateTrigger(true)
  56. {
  57. init();
  58. }
  59. Super() : TBase(), gateTrigger(true)
  60. {
  61. init();
  62. }
  63. /**
  64. * re-calculate everything that changes with sample
  65. * rate. Also everything that depends on baseFrequency.
  66. *
  67. * Only needs to be called once.
  68. */
  69. void init();
  70. enum ParamIds
  71. {
  72. OCTAVE_PARAM,
  73. SEMI_PARAM,
  74. FINE_PARAM,
  75. DETUNE_PARAM,
  76. DETUNE_TRIM_PARAM,
  77. MIX_PARAM,
  78. MIX_TRIM_PARAM,
  79. FM_PARAM,
  80. CLEAN_PARAM,
  81. NUM_PARAMS
  82. };
  83. enum InputIds
  84. {
  85. CV_INPUT,
  86. TRIGGER_INPUT,
  87. DETUNE_INPUT,
  88. MIX_INPUT,
  89. FM_INPUT,
  90. NUM_INPUTS
  91. };
  92. enum OutputIds
  93. {
  94. MAIN_OUTPUT,
  95. NUM_OUTPUTS
  96. };
  97. enum LightIds
  98. {
  99. NUM_LIGHTS
  100. };
  101. /**
  102. * Main processing entry point. Called every sample
  103. */
  104. void step() override;
  105. private:
  106. static const unsigned int MAX_OVERSAMPLE = 16;
  107. static const int numSaws = 7;
  108. float phase[numSaws] = {0};
  109. float phaseInc[numSaws] = {0};
  110. float globalPhaseInc = 0;
  111. std::function<float(float)> expLookup =
  112. ObjectCache<float>::getExp2Ex();
  113. std::shared_ptr<LookupTableParams<float>> audioTaper =
  114. ObjectCache<float>::getAudioTaper();
  115. // knob, cv, trim -> 0..1
  116. AudioMath::ScaleFun<float> scaleDetune;
  117. float runSaws();
  118. void updatePhaseInc();
  119. void updateAudioClassic();
  120. void updateAudioClean();
  121. void updateTrigger();
  122. void updateMix();
  123. int getOversampleRate();
  124. AudioMath::RandomUniformFunc random = AudioMath::random();
  125. int inputSubSampleCounter = 1;
  126. const static int inputSubSample = 4; // only look at knob/cv every 4
  127. // TODO: make static
  128. float const detuneFactors[numSaws] = {
  129. .89f,
  130. .94f,
  131. .98f,
  132. 1.f,
  133. 1.02f,
  134. 1.06f,
  135. 1.107f
  136. };
  137. void updateHPFilters();
  138. SawtoothDetuneCurve detuneCurve;
  139. GateTrigger gateTrigger;
  140. float gainCenter = 0;
  141. float gainSides = 0;
  142. StateVariable4PHP hpf;
  143. float buffer[MAX_OVERSAMPLE] = {};
  144. IIRDecimator decimator;
  145. };
  146. template <class TBase>
  147. inline void Super<TBase>::init()
  148. {
  149. scaleDetune = AudioMath::makeLinearScaler<float>(0, 1);
  150. const int rate = getOversampleRate();
  151. const int decimateDiv = std::max(rate, (int) MAX_OVERSAMPLE);
  152. decimator.setup(decimateDiv);
  153. }
  154. template <class TBase>
  155. inline int Super<TBase>::getOversampleRate()
  156. {
  157. int rate = 1;
  158. const int setting = (int) std::round(TBase::params[CLEAN_PARAM].value);
  159. switch (setting) {
  160. case 0:
  161. rate = 1;
  162. break;
  163. case 1:
  164. rate = 4;
  165. break;
  166. case 2:
  167. rate = 16;
  168. break;
  169. default:
  170. assert(false);
  171. }
  172. assert(rate <= (int) MAX_OVERSAMPLE);
  173. return rate;
  174. }
  175. template <class TBase>
  176. inline void Super<TBase>::updatePhaseInc()
  177. {
  178. const float cv = TBase::inputs[CV_INPUT].value;
  179. const float finePitch = TBase::params[FINE_PARAM].value / 12.0f;
  180. const float semiPitch = TBase::params[SEMI_PARAM].value / 12.0f;
  181. float pitch = 1.0f + roundf(TBase::params[OCTAVE_PARAM].value) +
  182. semiPitch +
  183. finePitch;
  184. pitch += cv;
  185. const float fm = TBase::inputs[FM_INPUT].value;
  186. const float fmDepth = AudioMath::quadraticBipolar(TBase::params[FM_PARAM].value);
  187. pitch += (fmDepth * fm);
  188. const float q = float(log2(261.626)); // move up to pitch range of EvenVCO
  189. pitch += q;
  190. const float freq = expLookup(pitch);
  191. globalPhaseInc = TBase::engineGetSampleTime() * freq;
  192. const float rawDetuneValue = scaleDetune(
  193. TBase::inputs[DETUNE_INPUT].value,
  194. TBase::params[DETUNE_PARAM].value,
  195. TBase::params[DETUNE_TRIM_PARAM].value);
  196. const float detuneInput = detuneCurve.getDetuneFactor(rawDetuneValue);
  197. // const bool classic = TBase::params[CLEAN_PARAM].value < .5f;
  198. const int oversampleRate = getOversampleRate();
  199. for (int i = 0; i < numSaws; ++i) {
  200. float detune = (detuneFactors[i] - 1) * detuneInput;
  201. detune += 1;
  202. float phaseIncI = globalPhaseInc * detune;
  203. phaseIncI = std::min(phaseIncI, .4f); // limit so saws don't go crazy
  204. if (oversampleRate > 1) {
  205. phaseIncI /= oversampleRate;
  206. }
  207. phaseInc[i] = phaseIncI;
  208. }
  209. }
  210. template <class TBase>
  211. inline float Super<TBase>::runSaws()
  212. {
  213. float mix = 0;
  214. for (int i = 0; i < numSaws; ++i) {
  215. phase[i] += phaseInc[i];
  216. if (phase[i] > 1) {
  217. phase[i] -= 1;
  218. }
  219. assert(phase[i] <= 1);
  220. assert(phase[i] >= 0);
  221. const float gain = (i == numSaws / 2) ? gainCenter : gainSides;
  222. // mix += phase[i] * gain;
  223. mix += (phase[i] - .5f) * gain; // experiment to get rid of DC
  224. }
  225. mix *= 4.5; // too low 2 too high 10
  226. return mix;
  227. }
  228. template <class TBase>
  229. inline void Super<TBase>::updateAudioClassic()
  230. {
  231. const float mix = runSaws();
  232. const float output = hpf.run(mix);
  233. TBase::outputs[MAIN_OUTPUT].value = output;
  234. }
  235. template <class TBase>
  236. inline void Super<TBase>::updateAudioClean()
  237. {
  238. const int bufferSize = getOversampleRate();
  239. decimator.setup(bufferSize);
  240. for (int i = 0; i < bufferSize; ++i) {
  241. const float mix = runSaws();
  242. buffer[i] = mix;
  243. }
  244. //const float output = hpf.run(mix);
  245. const float output = decimator.process(buffer);
  246. TBase::outputs[MAIN_OUTPUT].value = output;
  247. }
  248. template <class TBase>
  249. inline void Super<TBase>::updateHPFilters()
  250. {
  251. const float filterCutoff = std::min(globalPhaseInc, .1f);
  252. hpf.setCutoff(filterCutoff);
  253. }
  254. template <class TBase>
  255. inline void Super<TBase>::step()
  256. {
  257. updateTrigger();
  258. if (--inputSubSampleCounter <= 0) {
  259. inputSubSampleCounter = inputSubSample;
  260. updatePhaseInc();
  261. updateHPFilters();
  262. updateMix();
  263. }
  264. // const bool classic = TBase::params[CLEAN_PARAM].value < .5f;
  265. int rate = getOversampleRate();
  266. if (rate == 1) {
  267. updateAudioClassic();
  268. } else {
  269. updateAudioClean();
  270. }
  271. }
  272. template <class TBase>
  273. inline void Super<TBase>::updateTrigger()
  274. {
  275. gateTrigger.go(TBase::inputs[TRIGGER_INPUT].value);
  276. if (gateTrigger.trigger()) {
  277. for (int i = 0; i < numSaws; ++i) {
  278. phase[i] = this->random();
  279. }
  280. }
  281. }
  282. template <class TBase>
  283. inline void Super<TBase>::updateMix()
  284. {
  285. const float rawMixValue = scaleDetune(
  286. TBase::inputs[MIX_INPUT].value,
  287. TBase::params[MIX_PARAM].value,
  288. TBase::params[MIX_TRIM_PARAM].value);
  289. gainCenter = -0.55366f * rawMixValue + 0.99785f;
  290. gainSides = -0.73764f * rawMixValue * rawMixValue +
  291. 1.2841f * rawMixValue + 0.044372f;
  292. }