[chuck-users] LiSa zero-crossing snap how-to

Kassen signal.automatique at gmail.com
Mon Jan 14 21:16:32 EST 2008

Dear ChucKists.
(particularly Dan who asked me for examples of LiSa usage a while back)

Below you'll find a example of how to make LiSa's loop points snap to the
next positive zero crossing in her buffer, provided the buffer was loaded
from a recorded file using SndBuf.

This has many uses, particularly to glitch-free loop a sample that's not
noisy enough to hide clicks and/or particularly bass heavy in situations
where the overall structure of the program loops at a different rate then
the sample loop length and crossfading would mean adding shreds..This is a
polite way of saying it's especially good for cheap & easy stuttering

Mind that this will change the loop's length and thus pitch and/or timing,
it's much cleaner sound-wise though.
In some situations this may be cleaner or lead to cleaner code than the
methods in the examples/docs but it would be a bad idea to think this is the
best way to avoid clicks in all cases as it isn't. It's a interesting option
though and one I found to work well compared to it's complexity.

With lots of comments, I hope others will find this as usefull as I'm
finding it now.


//Zero crossing detection and snapping for LiSa by Kassen
//Permision granted for redistribution, modification, etc.
//no waranties, no refunds, please mind your speakers.
//Remixing strongly encouraged.

SndBuf buf => LiSa l => dac;

//your sample goes here
//this patch is primarily meant for low drum beats or notes
//clicks aren't as objectionable for claps and the like
//may be impractical for lengthy soundscapes.....

int n, m;
float last;
int crossings;

2::second => l.duration;

//detect the number of positive zero crossings in the file
//we do this faster than realtime
//if you try this for long samples while other stuff is running
//there may be glitches as this likely maxes out the cpu
//due to not advancing time
for (0=> n; n<buf.samples(); n++)
    if ((last < 0) && ( buf.valueAt(n) >= 0))
    buf.valueAt(n) => last;

<<<crossings, "positive zero crossings in sample">>>;

//make a array of this length
int zerocrossings[crossings];

//create a index of their locations
0 => crossings;
0 => last;
for (0=> n; n<buf.samples(); n++)
    if ((last < 0) && ( buf.valueAt(n) >= 0))
        n => zerocrossings[crossings];
    buf.valueAt(n) => last;

//go LiSa!
buf.samples()::samp => now;

//save cpu
buf =< l;

//loop forever to demo
//here we only snap loopEnd() to the crossings,
//loopStart() may be treated in the same way,
//at which point only using positive crossings becomes usefull.
    //start at the beginning
    0::ms => l.playPos;

    //modulate loop length
    for (1 => m; m < 8; m++)
        m* (2::second /16) => l.loopEnd;

        //don't bother trying to detect crossings if we already passed the
last one
        if ( (l.loopEnd()/samp)$ int < zerocrossings[zerocrossings.cap()-1])
            //find the next positive zero crossing...
            0 => n;
            while( (((l.loopEnd()/samp) $ int ) > zerocrossings[n]) &&n<
zerocrossings.cap()-1 ) n++;
            //snap to it
            zerocrossings[n +1]::samp => l.loopEnd;
        second => now;
    //get rid of clicks at the end, pause and start over
    second  => now;
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.princeton.edu/pipermail/chuck-users/attachments/20080115/4c12df53/attachment.htm 

More information about the chuck-users mailing list