Dictionary and array inside patterns

Hi guys,
this is a follow up of my previous question.

I’m trying to create a sampler in SC to play back the Sonatina Symphonic Orchestra samples.
So I have to put all the sound files inside some sort of ordered list and then retrieve the correct one according to the midi note I want to play, possibly taking into account the playback ratio.

in order to do this I’ve starter loading the samples inside an array

~first_violins_piz_rr1 = SoundFile.collectIntoBuffers("/path/to/Sonatina Symphonic Orchestra/Samples/1st Violins/1st-violins-piz-rr1-*.wav", s);

Then I have created a new array where I’m storing buffers and some other related information like note name, corresponding midi number, etc…
Note: every element of the array is a dictionary so - I’ve thought - it would be easy to order it by one of its keys (see below).

~array = Array.newClear();

~first_violins_piz_rr1.do{
    |item, index|

    var d = Dictionary.new;
    var string = PathName(item.path).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].postln;
    d.add(\midi -> midiNumber.asInteger);
    d.add(\note -> noteNameAll);
    d.add(\buffer -> item);
    ~array = ~array.add(d);
}

Then I’ve sorted the array by the midi number

~array.sortBy(\midi);

I’ve defined the synth in order to play the samples back

SynthDef(\violins_pizz, {
    |buf, rate=1.0, amp=1.0|
    var sig = PlayBuf.ar(1, buf, BufRateScale.ir(buf)*rate, 1, doneAction:2);
    sig = sig * amp;
    Out.ar(0, sig!2);
}).add;

Now I’ve tried to create a Pbind to play the samples back but I’m not able to get the buffer from the specific element of the array.
I thought to use Pindex to get the specific Dictionary from the array according to a calculation on the midi note and then using the Pdict to get the value of the key \buffer but I think I’m doing something wrong here…
Note: once I will be able to access the \buffer key I will also be able to get the \midi value from the Dictionary in order to calculate the ratio.

a = Pbind(
    \instrument, \violins_pizz,
    \midinote, Prand((54..95),inf),  
    \index, ((Pkey(\midinote) - (55-1))/3).floor,
    \dictionary, Pindex(~array, Pkey(\index), inf),
    \buf, Pdict(Pkey(\dictionary), \buffer, inf), // doesn't work
    \ratio, ?,
    \amp, 0.2,
    \dur, 3
).play;

What I’m doing wrong in this Pbind?
Is there a way to retrieve a particular value from dictionaries inside patterns?
Is Pdict the pattern I should use? Is it meant to be used this way?


I’ve tried another way. Because I think Pdict is not the correct pattern to be used in this case (documentations seems to specify Pdict will only work if values inside it are patterns, which is not my case), I’ve tried to convert the dictionaries in arrays so to use Pindex two times:

So I’ve taken I step back and converted all the dictionary elements inside ~array into lists, just after having ordered elements inside ~array by the \midi key ~array.sortBy(\midi); :

~array.do{
    |item, index|
    [index, item.values].postln;
    ~array.put(index, item.values.asArray);
}

So I’ve tried using this Pbind, but it doesn’t work too :frowning:

b = Pbind(
    \instrument, \violins_pizz,
    \index, Pseq([0, 1, 2, 3], inf),
    \buf, Pindex(Pindex(~array, Pkey(\index), inf), 0, inf),
    \amp, 0.2,
    \dur, 3
).play;

What is the correct way to do what I want?
I wish I was able to explain myself…
Thank very much for your patience and support

I’m not sure if I get your question right. If what you are trying is to change the buffer from inside a Pbind you need to put your ~array of buffers inside the pattern in \buf like this:

\buf, Pseq(~array[...],inf)

and then place the array of midi notes for the pattern as the key. With just notes you would write it like this

\buf, Pseq([...your-pattern-of-midi-notes...],inf)

but for your ~array of buffers it would have to be:

\buf, Pseq(~array[[...your-pattern-of-midi-notes...]],inf)

So in your example it would be something like this:

b = Pbind(
    \instrument, \violins_pizz,
    \buf, Pseq(~array[[0, 1, 2, 3]], inf),
    \amp, 0.2,
    \dur, 3
).play;

I’m not sure if that’s what you where asking for. Hope that helped.

1 Like

Thank you @loopier for you reply

It doesn’t seems to work for me and I think the problem is that elements in my array are not buffers but dictionaries in this form:

Dictionary[ (buffer -> Buffer(3, 121654, 2, 44100, /path/to/Sonatina Symphonic Orchestra/Samples/Bass Clarinet/bass_clarinet-d2.wav)), (note -> d2), (midi -> 38) ]

However I’ve tried another way that is working, see below (basically I’m creating two arrays, one for indices and the other one for buffers). The code sounds quite redundant to me but it works.

Was wondering if it exist a pattern to select keys from a dictionary the same Pindex do for arrays.

(
var tmp_buffers;
var tmp_array;

tmp_buffers = SoundFile.collectIntoBuffers("/path/to/Sonatina Symphonic Orchestra/Samples/Bass Clarinet/bass_clarinet-*.wav", s);

tmp_array = Array.newClear();

tmp_buffers.do{
  |item, index|
  var d = Dictionary.new;
  var string = PathName(item.path).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
  //[noteNameAll, noteName, octNumber, isSharp].postln;
  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].postln;
  d.add(\midi -> midiNumber.asInteger);
  d.add(\note -> noteNameAll);
  d.add(\buffer -> item);
  tmp_array = tmp_array.add(d);
};

tmp_array.sortBy(\midi);

~bass_clarinet_lowest_note = tmp_array[0][\midi];
~bass_clarinet_buffers = Array.newClear();
~bass_clarinet_midinotes = Array.newClear();

tmp_array.do{
  |item, index|
  [index, item.values].postln;
  ~bass_clarinet_buffers = ~bass_clarinet_buffers.add(item[\buffer]);
  ~bass_clarinet_midinotes = ~bass_clarinet_midinotes.add(item[\midi]);
};

SynthDef(\playbuf, {
  |buf, rate=1, amp=0.5, gate=1|
  var sig, env;
	env = EnvGen.ar(Env.asr(0.01, 1, 0.5), gate, doneAction:2);
  sig = PlayBuf.ar(2, buf, BufRateScale.ir(buf)*rate, 1, doneAction:0);
  sig = sig * amp * env;
  Out.ar(0, sig!2);
}).add;

a = Pbind(
	\instrument, \playbuf,
	\midinote, Pseq((37..75), inf).trace,
	\index, ((Pkey(\midinote) - (~bass_clarinet_lowest_note-1))/3).floor,
	\buf, Pindex(~bass_clarinet_buffers, Pkey(\index)).trace,
	\rate, (Pkey(\midinote) - Pindex(~bass_clarinet_midinotes, Pkey(\index))).midiratio,
	\amp, 0.2,
	\dur, 0.25
).asEventStreamPlayer;
)

a.play;
a.stop;

The dictionary (first arg) of Pdict cannot be a Pattern (and Pkey is one). To switch that first arg of Pdict you need to wrap it in an Plazy, see e.g. discussions here for replacing Pseq lists and/or reps. (And then you have to decide/think when the replacement needs to happen in your use case.)

1 Like