PageRenderTime 66ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/ChordSpace.lua

http://silencio.googlecode.com/
Lua | 1953 lines | 1062 code | 211 blank | 680 comment | 155 complexity | 20bb87c20154a78a5845a633fe9e6701 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. local Silencio = require("Silencio")
  2. local metalua_serialize = require("metalua_serialize")
  3. local io = require("io")
  4. local string = require("string")
  5. local ChordSpace = {}
  6. function ChordSpace.help()
  7. print [[
  8. C H O R D S P A C E
  9. Copyright 2010, 2011 by Michael Gogins.
  10. This software is licensed under the terms of the GNU Lesser General Public
  11. License.
  12. This package, part of Silencio, implements a geometric approach to some common
  13. operations on chords in neo-Riemannian music theory for use in score
  14. generating procedures:
  15. -- Identifying whether a chord belongs to some equivalence class of music
  16. theory, or sending a chord to its equivalent within a representative
  17. fundamental domain of some equivalence class. The equivalence classes are
  18. octave (O), permutational (P), transpositional, (T), inversional (I), and
  19. their compounds OP, OPT (set-class or chord type), and OPTI (prime form).
  20. -- Causing chord progressions to move strictly within an orbifold that
  21. generates some equivalence class.
  22. -- Implementing chord progressions based on the L, P, R, D, K, and Q
  23. operations of neo-Riemannian theory (thus implementing some aspects of
  24. "harmony").
  25. -- Implementing chord progressions performed within a more abstract
  26. equivalence class by means of the best-formed voice-leading within a less
  27. abstract equivalence class (thus implementing some fundamentals of
  28. "counterpoint").
  29. The associated ChordSpaceView package can display these chord spaces and
  30. operations for trichords in an interactive 3-dimensional view.
  31. DEFINITIONS
  32. Pitch is the perception of a distinct sound frequency. It is a logarithmic
  33. perception; octaves, which sound 'equivalent' in some sense, represent
  34. doublings or halvings of frequency.
  35. Pitches and intervals are represented as real numbers. Middle C is 60 and the
  36. octave is 12. Our usual system of 12-tone equal temperament, as well as MIDI
  37. key numbers, are completely represented by the whole numbers; any and all
  38. other pitches can be represented simply by using fractions.
  39. A voice is a distinct sound that is heard as having a pitch.
  40. A chord is simply a set of voices heard at the same time, represented here
  41. as a point in a chord space having one dimension of pitch for each voice
  42. in the chord.
  43. For the purposes of algorithmic composition in Silencio, a score is considered
  44. as a sequence of more or less fleeting chords.
  45. EQUIVALENCE CLASSES
  46. An equivalence class identifies elements of a set. Operations that send one
  47. equivalent point to another induce quotient spaces or orbifolds, where the
  48. equivalence operation identifies points on one face of the orbifold with
  49. points on an opposing face. The fundamental domain of the equivalence class
  50. is the space "within" the orbifold.
  51. Plain chord space has no equivalence classes. Ordered chords are represented
  52. as vectors in parentheses (p1, ..., pN). Unordered chords are represented as
  53. sorted vectors in braces {p1, ..., pN}. Unordering is itself an equivalence
  54. class.
  55. The following equivalence classes apply to pitches and chords, and exist in
  56. different orbifolds. Equivalence classes can be combined (Callendar, Quinn,
  57. and Tymoczko, "Generalized Voice-Leading Spaces," _Science_ 320, 2008), and
  58. the more equivalence classes are combined, the more abstract is the resulting
  59. orbifold compared to the parent space.
  60. In most cases, a chord space can be divided into a number, possibly
  61. infinite, of geometrically equivalent fundamental domains for the same
  62. equivalence class. Therefore, here we use the notion of 'representative'
  63. fundamental domain. For example, the representative fundamental domain of
  64. unordered sequences, out of all possible orderings, consists of all sequences
  65. in their ordinary sorted order. It is important, in the following, to identify
  66. representative fundamental domains that combine properly, e.g. such that the
  67. representative fundamental domain of OP / the representative fundamental
  68. domain of PI equals the representative fundamental domain of OPI. And this in
  69. turn may require accounting for duplicate elements of the representative
  70. fundamental domain caused by reflections or singularities in the orbifold.
  71. C Cardinality equivalence, e.g. {1, 1, 2} == {1, 2}. _Not_ assuming
  72. cardinality equivalence ensures that there is a proto-metric in plain
  73. chord space that is inherited by all child chord spaces. Cardinality
  74. equivalence is never assumed here, because we are working in chord
  75. spaces of fixed dimensionality; e.g. we represent the note middle C
  76. not as {60}, but as {60, 60, ..., 60}.
  77. O Octave equivalence. The fundamental domain is defined by the pitches
  78. in a chord spanning the range of an octave or less, and summing to
  79. an octave or less.
  80. P Permutational equivalence. The fundamental domain is defined by a
  81. "wedge" of plain chord space in which the voices of a chord are always
  82. sorted by pitch.
  83. T Transpositional equivalence, e.g. {1, 2} == {7, 8}. The fundamental
  84. domain is defined as a plane in chord space at right angles to the
  85. diagonal of unison chords. Represented by the chord always having a
  86. sum of pitches equal to 0.
  87. I Inversional equivalence. Care is needed to distinguish the
  88. mathematician's sense of 'invert', which means 'pitch-space inversion'
  89. or 'reflect in a point', from the musician's sense of 'invert', which
  90. varies according to context but in practice often means 'registral
  91. inversion' or 'revoice by adding an octave to the lowest tone of a
  92. chord.' Here, we use 'invert' and 'inversion' in the mathematician's
  93. sense, and we use the terms 'revoice' and 'voicing' for the musician's
  94. 'invert' and 'inversion'. The inversion point for any inversion lies
  95. on the unison diagonal. A fundamental domain is defined as any half of
  96. chord space that is bounded by a plane containing the inversion point.
  97. Represented as the chord having the first interval between voices be
  98. smaller than or equal to the final interval (recursing for chords of
  99. more than 3 voices).
  100. PI Inversional equivalence with permutational equivalence. The
  101. 'inversion flat' of unordered chord space is a hyperplane consisting
  102. of all those unordered chords that are invariant under inversion. A
  103. fundamental domain is defined by any half space bounded by a
  104. hyperplane containing the inversion flat. It is represented as that
  105. half of the space on or lower than the hyperplane defined by the
  106. inversion flat and the unison diagonal.
  107. OP Octave equivalence with permutational equivalence. Tymoczko's orbifold
  108. for chords; i.e. chords with a fixed number of voices in a harmonic
  109. context. The fundamental domain is defined as a hyperprism one octave
  110. long with as many sides as voices and the ends identified by octave
  111. equivalence and one cyclical permutation of voices, modulo the
  112. unordering. In OP for trichords in 12TET, the augmented triads run up
  113. the middle of the prism, the major and minor triads are in 6
  114. alternating columns around the augmented triads, the two-pitch chords
  115. form the 3 sides, and the one-pitch chords form the 3 edges that join
  116. the sides.
  117. OPT The layer of the OP prism as close as possible to the origin, modulo
  118. the number of voices. Chord type. Note that CM and Cm are different
  119. OPT. Because the OP prism is canted down from the origin, at least one
  120. pitch in each OPT chord (excepting the origin itself) is negative.
  121. OPI The OP prism modulo inversion, i.e. 1/2 of the OP prism. The
  122. representative fundamental consits of those chords less than or equal
  123. to their inversions modulo OP.
  124. OPTI The OPT layer modulo inversion, i.e. 1/2 of the OPT layer.
  125. Set-class. Note that CM and Cm are the same OPTI.
  126. OPERATIONS
  127. Each of the above equivalence classes is, of course, an operation that sends
  128. chords outside the fundamental domain to chords inside the fundamental domain.
  129. And we define the following additional operations:
  130. T(p, x) Translate p by x.
  131. I(p [, x]) Reflect p in x, by default the origin.
  132. P Send a major triad to the minor triad with the same root,
  133. or vice versa (Riemann's parallel transformation).
  134. L Send a major triad to the minor triad one major third higher,
  135. or vice versa (Riemann's Leittonwechsel or leading-tone
  136. exchange transformation).
  137. R Send a major triad to the minor triad one minor third lower,
  138. or vice versa (Riemann's relative transformation).
  139. D Send a triad to the next triad a perfect fifth lower
  140. (dominant transformation).
  141. P, L, and R have been extended as follows, see Fiore and Satyendra,
  142. "Generalized Contextual Groups", _Music Theory Online_ 11, August 2008:
  143. K(c) Interchange by inversion;
  144. K(c) := I(c, c[1] + c[2]).
  145. This is a generalized form of P; for major and minor triads,
  146. it is exactly the same as P, but it also works with other
  147. chord types.
  148. Q(c, n, m) Contexual transposition;
  149. Q(c, n, m) := T(c, n) if c is a T-form of m,
  150. or T(c, -n) if c is an I-form of M. Not a generalized form
  151. of L or R; but, like them, K and Q generate the T-I group.
  152. ]]
  153. end
  154. --[[
  155. LOG
  156. 2011-Sep-07
  157. Redoing this package from scratch using GVLS formulas.
  158. 2011-Sep-09
  159. There are definite problems with side effects in these tests. The first test
  160. passes, but not when in series with another test.
  161. Is there a bug in "~=" for Lua and/or LuaJIT?
  162. 2011-Sep-10
  163. There is definitely one or more bugs in LuaJIT vs. Lua. Tests run to 4 or 5
  164. voices in Lua that do not work in LuaJIT. There appear to be false temporaries
  165. or something in LuaJIT.
  166. 2011-Sep-11
  167. I am going to redo the equivalence formulas in sets: vanilla GVLS, GVLS with
  168. my modifications, and mine. This seems like the only way of sorting out the
  169. mess.
  170. 2011-Sep-22
  171. It may be that my R, P, T, and I do not fit together to make OPTI because my I
  172. does not use the inversion flat.
  173. [2011-Sep-28: Not so, but because my T was aligned on 12-TET.]
  174. 2011-Sep-27
  175. Redo tests of equivalences. [2011-Sep-28: They pass up to 5 voices.]
  176. 2011-Sep-28
  177. First, some lessons learned painfully. I can still think quite well, but I AM
  178. slower, and I have to ALLOW for that. So I actually need to think MORE
  179. CAREFULLY (which takes longer) in order to make sure tests and logic are quite
  180. clear, as this will save yet more time in testing and debugging. E.g., if I
  181. had had my current unit tests in place when I began to rewrite this code,
  182. I would have been where I am now four or five months ago. It wouldn't hurt,
  183. either, to ask for help sooner rather than later -- DT's advice was extremely
  184. helpful.
  185. OK, I get it now. My prior attempts to combine representative fundamental
  186. domains were ill-advised.
  187. The fundamental domain formulas are now as correct as the tests I have written
  188. can tell, although I should try to come up with some additional tests. Yet
  189. there still may be problems...
  190. 2011-Oct-11
  191. RPTT and RPTTI need unit tests. The definitions in :information and in
  192. ChordSpaceGroup are not consistent, this is why ChordSpaceGroup is failing.
  193. 2011-Oct-13
  194. I have redone ChordSpaceGroup using different orbifolds and equivalences that
  195. keep operations in OPTI, I, and T strictly within equal temperament. I do not
  196. use the GVLS fundamental domains directly for this at all. The symmetries of
  197. the operations are all I need musically, so I simply put the GVLS OPTIs into
  198. equal temperament and enumerate them for my set-class group (OPTTI). I do this
  199. by taking the floor of the OPTI and then transposing it up by one unit of
  200. transposition.
  201. 2011-Oct-16
  202. Problems with ChordSpaceGroup:toChord and :fromChord. Look at voicing number
  203. and transposition.
  204. 2011-Oct-17
  205. Problems, e.g. {-4, 0, 4} transposed 4 is also eOP {-4, 0, 4}, ditto
  206. transposed 8 and 12. The inversion flat would behave similiarly.
  207. In other words, for some chords ChordSpaceGroup:toChord will give the same
  208. chord for several different P, I, T, V, but ChordSpaceGroup:fromChord can only
  209. give the same P, I, T, V for all of those chords. This is correct, but it
  210. means that in unit testing from and should compare only chords, not numbers.
  211. 2011-Oct-19
  212. In 1 dimension, all pitches under OP equivalence fall in the fundamental
  213. domain {[0, 12)}; there are no duplicate equivalents.
  214. In 2 dimensions, all chords under OP equivalence fall in the fundamental
  215. domain {[-6, 6], [-6, 6], [12, 0], [0, 12]}; there are duplicate
  216. equivalents, because otherwise the chord {11, 11} has no equivalent in
  217. the domain (it is actually {-1, 11}).
  218. 2011-Oct-20
  219. DD'OOOOHHHHHHHHHHHHH.... -0 and 0 are messing up chord comparators and hashes.
  220. Hence the sets of OPTTI are wrong. Hence the ChordSpaceGroup is wrong. But,
  221. now I know what the problem is!
  222. 2011-Oct-24
  223. "allOf" fails for OPT and OPTI because of fractional pitches. This must be
  224. dealt with, but is not so serious right now.
  225. More urgently, there is still a problem with OPI for 5 voices. I should
  226. capture the offending chord and test it in solitary confinement. Well, here
  227. it is:
  228. TESTING CHORDS OF 5 VOICES...
  229. pitches: { -13.0000 -13.0000 -13.0000 -8.0000 -6.0000}
  230. I: { 13.0000 13.0000 13.0000 8.0000 6.0000}
  231. eO: { -1.0000 -1.0000 -1.0000 4.0000 6.0000} iseO: false
  232. eP: { -13.0000 -13.0000 -13.0000 -8.0000 -6.0000} iseP: true
  233. eT: { -2.4000 -2.4000 -2.4000 2.6000 4.6000} iseT: false
  234. { 0.0000 0.0000 0.0000 5.0000 7.0000}
  235. eI: { -13.0000 -13.0000 -13.0000 -8.0000 -6.0000} iseI: true
  236. eV: { -13.0000 -13.0000 -13.0000 -8.0000 -6.0000} iseV: true
  237. { 0.0000 0.0000 0.0000 5.0000 7.0000}
  238. eOP: { -1.0000 -1.0000 -1.0000 4.0000 6.0000} iseOP: false
  239. pcs: { 4.0000 6.0000 11.0000 11.0000 11.0000}
  240. eOPT: { -2.4000 -2.4000 -2.4000 2.6000 4.6000} iseOPT: false
  241. eOPTT: { -2.0000 -2.0000 -2.0000 3.0000 5.0000}
  242. { 0.0000 0.0000 0.0000 5.0000 7.0000}
  243. eOPI: { -4.0000 1.0000 1.0000 1.0000 6.0000} iseOPI: false
  244. eOPTI: { -2.4000 -2.4000 -2.4000 2.6000 4.6000} iseOPTI: false
  245. eOPTTI: { -2.0000 -2.0000 -2.0000 3.0000 5.0000}
  246. { 0.0000 0.0000 0.0000 5.0000 7.0000}
  247. layer: -53.00
  248. pitches: { -2.4000 -2.4000 -2.4000 2.6000 4.6000}
  249. I: { 2.4000 2.4000 2.4000 -2.6000 -4.6000}
  250. eO: { -2.4000 -2.4000 9.6000 2.6000 4.6000} iseO: true
  251. eP: { -2.4000 -2.4000 -2.4000 2.6000 4.6000} iseP: true
  252. eT: { -2.4000 -2.4000 -2.4000 2.6000 4.6000} iseT: true
  253. { 0.0000 0.0000 0.0000 5.0000 7.0000}
  254. eI: { -2.4000 -2.4000 -2.4000 2.6000 4.6000} iseI: true
  255. eV: { -2.4000 -2.4000 -2.4000 2.6000 4.6000} iseV: true
  256. { 0.0000 0.0000 0.0000 5.0000 7.0000}
  257. eOP: { -2.4000 -2.4000 2.6000 4.6000 9.6000} iseOP: true
  258. pcs: { 2.6000 4.6000 9.6000 9.6000 9.6000}
  259. eOPT: { -4.6000 -2.6000 2.4000 2.4000 2.4000} iseOPT: true
  260. eOPTT: { -4.0000 -2.0000 3.0000 3.0000 3.0000}
  261. { 0.0000 2.0000 7.0000 7.0000 7.0000}
  262. eOPI: { -2.6000 2.4000 2.4000 2.4000 7.4000} iseOPI: false
  263. eOPTI: { -4.6000 -2.6000 2.4000 2.4000 2.4000} iseOPTI: false
  264. eOPTTI: { -4.0000 -2.0000 3.0000 3.0000 3.0000}
  265. { 0.0000 2.0000 7.0000 7.0000 7.0000}
  266. layer: 0.00
  267. ========================================================================
  268. FAILED: chord:eOPTI():iseOPTI() == true
  269. ========================================================================
  270. pitches: { -13.0000 -13.0000 -13.0000 -8.0000 6.0000}
  271. I: { 13.0000 13.0000 13.0000 8.0000 -6.0000}
  272. eO: { -1.0000 -1.0000 -1.0000 4.0000 6.0000} iseO: false
  273. eP: { -13.0000 -13.0000 -13.0000 -8.0000 6.0000} iseP: true
  274. eT: { -4.8000 -4.8000 -4.8000 0.2000 14.2000} iseT: false
  275. { 0.0000 0.0000 0.0000 5.0000 19.0000}
  276. eI: { -13.0000 -13.0000 -13.0000 -8.0000 6.0000} iseI: true
  277. eV: { -8.0000 6.0000 -13.0000 -13.0000 -13.0000} iseV: false
  278. { 5.0000 19.0000 0.0000 0.0000 0.0000}
  279. eOP: { -1.0000 -1.0000 -1.0000 4.0000 6.0000} iseOP: false
  280. pcs: { 4.0000 6.0000 11.0000 11.0000 11.0000}
  281. eOPT: { -2.4000 -2.4000 -2.4000 2.6000 4.6000} iseOPT: false
  282. eOPTT: { -2.0000 -2.0000 -2.0000 3.0000 5.0000}
  283. { 0.0000 0.0000 0.0000 5.0000 7.0000}
  284. eOPI: { -4.0000 1.0000 1.0000 1.0000 6.0000} iseOPI: false
  285. eOPTI: { -2.4000 -2.4000 -2.4000 2.6000 4.6000} iseOPTI: false
  286. eOPTTI: { -2.0000 -2.0000 -2.0000 3.0000 5.0000}
  287. { 0.0000 0.0000 0.0000 5.0000 7.0000}
  288. layer: -41.00
  289. pitches: { -2.4000 -2.4000 -2.4000 2.6000 4.6000}
  290. I: { 2.4000 2.4000 2.4000 -2.6000 -4.6000}
  291. eO: { -2.4000 -2.4000 9.6000 2.6000 4.6000} iseO: true
  292. eP: { -2.4000 -2.4000 -2.4000 2.6000 4.6000} iseP: true
  293. eT: { -2.4000 -2.4000 -2.4000 2.6000 4.6000} iseT: true
  294. { 0.0000 0.0000 0.0000 5.0000 7.0000}
  295. eI: { -2.4000 -2.4000 -2.4000 2.6000 4.6000} iseI: true
  296. eV: { -2.4000 -2.4000 -2.4000 2.6000 4.6000} iseV: true
  297. { 0.0000 0.0000 0.0000 5.0000 7.0000}
  298. eOP: { -2.4000 -2.4000 2.6000 4.6000 9.6000} iseOP: true
  299. pcs: { 2.6000 4.6000 9.6000 9.6000 9.6000}
  300. eOPT: { -4.6000 -2.6000 2.4000 2.4000 2.4000} iseOPT: true
  301. eOPTT: { -4.0000 -2.0000 3.0000 3.0000 3.0000}
  302. { 0.0000 2.0000 7.0000 7.0000 7.0000}
  303. eOPI: { -2.6000 2.4000 2.4000 2.4000 7.4000} iseOPI: false
  304. eOPTI: { -4.6000 -2.6000 2.4000 2.4000 2.4000} iseOPTI: false
  305. eOPTTI: { -4.0000 -2.0000 3.0000 3.0000 3.0000}
  306. { 0.0000 2.0000 7.0000 7.0000 7.0000}
  307. layer: 0.00
  308. ========================================================================
  309. FAILED: chord:eOPTI():iseOPTI() == true
  310. ========================================================================
  311. pitches: { -13.0000 -13.0000 -13.0000 -7.0000 -7.0000}
  312. I: { 13.0000 13.0000 13.0000 7.0000 7.0000}
  313. eO: { -1.0000 -1.0000 -1.0000 5.0000 5.0000} iseO: false
  314. eP: { -13.0000 -13.0000 -13.0000 -7.0000 -7.0000} iseP: true
  315. eT: { -2.4000 -2.4000 -2.4000 3.6000 3.6000} iseT: false
  316. { 0.0000 0.0000 0.0000 6.0000 6.0000}
  317. eI: { -13.0000 -13.0000 -13.0000 -7.0000 -7.0000} iseI: true
  318. eV: { -13.0000 -13.0000 -13.0000 -7.0000 -7.0000} iseV: true
  319. { 0.0000 0.0000 0.0000 6.0000 6.0000}
  320. eOP: { -1.0000 -1.0000 -1.0000 5.0000 5.0000} iseOP: false
  321. pcs: { 5.0000 5.0000 11.0000 11.0000 11.0000}
  322. eOPT: { -2.4000 -2.4000 -2.4000 3.6000 3.6000} iseOPT: false
  323. eOPTT: { -2.0000 -2.0000 -2.0000 4.0000 4.0000}
  324. { 0.0000 0.0000 0.0000 6.0000 6.0000}
  325. eOPI: { -5.0000 1.0000 1.0000 1.0000 7.0000} iseOPI: false
  326. eOPTI: { -2.4000 -2.4000 -2.4000 3.6000 3.6000} iseOPTI: false
  327. eOPTTI: { -2.0000 -2.0000 -2.0000 4.0000 4.0000}
  328. { 0.0000 0.0000 0.0000 6.0000 6.0000}
  329. layer: -53.00
  330. pitches: { -2.4000 -2.4000 -2.4000 3.6000 3.6000}
  331. I: { 2.4000 2.4000 2.4000 -3.6000 -3.6000}
  332. eO: { -2.4000 -2.4000 9.6000 3.6000 3.6000} iseO: true
  333. eP: { -2.4000 -2.4000 -2.4000 3.6000 3.6000} iseP: true
  334. eT: { -2.4000 -2.4000 -2.4000 3.6000 3.6000} iseT: true
  335. { 0.0000 0.0000 0.0000 6.0000 6.0000}
  336. eI: { -2.4000 -2.4000 -2.4000 3.6000 3.6000} iseI: true
  337. eV: { -2.4000 -2.4000 -2.4000 3.6000 3.6000} iseV: true
  338. { 0.0000 0.0000 0.0000 6.0000 6.0000}
  339. eOP: { -2.4000 -2.4000 3.6000 3.6000 9.6000} iseOP: true
  340. pcs: { 3.6000 3.6000 9.6000 9.6000 9.6000}
  341. eOPT: { -3.6000 -3.6000 2.4000 2.4000 2.4000} iseOPT: true
  342. eOPTT: { -3.0000 -3.0000 3.0000 3.0000 3.0000}
  343. { 0.0000 0.0000 6.0000 6.0000 6.0000}
  344. eOPI: { -3.6000 2.4000 2.4000 2.4000 8.4000} iseOPI: false
  345. eOPTI: { -3.6000 -3.6000 2.4000 2.4000 2.4000} iseOPTI: false
  346. eOPTTI: { -3.0000 -3.0000 3.0000 3.0000 3.0000}
  347. { 0.0000 0.0000 6.0000 6.0000 6.0000}
  348. layer: 0.00
  349. ========================================================================
  350. FAILED: chord:eOPTI():iseOPTI() == true
  351. ========================================================================
  352. pitches: { -13.0000 -13.0000 -13.0000 -7.0000 5.0000}
  353. I: { 13.0000 13.0000 13.0000 7.0000 -5.0000}
  354. eO: { -1.0000 -1.0000 -1.0000 5.0000 5.0000} iseO: false
  355. eP: { -13.0000 -13.0000 -13.0000 -7.0000 5.0000} iseP: true
  356. eT: { -4.8000 -4.8000 -4.8000 1.2000 13.2000} iseT: false
  357. { 0.0000 0.0000 0.0000 6.0000 18.0000}
  358. eI: { -13.0000 -13.0000 -13.0000 -7.0000 5.0000} iseI: true
  359. eV: { -13.0000 -13.0000 -7.0000 5.0000 -13.0000} iseV: false
  360. { 0.0000 0.0000 6.0000 18.0000 0.0000}
  361. eOP: { -1.0000 -1.0000 -1.0000 5.0000 5.0000} iseOP: false
  362. pcs: { 5.0000 5.0000 11.0000 11.0000 11.0000}
  363. eOPT: { -2.4000 -2.4000 -2.4000 3.6000 3.6000} iseOPT: false
  364. eOPTT: { -2.0000 -2.0000 -2.0000 4.0000 4.0000}
  365. { 0.0000 0.0000 0.0000 6.0000 6.0000}
  366. eOPI: { -5.0000 1.0000 1.0000 1.0000 7.0000} iseOPI: false
  367. eOPTI: { -2.4000 -2.4000 -2.4000 3.6000 3.6000} iseOPTI: false
  368. eOPTTI: { -2.0000 -2.0000 -2.0000 4.0000 4.0000}
  369. { 0.0000 0.0000 0.0000 6.0000 6.0000}
  370. layer: -41.00
  371. pitches: { -2.4000 -2.4000 -2.4000 3.6000 3.6000}
  372. I: { 2.4000 2.4000 2.4000 -3.6000 -3.6000}
  373. eO: { -2.4000 -2.4000 9.6000 3.6000 3.6000} iseO: true
  374. eP: { -2.4000 -2.4000 -2.4000 3.6000 3.6000} iseP: true
  375. eT: { -2.4000 -2.4000 -2.4000 3.6000 3.6000} iseT: true
  376. { 0.0000 0.0000 0.0000 6.0000 6.0000}
  377. eI: { -2.4000 -2.4000 -2.4000 3.6000 3.6000} iseI: true
  378. eV: { -2.4000 -2.4000 -2.4000 3.6000 3.6000} iseV: true
  379. { 0.0000 0.0000 0.0000 6.0000 6.0000}
  380. eOP: { -2.4000 -2.4000 3.6000 3.6000 9.6000} iseOP: true
  381. pcs: { 3.6000 3.6000 9.6000 9.6000 9.6000}
  382. eOPT: { -3.6000 -3.6000 2.4000 2.4000 2.4000} iseOPT: true
  383. eOPTT: { -3.0000 -3.0000 3.0000 3.0000 3.0000}
  384. { 0.0000 0.0000 6.0000 6.0000 6.0000}
  385. eOPI: { -3.6000 2.4000 2.4000 2.4000 8.4000} iseOPI: false
  386. eOPTI: { -3.6000 -3.6000 2.4000 2.4000 2.4000} iseOPTI: false
  387. eOPTTI: { -3.0000 -3.0000 3.0000 3.0000 3.0000}
  388. { 0.0000 0.0000 6.0000 6.0000 6.0000}
  389. layer: 0.00
  390. ========================================================================
  391. FAILED: chord:eOPTI():iseOPTI() == true
  392. ========================================================================
  393. It looks like the real culprit is this eV thing.
  394. 2011-Oct-25
  395. Regarding eV, it's tricky because to start with a voicing and permute doesn't
  396. give the same permutations as starting with a chord and permuting. So the
  397. permutations have always to be in the same order. This may be my whole problem
  398. all along.
  399. I think eV may be the first permutation that is the closest to the unison
  400. diagonal.
  401. 2011-Oct-27
  402. It's official:
  403. From: 5 0 0 0
  404. { -3.0000 0.0000 4.0000}
  405. fromChord: chord: { -3.0000 0.0000 4.0000} true
  406. fromChord: op: { -3.0000 0.0000 4.0000}
  407. fromChord: optt: { -3.0000 0.0000 4.0000}
  408. fromChord: optt_t: { -3.0000 0.0000 4.0000} 0
  409. equals
  410. fromChord: optti: { -3.0000 0.0000 4.0000} -3,1.6653345369377e-016,4
  411. fromChord: voicing: { 0.0000 0.0000 0.0000} 0
  412. fromChord: nil 0 0 0
  413. To: nil 0 0 0
  414. c:\utah\opt\Csound\bin\luajit.exe: .\ChordSpace.lua:2477: attempt to perform arithmetic on local 'P' (a nil value)
  415. stack traceback:
  416. .\ChordSpace.lua:2477: in function 'toChord'
  417. ChordSpaceTest.lua:205: in main chunk
  418. [C]: ?
  419. Hashing will need clamping. That might need a global g.
  420. TODO:
  421. -- Redo basic unit tests to ensure nothing has been broken.
  422. -- Compute and save a chord space group file if the requested group does not
  423. exist; always load a chord space group from a file. This saves simply
  424. oodles of time.
  425. -- Display the fundamental domains in the viewer much more clearly.
  426. -- Display various temperament systems to see how harmony might work with
  427. voiceleading around the central diagonal. Alternatively, set up columns
  428. or lattices of chords that are in interesting relations, and see how they
  429. sound and work together.
  430. -- Implement Rachel Hall, "Linear Contextual Transformations," 2009,
  431. which seems to further extend the Generalized Contextual Group using
  432. affine transformations in chord space, and Maxx Cho, "The Voice-Leading
  433. Automorphism and Riemannian Operators," 2009, which may show that tonality
  434. arises from a voice-leading automorphism in the Riemannian group.
  435. -- Implement various scales found in 20th and 21st century harmony
  436. along with 'splitting' and 'merging' operations.
  437. ]]
  438. ChordSpace.help()
  439. -- Returns n!
  440. function ChordSpace.factorial (n)
  441. if n == 0 then
  442. return 1
  443. else
  444. return n * ChordSpace.factorial(n - 1)
  445. end
  446. end
  447. -- For taking numerical errors into account.
  448. ChordSpace.EPSILON = 1
  449. local epsilonFactor = 1000
  450. while true do
  451. ChordSpace.EPSILON = ChordSpace.EPSILON / 2
  452. local nextEpsilon = ChordSpace.EPSILON / 2
  453. local onePlusNextEpsilon = 1 + nextEpsilon
  454. if onePlusNextEpsilon == 1 then
  455. print(string.format('ChordSpace.EPSILON: %g', ChordSpace.EPSILON))
  456. break
  457. end
  458. end
  459. function ChordSpace.eq_epsilon(a, b, factor)
  460. factor = factor or epsilonFactor
  461. if (math.abs(a - b) < (ChordSpace.EPSILON * factor)) then
  462. return true
  463. end
  464. return false
  465. end
  466. function ChordSpace.gt_epsilon(a, b, factor)
  467. factor = factor or epsilonFactor
  468. local eq = ChordSpace.eq_epsilon(a, b, factor)
  469. if eq then
  470. return false
  471. end
  472. if a > b then
  473. return true
  474. end
  475. return false
  476. end
  477. function ChordSpace.lt_epsilon(a, b, factor)
  478. factor = factor or epsilonFactor
  479. local eq = ChordSpace.eq_epsilon(a, b, factor)
  480. if eq then
  481. return false
  482. end
  483. if a < b then
  484. return true
  485. end
  486. return false
  487. end
  488. function ChordSpace.ge_epsilon(a, b, factor)
  489. factor = factor or epsilonFactor
  490. local eq = ChordSpace.eq_epsilon(a, b, factor)
  491. if eq then
  492. return true
  493. end
  494. if a > b then
  495. return true
  496. end
  497. return false
  498. end
  499. function ChordSpace.le_epsilon(a, b, factor)
  500. factor = factor or epsilonFactor
  501. local eq = ChordSpace.eq_epsilon(a, b, factor)
  502. if eq then
  503. return true
  504. end
  505. if a < b then
  506. return true
  507. end
  508. return false
  509. end
  510. -- The size of the octave, defined to be consistent with
  511. -- 12 tone equal temperament and MIDI.
  512. ChordSpace.OCTAVE = 12
  513. -- Middle C.
  514. ChordSpace.MIDDLE_C = 60
  515. ChordSpace.C4 = ChordSpace.MIDDLE_C
  516. -- Returns the pitch transposed by semitones, which may be any scalar.
  517. -- NOTE: Does NOT return the result under any equivalence class.
  518. local function T(pitch, semitones)
  519. return pitch + semitones
  520. end
  521. -- Returns the pitch reflected in the center, which may be any pitch.
  522. -- NOTE: Does NOT return the result under any equivalence class.
  523. local function I(pitch, center)
  524. center = center or 0
  525. return center - pitch
  526. end
  527. -- Returns the Euclidean distance between chords a and b,
  528. -- which must have the same number of voices.
  529. function ChordSpace.euclidean(a, b)
  530. local sumOfSquaredDifferences = 0
  531. for voice = 1, #a do
  532. sumOfSquaredDifferences = sumOfSquaredDifferences + math.pow((a[voice] - b[voice]), 2)
  533. end
  534. return math.sqrt(sumOfSquaredDifferences)
  535. end
  536. -- Chords represent simultaneously sounding pitches.
  537. -- The pitches are represented as semitones with 0 at the origin
  538. -- and middle C as 60.
  539. Chord = {}
  540. -- Returns a new chord object with no voices.
  541. function Chord:new(o)
  542. o = o or {duration = {}, channel = {}, velocity = {}, pan = {}}
  543. if not o.duration then
  544. o.duration = {}
  545. end
  546. if not o.channel then
  547. o.channel = {}
  548. end
  549. if not o.velocity then
  550. o.velocity = {}
  551. end
  552. if not o.pan then
  553. o.pan = {}
  554. end
  555. setmetatable(o, self)
  556. self.__index = self
  557. return o
  558. end
  559. -- Returns a string representation of the chord.
  560. -- Quadratic complexity, but short enough not to matter.
  561. function Chord:__tostring()
  562. local buffer = '{'
  563. for voice = 1, #self do
  564. buffer = buffer .. string.format('%12.7f', self[voice])
  565. end
  566. buffer = buffer .. '}'
  567. return buffer
  568. end
  569. -- Resizes a chord to the specified number of voices.
  570. -- Existing voices are not changed. Extra voices are removed.
  571. -- New voices are initialized to 0.
  572. function Chord:resize(voices)
  573. while #self < voices do
  574. table.insert(self, 0)
  575. table.insert(self.duration, 0)
  576. table.insert(self.channel, 0)
  577. table.insert(self.velocity, 0)
  578. table.insert(self.pan, 0)
  579. end
  580. while #self > voices do
  581. table.remove(self)
  582. table.remove(self.duration)
  583. table.remove(self.channel)
  584. table.remove(self.velocity)
  585. table.remove(self.pan)
  586. end
  587. end
  588. function Chord:setDuration(value)
  589. for i = 1, #self do
  590. self.duration[i] = value
  591. end
  592. end
  593. function Chord:getDuration(voice)
  594. voice = voice or 1
  595. return self.duration[voice]
  596. end
  597. function Chord:setChannel(value)
  598. for i = 1, #self do
  599. self.channel[i] = value
  600. end
  601. end
  602. function Chord:getChannel(voice)
  603. voice = voice or 1
  604. return self.channel[voice]
  605. end
  606. function Chord:setVelocity(value)
  607. for i = 1, #self do
  608. self.velocity[i] = value
  609. end
  610. end
  611. function Chord:getVelocity(voice)
  612. voice = voice or 1
  613. return self.velocity[voice]
  614. end
  615. function Chord:setPan(value)
  616. for i = 1, #self do
  617. self.pan[i] = value
  618. end
  619. end
  620. function Chord:getPan(voice)
  621. voice = voice or 1
  622. return self.pan[voice]
  623. end
  624. function Chord:count(pitch)
  625. local n = 0
  626. for voice = 1, #self do
  627. if self[voice] == pitch then
  628. n = n + 1
  629. end
  630. end
  631. return n
  632. end
  633. -- Redefines the metamethod to implement value semantics
  634. -- for ==, for the pitches in this only.
  635. function Chord:__eq(other)
  636. if not (#self == #other) then
  637. return false
  638. end
  639. for voice = 1, #self do
  640. --if not (self[voice] == other[voice]) then
  641. if not (ChordSpace.eq_epsilon(self[voice], other[voice])) then
  642. return false
  643. end
  644. end
  645. return true
  646. end
  647. function Chord:__eq_epsilon(other)
  648. if not (#self == #other) then
  649. return false
  650. end
  651. for voice = 1, #self do
  652. if not (ChordSpace.eq_epsilon(self[voice], other[voice])) then
  653. return false
  654. end
  655. end
  656. return true
  657. end
  658. -- This hash function is used to give chords value semantics for sets.
  659. function Chord:__hash()
  660. local buffer = ''
  661. local comma = ','
  662. for voice = 1, #self do
  663. local digit = tostring(self[voice])
  664. if voice == 1 then
  665. buffer = buffer .. digit
  666. else
  667. buffer = buffer .. comma .. digit
  668. end
  669. end
  670. return buffer
  671. end
  672. -- Redefines the metamethod to implement value semantics
  673. -- for <, for the pitches in this only.
  674. function Chord:__lt(other)
  675. local voices = math.min(#self, #other)
  676. for voice = 1, voices do
  677. if ChordSpace.lt_epsilon(self[voice], other[voice]) then
  678. return true
  679. end
  680. if ChordSpace.gt_epsilon(self[voice], other[voice]) then
  681. return false
  682. end
  683. end
  684. if #self < #other then
  685. return true
  686. end
  687. return false
  688. end
  689. function Chord:__le(other)
  690. if self:__eq(other) then
  691. return true
  692. end
  693. return self:__lt(other)
  694. end
  695. -- Returns whether or not the chord contains the pitch.
  696. function Chord:contains(pitch)
  697. for voice, pitch_ in ipairs(self) do
  698. if pitch_ == pitch then
  699. return true
  700. end
  701. end
  702. return false
  703. end
  704. -- Returns the lowest pitch in the chord,
  705. -- and also its voice index.
  706. function Chord:min()
  707. local lowestVoice = 1
  708. local lowestPitch = self[lowestVoice]
  709. for voice = 2, #self do
  710. if self[voice] < lowestPitch then
  711. lowestPitch = self[voice]
  712. lowestVoice = voice
  713. end
  714. end
  715. return lowestPitch, lowestVoice
  716. end
  717. -- Returns the minimum interval in the chord.
  718. function Chord:minimumInterval()
  719. local minimumInterval = math.abs(self[1] - self[2])
  720. for v1 = 1, #self do
  721. for v2 = 1, #self do
  722. if t (v1 == v2) then
  723. local interval = math.abs(self[v1] - self[v2])
  724. if interval < minimumInterval then
  725. minimumInterval = interval
  726. end
  727. end
  728. end
  729. end
  730. return minimumInterval
  731. end
  732. -- Returns the highest pitch in the chord,
  733. -- and also its voice index.
  734. function Chord:max()
  735. local highestVoice = 1
  736. local highestPitch = self[highestVoice]
  737. for voice = 2, #self do
  738. if self[voice] > highestPitch then
  739. highestPitch = self[voice]
  740. highestVoice = voice
  741. end
  742. end
  743. return highestPitch, highestVoice
  744. end
  745. -- Returns the maximum interval in the chord.
  746. function Chord:maximumInterval()
  747. return self:max() - self:min()
  748. end
  749. -- Returns a new chord whose pitches are the floors of this chord's pitches.
  750. function Chord:floor()
  751. local chord = self:clone()
  752. for voice = 1, #self do
  753. chord[voice] = math.floor(self[voice])
  754. end
  755. return chord
  756. end
  757. -- Returns a new chord whose pitches are the ceilings of this chord's pitches.
  758. function Chord:ceil()
  759. local chord = self:clone()
  760. for voice = 1, #self do
  761. chord[voice] = math.ceil(self[voice])
  762. end
  763. return chord
  764. end
  765. -- Returns a value copy of the chord.
  766. function Chord:clone()
  767. local clone_ = Chord:new()
  768. for voice, pitch in ipairs(self) do
  769. clone_[voice] = pitch
  770. end
  771. for voice, value in ipairs(self.duration) do
  772. clone_.duration[voice] = value
  773. end
  774. for voice, value in ipairs(self.channel) do
  775. clone_.channel[voice] = value
  776. end
  777. for voice, value in ipairs(self.velocity) do
  778. clone_.velocity[voice] = value
  779. end
  780. for voice, value in ipairs(self.pan) do
  781. clone_.pan[voice] = value
  782. end
  783. return clone_
  784. end
  785. -- Returns the origin of the chord's space.
  786. function Chord:origin()
  787. local clone_ = self:clone()
  788. for voice = 1, #clone_ do
  789. clone_[voice] = 0
  790. end
  791. return clone_
  792. end
  793. function Chord:distanceToOrigin()
  794. local origin = self:origin()
  795. return ChordSpace.euclidean(self, origin)
  796. end
  797. function Chord:distanceToUnisonDiagonal()
  798. local unison = self:origin()
  799. local pitch = self:layer() / #self
  800. for voice = 1, #self do
  801. unison[voice] = pitch
  802. end
  803. return ChordSpace.euclidean(self, unison)
  804. end
  805. -- Returns the maximally even chord in the chord's space,
  806. -- e.g. the augmented triad for 3 dimensions.
  807. function Chord:maximallyEven()
  808. local clone_ = self:clone()
  809. local g = ChordSpace.OCTAVE / #clone_
  810. for i = 1, #clone_ do
  811. clone_[i] = (i - 1) * g
  812. end
  813. return clone_
  814. end
  815. -- Returns the sum of the pitches in the chord.
  816. function Chord:layer()
  817. local s = 0
  818. for voice, pitch in ipairs(self) do
  819. s = s + pitch
  820. end
  821. return s
  822. end
  823. -- Transposes the chord by the indicated interval (may be a fraction).
  824. -- NOTE: Does NOT return the result under any equivalence class.
  825. function Chord:T(interval)
  826. local clone_ = self:clone()
  827. for voice = 1, #clone_ do
  828. clone_[voice] = T(clone_[voice], interval)
  829. end
  830. return clone_
  831. end
  832. -- Inverts the chord by another chord that is on the unison diagonal, by
  833. -- default the origin. NOTE: Does NOT return the result under any equivalence
  834. -- class.
  835. function Chord:I(center)
  836. center = center or 0
  837. local inverse = self:clone()
  838. for voice = 1, #inverse do
  839. inverse[voice] = I(self[voice], center)
  840. end
  841. return inverse
  842. end
  843. -- Returns the equivalent of the pitch under pitch-class equivalence, i.e.
  844. -- the pitch is in the interval [0, OCTAVE).
  845. function ChordSpace.epc(pitch)
  846. --while pitch < 0 do
  847. while ChordSpace.lt_epsilon(pitch, 0) do
  848. pitch = pitch + ChordSpace.OCTAVE
  849. end
  850. --while pitch >= ChordSpace.OCTAVE do
  851. while ChordSpace.ge_epsilon(pitch, ChordSpace.OCTAVE) do
  852. pitch = pitch - ChordSpace.OCTAVE
  853. end
  854. return pitch
  855. end
  856. -- Returns whether the chord is within the fundamental domain of
  857. -- pitch-class equivalence, i.e. is a pitch-class set.
  858. function Chord:isepcs()
  859. for voice = 1, #chord do
  860. --if not (self[voice] == ChordSpace.epc(chord[voice])) then
  861. if not ChordSpace.eq_epsilon(self[voice], ChordSpace.epc(chord[voice])) then
  862. return false
  863. end
  864. end
  865. return true
  866. end
  867. -- Returns the equivalent of the chord under pitch-class equivalence,
  868. -- i.e. the pitch-class set of the chord.
  869. function Chord:epcs()
  870. local chord = self:clone()
  871. for voice = 1, #chord do
  872. chord[voice] = ChordSpace.epc(chord[voice])
  873. end
  874. return chord
  875. end
  876. -- Returns whether the chord is within the fundamental domain of
  877. -- transposition to 0.
  878. function Chord:iset()
  879. local et = self:et()
  880. if not (et == self) then
  881. return false
  882. end
  883. return true
  884. end
  885. -- Returns the equivalent of the chord within the fundamental domain of
  886. -- transposition to 0.
  887. function Chord:et()
  888. local min_ = self:min()
  889. return self:T(-min_)
  890. end
  891. -- Returns whether the chord is within the representative fundamental domain
  892. -- of the indicated range equivalence.
  893. function Chord:iseR(range)
  894. --[[ GVLS:
  895. local max_ = self:max()
  896. local min_ = self:min()
  897. if not (max_ <= (min_ + range)) then
  898. return false
  899. end
  900. local layer_ = self:layer()
  901. if not ((0 <= layer_) and (layer_ <= range)) then
  902. return false
  903. end
  904. return true
  905. --]]
  906. --[[ GVLS modified:
  907. local max_ = self:max()
  908. local min_ = self:min()
  909. if not ChordSpace.le_epsilon(max_, (min_ + range)) then
  910. return false
  911. end
  912. local layer_ = self:layer()
  913. if not (ChordSpace.le_epsilon(0, layer_) and ChordSpace.le_epsilon(layer_, range)) then
  914. return false
  915. end
  916. return true
  917. --]]
  918. ----[[ MKG several equivalents of boundary points in domain:
  919. local max_ = self:max()
  920. local min_ = self:min()
  921. if not ChordSpace.le_epsilon(max_, (min_ + range)) then
  922. return false
  923. end
  924. local layer_ = self:layer()
  925. if not (ChordSpace.le_epsilon(0, layer_) and ChordSpace.le_epsilon(layer_, range)) then
  926. return false
  927. end
  928. return true
  929. --]]
  930. end
  931. -- Returns whether the chord is within the representative fundamental domain
  932. -- of octave equivalence.
  933. function Chord:iseO()
  934. return self:iseR(ChordSpace.OCTAVE)
  935. end
  936. -- Returns the equivalent of the chord within the representative fundamental
  937. -- domain of a range equivalence.
  938. function Chord:eR(range)
  939. local chord = self:clone()
  940. --if chord:iseR(range) then
  941. -- return chord
  942. --end
  943. -- The clue here is that at least one voice must be >= 0,
  944. -- but no voice can be > range.
  945. -- First, move all pitches inside the interval [0, OCTAVE),
  946. -- which is not the same as the fundamental domain.
  947. chord = self:epcs()
  948. -- Then, reflect voices that are outside of the fundamental domain
  949. -- back into it, which will revoice the chord, i.e.
  950. -- the sum of pitches is in [0, OCTAVE].
  951. --while chord:layer() > range do
  952. while ChordSpace.gt_epsilon(chord:layer(), range) do
  953. local maximumPitch, maximumVoice = chord:max()
  954. -- Because no voice is above the range,
  955. -- any voices that need to be revoiced will now be negative.
  956. chord[maximumVoice] = maximumPitch - ChordSpace.OCTAVE
  957. end
  958. return chord
  959. end
  960. -- Returns the equivalent of the chord within the representative fundamental
  961. -- domain of octave equivalence.
  962. function Chord:eO()
  963. return self:eR(ChordSpace.OCTAVE)
  964. end
  965. -- Returns whether the chord is within the representative fundamental domain
  966. -- of permutational equivalence.
  967. function Chord:iseP()
  968. for voice = 2, #self do
  969. --if not (self[voice - 1] <= self[voice]) then
  970. if not ChordSpace.le_epsilon(self[voice - 1], self[voice]) then
  971. return false
  972. end
  973. end
  974. return true
  975. end
  976. -- Returns the equivalent of the chord within the representative fundamental
  977. -- domain of permutational equivalence.
  978. function Chord:eP()
  979. clone_ = self:clone()
  980. table.sort(clone_)
  981. return clone_
  982. end
  983. -- Returns whether the chord is within the representative fundamental domain
  984. -- of transpositional equivalence.
  985. function Chord:iseT()
  986. ----[[ GVLS:
  987. local layer_ = self:layer()
  988. if not ChordSpace.eq_epsilon(layer_, 0) then
  989. return false
  990. end
  991. return true
  992. --]]
  993. --[[ MKG:
  994. g = g or 1
  995. local ep = self:eP()
  996. if not (ep == ep:eT(g):eP()) then
  997. return false
  998. end
  999. return true
  1000. --]]
  1001. end
  1002. -- Returns the equivalent of the chord within the representative fundamental
  1003. -- domain of transpositonal equivalence.
  1004. function Chord:eT()
  1005. ----[[ GVLS:
  1006. local layer_ = self:layer()
  1007. local sumPerVoice = layer_ / #self
  1008. return self:T(-sumPerVoice)
  1009. --]]
  1010. --[[ MKG:
  1011. g = g or 1
  1012. local iterator = self
  1013. -- Transpose down to layer 0 or just below.
  1014. while iterator:layer() > 0 do
  1015. iterator = iterator:T(-g)
  1016. end
  1017. -- Transpose up to layer 0 or just above.
  1018. while iterator:layer() < 0 do
  1019. iterator = iterator:T(g)
  1020. end
  1021. return iterator
  1022. --]]
  1023. end
  1024. -- Returns the equivalent of the chord within the representative fundamental
  1025. -- domain of transpositonal equivalence and the equal temperament generated
  1026. -- by g. I.e., returns the chord transposed such that its layer is 0 or, under
  1027. -- transposition, the positive layer closest to 0. NOTE: Does NOT return the
  1028. -- result under any other equivalence class.
  1029. function Chord:eTT(g)
  1030. g = g or 1
  1031. local et = self:eT()
  1032. local transposition = math.ceil(et[1]) - et[1]
  1033. local ett = et:T(transposition)
  1034. return ett
  1035. end
  1036. -- Returns whether the chord is within the representative fundamental domain
  1037. -- of translational equivalence and the equal temperament generated by g.
  1038. function Chord:iseTT(g)
  1039. g = g or 1
  1040. local ep = self:eP()
  1041. if not (ep == ep:eTT(g)) then
  1042. return false
  1043. end
  1044. return true
  1045. end
  1046. -- Returns whether the chord is within the representative fundamental domain
  1047. -- of inversional equivalence.
  1048. function Chord:iseI(inverse)
  1049. --[[ GLVS:
  1050. local chord = self:clone()
  1051. local lowerVoice = 2
  1052. local upperVoice = #chord
  1053. while lowerVoice < upperVoice do
  1054. -- GVLS: tests only 1 interval: x[2] - x[1] <= x[#x] - x[#x - 1]
  1055. local lowerInterval = chord[lowerVoice] - chord[lowerVoice - 1]
  1056. local upperInterval = chord[upperVoice] - chord[upperVoice - 1]
  1057. if lowerInterval < upperInterval then
  1058. return true
  1059. end
  1060. if lowerInterval > upperInterval then
  1061. return false
  1062. end
  1063. lowerVoice = lowerVoice + 1
  1064. upperVoice = upperVoice - 1
  1065. end
  1066. return true
  1067. --]]
  1068. ----[[ MKG:
  1069. inverse = inverse or self:I()
  1070. if not self:__le(inverse) then
  1071. return false
  1072. end
  1073. return true
  1074. --]]
  1075. --[[ MKG:
  1076. inverse = self:I()
  1077. if not self:__le(inverse) then
  1078. return false
  1079. end
  1080. return true
  1081. --]]
  1082. end
  1083. -- Returns the equivalent of the chord within the representative fundamental
  1084. -- domain of inversional equivalence.
  1085. function Chord:eI()
  1086. if self:iseI() then
  1087. return self:clone()
  1088. end
  1089. return self:I()
  1090. end
  1091. -- Returns whether the chord is within the representative fundamental domain
  1092. -- of range and permutational equivalence.
  1093. function Chord:iseRP(range)
  1094. --[[ GVLS:
  1095. for voice = 2, #self do
  1096. if not (self[voice - 1] <= self[voice]) then
  1097. return false
  1098. end
  1099. end
  1100. if not (self[#self] <= (self[1] + range)) then
  1101. return false
  1102. end
  1103. local layer_ = self:layer()
  1104. if not ((0 <= layer_) and (layer_ <= range)) then
  1105. return false
  1106. end
  1107. return true
  1108. --]]
  1109. ----[[ MKG:
  1110. if not self:iseR(range) then
  1111. return false
  1112. end
  1113. if not self:iseP() then
  1114. return false
  1115. end
  1116. return true
  1117. --]]
  1118. end
  1119. -- Returns whether the chord is within the representative fundamental domain
  1120. -- of octave and permutational equivalence.
  1121. function Chord:iseOP()
  1122. return self:iseRP(ChordSpace.OCTAVE)
  1123. end
  1124. -- Returns the equivalent of the chord within the representative fundamental
  1125. -- domain of range and permutational equivalence.
  1126. function Chord:eRP(range)
  1127. return self:eR(range):eP()
  1128. end
  1129. -- Returns the equivalent of the chord within the representative fundamental
  1130. -- domain of octave and permutational equivalence.
  1131. function Chord:eOP()
  1132. return self:eRP(ChordSpace.OCTAVE)
  1133. end
  1134. -- Returns a copy of the chord cyclically permuted by a stride, by default 1.
  1135. -- The direction of rotation is the same as musicians' first inversion, second
  1136. -- inversion, and so on.
  1137. function Chord:cycle(stride)
  1138. stride = stride or 1
  1139. local clone_ = self:clone()
  1140. if stride < 0 then
  1141. for i = 1, stride do
  1142. local tail = table.remove(clone_)
  1143. table.insert(clone_, 1, tail)
  1144. end
  1145. return chord
  1146. end
  1147. if stride > 0 then
  1148. for i = 1, math.abs(stride) do
  1149. local head = table.remove(clone_, 1)
  1150. table.insert(clone_, head)
  1151. end
  1152. end
  1153. return clone_
  1154. end
  1155. -- Returns the permutations of the pitches in a chord. The permutations from
  1156. -- each particular permutation are always returned in the same order.
  1157. function Chord:permutations()
  1158. local chord = self:clone()
  1159. local permutations_ = {}
  1160. permutations_[1] = chord
  1161. for i = 2, #self do
  1162. chord = chord:cycle()
  1163. permutations_[i] = chord
  1164. end
  1165. table.sort(permutations_)
  1166. return permutations_
  1167. end
  1168. -- Returns whether the chord is within the representative fundamental domain
  1169. -- of voicing equivalence.
  1170. function Chord:iseV()
  1171. local eV = self:eV()
  1172. --print(string.format('chord: %s eV: %s', tostring(self), tostring(eV)))
  1173. if not (self == eV) then
  1174. return false
  1175. end
  1176. return true
  1177. end
  1178. -- Returns the equivalent of the chord within the representative fundamental
  1179. -- domain of voicing equivalence.
  1180. function Chord:eV()
  1181. ----[[
  1182. for index, voicing in ipairs(self:permutations()) do
  1183. local wraparound = voicing[1] + ChordSpace.OCTAVE - voicing[#voicing]
  1184. local iseV_ = true
  1185. for voice = 1, #voicing - 1 do
  1186. local inner = voicing[voice + 1] - voicing[voice]
  1187. if not ChordSpace.ge_epsilon(wraparound, inner) then
  1188. --if inner > wraparound then
  1189. iseV_ = false
  1190. end
  1191. end
  1192. if iseV_ then
  1193. return voicing
  1194. end
  1195. end
  1196. --]]
  1197. --[[
  1198. local voicings = self:permutations()
  1199. local distancesForIndexes = {}
  1200. for i = 1, #voicings do
  1201. distancesForIndexes[i] = voicings[i]:distanceToUnisonDiagonal()
  1202. end
  1203. local minimumDistanceToUnisonDiagonal = distancesForIndexes[1]
  1204. for i = 2, #voicings do
  1205. if distancesForIndexes[i] < minimumDistanceToUnisonDiagonal then
  1206. minimumDistanceToUnisonDiagonal = distancesForIndexes[i]
  1207. end
  1208. end
  1209. for i = 1, #voicings do
  1210. if distancesForIndexes[i] == minimumDistanceToUnisonDiagonal then
  1211. return voicings[i]
  1212. end
  1213. end
  1214. --]]
  1215. end
  1216. -- Returns whether the chord is within the representative fundamental domain
  1217. -- of range, permutational, and transpositional equivalence.
  1218. function Chord:iseRPT(range)
  1219. --[[ GVLS:
  1220. -- GVLS: if not (self[#self] <= self[1] + ChordSpace.OCTAVE) then
  1221. if not (ChordSpace.le_epsilon(self[#self], (self[1] + range))) then
  1222. return false
  1223. end
  1224. local layer_ = self:layer()
  1225. -- GVLS: if not (layer_ == 0) then
  1226. if not ChordSpace.eq_epsilon(layer_, 0) then
  1227. return false
  1228. end
  1229. if #self < 2 then
  1230. return true
  1231. end
  1232. local wraparound = self[1] + range - self[#self]
  1233. for voice = 1, #self - 1 do
  1234. local inner = self[voice + 1] - self[voice]
  1235. if not ChordSpace.le_epsilon(wraparound, inner) then
  1236. return false
  1237. end
  1238. end
  1239. return true
  1240. --]]
  1241. ----[[ MKG:
  1242. if not self:iseR(range) then
  1243. return false
  1244. end
  1245. if not self:iseP() then
  1246. return false
  1247. end
  1248. if not self:iseT() then
  1249. return false
  1250. end
  1251. if not self:iseV() then
  1252. return false
  1253. end
  1254. return true
  1255. --]]
  1256. end
  1257. function Chord:iseRPTT(range)
  1258. if not self:iseP() then
  1259. return false
  1260. end
  1261. if not self:iseR(range) then
  1262. return false
  1263. end

Large files files are truncated, but you can click here to view the full file