How to compensate for onepole~ delay?
Last week was my first exposure to Pure Data. Yesterday I downloaded Max. I’m a C++ programmer by profession (computer games) with a strong interest in experimental sound synthesis.
For my fist experiment, I’m tinkering with the Karplus-Strong algorithm. I’m sure most of you are familiar with it. It’s a tapin~/tapout~ delay with a onepole~ in the feedback loop. You have seen it a thousand times.
I noticed that when I reduce the cutoff frequency of the onepole~, the pitch goes down, and my notes sound flat. This is especially noticeable in the higher registers.
I speculate that the onepole~ introduces an additional delay, that gets longer as the cut off frequency gets lower.
Am I right? If that is the case, I think I could keep the pitch stable by reducing the length of the tapin~/tapout~ to compensate for the delay that is introduced by the onepole~.
But that would require me to compute the delay that is introduced by the onepole~ filter, as a function of the cut off frequency. How do I do that?
the onepole delay would not change– by definition and design it never changes– but there would be artifacts to do with tapin/out sub-sample interpoloation algorithm (or lack thereof) and other artifacts to do with the feedback delay limited by the signal vector size (which you can change). if you’re using max 6 then using gen to get around this particular problem. A compensatory delay is not going to solve it.
Maybe post an example patch to illustrate the cutoff freq of the onepole affecting pitch– I suspect it’s more like a timbral change affecting the perception of pitch change
Ok, I’ll try to post an example. (Never done this before… hope this works)
----------begin_max5_patcher---------- 795.3ocyXssbaBCD8Y3qfgG63lAILwPeqeGc5jQFjIJEjX.4T2jI7sWcAHzF Kt3PH9EwvJwtm8rqVshmssb2yNgqbc9lyObrrd11xRIRJvp4cK2bzo3LTkZY twr7bLk6tQOGGehqjWwQkcROvn7JxSX4Lvs230HldLmPyvbkl.MBKP736Izz 6JwwbMR.dAhuwI3V4XnZzWnEme17IjDkIY6e3q9v91jhxU1z86kDTl6qlkcj 2ZWOovWrskCadmNc7QtC6vgExsgQ8b6HuAcavZ31T7uEF6MdcFCkrGQSamPa E9eJvZ+vUMYGr6yJ.3rYEefjH.AJ93V0Hzy.q3MWVAbArRNtpBkheCs.77Lx HyiMfixFg5rC3MAxWNOa.iVC1HEQn0F86JRJUZMGWhX6SGJmm2BgA5GJWeqg fOLzsSQkBmkiKuCSQ6yTXw6rNL7Bb3pBA7JYUwrBb8DB3IrbAEoEqhZh7j9t vT4B.TGy80O7Z0kA1.tbg3CYLgRL5phoQbYHdg21qB5.ecMwA22CBGOS+iMo HomkLPPc.ukTBT4As.WtFwWVbj2mXlIaoOzXa23YIqcymRZsbtNQFpP9xcfx WpE6LhhhFqLxRUDEBzoSA8Nu0Xtk+ZTE0.wvn3BVFt1Ym2.Gt7AwNvdoTFYG 3mH6vQEBEW6.uAtyeQYGvnkgaxcB2NH6.9bYmgNYVLcLiRU90BSM5DmsCSMd ehTSbFI9W0qbFSSOaaACQKqRKa3mRPw0tys+iHYyl9g5fa23Y6Ce4tBBmkll gMFpL2U43AjWqrYNd.ufyJWt7TgAKYp9EWvqZMdKlM20Z2fYpuqRaJE5lQn+ ++dPAJo7+krpXGKiac4l8INuBqDbEmPQbBi1aMxRL8Vz8jjDLsebKgTICkIl yMmJb78l.dfQqFdfSgejWXZsvS3Dvi+0Gb.qCblRx7pQNx6GLNbBuxvCb0hV .+qqZOfIk9rdat.fqqZOSCOvqK7.VQ7Lkyt.qX8mI.mK8jTceGnhhGwkUMpT ADQ6XOvJkud6Fa8O5P+pRitk3GIsq22VpsWr+KvdfSLq -----------end_max5_patcher-----------
+1 for perception theory
Definitely not perception. I added a 440 Hz reference tone to make sure I’m not imagining it. Check out my demo patch. Reference tone is on the right slider, string sound on the left. When the cut off is at 10 KHz, there is barely any beating. As you move the cut off to 3 kHz, there is a distinctly audible beating between the two frequencies, as the string resonator goes flat.
----------begin_max5_patcher---------- 910.3ocyXssbaBCE7Y6uBFdriqGcALPeqeGc5jQFTbHEjX.4zbYh+1qj.ros FP1gHyKXiPbzd1ypUR71xEta4OSqbc9lyObVr3skKVnaR0vhl6W3lSdNNiTo 6lKi9a91GcWU+HA8Ygt4ubvArtsU9dQFUHdofVGY2pzcLRlqyOa5v8blnJ8U 8ign0fllY6ySYx2TORnlFKHh3GRY6tqjFKpi2F7Zj+J4aBjupCdi5p5+GieZ hFTRf90.2NiIijqGS2uWlJwyogsFw5wEpZ78kKUWV8wHk3WhynGb77rG0fgJ xHRSLAngHlM1fXxoUUjcz+iY7.8RHSFUD3qReXj9GLZsRwDddtv2FbwNRJ6v X5fUNtoLwINPdCsrP0Gv0LMIPlzHjlAPZ0.zqG4fm6w.UJI.4ndGkQ1lowG3 rj.5JHgXddNUlf+qfnRPJEtmovi7NedC6Oug.cB6q8EB0WwdmOqwnKsxClvj Nduvge+8STZih5j10N.8l1Paj183JlwIIaIrc8NUP+vKxE.NlgHzWyGa.C4H hA2RKQHvBdhMrQHXbOQTzr0T7BxVi8+Pg1w.rpPBuRdULufdvfBdBOWRQ0Mq qZRcR2TvTtncyR35e.swpG1.Mck36y3xfzapJeLQnJwS6z950.fX+wm2CCGW o+4JJR5LR8PPGAdKoDp0As.W0G4aVrWzkXL2jz+zhFdGudVxJ3xoj1QNuVHi zHe5VPQe1innHasGaDrVN42Y81d0V3a34O3LZAWcBj.v.Kt7IwNnNRpdYGzM jcDjBYfO3fViBvSJ6.G0FtQ6D5MH6.usryPqLKebLmwz40DSM0BGugoFvs7X 8Yow+5fkULM6YyCNDsXksrQeMgDev8p14INrt3d75Y2G9zcDDAe2tLZukp92 U43EjSNa8WOPWwZkSmNUNfkb89EmviZM9VLaNqUvfJ0Oj0lNftYor+8SWpAk p8+lrp36KiaS4l4INmfUBsRjxHhTNqSeTVLc5zCoIITV25VRZkpTlzu1zT3D X.b7rFZ1X.ZBrFZ7mUbimAnAO6PCzJnACL.N9VibLBNnHqgGjIlNpuBgsvS3 rRKaLbriXFYnI37AMPqUpflrdELblgGj0pVP77Z6EPijO1apNDNubBMCOn4E dfVDOlrRJzh9OF.mqcc85iVPJJdhVV0DRMPjm35Qdo51MqVV+sLquUGQ2R5S os8GuTEs2W9GhWFmDA -----------end_max5_patcher-----------
I can’t hear any beating in your example (admittedly listening through laptop speakers) , but have a look at this adaptation of your patch: is there any beating here? Make sure your vector size is 64 or less (for 44100hz sample rate) to get the 440 hz fundamental.
I’d say the drop in pitch you hear is very much perceptual, but there might be something else going on to cause the beating…
----------begin_max5_patcher---------- 1062.3oc2YtsbaBCDF9Z6mBFeYmTO5.BP8t9bjISGBH6PqQhwHOMMYhe1qPx PvIbPFaio8FbPXK92uc0pcUdc9rEOJdlkuv4aN26La1qymMSOTw.yNb+rEog OGsILW+0VDIRSYb4h6LOSxdVZFemzQrZU43YgxnmR3q+wVVjzL+DOvRvcNTO 8U8eicWBbd3vOIIVOQhG+4W8JmlUBtjGlxzO46aSB2T9D9tzD9FlTqJ36CJ1 IKGETpksrbkjCkIBdM83FDTnAh9J3vkJ0T7lySdQ+lQJYVL5aymWb4NKQ0pM Bkf5hH95WKzmT7gAOHPyHw+hfDTOHgpgAD62KSfnkk70L+x+jwLyRggGphPb V7XHe8BmGF.6hqYWMQt.C4v5Obqt1D4B5jOZsprfrcx5CWZlA..nxPOlkvtY IAnYIE0HJagYUOWoTs+R+5G.+3reqr8OsJ8K6UpfRocgVWnNNz7AwqqfRn6I FUhFBIcAXsHHmWTYdxZtRVCJbrEbJ3rLwF1dGekexBlBgum6qUlRFSlhlbLU FlodM6cPKQ93NXpKodbZfamL8xrgROLECmpwoJllv2aALMAntcCS5XByyL.U Y4QBNWOsCAnqC6Abtzk9DUkL.jQt5skNdqHtjsMSWPRkNqgS2SXQMpmXF0Vt p21vrz1JrKWFtU1AAf.c0KAZ+TfWWk0gQWz55t.knM7pYQJGeQtbZ+UyhgSM ytkzDpRPh0kr0tYi0IHfDP+UrhAWz7D1the30blxxyCWy9DVfct2to3SjoTd LZoJY.LnYhfnWzs1skHWmLepE7E48fjVy6UyvClHI4xyT5eqHORjw5x3fHi6 zzeAFX51.zl4g5z7hEoglx4u2obhpOSM6cuPtwdaBEQnllPC5eIML3h1E5zo eRjmABXb+8SB8uxMTNc6MDAMgGDhE8FhuII6tgc4URGDwht7P+uPGa6W6PJ0 CwN8zuF7lT5vsnyqRrXBZ5oyqaSEUmaOTsflnMIQ+xBz.Mm02giPoEzP+mKf g8RbXTOEXUX1lCG1s5ZikZeBoLFxNFRw50aXV3pPUNo1cUnA3P9D4GbEfsDL lxTkEpKLyBqzz8iemAjvaR98OWfjVaK1jv+3+lGs8UL9wvJWraaT4zU1zhy6 lXLKWlv0mVSsuj6QemmRhiY758rllDmIT9rCZnEOmsRp337lXRxFHAoipjBr PR9SNEUb72NvwKTx8J33HpDDjpyDff8Z3Nf4jV8.U2ctVBwFKYBtP8CB+pqI qVp5MtZxBIMtIOJJ0reJAGWMYClJZrZpoIzTTSAiqlP136viqlv1nIvooIDj pynW1faPC2YNyTHhVc24ZI91txXD2Y0JMMtQgVEDNtJxlZhvipiyZIMhThN8 xngsYGRz3tqs6jKXxcxEKQuBI8+PG2yea9eQMzVmg -----------end_max5_patcher-----------
Yes, in your version, if (for example) you set the dials at 8k and 4k respectively, I can clearly hear that the two strings are out of tune with each other.
I tried vector size 1, and even sample rate 96k, with the same result.
I think I understand what is going on. Sorry my math skillz are poor, I don’t speak the Greek letter lingo, but I can explain in programming terms.
Consider the original KS algorithm. Remember they did not use a filter, but rather they averaged the samples at [length] and [length+1]. A poor man’s low pass filter. But averaging two adjacent samples is what I would do to linearly interpolate delay at non-integer positions. In other words, KS were reading the interpolated sample at [length+0.5]. That’s half a sample longer than the intended string length. Hardly noticeable when your string is long, but the shorter it gets, the more significant the error becomes.
And I’m sure that onepole~ does something essentially similar. I bet onepole~ has a one or two sample delay inside of it, and combines the input with the delayed samples according to some set of coefficients. That’s how a digital filter works, right? Those extra one or two samples of delay in the filter make the string just a smidgen longer.
Yup. Check out the helpfile for onepole~ and biquad~ together. (onepole~ is really just a degenerate biquad~) Onepole~ is recursive with a one-sample delay in the feedback. Onepole~ has two coefficients a0 (undelayed) and b1 (recursive).
From looking at the help file, I noticed a shift in the magnitude between the a0 and b1 coefficients around 3-4k; at that point the absolute magnitude of a0 starts to exceed that of b1. Because the feedback is more pronounced than the input before this point, the delay line is functionally longer (as in it takes several iterations for the input to affect the output substantially), and this probably accounts for what you are experiencing. (in essence, it’s not only the length of the delay (1 sample), but also the weighting)
Also, something that could (and I’m not sure that it is the culprit here, but it could…) also affect this is that digital filters introduce phase shifts. (see the onepole~ help patch) In a feedback situation this becomes more pronounced because you have a series of phase shifts from the repetitions.
KS is nice for its simplicity, but definitely has its limitations. It seems like their lowpass scheme might be a simple way of reducing the weighting issues.
I altered your patch again to have both output signals going into the spectroscope, and you can see it’s not beating due to frequency difference (where all the harmonics would beat at the same rate), but most likely to do with phase response differences, as the second partial is the one that varies in amplitude mostly.
----------begin_max5_patcher---------- 1447.3oc4Z00biZCE8YmeETOSeoapqtRHDru0eG6rSGBH6kt1fGib61cmM+1 qPRjPRLBYarLIIOXFjIv4dz8dtef+wMyleW0230yC9XvmBlM6G2LalZolElY Ne17MoeKacZs5xlWx+2p69642p+JA+aB0xhf6Btqc0sohruTTt5u1wyD5aNP vKP2F.zvlCQnET4IIKPAe17+TjqtOx68uGRZuQKqJEkoa3pu5O2Ujtt8aJ2u onbMWn.E73hU6Esqh6bSpK9t5l.f7wqWVekh+aKWiv42kVtZ9sliAet4p94M 2z7wsNxMa300oq3ufbxVyS2YibRPJxIlzbfnnHf0C4fORxAePxANRx4zHjrp Ma3khWPH4bw9RdvuZgSXjHEM.Z+F.0RMGjTfQwio052tiWKgcpnnprWHgLe7 .d5RkXIPOA9xRv0RKbUTTbCXvHPQRXaAWjjqZv0x0UohSyYpGx4CAvBqbShN bh3.2DeUhsFeN4OB.DxJqvzgUgTEqnbe5kUXuQXkeaHOEU3ClfbvSI50FmH+ WkOaqtDpHD.ClLzJEkdLe5nJhzmdK1rgDOndqRR1JocVI16wg5WTwYVCzBM4 LvJ3GxrEngGWmpdX0HJUinvgYUuFdtQTszJSp7OiYlnSaDIaTbOGjHUfHANK d7znvuVutHmuyJcoHJrltzdgTROgyjiv8pc0pkKq4pGVXrchhpqKAiNHQ8BF onTEs1b3xnzEh0JcQQCqzg8hRGgAcPzqSkNhoRcL1Aktv2HkTLflEAoJ9LNw AMKznpYcc5qKauHPpKXgQnLc1GElBRr1UWzn1T2Hzd1fJKzXs0osK6JKrKWe XWVIh7NH7PbPBsSqEgn1OODGDa0RUXUZAa2K5tbqYF2HHgrFEzqBf46kOTEI ptSiXiF2KkuSj+YilnJsdr9PD1lqBbAkKs3qTWrpT9.FS8xpR91p076CXRJ2 A1AG8nLQurC8sB6HR2JeL2GfWfYDaEvD002INzJ6Dc4xq3c1on7dGnEsSSnc ZI4pPKRaHqprTA4SgZVkNDEDtfQkoTAc0+lo77DRnTv2sUkY7Ab1cTpmRq.8 r6eNUw2WIF0hzcBayRGz695lEirUeAAO0Jv3zqqBq6tROrS6kUQfolY2S.uL WctdtwV7208MQcXlUnqRD+3+1jfAxbpHAcgnlgXF2SKGIWkDmWFkO.orUhd5 sGT3qikGOQT4p2JMfcU0YUa4VsNhd+zLudj98Eh5y7vVMu7pMo5Be+jdVC5J oeZFh+oI68Gk74wUf8EZjMlgxCgwCGnCwuQ6rBGZ11IC2YEvd+1ZEFq0.YHG Zsh7dq0pV1Q6+LPqU32asVYDZM9NCzZE7to0pVZwH5XmVPuJaspGpIacQ1Wc fZ.8XuBAaTSxqNGF92ySyFptqF6VKjXF5GsmRvOBMiSImgnZ0p0bq6UpsGF6 w2GUuu3mSXC4fuJqQ80MvkEKpxoNrQBHUuQLqNjvUQe+kUHov170EkO+Ginx 9ZV+ojUc09cYs2t1eVbAOZh47ZQQo50r04hfmdQeoHOmW1sW1ME4aqjaZFPz yV23hIpWwjSPxqHBb.QM+hP84FWyiCFBSX+iog3oD+5KE4.jvzoGl.+5gSbB SQ9ESINfIxU.SfKXB7Glhcgm7abWyOHygwT7zKty29STWvDyuY6BcQK3H8mn QzlYeZF5KkBG3LyqEgQe3ry0Rbgcel4dwYWmJuwuES.tj3tom4iASXRnZeLI QOsaxANqcjogOb14ZItTpF1ukEAQSOLQbYGG6WcGrK4Lv9Ue9YN8ShZ0bxG2 y8Q5Vbmmy2GN816hcsca+U9Hvb0cxeXB6RXG1uItSt.4HO2Ftco3wIIh7X2Q N4J44oRjLAUKimbNSNCoolXoe24fjo2rabplWOOWYW7lXSuTJISuMtvomZ44 .I4I+7l+GCVEJKB -----------end_max5_patcher-----------
Thanks Peter. I bet that if I could compute the phase shift at the fundamental frequency (the one I’m trying to play) then I could shorten the delay line by just the right amount so it ends up resonating at the correct frequency.
But that involves math that is above my head. And it’s not tremendously important. I’m just playing around.
I managed to make a few nice additions to the KS algorithm. I can now morph from a string-like resonance to a pipe-like resonance.
But I think the more important part of what makes the KS algorithm into a playable instrument, is what you do with the excitation. Picking, plucking, bowing, blowing, bouncing… And how this is hooked up to velocity and midi controllers. That will be my project for next weekend.
For stuff like karplus-strong, you should probably be looking at gen~. In fact, look at /Max6/examples/gen/gen~.karplus-strong.maxpat .
Eventually the vector size of any delay object is going to be more of a pain in the neck than you want to deal with.
Also, be sure to mess around with the impulse that you send in. It can affect the sound drastically. If you "lowpass" the impulse itself…
I’d second what mzed said re: gen~. Also, if you’re checking out physical modelling, be sure to look at the old percolate library; it’s open-source and has plenty of fun things to mess with.
Strangely, that gen~.karplus-strong.maxpat sample appears to have the same CPU load as my own version: 14% (on an iMac with a 3.4 GHz Intel Core i7, Max sample rate 96k and vector size 1). And my patch does more than the gen~ sample (the one that I posted was stripped way down)
to measure the cpu usage of a gen~ object you need to give it the ‘@dumpoutlet 1′ argument, turn on ‘cpumeasure 1′ and then poll it with the ‘getcpu’ message. explained in the helpfile. the cpu usage in the minimixer or the audio status is of all of max. it would be great if the minimixer only computed local cpu usage, indeed. however, if you are seeing a huge cpu use like that for the gen~ example KS patch something is wrong. my cpu use for that patch is very very low.
thanks for a quality thread rpieket.
CPU usage of gen~ == percerption theory #2
@Ron make sure you set the signal vector size back to a reasonable value (64 or more). Gen~ can do feedback loop of just one sample delay inside so you don’t have to lower the signal vector size of the entire patcher do obtain high frequencies with the karplus strong.