Question about using a dictionary to store buffers

I have a dictionary object I have loaded a set of samples into which are all of the same BPM. I can play back those samples at different BPM by changing the rate of the sample after it’s referenced/called in the dictionary.

However, I also have a second set of samples that are of a different BPM than the first. Is it possible to store these samples in a different dictionary object with the rate alongside, so that whenever they are called, they automatically will play back at a certain rate (i.e. one that will make them match the BPM’s from the first set of samples), instead of having to specify the rate after they’ve been referenced/called in the dictionary object?

The reason I ask is because I have code that works with the first set of samples, but I’m trying to be able to just call the second set of samples with the same code without having the change the rate at which they’re played back, because that will then change the rate of the first set as well, based on how my code is structured. So if the second set is already stored in their buffers with the correct matching rate, it should work. I’m hoping to avoid having to just change the rate of all the samples in the second set manually as they exist in the source .wav files, but do it with code without having to alter the original .wav files. Maybe I’m missing something obvious?

Ways to do this totally automatically without changing any of your code, if I understand correctly, are by setting the buffer sample rates to both take into account their real sample rates and also their original BPM (assuming you use BufRateScale somewhere), or resample the buffers so everything plays at the same BPM. Both of these are things you would probably want to do to the files themselves.

A much simpler way that involves changing a little bit of your code but not altering the wav files is to put a rate adjustment somewhere in your synthdef, if e.g. you want to play a 100 BPM file at 120bpm you need to adjust playback rate by a factor of 1.2 (basically every time you play a buffer adjust rate by baseBPM / bufferBPM)

If you share a minimal example of how you are playing these buffers we could probably help more specifically

rough code snippet:

SynthDef.new(\bufplay, {
arg buf=0, rate=1, amp=1;
var sig;
sig = PlayBuf.ar(2, buf, BufRateScale.ir(buf) * rate, doneAction:2);
sig = sig * amp;
Out.ar(0,sig);
}).add;

(
d = Dictionary.new;
d.add(\wavSamples → PathName(“C:/path/to/wav/files”).entries.collect({
arg sf;
Buffer.read(s, sf.fullPath);
});
)

(
d2 = Dictionary.new;
d2.add(\wavSamplesAtDifferentBPM → PathName(“C:/path/to/wav/filesDifferentBPM”).entries.collect({
arg sf;
Buffer.read(s, sf.fullPath);
});
)

v = Pdef(
\beatsAttempt,
Pbind(
\amp, 0.5
\instrument, \bufplay,
\dur, 0.5,
\buf,
Pseq([
d[\wavSamples][0],
d[\wavSamples][1],
d[\wavSamples][2],
d2[\wavSamplesAtDifferentBPM][0],
d2[\wavSamplesAtDifferentBPM][1],
]);
… etc

Mayve I could use a something like \rate, Pfunc{ --code to detect sample selected and change rate accordingly-- }, but would that be able to change the rate before the Pseq selects said sample? I think it would only be able to act on it on the next Pseq loop, if that makes sense.

1 Like

My suggestion would be to create a data structure that binds the buffer to the bpm. In this case there are only two values, so an array wouldn’t be too confusing. (If you have more complex structures, I’d favor a dictionary or event [event is a subclass of dictionary with a convenient syntax].) I’m also thinking array here because there’s a Pbind trick.

You might not even need a dictionary this way.

d = Array.new;

~addDir = { |dir, bpm = 120|
    PathName(dir).entries.do { |path|
       d = d.add([Buffer.read(s, path.fullPath), bpm]);
    }
};

After doing ~addDir.(path, bpm) multiple times, you should have an array like:

[
    [ Buffer(...), 120 ],
    [ Buffer(...), 144 ]
]

Then the Pbind can include:

    [\buf, \bpm], Prand(d, inf),

And then the event would know what the BPM is for each buffer.

Sorting them into subcollections by BPM may or may not be ideal – if you need that, you could still duplicate the BPM in the per-buffer data structure – “redundant” but it will greatly simplify the Pbind.

hjh

PS Please use

```
code tags
```

… for code excerpts.

1 Like

Thanks, that is a neat trick with the Pbind, if I’m understanding it correctly, I might be able to try this.

The general idea is to structure the data according to the requirement. “I want to know a buffer’s bpm” implies an association between the buffer and the bpm. The association you came up with:

  • keyReferencingBPM → array of buffers

… makes it next to impossible to backtrack from the buffer to the containing layer.

I proposed instead to have some entity representing the buffer and auxiliary data about the buffer. You could have a list of events:

[ (buf: Buffer(…), bpm: 120), … more of these ]

Labeling all the properties is generally a good idea, but takes a little more work in the Pbind to unpack. An array can be unpacked natively in Pbind, and if there are only two properties, it’s pretty easy to keep track of which is which. To me the array is a bit of a compromise structurally (e.g., a weakness in Max and Pd is that data acquire meaning based on their positions in unlabeled lists, which doesn’t always scale up well) but an acceptable compromise given the simplicity of the use case.

Hope I’m not rambling – data structure is a tricky thing at times, thought it might be helpful to explain a bit more of why I suggested that approach.

hjh

1 Like