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.

384 lines
9.1KB

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