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.

259 lines
6.7KB

  1. #pragma once
  2. #include <vector>
  3. #include "ClockMult.h"
  4. #include "ObjectCache.h"
  5. #include "AsymRampShaper.h"
  6. #include "GateTrigger.h"
  7. /**
  8. * CPU usage was 15
  9. * down to 7.2 with /4 subsample
  10. */
  11. template <class TBase>
  12. class Tremolo : public TBase
  13. {
  14. public:
  15. Tremolo(struct Module * module) : TBase(module), gateTrigger(true)
  16. {
  17. }
  18. Tremolo() : TBase(), gateTrigger(true)
  19. {
  20. }
  21. void setSampleRate(float rate)
  22. {
  23. reciprocalSampleRate = 1 / rate;
  24. }
  25. // must be called after setSampleRate
  26. void init();
  27. enum ParamIds
  28. {
  29. LFO_RATE_PARAM,
  30. LFO_SHAPE_PARAM,
  31. LFO_SKEW_PARAM,
  32. LFO_PHASE_PARAM,
  33. MOD_DEPTH_PARAM,
  34. CLOCK_MULT_PARAM,
  35. LFO_SHAPE_TRIM_PARAM,
  36. LFO_SKEW_TRIM_PARAM,
  37. LFO_PHASE_TRIM_PARAM,
  38. MOD_DEPTH_TRIM_PARAM,
  39. NUM_PARAMS
  40. };
  41. enum InputIds
  42. {
  43. AUDIO_INPUT,
  44. CLOCK_INPUT,
  45. LFO_SHAPE_INPUT,
  46. LFO_SKEW_INPUT,
  47. LFO_PHASE_INPUT,
  48. MOD_DEPTH_INPUT,
  49. NUM_INPUTS
  50. };
  51. enum OutputIds
  52. {
  53. AUDIO_OUTPUT,
  54. SAW_OUTPUT,
  55. LFO_OUTPUT,
  56. NUM_OUTPUTS
  57. };
  58. enum LightIds
  59. {
  60. NUM_LIGHTS
  61. };
  62. /**
  63. * Main processing entry point. Called every sample
  64. */
  65. void step() override;
  66. private:
  67. int inputSubSampleCounter = 1;
  68. const static int inputSubSample = 4; // only look at knob/cv every 4
  69. float skew = .1f;
  70. float phase = 0;
  71. float shape = 0;
  72. float modDepth = 0;
  73. float shapeMul = 0;
  74. float gain = 0;
  75. void stepInput();
  76. ClockMult clock;
  77. std::shared_ptr<LookupTableParams<float>> tanhLookup;
  78. float reciprocalSampleRate = 0;
  79. AsymRampShaperParams rampShaper;
  80. std::shared_ptr<LookupTableParams<float>> exp2 = ObjectCache<float>::getExp2();
  81. // make some bootstrap scalers
  82. AudioMath::ScaleFun<float> scale_rate;
  83. AudioMath::ScaleFun<float> scale_skew;
  84. AudioMath::ScaleFun<float> scale_shape;
  85. AudioMath::ScaleFun<float> scale_depth;
  86. AudioMath::ScaleFun<float> scale_phase;
  87. GateTrigger gateTrigger;
  88. };
  89. template <class TBase>
  90. inline void Tremolo<TBase>::init()
  91. {
  92. tanhLookup = ObjectCache<float>::getTanh5();
  93. clock.setMultiplier(0);
  94. scale_rate = AudioMath::makeLinearScaler(4.f, 9.f); // log domain, 16 range
  95. scale_skew = AudioMath::makeLinearScaler(-.99f, .99f);
  96. scale_shape = AudioMath::makeLinearScaler(0.f, 1.f);
  97. scale_depth = AudioMath::makeLinearScaler(0.f, 1.f);
  98. scale_phase = AudioMath::makeLinearScaler(-1.f, 1.f);
  99. stepInput(); // call once to init
  100. }
  101. template <class TBase>
  102. inline void Tremolo<TBase>::stepInput()
  103. {
  104. int clockMul = (int) round(TBase::params[CLOCK_MULT_PARAM].value);
  105. // UI is shifted
  106. clockMul++;
  107. if (clockMul > 4) {
  108. clockMul = 0;
  109. }
  110. clock.setMultiplier(clockMul);
  111. // second look at knowb and CV
  112. shape = scale_shape(
  113. TBase::inputs[LFO_SHAPE_INPUT].value,
  114. TBase::params[LFO_SHAPE_PARAM].value,
  115. TBase::params[LFO_SHAPE_TRIM_PARAM].value);
  116. skew = scale_skew(
  117. TBase::inputs[LFO_SKEW_INPUT].value,
  118. TBase::params[LFO_SKEW_PARAM].value,
  119. TBase::params[LFO_SKEW_TRIM_PARAM].value);
  120. phase = scale_phase(
  121. TBase::inputs[LFO_PHASE_INPUT].value,
  122. TBase::params[LFO_PHASE_PARAM].value,
  123. TBase::params[LFO_PHASE_TRIM_PARAM].value);
  124. modDepth = scale_depth(
  125. TBase::inputs[MOD_DEPTH_INPUT].value,
  126. TBase::params[MOD_DEPTH_PARAM].value,
  127. TBase::params[MOD_DEPTH_TRIM_PARAM].value);
  128. shapeMul = std::max(.25f, 10 * shape);
  129. gain = modDepth /
  130. LookupTable<float>::lookup(*tanhLookup.get(), (shapeMul / 2));
  131. // update internal clock from knob
  132. if (clockMul == 0) // only calc rate for internal
  133. {
  134. const float logRate = scale_rate(
  135. 0,
  136. TBase::params[LFO_RATE_PARAM].value,
  137. 1);
  138. float rate = LookupTable<float>::lookup(*exp2, logRate);
  139. float scaledRate = rate * .06f;
  140. clock.setFreeRunFreq(scaledRate * reciprocalSampleRate);
  141. }
  142. AsymRampShaper::setup(rampShaper, skew, phase);
  143. }
  144. template <class TBase>
  145. inline void Tremolo<TBase>::step()
  146. {
  147. if (--inputSubSampleCounter <= 0) {
  148. inputSubSampleCounter = inputSubSample;
  149. stepInput();
  150. }
  151. // First: external clock proc
  152. gateTrigger.go(TBase::inputs[CLOCK_INPUT].value);
  153. if (gateTrigger.trigger()) {
  154. clock.refClock();
  155. }
  156. // ------------ now generate the lfo waveform
  157. clock.sampleClock();
  158. float mod = clock.getSaw();
  159. mod = AsymRampShaper::proc_1(rampShaper, mod);
  160. mod -= 0.5f;
  161. // now we have a skewed saw -.5 to .5
  162. TBase::outputs[SAW_OUTPUT].value = mod;
  163. // TODO: don't scale twice - just get it right the first time
  164. // const float shapeMul = std::max(.25f, 10 * shape);
  165. mod *= shapeMul;
  166. mod = LookupTable<float>::lookup(*tanhLookup.get(), mod);
  167. TBase::outputs[LFO_OUTPUT].value = mod;
  168. // TODO: move this intp input proc
  169. // const float gain = modDepth /
  170. // LookupTable<float>::lookup(*tanhLookup.get(), (shapeMul / 2));
  171. const float finalMod = gain * mod + 1; // TODO: this offset by 1 is pretty good, but we
  172. // could add an offset control to make it really "chop" off
  173. TBase::outputs[AUDIO_OUTPUT].value = TBase::inputs[AUDIO_INPUT].value * finalMod;
  174. }
  175. /*
  176. old plug proc loop.
  177. // Step 1: generate a saw
  178. // range is 0..1
  179. SawOsc<vec_t>::gen_v(*sawState, *sawParams, tempBuffer, sampleFrames);
  180. // step 2: apply skew and phase shift
  181. // range still 0..1
  182. AsymRampShaper<vec_t>::proc_v(*shaperParams, tempBuffer, tempBuffer, sampleFrames);
  183. // step 3: shift down to be centered at zero,
  184. // max excursion +-5 at shape "most square"
  185. // min is +/- .25 TODO: put the .25 into the control range itself
  186. // range = +/- (5 * shape)
  187. //
  188. f_t shapeMul = std::max(.25, 10 * controlValues.lfoShape);
  189. VecBasic<vec_t>::add_mul_c_imp(tempBuffer, sampleFrames, shapeMul, -.5f);
  190. // now tanh,
  191. // output contered around zero,
  192. // max is tanh(.25) to tanh(5), depending on shape value
  193. // rang = +/- tanh(5 * shape)
  194. LookupUniform<vec_t>::lookup_clip_v(*tanhParams, tempBuffer, tempBuffer, sampleFrames);
  195. // so: makeup gain of 1/tanh(shapeMul) will get us to +1/-1
  196. // then multiply by depth to get contered around zero with correct depth
  197. // the add one to get back to trem range!
  198. f_t gain = controlValues.modDepth / tanh(shapeMul/2);
  199. VecBasic<vec_t>::mul_add_c_imp(tempBuffer, sampleFrames, gain, 1);
  200. // scale then add constant
  201. // input = a * input + b
  202. static void mul_add_c_imp(f_t * inout, int size, f_t a, f_t b) {
  203. assert_size(size);
  204. // now range = +/- tanh(5*shape) * depth / tanh(10 * shape)
  205. */