[chuck-users] UGen v.s. code in timing; very tricky

Eric Hedekar afterthebeep at gmail.com
Thu Sep 18 05:37:59 EDT 2008

```Ah, the digital world - never enough bits!  My first reaction was to think
that this extra sample was a floating point truncation error in the
s.phase() and s.period() math, but then I came to think hmm...  would those
minuscule fractions of samples make any difference to a zero crossing
timing?  Furthermore, why would a truncation error be weighted?  Your
declaration that "the value is on -average- lower in magnitude" started me
thinking 'okay, how average?'  So I wrote up a little script:

TriOsc s => dac;

float one;
float two;
float three;
int count;
int count2;
int count3;
int signcount;
int signcount2;

for (int j;j<10000; j++)
{
Std.rand2f(10, 10000)=>s.freq;
10::ms=>now; // arbitrary offset of time
(1-s.phase())::s.period() => now;  // a la Kassen
s.last()=>one;
samp=>now;
s.last()=>two;
samp=>now;
s.last()=>three;
if (Std.fabs(one) < Std.fabs(two) && Std.fabs(one) <
Std.fabs(three)) count++;
if (Std.fabs(one) > Std.fabs(two) && Std.fabs(three) >
Std.fabs(two)) count2++;
if (Std.fabs(two) > Std.fabs(three) && Std.fabs(one) >
Std.fabs(three)) count3++;
if ((one <0 && two > 0) || (one >0 && two <0)) signcount++;
if ((two <0 && three > 0) || (two >0 && three <0)) signcount2++;
}
<<<count/100.0>>>;
<<<count2/100.0>>>;
<<<count3/100.0>>>;
<<<signcount/100.0>>>;
<<<signcount2/100.0>>>;

In the end, I ran it with a few different offset values and a variety of
differing frequency ranges (though I've yet to really test the sub-audio
range - maybe overnight) and this is the general output I would keep seeing:
13.060000 :(float)
74.370000 :(float)
12.570000 :(float)
49.140000 :(float)
50.860000 :(float)
I'll admit, at first I didn't have the zero crossing checks in, so I was
understanding very little, but once I realized that the zero crossings were
equally distributed between the two possible locations, things clicked.
Obviously the extra sample would "on average" (about 75% of the time) result
in a lower s.last() value, because it's the sample around which the zero
crossing would pivot.  If the zero crossing didn't pivot, then you'd see an
even distribution of probability for the lowest value.

Your initial calculation is advancing time to the end of the period (or so
it tries).  I think there is a truncation error that is hidden in the math
(that would explain why the zero crossing is jumping back and forth between
the two locations, with an even distribution), and therefore only half the
time you've advanced fully to the end of the period (the other half of the
time you're off by one sample).  The calculation is not finding the sample
closest to the zero crossing - which might be a more precise, yet more
laborious approach with little real world improvement.

I'd unchuck items with the inclusion of a while loop:
while (s.gain()>0.000000001)
{
s.gain()/1.005 => s.gain();
samp=>now;
}
s =< dac;

Though who knows how long that can take.
-Eric Hedekar

On Wed, Sep 17, 2008 at 4:14 PM, Kassen <signal.automatique at gmail.com>wrote:

> Dear list.
>
> Suppose we have a SinOsc named "s", connected to the dac,  that has been
> playing for some arbitrary amount of time at a arbitrary frequency. Suppose
> we'd like to disconnect this from the dac while minimising the "click".
>
> One thing to try would be this;
>
> (1 - s.phase() )::s.period() => now; //strong timing to the rescue!
> s =< dac;
>
> Quite ChucKian, I thought, however this still clicks a bit. Some clickery
> is to be expected as not all frequencies will have a period that can be
> expressed in a integer number of samp's but this seemed like a bit much.
>
> On a whim I tried;
>
> (1 - s.phase() )::s.period() + samp => now;
> s =< dac;
>
> This clicks less and when I print the s.last(), the value is on -average-
> lower in magnitude. I then tried lining up the shred's timing with the UGen
> graph's;
>
> samp - (now % samp) => now; //yes; that's quite anal
> (1 - s.phase() )::s.period()  => now;
> s =< dac;
>
> This doesn't affect matters quite as much as simply adding a samp to the
> time advanced; waiting a extra sample generally seems (to me) to decrease
> the magnitude of the s.last() right before we disconnect it.
>
> This would lead us to ask; "where does this samp come from?". I'm a bit at
> loss here, does anybody have any insights?
>
> Yours,
> Kas.
>
> _______________________________________________
> chuck-users mailing list
> chuck-users at lists.cs.princeton.edu
> https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
>
>

--
_______________________________________
http://greyrockstudio.blogspot.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.cs.princeton.edu/pipermail/chuck-users/attachments/20080918/4040432d/attachment.htm>
```