delay times of STK delays seem to be 1 sample off?
hi, i was running some simple tests to hear the difference between allpass vs linear interpolating delay lines when i noticed that there is obviously a little bug in how the delay time is calculated in the delay UGens from STK. the following example feeds an impulse into a DelayA with high feedback and compares the tuning to a TriOsc. can't i trust my ears or is the tuning only correct if i add 1 to deltime before calculating the frequency for TriOsc? thanks, volker. Impulse imp => Gain out => dac; out => DelayA delay => out; TriOsc tri => dac; // test oscillator 44100.0 => float sr; // sampling rate // delay time in samples 150.5 => float deltime; deltime::samp => delay.delay; // calc tri freq from delay time // have to add 1 to deltime to get correct tuning sr / (deltime) => tri.freq; // set feedback level 0.999 => delay.gain; 0.3 => tri.gain; while(true) { 1 => imp.next; 6::second => now; }
Hi, Volker!
I'm not sure if this is your issue here but some theory;
ChucK uses a "pull through" model to calculate samples. So; every time the
DAC wants to output a sample the DAC asks (pulls) all Ugens connected to it
to report a value and those in turn ask all their inputs for one before
returning it (recursively). This works very well and guarantees a proper
calculation order of samples (compare that to MAX which uses a left to right
evaluation and PD which calculates modules in the order they are placed, I
think).
All is well until we use feedback. If the DAC depends on a delay and that
delay in turn depends on itself we would get stuck so there we stop and the
sample used there is actually the *last* sample the delay calculated. This
last sample is called the "Z-1" (two samples ago would be "Z-2", etc).
So; all feedback loops get a sample added to their length, which can and
will affect tuning. Of course the higher the pitch the larger a samp as a
fraction of the pitch will be and so the more detuned it will be.
Fortunately this can be compensated for by subtracting a samp from the set
delay time.
Again, not sure if this is your issue but I'm a bit short on time at the
moment so I didn't run your code, it looks like it though. At least a little
theory can't hurt. It's a well known issue in systems like ours, but at
least thanks to having dur as a data type have plenty of tools to deal with
it and it's much, much better then the kind of thing you would get if you
tried the same in a system using block processing.
Hope that helps,
Yours,
Kas.
On 29/02/2008, volker böhm
hi, i was running some simple tests to hear the difference between allpass vs linear interpolating delay lines when i noticed that there is obviously a little bug in how the delay time is calculated in the delay UGens from STK.
the following example feeds an impulse into a DelayA with high feedback and compares the tuning to a TriOsc.
can't i trust my ears or is the tuning only correct if i add 1 to deltime before calculating the frequency for TriOsc?
thanks, volker.
Impulse imp => Gain out => dac; out => DelayA delay => out; TriOsc tri => dac; // test oscillator
44100.0 => float sr; // sampling rate
// delay time in samples 150.5 => float deltime;
deltime::samp => delay.delay;
// calc tri freq from delay time // have to add 1 to deltime to get correct tuning sr / (deltime) => tri.freq;
// set feedback level 0.999 => delay.gain; 0.3 => tri.gain;
while(true) { 1 => imp.next; 6::second => now; } _______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
On 29 Feb 2008, at 21:10, Kassen wrote:
ChucK uses a "pull through" model to calculate samples. So; every time the DAC wants to output a sample the DAC asks (pulls) all Ugens connected to it to report a value and those in turn ask all their inputs for one before returning it (recursively). This works very well and guarantees a proper calculation order of samples (compare that to MAX which uses a left to right evaluation and PD which calculates modules in the order they are placed, I think).
All is well until we use feedback. If the DAC depends on a delay and that delay in turn depends on itself we would get stuck so there we stop and the sample used there is actually the *last* sample the delay calculated. This last sample is called the "Z-1" (two samples ago would be "Z-2", etc).
thanks for your answer, kassen. you have a good point here. i'm a lousy chucker and simply needed some feedback with the delay ugens. but yes, it can't work like this - at least not correctly. but what did confuse me in the first place was that i had already tried to add a feedback path in the ugen source, only to find, that the timing still wasn't right. that led me to the conclusion that there must be something wrong with the way the delay time is calculated - sorry that was a little fast. i did some further tests and delay times are indeed set correctly. the problem i ran into, is related to the "Writing before reading allows delays from 0 to length-1" - approach, which is stated in the source. i changed the order of write and read, added a feedback parameter and everything is fine. thanks, volker.
On 01/03/2008, volker böhm
you have a good point here. i'm a lousy chucker and simply needed some feedback with the delay ugens. but yes, it can't work like this - at least not correctly.
Well, now, "lousy chucker" is really quite exaggerated. Calculation order and so on is really a quite advanced subject that nobody notices until it mysterious mucks everything up. At least it's predictable in most practical ChucK situations which can save a lot of noise compared to some other systems. I'd say you are a good listener instead, I might've missed why things were out of tune myself. :¬) ...in fact, now that I think of it, I might've missed this in some of my own code, I could probably tighten up a rhythmical delay I use. but what did confuse me in the first place was that i had already
tried to add a feedback path in the ugen source, only to find, that the timing still wasn't right. that led me to the conclusion that there must be something wrong with the way the delay time is calculated - sorry that was a little fast. i did some further tests and delay times are indeed set correctly. the problem i ran into, is related to the "Writing before reading allows delays from 0 to length-1" - approach, which is stated in the source. i changed the order of write and read, added a feedback parameter and everything is fine.
So, if I get this right, you made sure the delay now works at it's set length for both feedback and straight output? That would be quite a valuable addition if you'd submit it (not sure if there's a official process for this). At any rate this phenomenon could stand some attention in the manual because any system using feedback will run into this. Often the Z-1 function inherent in the feedback is actually beneficial and useful but we should probably document that it's there, where it is exactly and how it will affect things. Glad to have been of help. I hope you'll share your fix, sounds like a solution that would be good for everyone. Yours, Kas.
On 02 Mar 2008, at 01:54, Kassen wrote:
So, if I get this right, you made sure the delay now works at it's set length for both feedback and straight output?
yes, that's right. the feedback path is provided in the ugen itself. one drawback is that it's not possible anymore to have a delay of 0 samples (which for a feedback delay makes no sense anyway).
That would be quite a valuable addition if you'd submit it (not sure if there's a official process for this). At any rate this phenomenon could stand some attention in the manual because any system using feedback will run into this. Often the Z-1 function inherent in the feedback is actually beneficial and useful but we should probably document that it's there, where it is exactly and how it will affect things.
Glad to have been of help. I hope you'll share your fix, sounds like a solution that would be good for everyone.
i don't know about submitting. the delay ugens are part of the STK which also exists as a library independent from chuck. so changing something in there is not so easy. also i think e.g. DelayA is used by other STK ugens as well. might be better to have dedicated (interpolating) delay ugens with integrated feedback path - maybe there is already something like that. coulnd't find any in the ugen list, though. but of course i am happy to share what i have done, it's simple enough... this is from ugen_stk.cpp: MY_FLOAT DelayA :: tick(MY_FLOAT sample) { MY_FLOAT next = nextOut(); // READ from delay line (compute output sample) inputs[inPoint++] = sample + next * fb; // WRITE input to delay line (last output value added with feedback gain) // Increment input pointer modulo length. if (inPoint == length) inPoint -= length; outputs[0] = next; doNextOut = true; // Save the allpass input and increment modulo length. apInput = inputs[outPoint++]; if (outPoint == length) outPoint -= length; return outputs[0]; } and then there is the whole CK_DLL glibber to add the "fb" parameter and method. best, volker.
On 02/03/2008, volker böhm
yes, that's right. the feedback path is provided in the ugen itself. one drawback is that it's not possible anymore to have a delay of 0 samples (which for a feedback delay makes no sense anyway).
Zero length delays should still be attainable through putting the unit in bypass mode, using the .op() function, I think. Kinda trivial but could become relevant.
i don't know about submitting. the delay ugens are part of the STK which also exists as a library independent from chuck. so changing something in there is not so easy.
Well, aside from Perry Cook himself being on this list as well, there are already differences between the STK and ChucK versions of some STK Ugens. It's a different context, different choices may make sense. For example; the envelopes come from the STK but in ChucK they have some member functions that take variables of type duration. There are more examples, mainly convenience stuff. I don't think the library-nature of the STK needs to be a issue on it's own also i think e.g.
DelayA is used by other STK ugens as well.
That wouldn't need to be a show-stopper either because those wouldn't use the feedback. Oh, now it sounds like I'm arguing either way, I was really just pointing out factors. might be better to have dedicated (interpolating) delay ugens with
integrated feedback path - maybe there is already something like that. coulnd't find any in the ugen list, though.
I can't find anything either. I think LiSa could be tricked into becoming one using her feedback but I'm not sure right now if that would have the extra sample. We'll see what Ge & Perry think. I'm at least going to add a note about this feedback/Z-1 issue to the Wiki's page for suggested additions to the manual. At the moment I don't think the manual covers the calculation order at all yet it's quite important, in a way it could be considered a part of being "strongly timed". Kas.
On 29 Feb 2008, at 21:10, Kassen wrote:
ChucK uses a "pull through" model to calculate samples. So; every time the DAC wants to output a sample the DAC asks (pulls) all Ugens connected to it to report a value and those in turn ask all their inputs for one before returning it (recursively). This works very well and guarantees a proper calculation order of samples
I had a problem with the 'pull' model, though, in trying to craft a Ugen that would allow me to send UI input to chuck from an external environment. This was in the chuck~ object, where I hoped to be able to set up a way of connecting data coming from max/msp objects (sliders, etc.) into an executing chuck script. The difficulty was to create a Ugen who could 'grab' a value when data appeared, or at least do some kind of sample-and-hold equivalent so that a data value would be maintained without having to constantly poll all possible interface objects. I recall there being some problem trying to do this in chuck~ in addition to the inefficiency, but I don't remember exactly what the problem was. I also haven't looked at the source for the audicle stuff and how it handles UI input. Maybe there's an elegant solution I don't know. brad http://music.columbia.edu/~brad
On 03/03/2008, Brad Garton
I had a problem with the 'pull' model, though, in trying to craft a Ugen that would allow me to send UI input to chuck from an external environment. This was in the chuck~ object, where I hoped to be able to set up a way of connecting data coming from max/msp objects (sliders, etc.) into an executing chuck script.
<snip> If I understand correctly I don't think this is a "problem with the pull model" per-se but more of a issue in two very different models touching each-other. I don't think linking all MAX "cables" to ChucK "Ugen connections" is the most sensible way of linking them. Some MAX cables will represent concepts closer to ChucK code or events while others will indeed be more like Ugen-connections. How hard/possible would it be to represent such a slider as a variable event member and the changing of the slider as broadcasting the event? Perhaps a sort of virtual protocol could be created, something like Hid or MIDI except linked to MAX objects instead if hardware objects? I would say that here "crafting a event" might be a more fertile solution then "crafting a Ugen". My knowledge of MAX is rather rudimentary though. Hope that's at least of some help, Kas.
On Mar 3, 2008, at 11:55 AM, Kassen wrote:
If I understand correctly I don't think this is a "problem with the pull model" per-se but more of a issue in two very different models touching each-other.
true.
I don't think linking all MAX "cables" to ChucK "Ugen connections" is the most sensible way of linking them. Some MAX cables will represent concepts closer to ChucK code or events while others will indeed be more like Ugen-connections.
The idea would be to instantiate a chuck object like this [chuck~ 2 5] where the first number is the number of output channels and the second is the number of inlets on the object, each of which would be receiving a stream of data from some 'external' object. These would be represented as variables in the chuck~ script, and whenever a value of that variable was requested *during* the execution of the script (see below, this is the problem) it would deliver the currently-set value coming from the 'external' object. This isn't hard to do, I've built other objects like this.
How hard/possible would it be to represent such a slider as a variable event member and the changing of the slider as broadcasting the event? Perhaps a sort of virtual protocol could be created, something like Hid or MIDI except linked to MAX objects instead if hardware objects?
That's kind-of what I do now, variables can come into chuck~ as $1, $2, etc. BUT: when the script is sent to the chuck parser, those values get 'frozen' when the script executes. This is the problem -- I want to be able to have a chuck~ variable (probably would have to be a ugen) that will dynamically change as values come into the post-parser executing script. I think MIDI and OSC in chuck possibly work this way, but I'm not sure.
I would say that here "crafting a event" might be a more fertile solution then "crafting a Ugen". My knowledge of MAX is rather rudimentary though.
Could be -- how is chuck set up to respond to 'events' during script execution? I'm missing something, I think. Although this is max- oriented, I suspect that the pd chuck~ probably has a similar situation. brad http://music.columbia.edu/~brad
On 03/03/2008, Brad Garton
On Mar 3, 2008, at 11:55 AM, Kassen wrote:
Could be -- how is chuck set up to respond to 'events' during script execution? I'm missing something, I think. Although this is max-oriented, I suspect that the pd chuck~ probably has a similar situation.
Well, typically in order to respond to a event (be it a custom one or Hid/ Midi) you need a shred that is waiting for that event. from there on this shred may do any number of things depending on what's needed but most likely in this case would be updating a variable, then resume waiting. What you could do is extend event to hold both the name of the variable and it's new value, then have some shred wait for this and update variables as new values come in? I wouldn't do anything else in that shred as likely there will be quite a few such events and we wouldn't want to miss them or cause CPU strain. I have no idea how this would work out in practice, but at least this would make it easier to also communicate integers, strings and triggering pulses. Ugens only do float, after all. Perhaps the cleanest way is to have a MaxIn object in ChucK that would work exactly like MidiIn, except referring to virtual ports of the "~ChucK" object instead of MIDI ports? It's only logical that you run into this as the two systems are quite different but I would imagine it to be quite worthwhile. If you just make ChucK conform to the way MAX does things there is much less benefit of having a ~ChucK object, I imagine. Yours, Kas.
participants (3)
-
Brad Garton
-
Kassen
-
volker böhm