SoundFileView using Buffer

Hey!

is there a way to use the SoundFileView with a Buffer instead of reading a file from disk? So basically writing in real time to the SoundFileView, or at least once the recording is done, loading the buffer to the SoundFileView.

( // make a simple SoundFileView
y = Window.screenBounds.height - 120;
w = Window.new("soundfile test", Rect(200, y, 740, 100)).alwaysOnTop_(true);
w.front;
a = SoundFileView.new(w, Rect(20,20, 700, 60));

f = Buffer.alloc(s, s.sampleRate * 5.0, 2); 

f.read("path"); //instead of a File from disk, loading the recorded buffer here

a.soundfile = f;            // set soundfile
a.read(0, f.numFrames);     // read in the entire file.
a.refresh;                // refresh to display the file.
)

(
SynthDef(\record, {
    var sig, amp;
	sig = Impulse.ar(10) ! 2;
    RecordBuf.ar(sig, f, doneAction: Done.freeSelf, loop: 0);
}).play;
)

I also tried to write the SynthDef(\record) to disk, and then reload it into the FileView, but when I do so I get the following error:

ERROR: Qt: Invalid beginning and/or duration.

The saved file doesnt have information about its length. An suggestions? I used the code from examples. But this way seems more complicated though

Thank you :slight_smile:

Not directly – I saw that you tried a.soundfile = aBuffer but the programming interface is totally different between buffers and sound files, so naturally, you can’t interchange them.

You can get data out of the buffer and supply the data directly to the SoundFileView. (Typically SoundFileView is used with a sound file, but look up the help for the method setData – a file isn’t strictly required.)

Doing it in real time is a little bit tricky in terms of the details (one of the tricky things is that some of the methods are very strict about numeric type), but entirely possible:

s.boot;

b = Buffer.alloc(s, 5 * s.sampleRate.asInteger, 1);

(
a = { |bufnum, refreshRate = 20|
	var sig = SinOsc.ar(SinOsc.kr(0.3).exprange(2, 100)),
	frames = BufFrames.kr(bufnum),
	phase = Phasor.ar(0, 1, 0, frames, 0);
	BufWr.ar(sig, bufnum, phase);
	SendReply.ar(Impulse.ar(refreshRate), '/phase', phase);
	(sig * 0.1).dup
}.play;
)

(
v = SoundFileView(nil, Rect(200, 200, 800, 500))
.alloc(b.numFrames, 1, s.sampleRate)
.front;
)

(
var lastPhase, data = Array.fill(b.numFrames, 0);

OSCdef(\phase, { |msg|
	{
		var phase = msg[3].asInteger,
		cond = Condition.new;
		if(lastPhase.notNil) {
			if(lastPhase > phase) {
				// wrapping around; might need 2 segments
				// also found here, if phase == 0, then it's requesting
				// 0 data frames, which causes a "buffer overflow" (misleading error)
				if(phase > 0) {
					b.getn(0, phase, { |d|
						data.overWrite(d, 0);
						cond.unhang;
					});
					cond.hang;  // forgot this initially
				};
				b.getn(lastPhase, b.numFrames - lastPhase, { |d|
					data.overWrite(d, lastPhase);
					cond.unhang;
				});
				cond.hang;
			} {
				b.getn(lastPhase, phase - lastPhase + 1, { |d|
					data.overWrite(d, lastPhase);
					cond.unhang;
				});
				cond.hang; 
			};
			v.setData(data, 1, 0, 1, s.sampleRate.asInteger).refresh;
		};
		lastPhase = phase;
	}.fork(AppClock);
}, '/phase', s.addr, argTemplate: [a.nodeID]);

a.onFree { OSCdef(\phase).free };
)

a.free;

hjh

1 Like

Thank you for your answer! I think I am still missing something. When I excecute the code, there is still no data beeing written into the buffer.

OSCdef(\phase, { |msg|
	{
		var phase = msg[3].asInteger,
		cond = Condition.new;
		msg[3].postln; // = 0

this always gives zero. Shouldnt it run through the buffers phase? I could not find a way to change the phase, its always zero. So I guess nothing gets written into the buffer, since there is no chance that the conditions in the OSCDef are getting excecuted? Thank you!

Oh, I think I see it. bufnum is a synth argument but I didn’t supply it in the play call.

So it should be a = { ... }.play(args: [bufnum: b]);.

It happened when I was testing that b was using buffer number 0, so I didn’t notice.

hjh

1 Like

Hey, I have one more question:

what would be a good approach to start the OSCdef and the Synth at the same time?

a = Synth(\record, [\bufnum, b]);

so I basically need the opposite of this:

a.onFree { OSCdef(\phase).free };

I tried out something like the example below, but it doesnt work. Also doesnt work when I assign the OSCdef to a variable.

OSCdef(\phase).enable

Any suggestions? Thank you very much! :slight_smile:

There’s actually not any need for an opposite of this… which might sound strange at first.

We need onFree because a synth can end for many reasons: by being .free-d, by an envelope reaching its end with a doneAction that removes the node, by some other part of the code freeing the group to which the synth belongs. onFree reacts to all of those (which could be initiated from anywhere).

For a hypothetical onStart, you need a specific synth instance to attach it to. If it’s an old instance created somewhere else, then… it has already started, so there’s no point in attaching onStart now (the moment already passed). So it must be a new instance. There’s only one way for a specific synth instance to be created: for your code to do it.

If your code is doing it, then you can simply create or manipulate the OSCdef or OSCFunc at the same time. It’s completely under your control – no need for any Synth object magic.

Probably the easiest way is to write a function that does both:

~startScope = {
    ~recSynth = Synth(\record, [\bufnum, b]);
    OSCdef(......);
    ~recSynth  // return value
};

hjh

2 Likes