~ubuntu-branches/ubuntu/precise/csound/precise

« back to all changes in this revision

Viewing changes to frontends/CsoundAC/ChordSpace.hpp

  • Committer: Package Import Robot
  • Author(s): Felipe Sateler
  • Date: 2012-04-19 09:26:46 UTC
  • mfrom: (3.2.19 sid)
  • Revision ID: package-import@ubuntu.com-20120419092646-96xbj1n6atuqosk2
Tags: 1:5.17.6~dfsg-1
* New upstream release
 - Do not build the wiimote opcodes (we need wiiuse).
* Add new API function to symbols file
* Disable lua opcodes, they were broken. Requires OpenMP to be enabled.
* Backport fixes from upstream:
  - Link dssi4cs with dl. Backport
  - Fix building of CsoundAC

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * C S O U N D
 
3
 *
 
4
 * L I C E N S E
 
5
 *
 
6
 * This software is free software; you can redistribute it and/or
 
7
 * modify it under the terms of the GNU Lesser General Public
 
8
 * License as published by the Free Software Foundation; either
 
9
 * version 2.1 of the License, or (at your option) any later version.
 
10
 *
 
11
 * This software is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
 * Lesser General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU Lesser General Public
 
17
 * License along with this software; if not, write to the Free Software
 
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
19
 */
 
20
#ifndef SCORE_H
 
21
#define SCORE_H
 
22
#include "Platform.hpp"
 
23
#ifdef SWIG
 
24
%module CsoundAC
 
25
% {
 
26
#include <eigen3/Eigen/Dense>
 
27
#include "Event.hpp"
 
28
#include <iostream>
 
29
#include <sstream>
 
30
#include <vector>
 
31
        %
 
32
}
 
33
%include "std_string.i"
 
34
%include "std_vector.i"
 
35
#else
 
36
#include <eigen3/Eigen/Dense>
 
37
#include "Event.hpp"
 
38
#include <iostream>
 
39
#include <sstream>
 
40
#include <vector>
 
41
#endif
 
42
 
 
43
namespace csound {
 
44
/**
 
45
This class, part of CsoundAC, implements a geometric approach to some common
 
46
operations on chords in neo-Riemannian music theory for use in score
 
47
generating procedures:
 
48
 
 
49
--  Identifying whether a chord belongs to some equivalence class of music
 
50
  theory, or sending a chord to its equivalent within a representative
 
51
  fundamental domain of some equivalence class. The equivalence classes are
 
52
  octave (O), permutational (P), transpositional, (T), inversional (I), and
 
53
  their compounds OP, OPT (set-class or chord type), and OPTI (prime form).
 
54
 
 
55
--  Causing chord progressions to move strictly within an orbifold that
 
56
  generates some equivalence class.
 
57
 
 
58
--  Implementing chord progressions based on the L, P, R, D, K, and Q
 
59
  operations of neo-Riemannian theory (thus implementing some aspects of
 
60
  "harmony").
 
61
 
 
62
--  Implementing chord progressions performed within a more abstract
 
63
  equivalence class by means of the best-formed voice-leading within a less
 
64
  abstract equivalence class (thus implementing some fundamentals of
 
65
  "counterpoint").
 
66
 
 
67
The associated ChordSpaceView package can display these chord spaces and
 
68
operations for trichords in an interactive 3-dimensional view.
 
69
 
 
70
DEFINITIONS
 
71
 
 
72
Pitch is the perception of a distinct sound frequency. It is a logarithmic
 
73
perception; octaves, which sound 'equivalent' in some sense, represent
 
74
doublings or halvings of frequency.
 
75
 
 
76
Pitches and intervals are represented as real numbers. Middle C is 60 and the
 
77
octave is 12. Our usual system of 12-tone equal temperament, as well as MIDI
 
78
key numbers, are completely represented by the whole numbers; any and all
 
79
other pitches can be represented simply by using fractions.
 
80
 
 
81
A voice is a distinct sound that is heard as having a pitch.
 
82
 
 
83
A chord is simply a set of voices heard at the same time, represented here
 
84
as a point in a chord space having one dimension of pitch for each voice
 
85
in the chord.
 
86
 
 
87
For the purposes of algorithmic composition in CsoundAC, a score is considered
 
88
as a sequence of more or less fleeting chords.
 
89
 
 
90
EQUIVALENCE CLASSES
 
91
 
 
92
An equivalence class identifies elements of a set. Operations that send one
 
93
equivalent point to another induce quotient spaces or orbifolds, where the
 
94
equivalence operation identifies points on one face of the orbifold with
 
95
points on an opposing face. The fundamental domain of the equivalence class
 
96
is the space "within" the orbifold.
 
97
 
 
98
Plain chord space has no equivalence classes. Ordered chords are represented
 
99
as vectors in parentheses (p1, ..., pN). Unordered chords are represented as
 
100
sorted vectors in braces {p1, ..., pN}. Unordering is itself an equivalence
 
101
class.
 
102
 
 
103
The following equivalence classes apply to pitches and chords, and exist in
 
104
different orbifolds. Equivalence classes can be combined (Callendar, Quinn,
 
105
and Tymoczko, "Generalized Voice-Leading Spaces," _Science_ 320, 2008), and
 
106
the more equivalence classes are combined, the more abstract is the resulting
 
107
orbifold compared to the parent space.
 
108
 
 
109
In most cases, a chord space can be divided into a number, possibly
 
110
infinite, of geometrically equivalent fundamental domains for the same
 
111
equivalence class. Therefore, here we use the notion of 'representative'
 
112
fundamental domain. For example, the representative fundamental domain of
 
113
unordered sequences, out of all possible orderings, consists of all sequences
 
114
in their ordinary sorted order. It is important, in the following, to identify
 
115
representative fundamental domains that combine properly, e.g. such that the
 
116
representative fundamental domain of OP / the representative fundamental
 
117
domain of PI equals the representative fundamental domain of OPI. And this in
 
118
turn may require accounting for duplicate elements of the representative
 
119
fundamental domain caused by reflections or singularities in the orbifold.
 
120
 
 
121
C       Cardinality equivalence, e.g. {1, 1, 2} == {1, 2}. _Not_ assuming
 
122
      cardinality equivalence ensures that there is a proto-metric in plain
 
123
      chord space that is inherited by all child chord spaces. Cardinality
 
124
      equivalence is never assumed here, because we are working in chord
 
125
      spaces of fixed dimensionality; e.g. we represent the note middle C
 
126
      not as {60}, but as {60, 60, ..., 60}.
 
127
 
 
128
O       Octave equivalence. The fundamental domain is defined by the pitches
 
129
      in a chord spanning the range of an octave or less, and summing to
 
130
      an octave or less.
 
131
 
 
132
P       Permutational equivalence. The fundamental domain is defined by a
 
133
      "wedge" of plain chord space in which the voices of a chord are always
 
134
      sorted by pitch.
 
135
 
 
136
T       Transpositional equivalence, e.g. {1, 2} == {7, 8}. The fundamental
 
137
      domain is defined as a plane in chord space at right angles to the
 
138
      diagonal of unison chords. Represented by the chord always having a
 
139
      sum of pitches equal to 0.
 
140
 
 
141
I       Inversional equivalence. Care is needed to distinguish the
 
142
      mathematician's sense of 'invert', which means 'pitch-space inversion'
 
143
      or 'reflect in a point', from the musician's sense of 'invert', which
 
144
      varies according to context but in practice often means 'registral
 
145
      inversion' or 'revoice by adding an octave to the lowest tone of a
 
146
      chord.' Here, we use 'invert' and 'inversion' in the mathematician's
 
147
      sense, and we use the terms 'revoice' and 'voicing' for the musician's
 
148
      'invert' and 'inversion'. The inversion point for any inversion lies
 
149
      on the unison diagonal. A fundamental domain is defined as any half of
 
150
      chord space that is bounded by a plane containing the inversion point.
 
151
      Represented as the chord having the first interval between voices be
 
152
      smaller than or equal to the final interval (recursing for chords of
 
153
      more than 3 voices).
 
154
 
 
155
PI      Inversional equivalence with permutational equivalence. The
 
156
      'inversion flat' of unordered chord space is a hyperplane consisting
 
157
      of all those unordered chords that are invariant under inversion. A
 
158
      fundamental domain is defined by any half space bounded by a
 
159
      hyperplane containing the inversion flat. It is represented as that
 
160
      half of the space on or lower than the hyperplane defined by the
 
161
      inversion flat and the unison diagonal.
 
162
 
 
163
OP      Octave equivalence with permutational equivalence. Tymoczko's orbifold
 
164
      for chords; i.e. chords with a fixed number of voices in a harmonic
 
165
      context. The fundamental domain is defined as a hyperprism one octave
 
166
      long with as many sides as voices and the ends identified by octave
 
167
      equivalence and one cyclical permutation of voices, modulo the
 
168
      unordering. In OP for trichords in 12TET, the augmented triads run up
 
169
      the middle of the prism, the major and minor triads are in 6
 
170
      alternating columns around the augmented triads, the two-pitch chords
 
171
      form the 3 sides, and the one-pitch chords form the 3 edges that join
 
172
      the sides.
 
173
 
 
174
OPT     The layer of the OP prism as close as possible to the origin, modulo
 
175
      the number of voices. Chord type. Note that CM and Cm are different
 
176
      OPT. Because the OP prism is canted down from the origin, at least one
 
177
      pitch in each OPT chord (excepting the origin itself) is negative.
 
178
 
 
179
OPI     The OP prism modulo inversion, i.e. 1/2 of the OP prism. The
 
180
      representative fundamental consits of those chords less than or equal
 
181
      to their inversions modulo OP.
 
182
 
 
183
OPTI    The OPT layer modulo inversion, i.e. 1/2 of the OPT layer.
 
184
      Set-class. Note that CM and Cm are the same OPTI.
 
185
 
 
186
OPERATIONS
 
187
 
 
188
Each of the above equivalence classes is, of course, an operation that sends
 
189
chords outside the fundamental domain to chords inside the fundamental domain.
 
190
And we define the following additional operations:
 
191
 
 
192
T(p, x)         Translate p by x.
 
193
 
 
194
I(p [, x])      Reflect p in x, by default the origin.
 
195
 
 
196
P               Send a major triad to the minor triad with the same root,
 
197
              or vice versa (Riemann's parallel transformation).
 
198
 
 
199
L               Send a major triad to the minor triad one major third higher,
 
200
              or vice versa (Riemann's Leittonwechsel or leading-tone
 
201
              exchange transformation).
 
202
 
 
203
R               Send a major triad to the minor triad one minor third lower,
 
204
              or vice versa (Riemann's relative transformation).
 
205
 
 
206
D               Send a triad to the next triad a perfect fifth lower
 
207
              (dominant transformation).
 
208
 
 
209
P, L, and R have been extended as follows, see Fiore and Satyendra,
 
210
"Generalized Contextual Groups", _Music Theory Online_ 11, August 2008:
 
211
 
 
212
K(c)            Interchange by inversion;
 
213
              K(c) := I(c, c[1] + c[2]).
 
214
              This is a generalized form of P; for major and minor triads,
 
215
              it is exactly the same as P, but it also works with other
 
216
              chord types.
 
217
 
 
218
Q(c, n, m)      Contexual transposition;
 
219
              Q(c, n, m) := T(c, n) if c is a T-form of m,
 
220
              or T(c, -n) if c is an I-form of M. Not a generalized form
 
221
              of L or R; but, like them, K and Q generate the T-I group.
 
222
 */
 
223
 
 
224
/**
 
225
 * Returns n!
 
226
 */
 
227
double factorial(double n) {
 
228
        if (n == 0) {
 
229
                return 1;
 
230
        } else {
 
231
                return n * factorial(n = 1.0);
 
232
        }
 
233
}
 
234
 
 
235
double EPSILON() {
 
236
        static double epsilon = 1.0;
 
237
        if (epsilon == 1.0) {
 
238
                for (;;) {
 
239
                        epsilon = epsilon / 2.0;
 
240
                        double nextEpsilon = epsilon / 2.0;
 
241
                        double onePlusNextEpsilon = 1.0 + nextEpsilon;
 
242
                        if (onePlusNextEpsilon == 1.0) {
 
243
                                break;
 
244
                        }
 
245
                }
 
246
        }
 
247
        return epsilon;
 
248
}
 
249
 
 
250
double &epsilonFactor() {
 
251
        static double epsilonFactor = 1.0;
 
252
        return epsilonFactor;
 
253
}
 
254
 
 
255
bool eq_epsilon(double a, double b) {
 
256
        if (std::abs(a - b) < (EPSILON() * epsilonFactor())) {
 
257
                return true;
 
258
        } else {
 
259
                return false;
 
260
        }
 
261
 
 
262
}
 
263
 
 
264
double gt_epsilon(double a, double b) {
 
265
        if (eq_epsilon(a, b)) {
 
266
                return false;
 
267
        } else {
 
268
                return (a > b);
 
269
        }
 
270
}
 
271
 
 
272
double lt_epsilon(double a, double b) {
 
273
        if (eq_epsilon(a, b)) {
 
274
                return false;
 
275
        } else {
 
276
                return (a < b);
 
277
        }
 
278
}
 
279
 
 
280
double ge_epsilon(double a, double b) {
 
281
        if (eq_epsilon(a, b)) {
 
282
                return true;
 
283
        } else {
 
284
                return (a > b);
 
285
        }
 
286
}
 
287
 
 
288
double le_epsilon(double a, double b) {
 
289
        if (eq_epsilon(a, b)) {
 
290
                return true;
 
291
        } else {
 
292
                return (a < b);
 
293
        }
 
294
}
 
295
 
 
296
/**
 
297
 * The size of the octave, defined to be consistent with
 
298
 * 12 tone equal temperament and MIDI.
 
299
 */
 
300
double OCTAVE() {
 
301
        return 12.0;
 
302
}
 
303
 
 
304
double MIDDLE_C() {
 
305
        return 60.0;
 
306
}
 
307
 
 
308
double C4() {
 
309
        return MIDDLE_C();
 
310
}
 
311
 
 
312
/**
 
313
 * Returns the pitch transposed by semitones, which may be any scalar.
 
314
 * NOTE: Does NOT return the result under any equivalence class.
 
315
 */
 
316
double T(double pitch, double semitones) {
 
317
        return pitch + semitones;
 
318
}
 
319
 
 
320
/**
 
321
 * Returns the pitch reflected in the center, which may be any pitch.
 
322
 * NOTE: Does NOT return the result under any equivalence class.
 
323
 */
 
324
double I(double pitch, double center = 0.0) {
 
325
        return center - pitch;
 
326
}
 
327
 
 
328
/**
 
329
 * Chords represent simultaneously sounding pitches. The pitches are
 
330
 * represented as semitones with 0 at the origin and middle C as 60.
 
331
 * Each voice also has a duration, velocity, channel, and pan.
 
332
 */
 
333
class SILENCE_PUBLIC Chord : public Eigen::MatrixXd {
 
334
public:
 
335
        enum {
 
336
                PITCH = 0,
 
337
                DURATION = 1,
 
338
                LOUDNESS = 2,
 
339
                INSTRUMENT = 3,
 
340
                PAN = 4,
 
341
                COUNT = 5
 
342
        };
 
343
        Chord() {
 
344
                resize(3);
 
345
        }
 
346
        virtual ~Chord() {
 
347
        }
 
348
        virtual size_t voices() const {
 
349
                return rows();
 
350
        }
 
351
        virtual void resize(size_t voices) {
 
352
                Eigen::MatrixXd::resize(voices, COUNT);
 
353
        }
 
354
        /**
 
355
         * Returns a string representation of the chord.
 
356
         * Quadratic complexity, but short enough not to matter.
 
357
         */
 
358
        virtual std::string toString() const {
 
359
                char buffer[0x100];
 
360
                std::stringstream stream;
 
361
                for (size_t i = 0, n = rows(); i < n; ++i) {
 
362
                        std::snprintf(buffer, 0x100, "%12.7f", getPitch(i));
 
363
                        if (i > 0) {
 
364
                                stream << " ";
 
365
                        }
 
366
                        stream << buffer;
 
367
                }
 
368
                return stream.str();
 
369
        }
 
370
        virtual double getPitch(int voice) const {
 
371
                return coeff(voice, PITCH);
 
372
        }
 
373
        virtual void setPitch(double value, int voice) {
 
374
                coeffRef(voice, PITCH) = value;
 
375
        }
 
376
        virtual double getDuration(int voice = 0) const {
 
377
                return coeff(voice, DURATION);
 
378
        }
 
379
        virtual void setDuration(double value, int voice = -1) {
 
380
                if (voice == -1) {
 
381
                        for (voice = 0; voice < rows(); ++voice) {
 
382
                                coeffRef(voice, DURATION) = value;
 
383
                        }
 
384
                } else {
 
385
                        coeffRef(voice, DURATION) = value;
 
386
                }
 
387
        }
 
388
        virtual double getLoudness(int voice = 0) const {
 
389
                return coeff(voice, LOUDNESS);
 
390
        }
 
391
        virtual void setLoudness(double value, int voice = -1) {
 
392
                if (voice == -1) {
 
393
                        for (voice = 0; voice < rows(); ++voice) {
 
394
                                coeffRef(voice, LOUDNESS) = value;
 
395
                        }
 
396
                } else {
 
397
                        coeffRef(voice, LOUDNESS) = value;
 
398
                }
 
399
        }
 
400
        virtual double getInstrument(int voice = 0) const {
 
401
                return coeff(voice, INSTRUMENT);
 
402
        }
 
403
        virtual void setInstrument(double value, int voice = -1) {
 
404
                if (voice == -1) {
 
405
                        for (voice = 0; voice < rows(); ++voice) {
 
406
                                coeffRef(voice, INSTRUMENT) = value;
 
407
                        }
 
408
                } else {
 
409
                        coeffRef(voice, INSTRUMENT) = value;
 
410
                }
 
411
        }
 
412
        virtual double getPan(int voice = 0) const {
 
413
                return coeff(voice, PAN);
 
414
        }
 
415
        virtual void setPan(double value, int voice = -1) {
 
416
                if (voice == -1) {
 
417
                        for (voice = 0; voice < rows(); ++voice) {
 
418
                                coeffRef(voice, PAN) = value;
 
419
                        }
 
420
                } else {
 
421
                        coeffRef(voice, PAN) = value;
 
422
                }
 
423
        }
 
424
        virtual size_t count(double pitch) const {
 
425
                size_t n = 0;
 
426
                for (size_t voice = 0; voice < voices(); ++voice) {
 
427
                        if (eq_epsilon(getPitch(voice), pitch)) {
 
428
                                n++;
 
429
                        }
 
430
                }
 
431
                return n;
 
432
        }
 
433
        virtual bool operator == (const Chord &other) {
 
434
                if (rows() != other.voices()) {
 
435
                        return false;
 
436
                }
 
437
                for (size_t voice = 0; voice < rows(); ++voice) {
 
438
                        if (!eq_epsilon(getPitch(voice), other.getPitch(voice))) {
 
439
                                return false;
 
440
                        }
 
441
                }
 
442
                return true;
 
443
        }
 
444
        virtual bool operator < (const Chord &other) {
 
445
                if (rows() != other.voices()) {
 
446
                        return false;
 
447
                }
 
448
                for (size_t voice = 0; voice < rows(); ++voice) {
 
449
                        if (!lt_epsilon(getPitch(voice), other.getPitch(voice))) {
 
450
                                return false;
 
451
                        }
 
452
                }
 
453
                return true;
 
454
        }
 
455
 
 
456
};
 
457
 
 
458
/**
 
459
 * Returns the Euclidean distance between chords a and b,
 
460
 * which must have the same number of voices.
 
461
 */
 
462
double euclidean(const Chord &a, const Chord &b) {
 
463
        double sumOfSquaredDifferences = 0.0;
 
464
        for (size_t voice = 0, voices = a.voices(); voice < voices; ++voice) {
 
465
                sumOfSquaredDifferences += std::pow((a.getPitch(voice) - b.getPitch(voice)), 2.0);
 
466
        }
 
467
        return std::sqrt(sumOfSquaredDifferences);
 
468
}
 
469
 
 
470
}
 
