Buffer UGen: no buffer data

I’m running into trouble with playing audio files. My project is structured as follows:

I have an.scd file which loads audio files into buffers and defines functions to generate patterns which use those buffers, e.g.

(
var buf1 = Buffer.readChannel("/file1.aif");
var buf2 = Buffer.readChanenl("/file2.aif");

~buf1Pattern = Pbind(\buf, buf1, ...);
~buf2Pattern = Pbind(\buf, buf2, ...);
)

I use those patterns in another .scd file which imports this file and plays the patterns. This happens in a single code block, which makes sure that with a single ctrl+enter keypress I can load and play the latest version of the patterns.

(
"./patterns.scd".load
~buf1Pattern.value.play;
)

The issue I’m seeing is that playing the pattern sometimes results in the error “Buffer UGen: no buffer data” and no audio output. The only way to fix this is to restart the server. This initially happened non-deterministically and quite infrequently, but as the number of buffers has increased (I have about 30 now) it’s gotten to the point where it’s completely deterministic and I can never play audio twice in a row without restarting the server.

I attempted to fix this by adding logic to wait for all the buffers to load using Buffer.readChannel's action callback, which ensures that I only start playing patterns once all the buffers are fully loaded. This alleviated the problem in that now the first play after starting the server is always successful, but it didn’t fix the issue for any of the subsequent plays. Here’s the polling logic I’m using, where ~onLoad is the callback passed to Buffer.readChannel. The .postln are there to sanity check that we always start with 0 loaded buffers and end up with numberOfBuffers loaded buffers.

loadedBuffers = 0;
~onLoad = { loadedBuffers = loadedBuffers + 1; };
~waitForBuffers = {
	| action |
	Routine({
		while({ loadedBuffers < numberOfBuffers }, {
			loadedBuffers.postln;
			1.wait;
		});
		loadedBuffers.postln;
		1.wait;
		action.value;
	}).play;
};

I’m 99% sure the issue has nothing to do with the file paths or the files themselves, as it’s non deterministic and restarting the server allows me to play the full pattern which uses all of my samples.

Does this have to do with reloading the buffers on each execution of my main code block? The reason I’m doing this is that when I make changes to the file defining the patterns, I need to re-import it to get the most up to date patterns. I could move buffer loading to a specific code block which I only execute once, but this would mean
a) polluting the global namespace with global variables for each buffer
b) having to execute two code blocks (one to load the buffers and one to play the patterns) every time I start a new session or restart the server

Yes, you should only read the buffers once, at the beginning of your session.

I think the best practice here is to

  • load all your buffers into a single Array
  • store the array into a global variable (~myBuffers or something), so you refer to individual buffers as ~myBuffers[0], ~myBuffers[1] etc)

I think it’s best to only load buffers once if you are not really changing them all the time. Even though you are working with a relatively small number of sound files, you may end up hitting the maximum number of buffers allocated in the server if you are reloading them many many times in a single session. You could work around this with a Buffer.freeAll line inserted before you your buffer loading code, but it’s better design to remove redundant work from your code. That means separating your buffer loading code (which sounds like you really only need to do once at the beginning of the session) from your pattern definition code (which sounds like you are changing often within a single session).
In my own projects, even after I have it all finished and do not intend to do any further edits to the code, I still typically like to have a separate scd file to do all one-time server stuff (like loading buffers and SynthDefs), and another file to create all my Pbinds etc. I find it easier to debug this way.

Hope this helps,

Bruno

An alternate approach is to create an object that binds a pattern to the data/resources it needs. That’s the main topic of my chapter in The SuperCollider Book, if you have access to it.

Then the workflow is:

  1. Create object definitions for the processes. (“Process” = pattern + required data.) Buffers aren’t loaded at this time. “Process definitions” are like classes.
  2. Create instances of the processes. (Buffers are loaded here, per process instance – automatically, by the process’s constructor function.)
  3. Play the processes.

1 and 2 can be done in the same block.

One benefit is that you can delete and reload individual processes without reloading the entire environment.

The code is a bit heavier-weight but also more stable and reliable, because patterns and their resources are kept together (instead of patterns looking for external resources which may or may not have been loaded correctly).

If you can’t get hold of the SC Book, I have a couple of short intro pages here.

^^ Those are different pages btw :laughing:

hjh

Thanks for the replies!

An alternate approach is to create an object that binds a pattern to the data/resources it needs.

This is pretty much already the case in my code, I simplified it for this post but I actually use “pattern factory” functions. I don’t use classes but the buffers are vars in a private code block so they’re still encapsulated. For example:

(
var sample = Buffer.read("./sample.aif");
~samplePattern = {
    | some, args, that, define, how, to play, it |
    Pbind(\sample, sample, ...)
}
)

The problem with this approach is that it ties reloading pattern definitions to reloading buffers, so while it has the benefit you mentioned (separates launching and stopping patterns from reloading the pattern code), it doesn’t solve my problem.

Yes, you should only read the buffers once, at the beginning of your session.

