I came to the same conclusion.
My first question was : Why this design ? Why not having \midinote
(and \note
) referring to \scale.value
instead of \scale
? Could Event.default be changed to .value
~scale
too (and the other ones) ?
So I made a workaround: in my prototype \note
function, I’m forcing the evaluation of the \scale
function and store it so the Event.default function don’t need to evaluate \scale
anymore.
This is working fine.
I also followed your suggestion with ~finish
. It is working fine also.
Now I wonder, which is the most robust approach, and which has the nicest syntax and semantics.
Protoype approach
(
p=(
\note: #{
// currentEnvironment.debug("environment");
if (~name.isNil) {
var note, scale;
"Into \\note proto (without name)".postln;
// v-- FIX : Event.defaut.note does not `.value` scale, so the function is never executed.
// So I force its execution and store the value.
~scale=~scale.value;
Event.default.note.debug("note returned by proto");
} {
var tmp;
"Into \\note proto (with name)".postln;
tmp=~name.asChordEvent;
currentEnvironment.putAll(tmp);
tmp.note;
};
},
\scale: #{
// currentEnvironment.debug("environment");
if (~name.isNil) {
"Into \\scale proto (without name)".postln;
Event.default.scale.debug("scale returned by proto");
// ~scale=#[0,2,4,5,7,9,11].debug("scale returned by proto");
} {
var tmp;
"Into \\scale proto (with name)".postln;
tmp=~name.asChordEvent;
currentEnvironment.putAll(tmp);
tmp.scale;
}
},
\gtranspose: #{
// currentEnvironment.debug("environment");
if (~name.isNil) {
"Into \\gtranspose proto (without name)".postln;
Event.default.gtranspose.debug("gtranspose returned by proto");
// 0.debug("gtranspose returned by proto");
} {
var tmp;
"Into \\gtranspose proto (with name)".postln;
tmp=~name.asChordEvent;
currentEnvironment.putAll(tmp);
tmp.gtranspose;
}
},
\nature: #{
// currentEnvironment.debug("environment");
if (~name.isNil) {
"Into \\nature proto (without name)".postln;
nil; // non standard, so should return nil
} {
var tmp;
"Into \\nature proto (with name)".postln;
tmp=~name.asChordEvent;
currentEnvironment.putAll(tmp);
tmp.nature;
}
},
\rootname: #{
// currentEnvironment.debug("environment");
if (~name.isNil) {
"Into \\rootname proto (without name)".postln;
nil; // non standard, so should return nil
} {
var tmp;
"Into \\rootname proto (with name)".postln;
tmp=~name.asChordEvent;
currentEnvironment.putAll(tmp);
tmp.rootname;
}
},
)/*.proto_(Event.default)*/;
Pbind(
//\scale, Scale.minor, \degree, Pn(Pseq([0,2,4]),3,\next),\gtranspose, Pgate(Pseq([0,2,3]),inf,\next),
\name, Pseq(["C7add9","C#°7"]),
)
.play(protoEvent: p);
)
~finish approach
(
f={|e|
// v-- don't work, because finds those keys through Event.default, the prototyp
// [e.includesKey(\note),e.includesKey(\degree),e.includesKey(\scale),e.includesKey(\name),].postln;
// if (e.note.isNil.and(e.degree.isNil).and(e.scale.isNil).and(e.name.notNil)) {
if (e.name.notNil.and(
e.asKeyValuePairs.select{|item,i| i.even.and([\note,\degree,\scale].includes(item))}.size==0)) {
var tmp;
"Expansion required".postln;
tmp=e.name.asChordEvent.debug("chord event");
e.putAll(tmp);
}
{
"No expansion required".postln;
};
}
Pbind(
// \scale, Scale.minor, \degree, Pn(Pseq([0,2,4]),3,\next), \gtranspose, Pgate(Pseq([0,2,3]),inf,\next),
\name, Pseq(["C7add9","C#°7"]),
\finish, {|e|
f.(e); // expand the "name" into scale, note, gtranspose if necessay (and possible)
e.debug("finish");
},
)
.play();
)
For both approach, the protoype and the finish function will be “hidden” in a class.