I like to share one of my first experiment in using samples from SSO (https://github.com/peastman/sso).
Here’s the code I’ve used: http://sccode.org/1-5bz .
Here a recording of the piece: https://soundcloud.com/nicolaariutti/orchestral-texture-1 .
I like to share one of my first experiment in using samples from SSO (https://github.com/peastman/sso).
Here’s the code I’ve used: http://sccode.org/1-5bz .
Here a recording of the piece: https://soundcloud.com/nicolaariutti/orchestral-texture-1 .
I find it very interesting that you found an approach that doesn’t involve an external sampler process to load the sonatina soundfont.
Thank you @shiihs.
Happy that it is interesting for you.
Let me know if you have advices to improve the code or in case you are going to use it in your own project
Well… I’d remove some of the code duplication, e.g. something like
(note: a bug/limitation appears to exist in the syntax highlighter on the forum…)
// Orchestral Sound texture v1
(
s.options.numBuffers = 1024 * 32;
//increase the memory available to the server
s.options.memSize = 8192 * 128;
s.waitForBoot({
//LOAD SAMPLES //////////////////////////////////////////////////////////////////
// create data structure to load samples
var patterns = ();
var loadfunc;
if (~samples.notNil) {
~samples.do({
|key|
if (~samples[key].notNil) {
~samples[key][\buffers].do({
| buffer |
if (buffer.notNil) {
buffer.free;
};
});
};
});
};
~samples = ();
// add function to load samples
loadfunc = {
// loaded samples will be stored under samples[key]
| key, relpath, namefilter, destination, basepath="~/development/supercollider/sso/sso/Sonatina Symphonic Orchestra/Samples" |
var path = PathName(basepath +/+ relpath);
var tmp_buffers = path.entries.removeAllSuchThat({|item| item.fileName.contains(namefilter); });
var tmp_array = tmp_buffers.collect({
| item, index |
var d = ();
var string = item.fileNameWithoutExtension;
var output = string.findRegexp("[abcdefgABCDEFG]#?[0123456789]");
var noteNameAll = output[0][1];
var octNumber = noteNameAll.rotate(1)[0].asString.asInteger;
var noteName = noteNameAll[0].asString;
var isSharp = noteNameAll.contains("#"); // boolean
var midiNumber = (octNumber +1) * 12;
switch( noteName,
"c", { midiNumber = midiNumber+0; },
"d", { midiNumber = midiNumber+2; },
"e", { midiNumber = midiNumber+4; },
"f", { midiNumber = midiNumber+5; },
"g", { midiNumber = midiNumber+7; },
"a", { midiNumber = midiNumber+9; },
"b", { midiNumber = midiNumber+11; },
);
if (isSharp) {midiNumber = midiNumber + 1;};
[noteNameAll, noteName, isSharp, octNumber, midiNumber];//.debug("[fullname, note, sharp, octave, midinum]");
d[\midi] = midiNumber.asInteger;
d[\note] = noteNameAll;
d[\buffer] = Buffer.readChannel(s, item.fullPath.debug("reading file"), channels:1);
d;
});
key.debug("loading");
tmp_array.sortBy(\midi);
destination[key] = ();
destination[key][\lowest_note] = tmp_array[0][\midi];
destination[key][\buffers] = tmp_array.collect({|item| item[\buffer]; });
destination[key][\midinotes] = tmp_array.collect({|item| item[\midi]; });
destination;
};
// Load all samples
~samples = loadfunc.(\violins_1st_sus, "1st Violins", "1st-violins-sus-", ~samples);
~samples = loadfunc.(\violins_2nd_sus, "2nd Violins", "2nd-violins-sus-", ~samples);
~samples = loadfunc.(\celli_sus, "Celli", "celli-sus-", ~samples);
~samples = loadfunc.(\basses_sus, "Basses", "basses-sus-", ~samples);
~samples = loadfunc.(\oboes_sus, "Oboes", "oboes-sus-", ~samples);
~samples = loadfunc.(\bassoons_sus, "Bassoons", "bassoons-sus-", ~samples);
~samples = loadfunc.(\clarinets_sus, "Clarinets", "clarinets-sus-", ~samples);
s.sync;
SynthDef(\play_orchestra, {
|out=0,amp=0.5, gate=1, buf, rate=1.0,pan=0.0, atk=0.01, dcy=0.2, sus=0.7, rel=0.1|
var sig, env;
env = EnvGen.ar(Env.adsr(atk, dcy, sus, rel), gate, doneAction:2);
sig = Mix.ar(PlayBuf.ar(2, buf, BufRateScale.ir(buf)*rate, 1, doneAction:0));
sig = sig * amp * env;
Out.ar(out, Pan2.ar(sig, pan));
}).add;
// a synth to play buffers in a granular way
// NOTE: you will need buffers to be loaded as MONO !!!
SynthDef(\grain_buffer, {
|out=0, gate=1, amp=0.9, buf, atk=5, rel=5, rate=1, pan=0.0|
var sig, env;
var density = LFNoise0.kr(25).range(1, 5);
var trigger = Impulse.kr( density );
var pos = 0.5 + TRand.kr(trigger, -0.35, 0.35);
var length = 1 + TRand.kr(trigger, 0.25, 0.35);
env = EnvGen.kr(Env.asr(atk, 1, rel, -4), gate, doneAction:2);
sig = GrainBuf.ar(2,
trigger,
length,
buf,
rate,
pos,
2,
pan: pan);
sig = sig * amp * env;
Out.ar(out, sig);
}).add;
SynthDef(\verb, {
|out=0, mix=0.6, room=0.8, damp=0.6|
var in;
in = In.ar(out, 2);
in = FreeVerb.ar(in, mix, room, damp);
Out.ar(out, Pan2.ar(in));
}).add;
s.sync;
patterns[\violins_1st_sus] = Pbind(
\instrument, \grain_buffer,
\scale, Scale.major,
\root, Pfunc({~root}),
\octave, Pwrand([5,6,7],[1,0.5, 0.125].normalizeSum, inf),
\degree, Pxrand([1, 4, 5], inf)-1,
\index, Pfunc { |e| ((e.use{ ~midinote.()} - ~samples[\violins_1st_sus][\lowest_note] - 1)/3).floor },
\buf, Pindex(~samples[\violins_1st_sus][\buffers], Pkey(\index)),
\rate, (Pfunc{ |e| e.use {~midinote.()}} - Pindex(~samples[\violins_1st_sus][\midinotes], Pkey(\index))).midiratio,
\amp, Pwhite(0.05, 0.1, inf),
\dur, Pwhite(4,8, inf),
\type, Pwrand([\note, \rest], [1,0.25].normalizeSum, inf),
\atk, Pkey(\dur) * 0.6,
\rel, Pkey(\atk),
\out, 0,
\pan, Pwhite(-0.8, 0.2,inf)
);
patterns[\violins_2nd_sus] = Pbind(
\instrument, \grain_buffer,
\scale, Scale.major,
\root, Pfunc({~root}),
\octave, Pwhite(5,6,inf).round(1),
\degree, Pxrand([1, 4, 5], inf)-1,
\index, Pfunc { |e| ((e.use{ ~midinote.()} - ~samples[\violins_2nd_sus][\lowest_note]-1)/3).floor },
\buf, Pindex(~samples[\violins_2nd_sus][\buffers], Pkey(\index)),
\rate, (Pfunc{ |e| e.use {~midinote.()}} - Pindex(~samples[\violins_1st_sus][\midinotes], Pkey(\index))).midiratio,
\amp, Pwhite(0.05, 0.1, inf),
\dur, Pwhite(4,8, inf),
\type, Pwrand([\note, \rest], [1,0.25].normalizeSum, inf),
\atk, Pkey(\dur) * 0.6,
\rel, Pkey(\atk),
\out, 0,
\pan, Pwhite(-0.2, 0.8,inf)
);
patterns[\celli_sus] = Pbind(
\instrument, \grain_buffer,
\scale, Scale.major,
\root, Pfunc({~root}),
\octave, Pwrand([3, 4], [0.25, 1].normalizeSum, inf),
\degree, Pseq([1], inf)-1,
\index, Pfunc { |e| ((e.use{ ~midinote.()} - ~samples[\celli_sus][\lowest_note]-1)/3).floor },
\buf, Pindex(~samples[\celli_sus][\buffers], Pkey(\index)),
\rate, (Pfunc{ |e| e.use {~midinote.()}} - Pindex(~samples[\celli_sus][\midinotes], Pkey(\index))).midiratio,
\amp, Pwhite(0.1, 0.2, inf),
\dur, Pwhite(2,7, inf),
\type, Pwrand([\note, \rest], [1,0.125].normalizeSum, inf),
\atk, Pkey(\dur) * 0.6,
\rel, Pkey(\atk),
\out, 0,
\pan, Pwhite(-0.3, 0.3,inf)
);
patterns[\basses_sus] = Pbind(
\instrument, \grain_buffer,
\scale, Scale.major,
\root, Pfunc({~root}),
\octave, 3,
\degree, Pseq([1], inf)-1,
\index, Pfunc { |e| ((e.use{ ~midinote.()} - ~samples[\basses_sus][\lowest_note]-1)/3).floor },
\buf, Pindex(~samples[\basses_sus][\buffers], Pkey(\index)),
\rate, (Pfunc{ |e| e.use {~midinote.()}} - Pindex(~samples[\basses_sus][\midinotes], Pkey(\index))).midiratio,
\amp, Pwhite(0.1, 0.2, inf),
\dur, Pwhite(4,8, inf),
\type, Pwrand([\note, \rest], [1,0.125].normalizeSum, inf),
\atk, Pkey(\dur) * 0.6,
\rel, Pkey(\atk),
\out, 0,
\pan, 0.0
);
patterns[\oboes_sus] = Pbind(
\instrument, \grain_buffer,
\scale, Scale.major,
\root, Pfunc({~root}),
\octave, Pwhite(5,6,inf).round(1),
\degree, Pwrand([1,5],[0.3, 0.6], inf)-1,
\index, Pfunc { |e| ((e.use{ ~midinote.()} - ~samples[\oboes_sus][\lowest_note]-1)/3).floor },
\buf, Pindex(~samples[\oboes_sus][\buffers], Pkey(\index)),
\rate, (Pfunc{ |e| e.use {~midinote.()}} - Pindex(~samples[\oboes_sus][\midinotes], Pkey(\index))).midiratio,
\amp, Pwhite(0.1, 0.2, inf),
\dur, Pwhite(4,8, inf),
\type, Pwrand([\note, \rest], [1,0.25].normalizeSum, inf),
\atk, Pkey(\dur) * 0.6,
\rel, Pkey(\atk),
\out, 0,
\pan, Pwhite(-0.2, 0.8,inf)
);
patterns[\clarinets_sus] = Pbind(
\instrument, \grain_buffer,
\scale, Scale.major,
\root, Pfunc({~root}),
\octave, Pwhite(5,6,inf).round(1),
\degree, Pwrand([1,5],[0.3, 0.6], inf)-1,
\index, Pfunc { |e| ((e.use{ ~midinote.()} - ~samples[\clarinets_sus][\lowest_note]-1)/3).floor },
\buf, Pindex(~samples[\clarinets_sus][\buffers], Pkey(\index)),
\rate, (Pfunc{ |e| e.use {~midinote.()}} - Pindex(~samples[\clarinets_sus][\midinotes], Pkey(\index))).midiratio,
\amp, Pwhite(0.2, 0.3, inf),
\dur, Pwhite(4,8, inf),
\type, Pwrand([\note, \rest], [1,0.25].normalizeSum, inf),
\atk, Pkey(\dur) * 0.6,
\rel, Pkey(\atk),
\out, 0,
\pan, Pwhite(-0.8, 0.2,inf)
);
patterns[\bassoons_sus] = Pbind(
\instrument, \grain_buffer,
\scale, Scale.major,
\root, Pfunc({~root}),
\octave, Pwhite(4,5,inf).round(1),
\degree, Pwrand([1,5],[0.3, 0.6], inf)-1,
\index, Pfunc { |e| ((e.use{ ~midinote.()} - ~samples[\bassoons_sus][\lowest_note]-1)/3).floor },
\buf, Pindex(~samples[\bassoons_sus][\buffers], Pkey(\index)),
\rate, (Pfunc{ |e| e.use {~midinote.()}} - Pindex(~samples[\bassoons_sus][\midinotes], Pkey(\index))).midiratio,
\amp, Pwhite(0.1, 0.2, inf),
\dur, Pwhite(4,8, inf),
\type, Pwrand([\note, \rest], [1,0.25].normalizeSum, inf),
\atk, Pkey(\dur) * 0.6,
\rel, Pkey(\atk),
\out, 0,
\pan, Pwhite(-0.4, 0.4,inf)
);
~root = 0;
~ensemble = Pfx(Ppar([
patterns[\violins_1st_sus],
patterns[\violins_2nd_sus],
patterns[\oboes_sus],
patterns[\clarinets_sus],
patterns[\bassoons_sus],
patterns[\celli_sus],
patterns[\basses_sus]
],inf), \verb).asEventStreamPlayer;
~ensemble.play;
});
);
~root = 0; // also try to use 5, 7 and 9
~ensemble.play;
(
~ensemble.stop;
~samples.do({
|key|
~samples[key][\buffers].do({
| buffer |
buffer.free;
});
});
)
Since the patterns you use all look very similar, it’s possible to simplify those as well (variations on a base pattern) but I didn’t do it here, because in general I expect different patterns to be more independent.
Just made a Lua2SC release for reading sounfonts because of this thread!!
What a wonderful idea! How much of the .sfz spec does it implement?
This is the list of processed opcodes by the moment
{
[ampeg_attack]=0
[ampeg_decay]=0
[ampeg_delay]=0
[ampeg_hold]=0
[ampeg_release]=0.1
[ampeg_start]=0
[ampeg_sustain]=100
[ampeg_vel2attack]=0
[ampeg_vel2decay]=0
[ampeg_vel2delay]=0
[ampeg_vel2hold]=0
[ampeg_vel2release]=0
[ampeg_vel2sustain]=0
[default_path]=0
[group]=0
[hikey]=0
[hirand]=0
[hivel]=0
[key]=0
[lokey]=0
[loop_end]=0
[loop_start]=0
[lorand]=0
[lovel]=0
[offset]=0
[pan]=0
[pitch_keycenter]=0
[pitch_random]=0
[sample]=0
[seq_length]=0
[seq_position]=0
[transpose]=0
[tune]=0
[volume]=0
[xfin_hikey]=0
[xfin_hivel]=0
[xfin_lokey]=0
[xfin_lovel]=0
[xfout_hikey]=0
[xfout_hivel]=0
[xfout_lokey]=0
[xfout_lovel]=0}
I would appreciate anyone giving it a try!!
Unfortunately I don’t speak LUA at all at the moment so I’m not sure how high the learning curve would be, but I’d like to take a stab at creating an sclang based .sfz reader at some point (maybe a good summer holiday project, if outside temperatures don’t kill my energy levels). I think it’d be a very welcome addition to supercollider, especially for people who (like me) sometimes just want to toy with algorithmic composition without having to delve into sound design first.
Dont be afraid about Lua. Its learning curve is really not step.
In case you need information it is here:
https://www.lua.org/manual/5.1/
https://www.lua.org/pil/contents.html
And also: You can execute the examples in Lua2SC without knowing Lua at all!!
also a note for me:
take a look at this SC tutorial where it shows an alternative way of making multi-sample instruments!
@moscardo great work! Are you thinking about how to create glissandos or simulate strange things like multiphonics ? Have you also tried using a sound bank based on extended techniques ? Do you know if there is any literature explaining the details about how commercial samplers works?
Thank you @fmiramar for your interest,
great work! Are you thinking about how to create glissandos or simulate strange things like multiphonics ?
I was thinking about that even if I’m not planning to continue working on that till I’ll need it for a next new project. We will see…
For the moment, an empirical approach should be to sweep the rate parameter using a Pseg or something like that.
For sure I’ll keep this thread updated with all new improvements I’m gonna make
Your help, suggestions and support is also super appreciated!
I think that multiphonics is quite a complicated think to implement. As you have seen, the approach I’m using is strictly bind to the use of samples and while glissandos is something which can be solved from a “compositional” point of view (i think it can be addressed with some Pbind manipulation), multiphonics instead sounds to me as something should be faced from a synthesis perspective.
Have you also tried using a sound bank based on extended techniques ?
I confess: no. What kind of banks are those that use the extended technique? How this technique works? Could you give me some examples?
It would be great to test the system with different samples also because I’m looking for different and sometimes better sounds to use in my compositions. Do you know if are there other free/open/libre sound banks I could use?
Do you know if there is any literature explaining the details about how commercial samplers works?
I think some documentation might exists, don’t know a specific source but I think you can find something doing some research. One of the places I would look for would also be a software patent database.
Just tried it very nice.Thanks for posting that.
I learned quite a bit from that iterative way to load buffers.
This bank and example is quite useful. I managed to load the xilophone in stereo and made some basic phasing rhythms a-la Steve Reich.
The most famous that I know is this one, but there are smaller ones as well (IRCAM, Spitfire):
I really would like to know what happens under the hood in this thing, but I could not find a proper literature…
[long post]
In recent projects I have been working on this sampler idea within SuperCollider again, and from time to time I find myself making improvements to the code: here is an update on what we have already seen in the course of this thread.
In particular, I found myself improving the function for loading samples with the aim of making the function compatible with different naming conventions of audiofiles for different sample banks I was working with.
In general, it is my understanding that the name of an audio file within a sample bank can be composed of several fields that define, from time to time:
In addition, the name of the note field, might contain the identifier for the alteration (sharp). I have noticed that while some sample banks use ‘#
’ as a convention for notating sharp notes, others use the lowercase ‘s
’ instead.
It should also be considered that some samples are designed to be used alternately with others (especially samples for percussive instruments or for certain sound effects). In these cases, there is a field identifying the index within the series to be used for random selection (the round robin). Sometimes it is called RRx
(where x
is used here in place of a number, like 0, 1, 2 or 3), other times they use rrx
or r0x
, etc…
All of these fields, in turn, could be separated from each other by means of a delimiter character (generally the ‘_
’ but can also be the ‘-
’).
The function for loading samples now offers the possibility of specifying:
if you want to test the function please follow the passages below
First of all you need to download (one of all of) these three samplebanks:
These sound libraries were chosen for their permissiveness in terms of the licence to use.
In spite of the work done towards the community, the quality of the samples and the permissiveness of the licence under which it was released, I would in no way recommend the Philharmonia Sound Library because:
In the past few months I have pointed out these anomalies by writing them a message but the only support I got was a sentence like “there’s not much we can do about it, they are files that were created a long time ago”. too bad
After you have downloaded the samplesbanks and saved them to your PC then you have to save the paths (you will nee them later to load the samples)
~path_vscoBasepath = "/home/nicola/Musica/samples/VSCO-2-CE-master/";
~path_vcslBasepath = "/home/nicola/Musica/samples/VCSL/VCSL-1.2.2-RC/";
~path_ssoBasepath = "/home/nicola/Musica/samples/Sonatina Symphonic Orchestra/Samples/";
You create an empy dictionary where to store the samplebanks
if (~my_samples.notNil) {
~my_samples.do({
|key|
if (~my_samples[key].notNil) {
~my_samples[key][\buffers].do({
| buffer |
if (buffer.notNil) {
buffer.free;
};
});
};
});
};
~my_samples = ();
You evaluate the (updated!!!) loading function
~func_load_samplebank = {
| key, relpath, delimiterChar, sharpChar, namefilter, destination, basepath="/my/placeholder/path/here/" |
var path, tmp_buffers, tmp_array;
path = PathName(basepath +/+ relpath);
tmp_buffers = path.entries.removeAllSuchThat({
|item|
var fileNameComponentsAsSymbols = item.fileNameWithoutExtension.split( delimiterChar ).collect({|e| e.asSymbol});
fileNameComponentsAsSymbols.includesAll( namefilter );
});
tmp_array = tmp_buffers.collect({
| item, index |
var d, string, output, noteNameAll, octNumber, noteName, isSharp, midiNumber;
d = ();
string = item.fileNameWithoutExtension;
output = string.findRegexp("[abcdefgABCDEFG]"++sharpChar++"?[0123456789]");
noteNameAll = output[0][1];
octNumber = noteNameAll.rotate(1)[0].asString.asInteger;
noteName = noteNameAll[0].asString;
isSharp = noteNameAll.contains( sharpChar ); // boolean
midiNumber = (octNumber +1) * 12;
switch( noteName.toLower,
"c", { midiNumber = midiNumber+0; },
"d", { midiNumber = midiNumber+2; },
"e", { midiNumber = midiNumber+4; },
"f", { midiNumber = midiNumber+5; },
"g", { midiNumber = midiNumber+7; },
"a", { midiNumber = midiNumber+9; },
"b", { midiNumber = midiNumber+11; },
);
if (isSharp) {midiNumber = midiNumber + 1;};
//[noteNameAll, noteName, isSharp, octNumber, midiNumber].postln;//.debug("[fullname, note, sharp, octave, midinum]");
d[\midi] = midiNumber.asInteger;
d[\note] = noteNameAll;
// uncomment the line below if you want to see a more verbose debug
//d[\buffer] = Buffer.readChannel(s, item.fullPath.debug("reading file"), channels:0);
d[\buffer] = Buffer.readChannel(s, item.fullPath, channels:0);
d;
});
key.debug("loading");
tmp_array.sortBy(\midi);
destination[key] = ();
destination[key][\buffers] = tmp_array.collect({|item| item[\buffer]; });
destination[key][\midinotes] = tmp_array.collect({|item| item[\midi]; });
// check evaluation function in order to check if the samples array
// has been succesfully filled or not
if( destination[key][\buffers].isEmpty,
{ postf("ERROR: loading '%' samplebank", key); },{"loading succesfull".postln;}
);
destination;
};
);
And eventually load the samples. Here we will try our hand at using the loading function to load samples from all 3 libraries to test how it works.
Loading from VSCO
let’s try loading some String samples. Violins, Violas and Cellos are using this naming convention respectively: VlnEns_susVib_G4_v2.wav
, ViolaEns_susvib_G4_v2_1.wav
and susvib_G3_v3_1.wav
~func_load_samplebank.(\vsco_vln_susVib, "/Strings/Violin Section/susVib", $_, $#, [\VlnEns, \susVib, \v2], ~my_samples, basepath:~path_vscoBasepath);
~func_load_samplebank.(\vsco_viole_susVib, "/Strings/Viola Section/susvib", $_, $#, [\ViolaEns, \susvib, \v2], ~my_samples, basepath:~path_vscoBasepath);
~func_load_samplebank.(\vsco_cello_susVib, "/Strings/Cello Section/susvib", $_, $#, [\susvib, \v3], ~my_samples, basepath:~path_vscoBasepath);
test the samples banks
~my_samples[\vsco_vln_susVib][\buffers].size;
~my_samples[\vsco_vln_susVib][\midinotes].size;
~my_samples[\vsco_vln_susVib][\buffers].do({|b| b.path.postln});
~my_samples[\vsco_vln_susVib][\buffers][0].play;
~my_samples[\vsco_viole_susVib][\buffers].size;
~my_samples[\vsco_viole_susVib][\midinotes].size;
~my_samples[\vsco_viole_susVib][\buffers].do({|b| b.path.postln});
~my_samples[\vsco_viole_susVib][\buffers][0].play;
~my_samples[\vsco_cello_susVib][\buffers].size;
~my_samples[\vsco_cello_susVib][\midinotes].size;
~my_samples[\vsco_cello_susVib][\buffers].do({|b| b.path.postln});
~my_samples[\vsco_cello_susVib][\buffers][0].play;
Loading from VCSL
Let’s try with handchimes and three different dynamics for the glockenspiel. Their are respectively followind these naming conventions sus_G#5_r01_main.wav
and glock_soft_G6_01.wav
.
Let’s load and test them like this:
~func_load_samplebank.(\vcsl_handchimes, "/Idiophones/Struck Idiophones/Hand Chimes/", $_, $#, [\sus, \main, \r01], ~my_samples, basepath:~path_vcslBasepath);
~func_load_samplebank.(\vcsl_glock_soft, "/Idiophones/Struck Idiophones/Glockenspiel/", $_, $#, [\glock, \soft], ~my_samples, basepath:~path_vcslBasepath);
~func_load_samplebank.(\vcsl_glock_medium, "/Idiophones/Struck Idiophones/Glockenspiel/", $_, $#, [\glock, \medium], ~my_samples, basepath:~path_vcslBasepath);
~func_load_samplebank.(\vcsl_glock_loud, "/Idiophones/Struck Idiophones/Glockenspiel/", $_, $#, [\glock, \loud], ~my_samples, basepath:~path_vcslBasepath);
~my_samples[\vcsl_handchimes][\buffers][0].play
~my_samples[\vcsl_glock_soft][\buffers].size
~my_samples[\vcsl_glock_soft][\buffers][2].play
~my_samples[\vcsl_glock_medium][\buffers][2].play
~my_samples[\vcsl_glock_loud][\buffers][2].play
Let’s try now with a much longer sample like this bowed vibrophone (naming convention: Vibes_bowed_A2_rr1_Main.wav
):
~func_load_samplebank.(\vscl_vib_bowed, "/Idiophones/Struck Idiophones/Vibraphone/Bowed/", $_, $#, [\Vibes, \bowed, \Main], ~my_samples, basepath:~path_vcslBasepath);
~my_samples[\vscl_vib_bowed][\buffers][2].play
Loading from SSO
Sonatina’ Violins samples audifiles are named something like: 1st-violins-sus-a#6.wav
so we load them and test them like this:
~func_load_samplebank.(\sso_violins, "/1st Violins/", $-, $#, ["1st".asSymbol, \violins, \sus], ~my_samples, basepath:~path_ssoBasepath);
~my_samples[\sso_violins][\buffers].size
~my_samples[\sso_violins][\buffers][3].play