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.

231 lines
6.0KB

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