471
#endif
 
472
/*
 
473
 
 
474
 
 
475
function Chord:__le(other)
 
476
    if self:__eq(other) then
 
477
        return true
 
478
    end
 
479
    return self:__lt(other)
 
480
end
 
481
 
 
482
-- Returns whether or not the chord contains the pitch.
 
483
 
 
484
function Chord:contains(pitch)
 
485
    for voice, pitch_ in ipairs(self) do
 
486
        if pitch_ == pitch then
 
487
            return true
 
488
        end
 
489
    end
 
490
    return false
 
491
end
 
492
 
 
493
-- Returns the lowest pitch in the chord,
 
494
-- and also its voice index.
 
495
 
 
496
function Chord:min()
 
497
    local lowestVoice = 1
 
498
    local lowestPitch = self[lowestVoice]
 
499
    for voice = 2, #self do
 
500
        if self[voice] < lowestPitch then
 
501
            lowestPitch = self[voice]
 
502
            lowestVoice = voice
 
503
        end
 
504
    end
 
505
    return lowestPitch, lowestVoice
 
506
end
 
507
 
 
508
-- Returns the minimum interval in the chord.
 
509
 
 
510
function Chord:minimumInterval()
 
511
    local minimumInterval = math.abs(self[1] - self[2])
 
512
    for v1 = 1, #self do
 
513
        for v2 = 1, #self do
 
514
            if t (v1 == v2) then
 
515
                local interval = math.abs(self[v1] - self[v2])
 
516
                if interval < minimumInterval then
 
517
                    minimumInterval = interval
 
518
                end
 
519
            end
 
520
        end
 
521
    end
 
522
    return minimumInterval
 
523
end
 
524
 
 
525
-- Returns the highest pitch in the chord,
 
526
-- and also its voice index.
 
527
 
 
528
function Chord:max()
 
529
    local highestVoice = 1
 
530
    local highestPitch = self[highestVoice]
 
531
    for voice = 2, #self do
 
532
        if self[voice] > highestPitch then
 
533
            highestPitch = self[voice]
 
534
            highestVoice = voice
 
535
        end
 
536
    end
 
537
    return highestPitch, highestVoice
 
538
end
 
539
 
 
540
-- Returns the maximum interval in the chord.
 
541
 
 
542
function Chord:maximumInterval()
 
543
    return self:max() - self:min()
 
544
end
 
545
 
 
546
-- Returns a new chord whose pitches are the floors of this chord's pitches.
 
547
 
 
548
function Chord:floor()
 
549
    local chord = self:clone()
 
550
    for voice = 1, #self do
 
551
        chord[voice] = math.floor(self[voice])
 
552
    end
 
553
    return chord
 
554
end
 
555
 
 
556
-- Returns a new chord whose pitches are the ceilings of this chord's pitches.
 
557
 
 
558
function Chord:ceil()
 
559
    local chord = self:clone()
 
560
    for voice = 1, #self do
 
561
        chord[voice] = math.ceil(self[voice])
 
562
    end
 
563
    return chord
 
564
end
 
565
 
 
566
-- Returns a value copy of the chord.
 
567
 
 
568
function Chord:clone()
 
569
    local clone_ = Chord:new()
 
570
    for voice, pitch in ipairs(self) do
 
571
        clone_[voice] = pitch
 
572
    end
 
573
    for voice, value in ipairs(self.duration) do
 
574
        clone_.duration[voice] = value
 
575
    end
 
576
    for voice, value in ipairs(self.channel) do
 
577
        clone_.channel[voice] = value
 
578
    end
 
579
    for voice, value in ipairs(self.velocity) do
 
580
        clone_.velocity[voice] = value
 
581
    end
 
582
    for voice, value in ipairs(self.pan) do
 
583
        clone_.pan[voice] = value
 
584
    end
 
585
    return clone_
 
586
end
 
587
 
 
588
-- Returns the origin of the chord's space.
 
589
 
 
590
function Chord:origin()
 
591
    local clone_ = self:clone()
 
592
    for voice = 1, #clone_ do
 
593
        clone_[voice] = 0
 
594
    end
 
595
    return clone_
 
596
end
 
597
 
 
598
function Chord:distanceToOrigin()
 
599
    local origin = self:origin()
 
600
    return ChordSpace.euclidean(self, origin)
 
601
end
 
602
 
 
603
function Chord:distanceToUnisonDiagonal()
 
604
    local unison = self:origin()
 
605
    local pitch = self:layer() / #self
 
606
    for voice = 1, #self do
 
607
        unison[voice] = pitch
 
608
    end
 
609
    return ChordSpace.euclidean(self, unison)
 
610
end
 
611
 
 
612
-- Returns the maximally even chord in the chord's space,
 
613
-- e.g. the augmented triad for 3 dimensions.
 
614
 
 
615
function Chord:maximallyEven()
 
616
    local clone_ = self:clone()
 
617
    local g = ChordSpace.OCTAVE / #clone_
 
618
    for i = 1, #clone_ do
 
619
        clone_[i] = (i - 1) * g
 
620
    end
 
621
    return clone_
 
622
end
 
623
 
 
624
-- Returns the sum of the pitches in the chord.
 
625
 
 
626
function Chord:layer()
 
627
    local s = 0
 
628
    for voice, pitch in ipairs(self) do
 
629
        s = s + pitch
 
630
    end
 
631
    return s
 
632
end
 
633
 
 
634
-- Transposes the chord by the indicated interval (may be a fraction).
 
635
-- NOTE: Does NOT return the result under any equivalence class.
 
636
 
 
637
function Chord:T(interval)
 
638
    local clone_ = self:clone()
 
639
    for voice = 1, #clone_ do
 
640
        clone_[voice] = T(clone_[voice], interval)
 
641
    end
 
642
    return clone_
 
643
end
 
644
 
 
645
-- Inverts the chord by another chord that is on the unison diagonal, by
 
646
-- default the origin. NOTE: Does NOT return the result under any equivalence
 
647
-- class.
 
648
 
 
649
function Chord:I(center)
 
650
    center = center or 0
 
651
    local inverse = self:clone()
 
652
    for voice = 1, #inverse do
 
653
        inverse[voice] = I(self[voice], center)
 
654
    end
 
655
    return inverse
 
656
end
 
657
 
 
658
-- Returns the equivalent of the pitch under pitch-class equivalence, i.e.
 
659
-- the pitch is in the interval [0, OCTAVE).
 
660
 
 
661
function ChordSpace.epc(pitch)
 
662
    --while pitch < 0 do
 
663
    while ChordSpace.lt_epsilon(pitch, 0) do
 
664
        pitch = pitch + ChordSpace.OCTAVE
 
665
    end
 
666
    --while pitch >= ChordSpace.OCTAVE do
 
667
    while ChordSpace.ge_epsilon(pitch, ChordSpace.OCTAVE) do
 
668
        pitch = pitch - ChordSpace.OCTAVE
 
669
    end
 
670
    return pitch
 
671
end
 
672
 
 
673
-- Returns whether the chord is within the fundamental domain of
 
674
-- pitch-class equivalence, i.e. is a pitch-class set.
 
675
 
 
676
function Chord:isepcs()
 
677
    for voice = 1, #chord do
 
678
        --if not (self[voice] == ChordSpace.epc(chord[voice])) then
 
679
        if not ChordSpace.eq_epsilon(self[voice], ChordSpace.epc(chord[voice])) then
 
680
            return false
 
681
        end
 
682
    end
 
683
    return true
 
684
end
 
685
 
 
686
-- Returns the equivalent of the chord under pitch-class equivalence,
 
687
-- i.e. the pitch-class set of the chord.
 
688
 
 
689
function Chord:epcs()
 
690
    local chord = self:clone()
 
691
    for voice = 1, #chord do
 
692
        chord[voice] = ChordSpace.epc(chord[voice])
 
693
    end
 
694
    return chord
 
695
end
 
696
 
 
697
-- Returns whether the chord is within the fundamental domain of
 
698
-- transposition to 0.
 
699
 
 
700
function Chord:iset()
 
701
    local et = self:et()
 
702
    if not (et == self) then
 
703
        return false
 
704
    end
 
705
    return true
 
706
end
 
707
 
 
708
-- Returns the equivalent of the chord within the fundamental domain of
 
709
-- transposition to 0.
 
710
 
 
711
function Chord:et()
 
712
    local min_ = self:min()
 
713
    return self:T(-min_)
 
714
end
 
715
 
 
716
-- Returns whether the chord is within the representative fundamental domain
 
717
-- of the indicated range equivalence.
 
718
 
 
719
function Chord:iseR(range)
 
720
    --[[ GVLS:
 
721
    local max_ = self:max()
 
722
    local min_ = self:min()
 
723
    if not (max_ <= (min_ + range)) then
 
724
        return false
 
725
    end
 
726
    local layer_ = self:layer()
 
727
    if not ((0 <= layer_) and (layer_ <= range)) then
 
728
        return false
 
729
    end
 
730
    return true
 
731
    --]]
 
732
    --[[ GVLS modified:
 
733
    local max_ = self:max()
 
734
    local min_ = self:min()
 
735
    if not ChordSpace.le_epsilon(max_, (min_ + range)) then
 
736
        return false
 
737
    end
 
738
    local layer_ = self:layer()
 
739
    if not (ChordSpace.le_epsilon(0, layer_) and ChordSpace.le_epsilon(layer_, range)) then
 
740
        return false
 
741
    end
 
742
    return true
 
743
    --]]
 
744
    ----[[ MKG several equivalents of boundary points in domain:
 
745
    local max_ = self:max()
 
746
    local min_ = self:min()
 
747
    if not ChordSpace.le_epsilon(max_, (min_ + range)) then
 
748
        return false
 
749
    end
 
750
    local layer_ = self:layer()
 
751
    if not (ChordSpace.le_epsilon(0, layer_) and ChordSpace.le_epsilon(layer_, range)) then
 
752
        return false
 
753
    end
 
754
    return true
 
755
    --]]
 
756
end
 
757
 
 
758
-- Returns whether the chord is within the representative fundamental domain
 
759
-- of octave equivalence.
 
760
 
 
761
function Chord:iseO()
 
762
    return self:iseR(ChordSpace.OCTAVE)
 
763
end
 
764
 
 
765
-- Returns the equivalent of the chord within the representative fundamental
 
766
-- domain of a range equivalence.
 
767
 
 
768
function Chord:eR(range)
 
769
    local chord = self:clone()
 
770
    --if chord:iseR(range) then
 
771
    --    return chord
 
772
    --end
 
773
    -- The clue here is that at least one voice must be >= 0,
 
774
    -- but no voice can be > range.
 
775
    -- First, move all pitches inside the interval [0, OCTAVE),
 
776
    -- which is not the same as the fundamental domain.
 
777
    chord = self:epcs()
 
778
    -- Then, reflect voices that are outside of the fundamental domain
 
779
    -- back into it, which will revoice the chord, i.e.
 
780
    -- the sum of pitches is in [0, OCTAVE].
 
781
    --while chord:layer() > range do
 
782
    while ChordSpace.gt_epsilon(chord:layer(), range) do
 
783
        local maximumPitch, maximumVoice = chord:max()
 
784
        -- Because no voice is above the range,
 
785
        -- any voices that need to be revoiced will now be negative.
 
786
        chord[maximumVoice] = maximumPitch - ChordSpace.OCTAVE
 
787
    end
 
788
    return chord
 
789
end
 
790
 
 
791
-- Returns the equivalent of the chord within the representative fundamental
 
792
-- domain of octave equivalence.
 
793
 
 
794
function Chord:eO()
 
795
    return self:eR(ChordSpace.OCTAVE)
 
796
end
 
797
 
 
798
-- Returns whether the chord is within the representative fundamental domain
 
799
-- of permutational equivalence.
 
800
 
 
801
function Chord:iseP()
 
802
    for voice = 2, #self do
 
803
        --if not (self[voice - 1] <= self[voice]) then
 
804
        if not ChordSpace.le_epsilon(self[voice - 1], self[voice]) then
 
805
            return false
 
806
        end
 
807
    end
 
808
    return true
 
809
end
 
810
 
 
811
-- Returns the equivalent of the chord within the representative fundamental
 
812
-- domain of permutational equivalence.
 
813
 
 
814
function Chord:eP()
 
815
    clone_ = self:clone()
 
816
    table.sort(clone_)
 
817
    return clone_
 
818
end
 
819
 
 
820
-- Returns whether the chord is within the representative fundamental domain
 
821
-- of transpositional equivalence.
 
822
 
 
823
function Chord:iseT()
 
824
    ----[[ GVLS:
 
825
    local layer_ = self:layer()
 
826
    if not ChordSpace.eq_epsilon(layer_, 0) then
 
827
        return false
 
828
    end
 
829
    return true
 
830
    --]]
 
831
    --[[ MKG:
 
832
    g = g or 1
 
833
    local ep = self:eP()
 
834
    if not (ep == ep:eT(g):eP()) then
 
835
        return false
 
836
    end
 
837
    return true
 
838
    --]]
 
839
end
 
840
 
 
841
-- Returns the equivalent of the chord within the representative fundamental
 
842
-- domain of transpositonal equivalence.
 
843
 
 
844
function Chord:eT()
 
845
    ----[[ GVLS:
 
846
    local layer_ = self:layer()
 
847
    local sumPerVoice = layer_ / #self
 
848
    return self:T(-sumPerVoice)
 
849
    --]]
 
850
    --[[ MKG:
 
851
    g = g or 1
 
852
    local iterator = self
 
853
    -- Transpose down to layer 0 or just below.
 
854
    while iterator:layer() > 0 do
 
855
        iterator = iterator:T(-g)
 
856
    end
 
857
    -- Transpose up to layer 0 or just above.
 
858
    while iterator:layer() < 0 do
 
859
        iterator = iterator:T(g)
 
860
    end
 
861
    return iterator
 
862
    --]]
 
863
end
 
864
 
 
865
-- Returns the equivalent of the chord within the representative fundamental
 
866
-- domain of transpositonal equivalence and the equal temperament generated
 
867
-- by g. I.e., returns the chord transposed such that its layer is 0 or, under
 
868
-- transposition, the positive layer closest to 0. NOTE: Does NOT return the
 
869
-- result under any other equivalence class.
 
870
 
 
871
function Chord:eTT(g)
 
872
    g = g or 1
 
873
    local et = self:eT()
 
874
    local transposition = math.ceil(et[1]) - et[1]
 
875
    local ett = et:T(transposition)
 
876
    return ett
 
877
end
 
878
 
 
879
-- Returns whether the chord is within the representative fundamental domain
 
880
-- of translational equivalence and the equal temperament generated by g.
 
881
 
 
882
function Chord:iseTT(g)
 
883
    g = g or 1
 
884
    local ep = self:eP()
 
885
    if not (ep == ep:eTT(g)) then
 
886
        return false
 
887
    end
 
888
    return true
 
889
end
 
890
 
 
891
-- Returns whether the chord is within the representative fundamental domain
 
892
-- of inversional equivalence.
 
893
 
 
894
function Chord:iseI(inverse)
 
895
    --[[ GLVS:
 
896
    local chord = self:clone()
 
897
    local lowerVoice = 2
 
898
    local upperVoice = #chord
 
899
    while lowerVoice < upperVoice do
 
900
        -- GVLS: tests only 1 interval: x[2] - x[1] <= x[#x] - x[#x - 1]
 
901
        local lowerInterval = chord[lowerVoice] - chord[lowerVoice - 1]
 
902
        local upperInterval = chord[upperVoice] - chord[upperVoice - 1]
 
903
        if lowerInterval < upperInterval then
 
904
            return true
 
905
        end
 
906
        if lowerInterval > upperInterval then
 
907
            return false
 
908
        end
 
909
        lowerVoice = lowerVoice + 1
 
910
        upperVoice = upperVoice - 1
 
911
    end
 
912
    return true
 
913
    --]]
 
914
    ----[[ MKG:
 
915
    inverse = inverse or self:I()
 
916
    if not self:__le(inverse) then
 
917
        return false
 
918
    end
 
919
    return true
 
920
    --]]
 
921
    --[[ MKG:
 
922
    inverse = self:I()
 
923
    if not self:__le(inverse) then
 
924
        return false
 
925
    end
 
926
    return true
 
927
    --]]
 
928
end
 
929
 
 
930
-- Returns the equivalent of the chord within the representative fundamental
 
931
-- domain of inversional equivalence.
 
932
 
 
933
function Chord:eI()
 
934
    if self:iseI() then
 
935
        return self:clone()
 
936
    end
 
937
    return self:I()
 
938
end
 
939
 
 
940
-- Returns whether the chord is within the representative fundamental domain
 
941
-- of range and permutational equivalence.
 
942
 
 
943
function Chord:iseRP(range)
 
944
    --[[ GVLS:
 
945
    for voice = 2, #self do
 
946
        if not (self[voice - 1] <= self[voice]) then
 
947
            return false
 
948
        end
 
949
    end
 
950
    if not (self[#self] <= (self[1] + range)) then
 
951
        return false
 
952
    end
 
953
    local layer_ = self:layer()
 
954
    if not ((0 <= layer_) and (layer_ <= range)) then
 
955
        return false
 
956
    end
 
957
    return true
 
958
    --]]
 
959
    ----[[ MKG:
 
960
    if not self:iseR(range) then
 
961
        return false
 
962
    end
 
963
    if not self:iseP() then
 
964
        return false
 
965
    end
 
966
    return true
 
967
    --]]
 
968
end
 
969
 
 
970
-- Returns whether the chord is within the representative fundamental domain
 
971
-- of octave and permutational equivalence.
 
972
 
 
973
function Chord:iseOP()
 
974
    return self:iseRP(ChordSpace.OCTAVE)
 
975
end
 
976
 
 
977
-- Returns the equivalent of the chord within the representative fundamental
 
978
-- domain of range and permutational equivalence.
 
979
 
 
980
function Chord:eRP(range)
 
981
    return self:eR(range):eP()
 
982
end
 
983
 
 
984
-- Returns the equivalent of the chord within the representative fundamental
 
985
-- domain of octave and permutational equivalence.
 
986
 
 
987
function Chord:eOP()
 
988
    return self:eRP(ChordSpace.OCTAVE)
 
989
end
 
990
 
 
991
-- Returns a copy of the chord cyclically permuted by a stride, by default 1.
 
992
-- The direction of rotation is the same as musicians' first inversion, second
 
993
-- inversion, and so on.
 
994
 
 
995
function Chord:cycle(stride)
 
996
    stride = stride or 1
 
997
    local clone_ = self:clone()
 
998
    if stride < 0 then
 
999
        for i = 1, stride do
 
1000
            local tail = table.remove(clone_)
 
1001
            table.insert(clone_, 1, tail)
 
1002
        end
 
1003
        return chord
 
1004
    end
 
1005
    if stride > 0 then
 
1006
        for i = 1, math.abs(stride) do
 
1007
            local head = table.remove(clone_, 1)
 
1008
            table.insert(clone_, head)
 
1009
        end
 
1010
    end
 
1011
    return clone_
 
1012
end
 
1013
 
 
1014
-- Returns the permutations of the pitches in a chord. The permutations from
 
1015
-- each particular permutation are always returned in the same order.
 
1016
 
 
1017
function Chord:permutations()
 
1018
    local chord = self:clone()
 
1019
    local permutations_ = {}
 
1020
    permutations_[1] = chord
 
1021
    for i = 2, #self do
 
1022
        chord = chord:cycle()
 
1023
        permutations_[i] = chord
 
1024
    end
 
1025
    table.sort(permutations_)
 
1026
    return permutations_
 
1027
end
 
1028
 
 
1029
-- Returns whether the chord is within the representative fundamental domain
 
1030
-- of voicing equivalence.
 
1031
 
 
1032
function Chord:iseV()
 
1033
    local eV = self:eV()
 
1034
    --print(string.format('chord: %s  eV: %s', tostring(self), tostring(eV)))
 
1035
    if not (self == eV) then
 
1036
        return false
 
1037
    end
 
1038
    return true
 
1039
end
 
1040
 
 
1041
-- Returns the equivalent of the chord within the representative fundamental
 
1042
-- domain of voicing equivalence.
 
1043
 
 
1044
function Chord:eV()
 
1045
    ----[[
 
1046
    for index, voicing in ipairs(self:permutations()) do
 
1047
        local wraparound = voicing[1] + ChordSpace.OCTAVE - voicing[#voicing]
 
1048
        local iseV_ = true
 
1049
        for voice = 1, #voicing - 1 do
 
1050
            local inner = voicing[voice + 1] - voicing[voice]
 
1051
            if not ChordSpace.ge_epsilon(wraparound, inner) then
 
1052
            --if inner > wraparound then
 
1053
                iseV_ = false
 
1054
            end
 
1055
        end
 
1056
        if iseV_ then
 
1057
            return voicing
 
1058
        end
 
1059
    end
 
1060
    --]]
 
1061
    --[[
 
1062
    local voicings = self:permutations()
 
1063
    local distancesForIndexes = {}
 
1064
    for i = 1, #voicings do
 
1065
        distancesForIndexes[i] = voicings[i]:distanceToUnisonDiagonal()
 
1066
    end
 
1067
    local minimumDistanceToUnisonDiagonal = distancesForIndexes[1]
 
1068
    for i = 2, #voicings do
 
1069
        if distancesForIndexes[i] < minimumDistanceToUnisonDiagonal then
 
1070
            minimumDistanceToUnisonDiagonal = distancesForIndexes[i]
 
1071
        end
 
1072
    end
 
1073
    for i = 1, #voicings do
 
1074
         if distancesForIndexes[i] == minimumDistanceToUnisonDiagonal then
 
1075
            return voicings[i]
 
1076
        end
 
1077
    end
 
1078
    --]]
 
1079
end
 
1080
 
 
1081
-- Returns whether the chord is within the representative fundamental domain
 
1082
-- of range, permutational, and transpositional equivalence.
 
1083
 
 
1084
function Chord:iseRPT(range)
 
1085
    --[[ GVLS:
 
1086
    -- GVLS: if not (self[#self] <= self[1] + ChordSpace.OCTAVE) then
 
1087
    if not (ChordSpace.le_epsilon(self[#self], (self[1] + range))) then
 
1088
        return false
 
1089
    end
 
1090
    local layer_ = self:layer()
 
1091
    -- GVLS: if not (layer_ == 0) then
 
1092
    if not ChordSpace.eq_epsilon(layer_, 0) then
 
1093
        return false
 
1094
    end
 
1095
    if #self < 2 then
 
1096
        return true
 
1097
    end
 
1098
    local wraparound = self[1] + range - self[#self]
 
1099
    for voice = 1, #self - 1  do
 
1100
        local inner = self[voice + 1] - self[voice]
 
1101
        if not ChordSpace.le_epsilon(wraparound, inner) then
 
1102
            return false
 
1103
        end
 
1104
    end
 
1105
    return true
 
1106
    --]]
 
1107
    ----[[ MKG:
 
1108
    if not self:iseR(range) then
 
1109
        return false
 
1110
    end
 
1111
    if not self:iseP() then
 
1112
        return false
 
1113
    end
 
1114
    if not self:iseT() then
 
1115
        return false
 
1116
    end
 
1117
    if not self:iseV() then
 
1118
        return false
 
1119
    end
 
1120
    return true
 
1121
    --]]
 
1122
end
 
1123
 
 
1124
function Chord:iseRPTT(range)
 
1125
    if not self:iseP() then
 
1126
        return false
 
1127
    end
 
1128
    if not self:iseR(range) then
 
1129
        return false
 
1130
    end
 
1131
    if not self:iseTT() then
 
1132
        return false
 
1133
    end
 
1134
    if not self:iseV() then
 
1135
        return false
 
1136
    end
 
1137
    return true
 
1138
end
 
1139
 
 
1140
-- Returns whether the chord is within the representative fundamental domain
 
1141
-- of octave, permutational, and transpositional equivalence.
 
1142
 
 
1143
function Chord:iseOPT()
 
1144
    return self:iseRPT(ChordSpace.OCTAVE)
 
1145
end
 
1146
 
 
1147
function Chord:iseOPTT()
 
1148
    return self:iseRPTT(ChordSpace.OCTAVE)
 
1149
end
 
1150
 
 
1151
-- Returns a copy of the chord 'inverted' in the musician's sense,
 
1152
-- i.e. revoiced by cyclically permuting the chord and
 
1153
-- adding (or subtracting) an octave to the highest (or lowest) voice.
 
1154
-- The revoicing will move the chord up or down in pitch.
 
1155
-- A positive direction is the same as a musician's first inversion,
 
1156
-- second inversion, etc.
 
1157
 
 
1158
function Chord:v(direction)
 
1159
    direction = direction or 1
 
1160
    local chord = self:clone()
 
1161
    while direction > 0 do
 
1162
        chord = chord:cycle(1)
 
1163
        chord[#chord] = chord[#chord] + ChordSpace.OCTAVE
 
1164
        direction = direction - 1
 
1165
    end
 
1166
    while direction < 0 do
 
1167
        chord = chord:cycle(-1)
 
1168
        chord[1] = chord[1] - ChordSpace.OCTAVE
 
1169
        direction = direction + 1
 
1170
    end
 
1171
    return chord
 
1172
end
 
1173
 
 
1174
-- Returns all the 'inversions' (in the musician's sense)
 
1175
-- or octavewise revoicings of the chord.
 
1176
 
 
1177
function Chord:voicings()
 
1178
    local chord = self:clone()
 
1179
    local voicings = {}
 
1180
    voicings[1] = chord
 
1181
    for i = 2, #self do
 
1182
        chord = chord:v()
 
1183
        voicings[i] = chord
 
1184
    end
 
1185
    return voicings
 
1186
end
 
1187
 
 
1188
-- Returns the equivalent of the chord within the representative fundamental
 
1189
-- domain of range, permutational, and transpositional equivalence; the same
 
1190
-- as set-class type, or chord type.
 
1191
 
 
1192
function Chord:eRPT(range)
 
1193
    --[[
 
1194
    local erp = self:eRP(range)
 
1195
    local voicings_ = erp:voicings()
 
1196
    for voice = 1, #voicings_ do
 
1197
        local voicing = voicings_[voice]:eT()
 
1198
        if voicing:iseV() then
 
1199
            return voicing
 
1200
        end
 
1201
    end
 
1202
    print('ERROR: Chord:eRPT() should not come here: ' .. tostring(self))
 
1203
    --]]
 
1204
    ----[[
 
1205
    local erp = self:eRP(range)
 
1206
    local voicings_ = erp:voicings()
 
1207
    for voice = 1, #voicings_ do
 
1208
        local voicing = voicings_[voice]
 
1209
        if voicing:iseV() then
 
1210
            return voicing:eT()
 
1211
        end
 
1212
    end
 
1213
    print('ERROR: Chord:eRPT() should not come here: ' .. tostring(self))
 
1214
    --]]
 
1215
end
 
1216
 
 
1217
function Chord:eRPTT(range)
 
1218
    local erp = self:eRP(range)
 
1219
    local voicings_ = erp:voicings()
 
1220
    for voice = 1, #voicings_ do
 
1221
        local voicing = voicings_[voice]:eTT()
 
1222
        if voicing:iseV() then
 
1223
            return voicing
 
1224
        end
 
1225
    end
 
1226
    print('ERROR: Chord:eRPTT() should not come here: ' .. tostring(self))
 
1227
end
 
1228
 
 
1229
-- Returns the equivalent of the chord within the representative fundamental
 
1230
-- domain of octave, permutational, and transpositional equivalence.
 
1231
 
 
1232
function Chord:eOPT()
 
1233
    return self:eRPT(ChordSpace.OCTAVE)
 
1234
end
 
1235
 
 
1236
function Chord:eOPTT()
 
1237
    return self:eRPTT(ChordSpace.OCTAVE)
 
1238
end
 
1239
 
 
1240
-- Returns whether the chord is within the representative fundamental domain
 
1241
-- of range, permutational, and inversional equivalence.
 
1242
 
 
1243
function Chord:iseRPI(range)
 
1244
    if not self:iseRP(range) then
 
1245
        return false
 
1246
    end
 
1247
    local inverse = self:I():eRP(range)
 
1248
    assert(inverse, 'Inverse is nil.')
 
1249
    if not self:iseI(inverse) then
 
1250
        return false
 
1251
    end
 
1252
    return true
 
1253
end
 
1254
 
 
1255
-- Returns whether the chord is within the representative fundamental domain
 
1256
-- of octave, permutational, and inversional equivalence.
 
1257
 
 
1258
function Chord:iseOPI()
 
1259
    return self:iseRPI(ChordSpace.OCTAVE)
 
1260
end
 
1261
 
 
1262
-- Returns the equivalent of the chord within the representative fundamental
 
1263
-- domain of range, permutational, and inversional equivalence.
 
1264
 
 
1265
function Chord:eRPI(range)
 
1266
    local erp = self:eRP(range)
 
1267
    if erp:iseRPI(range) then
 
1268
        return erp
 
1269
    end
 
1270
    return erp:I():eRP(range)
 
1271
end
 
1272
 
 
1273
-- Returns the equivalent of the chord within the representative fundamental
 
1274
-- domain of octave, permutational, and inversional equivalence.
 
1275
 
 
1276
function Chord:eOPI()
 
1277
    return self:eRPI(ChordSpace.OCTAVE)
 
1278
end
 
1279
 
 
1280
-- Returns whether the chord is within the representative fundamental domain
 
1281
-- of range, permutational, transpositional, and inversional equivalence.
 
1282
 
 
1283
function Chord:iseRPTI(range)
 
1284
    --[[ GVLS:
 
1285
    -- GVLS: if not (self[#self] <= self[1] + ChordSpace.OCTAVE) then
 
1286
    if not ChordSpace.le_epsilon(self[#self], (self[1] + range)) then
 
1287
        return false
 
1288
    end
 
1289
    local layer_ = self:layer()
 
1290
    -- GVLS: if not (layer_ == 0) then
 
1291
    if not ChordSpace.eq_epsilon(layer_, 0) then
 
1292
        return false
 
1293
    end
 
1294
    if #self <= 2 then
 
1295
        return true
 
1296
    end
 
1297
    local wraparound = self[1] + range - self[#self]
 
1298
    for voice = 1, #self - 1  do
 
1299
        local inner = self[voice + 1] - self[voice]
 
1300
        if not ChordSpace.le_epsilon(wraparound, inner) then
 
1301
            return false
 
1302
        end
 
1303
    end
 
1304
    if not self:iseI() then
 
1305
        return false
 
1306
    end
 
1307
    return true
 
1308
    --]]
 
1309
    ----[[ MKG:
 
1310
    if not self:iseRPT(range) then
 
1311
        return false
 
1312
    end
 
1313
    if not self:iseI() then
 
1314
        return false
 
1315
    end
 
1316
    return true
 
1317
    --]]
 
1318
end
 
1319
 
 
1320
function Chord:iseRPTTI(range)
 
1321
    if not self:iseRPTT(range) then
 
1322
        return false
 
1323
    end
 
1324
    if not self:iseI() then
 
1325
        return false
 
1326
    end
 
1327
    return true
 
1328
end
 
1329
 
 
1330
-- Returns whether the chord is within the representative fundamental domain
 
1331
-- of octave, permutational, transpositional, and inversional equivalence.
 
1332
 
 
1333
function Chord:iseOPTI()
 
1334
    return self:iseRPTI(ChordSpace.OCTAVE)
 
1335
end
 
1336
 
 
1337
function Chord:iseOPTTI()
 
1338
    return self:iseRPTTI(ChordSpace.OCTAVE)
 
1339
end
 
1340
 
 
1341
-- Returns the equivalent of the chord within the representative fundamental
 
1342
-- domain of range, permutational, transpositional, and inversional
 
1343
-- equivalence.
 
1344
 
 
1345
function Chord:eRPTI(range)
 
1346
    local rpt = self:eRPT(range)
 
1347
    if rpt:iseI() then
 
1348
        return rpt
 
1349
    end
 
1350
    return rpt:I():eRPT(range)
 
1351
end
 
1352
 
 
1353
function Chord:eRPTTI(range)
 
1354
    local rpt = self:eRPTT(range)
 
1355
    if rpt:iseRPTTI(range) then
 
1356
        return rpt
 
1357
    end
 
1358
    return rpt:I():eRPTT(range)
 
1359
end
 
1360
 
 
1361
-- Returns the equivalent of the chord within the representative fundamental
 
1362
-- domain of range, permutational, transpositional, and inversional
 
1363
-- equivalence.
 
1364
 
 
1365
function Chord:eOPTI()
 
1366
    return self:eRPTI(ChordSpace.OCTAVE)
 
1367
end
 
1368
 
 
1369
function Chord:eOPTTI()
 
1370
    return self:eRPTTI(ChordSpace.OCTAVE)
 
1371
end
 
1372
 
 
1373
function Chord:name()
 
1374
    local chordName = ChordSpace.namesForChords[self:__hash()]
 
1375
    if chordName == nil then
 
1376
        chordName = ''
 
1377
    end
 
1378
    return chordName
 
1379
end
 
1380
 
 
1381
-- Returns a formatted string with information about the chord.
 
1382
 
 
1383
function Chord:information()
 
1384
    local et = self:eT():et()
 
1385
    local evt = self:eV():et()
 
1386
    local eopt = self:eOPT():et()
 
1387
    local epcs = self:epcs():eP()
 
1388
    local eopti = self:eOPTI():et()
 
1389
    local eOP = self:eOP()
 
1390
    chordName = eOP:name()
 
1391
    return string.format([[pitches:  %s  %s
 
1392
I:        %s
 
1393
eO:       %s  iseO:    %s
 
1394
eP:       %s  iseP:    %s
 
1395
eT:       %s  iseT:    %s
 
1396
          %s
 
1397
eI:       %s  iseI:    %s
 
1398
eV:       %s  iseV:    %s
 
1399
          %s
 
1400
eOP:      %s  iseOP:   %s
 
1401
pcs:      %s
 
1402
eOPT:     %s  iseOPT:  %s
 
1403
eOPTT:    %s
 
1404
          %s
 
1405
eOPI:     %s  iseOPI:  %s
 
1406
eOPTI:    %s  iseOPTI: %s
 
1407
eOPTTI:   %s
 
1408
          %s
 
1409
layer:      %6.2f]],
 
1410
tostring(self), chordName,
 
1411
tostring(self:I()),
 
1412
tostring(self:eO()),    tostring(self:iseO()),
 
1413
tostring(self:eP()),    tostring(self:iseP()),
 
1414
tostring(self:eT()),    tostring(self:iseT()),
 
1415
tostring(et),
 
1416
tostring(self:eI()),    tostring(self:iseI()),
 
1417
tostring(self:eV()),    tostring(self:iseV()),
 
1418
tostring(evt),
 
1419
tostring(self:eOP()),   tostring(self:iseOP()),
 
1420
tostring(epcs),
 
1421
tostring(self:eOPT()),  tostring(self:iseOPT()),
 
1422
tostring(self:eOPTT()),
 
1423
tostring(eopt),
 
1424
tostring(self:eOPI()),  tostring(self:iseOPI()),
 
1425
tostring(self:eOPTI()), tostring(self:iseOPTI()),
 
1426
tostring(self:eOPTTI()),
 
1427
tostring(eopti),
 
1428
self:layer())
 
1429
end
 
1430
 
 
1431
function ChordSpace.set(collection)
 
1432
    local set_ = {}
 
1433
    for key, value in pairs(collection) do
 
1434
        set_[value:__hash()] = value
 
1435
    end
 
1436
    return set_
 
1437
end
 
1438
 
 
1439
function ChordSpace.sortedSet(collection)
 
1440
    local set_ = ChordSpace.set(collection)
 
1441
    local sortedSet_ = {}
 
1442
    for key, value in pairs(set_) do
 
1443
        table.insert(sortedSet_, value)
 
1444
    end
 
1445
    table.sort(sortedSet_)
 
1446
    return sortedSet_
 
1447
end
 
1448
 
 
1449
function ChordSpace.zeroBasedSet(sortedSet)
 
1450
    local zeroBasedSet = {}
 
1451
    for index, value in pairs(sortedSet) do
 
1452
        zeroBasedSet[index - 1] = value
 
1453
    end
 
1454
    return zeroBasedSet
 
1455
end
 
1456
 
 
1457
function ChordSpace.setContains(setA, x)
 
1458
    if setA[x:__hash()] == x then
 
1459
        return true
 
1460
    end
 
1461
    return false
 
1462
end
 
1463
 
 
1464
function ChordSpace.setInsert(setA, x)
 
1465
    if not ChordSpace.setContains(setA, x) then
 
1466
        setA[x:__hash()] = x
 
1467
    end
 
1468
end
 
1469
 
 
1470
function ChordSpace.sortedEquals(sortedA, sortedB)
 
1471
    if not (#sortedA == #sortedB) then
 
1472
        return false
 
1473
    end
 
1474
    for i = 1, #sortedA do
 
1475
        if not (sortedA[i] == sortedB[i]) then
 
1476
            return false
 
1477
        end
 
1478
    end
 
1479
    return true
 
1480
end
 
1481
 
 
1482
function ChordSpace.setIntersection(A, setB)
 
1483
    local result = {}
 
1484
    for index, value in pairs(A) do
 
1485
        if ChordSpace.setContains(setB, value) then
 
1486
            ChordSpace.setInsert(result, value)
 
1487
        end
 
1488
    end
 
1489
    return result
 
1490
end
 
1491
 
 
1492
function ChordSpace.union(A, B)
 
1493
    local result = {}
 
1494
    for index, value in pairs(A) do
 
1495
        ChordSpace.setInsert(result, value)
 
1496
    end
 
1497
    for index, value in pairs(B) do
 
1498
        ChordSpace.setInsert(result, value)
 
1499
    end
 
1500
    return result
 
1501
end
 
1502
 
 
1503
ChordSpace.pitchClassesForNames = {}
 
1504
 
 
1505
ChordSpace.pitchClassesForNames["C" ] =  0
 
1506
ChordSpace.pitchClassesForNames["C#"] =  1
 
1507
ChordSpace.pitchClassesForNames["Db"] =  1
 
1508
ChordSpace.pitchClassesForNames["D" ] =  2
 
1509
ChordSpace.pitchClassesForNames["D#"] =  3
 
1510
ChordSpace.pitchClassesForNames["Eb"] =  3
 
1511
ChordSpace.pitchClassesForNames["E" ] =  4
 
1512
ChordSpace.pitchClassesForNames["F" ] =  5
 
1513
ChordSpace.pitchClassesForNames["F#"] =  6
 
1514
ChordSpace.pitchClassesForNames["Gb"] =  6
 
1515
ChordSpace.pitchClassesForNames["G" ] =  7
 
1516
ChordSpace.pitchClassesForNames["G#"] =  8
 
1517
ChordSpace.pitchClassesForNames["Ab"] =  8
 
1518
ChordSpace.pitchClassesForNames["A" ] =  9
 
1519
ChordSpace.pitchClassesForNames["A#"] = 10
 
1520
ChordSpace.pitchClassesForNames["Bb"] = 10
 
1521
ChordSpace.pitchClassesForNames["B" ] = 11
 
1522
 
 
1523
ChordSpace.chordsForNames = {}
 
1524
ChordSpace.namesForChords = {}
 
1525
 
 
1526
local function fill(rootName, rootPitch, typeName, typePitches)
 
1527
    local chordName = rootName .. typeName
 
1528
    local chord = Chord:new()
 
1529
    local splitPitches = Silencio.split(typePitches)
 
1530
    chord:resize(#splitPitches)
 
1531
    for voice, pitchName in ipairs(splitPitches) do
 
1532
        local pitch = ChordSpace.pitchClassesForNames[pitchName]
 
1533
        chord[voice] = rootPitch + pitch
 
1534
    end
 
1535
    chord = chord:eOP()
 
1536
    ChordSpace.chordsForNames[chordName] = chord
 
1537
    ChordSpace.namesForChords[chord:__hash()] = chordName
 
1538
end
 
1539
 
 
1540
for rootName, rootPitch in pairs(ChordSpace.pitchClassesForNames) do
 
1541
    fill(rootName, rootPitch, " minor second",     "C  C#                             ")
 
1542
    fill(rootName, rootPitch, " major second",     "C     D                           ")
 
1543
    fill(rootName, rootPitch, " minor third",      "C        Eb                       ")
 
1544
    fill(rootName, rootPitch, " major third",      "C           E                     ")
 
1545
    fill(rootName, rootPitch, " perfect fourth",   "C              F                  ")
 
1546
    fill(rootName, rootPitch, " tritone",          "C                 F#              ")
 
1547
    fill(rootName, rootPitch, " perfect fifth",    "C                    G            ")
 
1548
    fill(rootName, rootPitch, " augmented fifth",  "C                       G#        ")
 
1549
    fill(rootName, rootPitch, " sixth",            "C                          A      ")
 
1550
    fill(rootName, rootPitch, " minor seventh  ",  "C                             Bb  ")
 
1551
    fill(rootName, rootPitch, " major seventh",    "C                                B")
 
1552
    -- Scales.
 
1553
    fill(rootName, rootPitch, " major",            "C     D     E  F     G     A     B")
 
1554
    fill(rootName, rootPitch, " minor",            "C     D  Eb    F     G  Ab    Bb  ")
 
1555
    fill(rootName, rootPitch, " natural minor",    "C     D  Eb    F     G  Ab    Bb  ")
 
1556
    fill(rootName, rootPitch, " harmonic minor",   "C     D  Eb    F     G  Ab       B")
 
1557
    fill(rootName, rootPitch, " chromatic",        "C  C# D  D# E  F  F# G  G# A  A# B")
 
1558
    fill(rootName, rootPitch, " whole tone",       "C     D     E     F#    G#    A#  ")
 
1559
    fill(rootName, rootPitch, " diminished",       "C     D  D#    F  F#    G# A     B")
 
1560
    fill(rootName, rootPitch, " pentatonic",       "C     D     E        G     A      ")
 
1561
    fill(rootName, rootPitch, " pentatonic major", "C     D     E        G     A      ")
 
1562
    fill(rootName, rootPitch, " pentatonic minor", "C        Eb    F     G        Bb  ")
 
1563
    fill(rootName, rootPitch, " augmented",        "C        Eb E        G  Ab    Bb  ")
 
1564
    fill(rootName, rootPitch, " Lydian dominant",  "C     D     E     Gb G     A  Bb  ")
 
1565
    fill(rootName, rootPitch, " 3 semitone",       "C        D#       F#       A      ")
 
1566
    fill(rootName, rootPitch, " 4 semitone",       "C           E           G#        ")
 
1567
    fill(rootName, rootPitch, " blues",            "C     D  Eb    F  Gb G        Bb  ")
 
1568
    fill(rootName, rootPitch, " bebop",            "C     D     E  F     G     A  Bb B")
 
1569
    -- Major chords.
 
1570
    fill(rootName, rootPitch, "M",                 "C           E        G            ")
 
1571
    fill(rootName, rootPitch, "6",                 "C           E        G     A      ")
 
1572
    fill(rootName, rootPitch, "69",                "C     D     E        G     A      ")
 
1573
    fill(rootName, rootPitch, "69b5",              "C     D     E     Gb       A      ")
 
1574
    fill(rootName, rootPitch, "M7",                "C           E        G           B")
 
1575
    fill(rootName, rootPitch, "M9",                "C     D     E        G           B")
 
1576
    fill(rootName, rootPitch, "M11",               "C     D     E  F     G           B")
 
1577
    fill(rootName, rootPitch, "M#11",              "C     D     E  F#    G           B")
 
1578
    fill(rootName, rootPitch, "M13",               "C     D     E  F     G     A     B")
 
1579
    -- Minor chords.
 
1580
    fill(rootName, rootPitch, "m",                 "C        Eb          G            ")
 
1581
    fill(rootName, rootPitch, "m6",                "C        Eb          G     A      ")
 
1582
    fill(rootName, rootPitch, "m69",               "C     D  Eb          G     A      ")
 
1583
    fill(rootName, rootPitch, "m7",                "C        Eb          G        Bb  ")
 
1584
    fill(rootName, rootPitch, "m#7",               "C        Eb          G           B")
 
1585
    fill(rootName, rootPitch, "m7b5",              "C        Eb       Gb          Bb  ")
 
1586
    fill(rootName, rootPitch, "m9",                "C     D  Eb          G        Bb  ")
 
1587
    fill(rootName, rootPitch, "m9#7",              "C     D  Eb          G           B")
 
1588
    fill(rootName, rootPitch, "m11",               "C     D  Eb    F     G        Bb  ")
 
1589
    fill(rootName, rootPitch, "m13",               "C     D  Eb    F     G     A  Bb  ")
 
1590
    -- Augmented chords.
 
1591
    fill(rootName, rootPitch, "+",                 "C            E         G#         ")
 
1592
    fill(rootName, rootPitch, "7#5",               "C            E         G#     Bb  ")
 
1593
    fill(rootName, rootPitch, "7b9#5",             "C  Db        E         G#     Bb  ")
 
1594
    fill(rootName, rootPitch, "9#5",               "C     D      E         G#     Bb  ")
 
1595
    -- Diminished chords.
 
1596
    fill(rootName, rootPitch, "o",                 "C        Eb       Gb              ")
 
1597
    fill(rootName, rootPitch, "o7",                "C        Eb       Gb       A      ")
 
1598
    -- Suspended chords.
 
1599
    fill(rootName, rootPitch, "6sus",              "C              F     G     A      ")
 
1600
    fill(rootName, rootPitch, "69sus",             "C     D        F     G     A      ")
 
1601
    fill(rootName, rootPitch, "7sus",              "C              F     G        Bb  ")
 
1602
    fill(rootName, rootPitch, "9sus",              "C     D        F     G        Bb  ")
 
1603
    fill(rootName, rootPitch, "M7sus",             "C              F     G           B")
 
1604
    fill(rootName, rootPitch, "M9sus",             "C     D        F     G           B")
 
1605
    -- Dominant chords.
 
1606
    fill(rootName, rootPitch, "7",                 "C            E       G        Bb  ")
 
1607
    fill(rootName, rootPitch, "7b5",               "C            E    Gb          Bb  ")
 
1608
    fill(rootName, rootPitch, "7b9",               "C  Db        E       G        Bb  ")
 
1609
    fill(rootName, rootPitch, "7b9b5",             "C  Db        E    Gb          Bb  ")
 
1610
    fill(rootName, rootPitch, "9",                 "C     D      E       G        Bb  ")
 
1611
    fill(rootName, rootPitch, "9#11",              "C     D      E F#    G        Bb  ")
 
1612
    fill(rootName, rootPitch, "13",                "C     D      E F     G     A  Bb  ")
 
1613
    fill(rootName, rootPitch, "13#11",             "C     D      E F#    G     A  Bb  ")
 
1614
end
 
1615
 
 
1616
table.sort(ChordSpace.chordsForNames)
 
1617
table.sort(ChordSpace.namesForChords)
 
1618
 
 
1619
-- Increment a chord voicewise through chord space,
 
1620
-- from a low point on the unison diagonal through a high point
 
1621
-- on the unison diagonal. g is the generator of transposition.
 
1622
-- It may be necessary to set the chord to the low point to start.
 
1623
 
 
1624
function ChordSpace.next(odometer, low, high, g)
 
1625
    local voices = #odometer
 
1626
    odometer[voices] = odometer[voices] + g
 
1627
     -- "Carry."
 
1628
    for voice = voices, 2, -1 do
 
1629
        if odometer[voice] > high then
 
1630
            odometer[voice] = low
 
1631
            odometer[voice - 1] = odometer[voice - 1] + g
 
1632
        end
 
1633
    end
 
1634
    if odometer[1] > high then
 
1635
        return false
 
1636
    end
 
1637
    return true
 
1638
end
 
1639
 
 
1640
function ChordSpace.allOfEquivalenceClass(voices, equivalence, g)
 
1641
    g = g or 1
 
1642
    local equivalenceMapper = nil
 
1643
    if equivalence == 'OP' then
 
1644
        equivalenceMapper = Chord.iseOP
 
1645
    end
 
1646
    if equivalence == 'OPT' then
 
1647
        equivalenceMapper = Chord.iseOPT
 
1648
    end
 
1649
    if equivalence == 'OPTT' then
 
1650
        equivalenceMapper = Chord.iseOPT
 
1651
    end
 
1652
    if equivalence == 'OPI' then
 
1653
        equivalenceMapper = Chord.iseOPI
 
1654
    end
 
1655
    if equivalence == 'OPTI' then
 
1656
        equivalenceMapper = Chord.iseOPTI
 
1657
    end
 
1658
    if equivalence == 'OPTTI' then
 
1659
        equivalenceMapper = Chord.iseOPTTI
 
1660
    end
 
1661
    local equivalentChords = {}
 
1662
    -- Enumerate all chords in [-O, O].
 
1663
    local iterator = ChordSpace.iterator(voices, -13)
 
1664
    -- print('iterator:', tostring(iterator))
 
1665
    while ChordSpace.next(iterator, -13, 13, g) == true do
 
1666
        if iterator:iseP() == true then
 
1667
            local eP = iterator:clone()
 
1668
            if equivalenceMapper(eP) then
 
1669
                ChordSpace.setInsert(equivalentChords, eP)
 
1670
            end
 
1671
        end
 
1672
    end
 
1673
    local equivalentChords = ChordSpace.sortedSet(equivalentChords)
 
1674
    local zeroBasedChords = ChordSpace.zeroBasedSet(equivalentChords)
 
1675
    return zeroBasedChords, equivalentChords
 
1676
end
 
1677
 
 
1678
-- Returns a chord with the specified number of voices all set to a first
 
1679
-- pitch, useful as an iterator.
 
1680
 
 
1681
function ChordSpace.iterator(voices, first)
 
1682
    local odometer = Chord:new()
 
1683
    odometer:resize(voices)
 
1684
    for voice = 1, voices do
 
1685
        odometer[voice] = first
 
1686
    end
 
1687
    return odometer
 
1688
end
 
1689
 
 
1690
-- Returns a collection of all chords for the specified number of voices in a
 
1691
-- range, by default the octave. g is the generator of transposition, by default the
 
1692
-- semitone.
 
1693
 
 
1694
function ChordSpace.allChordsInRange(voices, first, last, g)
 
1695
    first = first or 0
 
1696
    last = last or ChordSpace.OCTAVE
 
1697
    g = g or 1
 
1698
    -- Enumerate all chords in the range.
 
1699
    local chordset = {}
 
1700
    local iterator = ChordSpace.iterator(voices, first)
 
1701
    while ChordSpace.next(iterator, first, last, g) do
 
1702
        local chord = iterator:clone()
 
1703
        chordset[chord:__hash()] = chord
 
1704
    end
 
1705
    return ChordSpace.sortedSet(chordset)
 
1706
end
 
1707
 
 
1708
-- Move 1 voice of the chord,
 
1709
-- optionally under range equivalence
 
1710
-- NOTE: Does NOT return the result under any equivalence class.
 
1711
 
 
1712
function Chord:move(voice, interval)
 
1713
    local chord = self:clone()
 
1714
    chord[voice] = T(chord[voice], interval)
 
1715
    return chord
 
1716
end
 
1717
 
 
1718
-- Performs the neo-Riemannian Lettonwechsel transformation.
 
1719
-- NOTE: Does NOT return the result under any equivalence class.
 
1720
 
 
1721
function Chord:nrL()
 
1722
    local cv = self:eV()
 
1723
    local cvt = self:eV():et()
 
1724
    if cvt[2] == 4 then
 
1725
        cv[1] = cv[1] - 1
 
1726
    else
 
1727
        if cvt[2] == 3 then
 
1728
            cv[3] = cv[3] + 1
 
1729
        end
 
1730
    end
 
1731
    return cv
 
1732
end
 
1733
 
 
1734
-- Performs the neo-Riemannian parallel transformation.
 
1735
-- NOTE: Does NOT return the result under any equivalence class.
 
1736
 
 
1737
function Chord:nrP()
 
1738
    local cv = self:eV()
 
1739
    local cvt = self:eV():et()
 
1740
    if cvt[2] == 4 then
 
1741
        cv[2] = cv[2] - 1
 
1742
    else
 
1743
        if cvt[2] == 3 then
 
1744
            cv[2] = cv[2] + 1
 
1745
        end
 
1746
    end
 
1747
    return cv
 
1748
end
 
1749
 
 
1750
-- Performs the neo-Riemannian relative transformation.
 
1751
-- NOTE: Does NOT return the result under any equivalence class.
 
1752
 
 
1753
function Chord:nrR()
 
1754
    local cv = self:eV()
 
1755
    local cvt = self:eV():et()
 
1756
    if cvt[2] == 4 then
 
1757
        cv[3] = cv[3] + 2
 
1758
    else
 
1759
        if cvt[2] == 3 then
 
1760
            cv[1] = cv[1] - 2
 
1761
        end
 
1762
    end
 
1763
    return cv
 
1764
end
 
1765
 
 
1766
-- Performs the neo-Riemannian dominant transformation.
 
1767
-- NOTE: Does NOT return the result under any equivalence class.
 
1768
 
 
1769
function Chord:nrD()
 
1770
    return self:eep():T(-7)
 
1771
end
 
1772
 
 
1773
-- Returns the chord inverted by the sum of its first two voices.
 
1774
-- NOTE: Does NOT return the result under any equivalence class.
 
1775
 
 
1776
function Chord:K(range)
 
1777
    range = range or ChordSpace.OCTAVE
 
1778
    local chord = self:clone()
 
1779
    if #chord < 2 then
 
1780
        return chord
 
1781
    end
 
1782
    local ep = chord:eP()
 
1783
    local x = ep[1] + ep[2]
 
1784
    return self:I(x)
 
1785
end
 
1786
 
 
1787
-- Returns whether the chord is a transpositional form of Y with interval size g.
 
1788
-- Only works in equal temperament.
 
1789
 
 
1790
function Chord:Tform(Y, g)
 
1791
    local eopx = self:epcs()
 
1792
    local i = 0
 
1793
    while i < ChordSpace.OCTAVE do
 
1794
        local ty = Y:T(i)
 
1795
        local eopty = ty:epcs()
 
1796
        if eopx == eopty then
 
1797
            return true
 
1798
        end
 
1799
        i = i + g
 
1800
    end
 
1801
    return false
 
1802
end
 
1803
 
 
1804
-- Returns whether the chord is an inversional form of Y with interval size g.
 
1805
-- Only works in equal temperament.
 
1806
 
 
1807
function Chord:Iform(Y, g)
 
1808
    local eopx = self:epcs()
 
1809
    local i = 0
 
1810
    while i < ChordSpace.OCTAVE do
 
1811
        local iy = Y:I(i)
 
1812
        local eopiy = iy:epcs()
 
1813
        if eopx == eopiy then
 
1814
            return true
 
1815
        end
 
1816
        i = i + g
 
1817
    end
 
1818
    return false
 
1819
end
 
1820
 
 
1821
-- Returns the contextual transposition of the chord by x with respect to m
 
1822
-- with minimum interval size g.
 
1823
-- NOTE: Does NOT return the result under any equivalence class.
 
1824
 
 
1825
function Chord:Q(x, m, g)
 
1826
    g = g or 1
 
1827
    if self:Tform(m, g) then
 
1828
        return self:T(x)
 
1829
    end
 
1830
    if self:Iform(m, g) then
 
1831
        return self:T(-x)
 
1832
    end
 
1833
    return self:clone()
 
1834
end
 
1835
 
 
1836
-- Returns the voice-leading between chords a and b,
 
1837
-- i.e. what you have to add to a to get b, as a
 
1838
-- chord of directed intervals.
 
1839
 
 
1840
function ChordSpace.voiceleading(a, b)
 
1841
    local voiceleading = a:clone()
 
1842
    for voice = 1, #voiceleading do
 
1843
        voiceleading[voice] = b[voice] - a[voice]
 
1844
    end
 
1845
    return voiceleading
 
1846
end
 
1847
 
 
1848
-- Returns whether the voiceleading
 
1849
-- between chords a and b contains a parallel fifth.
 
1850
 
 
1851
function ChordSpace.parallelFifth(a, b)
 
1852
    local v = ChordSpace.voiceleading(a, b)
 
1853
    if v:count(7) > 1 then
 
1854
        return true
 
1855
    else
 
1856
        return false
 
1857
    end
 
1858
end
 
1859
 
 
1860
-- Returns the smoothness of the voiceleading between
 
1861
-- chords a and b by L1 norm.
 
1862
 
 
1863
function ChordSpace.voiceleadingSmoothness(a, b)
 
1864
    local L1 = 0
 
1865
    for voice = 1, #a do
 
1866
        L1 = L1 + math.abs(b[voice] - a[voice])
 
1867
    end
 
1868
    return L1
 
1869
end
 
1870
 
 
1871
-- Returns which of the voiceleadings (source to d1, source to d2)
 
1872
-- is the smoother (shortest moves), optionally avoiding parallel fifths.
 
1873
 
 
1874
function ChordSpace.voiceleadingSmoother(source, d1, d2, avoidParallels, range)
 
1875
    range = range or ChordSpace.OCTAVE
 
1876
    if avoidParallels then
 
1877
        if ChordSpace.parallelFifth(source, d1) then
 
1878
            return d2
 
1879
        end
 
1880
        if ChordSpace.parallelFifth(source, d2) then
 
1881
            return d1
 
1882
        end
 
1883
    end
 
1884
    local s1 = ChordSpace.voiceleadingSmoothness(source, d1)
 
1885
    local s2 = ChordSpace.voiceleadingSmoothness(source, d2)
 
1886
    if s1 <= s2 then
 
1887
        return d1
 
1888
    else
 
1889
        return d2
 
1890
    end
 
1891
end
 
1892
 
 
1893
-- Returns which of the voiceleadings (source to d1, source to d2)
 
1894
-- is the simpler (fewest moves), optionally avoiding parallel fifths.
 
1895
 
 
1896
function ChordSpace.voiceleadingSimpler(source, d1, d2, avoidParallels)
 
1897
    avoidParallels = avoidParallels or false
 
1898
    if avoidParallels then
 
1899
        if ChordSpace.parallelFifth(source, d1) then
 
1900
            return d2
 
1901
        end
 
1902
        if ChordSpace.parallelFifth(source, d2) then
 
1903
            return d1
 
1904
        end
 
1905
    end
 
1906
    local v1 = ChordSpace.voiceleading(source, d1):eP()
 
1907
    local v2 = ChordSpace.voiceleading(source, d2):eP()
 
1908
    for voice = #v1, 1, -1 do
 
1909
        if v1[voice] < v2[voice] then
 
1910
            return d1
 
1911
        end
 
1912
        if v2[voice] < v1[voice] then
 
1913
            return d2
 
1914
        end
 
1915
    end
 
1916
    return d1
 
1917
end
 
1918
 
 
1919
-- Returns which of the voiceleadings (source to d1, source to d2)
 
1920
-- is the closer (first smoother, then simpler), optionally avoiding parallel fifths.
 
1921
 
 
1922
function ChordSpace.voiceleadingCloser(source, d1, d2, avoidParallels)
 
1923
    avoidParallels = avoidParallels or false
 
1924
    if avoidParallels then
 
1925
        if ChordSpace.parallelFifth(source, d1) then
 
1926
            return d2
 
1927
        end
 
1928
        if ChordSpace.parallelFifth(source, d2) then
 
1929
            return d1
 
1930
        end
 
1931
    end
 
1932
    local s1 = ChordSpace.voiceleadingSmoothness(source, d1)
 
1933
    local s2 = ChordSpace.voiceleadingSmoothness(source, d2)
 
1934
    if s1 < s2 then
 
1935
        return d1
 
1936
    end
 
1937
    if s1 > s2 then
 
1938
        return d2
 
1939
    end
 
1940
    return ChordSpace.voiceleadingSimpler(source, d1, d2, avoidParallels)
 
1941
end
 
1942
 
 
1943
-- Returns the voicing of the destination which has the closest voice-leading
 
1944
-- from the source within the range, optionally avoiding parallel fifths.
 
1945
 
 
1946
function ChordSpace.voiceleadingClosestRange(source, destination, range, avoidParallels)
 
1947
    local destinationeOP = destination:eOP()
 
1948
    local d = destinationeOP:clone()
 
1949
    local odometer = source:origin()
 
1950
    while ChordSpace.next(odometer, 0, range, ChordSpace.OCTAVE) == true do
 
1951
        local revoicing = odometer:clone()
 
1952
        for voice = 1, #revoicing do
 
1953
            revoicing[voice] = revoicing[voice] + destinationeOP[voice]
 
1954
        end
 
1955
        d = ChordSpace.voiceleadingCloser(source, d, revoicing, avoidParallels)
 
1956
    end
 
1957
    return d
 
1958
end
 
1959
 
 
1960
-- Creates a complete Silencio "note on" event for the
 
1961
-- indicated voice of the chord. The other parameters are used
 
1962
-- if the internal duration, channel, velocity, and pan of the
 
1963
-- chord are nil.
 
1964
 
 
1965
function Chord:note(voice_, time_, duration_, channel_, velocity_, pan_)
 
1966
    local note_ = Event:new()
 
1967
    note_[TIME] = time_
 
1968
    note_[DURATION] = duration_ or self.duration[voice_]
 
1969
    note_[CHANNEL] = channel_ or self.channel[voice_]
 
1970
    note_[KEY] = self[voice_]
 
1971
    note_[VELOCITY] = velocity_ or self.velocity[voice_]
 
1972
    note_[PAN] = pan_ or self.pan[voice_]
 
1973
    return note_
 
1974
end
 
1975
 
 
1976
-- Returns an individual note for each voice of the chord.
 
1977
-- The chord's duration, instrument, and loudness are used if present,
 
1978
-- if not the specified values are used.
 
1979
 
 
1980
function Chord:notes(time_, duration_, channel_, velocity_, pan_)
 
1981
    local notes_ = Score:new()
 
1982
    for voice, key in ipairs(self) do
 
1983
        table.insert(notes_, self:note(voice, time_, duration_, channel_, velocity_, pan_))
 
1984
    end
 
1985
    return notes_
 
1986
end
 
1987
 
 
1988
function Chord:toScore(score, time_, duration_, channel_, velocity_, pan_)
 
1989
    for voice, key in ipairs(self) do
 
1990
        score:append(self:note(voice, time_, duration_, channel_, velocity_, pan_))
 
1991
    end
 
1992
    return score
 
1993
end
 
1994
 
 
1995
-- If the event is a note, moves its pitch
 
1996
-- to the closest pitch of the chord.
 
1997
-- If octaveEquivalence is true (the default),
 
1998
-- the pitch-class of the note is moved to the closest pitch-class
 
1999
-- of the chord; otherwise, the pitch of the note is moved to the closest
 
2000
-- absolute pitch of the chord.
 
2001
 
 
2002
function conformToChord(event, chord, octaveEquivalence)
 
2003
    octaveEquivalence = octaveEquivalence or true
 
2004
    if event[STATUS] ~= 144 then
 
2005
        return
 
2006
    else
 
2007
        local pitch = event[KEY]
 
2008
        if octaveEquivalence then
 
2009
            local pitchClass = pitch % ChordSpace.OCTAVE
 
2010
            local octave = pitch - pitchClass
 
2011
            local chordPitchClass = chord[1] % ChordSpace.OCTAVE
 
2012
            local distance = math.abs(chordPitchClass - pitchClass)
 
2013
            local closestPitchClass = chordPitchClass
 
2014
            local minimumDistance = distance
 
2015
            for voice = 2, #chord do
 
2016
                chordPitchClass = chord[voice] % ChordSpace.OCTAVE
 
2017
                distance = math.abs(chordPitchClass - pitchClass)
 
2018
                if distance < minimumDistance then
 
2019
                    minimumDistance = distance
 
2020
                    closestPitchClass = chordPitchClass
 
2021
                end
 
2022
            end
 
2023
            event[KEY] = octave + closestPitchClass
 
2024
        else
 
2025
            local chordPitch = chord[1]
 
2026
            local distance = math.abs(chordPitch - pitch)
 
2027
            local closestPitch = chordPitch
 
2028
            local minimumDistance = distance
 
2029
            for voice = 2, #chord do
 
2030
                chordPitch = chord[voice]
 
2031
                distance = math.abs(chordPitch - pitch)
 
2032
                if distance < minimumDistance then
 
2033
                    minimumDistance = distance
 
2034
                    closestPitch = chordPitch
 
2035
                end
 
2036
            end
 
2037
            event[KEY] = closestPitch
 
2038
        end
 
2039
    end
 
2040
end
 
2041
 
 
2042
-- Inserts the notes of the chord into the score at the specified time.
 
2043
-- The internal duration, instrument, and loudness are used if present,
 
2044
-- if not the specified values are used.
 
2045
 
 
2046
function ChordSpace.insert(score, chord, time_, duration, channel, velocity, pan)
 
2047
    -- print(score, chord, time_, duration, channel, velocity, pan)
 
2048
    for voice = 1, #chord do
 
2049
        local event = chord:note(voice, time_, duration, channel, velocity, pan)
 
2050
        table.insert(score, event)
 
2051
    end
 
2052
end
 
2053
 
 
2054
-- For all the notes in the score
 
2055
-- beginning at or later than the start time,
 
2056
-- and up to but not including the end time,
 
2057
-- moves the pitch of the note to belong to the chord, using the
 
2058
-- conformToChord function.
 
2059
 
 
2060
function ChordSpace.apply(score, chord, start, end_, octaveEquivalence)
 
2061
    octaveEquivalence = octaveEquivalence or true
 
2062
    local slice = score:slice(start, end_)
 
2063
    for index, event in ipairs(slice) do
 
2064
        conformToChord(event, chord, octaveEquivalence)
 
2065
    end
 
2066
end
 
2067
 
 
2068
-- Returns a chord containing all the pitches of the score
 
2069
-- beginning at or later than the start time,
 
2070
-- and up to but not including the end time.
 
2071
 
 
2072
function gather(score, start, end_)
 
2073
    local chord = Chord:new()
 
2074
    local slice = score:slice(start, end_)
 
2075
    for index, event in ipairs(slice) do
 
2076
        local pitch = event[KEY]
 
2077
        if not chord:contains(pitch) then
 
2078
            table.insert(chord, pitch)
 
2079
        end
 
2080
    end
 
2081
    return chord
 
2082
end
 
2083
 
 
2084
-- Orthogonal additive groups for unordered chords of given arity under range
 
2085
-- equivalence (RP): prime form or P, inversion or I, transposition or T, and
 
2086
-- voicing or V. P x I x T = OP, P x I x T x V = RP. Therefore, an
 
2087
-- operation on P, I, T, or V may be used to independently transform the
 
2088
-- respective symmetry of any chord. Some of these operations will reflect
 
2089
-- in RP.
 
2090
 
 
2091
ChordSpaceGroup = {}
 
2092
 
 
2093
-- N is the number of voices in the chord space, g is the generator of
 
2094
-- transposition, range is the size of chord space,
 
2095
-- optis is an ordered table of all OPTI chords for g,
 
2096
-- voicings is an ordered table of all octavewise permutations in RP.
 
2097
 
 
2098
function ChordSpaceGroup:new(o)
 
2099
    local o = o or {optisForIndexes = {}, indexesForOptis = {}, voicingsForIndexes = {}, indexesForVoicings = {}}
 
2100
    setmetatable(o, self)
 
2101
    self.__index = self
 
2102
    return o
 
2103
end
 
2104
 
 
2105
function ChordSpace.octavewiseRevoicings(chord, range)
 
2106
    range = range or ChordSpace.OCTAVE
 
2107
    local voices = #chord
 
2108
    local odometer = chord:origin()
 
2109
    -- Enumerate the permutations.
 
2110
    -- iterator[1] is the most significant voice, and
 
2111
    -- iterator[N] is the least significant voice.
 
2112
    local voicings = 0
 
2113
    while ChordSpace.next(odometer, 0, range, ChordSpace.OCTAVE) == true do
 
2114
        voicings = voicings + 1
 
2115
    end
 
2116
    return voicings
 
2117
end
 
2118
 
 
2119
function ChordSpace.octavewiseRevoicing(chord, index, range)
 
2120
    local voices = #chord
 
2121
    local odometer = chord:origin()
 
2122
    local eop = chord:eOP()
 
2123
    -- Enumerate the permutations.
 
2124
    -- iterator[1] is the most significant voice, and
 
2125
    -- iterator[N] is the least significant voice.
 
2126
    local voicings = 0
 
2127
    for v = 1, index do
 
2128
        ChordSpace.next(odometer, 0, range, ChordSpace.OCTAVE)
 
2129
        -- Wrap around?
 
2130
        if odometer[1] > range then
 
2131
            odometer = chord:origin()
 
2132
        end
 
2133
        voicings = voicings + 1
 
2134
    end
 
2135
    for voice = 1, #chord do
 
2136
        odometer[voice] = odometer[voice] + eop[voice]
 
2137
    end
 
2138
    return odometer
 
2139
end
 
2140
 
 
2141
-- Returns the ith arpeggiation, current voice, and corresponding revoicing
 
2142
-- of the chord. Positive arpeggiations start with the lowest voice of the
 
2143
-- chord and revoice up; negative arpeggiations start with the highest voice
 
2144
-- of the chord and revoice down.
 
2145
 
 
2146
function Chord:a(arpeggiation)
 
2147
    local chord = self:v(arpeggiation)
 
2148
    if arpeggiation < 0 then
 
2149
        return chord[#chord], #chord, chord
 
2150
    end
 
2151
    return chord[1], 1, chord
 
2152
end
 
2153
 
 
2154
function ChordSpaceGroup:initialize(voices, range, g)
 
2155
    self.voices = voices or 3
 
2156
    self.range = range or 60
 
2157
    self.g = g or 1
 
2158
    self.countP = 0
 
2159
    self.countI = 2
 
2160
    self.countT = ChordSpace.OCTAVE / self.g
 
2161
    local chord = Chord:new()
 
2162
    chord:resize(voices)
 
2163
    self.countV = ChordSpace.octavewiseRevoicings(chord, self.range)
 
2164
    self.indexesForOptis = {}
 
2165
    self.optisForIndexes = ChordSpace.allOfEquivalenceClass(voices, 'OPTTI')
 
2166
    for index, optti in pairs(self.optisForIndexes) do
 
2167
        self.indexesForOptis[optti:__hash()] = index
 
2168
        self.countP = self.countP + 1
 
2169
    end
 
2170
    self:list()
 
2171
end
 
2172
 
 
2173
function ChordSpace.createFilename(voices, range, g, extension)
 
2174
    extension = extension or '.lua'
 
2175
    local gstring = string.format('g%.6f', g)
 
2176
    gstring = string.gsub(gstring, '%.', '_')
 
2177
    local filename = string.format('ChordSpaceGroup_V%d_R%d_%s%s', voices, range, gstring, extension)
 
2178
    return filename
 
2179
end
 
2180
 
 
2181
-- Loads the group if found, creates and saves it otherwise.
 
2182
 
 
2183
function ChordSpace.createChordSpaceGroup(voices, range, g)
 
2184
    local filename = ChordSpace.createFilename(voices, range, 1)
 
2185
    local file, message, error = io.open(filename, 'r')
 
2186
    if file == nil then
 
2187
        print(string.format('File "%s" not found, creating...', filename))
 
2188
        chordSpaceGroup = ChordSpaceGroup:new()
 
2189
        chordSpaceGroup:initialize(voices, range, g)
 
2190
        chordSpaceGroup:save()
 
2191
        return chordSpaceGroup
 
2192
    else
 
2193
        print(string.format('Loading ChordSpaceGroup from file "%s"...', filename))
 
2194
        return ChordSpace.load(voices, range, g)
 
2195
    end
 
2196
end
 
2197
 
 
2198
function ChordSpace.load(voices, range, g)
 
2199
    local filename = ChordSpace.createFilename(voices, range, 1)
 
2200
    print('Loading:', filename)
 
2201
    local deserialized = ChordSpaceGroup.load(filename)
 
2202
    return deserialized
 
2203
end
 
2204
 
 
2205
function ChordSpaceGroup:save(filename)
 
2206
    filename = filename or ChordSpace.createFilename(self.voices, self.range, self.g, '.lua')
 
2207
    local text = serialize(self)
 
2208
    local writer = io.open(filename, 'w+')
 
2209
    writer:write(text)
 
2210
    writer:close()
 
2211
end
 
2212
 
 
2213
function ChordSpaceGroup.load(filename)
 
2214
    local reader = io.open(filename)
 
2215
    local text = reader:read("*all")
 
2216
    -- What's deserialized is a function, which needs to be called.
 
2217
    local object = loadstring(text)()
 
2218
    -- Fix up metatable.
 
2219
    local chordSpaceGroup = ChordSpaceGroup:new(object)
 
2220
    -- Fix up metatables of chords too.
 
2221
    for index, opti in pairs(chordSpaceGroup.optisForIndexes) do
 
2222
        chordSpaceGroup.optisForIndexes[index] = Chord:new(opti)
 
2223
    end
 
2224
    return chordSpaceGroup
 
2225
end
 
2226
 
 
2227
-- Returns the chord for the indices of prime form, inversion,
 
2228
-- transposition, and voicing. The chord is not in RP; rather, each voice of
 
2229
-- the chord's OP may have zero or more octaves added to it.
 
2230
 
 
2231
function ChordSpaceGroup:toChord(P, I, T, V, printme)
 
2232
    printme = printme or false
 
2233
    P = P % self.countP
 
2234
    I = I % 2
 
2235
    T = T % ChordSpace.OCTAVE
 
2236
    V = V % self.countV
 
2237
    if printme then
 
2238
        print('toChord:             ', P, I, T, V)
 
2239
    end
 
2240
    local optti = self.optisForIndexes[P]
 
2241
    if printme then
 
2242
        print('toChord:   optti:    ', optti, optti:__hash())
 
2243
    end
 
2244
    local optt = nil
 
2245
    if I == 0 then
 
2246
        optt = optti
 
2247
    else
 
2248
        optt = optti:I():eOPTT()
 
2249
    end
 
2250
    if printme then
 
2251
        print('toChord:   optt:     ', optt)
 
2252
    end
 
2253
    local optt_t = optt:T(T)
 
2254
    if printme then
 
2255
        print('toChord:   optt_t:   ', optt_t)
 
2256
    end
 
2257
    local op = optt_t:eOP()
 
2258
    if printme then
 
2259
        print('toChord:   op:       ', op)
 
2260
    end
 
2261
    V = V % self.countV
 
2262
    local revoicing = ChordSpace.octavewiseRevoicing(op, V, self.range)
 
2263
    if printme then
 
2264
        print('toChord:   revoicing:', revoicing)
 
2265
    end
 
2266
    return revoicing, opti, op
 
2267
end
 
2268
 
 
2269
-- Returns the indices of prime form, inversion, transposition,
 
2270
-- and voicing for a chord.
 
2271
 
 
2272
function ChordSpaceGroup:fromChord(chord, printme)
 
2273
    printme = printme or false
 
2274
    if printme then
 
2275
        print('fromChord: chord:    ', chord, chord:iseOP())
 
2276
    end
 
2277
    local op = nil
 
2278
    if chord:iseOP() then
 
2279
        op = chord:clone()
 
2280
    else
 
2281
        op = chord:eOP()
 
2282
    end
 
2283
    if printme then
 
2284
        print('fromChord: op:       ', op)
 
2285
    end
 
2286
    local optt = chord:eOPTT()
 
2287
    if printme then
 
2288
        print('fromChord: optt:     ', optt)
 
2289
    end
 
2290
    local T = 0
 
2291
    for t = 0, ChordSpace.OCTAVE - 1, self.g do
 
2292
        local optt_t = optt:T(t):eOP()
 
2293
        if printme then
 
2294
            print('fromChord: optt_t:   ', optt_t, t)
 
2295
        end
 
2296
        if optt_t:__eq_epsilon(op) == true then
 
2297
            if printme then
 
2298
                print('equals')
 
2299
            end
 
2300
            T = t
 
2301
            break
 
2302
        end
 
2303
    end
 
2304
    local optti = chord:eOPTTI()
 
2305
    if printme then
 
2306
        print('fromChord: optti:    ', optti, optti:__hash())
 
2307
    end
 
2308
    local P = self.indexesForOptis[optti:__hash()]
 
2309
    local I = 0
 
2310
    if optti ~= optt then
 
2311
        I = 1
 
2312
        local optt_i_optt = optt:I():eOPTT()
 
2313
        if optt_i_optt ~= optti then
 
2314
            print("Error: OPTT(I(OPTT)) must equal OPTTI.")
 
2315
            print('optt_i_optt:', optt_i_optt:information())
 
2316
            print('optti:      ', optti:information())
 
2317
            os.exit()
 
2318
        end
 
2319
    end
 
2320
    local voicing = ChordSpace.voiceleading(op, chord)
 
2321
    V = self.indexesForVoicings[voicing:__hash()]
 
2322
    if printme then
 
2323
        print('fromChord: voicing:  ', voicing, V)
 
2324
        print('fromChord:           ', P, I, T, V)
 
2325
    end
 
2326
    return P, I, T, V
 
2327
end
 
2328
 
 
2329
function ChordSpaceGroup:list(listheader, listopttis, listvoicings)
 
2330
    listheader = listheader or true
 
2331
    listopttis = listopttis or false
 
2332
    listvoicings = listvoicings or false
 
2333
    if listheader then
 
2334
        print(string.format('ChordSpaceGroup.voices: %8d', self.voices))
 
2335
        print(string.format('ChordSpaceGroup.range : %8d', self.range))
 
2336
        print(string.format('ChordSpaceGroup.g     : %13.4f', self.g))
 
2337
        print(string.format('ChordSpaceGroup.countP: %8d', self.countP))
 
2338
        print(string.format('ChordSpaceGroup.countI: %8d', self.countI))
 
2339
        print(string.format('ChordSpaceGroup.countT: %8d', self.countT))
 
2340
        print(string.format('ChordSpaceGroup.countV: %8d', self.countV))
 
2341
    end
 
2342
    if listopttis then
 
2343
        for index, opti in pairs(self.optisForIndexes) do
 
2344
            print(string.format('index: %5d  opti: %s  index from opti: %s', index, tostring(opti), self.indexesForOptis[opti:__hash()]))
 
2345
        end
 
2346
        for index = 0, #self.optisForIndexes - 1 do
 
2347
            print(string.format('opti from index: %s  index:  %5d', tostring(self.optisForIndexes[index]), index))
 
2348
        end
 
2349
    end
 
2350
    if listvoicings then
 
2351
        for index, voicing in pairs(self.voicingsForIndexes) do
 
2352
            print(string.format('voicing index: %5d  voicing: %s  index from voicing: %5d', index, tostring(voicing), self.indexesForVoicings[voicing:__hash()]))
 
2353
        end
 
2354
    end
 
2355
end
 
2356
 
 
2357
function ChordSpaceGroup:printChords()
 
2358
    for index, opti in pairs(self.optisForIndexes) do
 
2359
        print(string.format('index: %5d  opti: %s %s', index, tostring(opti), opti:name()))
 
2360
    end
 
2361
end
 
2362
 
 
2363
function ChordSpaceGroup:printNamedChords()
 
2364
    for index, opti in pairs(self.optisForIndexes) do
 
2365
        local name = opti:name()
 
2366
        if name ~= '' then
 
2367
            print(string.format('index: %5d  opti: %s %s', index, tostring(opti), opti:name()))
 
2368
        end
 
2369
    end
 
2370
end
 
2371
 
 
2372
return ChordSpace
 
2373
*/