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.

589 lines
15KB

  1. #pragma once
  2. // Need to make this compile in MS tools for unit tests
  3. #if defined(_MSC_VER)
  4. #define __attribute__(x)
  5. #pragma warning (push)
  6. #pragma warning ( disable: 4244 4267 )
  7. #endif
  8. #ifndef _CLAMP
  9. #define _CLAMP
  10. namespace std {
  11. inline float clamp(float v, float lo, float hi)
  12. {
  13. assert(lo < hi);
  14. #define sMIN(a,b) (((a)>(b))?(b):(a))
  15. #define sMAX(a,b) (((a)>(b))?(a):(b))
  16. //return std::min(hi, std::max(v, lo));
  17. return sMIN(hi, sMAX(v, lo));
  18. #undef sMIN
  19. #undef sMAX
  20. }
  21. }
  22. #endif
  23. //#include "math.hpp"
  24. #include "dsp/minblep.hpp"
  25. #include "dsp/filter.hpp"
  26. #include "AudioMath.h"
  27. #include "ObjectCache.h"
  28. #include <functional>
  29. // until c++17
  30. #ifndef _CLAMP
  31. #define _CLAMP
  32. namespace std {
  33. inline float clamp(float v, float lo, float hi)
  34. {
  35. assert(lo < hi);
  36. return std::min(hi, std::max(v, lo));
  37. }
  38. }
  39. #endif
  40. /* VCO core using MinBLEP to reduce aliasing.
  41. * Originally based on Befaco EvenVCO
  42. */
  43. class MinBLEPVCO
  44. {
  45. public:
  46. friend class TestMB;
  47. /**
  48. * ph is the "phase (-1..0)"
  49. */
  50. using SyncCallback = std::function<void(float p)>;
  51. MinBLEPVCO();
  52. enum class Waveform
  53. {
  54. Sin, Tri, Saw, Square, Even, END
  55. };
  56. void step();
  57. void setNormalizedFreq(float f, float st)
  58. {
  59. normalizedFreq = std::clamp(f, 1e-6f, 0.5f);
  60. sampleTime = st;
  61. }
  62. void setWaveform(Waveform);
  63. float getOutput() const
  64. {
  65. return output;
  66. }
  67. /**
  68. * Send the sync waveform to VCO.
  69. * usually called from outside.
  70. */
  71. void onMasterSync(float phase);
  72. void setSyncCallback(SyncCallback);
  73. void setPulseWidth(float);
  74. void setSyncEnabled(bool f)
  75. {
  76. syncEnabled = f;
  77. }
  78. private:
  79. float output = 0;
  80. Waveform waveform = Waveform::Saw;
  81. float phase = 0.0;
  82. float normalizedFreq = 0;
  83. float sampleTime = 0;
  84. SyncCallback syncCallback = nullptr;
  85. float tri = 0;
  86. bool syncEnabled = false;
  87. bool gotSyncCallback = false;
  88. float syncCallbackCrossing = 0;
  89. /**
  90. * References to shared lookup tables.
  91. * Destructor will free them automatically.
  92. */
  93. std::shared_ptr<LookupTableParams<float>> sinLookup = {ObjectCache<float>::getSinLookup()};
  94. /** Whether we are past the pulse width already */
  95. bool halfPhase = false;
  96. int loopCounter = 0; // still used?
  97. float pulseWidth = .5;
  98. rack::MinBLEP<16> syncMinBLEP;
  99. rack::MinBLEP<16> aMinBLEP;
  100. rack::MinBLEP<16> bMinBLEP;
  101. bool aIsNext = false;
  102. rack::MinBLEP<16>* getNextMinBLEP();
  103. /**
  104. * Waveform generation helper
  105. */
  106. void step_even();
  107. void step_saw();
  108. void step_sq();
  109. void step_sin();
  110. void step_tri();
  111. /**
  112. * input = phase, 0..1
  113. * output = sin(2pi * input)
  114. */
  115. float sineLook(float input) const;
  116. float evenLook(float input) const;
  117. std::string name;
  118. bool lastSq = false;
  119. bool isSqHigh() const;
  120. };
  121. inline MinBLEPVCO::MinBLEPVCO()
  122. {
  123. syncMinBLEP.minblep = rack::minblep_16_32;
  124. syncMinBLEP.oversample = 32;
  125. aMinBLEP.minblep = rack::minblep_16_32;
  126. aMinBLEP.oversample = 32;
  127. bMinBLEP.minblep = rack::minblep_16_32;
  128. bMinBLEP.oversample = 32;
  129. }
  130. inline rack::MinBLEP<16>* MinBLEPVCO::getNextMinBLEP()
  131. {
  132. aIsNext = !aIsNext;
  133. return aIsNext ? &aMinBLEP : &bMinBLEP;
  134. }
  135. inline void MinBLEPVCO::setSyncCallback(SyncCallback cb)
  136. {
  137. assert(!syncCallback);
  138. syncCallback = cb;
  139. }
  140. inline void MinBLEPVCO::setWaveform(Waveform wf)
  141. {
  142. waveform = wf;
  143. }
  144. inline void MinBLEPVCO::setPulseWidth(float pw)
  145. {
  146. pulseWidth = pw;
  147. }
  148. inline void MinBLEPVCO::step()
  149. {
  150. // call the dedicated dispatch routines for the special case waveforms.
  151. switch (waveform) {
  152. case Waveform::Saw:
  153. step_saw();
  154. break;
  155. case Waveform::Square:
  156. step_sq();
  157. break;
  158. case Waveform::Sin:
  159. step_sin();
  160. break;
  161. case Waveform::Tri:
  162. // Tri sync doesn't work -> use sin
  163. if (syncEnabled) {
  164. step_sin();
  165. } else {
  166. step_tri();
  167. }
  168. break;
  169. case Waveform::Even:
  170. step_even();
  171. break;
  172. case Waveform::END:
  173. output = 0;
  174. break; // don't do anything if no outputs
  175. default:
  176. assert(false);
  177. }
  178. }
  179. // callback from master sync when it rolls over
  180. inline void MinBLEPVCO::onMasterSync(float masterPhase)
  181. {
  182. gotSyncCallback = true;
  183. syncCallbackCrossing = masterPhase;
  184. }
  185. inline void MinBLEPVCO::step_saw()
  186. {
  187. phase += normalizedFreq;
  188. const float predictedPhase = phase;
  189. if (gotSyncCallback) {
  190. const float excess = -syncCallbackCrossing * normalizedFreq;
  191. // Figure out where our sub-sample phase should be after reset
  192. // reset to zero
  193. const float newPhase = excess;
  194. phase = newPhase;
  195. }
  196. if (phase >= 1.0) {
  197. phase -= 1.0;
  198. }
  199. // see if we jumped
  200. if (phase != predictedPhase) {
  201. const float jump = phase - predictedPhase;
  202. // printf("%s jump = %f\n", name.c_str(), jump); fflush(stdout);
  203. if (gotSyncCallback) {
  204. const float crossing = syncCallbackCrossing;
  205. syncMinBLEP.jump(crossing, jump);
  206. if (syncCallback) {
  207. syncCallback(crossing);
  208. }
  209. } else {
  210. // phase overflowed
  211. const float crossing = -phase / normalizedFreq;
  212. aMinBLEP.jump(crossing, jump);
  213. if (syncCallback) {
  214. syncCallback(crossing);
  215. }
  216. }
  217. }
  218. float totalPhase = phase;
  219. totalPhase += aMinBLEP.shift();
  220. totalPhase += syncMinBLEP.shift();
  221. float saw = -1.0 + 2.0 * totalPhase;
  222. output = 5.0*saw;
  223. gotSyncCallback = false;
  224. }
  225. inline bool MinBLEPVCO::isSqHigh() const
  226. {
  227. return phase >= pulseWidth;
  228. }
  229. inline void MinBLEPVCO::step_sq()
  230. {
  231. bool phaseDidOverflow = false;
  232. phase += normalizedFreq;
  233. if (gotSyncCallback) {
  234. const float excess = -syncCallbackCrossing * normalizedFreq;
  235. // reset phase to near zero on sync
  236. phase = excess;
  237. }
  238. if (phase > 1.0f) {
  239. phase -= 1.0f;
  240. phaseDidOverflow = true;
  241. }
  242. // now examine for any pending edges,
  243. // and if found apply minBLEP and
  244. // send sync signal
  245. bool newSq = isSqHigh();
  246. if (newSq != lastSq) {
  247. lastSq = newSq;
  248. const float jump = newSq ? 2 : -2;
  249. if (gotSyncCallback) {
  250. const float crossing = syncCallbackCrossing;
  251. syncMinBLEP.jump(crossing, jump);
  252. if (syncCallback) {
  253. syncCallback(crossing);
  254. }
  255. } else if (phaseDidOverflow) {
  256. const float crossing = -phase / normalizedFreq;
  257. aMinBLEP.jump(crossing, jump);
  258. if (syncCallback) {
  259. syncCallback(crossing);
  260. }
  261. } else {
  262. // crossed PW boundary
  263. const float crossing = -(phase - pulseWidth) / normalizedFreq;
  264. bMinBLEP.jump(crossing, jump);
  265. }
  266. }
  267. float square = newSq ? 1.0f : -1.0f;
  268. square += aMinBLEP.shift();
  269. square += bMinBLEP.shift();
  270. square += syncMinBLEP.shift();
  271. output = 5.0*square;
  272. gotSyncCallback = false;
  273. }
  274. inline float MinBLEPVCO::sineLook(float input) const
  275. {
  276. // want cosine, but only have sine lookup
  277. float adjPhase = input + .25f;
  278. if (adjPhase >= 1) {
  279. adjPhase -= 1;
  280. }
  281. return -LookupTable<float>::lookup(*sinLookup, adjPhase, true);
  282. }
  283. inline void MinBLEPVCO::step_sin()
  284. {
  285. if (gotSyncCallback) {
  286. gotSyncCallback = false;
  287. // All calculations based on slave sync discontinuity happening at
  288. // the same sub-sample as the mater discontinuity.
  289. // First, figure out how much excess phase we are going to have after reset
  290. const float excess = -syncCallbackCrossing * normalizedFreq;
  291. // Figure out where our sub-sample phase should be after reset
  292. const float newPhase = .5 + excess;
  293. const float oldOutput = sineLook(phase);
  294. const float newOutput = sineLook(newPhase);
  295. const float jump = newOutput - oldOutput;
  296. syncMinBLEP.jump(syncCallbackCrossing, jump);
  297. this->phase = newPhase;
  298. // return;
  299. } else {
  300. phase += normalizedFreq;
  301. // Reset phase if at end of cycle
  302. if (phase >= 1.0) {
  303. phase -= 1.0;
  304. if (syncCallback) {
  305. float crossing = -phase / normalizedFreq;
  306. syncCallback(crossing);
  307. }
  308. }
  309. }
  310. float sine = sineLook(phase);
  311. sine += syncMinBLEP.shift();
  312. output = 5.0*sine;
  313. }
  314. inline void MinBLEPVCO::step_tri()
  315. {
  316. if (gotSyncCallback) {
  317. gotSyncCallback = false;
  318. // All calculations based on slave sync discontinuity happening at
  319. // the same sub-sample as the mater discontinuity.
  320. // First, figure out how much excess phase we are going to have after reset
  321. const float excess = -syncCallbackCrossing * normalizedFreq;
  322. // Figure out where our sub-sample phase should be after reset
  323. const float newPhase = .5 + excess;
  324. const float jump = -2.f * (phase - newPhase);
  325. #ifdef _LOG
  326. printf("%s: got sync ph=%.2f nph=%.2f excess=%.2f send cross %.2f jump %.2f \n", name.c_str(),
  327. phase, newPhase,
  328. excess,
  329. syncCallbackCrossing, jump);
  330. #endif
  331. syncMinBLEP.jump(syncCallbackCrossing, jump);
  332. this->phase = newPhase;
  333. return;
  334. }
  335. float oldPhase = phase;
  336. phase += normalizedFreq;
  337. if (oldPhase < 0.5 && phase >= 0.5) {
  338. const float crossing = -(phase - 0.5) / normalizedFreq;
  339. aMinBLEP.jump(crossing, 2.0);
  340. }
  341. // Reset phase if at end of cycle
  342. if (phase >= 1.0) {
  343. phase -= 1.0;
  344. float crossing = -phase / normalizedFreq;
  345. aMinBLEP.jump(crossing, -2.0);
  346. halfPhase = false;
  347. if (syncCallback) {
  348. syncCallback(crossing);
  349. }
  350. }
  351. // Outputs
  352. float triSquare = (phase < 0.5) ? -1.0 : 1.0;
  353. triSquare += aMinBLEP.shift();
  354. triSquare += syncMinBLEP.shift();
  355. // Integrate square for triangle
  356. tri += 4.0 * triSquare * normalizedFreq;
  357. tri *= (1.0 - 40.0 * sampleTime);
  358. // Set output
  359. output = 5.0*tri;
  360. }
  361. inline float calcDoubleSaw(float phase)
  362. {
  363. return (phase < 0.5) ? (-1.0 + 4.0*phase) : (-1.0 + 4.0*(phase - 0.5));
  364. }
  365. inline float MinBLEPVCO::evenLook(float input) const
  366. {
  367. float doubleSaw = calcDoubleSaw(input);
  368. const float sine = sineLook(input);
  369. const float even = 0.55 * (doubleSaw + 1.27 * sine);
  370. return even;
  371. }
  372. inline void MinBLEPVCO::step_even()
  373. {
  374. float oldPhase = phase;
  375. phase += normalizedFreq;
  376. float syncJump = 0;
  377. if (gotSyncCallback) {
  378. // All calculations based on slave sync discontinuity happening at
  379. // the same sub-sample as the mater discontinuity.
  380. // First, figure out how much excess phase we are going to have after reset
  381. const float excess = -syncCallbackCrossing * normalizedFreq;
  382. // Figure out where our sub-sample phase should be after reset
  383. const float newPhase = .5 + excess;
  384. // const float jump = -2.f * (phase - newPhase);
  385. #ifdef _LOG
  386. printf("%s: got sync ph=%.2f nph=%.2f excess=%.2f send cross %.2f jump %.2f \n", name.c_str(),
  387. phase, newPhase,
  388. excess,
  389. syncCallbackCrossing, jump);
  390. #endif
  391. // syncMinBLEP.jump(syncCallbackCrossing, jump);
  392. syncJump = evenLook(newPhase) - evenLook(this->phase);
  393. this->phase = newPhase;
  394. }
  395. bool jump5 = false;
  396. bool jump1 = false;
  397. if (oldPhase < 0.5 && phase >= 0.5) {
  398. jump5 = true;
  399. }
  400. // Reset phase if at end of cycle
  401. if (phase >= 1.0) {
  402. phase -= 1.0;
  403. jump1 = true;
  404. }
  405. if (gotSyncCallback) {
  406. const float crossing = syncCallbackCrossing;
  407. // FIXME!!
  408. float jump = syncJump;
  409. syncMinBLEP.jump(crossing, jump);
  410. if (syncCallback) {
  411. syncCallback(crossing);
  412. }
  413. } else if (jump1) {
  414. const float jump = -2;
  415. float crossing = -phase / normalizedFreq;
  416. aMinBLEP.jump(crossing, jump);
  417. if (syncCallback) {
  418. syncCallback(crossing);
  419. }
  420. } else if (jump5) {
  421. const float jump = -2;
  422. const float crossing = -(phase - 0.5) / normalizedFreq;
  423. aMinBLEP.jump(crossing, jump);
  424. }
  425. // note that non-sync minBLEP is added to double saw,
  426. // but for sync it's added to even.
  427. const float sine = sineLook(phase);
  428. float doubleSaw = (phase < 0.5) ? (-1.0 + 4.0*phase) : (-1.0 + 4.0*(phase - 0.5));
  429. doubleSaw += aMinBLEP.shift();
  430. float even = 0.55 * (doubleSaw + 1.27 * sine);
  431. even += syncMinBLEP.shift();
  432. output = 5.0*even;
  433. gotSyncCallback = false;
  434. }
  435. #if 0 // old way
  436. inline void MinBLEPVCO::step_even()
  437. {
  438. if (gotSyncCallback) {
  439. gotSyncCallback = false;
  440. // All calculations based on slave sync discontinuity happening at
  441. // the same sub-sample as the mater discontinuity.
  442. // First, figure out how much excess phase we are going to have after reset
  443. const float excess = -syncCallbackCrossing * normalizedFreq;
  444. // Figure out where our sub-sample phase should be after reset
  445. const float newPhase = .5 + excess;
  446. const float jump = -2.f * (phase - newPhase);
  447. #ifdef _LOG
  448. printf("%s: got sync ph=%.2f nph=%.2f excess=%.2f send cross %.2f jump %.2f \n", name.c_str(),
  449. phase, newPhase,
  450. excess,
  451. syncCallbackCrossing, jump);
  452. #endif
  453. syncMinBLEP.jump(syncCallbackCrossing, jump);
  454. this->phase = newPhase;
  455. return;
  456. }
  457. float oldPhase = phase;
  458. phase += normalizedFreq;
  459. if (oldPhase < 0.5 && phase >= 0.5) {
  460. float crossing = -(phase - 0.5) / normalizedFreq;
  461. aMinBLEP.jump(crossing, -2.0);
  462. }
  463. // Reset phase if at end of cycle
  464. if (phase >= 1.0) {
  465. phase -= 1.0;
  466. float crossing = -phase / normalizedFreq;
  467. aMinBLEP.jump(crossing, -2.0);
  468. if (syncCallback) {
  469. syncCallback(crossing);
  470. }
  471. }
  472. //sine = -cosf(2*AudioMath::Pi * phase);
  473. // want cosine, but only have sine lookup
  474. float adjPhase = phase + .25f;
  475. if (adjPhase >= 1) {
  476. adjPhase -= 1;
  477. }
  478. const float sine = -LookupTable<float>::lookup(*sinLookup, adjPhase, true);
  479. float doubleSaw = (phase < 0.5) ? (-1.0 + 4.0*phase) : (-1.0 + 4.0*(phase - 0.5));
  480. doubleSaw += aMinBLEP.shift();
  481. doubleSaw += syncMinBLEP.shift();
  482. const float even = 0.55 * (doubleSaw + 1.27 * sine);
  483. //TBase::outputs[SINE_OUTPUT].value = 5.0*sine;
  484. output = 5.0*even;
  485. }
  486. #endif
  487. #if defined(_MSC_VER)
  488. #pragma warning (pop)
  489. #endif