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