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 kick-drums.
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.
Yours,
Kas.
==================================================
//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.....
buf.read("kick.wav");
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))
{
crossings++;
}
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];
crossings++;
}
buf.valueAt(n) => last;
}
//go LiSa!
l.record(1);
buf.samples()::samp => now;
l.record(0);
//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.
while(1)
{
//start at the beginning
0::ms => l.playPos;
l.play(1);
//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
l.rampDown(50::ms);
second => now;
}