This makes sense to me as I already have a code block I run at startup to pick the right soundcard and launch the server, so the buffer loading logic could go there. The drawback is that this means a little bit more mental overhead since you have to run a different code block every time you add or change a sample.

You could work around this with a Buffer.freeAll line inserted before you your buffer loading code

This fixed the issue completely, thanks a million!

it’s better design to remove redundant work from your code.

This does cause a ~1 second load time on every play, but having a single automatic “ok I changed something, now please play the result” button is invaluable to me. I can already imagine changing or adding a sample file, not hearing the change, and spending 5 minutes trying to debug the patterns before realizing I forgot to run the “load buffers” code block again.

load all your buffers into a single Array

I would not recommend this approach, as it means your buffers end up with just integer ids (the array indexes) instead of names. Unless you’re only ever doing something like Prand(allMySamples).

I wouldn’t claim that my framework solves everyone’s problems to an equal degree, but you might be defining the nature of your problem, and acceptable solutions, a bit narrowly.

One – ddwChucklib processes can be deleted, edited and reloaded individually. My experience has been that it’s a lot friendlier than “oh drat, I have to reload the whole setup to change one value in one Pbind.”

This is the way I’ve done it but other ways are possible.

// setup file

(
Proto {
    ... stuff...
} => PR(\aProcess);

BP(\aProc).free;
PR(\aProcess) => BP(\aProc);
);

(
Proto {
    ... stuff...
} => PR(\otherProcess);

BP(\oProc).free;
PR(\otherProcess) => BP(\oProc);
);

The whole setup file can be run at once, and you can run a single block by itself. Thus many edits don’t have to touch processes that are working fine.

Two – In ddwChucklib, process patterns can be parameterized (BPStream) so you can change (some of) their behavior without reloading. Anything that reduces the need to reload from scratch also reduces the severity of the problems you’re facing.

hjh

2 Likes

When referring to samples by name is important, I tend to use Dictionary or Identity dictionary instead of a plain Array.

As a tangent, i rely on the buffer’s path to give me some meaningful info. I’ve not gone overboard in organising my soundfile library – though I can imagine one might want to – but the filename and the name of the parent directory, and in some cases that of the grandparent and even the great-grandparent, tell me a lot about the sound. They’re always right there in the Buffer object, you don’t have to manage anything at all (and so it doesn’t matter if you collect them in arrays or dictionaries or sets or …)

Cheers!
eddi
https://alln4tural.bandcamp.com

Going on with the tangent :slight_smile: , in our laptop orchestra here we typically load thousands of samples at the beginning of each session. The goal is to have a large sound bank readily available during live coding improvisations. For example, we have been using Dirt Samples which has 2000+ short sound files ready to go and it’s easy to add as a Quark, and players often add their custom samples too.

I wrote a little class called SCLOrkSamples to manage loading and organizing all samples. It uses an IdentityDictionary and does some housekeeping following an idiosyncratic naming convention that works for us — essentially I ignore file names and use parent folder names as the base name + a number; so all files inside a folder called ‘ab’ go into the dictionary with corresponding keys ab1, ab2, ab3, ab4… etc). If I recall correctly that’s more or less how Tidal Cycles deals with sample names, from which I took inspiration.

As a convention we also always save that big IdentityDictionary (created with know: true) under the global variable ‘d’, so that during live coding we can access any sample by name with d.ab1, d.ab2 etc. Also when we share code over the network during a performance, the same code is guaranteed to work on all players’ machines.

I also added a simple GUI that lets players browse the entire collection alphabetically and audition samples before using them.

The increase in the number of buffers available (from the default 1024) is done elsewhere before booting the server, so it’s not shown in that file.

2 Likes

Here’s the code I use to load samples from disk to buffers and storing objects in Dictionary - and then recalling the buffers by partial name of the sample:

(
// preload samples from smp/ folder under this file's location:
var smpPath = PathName(thisProcess.nowExecutingPath.dirname +/+ "smp");

// load samples 
"\n--- load samples: ...".postln;

// free all buffers to restart buffer count
~smpBuffers.do(_.free); 

// create Dictionary
~smpBuffers = Dictionary();

// iterate over each file in the folder
smpPath.filesDo({ |smpfile,i|
        
        // tell me what you are loading:
        postln("   " + i + smpfile.fileName );
        
        // add a sample into a buffer, store object to Dictionary
        ~smpBuffers.add(smpfile.fileName -> Buffer.readChannel(s,
                smpfile.fullPath, channels:[0]));
});

// function to partially match filename for buffers
~getSmp = { |regexp|
        ~smpBuffers.detect { |buf|
                regexp.matchRegexp(buf.path)
        }
};

When I need to pass a buffer to a Synth, like in a Pbind for example I just use ~getSmp.("samplename") like this:

Pbind(
   \instrument, \smplr, 
   \bufnum, ~getSmp.("perc07"),
   ...


perhaps that helps in avoiding buffers in an Array with just an index.