Custom sampler: playing chords

Hi guys,

it has been some month I’m working on this sampler synth of mine and I’m improving and testing it day by day also thanks to your precious help (Dictionary and array inside patterns, Orchetral Texture v1).

Now I want to add a new feature: I want it to be played in a polyphonic way.

I now that if I evaluate the following code (I’m using the SSO samples here) I obtain a chord:

(
Pbindef(
	\test,
	\instrument, \default,
	\degree, Pseq([[0,4,7]], inf),
	\dur, 1,
	\amp, 0.25
);
)
Pbindef(\test).play;

but if I do the same with my code I get an error.

(
// create data structure to load samples
~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="/path/to/your/SSO/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: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]; });
	destination;
	// TODO: add here a check evaluation function in order to check if the samples array
	// has been succesfully filled or not
};
)

~samples = ~loadfunc.(\celli_pizz, "Celli", "celli-piz-rr1-", ~samples);

(
SynthDef(\oneshot_player, {
	|
	out=0, amp=0.5, gate=1, buf, rate=1.0, pan=0.0
	|
	var sig;
	sig = PlayBuf.ar(1, buf, BufRateScale.ir(buf)*rate, 1, doneAction:2);
	sig = sig * amp;
	Out.ar(out, Pan2.ar(sig, pan));
}).add;
);

(
Pbindef(
	\celli,
	\instrument, \oneshot_player,
	\scale, Scale.major,
	\root, Pfunc({~root}),
	\octave, 5,
	\degree, Pseq([[0,4,7]],inf),
	\index,  Pfunc({ |e| ~samples[\celli_pizz][\midinotes].indexIn( e.use{ ~midinote.()} ) }),
	\buf, Pindex(~samples[\celli_pizz][\buffers], Pkey(\index)),
	\rate, (Pfunc{ |e| e.use {~midinote.()}} - Pindex(~samples[\celli_pizz][\midinotes], Pkey(\index))).midiratio,
	\amp, 0.9,
	\dur, 1,
	\out, 0,
);
)

Pbindef(\celli).play;

How to solve it? I think is something related to simultaneous events to be created by the event pattern but I don’t know how can I change it in order to scale it up to play both single notes and chords.

Thank you very much for your support

Can you post the error you’re seeing?

1 Like

Thank you @scztt.
below the error I get:

ERROR: Non Boolean in test.
RECEIVER:
Instance of Array {    (0x563279093028, gc=0C, fmt=01, flg=00, set=02)
  indexed slots [3]
      0 : false
      1 : false
      2 : false
}

PROTECTED CALL STACK:
	Meta_MethodError:new	0x563276e80780
		arg this = MustBeBooleanError
		arg what = nil
		arg receiver = [ false, false, false ]
	Object:mustBeBoolean	0x5632762eb700
		arg this = [ false, false, false ]
	a FunctionDef	0x563277d6a800
		sourceCode = "<an open Function>"
		arg elem = 36
		arg i = 0
	ArrayedCollection:do	0x563277d98200
		arg this = [ 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72 ]
		arg function = a Function
		var i = 0
	Collection:detectIndex	0x563277d6a500
		arg this = [ 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72 ]
		arg function = a Function
	SequenceableCollection:indexIn	0x563277dfbb80
		arg this = [ 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72 ]
		arg val = [ 60.0, 67.0, 72.0 ]
		var i = nil
		var a = nil
		var b = nil
		var j = nil
	a FunctionDef	0x563277f7f540
		sourceCode = "<an open Function>"
	Function:prTry	0x56327713a400
		arg this = a Function
		var result = nil
		var thread = a Routine
		var next = nil
		var wasInProtectedFunc = false
	
CALL STACK:
	MethodError:reportError
		arg this = <instance of MustBeBooleanError>
	Nil:handleError
		arg this = nil
		arg error = <instance of MustBeBooleanError>
	Thread:handleError
		arg this = <instance of Routine>
		arg error = <instance of MustBeBooleanError>
	Thread:handleError
		arg this = <instance of Routine>
		arg error = <instance of MustBeBooleanError>
	Thread:handleError
		arg this = <instance of Routine>
		arg error = <instance of MustBeBooleanError>
	Thread:handleError
		arg this = <instance of Routine>
		arg error = <instance of MustBeBooleanError>
	Object:throw
		arg this = <instance of MustBeBooleanError>
	Function:protect
		arg this = <instance of Function>
		arg handler = <instance of Function>
		var result = <instance of MustBeBooleanError>
	Environment:use
		arg this = <instance of Environment>
		arg function = <instance of Function>
		var result = nil
		var saveEnvir = <instance of Environment>
	PatternProxy:embedInStream
		arg this = <instance of PatternProxy>
		arg inval = <instance of Event>
		arg embed = false
		arg default = nil
		var outval = nil
		var count = 0
		var pat = <instance of Pfunc>
		var test = true
		var resetTest = nil
		var stream = <instance of FuncStream>
	Routine:prStart
		arg this = <instance of Routine>
		arg inval = <instance of Event>
^^ The preceding error dump is for ERROR: Non Boolean in test.
RECEIVER: [ false, false, false ]

Any news?
someone can help me understand this error?
Thank you so much for your help

it’s hard to help debug this because you’ve got so much code here… have you tried removing things from your Pbindef until it no longer errors? what is the value of ~root? i don’t see it defined anywhere.

Thank you @VIRTUALDOG,
you are right, so much code here.
sorry, my fault, ~root is simply a way for me to define the root note for all the patterns but i forgot to substitute it here with and hardcoded value (say 0): it seems I’m not allowed to modify my first post so I cannot change it.

Thank you so much for your help, I really appreciate it.
I will try to debug it step by step.
I will let you know.

I’m going to guess that the issue is here:

\index,  Pfunc({ |e| ~samples[\celli_pizz][\midinotes].indexIn( e.use{ ~midinote.()} ) })

If indexIn uses if/then logic, then you can’t pass an array of note numbers into it.

But you could .collect over that array:

\index,  Pfunc({ |e| e.use { ~midinote.() }.asArray.collect { |note| ~samples[\celli_pizz][\midinotes].indexIn(note) } })

This is not tested and I’m not even sure that’s the issue, but it seems the riskiest place.

hjh

1 Like

Thank you @jamshark70, your code works for me!