Dear ChucKists.<br>(particularly Dan who asked me for examples of LiSa usage a while back)<br><br>Below you&#39;ll find a example of how to make LiSa&#39;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&#39;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&#39;s especially good for cheap &amp; easy stuttering kick-drums.
<br><br>Mind that this will change the loop&#39;s length and thus pitch and/or timing, it&#39;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&#39;t. It&#39;s a interesting option though and one I found to work well compared to it&#39;s complexity.
<br><br>With lots of comments, I hope others will find this as usefull as I&#39;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 =&gt; LiSa l =&gt; dac;<br><br>//your sample goes here
<br>//this patch is primarily meant for low drum beats or notes<br>//clicks aren&#39;t as objectionable for claps and the like<br>//may be impractical for lengthy soundscapes.....<br>buf.read(&quot;kick.wav&quot;);<br><br>
int n, m;<br>float last;<br>int crossings;<br><br>2::second =&gt; 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=&gt; n; n&lt;buf.samples(); n++)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; if ((last &lt; 0) &amp;&amp; ( buf.valueAt(n) &gt;= 0))<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; crossings++;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; buf.valueAt(n) =&gt; last;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; <br>&lt;&lt;&lt;crossings, &quot;positive zero crossings in sample&quot;&gt;&gt;&gt;;<br><br>//make a array of this length<br>int zerocrossings[crossings];
<br><br>//create a index of their locations<br>0 =&gt; crossings;<br>0 =&gt; last;<br>for (0=&gt; n; n&lt;buf.samples(); n++)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; if ((last &lt; 0) &amp;&amp; ( buf.valueAt(n) &gt;= 0))<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; n =&gt; zerocrossings[crossings];
<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; crossings++;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; buf.valueAt(n) =&gt; last;<br>&nbsp;&nbsp;&nbsp; }<br><br>//go LiSa!<br>l.record(1);<br>buf.samples()::samp =&gt; now;<br>l.record(0);<br><br>//save cpu<br>buf =&lt; 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>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; //start at the beginning<br>
&nbsp;&nbsp;&nbsp; 0::ms =&gt; l.playPos;<br>&nbsp;&nbsp;&nbsp; l.play(1);<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; //modulate loop length<br>&nbsp;&nbsp;&nbsp; for (1 =&gt; m; m &lt; 8; m++)<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; m* (2::second /16) =&gt; l.loopEnd;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; //don&#39;t bother trying to detect crossings if we already passed the last one 
<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if ( (l.loopEnd()/samp)$ int &lt; zerocrossings[zerocrossings.cap()-1])<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; //find the next positive zero crossing...<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 0 =&gt; n;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; while( (((l.loopEnd()/samp) $ int ) &gt; zerocrossings[n]) &amp;&amp;n&lt;
zerocrossings.cap()-1 ) n++;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; //snap to it<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; zerocrossings[n +1]::samp =&gt; l.loopEnd;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; second =&gt; now;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; //get rid of clicks at the end, pause and start over
<br>&nbsp;&nbsp;&nbsp; l.rampDown(50::ms);<br>&nbsp;&nbsp;&nbsp; second&nbsp; =&gt; now;<br>&nbsp;&nbsp;&nbsp; }<br>