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.

307 lines
8.8KB

  1. #pragma once
  2. #include <algorithm>
  3. #include "AudioMath.h"
  4. #include "LookupTable.h"
  5. #include "LookupTableFactory.h"
  6. #include "MultiModOsc.h"
  7. #include "ObjectCache.h"
  8. #include "StateVariableFilter.h"
  9. #define _ANORM
  10. /**
  11. * Version 2 - make the math sane.
  12. */
  13. template <class TBase>
  14. class VocalAnimator : public TBase
  15. {
  16. public:
  17. typedef float T;
  18. static const int numTriangle = 4;
  19. static const int numModOutputs = 3;
  20. static const int numFilters = 4;
  21. VocalAnimator(struct Module * module) : TBase(module)
  22. {
  23. }
  24. VocalAnimator() : TBase()
  25. {
  26. }
  27. void setSampleRate(float rate)
  28. {
  29. reciprocalSampleRate = 1 / rate;
  30. modulatorParams.setRateAndSpread(.5, .5, 0, reciprocalSampleRate);
  31. }
  32. enum ParamIds
  33. {
  34. LFO_RATE_PARAM,
  35. FILTER_Q_PARAM,
  36. FILTER_FC_PARAM,
  37. FILTER_MOD_DEPTH_PARAM,
  38. LFO_RATE_TRIM_PARAM,
  39. FILTER_Q_TRIM_PARAM,
  40. FILTER_FC_TRIM_PARAM,
  41. FILTER_MOD_DEPTH_TRIM_PARAM,
  42. BASS_EXP_PARAM,
  43. // tracking:
  44. // 0 = all 1v/octave, mod scaled, no on top
  45. // 1 = mod and cv scaled
  46. // 2 = 1, + top filter gets some mod
  47. TRACK_EXP_PARAM,
  48. // LFO mixing options
  49. // 0 = classic
  50. // 1 = option
  51. // 2 = lf sub
  52. LFO_MIX_PARAM,
  53. NUM_PARAMS
  54. };
  55. enum InputIds
  56. {
  57. AUDIO_INPUT,
  58. LFO_RATE_CV_INPUT,
  59. FILTER_Q_CV_INPUT,
  60. FILTER_FC_CV_INPUT,
  61. FILTER_MOD_DEPTH_CV_INPUT,
  62. NUM_INPUTS
  63. };
  64. enum OutputIds
  65. {
  66. AUDIO_OUTPUT,
  67. LFO0_OUTPUT,
  68. LFO1_OUTPUT,
  69. LFO2_OUTPUT,
  70. NUM_OUTPUTS
  71. };
  72. enum LightIds
  73. {
  74. LFO0_LIGHT,
  75. LFO1_LIGHT,
  76. LFO2_LIGHT,
  77. BASS_LIGHT,
  78. NUM_LIGHTS
  79. };
  80. void init();
  81. void step();
  82. T modulatorOutput[numModOutputs];
  83. // The frequency inputs to the filters, exposed for testing.
  84. T filterFrequencyLog[numFilters];
  85. const T nominalFilterCenterHz[numFilters] = {522, 1340, 2570, 3700};
  86. const T nominalFilterCenterLog2[numFilters] = {
  87. std::log2(T(522)),
  88. std::log2(T(1340)),
  89. std::log2(T(2570)),
  90. std::log2(T(3700))
  91. };
  92. // 1, .937 .3125
  93. const T nominalModSensitivity[numFilters] = {T(1), T(.937), T(.3125), 0};
  94. // Following are for unit tests.
  95. T normalizedFilterFreq[numFilters];
  96. bool jamModForTest = false;
  97. T modValueForTest = 0;
  98. float reciprocalSampleRate;
  99. using osc = MultiModOsc<T, numTriangle, numModOutputs>;
  100. typename osc::State modulatorState;
  101. typename osc::Params modulatorParams;
  102. StateVariableFilterState<T> filterStates[numFilters];
  103. StateVariableFilterParams<T> filterParams[numFilters];
  104. std::shared_ptr<LookupTableParams<T>> expLookup;
  105. // We need a bunch of scalers to convert knob, CV, trim into the voltage
  106. // range each parameter needs.
  107. AudioMath::ScaleFun<T> scale0_1;
  108. AudioMath::ScaleFun<T> scalem2_2;
  109. AudioMath::ScaleFun<T> scaleQ;
  110. AudioMath::ScaleFun<T> scalen5_5;
  111. };
  112. template <class TBase>
  113. inline void VocalAnimator<TBase>::init()
  114. {
  115. for (int i = 0; i < numFilters; ++i) {
  116. filterParams[i].setMode(StateVariableFilterParams<T>::Mode::BandPass);
  117. filterParams[i].setQ(15); // or should it be 5?
  118. filterParams[i].setFreq(nominalFilterCenterHz[i] * reciprocalSampleRate);
  119. filterFrequencyLog[i] = nominalFilterCenterLog2[i];
  120. normalizedFilterFreq[i] = nominalFilterCenterHz[i] * reciprocalSampleRate;
  121. }
  122. scale0_1 = AudioMath::makeBipolarAudioScaler(0, 1); // full CV range -> 0..1
  123. scalem2_2 = AudioMath::makeBipolarAudioScaler(-2, 2); // full CV range -> -2..2
  124. scaleQ = AudioMath::makeBipolarAudioScaler(.71f, 21);
  125. scalen5_5 = AudioMath::makeBipolarAudioScaler(-5, 5);
  126. // make table of 2 ** x
  127. expLookup = ObjectCache<T>::getExp2();
  128. }
  129. template <class TBase>
  130. inline void VocalAnimator<TBase>::step()
  131. {
  132. const bool bass = TBase::params[BASS_EXP_PARAM].value > .5;
  133. const auto mode = bass ?
  134. StateVariableFilterParams<T>::Mode::LowPass :
  135. StateVariableFilterParams<T>::Mode::BandPass;
  136. for (int i = 0; i < numFilters +1 - 1; ++i) {
  137. filterParams[i].setMode(mode);
  138. }
  139. // Run the modulators, hold onto their output.
  140. // Raw Modulator outputs put in modulatorOutputs[].
  141. osc::run(modulatorOutput, modulatorState, modulatorParams);
  142. static const OutputIds LEDOutputs[] = {
  143. LFO0_OUTPUT,
  144. LFO1_OUTPUT,
  145. LFO2_OUTPUT,
  146. };
  147. // Light up the LEDs with the unscaled Modulator outputs.
  148. for (int i = LFO0_LIGHT; i <= LFO2_LIGHT; ++i) {
  149. TBase::outputs[LEDOutputs[i]].value = modulatorOutput[i];
  150. TBase::lights[i].value = (modulatorOutput[i] ) * .3f;
  151. TBase::outputs[LEDOutputs[i]].value = modulatorOutput[i];
  152. }
  153. // Normalize all the parameters out here
  154. const T qFinal = scaleQ(
  155. TBase::inputs[FILTER_Q_CV_INPUT].value,
  156. TBase::params[FILTER_Q_PARAM].value,
  157. TBase::params[FILTER_Q_TRIM_PARAM].value);
  158. const T fc = scalen5_5(
  159. TBase::inputs[FILTER_FC_CV_INPUT].value,
  160. TBase::params[FILTER_FC_PARAM].value,
  161. TBase::params[FILTER_FC_TRIM_PARAM].value);
  162. // put together a mod depth parameter from all the inputs
  163. // range is 0..1
  164. // cv, knob, trim
  165. const T baseModDepth = scale0_1(
  166. TBase::inputs[FILTER_MOD_DEPTH_CV_INPUT].value,
  167. TBase::params[FILTER_MOD_DEPTH_PARAM].value,
  168. TBase::params[FILTER_MOD_DEPTH_TRIM_PARAM].value);
  169. // tracking:
  170. // 0 = all 1v/octave, mod scaled, no on top
  171. // 1 = mod and cv scaled
  172. // 2 = 1, + top filter gets some mod
  173. int cvScaleMode = 0;
  174. const float cvScaleParam = TBase::params[TRACK_EXP_PARAM].value;
  175. if (cvScaleParam < .5) {
  176. cvScaleMode = 0;
  177. } else if (cvScaleParam < 1.5) {
  178. cvScaleMode = 1;
  179. } else {
  180. cvScaleMode = 2;
  181. assert(cvScaleParam < 2.5);
  182. }
  183. // Just do the Q division once, in the outer loop
  184. const T filterNormalizedBandwidth = T(1) / qFinal;
  185. const T input = TBase::inputs[AUDIO_INPUT].value;
  186. T filterMix = 0; // Sum the folder outputs here
  187. for (int i = 0; i < numFilters; ++i) {
  188. T logFreq = nominalFilterCenterLog2[i];
  189. switch (cvScaleMode) {
  190. case 1:
  191. // In this mode (1) CV comes straight through at 1V/8
  192. // Even on the top (fixed) filter
  193. logFreq += fc; // add without attenuation for 1V/octave
  194. break;
  195. case 0:
  196. // In mode (0) CV gets scaled per filter, as in the original design.
  197. // Since nominalModSensitivity[3] == 0, top doesn't track
  198. logFreq += fc * nominalModSensitivity[i];
  199. break;
  200. case 2:
  201. if (fc < 0) {
  202. // Normal scaling for Fc less than nominal
  203. logFreq += fc * nominalModSensitivity[i];
  204. } else {
  205. // above nominal, they all track the high one (so they don't cross)
  206. logFreq += fc * nominalModSensitivity[2];
  207. }
  208. break;
  209. default:
  210. assert(false);
  211. }
  212. // First three filters always modulated,
  213. // (wanted to try modulating 4, but don't have an LFO (yet
  214. const bool modulateThisFilter = (i < 3);
  215. if (modulateThisFilter) {
  216. logFreq += modulatorOutput[i] *
  217. baseModDepth *
  218. nominalModSensitivity[i];
  219. }
  220. logFreq += ((i < 3) ? modulatorOutput[i] : 0) *
  221. baseModDepth *
  222. nominalModSensitivity[i];
  223. filterFrequencyLog[i] = logFreq;
  224. // tell lookup not to assert - we know we can go slightly out of range.
  225. T normFreq = LookupTable<T>::lookup(*expLookup, logFreq, true) * reciprocalSampleRate;
  226. normFreq = std::min(normFreq, T(.2));
  227. normalizedFilterFreq[i] = normFreq;
  228. filterParams[i].setFreq(normFreq);
  229. filterParams[i].setNormalizedBandwidth(filterNormalizedBandwidth);
  230. filterMix += StateVariableFilter<T>::run(input, filterStates[i], filterParams[i]);
  231. }
  232. #ifdef _ANORM
  233. filterMix *= filterNormalizedBandwidth * 2;
  234. #else
  235. filterMix *= T(.3); // attenuate to avoid clip
  236. #endif
  237. TBase::outputs[AUDIO_OUTPUT].value = filterMix;
  238. int matrixMode;
  239. float mmParam = TBase::params[LFO_MIX_PARAM].value;
  240. if (mmParam < .5) {
  241. matrixMode = 0;
  242. } else if (mmParam < 1.5) {
  243. matrixMode = 1;
  244. } else {
  245. matrixMode = 2;
  246. assert(mmParam < 2.5);
  247. }
  248. const T spread = T(1.0);
  249. modulatorParams.setRateAndSpread(
  250. scalem2_2(
  251. TBase::inputs[LFO_RATE_CV_INPUT].value,
  252. TBase::params[LFO_RATE_PARAM].value,
  253. TBase::params[LFO_RATE_TRIM_PARAM].value),
  254. spread,
  255. matrixMode,
  256. reciprocalSampleRate);
  257. }