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.
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.
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
22
#include "Platform.hpp"
26
#include <eigen3/Eigen/Dense>
33
%include "std_string.i"
34
%include "std_vector.i"
36
#include <eigen3/Eigen/Dense>
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:
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).
55
-- Causing chord progressions to move strictly within an orbifold that
56
generates some equivalence class.
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
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
67
The associated ChordSpaceView package can display these chord spaces and
68
operations for trichords in an interactive 3-dimensional view.
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.
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.
81
A voice is a distinct sound that is heard as having a pitch.
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
87
For the purposes of algorithmic composition in CsoundAC, a score is considered
88
as a sequence of more or less fleeting chords.
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.
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
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.
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.
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}.
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
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
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.
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
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.
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
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.
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.
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.
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:
192
T(p, x) Translate p by x.
194
I(p [, x]) Reflect p in x, by default the origin.
196
P Send a major triad to the minor triad with the same root,
197
or vice versa (Riemann's parallel transformation).
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).
203
R Send a major triad to the minor triad one minor third lower,
204
or vice versa (Riemann's relative transformation).
206
D Send a triad to the next triad a perfect fifth lower
207
(dominant transformation).
209
P, L, and R have been extended as follows, see Fiore and Satyendra,
210
"Generalized Contextual Groups", _Music Theory Online_ 11, August 2008:
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
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.
227
double factorial(double n) {
231
return n * factorial(n = 1.0);
236
static double epsilon = 1.0;
237
if (epsilon == 1.0) {
239
epsilon = epsilon / 2.0;
240
double nextEpsilon = epsilon / 2.0;
241
double onePlusNextEpsilon = 1.0 + nextEpsilon;
242
if (onePlusNextEpsilon == 1.0) {
250
double &epsilonFactor() {
251
static double epsilonFactor = 1.0;
252
return epsilonFactor;
255
bool eq_epsilon(double a, double b) {
256
if (std::abs(a - b) < (EPSILON() * epsilonFactor())) {
264
double gt_epsilon(double a, double b) {
265
if (eq_epsilon(a, b)) {
272
double lt_epsilon(double a, double b) {
273
if (eq_epsilon(a, b)) {
280
double ge_epsilon(double a, double b) {
281
if (eq_epsilon(a, b)) {
288
double le_epsilon(double a, double b) {
289
if (eq_epsilon(a, b)) {
297
* The size of the octave, defined to be consistent with
298
* 12 tone equal temperament and MIDI.
313
* Returns the pitch transposed by semitones, which may be any scalar.
314
* NOTE: Does NOT return the result under any equivalence class.
316
double T(double pitch, double semitones) {
317
return pitch + semitones;
321
* Returns the pitch reflected in the center, which may be any pitch.
322
* NOTE: Does NOT return the result under any equivalence class.
324
double I(double pitch, double center = 0.0) {
325
return center - pitch;
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.
333
class SILENCE_PUBLIC Chord : public Eigen::MatrixXd {
348
virtual size_t voices() const {
351
virtual void resize(size_t voices) {
352
Eigen::MatrixXd::resize(voices, COUNT);
355
* Returns a string representation of the chord.
356
* Quadratic complexity, but short enough not to matter.
358
virtual std::string toString() const {
360
std::stringstream stream;
361
for (size_t i = 0, n = rows(); i < n; ++i) {
362
std::snprintf(buffer, 0x100, "%12.7f", getPitch(i));
370
virtual double getPitch(int voice) const {
371
return coeff(voice, PITCH);
373
virtual void setPitch(double value, int voice) {
374
coeffRef(voice, PITCH) = value;
376
virtual double getDuration(int voice = 0) const {
377
return coeff(voice, DURATION);
379
virtual void setDuration(double value, int voice = -1) {
381
for (voice = 0; voice < rows(); ++voice) {
382
coeffRef(voice, DURATION) = value;
385
coeffRef(voice, DURATION) = value;
388
virtual double getLoudness(int voice = 0) const {
389
return coeff(voice, LOUDNESS);
391
virtual void setLoudness(double value, int voice = -1) {
393
for (voice = 0; voice < rows(); ++voice) {
394
coeffRef(voice, LOUDNESS) = value;
397
coeffRef(voice, LOUDNESS) = value;
400
virtual double getInstrument(int voice = 0) const {
401
return coeff(voice, INSTRUMENT);
403
virtual void setInstrument(double value, int voice = -1) {
405
for (voice = 0; voice < rows(); ++voice) {
406
coeffRef(voice, INSTRUMENT) = value;
409
coeffRef(voice, INSTRUMENT) = value;
412
virtual double getPan(int voice = 0) const {
413
return coeff(voice, PAN);
415
virtual void setPan(double value, int voice = -1) {
417
for (voice = 0; voice < rows(); ++voice) {
418
coeffRef(voice, PAN) = value;
421
coeffRef(voice, PAN) = value;
424
virtual size_t count(double pitch) const {
426
for (size_t voice = 0; voice < voices(); ++voice) {
427
if (eq_epsilon(getPitch(voice), pitch)) {
433
virtual bool operator == (const Chord &other) {
434
if (rows() != other.voices()) {
437
for (size_t voice = 0; voice < rows(); ++voice) {
438
if (!eq_epsilon(getPitch(voice), other.getPitch(voice))) {
444
virtual bool operator < (const Chord &other) {
445
if (rows() != other.voices()) {
448
for (size_t voice = 0; voice < rows(); ++voice) {
449
if (!lt_epsilon(getPitch(voice), other.getPitch(voice))) {
459
* Returns the Euclidean distance between chords a and b,
460
* which must have the same number of voices.
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);
467
return std::sqrt(sumOfSquaredDifferences);
475
function Chord:__le(other)
476
if self:__eq(other) then
479
return self:__lt(other)
482
-- Returns whether or not the chord contains the pitch.
484
function Chord:contains(pitch)
485
for voice, pitch_ in ipairs(self) do
486
if pitch_ == pitch then
493
-- Returns the lowest pitch in the chord,
494
-- and also its voice index.
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]
505
return lowestPitch, lowestVoice
508
-- Returns the minimum interval in the chord.
510
function Chord:minimumInterval()
511
local minimumInterval = math.abs(self[1] - self[2])
515
local interval = math.abs(self[v1] - self[v2])
516
if interval < minimumInterval then
517
minimumInterval = interval
522
return minimumInterval
525
-- Returns the highest pitch in the chord,
526
-- and also its voice index.
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]
537
return highestPitch, highestVoice
540
-- Returns the maximum interval in the chord.
542
function Chord:maximumInterval()
543
return self:max() - self:min()
546
-- Returns a new chord whose pitches are the floors of this chord's pitches.
548
function Chord:floor()
549
local chord = self:clone()
550
for voice = 1, #self do
551
chord[voice] = math.floor(self[voice])
556
-- Returns a new chord whose pitches are the ceilings of this chord's pitches.
558
function Chord:ceil()
559
local chord = self:clone()
560
for voice = 1, #self do
561
chord[voice] = math.ceil(self[voice])
566
-- Returns a value copy of the chord.
568
function Chord:clone()
569
local clone_ = Chord:new()
570
for voice, pitch in ipairs(self) do
571
clone_[voice] = pitch
573
for voice, value in ipairs(self.duration) do
574
clone_.duration[voice] = value
576
for voice, value in ipairs(self.channel) do
577
clone_.channel[voice] = value
579
for voice, value in ipairs(self.velocity) do
580
clone_.velocity[voice] = value
582
for voice, value in ipairs(self.pan) do
583
clone_.pan[voice] = value
588
-- Returns the origin of the chord's space.
590
function Chord:origin()
591
local clone_ = self:clone()
592
for voice = 1, #clone_ do
598
function Chord:distanceToOrigin()
599
local origin = self:origin()
600
return ChordSpace.euclidean(self, origin)
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
609
return ChordSpace.euclidean(self, unison)
612
-- Returns the maximally even chord in the chord's space,
613
-- e.g. the augmented triad for 3 dimensions.
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
624
-- Returns the sum of the pitches in the chord.
626
function Chord:layer()
628
for voice, pitch in ipairs(self) do
634
-- Transposes the chord by the indicated interval (may be a fraction).
635
-- NOTE: Does NOT return the result under any equivalence class.
637
function Chord:T(interval)
638
local clone_ = self:clone()
639
for voice = 1, #clone_ do
640
clone_[voice] = T(clone_[voice], interval)
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
649
function Chord:I(center)
651
local inverse = self:clone()
652
for voice = 1, #inverse do
653
inverse[voice] = I(self[voice], center)
658
-- Returns the equivalent of the pitch under pitch-class equivalence, i.e.
659
-- the pitch is in the interval [0, OCTAVE).
661
function ChordSpace.epc(pitch)
663
while ChordSpace.lt_epsilon(pitch, 0) do
664
pitch = pitch + ChordSpace.OCTAVE
666
--while pitch >= ChordSpace.OCTAVE do
667
while ChordSpace.ge_epsilon(pitch, ChordSpace.OCTAVE) do
668
pitch = pitch - ChordSpace.OCTAVE
673
-- Returns whether the chord is within the fundamental domain of
674
-- pitch-class equivalence, i.e. is a pitch-class set.
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
686
-- Returns the equivalent of the chord under pitch-class equivalence,
687
-- i.e. the pitch-class set of the chord.
689
function Chord:epcs()
690
local chord = self:clone()
691
for voice = 1, #chord do
692
chord[voice] = ChordSpace.epc(chord[voice])
697
-- Returns whether the chord is within the fundamental domain of
698
-- transposition to 0.
700
function Chord:iset()
702
if not (et == self) then
708
-- Returns the equivalent of the chord within the fundamental domain of
709
-- transposition to 0.
712
local min_ = self:min()
716
-- Returns whether the chord is within the representative fundamental domain
717
-- of the indicated range equivalence.
719
function Chord:iseR(range)
721
local max_ = self:max()
722
local min_ = self:min()
723
if not (max_ <= (min_ + range)) then
726
local layer_ = self:layer()
727
if not ((0 <= layer_) and (layer_ <= range)) then
733
local max_ = self:max()
734
local min_ = self:min()
735
if not ChordSpace.le_epsilon(max_, (min_ + range)) then
738
local layer_ = self:layer()
739
if not (ChordSpace.le_epsilon(0, layer_) and ChordSpace.le_epsilon(layer_, range)) then
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
750
local layer_ = self:layer()
751
if not (ChordSpace.le_epsilon(0, layer_) and ChordSpace.le_epsilon(layer_, range)) then
758
-- Returns whether the chord is within the representative fundamental domain
759
-- of octave equivalence.
761
function Chord:iseO()
762
return self:iseR(ChordSpace.OCTAVE)
765
-- Returns the equivalent of the chord within the representative fundamental
766
-- domain of a range equivalence.
768
function Chord:eR(range)
769
local chord = self:clone()
770
--if chord:iseR(range) then
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.
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
791
-- Returns the equivalent of the chord within the representative fundamental
792
-- domain of octave equivalence.
795
return self:eR(ChordSpace.OCTAVE)
798
-- Returns whether the chord is within the representative fundamental domain
799
-- of permutational equivalence.
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
811
-- Returns the equivalent of the chord within the representative fundamental
812
-- domain of permutational equivalence.
815
clone_ = self:clone()
820
-- Returns whether the chord is within the representative fundamental domain
821
-- of transpositional equivalence.
823
function Chord:iseT()
825
local layer_ = self:layer()
826
if not ChordSpace.eq_epsilon(layer_, 0) then
834
if not (ep == ep:eT(g):eP()) then
841
-- Returns the equivalent of the chord within the representative fundamental
842
-- domain of transpositonal equivalence.
846
local layer_ = self:layer()
847
local sumPerVoice = layer_ / #self
848
return self:T(-sumPerVoice)
852
local iterator = self
853
-- Transpose down to layer 0 or just below.
854
while iterator:layer() > 0 do
855
iterator = iterator:T(-g)
857
-- Transpose up to layer 0 or just above.
858
while iterator:layer() < 0 do
859
iterator = iterator:T(g)
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.
871
function Chord:eTT(g)
874
local transposition = math.ceil(et[1]) - et[1]
875
local ett = et:T(transposition)
879
-- Returns whether the chord is within the representative fundamental domain
880
-- of translational equivalence and the equal temperament generated by g.
882
function Chord:iseTT(g)
885
if not (ep == ep:eTT(g)) then
891
-- Returns whether the chord is within the representative fundamental domain
892
-- of inversional equivalence.
894
function Chord:iseI(inverse)
896
local chord = self:clone()
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
906
if lowerInterval > upperInterval then
909
lowerVoice = lowerVoice + 1
910
upperVoice = upperVoice - 1
915
inverse = inverse or self:I()
916
if not self:__le(inverse) then
923
if not self:__le(inverse) then
930
-- Returns the equivalent of the chord within the representative fundamental
931
-- domain of inversional equivalence.
940
-- Returns whether the chord is within the representative fundamental domain
941
-- of range and permutational equivalence.
943
function Chord:iseRP(range)
945
for voice = 2, #self do
946
if not (self[voice - 1] <= self[voice]) then
950
if not (self[#self] <= (self[1] + range)) then
953
local layer_ = self:layer()
954
if not ((0 <= layer_) and (layer_ <= range)) then
960
if not self:iseR(range) then
963
if not self:iseP() then
970
-- Returns whether the chord is within the representative fundamental domain
971
-- of octave and permutational equivalence.
973
function Chord:iseOP()
974
return self:iseRP(ChordSpace.OCTAVE)
977
-- Returns the equivalent of the chord within the representative fundamental
978
-- domain of range and permutational equivalence.
980
function Chord:eRP(range)
981
return self:eR(range):eP()
984
-- Returns the equivalent of the chord within the representative fundamental
985
-- domain of octave and permutational equivalence.
988
return self:eRP(ChordSpace.OCTAVE)
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.
995
function Chord:cycle(stride)
997
local clone_ = self:clone()
1000
local tail = table.remove(clone_)
1001
table.insert(clone_, 1, tail)
1006
for i = 1, math.abs(stride) do
1007
local head = table.remove(clone_, 1)
1008
table.insert(clone_, head)
1014
-- Returns the permutations of the pitches in a chord. The permutations from
1015
-- each particular permutation are always returned in the same order.
1017
function Chord:permutations()
1018
local chord = self:clone()
1019
local permutations_ = {}
1020
permutations_[1] = chord
1022
chord = chord:cycle()
1023
permutations_[i] = chord
1025
table.sort(permutations_)
1026
return permutations_
1029
-- Returns whether the chord is within the representative fundamental domain
1030
-- of voicing equivalence.
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
1041
-- Returns the equivalent of the chord within the representative fundamental
1042
-- domain of voicing equivalence.
1046
for index, voicing in ipairs(self:permutations()) do
1047
local wraparound = voicing[1] + ChordSpace.OCTAVE - voicing[#voicing]
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
1062
local voicings = self:permutations()
1063
local distancesForIndexes = {}
1064
for i = 1, #voicings do
1065
distancesForIndexes[i] = voicings[i]:distanceToUnisonDiagonal()
1067
local minimumDistanceToUnisonDiagonal = distancesForIndexes[1]
1068
for i = 2, #voicings do
1069
if distancesForIndexes[i] < minimumDistanceToUnisonDiagonal then
1070
minimumDistanceToUnisonDiagonal = distancesForIndexes[i]
1073
for i = 1, #voicings do
1074
if distancesForIndexes[i] == minimumDistanceToUnisonDiagonal then
1081
-- Returns whether the chord is within the representative fundamental domain
1082
-- of range, permutational, and transpositional equivalence.
1084
function Chord:iseRPT(range)
1086
-- GVLS: if not (self[#self] <= self[1] + ChordSpace.OCTAVE) then
1087
if not (ChordSpace.le_epsilon(self[#self], (self[1] + range))) then
1090
local layer_ = self:layer()
1091
-- GVLS: if not (layer_ == 0) then
1092
if not ChordSpace.eq_epsilon(layer_, 0) then
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
1108
if not self:iseR(range) then
1111
if not self:iseP() then
1114
if not self:iseT() then
1117
if not self:iseV() then
1124
function Chord:iseRPTT(range)
1125
if not self:iseP() then
1128
if not self:iseR(range) then
1131
if not self:iseTT() then
1134
if not self:iseV() then
1140
-- Returns whether the chord is within the representative fundamental domain
1141
-- of octave, permutational, and transpositional equivalence.
1143
function Chord:iseOPT()
1144
return self:iseRPT(ChordSpace.OCTAVE)
1147
function Chord:iseOPTT()
1148
return self:iseRPTT(ChordSpace.OCTAVE)
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.
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
1166
while direction < 0 do
1167
chord = chord:cycle(-1)
1168
chord[1] = chord[1] - ChordSpace.OCTAVE
1169
direction = direction + 1
1174
-- Returns all the 'inversions' (in the musician's sense)
1175
-- or octavewise revoicings of the chord.
1177
function Chord:voicings()
1178
local chord = self:clone()
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.
1192
function Chord:eRPT(range)
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
1202
print('ERROR: Chord:eRPT() should not come here: ' .. tostring(self))
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
1213
print('ERROR: Chord:eRPT() should not come here: ' .. tostring(self))
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
1226
print('ERROR: Chord:eRPTT() should not come here: ' .. tostring(self))
1229
-- Returns the equivalent of the chord within the representative fundamental
1230
-- domain of octave, permutational, and transpositional equivalence.
1232
function Chord:eOPT()
1233
return self:eRPT(ChordSpace.OCTAVE)
1236
function Chord:eOPTT()
1237
return self:eRPTT(ChordSpace.OCTAVE)
1240
-- Returns whether the chord is within the representative fundamental domain
1241
-- of range, permutational, and inversional equivalence.
1243
function Chord:iseRPI(range)
1244
if not self:iseRP(range) then
1247
local inverse = self:I():eRP(range)
1248
assert(inverse, 'Inverse is nil.')
1249
if not self:iseI(inverse) then
1255
-- Returns whether the chord is within the representative fundamental domain
1256
-- of octave, permutational, and inversional equivalence.
1258
function Chord:iseOPI()
1259
return self:iseRPI(ChordSpace.OCTAVE)
1262
-- Returns the equivalent of the chord within the representative fundamental
1263
-- domain of range, permutational, and inversional equivalence.
1265
function Chord:eRPI(range)
1266
local erp = self:eRP(range)
1267
if erp:iseRPI(range) then
1270
return erp:I():eRP(range)
1273
-- Returns the equivalent of the chord within the representative fundamental
1274
-- domain of octave, permutational, and inversional equivalence.
1276
function Chord:eOPI()
1277
return self:eRPI(ChordSpace.OCTAVE)
1280
-- Returns whether the chord is within the representative fundamental domain
1281
-- of range, permutational, transpositional, and inversional equivalence.
1283
function Chord:iseRPTI(range)
1285
-- GVLS: if not (self[#self] <= self[1] + ChordSpace.OCTAVE) then
1286
if not ChordSpace.le_epsilon(self[#self], (self[1] + range)) then
1289
local layer_ = self:layer()
1290
-- GVLS: if not (layer_ == 0) then
1291
if not ChordSpace.eq_epsilon(layer_, 0) then
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
1304
if not self:iseI() then
1310
if not self:iseRPT(range) then
1313
if not self:iseI() then
1320
function Chord:iseRPTTI(range)
1321
if not self:iseRPTT(range) then
1324
if not self:iseI() then
1330
-- Returns whether the chord is within the representative fundamental domain
1331
-- of octave, permutational, transpositional, and inversional equivalence.
1333
function Chord:iseOPTI()
1334
return self:iseRPTI(ChordSpace.OCTAVE)
1337
function Chord:iseOPTTI()
1338
return self:iseRPTTI(ChordSpace.OCTAVE)
1341
-- Returns the equivalent of the chord within the representative fundamental
1342
-- domain of range, permutational, transpositional, and inversional
1345
function Chord:eRPTI(range)
1346
local rpt = self:eRPT(range)
1350
return rpt:I():eRPT(range)
1353
function Chord:eRPTTI(range)
1354
local rpt = self:eRPTT(range)
1355
if rpt:iseRPTTI(range) then
1358
return rpt:I():eRPTT(range)
1361
-- Returns the equivalent of the chord within the representative fundamental
1362
-- domain of range, permutational, transpositional, and inversional
1365
function Chord:eOPTI()
1366
return self:eRPTI(ChordSpace.OCTAVE)
1369
function Chord:eOPTTI()
1370
return self:eRPTTI(ChordSpace.OCTAVE)
1373
function Chord:name()
1374
local chordName = ChordSpace.namesForChords[self:__hash()]
1375
if chordName == nil then
1381
-- Returns a formatted string with information about the chord.
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
1406
eOPTI: %s iseOPTI: %s
1410
tostring(self), chordName,
1412
tostring(self:eO()), tostring(self:iseO()),
1413
tostring(self:eP()), tostring(self:iseP()),
1414
tostring(self:eT()), tostring(self:iseT()),
1416
tostring(self:eI()), tostring(self:iseI()),
1417
tostring(self:eV()), tostring(self:iseV()),
1419
tostring(self:eOP()), tostring(self:iseOP()),
1421
tostring(self:eOPT()), tostring(self:iseOPT()),
1422
tostring(self:eOPTT()),
1424
tostring(self:eOPI()), tostring(self:iseOPI()),
1425
tostring(self:eOPTI()), tostring(self:iseOPTI()),
1426
tostring(self:eOPTTI()),
1431
function ChordSpace.set(collection)
1433
for key, value in pairs(collection) do
1434
set_[value:__hash()] = value
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)
1445
table.sort(sortedSet_)
1449
function ChordSpace.zeroBasedSet(sortedSet)
1450
local zeroBasedSet = {}
1451
for index, value in pairs(sortedSet) do
1452
zeroBasedSet[index - 1] = value
1457
function ChordSpace.setContains(setA, x)
1458
if setA[x:__hash()] == x then
1464
function ChordSpace.setInsert(setA, x)
1465
if not ChordSpace.setContains(setA, x) then
1466
setA[x:__hash()] = x
1470
function ChordSpace.sortedEquals(sortedA, sortedB)
1471
if not (#sortedA == #sortedB) then
1474
for i = 1, #sortedA do
1475
if not (sortedA[i] == sortedB[i]) then
1482
function ChordSpace.setIntersection(A, setB)
1484
for index, value in pairs(A) do
1485
if ChordSpace.setContains(setB, value) then
1486
ChordSpace.setInsert(result, value)
1492
function ChordSpace.union(A, B)
1494
for index, value in pairs(A) do
1495
ChordSpace.setInsert(result, value)
1497
for index, value in pairs(B) do
1498
ChordSpace.setInsert(result, value)
1503
ChordSpace.pitchClassesForNames = {}
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
1523
ChordSpace.chordsForNames = {}
1524
ChordSpace.namesForChords = {}
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
1536
ChordSpace.chordsForNames[chordName] = chord
1537
ChordSpace.namesForChords[chord:__hash()] = chordName
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")
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")
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")
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")
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 ")
1616
table.sort(ChordSpace.chordsForNames)
1617
table.sort(ChordSpace.namesForChords)
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.
1624
function ChordSpace.next(odometer, low, high, g)
1625
local voices = #odometer
1626
odometer[voices] = odometer[voices] + g
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
1634
if odometer[1] > high then
1640
function ChordSpace.allOfEquivalenceClass(voices, equivalence, g)
1642
local equivalenceMapper = nil
1643
if equivalence == 'OP' then
1644
equivalenceMapper = Chord.iseOP
1646
if equivalence == 'OPT' then
1647
equivalenceMapper = Chord.iseOPT
1649
if equivalence == 'OPTT' then
1650
equivalenceMapper = Chord.iseOPT
1652
if equivalence == 'OPI' then
1653
equivalenceMapper = Chord.iseOPI
1655
if equivalence == 'OPTI' then
1656
equivalenceMapper = Chord.iseOPTI
1658
if equivalence == 'OPTTI' then
1659
equivalenceMapper = Chord.iseOPTTI
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)
1673
local equivalentChords = ChordSpace.sortedSet(equivalentChords)
1674
local zeroBasedChords = ChordSpace.zeroBasedSet(equivalentChords)
1675
return zeroBasedChords, equivalentChords
1678
-- Returns a chord with the specified number of voices all set to a first
1679
-- pitch, useful as an iterator.
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
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
1694
function ChordSpace.allChordsInRange(voices, first, last, g)
1696
last = last or ChordSpace.OCTAVE
1698
-- Enumerate all chords in the range.
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
1705
return ChordSpace.sortedSet(chordset)
1708
-- Move 1 voice of the chord,
1709
-- optionally under range equivalence
1710
-- NOTE: Does NOT return the result under any equivalence class.
1712
function Chord:move(voice, interval)
1713
local chord = self:clone()
1714
chord[voice] = T(chord[voice], interval)
1718
-- Performs the neo-Riemannian Lettonwechsel transformation.
1719
-- NOTE: Does NOT return the result under any equivalence class.
1721
function Chord:nrL()
1722
local cv = self:eV()
1723
local cvt = self:eV():et()
1734
-- Performs the neo-Riemannian parallel transformation.
1735
-- NOTE: Does NOT return the result under any equivalence class.
1737
function Chord:nrP()
1738
local cv = self:eV()
1739
local cvt = self:eV():et()
1750
-- Performs the neo-Riemannian relative transformation.
1751
-- NOTE: Does NOT return the result under any equivalence class.
1753
function Chord:nrR()
1754
local cv = self:eV()
1755
local cvt = self:eV():et()
1766
-- Performs the neo-Riemannian dominant transformation.
1767
-- NOTE: Does NOT return the result under any equivalence class.
1769
function Chord:nrD()
1770
return self:eep():T(-7)
1773
-- Returns the chord inverted by the sum of its first two voices.
1774
-- NOTE: Does NOT return the result under any equivalence class.
1776
function Chord:K(range)
1777
range = range or ChordSpace.OCTAVE
1778
local chord = self:clone()
1782
local ep = chord:eP()
1783
local x = ep[1] + ep[2]
1787
-- Returns whether the chord is a transpositional form of Y with interval size g.
1788
-- Only works in equal temperament.
1790
function Chord:Tform(Y, g)
1791
local eopx = self:epcs()
1793
while i < ChordSpace.OCTAVE do
1795
local eopty = ty:epcs()
1796
if eopx == eopty then
1804
-- Returns whether the chord is an inversional form of Y with interval size g.
1805
-- Only works in equal temperament.
1807
function Chord:Iform(Y, g)
1808
local eopx = self:epcs()
1810
while i < ChordSpace.OCTAVE do
1812
local eopiy = iy:epcs()
1813
if eopx == eopiy then
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.
1825
function Chord:Q(x, m, g)
1827
if self:Tform(m, g) then
1830
if self:Iform(m, g) then
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.
1840
function ChordSpace.voiceleading(a, b)
1841
local voiceleading = a:clone()
1842
for voice = 1, #voiceleading do
1843
voiceleading[voice] = b[voice] - a[voice]
1848
-- Returns whether the voiceleading
1849
-- between chords a and b contains a parallel fifth.
1851
function ChordSpace.parallelFifth(a, b)
1852
local v = ChordSpace.voiceleading(a, b)
1853
if v:count(7) > 1 then
1860
-- Returns the smoothness of the voiceleading between
1861
-- chords a and b by L1 norm.
1863
function ChordSpace.voiceleadingSmoothness(a, b)
1865
for voice = 1, #a do
1866
L1 = L1 + math.abs(b[voice] - a[voice])
1871
-- Returns which of the voiceleadings (source to d1, source to d2)
1872
-- is the smoother (shortest moves), optionally avoiding parallel fifths.
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
1880
if ChordSpace.parallelFifth(source, d2) then
1884
local s1 = ChordSpace.voiceleadingSmoothness(source, d1)
1885
local s2 = ChordSpace.voiceleadingSmoothness(source, d2)
1893
-- Returns which of the voiceleadings (source to d1, source to d2)
1894
-- is the simpler (fewest moves), optionally avoiding parallel fifths.
1896
function ChordSpace.voiceleadingSimpler(source, d1, d2, avoidParallels)
1897
avoidParallels = avoidParallels or false
1898
if avoidParallels then
1899
if ChordSpace.parallelFifth(source, d1) then
1902
if ChordSpace.parallelFifth(source, d2) then
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
1912
if v2[voice] < v1[voice] then
1919
-- Returns which of the voiceleadings (source to d1, source to d2)
1920
-- is the closer (first smoother, then simpler), optionally avoiding parallel fifths.
1922
function ChordSpace.voiceleadingCloser(source, d1, d2, avoidParallels)
1923
avoidParallels = avoidParallels or false
1924
if avoidParallels then
1925
if ChordSpace.parallelFifth(source, d1) then
1928
if ChordSpace.parallelFifth(source, d2) then
1932
local s1 = ChordSpace.voiceleadingSmoothness(source, d1)
1933
local s2 = ChordSpace.voiceleadingSmoothness(source, d2)
1940
return ChordSpace.voiceleadingSimpler(source, d1, d2, avoidParallels)
1943
-- Returns the voicing of the destination which has the closest voice-leading
1944
-- from the source within the range, optionally avoiding parallel fifths.
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]
1955
d = ChordSpace.voiceleadingCloser(source, d, revoicing, avoidParallels)
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
1965
function Chord:note(voice_, time_, duration_, channel_, velocity_, pan_)
1966
local note_ = Event:new()
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_]
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.
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_))
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_))
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.
2002
function conformToChord(event, chord, octaveEquivalence)
2003
octaveEquivalence = octaveEquivalence or true
2004
if event[STATUS] ~= 144 then
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
2023
event[KEY] = octave + closestPitchClass
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
2037
event[KEY] = closestPitch
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.
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)
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.
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)
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.
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)
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
2091
ChordSpaceGroup = {}
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.
2098
function ChordSpaceGroup:new(o)
2099
local o = o or {optisForIndexes = {}, indexesForOptis = {}, voicingsForIndexes = {}, indexesForVoicings = {}}
2100
setmetatable(o, self)
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.
2113
while ChordSpace.next(odometer, 0, range, ChordSpace.OCTAVE) == true do
2114
voicings = voicings + 1
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.
2128
ChordSpace.next(odometer, 0, range, ChordSpace.OCTAVE)
2130
if odometer[1] > range then
2131
odometer = chord:origin()
2133
voicings = voicings + 1
2135
for voice = 1, #chord do
2136
odometer[voice] = odometer[voice] + eop[voice]
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.
2146
function Chord:a(arpeggiation)
2147
local chord = self:v(arpeggiation)
2148
if arpeggiation < 0 then
2149
return chord[#chord], #chord, chord
2151
return chord[1], 1, chord
2154
function ChordSpaceGroup:initialize(voices, range, g)
2155
self.voices = voices or 3
2156
self.range = range or 60
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
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)
2181
-- Loads the group if found, creates and saves it otherwise.
2183
function ChordSpace.createChordSpaceGroup(voices, range, g)
2184
local filename = ChordSpace.createFilename(voices, range, 1)
2185
local file, message, error = io.open(filename, 'r')
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
2193
print(string.format('Loading ChordSpaceGroup from file "%s"...', filename))
2194
return ChordSpace.load(voices, range, g)
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)
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+')
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)
2224
return chordSpaceGroup
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.
2231
function ChordSpaceGroup:toChord(P, I, T, V, printme)
2232
printme = printme or false
2235
T = T % ChordSpace.OCTAVE
2238
print('toChord: ', P, I, T, V)
2240
local optti = self.optisForIndexes[P]
2242
print('toChord: optti: ', optti, optti:__hash())
2248
optt = optti:I():eOPTT()
2251
print('toChord: optt: ', optt)
2253
local optt_t = optt:T(T)
2255
print('toChord: optt_t: ', optt_t)
2257
local op = optt_t:eOP()
2259
print('toChord: op: ', op)
2262
local revoicing = ChordSpace.octavewiseRevoicing(op, V, self.range)
2264
print('toChord: revoicing:', revoicing)
2266
return revoicing, opti, op
2269
-- Returns the indices of prime form, inversion, transposition,
2270
-- and voicing for a chord.
2272
function ChordSpaceGroup:fromChord(chord, printme)
2273
printme = printme or false
2275
print('fromChord: chord: ', chord, chord:iseOP())
2278
if chord:iseOP() then
2284
print('fromChord: op: ', op)
2286
local optt = chord:eOPTT()
2288
print('fromChord: optt: ', optt)
2291
for t = 0, ChordSpace.OCTAVE - 1, self.g do
2292
local optt_t = optt:T(t):eOP()
2294
print('fromChord: optt_t: ', optt_t, t)
2296
if optt_t:__eq_epsilon(op) == true then
2304
local optti = chord:eOPTTI()
2306
print('fromChord: optti: ', optti, optti:__hash())
2308
local P = self.indexesForOptis[optti:__hash()]
2310
if optti ~= optt then
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())
2320
local voicing = ChordSpace.voiceleading(op, chord)
2321
V = self.indexesForVoicings[voicing:__hash()]
2323
print('fromChord: voicing: ', voicing, V)
2324
print('fromChord: ', P, I, T, V)
2329
function ChordSpaceGroup:list(listheader, listopttis, listvoicings)
2330
listheader = listheader or true
2331
listopttis = listopttis or false
2332
listvoicings = listvoicings or false
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))
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()]))
2346
for index = 0, #self.optisForIndexes - 1 do
2347
print(string.format('opti from index: %s index: %5d', tostring(self.optisForIndexes[index]), index))
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()]))
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()))
2363
function ChordSpaceGroup:printNamedChords()
2364
for index, opti in pairs(self.optisForIndexes) do
2365
local name = opti:name()
2367
print(string.format('index: %5d opti: %s %s', index, tostring(opti), opti:name()))