SAMP Update + Buffer Allocation Fix

- Updating to the latest version of SAMP (Bank)
- Reserving an absurdly high number of buffers for samples
This commit is contained in:
2024-02-24 01:23:32 +01:00
parent 14aa95f3cf
commit a15036079d
2 changed files with 723 additions and 325 deletions

View File

@ -3,13 +3,341 @@
* I have added a method to list the samples in the bank.
*/
// Bank : Singleton {
// classvar <>root, <>extensions, <>lazyLoading=true;
// var <paths, buffers, <channels, <foundRoot, <foundRootModTime, markersCache, atCache;
//
// *initClass {
// root = "/Users/bubo/.config/livecoding/samples".standardizePath;
// extensions = ["wav", "aiff", "aif", "flac", "mp3"];
// }
//
// *list {
// PathName(Bank.root).entries.do({
// arg item; item.folderName.postln;
// })
// }
//
// *new {
// |path, channels|
// ^super.new(path, channels);
// }
//
// printOn {
// |stream|
// super.printOn(stream);
// stream << "[%]".format(paths.size)
// }
//
// init {
// buffers = [];
// ServerQuit.add(this);
// ServerBoot.add(this);
// }
//
// lazyLoading_{
//
// |lazy|
//
// if (lazyLoading != lazy) {
// lazyLoading = lazy;
// this.prUpdateBuffers();
// }
// }
//
// buffers {
// ^paths.size.collect {
// |i|
// this.bufferAt(i)
// }
// }
//
// gui {
// var view, button, name;
//
// this.lazyLoading = false;
//
// view = View().layout_(GridLayout.rows());
// paths.do {
// |path, i|
// name = PathName(path).fileNameWithoutExtension;
// view.layout.add(
// DragSource()
// .object_("%(%)[%]".format(
// this.class.name,
// channels !? {
// "'%', %".format(this.name, channels)
// } ?? {
// "'%'".format(this.name)
// },
// name.quote
// ))
// .string_(name)
// .canFocus_(true)
// .font_(Font("M+ 2c", 10, false))
// .minWidth_(100)
// .mouseDownAction_({ |v| if (v.focus) { this.bufferAt(i).play } })
// .keyDownAction_({ this.bufferAt(i).play })
// .focusGainedAction_({ this.bufferAt(i).play }),
// (i / 4).floor,
// i % 4
// );
// };
// ScrollView(bounds:500@600).canvas_(view).front;
// }
//
// set {
// |inChannels|
// var currentRoot, currentExtensions, foundPaths=[], attempts = List();
//
// if (channels != inChannels) {
// channels = inChannels;
// this.clear();
// };
//
// if (foundRootModTime.notNil) {
// if (File.mtime(foundRoot) == foundRootModTime) {
// ^this; // no changes, so early return!
// }
// };
//
// currentExtensions = this.class.extensions;
// currentRoot = thisProcess.nowExecutingPath;
//
// if (currentRoot.notNil) {
// currentRoot = PathName(currentRoot).parentPath;
// foundPaths = Require.resolvePaths(name.asString, currentRoot, currentExtensions, attempts);
// };
//
// if (currentRoot.notNil) {
// currentRoot = currentRoot +/+ name.asString;
// foundPaths = Require.resolvePaths("*", currentRoot, currentExtensions, attempts);
// };
//
// if (foundPaths.isEmpty) {
// currentRoot = this.class.root;
// foundPaths = Require.resolvePaths(name.asString, currentRoot, currentExtensions, attempts);
// };
//
// if (foundPaths.isEmpty) {
// currentRoot = currentRoot +/+ name.asString;
// foundPaths = Require.resolvePaths("*", currentRoot, currentExtensions, attempts);
// };
//
// if (foundPaths.isEmpty) {
// foundRoot = nil;
// foundRootModTime = nil;
// "No samples found, attempted paths: ".warn;
// attempts.do {
// |a|
// "\t%.{%}".format(a, currentExtensions.join(",")).warn
// };
// } {
// foundRoot = currentRoot;
// foundRootModTime = File.mtime(foundRoot) ?? {0};
// };
//
// foundPaths = foundPaths.sort({
// |a, b|
// var pair;
// #a, b = [a, b].collect {
// |p|
// p = p.toLower;
// p = p.split($.).first;
// p = p.split($/).reverse;
// };
// pair = [a, b].flop.detect({
// |pair|
// pair[0] != pair[1]
// });
// pair !? {
// pair[0] < pair[1]
// } ?? { false }
// });
//
// if (paths != foundPaths) {
// paths = foundPaths;
// atCache = ();
// this.prUpdateBuffers();
// }
// }
//
// clear {
// paths = [];
// atCache = ();
// this.prUpdateBuffers()
// }
//
// bufferAt {
// |index|
// ^buffers !? {
// buffers[index] ?? {
// if (Server.default.serverRunning) {
// if (channels.isNil) {
// buffers[index] = Buffer.read(Server.default, paths[index])
// } {
// buffers[index] = Buffer.readChannel(Server.default, paths[index], channels:Array.series(channels));
// };
// };
// buffers[index];
// }
// }
// }
//
// at {
// |key|
// var index;
//
// if (key.isArray && { key.isString.not }) {
// ^key.collect(this.at(_))
// };
//
// if (key.isInteger) {
// index = key
// } {
// index = atCache[key.asSymbol];
// if (index.isNil) {
// index = paths.detectIndex({
// |path|
// key.asString.toLower.replace("*", ".*").matchRegexp(
// path.asString.toLower
// );
// });
// atCache[key.asSymbol] = index;
// }
// };
//
// ^this.bufferAt(index);
// }
//
// markers {
// ^markersCache ?? {
// markersCache = paths.collect({
// |path|
// SoundFile(path).extractMarkers
// })
// }
// }
//
// wrapAt {
// |index|
// if (index.isInteger) {
// index = index % buffers.size;
// };
// ^this.at(index);
// }
//
// do {
// |...args|
// buffers.size.collect(this.bufferAt(_)).do(*args)
// }
//
// collect {
// |...args|
// ^buffers.size.collect(this.bufferAt(_)).collect(*args)
// }
//
// prUpdateBuffers {
// if (Server.default.serverBooting or: {
// Server.default.hasBooted && Server.default.serverRunning.not
// }) {
// Server.default.doWhenBooted {
// this.prUpdateBuffers();
// };
// ^this;
// };
//
// if (Server.default.serverRunning.not) {
// buffers = [];
// } {
// if (paths.size > buffers.size) { buffers = buffers.extend(paths.size) };
//
// paths.do {
// |path, i|
// var buffer;
//
// buffer = buffers[i];
//
// if (path.notNil) {
// if (lazyLoading.not) {
// this.bufferAt(i)
// }
// } {
// if (buffer.notNil) {
// buffer.free;
// buffers[i] = buffer = nil;
// }
// }
// };
//
// buffers.extend(paths.size);
// }
// }
//
// doOnServerBoot {
// if (paths.size > 0) {
// buffers = [];
// this.prUpdateBuffers();
// "***Loaded samples for %***".format(this.asString).postln;
// }
// }
//
// doOnServerQuit {
// buffers = [];
// }
//
// pat {
// |keyPat|
// ^Pindex(Pseq([this], inf), keyPat)
// }
//
// asBuffer {
// ^this.singleSampleWrap(nil)
// }
//
// asControlInput {
// |...args|
// ^this.prSingleSampleWrap(\asControlInput, *args)
// }
//
// play {
// |...args|
// ^this.prSingleSampleWrap(\play, *args)
// }
//
// prSingleSampleWrap {
// |method ...args|
// var buffer;
// if (buffers.size == 1) {
// buffer = this.bufferAt(0);
//
// if (method.isNil) {
// ^buffer
// } {
// if (buffer.numFrames.isNil) {
// fork {
// Server.default.sync;
// buffer.performList(method, args)
// };
// ^nil;
// } {
// ^buffer.performList(method, args)
// }
// }
// } {
// Error("Trying to % a bank with multiple buffers".format(method)).throw;
// }
// }
// }
Bank : Singleton {
classvar <>root, <>extensions, <>lazyLoading=true;
var <paths, buffers, <channels, <foundRoot, <foundRootModTime, markersCache, atCache;
var metadata;
*initClass {
root = "/Users/bubo/.config/livecoding/samples".standardizePath;
extensions = ["wav", "aiff", "aif", "flac", "mp3"];
extensions = ["wav", "aiff", "aif", "flac", "ogg"];
}
*list {
@ -23,6 +351,10 @@ Bank : Singleton {
^super.new(path, channels);
}
size {
^paths.size
}
printOn {
|stream|
super.printOn(stream);
@ -30,73 +362,21 @@ Bank : Singleton {
}
init {
var currentRoot, currentExtensions, foundPaths=[], attempts = List();
var fixedName;
buffers = [];
ServerQuit.add(this);
ServerBoot.add(this);
}
lazyLoading_{
|lazy|
if (lazyLoading != lazy) {
lazyLoading = lazy;
this.prUpdateBuffers();
}
}
buffers {
^paths.size.collect {
|i|
this.bufferAt(i)
}
}
gui {
var view, button, name;
this.lazyLoading = false;
view = View().layout_(GridLayout.rows());
paths.do {
|path, i|
name = PathName(path).fileNameWithoutExtension;
view.layout.add(
DragSource()
.object_("%(%)[%]".format(
this.class.name,
channels !? {
"'%', %".format(this.name, channels)
} ?? {
"'%'".format(this.name)
},
name.quote
))
.string_(name)
.canFocus_(true)
.font_(Font("M+ 2c", 10, false))
.minWidth_(100)
.mouseDownAction_({ |v| if (v.focus) { this.bufferAt(i).play } })
.keyDownAction_({ this.bufferAt(i).play })
.focusGainedAction_({ this.bufferAt(i).play }),
(i / 4).floor,
i % 4
);
};
ScrollView(bounds:500@600).canvas_(view).front;
}
set {
|inChannels|
var currentRoot, currentExtensions, foundPaths=[], attempts = List();
if (channels != inChannels) {
channels = inChannels;
this.clear();
};
fixedName = name.asString
.replace("[", "\\[")
.replace("]", "\\]")
.replace("(", "\\(")
.replace(")", "\\)");
if (foundRootModTime.notNil) {
if (File.mtime(foundRoot) == foundRootModTime) {
if (foundRoot.notNil and: { File.exists(foundRoot) } and: { File.mtime(foundRoot) == foundRootModTime }) {
^this; // no changes, so early return!
}
};
@ -104,25 +384,31 @@ Bank : Singleton {
currentExtensions = this.class.extensions;
currentRoot = thisProcess.nowExecutingPath;
// Try an absolute path resolve first
foundPaths = Require.resolvePaths(fixedName, [], currentExtensions, attempts);
if (foundPaths.isEmpty.not) {
currentRoot = PathName(fixedName).parentPath;
} {
if (currentRoot.notNil) {
currentRoot = PathName(currentRoot).parentPath;
foundPaths = Require.resolvePaths(name.asString, currentRoot, currentExtensions, attempts);
foundPaths = Require.resolvePaths(fixedName, currentRoot, currentExtensions, attempts);
};
if (currentRoot.notNil) {
currentRoot = currentRoot +/+ name.asString;
if (foundPaths.isEmpty and: { currentRoot.notNil }) {
currentRoot = currentRoot +/+ fixedName.asString;
foundPaths = Require.resolvePaths("*", currentRoot, currentExtensions, attempts);
};
if (foundPaths.isEmpty) {
currentRoot = this.class.root;
foundPaths = Require.resolvePaths(name.asString, currentRoot, currentExtensions, attempts);
foundPaths = Require.resolvePaths(fixedName, currentRoot, currentExtensions, attempts);
};
if (foundPaths.isEmpty) {
currentRoot = currentRoot +/+ name.asString;
currentRoot = currentRoot +/+ fixedName;
foundPaths = Require.resolvePaths("*", currentRoot, currentExtensions, attempts);
};
};
if (foundPaths.isEmpty) {
foundRoot = nil;
@ -155,40 +441,157 @@ Bank : Singleton {
} ?? { false }
});
if (paths != foundPaths) {
paths = foundPaths;
atCache = ();
paths = foundPaths;
this.metadata;
}
prGetMetadata {
^paths.collect {
|path|
SoundFile.openRead(path) !? {
|sf|
var e = (
sampleRate: sf.sampleRate,
numChannels: sf.numChannels,
numFrames: sf.numFrames,
sampleFormat: sf.sampleFormat,
headerFormat: sf.headerFormat,
);
sf.close;
e
};
}
}
metadata {
|key|
metadata = metadata ?? this.prGetMetadata(_);
if (key.isNil) {
^metadata
} {
^metadata[this.indexForKey(key)]
}
}
lazyLoading_{
|lazy|
if (lazyLoading != lazy) {
lazyLoading = lazy;
this.prUpdateBuffers();
}
}
buffers {
^paths.size.collect {
|i|
this.bufferAt(i)
}
}
gui {
var view, sampleViews, button, name;
var playNode;
this.lazyLoading = false;
view = View().layout_(GridLayout.rows());
paths.do {
|path, i|
var sampleView;
name = PathName(path).fileNameWithoutExtension;
view.layout.add(
sampleView = DragSource()
.object_("%(%)[%]".format(
this.class.name,
channels !? {
"'%', %".format(this.name, channels)
} ?? {
"'%'".format(this.name)
},
name.quote
))
.string_(name)
.canFocus_(true)
.font_(Font("M+ 2c", 10, false))
.minWidth_(100)
.mouseDownAction_({
|v|
if (v.focus) {
playNode.free;
playNode = this.bufferAt(i).play
}
})
.focusGainedAction_({ this.bufferAt(i).play(mul:[1, 1]) }),
(i / 4).floor,
i % 4
);
sampleViews = sampleViews.add(sampleView);
};
view.keyUpAction = {
|view, char, modifiers, unicode, keycode, key|
switch(
keycode.postln,
123, {
sampleViews[-1 + (sampleViews.detectIndex({ |v| v.hasFocus }) ? 0)].focus
},
124, {
sampleViews[ 1 + (sampleViews.detectIndex({ |v| v.hasFocus }) ? 0)].focus
},
125, {
sampleViews[ 4 + (sampleViews.detectIndex({ |v| v.hasFocus }) ? 0)].focus
},
126, {
sampleViews[-4 + (sampleViews.detectIndex({ |v| v.hasFocus }) ? 0)].focus
}
)
};
ScrollView(bounds:500@600).canvas_(view).front;
}
set {
|inChannels|
if (channels != inChannels) {
channels = inChannels;
this.clear();
this.prUpdateBuffers();
};
}
clear {
paths = [];
atCache = ();
this.prUpdateBuffers()
this.prUpdateBuffers();
}
bufferAt {
|index|
var sf;
^buffers !? {
buffers[index] ?? {
if (Server.default.serverRunning) {
buffers = buffers.extend(index + 1, nil);
if (channels.isNil) {
buffers[index] = Buffer.read(Server.default, paths[index])
buffers[index] = Buffer.read(Server.default, paths[index]);
buffers[index].numChannels = metadata[index][\numChannels];
buffers[index];
} {
buffers[index] = Buffer.readChannel(Server.default, paths[index], channels:Array.series(channels));
};
};
buffers[index];
}
}
}
at {
indexForKey {
|key|
var index;
if (key.isArray && { key.isString.not }) {
if (key.isArray && key.isString.not) {
^key.collect(this.at(_))
};
@ -207,7 +610,12 @@ Bank : Singleton {
}
};
^this.bufferAt(index);
^index
}
at {
|key|
^this.bufferAt(this.indexForKey(key));
}
markers {
@ -227,15 +635,8 @@ Bank : Singleton {
^this.at(index);
}
do {
|...args|
buffers.size.collect(this.bufferAt(_)).do(*args)
}
collect {
|...args|
^buffers.size.collect(this.bufferAt(_)).collect(*args)
}
do { |...args| buffers.size.collect(this.bufferAt(_)).do(*args) }
collect { |...args| ^buffers.size.collect(this.bufferAt(_)).collect(*args) }
prUpdateBuffers {
if (Server.default.serverBooting or: {
@ -270,6 +671,10 @@ Bank : Singleton {
}
};
buffers[paths.size..].do {
|b|
b.free;
};
buffers.extend(paths.size);
}
}
@ -291,19 +696,10 @@ Bank : Singleton {
^Pindex(Pseq([this], inf), keyPat)
}
asBuffer {
^this.singleSampleWrap(nil)
}
asControlInput {
|...args|
^this.prSingleSampleWrap(\asControlInput, *args)
}
play {
|...args|
^this.prSingleSampleWrap(\play, *args)
}
// Single buffer support
asBuffer { ^this.singleSampleWrap(nil) }
asControlInput { |...args| ^this.prSingleSampleWrap(\asControlInput, *args) }
play { |...args| ^this.prSingleSampleWrap(\play, *args) }
prSingleSampleWrap {
|method ...args|
@ -325,7 +721,7 @@ Bank : Singleton {
}
}
} {
Error("Trying to % a bank with multiple buffers".format(method)).throw;
Error("Trying to % a SAMP with multiple buffers".format(method)).throw;
}
}
}

View File

@ -14,7 +14,7 @@ Boot {
{
"-> Booting using default server configuration".postln;
s = Server.default;
s.options.numBuffers = 2048 * 512;
s.options.numBuffers = (2048 * 2048) * 2; // Some arbitrary number
s.options.memSize = 8192 * 64;
s.options.numWireBufs = 2048;
s.options.maxNodes = 1024 * 32;
@ -25,6 +25,8 @@ Boot {
{
"-> Booting using user server configuration".postln;
s = Server.default;
// Imposing a very high number of buffers!
serverOptions.numBuffers = (2048 * 512) * 2;
s.options = serverOptions;
},
);
@ -72,7 +74,7 @@ Boot {
~sp = ~sp ?? 'default';
~nb = ~nb ?? 0;
~buf = Bank(~sp)[~nb % Bank(~sp).paths.size];
if (~buf.numChannels == 1) {
if (Bank(~sp).metadata[~nb % Bank(~sp).size][\numChannels] == 1) {
~instrument = \player;
} {
~instrument = \splayer;