I must admit that I am a bit struggling to understand what is going on with arguments.
Sure. One tricky thing about patterns/events is that there are invisible calculations going on, and it isn’t immediately clear where those are happening.
The Pbind(ef) does only the operations written within it. There are no automatic conversions within Pbind.
p = Pbind(\degree, Pseries(0, 1, inf), \dur, 0.5).asStream;
e = p.next(Event.default);
-> ( 'degree': 0, 'dur': 0.5 )
After evaluation of the Pbind, the resulting event has not done anything to convert the scale degree into a frequency.
When you play an event based on the default event prototype, certain keys (e.g. \freq) are automatically calculated based on other keys.
e.play;
-> ( 'instrument': default, 'degree': 0, 'dur': 0.5, 'amp': 0.1,
'server': localhost, 'sustain': 0.4, 'isPlaying': true, 'hasGate': true, 'id': [ 1000 ],
'msgFunc': a Function, 'freq': 261.6255653006 )
Now there is a frequency (and sustain and amp). These automatic conversions are described in the help file I posted earlier.
So when you said “I was expecting it to be evaluated inside the Pbindef” – that’s a minor misconception to correct. Automatic frequency conversions are never part of a Pbind’s processing. (You might write them explicitly, as in my Pfunc, but then they are not automatic .)
I defined a “chord” argument as an array of integers ( I voluntarily didn’t call it “degree”). As the method .degreeToFreq can be applied to an integer or an array of integers.
Let me take a Socratic approach for a minute.
According to this logic, then, degreeToFreq
should be applied to any data types present in the event that understand it.
What about \dur
? If I write \dur, 0.1
, should it then convert this value to a tonic note raised by a semitone?
Of course not. \dur
is not relevant to pitch in any way.
Now, go back and read the help file about automatic conversions. The relevant event keys for pitch are \degree
, \note
, \midinote
, and \freq
. The event defines these specific names to be associated with pitch.
\chord
is not one of these. So, from the event’s point of view, \chord
is exactly as relevant to pitch as \dur
is – i.e., not relevant in the slightest. So the event does the same thing that it does with any keys that are not defined in the event to have a special meaning: it passes them to the synth without any other calculations being done.
I think your confusion is that in English, \chord
is likely to be about pitch. But the event doesn’t speak English. It only understands the terms defined within the event prototype. So it doesn’t matter what you think \chord
should mean. (Well… if you want \chord
to mean something, you can define another event type or an alternate event prototype. The point is that the default event prototype can’t possibly account for every user’s assumptions.)
Now I realised that if the freq arq is an array, when accessing it with Pbindef, not all the elements of the array are modified :
This is an extremely common point of confusion with events and arrays.
If you write (frequency: [a, b, c])
, it does not mean to pass the entire array to frequency
. The real meaning is to produce three separate synths, one for each given frequency. This is the common use case: each synth produces only one note, so multiple parameters should produce multiple notes.
If you want a single synth to produce multiple notes (a less common case), then the syntax must distinguish this case from the other case. (Computer languages generally don’t like it when you write two different meanings exactly the same way.) SC events tell the difference by wrapping the array in another level of array: \frequency, #[[90, 200, 300]]
. The outer level of array means “only one synth.” The inner level of array means “this is what to pass to the argument.”
(
SynthDef(\testArray, { |array = #[0, 0, 0], t_trig = 0, gate = 1|
Poll.kr(t_trig, array);
FreeSelf.kr(gate <= 0);
}).add;
)
e = (type: \on, instrument: \testArray, array: [1, 2, 3], trig: 1).play;
UGen(OutputProxy): 3
UGen(OutputProxy): 0
UGen(OutputProxy): 0
UGen(OutputProxy): 2
UGen(OutputProxy): 0
UGen(OutputProxy): 0
UGen(OutputProxy): 1
UGen(OutputProxy): 0
UGen(OutputProxy): 0
// ^^ crikey, that didn't work
e.put(\type, \off).play;
e = (type: \on, instrument: \testArray, array: [[1, 2, 3]], trig: 1).play;
UGen(OutputProxy): 1
UGen(OutputProxy): 2
UGen(OutputProxy): 3
e.putAll((type: \set, args: #[array, t_trig], array: [[10, 20, 30]], t_trig: 1)).play;
UGen(OutputProxy): 10
UGen(OutputProxy): 20
UGen(OutputProxy): 30
e.put(\type, \off).play;
So…
Pbindef(\testLayer2,
\type, \set,
\id, a.nodeID,
\args, #[\freq, \gate],
\chord, #[[0,2,4]],
\scale, Scale.major,
\freq, Pfunc ({ |ev| ev[\scale].degreeToFreq(ev[\chord], 40.midicps, 1) }),
// \freq, Pkey(\scale).degreeToFreq(Pkey(\chord), 60.midicps, 1) }), doesn't work
\gate, 1,
\dur, 1,
\amp, 0.6
).play(quant: 1);
Pbindef(\testLayer2,\chord, #[[1, 3, 5]]).play(quant: 1);
hjh