Issues with RecordBuf recording into a Buffer going into PlayBuf

Hey community,

I got this setup:

(
Buffer.freeAll;

// inputs
~bus0 = 0;

// buffers
~sampleRate = s.options.sampleRate;
~bTest = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");
~b1 = Buffer.alloc(s, ~sampleRate * 0.2, numChannels: 1); // 0.2 s
)

After evaluating this my instrument with live-input is:

(
Ndef(\instrument1, {
	var input, buffer, sig;

	input = SoundIn.ar(~bus0);
	input = RHPF.ar(input, freq: 70, rq: 0.75);
	buffer = RecordBuf.ar(input, ~b1.bufnum, loop: 1, doneAction: 2);

	sig = PlayBuf.ar(1, buffer,
		rate: 0.97,
		loop: 1,
		doneAction: 2
	);

	sig = Limiter.ar(sig, 1.0, dur: 0.01);
	sig = PanX.ar(2, sig, pos: 0.5);
	Out.ar(0, sig);
}).play;
)

All this together should give back the input signal but a bit pitch shifted.

Two issues:

  1. Why does it take ~bTest as input even when declared in an environment? When I cut and paste the Buffer.read line below the Buffer.alloc it works.

  2. I often get small sound glitches, sometimes they get worse and crash the whole input… What could be the issue for that?

Thank you all and have a nice day…

This looks suspicious, does RecordBuf return ~b1.bufnum? I can’t find this in the docs, nor implementation.

Input to what?
Sounds like you need to do s.sync between each command. I’d recommend using s.waitForBoot here.
i.e…

s.waitForBoot {
   ~sampleRate = s.options.sampleRate;
   ~bTest = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");
   ~b1 = Buffer.alloc(s, ~sampleRate * 0.2, numChannels: 1); // 0.2 s
   s.sync;

   Ndef(\instrument1, {
	var input, buffer, sig;

	input = SoundIn.ar(~bus0);
	input = RHPF.ar(input, freq: 70, rq: 0.75);
	buffer = RecordBuf.ar(input, ~b1.bufnum, loop: 1, doneAction: 2);

	sig = PlayBuf.ar(1, buffer,
		rate: 0.97,
		loop: 1,
		doneAction: 2
	);

	sig = Limiter.ar(sig, 1.0, dur: 0.01);
	sig = PanX.ar(2, sig, pos: 0.5);
	Out.ar(0, sig);
   }).play;

};

AFAIK this is not recommended (as Jordan said).

There are some SynthDef-ordering cases where it ends up putting the recorder after the player. To guarantee order, you can use the firstArg math op – declare another variable “recorder” and then:

	buffer = ~b1.bufnum;

	recorder = RecordBuf.ar(input, buffer, loop: 1, doneAction: 2);

	sig = PlayBuf.ar(1, buffer <! recorder,

hjh

1 Like

I also need help understanding this thread correctly. To understand this thread, I slightly changed the first codes of this thread. I realised there were glitches in the sound produced by evaluating the codes below, and I could not understand why they occurred. Could it be explained? You could hear the glitches at around 6.5 seconds and 13 seconds in the linked sound file of each code.

(
s.waitForBoot {
	s.prepareForRecord;
	~b1 = Buffer.alloc(s, s.sampleRate * 0.2, numChannels: 1); 
	s.sync;
	s.record;
	Ndef(\test, { SinOsc.ar * 0.1 }).play;
	Ndef(\instrument1, {
		var input, buffer, recorder, sig;
		input = SoundIn.ar(0);
		buffer = ~b1.bufnum;
		recorder = RecordBuf.ar(input, buffer);
		sig = PlayBuf.ar(1, buffer, -0.52.midiratio, loop: 1);
		Pan2.ar(sig, pos: 1);
	}).play;
	20.wait;
	s.stopRecording;
	Ndef(\test).clear;
	Ndef(\instrument1).clear
}
)

(
s.waitForBoot {
	s.prepareForRecord;
	~b1 = Buffer.alloc(s, s.sampleRate * 0.2, numChannels: 1); 
	s.sync;
	s.record;
	x = { SinOsc.ar * 0.1 }.play;
	y = {
		var input, buffer, recorder, sig;
		input = SoundIn.ar(0);
		buffer = ~b1.bufnum;
		recorder = RecordBuf.ar(input, buffer);
		sig = PlayBuf.ar(1, buffer, -0.52.midiratio, loop: 1);
		Pan2.ar(sig, pos: 1);
	}.play;
	20.wait;
	s.stopRecording;
	x.free;
	y.free
}
)

So sorry guys for my delay here…

Thank you for that @jordan
Okay so RecordBuf not returning ~b1.bufnum seems to be one part of the problem…

I meant ~bTest as input for the Ndef’s RecordBuf instead of ~b1 as declared…

When I evaluate your code, I still got the issue that ~bTest is being played. When I out-comment the ~bTest line I get my signal from SoundIn.ar(~bus0); - but with those clicks in it like @prko described…

Maybe its from the fact that the allocated 0.2 seconds buffer is not “not windowed”?

Unfortunately this makes a glitchy/harsh mixture of the buffer with the live signal…
Maybe my system is corrupted?

One way to proceed here is to build up from the absolute minimum functionality, step-by-step. At the point where it breaks, that’s where the problem is.

(
Ndef(\x, {
	var input = SoundIn.ar(0);  // or other source; mind feedback!
	var recorder = RecordBuf.ar(input, b, loop: 1);
	var player = PlayBuf.ar(1, b.bufnum <! A2K.kr(recorder),
		rate: 1  // I have a reason for this, at first
	);
	(player * 0.6).dup
}).play;
)

I found here the first gotcha – b.bufnum is not audio rate, while recorder is audio rate. This causes <! to behave incorrectly.

But even after fixing that, it’s still broken – there is a click at every loop point.

So RecordBuf / PlayBuf may be a non-starter.

(
Ndef(\x, {
	var input = SoundIn.ar(0);
	var phase = Phasor.ar(0, 1, 0, b.numFrames);
	var recorder = BufWr.ar(input, b, phase);
	var player = BufRd.ar(1, b, phase <! recorder);
	(player * 0.6).dup
}).play;
)

This version is completely smooth. So, better to start with this approach and abandon RecordBuf / PlayBuf.

The next problem you will run into is: playing back at rate = 0.97 while recording at rate = 1 will also click every time the playback head crosses the record head. You can’t transparently drift further and further back into the past, infinitely. So you might have to rethink what you mean by rate: 0.97.

hjh

1 Like

Thanks for that!

The deal with rate: 0.97 or in my final case rate: -0.5.midiratio (which is of course unprecise because of non-linear frequency distribution) is to playback a signal/instrument but slighty pitchshifted…
I tried dozens of pitch-shifting possiblities in SuperCollider but this approach just sounds the best - if there weren’t those clicks…

For short periods of time, you could avoid clicks by using a longer buffer. But it isn’t possible to do indefinitely. Memory is finite, so the buffer size must be finite, meaning that there is a finite amount of time before the playback and record heads cross. You could reduce the clicking by crossfading two players, but there will inevitably be a temporal discontinuity in the result. This is unavoidable – you cannot have playback keep going further and further back into the past forever. Eventually you have to have a temporal edit point, or just stop.

If that’s not acceptable, then a granular approach would be your best bet.

hjh

2 Likes

I see… Thank you for your examinations, I guess I will have try sth else then…