/* Simple Karplus-Strong class in C++ by Pieter Suurmond, 2004. The implementation in ks.cxx. COPYLEFT Diagram of our 'dirty tunable' Karplus-Strong system, meaning that tuning can be done more elegantly (with an allpass filter), here, parameter (C) also influences timbre a bit. (C) | v ---------- --------> x ---- (gain) | | | | | | -1 v | -1 v v x[n] ---> + ---> z --> + ----> z --> x --> + --> x ---> y[n] ^ ^ | | | | | (1-C) | | -(L) | ----- z <------------------------------- STABILITY ========= The absolute value of parameter (gain) must be less than a half (-0.5 < gain < +0.5) to ensure the system's stability. Furthermore, parameter (C) is expected to be between zero and one (0.0 <= C <= 1.00). TUNING ====== In this sytem, the fundamental wavelength is (a possibly fractional) number of samples, let's say W, and the total closed-loop delay is deter- mined by all the individual delays in the loop, that is W = L + 0.5 + (1-C) The lowpass filter on the left has a fixed delay of a half (0.5) sample. The lowpass filter on the right has a tunable delay, between zero (0.0) and one (1.0) sample. Of course, adjusting parameter C also affects the timbre of the system, but in practice, this appears to be hardly noticeble. Given a requested (fractional) wavelength of W samples, we calculate parameters L and C: double W; int L = (int)(W - 0.5); double C = 1.0 - (W - (double)L - 0.5); So, for example: W: L: fxd: 1-C: C: 100.0 --> 99 + 0.5 + 0.5. 0.5 100.1 --> 99 + 0.5 + 0.6. 0.4 100.4 --> 99 + 0.5 + 0.9. 0.1 100.5 --> 100 + 0.5 + 0.0. 1.0 (or --> 99 + 0.5 + 1.0. 0.0) 100.6 --> 100 + 0.5 + 0.1. 0.9 100.9 --> 100 + 0.5 + 0.4. 0.6 */ class KS { public: KS(double length); // Constructor. Creates and clears buffers. ~KS(); // Destructor. Releases buffers. double process(double x); // Method to call to process one sample. private: int del_length; // What we call (L) in the diagram above. int max_length; // Actual allocated size of the large delay buffer, in samples. double* delay; // Pointer to start of our large delay buffer. int bmask; // Bitmask to use in ALL reading and writing from and to delay. int n; // Index in our large delay buffer, to be bitmasked by 'bmask'. // Coefficients for tunable lowpass filter (on the rightside). double c0; // What we call (C) above. double c1; // What we call (1-C) above. double lp_fixed; // Very short buffer for the fixed lowpass filter (leftside). double lp_tuned; // Very short buffer for the tunable lowpass filter. double gain; double energy_accumulated; // For non-linear extension of the simple model: }; // string gets loose after a while, and looser...