Max MSP — Copying regions of audio "buffer~" objects
I am trying to learn a bit of Max MSP and was looking for some guides on how to "copy a region" of a buffer into another on Max MSP. Since not many results came back and for my own benefit, I decided to document what I explored.
I have a “buffer~” object and would like to copy a region of it to another “buffer~”.
Max MSP provides the "duplicate" message, which can be used to copy the entirety of a buffer over, but this won't allow copying a single region.
I explored 4 different ways of doing this using Max, RNBO, Gen and JS. I tried to report timing information, but still need to validate if numbers translate to real usage performance/freezes.
Max implementation
The max implementation looks like this:
Given a "start" number and a "length" number, we first resize the destination buffer. This step may be skipped.
Then we have a "uzi" object which bangs for length times and use its index outlet and the start offset to peek into the source buffer and set the corresponding sample on destination. Multiple channels could be supported with another outer loop.
I used the timer object to time this and it reports success to copy 10s of 44.1kHz audio in around 100ms.
RNBO
The RNBO patch is exactly the same.
The same task seems to finish in less than 1 millisecond. However, due to lack of symbol message support in RNBO, the buffer names can't be dynamic in this implementation.
Get Pedro Tacla Yamada’s stories in your inbox
Join Medium for free to get updates from this writer.
gen~
The gen~ has two parts; on a codebox we have the following GenExpr code:
Buffer source_buffer;
Buffer destination_buffer;
History has_copied(0);
start = in2;
length = in3;
end = start + length;
if (in4 > 0) {
if (has_copied > 0) {
out1 = 1;
} else {
has_copied = 1;
for (i = 0; i < length; i += 1) {
index = start + i;
src_value = peek(source_buffer, index, 0);
poke(destination_buffer, src_value, i, 0);
}
out1 = 1;
}
} else {
out1 = 0;
has_copied = 0;
}
out2 = in4;The expressions receive source and destination buffers and keep track of whether they are currently copying.
One of the inlets, in4, indicates via a signal toggling between 0–1 whether copying should start. If it starts we update appropriate state and outputs and loop through the buffers.
Additional objects handle converting external event bangs into the signals this GenExpr will expect:
This reports completion in around 10ms, often under 3ms. It supports dynamic buffer names. Possibly there are delays on how this gets measured and I imagine copying might block audio processing.
JS
The JS implementation looks like this:
src = Buffer('js_copy_src');
dest = Buffer('js_copy_dest');
inlets = 3;
outlets = 2;
start = 0;
end = 1000;
function source_buffer(name) {
src = Buffer(name);
}
function destination_buffer(name) {
dest = Buffer(name);
}
function msg_int(value) {
if (inlet == 0) {
start = value;
} else {
end = value;
}
}
function bang() {
const outputLength = end - start;
for (var i = 0; i < outputLength; i += 1) {
var sample = src.peek(0, i + start, 1);
dest.poke(0, i, sample);
}
outlet(0, 1);
outlet(1, outputLength);
}We handle a few messages to switch buffer names and start/end values, then loop over them when there’s a bang. This performs similarly to the Max implementation but is perhaps a bit slower.
Code you can copy
You can find the patch on https://github.com/yamadapc/max-msp-spike-copy-buffers.







