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.

373 lines
11KB

  1. #include "asserts.h"
  2. #include "ExtremeTester.h"
  3. #include "VocalAnimator.h"
  4. #include "TestComposite.h"
  5. #include "VocalFilter.h"
  6. #include "FormantTables2.h"
  7. using Animator = VocalAnimator<TestComposite>;
  8. /**
  9. * Verify no output with no input.
  10. */
  11. static void test0()
  12. {
  13. Animator anim;
  14. anim.setSampleRate(44100);
  15. anim.init();
  16. anim.outputs[Animator::AUDIO_OUTPUT].value = 0;
  17. anim.step(); // prime it
  18. // with no input, should have no output
  19. for (int i = 0; i < 50; ++i) {
  20. anim.step();
  21. assert(anim.outputs[Animator::AUDIO_OUTPUT].value == 0);
  22. }
  23. }
  24. /**
  25. * Verify output with input.
  26. */
  27. static void test1()
  28. {
  29. Animator anim;
  30. anim.setSampleRate(44100);
  31. anim.init();
  32. anim.outputs[Animator::AUDIO_OUTPUT].value = 0;
  33. anim.inputs[Animator::AUDIO_INPUT].value = 1;
  34. anim.step(); // prime it
  35. // with input, should have output
  36. for (int i = 0; i < 50; ++i) {
  37. anim.step();
  38. assert(anim.outputs[Animator::AUDIO_OUTPUT].value != 0);
  39. }
  40. }
  41. /**
  42. * Verify filter settings with no mod.
  43. */
  44. static void test2()
  45. {
  46. Animator anim;
  47. anim.setSampleRate(44100);
  48. anim.init();
  49. for (int i = 0; i < 4; ++i) {
  50. float freq = anim.normalizedFilterFreq[i] * 44100;
  51. assertEQ(freq, anim.nominalFilterCenterHz[i]);
  52. }
  53. }
  54. /**
  55. * Verify filter settings respond to Fc.
  56. */
  57. static void test3()
  58. {
  59. Animator anim;
  60. anim.setSampleRate(44100);
  61. anim.init();
  62. anim.params[anim.FILTER_FC_PARAM].value = 0;
  63. anim.step();
  64. for (int i = 0; i < 4; ++i) {
  65. // assert(anim.filterFrequency[i] == anim.nominalFilterCenter[i]);
  66. float freq = anim.normalizedFilterFreq[i] * 44100;
  67. assertClose(freq, anim.nominalFilterCenterHz[i], 1);
  68. }
  69. anim.params[anim.FILTER_FC_PARAM].value = 1;
  70. anim.step();
  71. // assert that when we shift up, the expected values shift up
  72. for (int i = 0; i < 4; ++i) {
  73. float freq = anim.normalizedFilterFreq[i] * 44100;
  74. //printf("i=%d, freq=%f, nominal=%f\n", i, freq, anim.nominalFilterCenterHz[i]);
  75. if (i == 3) {
  76. assertClose(freq, anim.nominalFilterCenterHz[i], 1);
  77. } else
  78. assert(freq > anim.nominalFilterCenterHz[i]);
  79. }
  80. #if 0
  81. anim.params[anim.FILTER_FC_PARAM].value = -1;
  82. anim.step();
  83. for (int i = 0; i < 4; ++i) {
  84. if (i == 3)
  85. assert(anim.filterFrequency[i] == anim.nominalFilterCenter[i]);
  86. else
  87. assert(anim.filterFrequency[i] < anim.nominalFilterCenter[i]);
  88. }
  89. #endif
  90. }
  91. static void testScalers()
  92. {
  93. Animator anim;
  94. anim.setSampleRate(44100);
  95. anim.init();
  96. // cv/knob, trim
  97. // cases with no CV
  98. assertClose(.5, anim.scale0_1(0, 0, 1), .001); // knob half, full trim
  99. assertClose(.5, anim.scale0_1(0, 0, -1), .001); // knob half, full neg trim
  100. assertClose(1, anim.scale0_1(0, 5, 0), .001); // knob full
  101. assertClose(0, anim.scale0_1(0, -5, 0), .001); // knob down full
  102. assertClose(.75, anim.scale0_1(0, (5.0f * .5f), 0), .001); // knob 3/4
  103. // CV, no knob
  104. assertClose(1, anim.scale0_1(5, 0, 1), .001); // full cv, untrimmed
  105. assertClose(0, anim.scale0_1(-5, 0, 1), .001); // full cv, untrimmed
  106. assertClose(.25, anim.scale0_1((-5.0f * .5f), 0, 1), .001); // 3/4 cv, untrimmed
  107. // assertClose(.75, anim.scale0_1(5, 0, .5f), .001); // full cv, half trim
  108. assertClose(0, anim.scale0_1(5, 0, -1), .001); // full cv, full neg trim
  109. }
  110. #if 0
  111. static void dump(const char * msg, const Animator& anim)
  112. {
  113. std::cout << "dumping " << msg << "\nfiltFreq"
  114. << " " << std::pow(2, anim.filterFrequencyLog[0])
  115. << " " << std::pow(2, anim.filterFrequencyLog[1])
  116. << " " << std::pow(2, anim.filterFrequencyLog[2])
  117. << " " << std::pow(2, anim.filterFrequencyLog[3])
  118. << std::endl;
  119. }
  120. static void x()
  121. {
  122. Animator anim;
  123. anim.setSampleRate(44100);
  124. anim.init();
  125. anim.params[anim.FILTER_FC_PARAM].value = 0;
  126. anim.step();
  127. dump("init", anim);
  128. // TODO: assert here
  129. anim.params[anim.FILTER_FC_PARAM].value = 5;
  130. anim.step();
  131. dump("fc 5", anim);
  132. anim.params[anim.FILTER_FC_PARAM].value = -5;
  133. anim.step();
  134. dump("fc -5", anim);
  135. std::cout << "\nabout to modulate up. maxLFO, def depth\n";
  136. anim.params[anim.FILTER_FC_PARAM].value = 0;
  137. anim.params[anim.FILTER_MOD_DEPTH_PARAM].value = 0;
  138. anim.jamModForTest = true;
  139. anim.modValueForTest = 5;
  140. anim.step();
  141. dump("max up def", anim);
  142. std::cout << "\nabout to modulate up. minLFO, def depth\n";
  143. anim.params[anim.FILTER_FC_PARAM].value = 0;
  144. anim.params[anim.FILTER_MOD_DEPTH_PARAM].value = 0;
  145. anim.jamModForTest = true;
  146. anim.modValueForTest = -5;
  147. anim.step();
  148. dump("max down def", anim);
  149. std::cout << "\nabout to modulate up. maxLFO, max depth\n";
  150. anim.params[anim.FILTER_FC_PARAM].value = 0;
  151. anim.params[anim.FILTER_MOD_DEPTH_PARAM].value = 5;
  152. anim.jamModForTest = true;
  153. anim.modValueForTest = 5;
  154. anim.step();
  155. dump(" modulate up. maxLFO, max depthf", anim);
  156. std::cout << "\nabout to modulate down. minLFO, max depth\n";
  157. anim.params[anim.FILTER_FC_PARAM].value = 0;
  158. anim.params[anim.FILTER_MOD_DEPTH_PARAM].value = 5;
  159. anim.jamModForTest = true;
  160. anim.modValueForTest = -5;
  161. anim.step();
  162. dump(" modulate up. maxLFO, max depthf", anim);
  163. #if 0
  164. // TODO: would be nice to be able to inject an LFO voltage
  165. anim.params[anim.FILTER_FC_PARAM].value = 0;
  166. anim.params[anim.FILTER_MOD_DEPTH_PARAM].value = 5;
  167. for (int i = 0; i < 40000; ++i) {
  168. anim.step();
  169. }
  170. dump("fc 0 depth 1", anim);
  171. std::cout << "about to to depth -\n";
  172. // TODO: would be nice to be able to inject an LFO voltage
  173. anim.params[anim.FILTER_FC_PARAM].value = 0;
  174. anim.params[anim.FILTER_MOD_DEPTH_PARAM].value = -5;
  175. for (int i = 0; i < 4000; ++i) {
  176. anim.step();
  177. }
  178. dump("fc 0 depth -5", anim);
  179. #endif
  180. }
  181. #endif
  182. /**
  183. *Interpolates the frequency using lookups
  184. * @param model = 0(bass) 1(tenor) 2(countertenor) 3(alto) 4(soprano)
  185. * @param index = 0..4 (formant F1..F5)
  186. * @param vowel is the continuous index into the per / vowel lookup tables(0..4)
  187. * 0 = a, 1 = e, 2 = i, 3 = o 4 = u
  188. */
  189. //float getLogFrequency(int model, int index, float vowel)
  190. static void testFormantTables()
  191. {
  192. FormantTables2 ff;
  193. float x = ff.getLogFrequency(0, 0, 0);
  194. assert(x > 0);
  195. x = ff.getNormalizedBandwidth(0, 0, 0);
  196. assert(x > 0);
  197. x = ff.getGain(0, 0, 0);
  198. #if 1 // store DB, not gain
  199. assert(x <= 0);
  200. assert(x >= -62);
  201. #else
  202. assert(x > 0)
  203. #endif
  204. // spot check a few freq
  205. // formant F2 of alto, 'u'
  206. x = ff.getLogFrequency(3, 1, 4);
  207. assertClose(x, std::log2(700), .0001);
  208. // formant F3 of soprano, 'o'
  209. x = ff.getLogFrequency(4, 2, 3);
  210. assertClose(x, std::log2(2830), .0001);
  211. }
  212. static void testFormantTables2()
  213. {
  214. FormantTables2 ff;
  215. for (int model = 0; model < FormantTables2::numModels; ++model) {
  216. for (int formantBand = 0; formantBand < FormantTables2::numFormantBands; ++formantBand) {
  217. for (int vowel = 0; vowel < FormantTables2::numVowels; ++vowel) {
  218. const float f = ff.getLogFrequency(model, formantBand, float(vowel));
  219. // check that the frequencies are possible formants
  220. assert(std::pow(2, f) > 100);
  221. assert(std::pow(2, f) < 5500);
  222. const float nBw = ff.getNormalizedBandwidth(model, formantBand, float(vowel));
  223. assert(nBw < .5);
  224. assert(nBw > .01);
  225. // db now
  226. const float gain = ff.getGain(model, formantBand, float(vowel));
  227. assertLE(gain, 0);
  228. assertGT(gain, -70);
  229. // assertLE(gain, 1);
  230. // assert(gain > 0);
  231. }
  232. }
  233. }
  234. }
  235. static void testVocalFilter()
  236. {
  237. VocalFilter<TestComposite> vf;
  238. vf.setSampleRate(44100);
  239. vf.init();
  240. vf.outputs[VocalFilter<TestComposite>::AUDIO_OUTPUT].value = 0;
  241. vf.inputs[VocalFilter<TestComposite>::AUDIO_INPUT].value = 1;
  242. vf.step(); // prime it
  243. // with input, should have output
  244. for (int i = 0; i < 50; ++i) {
  245. vf.step();
  246. assert(vf.outputs[VocalFilter<TestComposite>::AUDIO_OUTPUT].value != 0);
  247. }
  248. }
  249. static void testInputExtremes()
  250. {
  251. VocalAnimator<TestComposite> va;
  252. va.setSampleRate(44100);
  253. va.init();
  254. using fp = std::pair<float, float>;
  255. std::vector< std::pair<float, float> > paramLimits;
  256. paramLimits.resize(va.NUM_PARAMS);
  257. paramLimits[va.LFO_RATE_PARAM] = fp(-5.0f, 5.0f);
  258. // paramLimits[va.LFO_SPREAD_PARAM] = fp(-5.0f, 5.0f);
  259. paramLimits[va.FILTER_FC_PARAM] = fp(-5.0f, 5.0f);
  260. paramLimits[va.FILTER_Q_PARAM] = fp(-5.0f, 5.0f);
  261. paramLimits[va.FILTER_MOD_DEPTH_PARAM] = fp(-5.0f, 5.0f);
  262. paramLimits[va.LFO_RATE_TRIM_PARAM] = fp(-1.0f, 1.0f);
  263. paramLimits[va.FILTER_Q_TRIM_PARAM] = fp(-1.0f, 1.0f);
  264. paramLimits[va.FILTER_FC_TRIM_PARAM] = fp(-1.0f, 1.0f);
  265. paramLimits[va.FILTER_MOD_DEPTH_TRIM_PARAM] = fp(-1.0f, 1.0f);
  266. paramLimits[va.BASS_EXP_PARAM] = fp(0.f, 1.0f);
  267. paramLimits[va.TRACK_EXP_PARAM] = fp(0.f, 2.0f);
  268. paramLimits[va.LFO_MIX_PARAM] = fp(0.f, 1.0f);
  269. // TODO: why is output going so high?
  270. ExtremeTester< VocalAnimator<TestComposite>>::test(va, paramLimits, false, "vocal animator");
  271. }
  272. static void testVocalExtremes()
  273. {
  274. VocalFilter<TestComposite> va;
  275. va.setSampleRate(44100);
  276. va.init();
  277. using fp = std::pair<float, float>;
  278. std::vector< std::pair<float, float> > paramLimits;
  279. paramLimits.resize(va.NUM_PARAMS);
  280. paramLimits[va.FILTER_Q_PARAM] = fp(-5.0f, 5.0f);
  281. paramLimits[va.FILTER_Q_TRIM_PARAM] = fp(-1.0f, 1.0f);
  282. paramLimits[va.FILTER_FC_PARAM] = fp(-5.0f, 5.0f);
  283. paramLimits[va.FILTER_FC_TRIM_PARAM] = fp(-1.0f, 1.0f);
  284. paramLimits[va.FILTER_VOWEL_PARAM] = fp(-5.f, 5.0f);
  285. paramLimits[va.FILTER_VOWEL_TRIM_PARAM] = fp(-1.f, 1.0f);
  286. paramLimits[va.FILTER_MODEL_SELECT_PARAM] = fp(0.f, 4.0f);
  287. paramLimits[va.FILTER_BRIGHTNESS_PARAM] = fp(-5.f, 5.0f);
  288. paramLimits[va.FILTER_BRIGHTNESS_TRIM_PARAM] = fp(-1.0f, 1.0f);
  289. ExtremeTester< VocalFilter<TestComposite>>::test(va, paramLimits, false, "vocal filter");
  290. }
  291. void testVocalAnimator()
  292. {
  293. test0();
  294. test1();
  295. test2();
  296. test3();
  297. testScalers();
  298. testFormantTables();
  299. testFormantTables2();
  300. testVocalFilter();
  301. #if defined(_DEBUG) && true
  302. printf("skipping extremes\n");
  303. #else
  304. testVocalExtremes();
  305. testInputExtremes();
  306. #endif
  307. }