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.

438 lines
10KB

  1. #pragma once
  2. #include "AsymWaveShaper.h"
  3. #include "ButterworthFilterDesigner.h"
  4. #include "IComposite.h"
  5. #include "IIRUpsampler.h"
  6. #include "IIRDecimator.h"
  7. #include "LookupTable.h"
  8. #include "ObjectCache.h"
  9. template <class TBase>
  10. class ShaperDescription : public IComposite
  11. {
  12. public:
  13. Config getParam(int i) override;
  14. int getNumParams() override;
  15. };
  16. /**
  17. Version 1, cpu usage:
  18. full wave: 95
  19. crush: 281
  20. asy:149
  21. fold: 102
  22. fold2: 154
  23. X4 on input scanning:
  24. full wave: 85
  25. crush: 278
  26. asy:163
  27. fold: 89
  28. fold2: 154
  29. inline:
  30. fw: 75
  31. crush: 87
  32. asy: 112
  33. fold: 77
  34. fold2: 136
  35. */
  36. template <class TBase>
  37. class Shaper : public TBase
  38. {
  39. public:
  40. Shaper(struct Module * module) : TBase(module)
  41. {
  42. init();
  43. }
  44. Shaper() : TBase()
  45. {
  46. init();
  47. }
  48. void onSampleRateChange();
  49. enum class Shapes
  50. {
  51. AsymSpline,
  52. Clip,
  53. EmitterCoupled,
  54. FullWave,
  55. HalfWave,
  56. Fold,
  57. Fold2,
  58. Crush,
  59. Invalid
  60. };
  61. static const char* getString(Shapes);
  62. enum ParamIds
  63. {
  64. PARAM_SHAPE,
  65. PARAM_GAIN,
  66. PARAM_GAIN_TRIM,
  67. PARAM_OFFSET,
  68. PARAM_OFFSET_TRIM,
  69. PARAM_OVERSAMPLE,
  70. PARAM_ACDC,
  71. NUM_PARAMS
  72. };
  73. enum InputIds
  74. {
  75. INPUT_AUDIO,
  76. INPUT_GAIN,
  77. INPUT_OFFSET,
  78. NUM_INPUTS
  79. };
  80. enum OutputIds
  81. {
  82. OUTPUT_AUDIO,
  83. NUM_OUTPUTS
  84. };
  85. enum LightIds
  86. {
  87. NUM_LIGHTS
  88. };
  89. /** Implement IComposite
  90. */
  91. static std::shared_ptr<IComposite> getDescription()
  92. {
  93. return std::make_shared<ShaperDescription<TBase>>();
  94. }
  95. /**
  96. * Main processing entry point. Called every sample
  97. */
  98. void step() override;
  99. float _gain = 0;
  100. float _offset = 0;
  101. float _gainInput = 0;
  102. private:
  103. std::shared_ptr<LookupTableParams<float>> audioTaper = {ObjectCache<float>::getAudioTaper()};
  104. std::shared_ptr<LookupTableParams<float>> sinLookup = {ObjectCache<float>::getSinLookup()};
  105. AudioMath::ScaleFun<float> scaleGain = AudioMath::makeLinearScaler<float>(0, 1);
  106. AudioMath::ScaleFun<float> scaleOffset = AudioMath::makeLinearScaler<float>(-5, 5);
  107. const static int maxOversample = 16;
  108. int curOversample = 16;
  109. void init();
  110. IIRUpsampler up;
  111. IIRDecimator dec;
  112. std::shared_ptr<LookupTableParams<float>> tanhLookup;
  113. AsymWaveShaper asymShaper;
  114. int cycleCount = 0;
  115. Shapes shape = Shapes::Clip;
  116. int asymCurveindex = 0;
  117. /**
  118. * 4 pole butterworth HP
  119. */
  120. using Thpf = double;
  121. BiquadParams<Thpf, 2> dcBlockParams;
  122. BiquadState<Thpf, 2> dcBlockState;
  123. void processCV();
  124. void setOversample();
  125. void processBuffer(float *) const;
  126. };
  127. template <class TBase>
  128. const char* Shaper<TBase>::getString(Shapes shape)
  129. {
  130. const char* ret = "";
  131. switch (shape) {
  132. case Shapes::Clip:
  133. ret = "Clip";
  134. break;
  135. case Shapes::EmitterCoupled:
  136. ret = "Emitter Coupled";
  137. break;
  138. case Shapes::FullWave:
  139. ret = "Full Wave";
  140. break;
  141. case Shapes::HalfWave:
  142. ret = "Half Wave";
  143. break;
  144. case Shapes::Fold:
  145. ret = "Folder";
  146. break;
  147. case Shapes::Fold2:
  148. ret = "Folder II";
  149. break;
  150. case Shapes::AsymSpline:
  151. ret = "Smooth";
  152. break;
  153. case Shapes::Crush:
  154. ret = "Crush";
  155. break;
  156. default:
  157. assert(false);
  158. ret = "error";
  159. }
  160. return ret;
  161. }
  162. template <class TBase>
  163. void Shaper<TBase>::init()
  164. {
  165. onSampleRateChange();
  166. setOversample();
  167. tanhLookup = ObjectCache<float>::getTanh5();
  168. }
  169. template <class TBase>
  170. void Shaper<TBase>::setOversample()
  171. {
  172. // float fc = .25 / float(oversample);
  173. up.setup(curOversample);
  174. dec.setup(curOversample);
  175. }
  176. template <class TBase>
  177. void Shaper<TBase>::onSampleRateChange()
  178. {
  179. const float cutoffHz = 20.f;
  180. float fcNormalized = cutoffHz * this->engineGetSampleTime();
  181. assert((fcNormalized > 0) && (fcNormalized < .1));
  182. ButterworthFilterDesigner<Thpf>::designFourPoleHighpass(dcBlockParams, fcNormalized);
  183. }
  184. template <class TBase>
  185. void Shaper<TBase>::processCV()
  186. {
  187. int oversampleCode = (int) std::round(TBase::params[PARAM_OVERSAMPLE].value);
  188. switch (oversampleCode) {
  189. case 0:
  190. curOversample = 16;
  191. setOversample();
  192. break;
  193. case 1:
  194. curOversample = 4;
  195. setOversample();
  196. break;
  197. case 2:
  198. curOversample = 1;
  199. break;
  200. default:
  201. assert(false);
  202. }
  203. // 0..1
  204. _gainInput = scaleGain(
  205. TBase::inputs[INPUT_GAIN].value,
  206. TBase::params[PARAM_GAIN].value,
  207. TBase::params[PARAM_GAIN_TRIM].value);
  208. _gain = 5 * LookupTable<float>::lookup(*audioTaper, _gainInput, false);
  209. // -5 .. 5
  210. const float offsetInput = scaleOffset(
  211. TBase::inputs[INPUT_OFFSET].value,
  212. TBase::params[PARAM_OFFSET].value,
  213. TBase::params[PARAM_OFFSET_TRIM].value);
  214. _offset = offsetInput;
  215. const int iShape = (int) std::round(TBase::params[PARAM_SHAPE].value);
  216. shape = Shapes(iShape);
  217. const float sym = .1f * (5 - _offset);
  218. asymCurveindex = (int) round(sym * 15.1); // This math belongs in the shaper
  219. }
  220. template <class TBase>
  221. void Shaper<TBase>::step()
  222. {
  223. if (--cycleCount < 0) {
  224. cycleCount = 7;
  225. processCV();
  226. }
  227. float buffer[maxOversample];
  228. float input = TBase::inputs[INPUT_AUDIO].value;
  229. // const float rawInput = input;
  230. // TODO: maybe add offset after gain?
  231. if (shape != Shapes::AsymSpline) {
  232. input += _offset;
  233. }
  234. if (shape != Shapes::Crush) {
  235. input *= _gain;
  236. }
  237. if (curOversample != 1) {
  238. up.process(buffer, input);
  239. } else {
  240. buffer[0] = input;
  241. }
  242. processBuffer(buffer);
  243. float output;
  244. if (curOversample != 1) {
  245. output = dec.process(buffer);
  246. } else {
  247. output = buffer[0];
  248. }
  249. if (TBase::params[PARAM_ACDC].value < .5) {
  250. output = float(BiquadFilter<Thpf>::run(output, dcBlockState, dcBlockParams));
  251. }
  252. TBase::outputs[OUTPUT_AUDIO].value = output;
  253. }
  254. template <class TBase>
  255. void Shaper<TBase>::processBuffer(float* buffer) const
  256. {
  257. switch (shape) {
  258. case Shapes::FullWave:
  259. for (int i = 0; i < curOversample; ++i) {
  260. float x = buffer[i];
  261. x = std::abs(x);
  262. x *= 1.94f;
  263. x = std::min(x, 10.f);
  264. buffer[i] = x;
  265. }
  266. break;
  267. case Shapes::AsymSpline:
  268. for (int i = 0; i < curOversample; ++i) {
  269. float x = buffer[i];
  270. x *= .15f;
  271. x = asymShaper.lookup(x, asymCurveindex);
  272. x *= 6.1f;
  273. buffer[i] = x;
  274. }
  275. break;
  276. case Shapes::Clip:
  277. for (int i = 0; i < curOversample; ++i) {
  278. float x = buffer[i];
  279. x *= 3;
  280. x = std::min(3.f, x);
  281. x = std::max(-3.f, x);
  282. x *= 1.2f;
  283. buffer[i] = x;
  284. }
  285. break;
  286. case Shapes::EmitterCoupled:
  287. for (int i = 0; i < curOversample; ++i) {
  288. float x = buffer[i];
  289. x *= .25;
  290. x = LookupTable<float>::lookup(*tanhLookup.get(), x, true);
  291. x *= 5.4f;
  292. buffer[i] = x;
  293. }
  294. break;
  295. case Shapes::HalfWave:
  296. for (int i = 0; i < curOversample; ++i) {
  297. float x = buffer[i];
  298. x = std::max(0.f, x);
  299. x *= 1.4f * 1.26f;
  300. x = std::min(x, 10.f);
  301. buffer[i] = x;
  302. }
  303. break;
  304. case Shapes::Fold:
  305. for (int i = 0; i < curOversample; ++i) {
  306. float x = buffer[i];
  307. x = AudioMath::fold(x);
  308. x *= 5.6f;
  309. buffer[i] = x;
  310. }
  311. break;
  312. case Shapes::Fold2:
  313. for (int i = 0; i < curOversample; ++i) {
  314. float x = buffer[i];
  315. x = .3f * AudioMath::fold(x);
  316. if (x > 0) {
  317. x = LookupTable<float>::lookup(*sinLookup, 1.3f * x, false);
  318. } else {
  319. x = -LookupTable<float>::lookup(*sinLookup, -x, false);
  320. }
  321. if (x > 0) x = std::sqrt(x);
  322. x *= 4.4f;
  323. buffer[i] = x;
  324. }
  325. break;
  326. case Shapes::Crush:
  327. {
  328. float invGain = 1 + (1 - _gainInput) * 100; //0..10
  329. invGain *= .01f;
  330. invGain = std::max(invGain, .09f);
  331. assert(invGain >= .09);
  332. for (int i = 0; i < curOversample; ++i) {
  333. float x = buffer[i]; // for crush, no gain has been applied
  334. x *= invGain;
  335. x = std::round(x + .5f) - .5f;
  336. x /= invGain;
  337. buffer[i] = x;
  338. }
  339. }
  340. break;
  341. default:
  342. assert(false);
  343. }
  344. }
  345. template <class TBase>
  346. int ShaperDescription<TBase>::getNumParams()
  347. {
  348. return Shaper<TBase>::NUM_PARAMS;
  349. }
  350. template <class TBase>
  351. IComposite::Config ShaperDescription<TBase>::getParam(int i)
  352. {
  353. Config ret(0, 1, 0, "");
  354. switch (i) {
  355. case Shaper<TBase>::PARAM_SHAPE:
  356. ret = {0,
  357. float(Shaper<TBase>::Shapes::Invalid) - 1,
  358. 0,
  359. "Shape"};
  360. break;
  361. case Shaper<TBase>::PARAM_GAIN:
  362. ret = {-5, 5, 0, "Gain"};
  363. break;
  364. case Shaper<TBase>::PARAM_GAIN_TRIM:
  365. ret = {-1, 1, 0, "Gain trim"};
  366. break;
  367. case Shaper<TBase>::PARAM_OFFSET:
  368. ret = {-5, 5, 0, "Offset/Bias"};
  369. break;
  370. case Shaper<TBase>::PARAM_OFFSET_TRIM:
  371. ret = {-1, 1, 0, "Offset trim"};
  372. break;
  373. case Shaper<TBase>::PARAM_OVERSAMPLE:
  374. ret = {0.0f, 2.f, 0, "Oversample"};
  375. break;
  376. case Shaper<TBase>::PARAM_ACDC:
  377. ret = {0.0f, 1.f, 0, "AC/DC"};
  378. break;
  379. default:
  380. assert(false);
  381. }
  382. return ret;
  383. }