feat: unified note/scale source of truth
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
// =====================================================================
|
||||
// palette/note.scd -- Source de verite : gammes + degree->note
|
||||
//
|
||||
// Une seule definition de la resolution degree->note MIDI, consommee par
|
||||
// les resolveurs (~ccNote concert, ~lpNote launchpad, ~fpResolve air-piano).
|
||||
// Independant : aucun pre-requis, chargeable dans LES DEUX chemins de boot
|
||||
// (LIVE et data-only) AVANT tout consommateur.
|
||||
//
|
||||
// NB : les arrays ~scaleXxx de palette/scales.scd (melodies LIVE, 8 notes
|
||||
// sur-mesure) sont distincts et NON touches ici.
|
||||
// Un seul bloc top-level (compatible .load).
|
||||
// =====================================================================
|
||||
(
|
||||
// Gammes en OFFSETS de demi-tons depuis la fondamentale.
|
||||
~scales = IdentityDictionary[
|
||||
\minor -> [0, 2, 3, 5, 7, 8, 10],
|
||||
\dorian -> [0, 2, 3, 5, 7, 9, 10],
|
||||
\phrygian -> [0, 1, 3, 5, 7, 8, 10],
|
||||
\penta -> [0, 2, 4, 7, 9], // pentatonique majeure (concert)
|
||||
\minorPent -> [0, 3, 5, 7, 10],
|
||||
\blues -> [0, 3, 5, 6, 7, 10],
|
||||
\japanese -> [0, 2, 5, 7, 9],
|
||||
\hijaz -> [0, 1, 4, 5, 7, 8, 10],
|
||||
\hirajoshi -> [0, 2, 3, 7, 8]
|
||||
];
|
||||
|
||||
// degree -> note MIDI, avec report d'octave (mod plancher gere les degres
|
||||
// negatifs : -1 descend). root en MIDI, octave en octaves entieres.
|
||||
~noteFor = { |degree, scaleName = \minor, root = 60, octave = 0|
|
||||
var sc = ~scales[scaleName] ? ~scales[\minor];
|
||||
var d = degree.asInteger;
|
||||
(root + ((octave + (d div: sc.size)) * 12) + sc[d % sc.size]).asInteger;
|
||||
};
|
||||
|
||||
~freqFor = { |degree, scaleName = \minor, root = 60, octave = 0|
|
||||
~noteFor.(degree, scaleName, root, octave).midicps;
|
||||
};
|
||||
|
||||
"[OK] palette/note.scd : ~scales, ~noteFor, ~freqFor".postln;
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
// Headless unit test for palette/note.scd.
|
||||
// Run: sclang sound_algo/tests/test_note.scd
|
||||
(
|
||||
var ok = true, base;
|
||||
base = thisProcess.nowExecutingPath.dirname.dirname ++ "/";
|
||||
(base ++ "palette/note.scd").load;
|
||||
|
||||
// root note of minor pentatonic at root 60
|
||||
if(~noteFor.(0, \minorPent, 60, 0) != 60) { ok = false; "FAIL: root".postln };
|
||||
// degree 5 wraps one octave on a 5-note scale -> 72
|
||||
if(~noteFor.(5, \minorPent, 60, 0) != 72) { ok = false; "FAIL: wrap".postln };
|
||||
// minor degree 2 = +3 semitones -> 63
|
||||
if(~noteFor.(2, \minor, 60, 0) != 63) { ok = false; "FAIL: minor".postln };
|
||||
// concert parity: minor root 45 degree 2 -> 48 (old ~ccNote value)
|
||||
if(~noteFor.(2, \minor, 45, 0) != 48) { ok = false; "FAIL: cc".postln };
|
||||
// negative degree descends with floored mod: -1 on minor -> 58 (Bb, one step below C)
|
||||
if(~noteFor.(-1, \minor, 60, 0) != 58) { ok = false; "FAIL: neg".postln };
|
||||
// freq matches midicps
|
||||
if(~freqFor.(0, \minorPent, 60, 0) != 60.midicps) { ok = false; "FAIL: freq".postln };
|
||||
|
||||
if(ok) { "PASS test_note".postln } { "TESTS FAILED".postln };
|
||||
0.exit;
|
||||
)
|
||||
Reference in New Issue
Block a user