cmake build system: visiblity support for clang
[supercollider.git] / SCClassLibrary / Common / Collections / Scale.sc
blobaa129eb2d7b2601453033258f1c7636316613a9c
1 Scale {
3         var <degrees, <pitchesPerOctave, <tuning, <>name;
5         *new { | degrees, pitchesPerOctave, tuning, name = "Unknown Scale" |
6                 ^super.new.init(degrees ? \ionian, pitchesPerOctave, tuning, name);
7         }
9         init { | inDegrees, inPitchesPerOctave, inTuning, inName |
10                 degrees = inDegrees.asArray.asInteger;
11                 pitchesPerOctave = inPitchesPerOctave ? this.guessPPO(degrees);
12                 name = inName;
13                 ^this.tuning_(inTuning ? Tuning.default(pitchesPerOctave));
14         }
16         *newFromKey { |key, tuning|
17                 var scale = ScaleInfo.at(key);
18                 scale ?? { ("Unknown scale " ++ key.asString).warn; ^nil };
19                 tuning !? { scale.tuning_(tuning.asTuning) };
20                 ^scale
21         }
23         checkTuningForMismatch { |aTuning|
24                 ^pitchesPerOctave == aTuning.size;
25         }
27         tuning_ { | inTuning |
28                 inTuning = inTuning.asTuning;
29                 if(this.checkTuningForMismatch(inTuning)) {
30                         tuning = inTuning
31                 } {
32                         "Scale steps per octave % does not match tuning size ".format(pitchesPerOctave).warn;
33                 }
34         }
36         guessPPO {
37                 // most common flavors of ET
38                 // pick the smallest one that contains all scale degrees
39                 var etTypes = #[12, 19, 24, 53, 128];
40                 ^etTypes[etTypes.indexInBetween(degrees.maxItem).ceil];
41         }
43         as { |class|
44                 ^this.semitones.as(class)
45         }
47         size {
48                 ^degrees.size
49         }
51         semitones {
52                 ^degrees.collect(tuning.wrapAt(_));
53         }
55         cents {
56                 ^this.semitones * 100
57         }
59         ratios {
60                 ^this.semitones.midiratio
61         }
63         at { |index|
64                 ^tuning.at(degrees.wrapAt(index))
65         }
67         wrapAt { |index|
68                 ^tuning.wrapAt(degrees.wrapAt(index))
69         }
71         performDegreeToKey { | scaleDegree, stepsPerOctave, accidental = 0 |
72                 var baseKey;
73                 stepsPerOctave = stepsPerOctave ? tuning.stepsPerOctave;
74                 baseKey = (stepsPerOctave * (scaleDegree div: this.size)) + this.wrapAt(scaleDegree);
75                 ^if(accidental == 0) { baseKey } { baseKey + (accidental * (stepsPerOctave / 12.0)) }
76         }
78         performKeyToDegree { | degree, stepsPerOctave = 12 |
79                 ^degrees.performKeyToDegree(degree, stepsPerOctave)
80         }
82         performNearestInList { | degree |
83                 ^degrees.performNearestInList(degree)
84         }
86         performNearestInScale { | degree, stepsPerOctave=12 | // collection is sorted
87                 ^degrees.performNearestInScale(degree, stepsPerOctave)
88         }
90         degreeToRatio { |degree, octave = 0|
91                 ^this.ratios.at(degree) * (this.octaveRatio ** octave);
92         }
94         degreeToFreq { |degree, rootFreq, octave|
95                 ^this.degreeToRatio(degree, octave) * rootFreq;
96         }
98         *choose { |size = 7, pitchesPerOctave = 12|
99                 var scale = ScaleInfo.choose({
100                         |x| (x.size == size) && (x.pitchesPerOctave == pitchesPerOctave)
101                 });
102                 scale.isNil.if({
103                         ("No known scales with size " ++ size.asString ++
104                                 " and pitchesPerOctave " ++ pitchesPerOctave.asString).warn;
105                         ^nil
106                 });
107                 ^scale
108         }
110         *doesNotUnderstand { |selector, args|
111                 var scale = this.newFromKey(selector, args);
112                 ^scale ?? { super.doesNotUnderstand(selector, args) };
113         }
116         *directory {
117                 ^ScaleInfo.directory
118         }
120         octaveRatio {
121                 ^tuning.octaveRatio
122         }
124         stepsPerOctave {
125                 ^tuning.stepsPerOctave
126         }
128         == { |scale|
129                 ^degrees == scale.degrees and: { tuning == scale.tuning }
130         }
132         hash {
133                 ^degrees.hash bitXor: tuning.hash
134         }
136         storeOn { |stream|
137                 var storedKey = this.storedKey;
138                 stream << this.class.name;
139                 if(storedKey.notNil) { stream << "." << storedKey } { this.storeParamsOn(stream) }
140         }
142         storedKey {
143                 // can be optimised later
144                 var stored = ScaleInfo.scales.detect(_ == this);
145                 ^stored !? { ScaleInfo.scales.findKeyForValue(stored) }
146         }
148         storeArgs { ^[degrees, pitchesPerOctave, tuning, name] }
151         printOn { |stream|
152                 this.storeOn(stream)
153         }
158 Tuning {
160         var <tuning, <octaveRatio, <>name;
162         *new { | tuning, octaveRatio = 2.0, name = "Unknown Tuning" |
163                 ^super.newCopyArgs(tuning, octaveRatio, name);
164         }
166         *newFromKey { | key |
167                 ^TuningInfo.at(key)
168         }
170         *default { | pitchesPerOctave |
171                 ^this.et(pitchesPerOctave);
172         }
174         *et { |pitchesPerOctave = 12 |
175                 ^this.new(this.calcET(pitchesPerOctave), 2.0, this.etName(pitchesPerOctave));
176         }
178         *calcET { | pitchesPerOctave |
179                 ^(0..(pitchesPerOctave - 1)) * (12/pitchesPerOctave)
180         }
182         *etName { |pitchesPerOctave|
183                 ^"ET" ++ pitchesPerOctave.asString
184         }
186         *choose { |size = 12|
187                 ^TuningInfo.choose({ |x| x.size == size })
188         }
190         ratios {
191                 ^tuning.midiratio
192         }
194         semitones {
195                 ^tuning
196         }
198         cents {
199                 ^this.semitones * 100
200         }
202         as { |class|
203                 ^this.semitones.as(class)
204         }
206         size {
207                 ^tuning.size
208         }
210         at { |index|
211                 ^tuning.at(index)
212         }
214         wrapAt { |index|
215                 ^tuning.wrapAt(index)
216         }
218         == { |argTuning|
219                 ^tuning == argTuning.tuning and: { octaveRatio == argTuning.octaveRatio }       }
221         hash {
222                 ^tuning.hash bitXor: octaveRatio.hash
223         }
225         *doesNotUnderstand { |selector, args|
226                 var tuning = this.newFromKey(selector, args);
227                 ^tuning ?? { super.doesNotUnderstand(selector, args) }
228         }
230         *directory {
231                 ^TuningInfo.directory
232         }
234         stepsPerOctave {
235                 ^octaveRatio.log2 * 12.0
236         }
238         asTuning {
239                 ^this
240         }
242         printOn { |stream|
243                 this.storeOn(stream)
244         }
246         storeOn { |stream|
247                 var storedKey = this.storedKey;
248                 stream << this.class.name;
249                 if(storedKey.notNil) { stream << "." << storedKey } { this.storeParamsOn(stream) }
250         }
252         storedKey {
253                 // can be optimised later
254                 var stored = TuningInfo.tunings.detect(_ == this);
255                 ^stored !? { TuningInfo.tunings.findKeyForValue(stored) }
256         }
258         storeArgs {
259                 ^[tuning, octaveRatio, name]
260         }
264 ScaleAD : Scale {
265         var <>descScale;
266         *new { | degrees, pitchesPerOctave, descDegrees, tuning, name = "Unknown Scale" |
267                 ^super.new.init(degrees ? \ionian, pitchesPerOctave, tuning, name)
268                         .descScale_(Scale(descDegrees ? \ionian, pitchesPerOctave, tuning, name ++ "desc"))
269                 ;
270         }
271         asStream { ^ScaleStream(this, 0) }
272         embedInStream { ScaleStream(this).yield }
276 ScaleStream {
277         var <>scale, <>curDegree;
279         *new { | scale, startDegree = 0 | ^super.newCopyArgs(scale, startDegree) }
281         chooseScale { |scaleDegree |
282                 if (scaleDegree >= curDegree) {
283                         curDegree = scaleDegree;
284                         ^scale
285                 } {
286                         curDegree = scaleDegree;
287                         ^scale.descScale
288                 }
289         }
290         performDegreeToKey { | degree, stepsPerOctave, accidental = 0 |
291                 ^this.chooseScale(degree).performDegreeToKey(degree, stepsPerOctave, accidental);
292         }
295         performKeyToDegree { | degree, stepsPerOctave = 12 |
296                 ^this.chooseScale(degree).performKeyToDegree(degree, stepsPerOctave)
297         }
299         performNearestInList { | degree |
300                 ^this.chooseScale(degree).performNearestInList(degree)
301         }
303         performNearestInScale { | degree, stepsPerOctave=12 | // collection is sorted
304                 ^this.chooseScale(degree).performNearestInScale(degree, stepsPerOctave)
305         }
308 ScaleInfo {
310         classvar <scales, dirDoc;
312         *initClass {
314                 Class.initClassTree(TuningInfo);
316                 scales = IdentityDictionary[
318                         // TWELVE TONES PER OCTAVE
319                         // 5 note scales
320                         \minorPentatonic -> Scale.new(#[0,3,5,7,10], 12, name: "Minor Pentatonic"),
321                         \majorPentatonic -> Scale.new(#[0,2,4,7,9], 12, name: "Major Pentatonic"),
322                         // another mode of major pentatonic
323                         \ritusen -> Scale.new(#[0,2,5,7,9], 12, name: "Ritusen"),
324                         // another mode of major pentatonic
325                         \egyptian -> Scale.new(#[0,2,5,7,10], 12, name: "Egyptian"),
327                         \kumoi -> Scale.new(#[0,2,3,7,9], 12, name: "Kumai"),
328                         \hirajoshi -> Scale.new(#[0,2,3,7,8], 12, name: "Hirajoshi"),
329                         \iwato -> Scale.new(#[0,1,5,6,10], 12, name: "Iwato"), // mode of hirajoshi
330                         \chinese -> Scale.new(#[0,4,6,7,11], 12, name: "Chinese"), // mode of hirajoshi
331                         \indian -> Scale.new(#[0,4,5,7,10], 12, name: "Indian"),
332                         \pelog -> Scale.new(#[0,1,3,7,8], 12, name: "Pelog"),
334                         \prometheus -> Scale.new(#[0,2,4,6,11], 12, name: "Prometheus"),
335                         \scriabin -> Scale.new(#[0,1,4,7,9], 12, name: "Scriabin"),
337                         // han chinese pentatonic scales
338                         \gong -> Scale.new(#[0,2,4,7,9], 12, name: "Gong"),
339                         \shang -> Scale.new(#[0,2,5,7,10], 12, name: "Shang"),
340                         \jiao -> Scale.new(#[0,3,5,8,10], 12, name: "Jiao"),
341                         \zhi -> Scale.new(#[0,2,5,7,9], 12, name: "Zhi"),
342                         \yu -> Scale.new(#[0,3,5,7,10], 12, name: "Yu"),
345                         // 6 note scales
346                         \whole -> Scale.new((0,2..10), 12, name: "Whole Tone"),
347                         \augmented -> Scale.new(#[0,3,4,7,8,11], 12, name: "Augmented"),
348                         \augmented2 -> Scale.new(#[0,1,4,5,8,9], 12, name: "Augmented 2"),
350                         // Partch's Otonalities and Utonalities
351                         \partch_o1 -> Scale.new(#[0,8,14,20,25,34], 43,
352                                 Tuning.partch, "Partch Otonality 1"),
353                         \partch_o2 -> Scale.new(#[0,7,13,18,27,35], 43,
354                                 Tuning.partch, "Partch Otonality 2"),
355                         \partch_o3 -> Scale.new(#[0,6,12,21,29,36], 43,
356                                 Tuning.partch, "Partch Otonality 3"),
357                         \partch_o4 -> Scale.new(#[0,5,15,23,30,37], 43,
358                                 Tuning.partch, "Partch Otonality 4"),
359                         \partch_o5 -> Scale.new(#[0,10,18,25,31,38], 43,
360                                 Tuning.partch, "Partch Otonality 5"),
361                         \partch_o6 -> Scale.new(#[0,9,16,22,28,33], 43,
362                                 Tuning.partch, "Partch Otonality 6"),
363                         \partch_u1 -> Scale.new(#[0,9,18,23,29,35], 43,
364                                 Tuning.partch, "Partch Utonality 1"),
365                         \partch_u2 -> Scale.new(#[0,8,16,25,30,36], 43,
366                                 Tuning.partch, "Partch Utonality 2"),
367                         \partch_u3 -> Scale.new(#[0,7,14,22,31,37], 43,
368                                 Tuning.partch, "Partch Utonality 3"),
369                         \partch_u4 -> Scale.new(#[0,6,13,20,28,38], 43,
370                                 Tuning.partch, "Partch Utonality 4"),
371                         \partch_u5 -> Scale.new(#[0,5,12,18,25,33], 43,
372                                 Tuning.partch, "Partch Utonality 5"),
373                         \partch_u6 -> Scale.new(#[0,10,15,21,27,34], 43,
374                                 Tuning.partch, "Partch Utonality 6"),
376                         // hexatonic modes with no tritone
377                         \hexMajor7 -> Scale.new(#[0,2,4,7,9,11], 12, name: "Hex Major 7"),
378                         \hexDorian -> Scale.new(#[0,2,3,5,7,10], 12, name: "Hex Dorian"),
379                         \hexPhrygian -> Scale.new(#[0,1,3,5,8,10], 12, name: "Hex Phrygian"),
380                         \hexSus -> Scale.new(#[0,2,5,7,9,10], 12, name: "Hex Sus"),
381                         \hexMajor6 -> Scale.new(#[0,2,4,5,7,9], 12, name: "Hex Major 6"),
382                         \hexAeolian -> Scale.new(#[0,3,5,7,8,10], 12, name: "Hex Aeolian"),
384                         // 7 note scales
385                         \major -> Scale.new(#[0,2,4,5,7,9,11], 12, name: "Major"),
386                         \ionian -> Scale.new(#[0,2,4,5,7,9,11], 12, name: "Ionian"),
387                         \dorian -> Scale.new(#[0,2,3,5,7,9,10], 12, name: "Dorian"),
388                         \phrygian -> Scale.new(#[0,1,3,5,7,8,10], 12, name: "Phrygian"),
389                         \lydian -> Scale.new(#[0,2,4,6,7,9,11], 12, name: "Lydian"),
390                         \mixolydian -> Scale.new(#[0,2,4,5,7,9,10], 12, name: "Mixolydian"),
391                         \aeolian -> Scale.new(#[0,2,3,5,7,8,10], 12, name: "Aeolian"),
392                         \minor -> Scale.new(#[0,2,3,5,7,8,10], 12, name: "Natural Minor"),
393                         \locrian -> Scale.new(#[0,1,3,5,6,8,10], 12, name: "Locrian"),
395                         \harmonicMinor -> Scale.new(#[0,2,3,5,7,8,11], 12, name: "Harmonic Minor"),
396                         \harmonicMajor -> Scale.new(#[0,2,4,5,7,8,11], 12, name: "Harmonic Major"),
398                         \melodicMinor -> Scale.new(#[0,2,3,5,7,9,11], 12, name: "Melodic Minor"),
399                         \melodicMinorDesc -> Scale.new(#[0,2,3,5,7,8,10], 12,
400                                 name: "Melodic Minor Descending"),
401                         \melodicMajor -> Scale.new(#[0,2,4,5,7,8,10], 12, name: "Melodic Major"),
403                         \bartok -> Scale.new(#[0,2,4,5,7,8,10], 12, name: "Bartok"),
404                         \hindu -> Scale.new(#[0,2,4,5,7,8,10], 12, name: "Hindu"),
406                         // raga modes
407                         \todi -> Scale.new(#[0,1,3,6,7,8,11], 12, name: "Todi"),
408                         \purvi -> Scale.new(#[0,1,4,6,7,8,11], 12, name: "Purvi"),
409                         \marva -> Scale.new(#[0,1,4,6,7,9,11], 12, name: "Marva"),
410                         \bhairav -> Scale.new(#[0,1,4,5,7,8,11], 12, name: "Bhairav"),
411                         \ahirbhairav -> Scale.new(#[0,1,4,5,7,9,10], 12, name: "Ahirbhairav"),
413                         \superLocrian -> Scale.new(#[0,1,3,4,6,8,10], 12, name: "Super Locrian"),
414                         \romanianMinor -> Scale.new(#[0,2,3,6,7,9,10], 12, name: "Romanian Minor"),                     \hungarianMinor -> Scale.new(#[0,2,3,6,7,8,11], 12, name: "Hungarian Minor"),
415                         \neapolitanMinor -> Scale.new(#[0,1,3,5,7,8,11], 12, name: "Neapolitan Minor"),
416                         \enigmatic -> Scale.new(#[0,1,4,6,8,10,11], 12, name: "Enigmatic"),
417                         \spanish -> Scale.new(#[0,1,4,5,7,8,10], 12, name: "Spanish"),
419                         // modes of whole tones with added note ->
420                         \leadingWhole -> Scale.new(#[0,2,4,6,8,10,11], 12, name: "Leading Whole Tone"),
421                         \lydianMinor -> Scale.new(#[0,2,4,6,7,8,10], 12, name: "Lydian Minor"),
422                         \neapolitanMajor -> Scale.new(#[0,1,3,5,7,9,11], 12, name: "Neapolitan Major"),
423                         \locrianMajor -> Scale.new(#[0,2,4,5,6,8,10], 12, name: "Locrian Major"),
425                         // 8 note scales
426                         \diminished -> Scale.new(#[0,1,3,4,6,7,9,10], 12, name: "Diminished"),
427                         \diminished2 -> Scale.new(#[0,2,3,5,6,8,9,11], 12, name: "Diminished 2"),
429                         // 12 note scales
430                         \chromatic -> Scale.new((0..11), 12, name: "Chromatic"),
432                         // TWENTY-FOUR TONES PER OCTAVE
434                         \chromatic24 -> Scale.new((0..23), 24, name: "Chromatic 24"),
436                         // maqam ajam
437                         \ajam -> Scale.new(#[0,4,8,10,14,18,22], 24, name: "Ajam"),
438                         \jiharkah -> Scale.new(#[0,4,8,10,14,18,21], 24, name: "Jiharkah"),
439                         \shawqAfza -> Scale.new(#[0,4,8,10,14,16,22], 24, name: "Shawq Afza"),
441                         // maqam sikah
442                         \sikah -> Scale.new(#[0,3,7,11,14,17,21], 24, name: "Sikah"),
443                         \sikahDesc -> Scale.new(#[0,3,7,11,13,17,21], 24, name: "Sikah Descending"),
444                         \huzam -> Scale.new(#[0,3,7,9,15,17,21], 24, name: "Huzam"),
445                         \iraq -> Scale.new(#[0,3,7,10,13,17,21], 24, name: "Iraq"),
446                         \bastanikar -> Scale.new(#[0,3,7,10,13,15,21], 24, name: "Bastanikar"),
447                         \mustar -> Scale.new(#[0,5,7,11,13,17,21], 24, name: "Mustar"),
449                         // maqam bayati
450                         \bayati -> Scale.new(#[0,3,6,10,14,16,20], 24, name: "Bayati"),
451                         \karjighar -> Scale.new(#[0,3,6,10,12,18,20], 24, name: "Karjighar"),
452                         \husseini -> Scale.new(#[0,3,6,10,14,17,21], 24, name: "Husseini"),
454                         // maqam nahawand
455                         \nahawand -> Scale.new(#[0,4,6,10,14,16,22], 24, name: "Nahawand"),
456                         \nahawandDesc -> Scale.new(#[0,4,6,10,14,16,20], 24, name: "Nahawand Descending"),
457                         \farahfaza -> Scale.new(#[0,4,6,10,14,16,20], 24, name: "Farahfaza"),
458                         \murassah -> Scale.new(#[0,4,6,10,12,18,20], 24, name: "Murassah"),
459                         \ushaqMashri -> Scale.new(#[0,4,6,10,14,17,21], 24, name: "Ushaq Mashri"),
461                         // maqam rast
462                         \rast -> Scale.new(#[0,4,7,10,14,18,21], 24, name: "Rast"),
463                         \rastDesc -> Scale.new(#[0,4,7,10,14,18,20], 24, name: "Rast Descending"),
464                         \suznak -> Scale.new(#[0,4,7,10,14,16,22], 24, name: "Suznak"),
465                         \nairuz -> Scale.new(#[0,4,7,10,14,17,20], 24, name: "Nairuz"),
466                         \yakah -> Scale.new(#[0,4,7,10,14,18,21], 24, name: "Yakah"),
467                         \yakahDesc -> Scale.new(#[0,4,7,10,14,18,20], 24, name: "Yakah Descending"),
468                         \mahur -> Scale.new(#[0,4,7,10,14,18,22], 24, name: "Mahur"),
470                         // maqam hijaz
471                         \hijaz -> Scale.new(#[0,2,8,10,14,17,20], 24, name: "Hijaz"),
472                         \hijazDesc -> Scale.new(#[0,2,8,10,14,16,20], 24, name: "Hijaz Descending"),
473                         \zanjaran -> Scale.new(#[0,2,8,10,14,18,20], 24, name: "Zanjaran"),
475                         // maqam hijazKar
476                         \zanjaran -> Scale.new(#[0,2,8,10,14,16,22], 24, name: "Zanjaran"),
478                         // maqam saba
479                         \saba -> Scale.new(#[0,3,6,8,12,16,20], 24, name: "Saba"),
480                         \zamzam -> Scale.new(#[0,2,6,8,14,16,20], 24, name: "Zamzam"),
482                         // maqam kurd
483                         \kurd -> Scale.new(#[0,2,6,10,14,16,20], 24, name: "Kurd"),
484                         \kijazKarKurd -> Scale.new(#[0,2,8,10,14,16,22], 24, name: "Kijaz Kar Kurd"),
486                         // maqam nawa Athar
487                         \nawaAthar -> Scale.new(#[0,4,6,12,14,16,22], 24, name: "Nawa Athar"),
488                         \nikriz -> Scale.new(#[0,4,6,12,14,18,20], 24, name: "Nikriz"),
489                         \atharKurd -> Scale.new(#[0,2,6,12,14,16,22], 24, name: "Athar Kurd"),
492                         // Ascending/descending scales
493                         \melodicMinor -> ScaleAD(#[0,2,3,5,7,9,11], 12, #[0,2,3,5,7,8,10], name: "Melodic Minor"),
494                         \sikah -> ScaleAD(#[0,3,7,11,14,17,21], 24, #[0,3,7,11,13,17,21], name: "Sikah"),
495                         \nahawand -> ScaleAD(#[0,4,6,10,14,16,22], 24, #[0,4,6,10,14,16,20], name: "Nahawand"),
496                 ];
498         }
500         *at { |key|
501                 var res = scales[key];
502                 ^res !? { res.deepCopy };
503         }
505         *choose {
506                 |selectFunc|
507                 ^scales.values.select(selectFunc ? { true }).choose.deepCopy;
508         }
510         *names {
511                 ^scales.keys.asArray.sort
512         }
513         *directory {
514                 var dirString = scales.keys.asArray.sort.collect({ |k|
515                         "\\" ++ k ++ ": " ++ scales.at(k).name
516                 }).join("\n");
518                 dirDoc = dirDoc ?? {
519                                 Document.new("Tuning Directory", dirString)
520                                 .onClose_({ dirDoc.free; dirDoc = nil });
521                 };
522                 dirDoc.front;
523                 dirDoc.string = dirString;
525         }
528 TuningInfo {
530         classvar <tunings, dirDoc;
532         *initClass {
534                 tunings = IdentityDictionary[
536                         //TWELVE-TONE TUNINGS
537                         \et12 -> Tuning.new((0..11), 2, "ET12"),
539                         \pythagorean -> Tuning.new([1, 256/243, 9/8, 32/27, 81/64, 4/3, 729/512, 3/2,
540                                 128/81, 27/16, 16/9, 243/128].ratiomidi, 2, "Pythagorean"),
541                         \just -> Tuning.new([1, 16/15, 9/8, 6/5, 5/4, 4/3, 45/32, 3/2, 8/5, 5/3,
542                                 9/5, 15/8].ratiomidi, 2, "5-Limit Just Intonation"),
543                         \sept1 -> Tuning.new([1, 16/15, 9/8, 6/5, 5/4, 4/3, 7/5, 3/2, 8/5, 5/3,
544                                 9/5, 15/8].ratiomidi, 2, "Septimal Tritone Just Intonation"),
545                         \sept2 -> Tuning.new([1, 16/15, 9/8, 6/5, 5/4, 4/3, 7/5, 3/2, 8/5, 5/3,
546                                 7/4, 15/8].ratiomidi, 2, "7-Limit Just Intonation"),
547                         \mean4 -> Tuning.new(#[0, 0.755, 1.93, 3.105, 3.86, 5.035, 5.79, 6.965,
548                                 7.72, 8.895, 10.07, 10.82], 2, "Meantone, 1/4 Syntonic Comma"),
549                         \mean5 -> Tuning.new(#[0, 0.804, 1.944, 3.084, 3.888, 5.028, 5.832, 6.972,
550                                 7.776, 8.916, 10.056, 10.86], 2, "Meantone, 1/5 Pythagorean Comma"),
551                         \mean6 -> Tuning.new(#[0, 0.86, 1.96, 3.06, 3.92, 5.02, 5.88, 6.98, 7.84,
552                                 8.94, 10.04, 10.9], 2, "Meantone, 1/6 Pythagorean Comma"),
553                         \kirnberger -> Tuning.new([1, 256/243, (5.sqrt)/2, 32/27, 5/4, 4/3,
554                                 45/32, 5 ** 0.25, 128/81, (5 ** 0.75)/2, 16/9, 15/8].ratiomidi, 2,
555                                 "Kirnberger III"),
556                         \werckmeister -> Tuning.new(#[0, 0.92, 1.93, 2.94, 3.915, 4.98, 5.9, 6.965,
557                                 7.93, 8.895, 9.96, 10.935], 2, "Werckmeister III"),
558                         \vallotti -> Tuning.new(#[0, 0.94135, 1.9609, 2.98045, 3.92180, 5.01955,
559                                 5.9218, 6.98045, 7.9609, 8.94135, 10, 10.90225], 2, "Vallotti"),
560                         \young -> Tuning.new(#[0, 0.9, 1.96, 2.94, 3.92, 4.98, 5.88, 6.98, 7.92,
561                                 8.94, 9.96, 10.9], 2, "Young"),
562                         \reinhard -> Tuning.new([1, 14/13, 13/12, 16/13, 13/10, 18/13, 13/9,
563                                 20/13, 13/8, 22/13, 13/7, 208/105].ratiomidi, 2, "Mayumi Reinhard"),
564                         \wcHarm -> Tuning.new([1, 17/16, 9/8, 19/16, 5/4, 21/16, 11/8, 3/2, 13/8,
565                                 27/16, 7/4, 15/8].ratiomidi, 2, "Wendy Carlos Harmonic"),
566                         \wcSJ -> Tuning.new([1, 17/16, 9/8, 6/5, 5/4, 4/3, 11/8, 3/2, 13/8, 5/3,
567                                 7/4, 15/8].ratiomidi, 2, "Wendy Carlos Super Just"),
569                         //MORE THAN TWELVE-TONE ET
570                         \et19 -> Tuning.new((0 .. 18) * 12/19, 2, "ET19"),
571                         \et22 -> Tuning.new((0 .. 21) * 6/11, 2, "ET22"),
572                         \et24 -> Tuning.new((0 .. 23) * 0.5, 2, "ET24"),
573                         \et31 -> Tuning.new((0 .. 30) * 12/31, 2, "ET31"),
574                         \et41 -> Tuning.new((0 .. 40) * 12/41, 2, "ET41"),
575                         \et53 -> Tuning.new((0 .. 52) * 12/53, 2, "ET53"),
577                         //NON-TWELVE-TONE JI
578                         \johnston -> Tuning.new([1, 25/24, 135/128, 16/15, 10/9, 9/8, 75/64, 6/5,
579                                 5/4, 81/64, 32/25, 4/3, 27/20, 45/32, 36/25, 3/2, 25/16, 8/5, 5/3,
580                                 27/16, 225/128, 16/9, 9/5, 15/8, 48/25].ratiomidi, 2, "Ben Johnston"),
581                         \partch -> Tuning.new([1, 81/80, 33/32, 21/20, 16/15, 12/11, 11/10, 10/9, 9/8,
582                                 8/7, 7/6, 32/27, 6/5, 11/9, 5/4, 14/11, 9/7, 21/16, 4/3, 27/20, 11/8,
583                                 7/5, 10/7, 16/11, 40/27, 3/2, 32/21, 14/9, 11/7, 8/5, 18/11, 5/3, 27/16,
584                                 12/7, 7/4, 16/9, 9/5, 20/11, 11/6, 15/8, 40/21, 64/33, 160/81].ratiomidi, 2,
585                                 "Harry Partch"),
586                         \catler -> Tuning.new([1, 33/32, 16/15, 9/8, 8/7, 7/6, 6/5, 128/105, 16/13,
587                                 5/4, 21/16, 4/3, 11/8, 45/32, 16/11, 3/2, 8/5, 13/8, 5/3, 27/16, 7/4,
588                                 16/9, 24/13, 15/8].ratiomidi, 2, "Jon Catler"),
589                         \chalmers -> Tuning.new([1, 21/20, 16/15, 9/8, 7/6, 6/5, 5/4, 21/16, 4/3, 7/5,
590                                 35/24, 3/2, 63/40, 8/5, 5/3, 7/4, 9/5, 28/15, 63/32].ratiomidi, 2,
591                                 "John Chalmers"),
592                         \harrison -> Tuning.new([1, 16/15, 10/9, 8/7, 7/6, 6/5, 5/4, 4/3, 17/12, 3/2,
593                                 8/5, 5/3, 12/7, 7/4, 9/5, 15/8].ratiomidi, 2, "Lou Harrison"),
594                         \sruti -> Tuning.new([1, 256/243, 16/15, 10/9, 9/8, 32/27, 6/5, 5/4, 81/64,
595                                 4/3, 27/20, 45/32, 729/512, 3/2, 128/81, 8/5, 5/3, 27/16, 16/9, 9/5,
596                                 15/8, 243/128].ratiomidi, 2, "Sruti"),
598                         //HARMONIC SERIES -- length arbitary
599                         \harmonic -> Tuning.new((1 .. 24).ratiomidi, 2, "Harmonic Series 24"),
601                         //STRETCHED/SHRUNK OCTAVE
602                         //Bohlen-Pierce
603                         \bp -> Tuning.new((0 .. 12) * (3.ratiomidi/13), 3.0, "Bohlen-Pierce"),
605                         \wcAlpha -> Tuning.new((0 .. 14) * 0.78, (15 * 0.78).midiratio, "Wendy Carlos Alpha"),
606                         \wcBeta -> Tuning.new((0 .. 18) * 0.638, (19 * 0.638).midiratio, "Wendy Carlos Beta"),
607                         \wcGamma -> Tuning.new((0 .. 33) * 0.351, (34 * 0.351).midiratio,
608                                 "Wendy Carlos Gamma")
609                 ];
612         }
614         *choose { |selectFunc|
615                 ^tunings.values.select(selectFunc ? { true }).choose;
616         }
618         *names {
619                 ^tunings.keys.asArray.sort
620         }
622         *at { |key|
623                 var res = tunings[key];
624                 ^res !? { res.deepCopy };
625         }
627         *directory {
628                 var dirString = tunings.keys.asArray.sort.collect({ |k|
629                         "\\" ++ k ++ ": " ++ tunings.at(k).name
630                 }).join("\n");
632                 dirDoc = dirDoc ?? {
633                                 Document.new("Tuning Directory", dirString)
634                                 .onClose_({ dirDoc.free; dirDoc = nil });
635                 };
636                 dirDoc.front;
637                 dirDoc.string = dirString;
639         }