Dear ChucKists.<br>(particularly Dan who asked me for examples of LiSa usage a while back)<br><br>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.
<br><br>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.
<br><br>Mind that this will change the loop's length and thus pitch and/or timing, it's much cleaner sound-wise though.<br>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.
<br><br>With lots of comments, I hope others will find this as usefull as I'm finding it now.<br><br>Yours,<br>Kas.<br><br>==================================================<br>//Zero crossing detection and snapping for LiSa by Kassen
<br>//Permision granted for redistribution, modification, etc.<br>//no waranties, no refunds, please mind your speakers.<br>//Remixing strongly encouraged.<br><br>SndBuf buf => LiSa l => dac;<br><br>//your sample goes here
<br>//this patch is primarily meant for low drum beats or notes<br>//clicks aren't as objectionable for claps and the like<br>//may be impractical for lengthy soundscapes.....<br>buf.read("kick.wav");<br><br>
int n, m;<br>float last;<br>int crossings;<br><br>2::second => l.duration;<br><br>//detect the number of positive zero crossings in the file<br>//we do this faster than realtime<br>//if you try this for long samples while other stuff is running
<br>//there may be glitches as this likely maxes out the cpu<br>//due to not advancing time<br>for (0=> n; n<buf.samples(); n++)<br> {<br> if ((last < 0) && ( buf.valueAt(n) >= 0))<br> {<br>
crossings++;<br> }<br> buf.valueAt(n) => last;<br> }<br> <br><<<crossings, "positive zero crossings in sample">>>;<br><br>//make a array of this length<br>int zerocrossings[crossings];
<br><br>//create a index of their locations<br>0 => crossings;<br>0 => last;<br>for (0=> n; n<buf.samples(); n++)<br> {<br> if ((last < 0) && ( buf.valueAt(n) >= 0))<br> {<br> n => zerocrossings[crossings];
<br> crossings++;<br> }<br> buf.valueAt(n) => last;<br> }<br><br>//go LiSa!<br>l.record(1);<br>buf.samples()::samp => now;<br>l.record(0);<br><br>//save cpu<br>buf =< l;<br><br><br>//loop forever to demo
<br>//here we only snap loopEnd() to the crossings,<br>//loopStart() may be treated in the same way,<br>//at which point only using positive crossings becomes usefull.<br>while(1)<br> {<br> //start at the beginning<br>
0::ms => l.playPos;<br> l.play(1);<br> <br> //modulate loop length<br> for (1 => m; m < 8; m++)<br> {<br> m* (2::second /16) => l.loopEnd;<br> <br> //don't bother trying to detect crossings if we already passed the last one
<br> if ( (l.loopEnd()/samp)$ int < zerocrossings[zerocrossings.cap()-1])<br> {<br> //find the next positive zero crossing...<br> 0 => n;<br> while( (((l.loopEnd()/samp) $ int ) > zerocrossings[n]) &&n<
zerocrossings.cap()-1 ) n++;<br> //snap to it<br> zerocrossings[n +1]::samp => l.loopEnd;<br> }<br> second => now;<br> }<br> //get rid of clicks at the end, pause and start over
<br> l.rampDown(50::ms);<br> second => now;<br> }<br>