Buffer xFader

2020-03-29 13:42:09 supercollider

This SuperCollider function automates the process of making seamless loops as shown in this animation...

xfader audacity

i.e. creating continuous loops from buffers following this recipe...

  1. select a few seconds from the end.
  2. fade out the selection.
  3. cut the selection.
  4. paste the selection at time 0.
  5. select a few seconds from the beginning (usually same length as in #1).
  6. fade in the selection.
  7. mix the two tracks.
~xfader= {|inBuffer, duration= 2, curve= -2, action|
	var frames= duration*inBuffer.sampleRate;
	if(frames>inBuffer.numFrames, {
		"xfader: crossfade duration longer than half buffer - clipped.".warn;
	frames= frames.min(inBuffer.numFrames.div(2)).asInteger;
	Buffer.alloc(inBuffer.server, inBuffer.numFrames-frames, inBuffer.numChannels, {|outBuffer|
			var interleavedFrames= frames*inBuffer.numChannels;
			var startArr= arr.copyRange(0, interleavedFrames-1);
			var endArr= arr.copyRange(arr.size-interleavedFrames, arr.size-1);
			var result= arr.copyRange(0, arr.size-1-interleavedFrames);
				var fadeIn= i.lincurve(0, interleavedFrames-1, 0, 1, curve);
				var fadeOut= i.lincurve(0, interleavedFrames-1, 1, 0, 0-curve);
				result[i]= (startArr[i]*fadeIn)+(endArr[i]*fadeOut);
			outBuffer.loadCollection(result, 0, action);


//edit and load a soundfile (or use an already existing buffer)
b.free;  b= Buffer.read(s, "~/Desktop/testnoise.wav".standardizePath);

//evaluate the function to create the seamless cross fade
c= ~xfader.value(b);

//try looping it - should loop smoothly and without discontinuities
d= {PlayBuf.ar(c.numChannels, c, loop:1)}.play;

//compare with the input file - this will probably have a hickup
d= {PlayBuf.ar(b.numChannels, b, loop:1)}.play;


//--save to disk example with shorter cross fade and done action function.
b.free;  b= Buffer.read(s, "~/Desktop/testnoise.wav".standardizePath);
c= ~xfader.value(b, 0.5, -3, action:{|buf| ("done with buffer"+b).postln});

The SuperCollider code works with any Buffer containing soundfiles, live sampled or generated sounds. The duration argument set the cross fade length in seconds and with the curve argument one can set curvature.

Note that duration can not be longer than half the duration of the input buffer and a curve of 0.0 or greater will mean that the amplitude will dip in the middle of the crossfade. So it is recommended to bend the curve a bit to get more of an equal power crossfade. -2.0 to -4.0 seem like sensible values. The function will allocate and return a new Buffer instance and not touch the buffer passed in as argument.

An action function is optional. The function is asynchronous. Also note that the resulting file will be shorter than the original because of the crossfade.

I remember learning this trick from Peter Lundén ~20 years ago. He showed it to me on a SGI machine running Irix, possibly using the Snd sound editor.