musa-dsl 0.30.2 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -1
  3. data/.version +6 -0
  4. data/.yardopts +7 -0
  5. data/Gemfile +0 -1
  6. data/README.md +227 -6
  7. data/docs/README.md +83 -0
  8. data/docs/api-reference.md +86 -0
  9. data/docs/getting-started/quick-start.md +93 -0
  10. data/docs/getting-started/tutorial.md +58 -0
  11. data/docs/subsystems/core-extensions.md +316 -0
  12. data/docs/subsystems/datasets.md +465 -0
  13. data/docs/subsystems/generative.md +290 -0
  14. data/docs/subsystems/matrix.md +63 -0
  15. data/docs/subsystems/midi.md +123 -0
  16. data/docs/subsystems/music.md +544 -0
  17. data/docs/subsystems/musicxml-builder.md +264 -0
  18. data/docs/subsystems/neumas.md +71 -0
  19. data/docs/subsystems/repl.md +135 -0
  20. data/docs/subsystems/sequencer.md +98 -0
  21. data/docs/subsystems/series.md +302 -0
  22. data/docs/subsystems/transcription.md +152 -0
  23. data/docs/subsystems/transport.md +177 -0
  24. data/lib/musa-dsl/core-ext/array-explode-ranges.rb +68 -0
  25. data/lib/musa-dsl/core-ext/arrayfy.rb +110 -0
  26. data/lib/musa-dsl/core-ext/attribute-builder.rb +91 -30
  27. data/lib/musa-dsl/core-ext/deep-copy.rb +125 -2
  28. data/lib/musa-dsl/core-ext/dynamic-proxy.rb +78 -0
  29. data/lib/musa-dsl/core-ext/extension.rb +53 -0
  30. data/lib/musa-dsl/core-ext/hashify.rb +162 -1
  31. data/lib/musa-dsl/core-ext/inspect-nice.rb +154 -0
  32. data/lib/musa-dsl/core-ext/smart-proc-binder.rb +117 -0
  33. data/lib/musa-dsl/core-ext/with.rb +114 -0
  34. data/lib/musa-dsl/datasets/dataset.rb +109 -0
  35. data/lib/musa-dsl/datasets/delta-d.rb +78 -0
  36. data/lib/musa-dsl/datasets/e.rb +186 -2
  37. data/lib/musa-dsl/datasets/gdv.rb +279 -2
  38. data/lib/musa-dsl/datasets/gdvd.rb +201 -0
  39. data/lib/musa-dsl/datasets/helper.rb +75 -0
  40. data/lib/musa-dsl/datasets/p.rb +177 -2
  41. data/lib/musa-dsl/datasets/packed-v.rb +91 -0
  42. data/lib/musa-dsl/datasets/pdv.rb +136 -1
  43. data/lib/musa-dsl/datasets/ps.rb +134 -4
  44. data/lib/musa-dsl/datasets/score/queriable.rb +143 -1
  45. data/lib/musa-dsl/datasets/score/render.rb +105 -1
  46. data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +138 -1
  47. data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +111 -0
  48. data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +200 -1
  49. data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +145 -1
  50. data/lib/musa-dsl/datasets/score.rb +279 -0
  51. data/lib/musa-dsl/datasets/v.rb +88 -0
  52. data/lib/musa-dsl/generative/darwin.rb +215 -1
  53. data/lib/musa-dsl/generative/generative-grammar.rb +387 -0
  54. data/lib/musa-dsl/generative/markov.rb +135 -3
  55. data/lib/musa-dsl/generative/rules.rb +312 -4
  56. data/lib/musa-dsl/generative/variatio.rb +286 -2
  57. data/lib/musa-dsl/logger/logger.rb +267 -2
  58. data/lib/musa-dsl/matrix/matrix.rb +256 -10
  59. data/lib/musa-dsl/midi/midi-recorder.rb +113 -2
  60. data/lib/musa-dsl/midi/midi-voices.rb +275 -4
  61. data/lib/musa-dsl/music/chord-definition.rb +233 -1
  62. data/lib/musa-dsl/music/chord-definitions.rb +33 -6
  63. data/lib/musa-dsl/music/chords.rb +353 -2
  64. data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +70 -206
  65. data/lib/musa-dsl/music/scale_kinds/bebop/bebop_dominant_scale_kind.rb +110 -0
  66. data/lib/musa-dsl/music/scale_kinds/bebop/bebop_major_scale_kind.rb +110 -0
  67. data/lib/musa-dsl/music/scale_kinds/bebop/bebop_minor_scale_kind.rb +110 -0
  68. data/lib/musa-dsl/music/scale_kinds/blues/blues_major_scale_kind.rb +100 -0
  69. data/lib/musa-dsl/music/scale_kinds/blues/blues_scale_kind.rb +99 -0
  70. data/lib/musa-dsl/music/scale_kinds/chromatic_scale_kind.rb +79 -0
  71. data/lib/musa-dsl/music/scale_kinds/ethnic/double_harmonic_scale_kind.rb +102 -0
  72. data/lib/musa-dsl/music/scale_kinds/ethnic/hungarian_minor_scale_kind.rb +102 -0
  73. data/lib/musa-dsl/music/scale_kinds/ethnic/neapolitan_major_scale_kind.rb +102 -0
  74. data/lib/musa-dsl/music/scale_kinds/ethnic/neapolitan_minor_scale_kind.rb +101 -0
  75. data/lib/musa-dsl/music/scale_kinds/ethnic/phrygian_dominant_scale_kind.rb +103 -0
  76. data/lib/musa-dsl/music/scale_kinds/harmonic_major/harmonic_major_scale_kind.rb +104 -0
  77. data/lib/musa-dsl/music/scale_kinds/major_scale_kind.rb +110 -0
  78. data/lib/musa-dsl/music/scale_kinds/melodic_minor/altered_scale_kind.rb +106 -0
  79. data/lib/musa-dsl/music/scale_kinds/melodic_minor/dorian_b2_scale_kind.rb +104 -0
  80. data/lib/musa-dsl/music/scale_kinds/melodic_minor/locrian_sharp2_scale_kind.rb +103 -0
  81. data/lib/musa-dsl/music/scale_kinds/melodic_minor/lydian_augmented_scale_kind.rb +103 -0
  82. data/lib/musa-dsl/music/scale_kinds/melodic_minor/lydian_dominant_scale_kind.rb +106 -0
  83. data/lib/musa-dsl/music/scale_kinds/melodic_minor/melodic_minor_scale_kind.rb +104 -0
  84. data/lib/musa-dsl/music/scale_kinds/melodic_minor/mixolydian_b6_scale_kind.rb +103 -0
  85. data/lib/musa-dsl/music/scale_kinds/minor_harmonic_scale_kind.rb +125 -0
  86. data/lib/musa-dsl/music/scale_kinds/minor_natural_scale_kind.rb +123 -0
  87. data/lib/musa-dsl/music/scale_kinds/modes/dorian_scale_kind.rb +111 -0
  88. data/lib/musa-dsl/music/scale_kinds/modes/locrian_scale_kind.rb +114 -0
  89. data/lib/musa-dsl/music/scale_kinds/modes/lydian_scale_kind.rb +111 -0
  90. data/lib/musa-dsl/music/scale_kinds/modes/mixolydian_scale_kind.rb +111 -0
  91. data/lib/musa-dsl/music/scale_kinds/modes/phrygian_scale_kind.rb +111 -0
  92. data/lib/musa-dsl/music/scale_kinds/pentatonic/pentatonic_major_scale_kind.rb +93 -0
  93. data/lib/musa-dsl/music/scale_kinds/pentatonic/pentatonic_minor_scale_kind.rb +99 -0
  94. data/lib/musa-dsl/music/scale_kinds/symmetric/diminished_hw_scale_kind.rb +110 -0
  95. data/lib/musa-dsl/music/scale_kinds/symmetric/diminished_wh_scale_kind.rb +110 -0
  96. data/lib/musa-dsl/music/scale_kinds/symmetric/whole_tone_scale_kind.rb +99 -0
  97. data/lib/musa-dsl/music/scale_systems/equally_tempered_12_tone_scale_system.rb +80 -0
  98. data/lib/musa-dsl/music/scale_systems/twelve_semitones_scale_system.rb +60 -0
  99. data/lib/musa-dsl/music/scales.rb +1384 -40
  100. data/lib/musa-dsl/musicxml/builder/attributes.rb +483 -3
  101. data/lib/musa-dsl/musicxml/builder/backup-forward.rb +166 -1
  102. data/lib/musa-dsl/musicxml/builder/direction.rb +243 -0
  103. data/lib/musa-dsl/musicxml/builder/helper.rb +240 -0
  104. data/lib/musa-dsl/musicxml/builder/measure.rb +284 -0
  105. data/lib/musa-dsl/musicxml/builder/note-complexities.rb +324 -8
  106. data/lib/musa-dsl/musicxml/builder/note.rb +285 -0
  107. data/lib/musa-dsl/musicxml/builder/part-group.rb +108 -1
  108. data/lib/musa-dsl/musicxml/builder/part.rb +139 -0
  109. data/lib/musa-dsl/musicxml/builder/pitched-note.rb +124 -0
  110. data/lib/musa-dsl/musicxml/builder/rest.rb +93 -0
  111. data/lib/musa-dsl/musicxml/builder/score-partwise.rb +276 -0
  112. data/lib/musa-dsl/musicxml/builder/typed-text.rb +62 -1
  113. data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +83 -0
  114. data/lib/musa-dsl/neumalang/neumalang.rb +675 -0
  115. data/lib/musa-dsl/neumas/array-to-neumas.rb +149 -0
  116. data/lib/musa-dsl/neumas/neuma-decoder.rb +253 -0
  117. data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +142 -2
  118. data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +82 -0
  119. data/lib/musa-dsl/neumas/neumas.rb +67 -0
  120. data/lib/musa-dsl/neumas/string-to-neumas.rb +233 -1
  121. data/lib/musa-dsl/repl/repl.rb +550 -0
  122. data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +118 -2
  123. data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +149 -2
  124. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +296 -0
  125. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +88 -2
  126. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +161 -0
  127. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +263 -0
  128. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +173 -1
  129. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +177 -0
  130. data/lib/musa-dsl/sequencer/base-sequencer.rb +710 -10
  131. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +210 -0
  132. data/lib/musa-dsl/sequencer/timeslots.rb +79 -0
  133. data/lib/musa-dsl/series/array-to-serie.rb +37 -1
  134. data/lib/musa-dsl/series/base-series.rb +843 -5
  135. data/lib/musa-dsl/series/buffer-serie.rb +54 -0
  136. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +64 -0
  137. data/lib/musa-dsl/series/main-serie-constructors.rb +398 -2
  138. data/lib/musa-dsl/series/main-serie-operations.rb +538 -16
  139. data/lib/musa-dsl/series/proxy-serie.rb +67 -0
  140. data/lib/musa-dsl/series/quantizer-serie.rb +57 -7
  141. data/lib/musa-dsl/series/queue-serie.rb +78 -0
  142. data/lib/musa-dsl/series/series-composer.rb +701 -0
  143. data/lib/musa-dsl/series/timed-serie.rb +473 -28
  144. data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +404 -1
  145. data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +118 -0
  146. data/lib/musa-dsl/transcription/from-gdv.rb +84 -1
  147. data/lib/musa-dsl/transcription/transcription.rb +265 -0
  148. data/lib/musa-dsl/transport/clock.rb +125 -0
  149. data/lib/musa-dsl/transport/dummy-clock.rb +89 -2
  150. data/lib/musa-dsl/transport/external-tick-clock.rb +91 -0
  151. data/lib/musa-dsl/transport/input-midi-clock.rb +133 -1
  152. data/lib/musa-dsl/transport/timer-clock.rb +183 -1
  153. data/lib/musa-dsl/transport/timer.rb +83 -0
  154. data/lib/musa-dsl/transport/transport.rb +318 -0
  155. data/lib/musa-dsl/version.rb +2 -1
  156. data/lib/musa-dsl.rb +132 -25
  157. data/musa-dsl.gemspec +25 -18
  158. metadata +158 -16
@@ -0,0 +1,544 @@
1
+ # Music - Scales & Chords
2
+
3
+ Comprehensive framework for working with musical scales, tuning systems, and chord structures. The system resolves two main domains:
4
+
5
+ ## Scales System
6
+
7
+ The **Scales** module provides hierarchical access to musical scales with multiple tuning systems and scale types:
8
+
9
+ **Architecture:**
10
+ - **ScaleSystem** (`Musa::Scales::ScaleSystem`): Defines tuning systems (e.g., 12-tone equal temperament)
11
+ - **ScaleSystemTuning** (`Musa::Scales::ScaleSystemTuning`): A scale system with specific reference frequency (e.g., A=440Hz)
12
+ - **ScaleKind** (`Musa::Scales::ScaleKind`): Scale types (major, minor, chromatic, etc.)
13
+ - **Scale** (`Musa::Scales::Scale`): A scale kind rooted on a specific pitch (e.g., C major)
14
+ - **NoteInScale** (`Musa::Scales::NoteInScale`): A specific note within a scale
15
+
16
+ **Registry:**
17
+ - **Scales::Scales** (`Musa::Scales::Scales`): Central registry for accessing scale systems by ID or method name
18
+
19
+ **Available scale systems:**
20
+ - `:et12` (EquallyTempered12ToneScaleSystem): 12-tone equal temperament (default)
21
+
22
+ **Available scale kinds in et12:**
23
+
24
+ *Core scales:*
25
+ - `:major` - Major scale (Ionian mode) - 7 notes
26
+ - `:minor` - Natural minor scale (Aeolian mode) - 7 notes
27
+ - `:minor_harmonic` - Harmonic minor scale (raised 7th) - 7 notes
28
+ - `:major_harmonic` - Harmonic major scale (lowered 6th) - 7 notes
29
+ - `:chromatic` - Chromatic scale (all 12 semitones) - 12 notes
30
+
31
+ *Greek/church modes:*
32
+ - `:dorian` - Dorian mode (minor with major 6th) - 7 notes
33
+ - `:phrygian` - Phrygian mode (minor with minor 2nd) - 7 notes
34
+ - `:lydian` - Lydian mode (major with augmented 4th) - 7 notes
35
+ - `:mixolydian` - Mixolydian mode (major with minor 7th) - 7 notes
36
+ - `:locrian` - Locrian mode (diminished 5th and minor 2nd) - 7 notes
37
+
38
+ *Pentatonic scales:*
39
+ - `:pentatonic_major` - Major pentatonic (no 4th or 7th) - 5 notes
40
+ - `:pentatonic_minor` - Minor pentatonic (no 2nd or 6th) - 5 notes
41
+
42
+ *Blues scales:*
43
+ - `:blues` - Blues scale (minor pentatonic + b5 blue note) - 6 notes
44
+ - `:blues_major` - Major blues scale (major pentatonic + b3 blue note) - 6 notes
45
+
46
+ *Symmetric scales:*
47
+ - `:whole_tone` - Whole tone scale (all whole steps) - 6 notes
48
+ - `:diminished_hw` - Diminished half-whole (octatonic) - 8 notes
49
+ - `:diminished_wh` - Diminished whole-half (dominant diminished) - 8 notes
50
+
51
+ *Melodic minor modes:*
52
+ - `:minor_melodic` - Melodic minor (jazz minor) - 7 notes
53
+ - `:dorian_b2` - Dorian b2 / Phrygian #6 - 7 notes
54
+ - `:lydian_augmented` - Lydian augmented (#4, #5) - 7 notes
55
+ - `:lydian_dominant` - Lydian dominant / Bartók scale (#4, b7) - 7 notes
56
+ - `:mixolydian_b6` - Mixolydian b6 / Hindu scale - 7 notes
57
+ - `:locrian_sharp2` - Locrian #2 / Half-diminished scale - 7 notes
58
+ - `:altered` - Altered / Super Locrian (all tensions altered) - 7 notes
59
+
60
+ *Ethnic/exotic scales:*
61
+ - `:double_harmonic` - Double harmonic / Byzantine (two augmented 2nds) - 7 notes
62
+ - `:hungarian_minor` - Hungarian minor / Gypsy minor (#4, raised 7th) - 7 notes
63
+ - `:phrygian_dominant` - Phrygian dominant / Spanish Phrygian (b2, major 3rd) - 7 notes
64
+ - `:neapolitan_minor` - Neapolitan minor (harmonic minor with b2) - 7 notes
65
+ - `:neapolitan_major` - Neapolitan major (melodic minor with b2) - 7 notes
66
+
67
+ *Bebop scales:*
68
+ - `:bebop_dominant` - Bebop dominant (Mixolydian + major 7th passing) - 8 notes
69
+ - `:bebop_major` - Bebop major (major + #5 passing) - 8 notes
70
+ - `:bebop_minor` - Bebop minor (Dorian + major 7th passing) - 8 notes
71
+
72
+ ## Chords System
73
+
74
+ The **Chords** module provides chord structures with scale context:
75
+
76
+ **Architecture:**
77
+ - **Chord** (`Musa::Chords::Chord`): Instantiated chord with root note and scale context
78
+ - **ChordDefinition** (`Musa::Chords::ChordDefinition`): Abstract chord structure definition (quality, size, intervals)
79
+
80
+ **Features:**
81
+ - Access chord tones by name (root, third, fifth, seventh, etc.)
82
+ - Voicing modifications (move, duplicate, octave)
83
+ - Navigate between related chords (change quality, add extensions)
84
+ - Extract pitches and notes
85
+
86
+ **Usage examples:**
87
+
88
+ ```ruby
89
+ require 'musa-dsl'
90
+
91
+ include Musa::Scales
92
+ include Musa::Chords
93
+
94
+ # Access default system and tuning
95
+ tuning = Scales.default_system.default_tuning # A=440Hz
96
+
97
+ # Create scales using available scale kinds
98
+ c_major = tuning.major[60] # C major (tonic pitch 60)
99
+ d_minor = tuning.minor[62] # D minor (natural)
100
+ e_harmonic = tuning.minor_harmonic[64] # E harmonic minor
101
+ chromatic = tuning.chromatic[60] # C chromatic
102
+
103
+ # Alternative access methods
104
+ c_major = Scales.et12[440.0].major[60] # Explicit system and frequency
105
+
106
+ # Access notes by grade (0-based) or function
107
+ tonic = c_major[0] # => C (grade 0)
108
+ mediant = c_major[2] # => E (grade 2)
109
+ dominant = c_major[4] # => G (grade 4)
110
+
111
+ # Access by function name
112
+ tonic = c_major.tonic # => C
113
+ supertonic = c_major.supertonic # => D
114
+ mediant = c_major.mediant # => E
115
+ subdominant = c_major.subdominant # => F
116
+ dominant = c_major.dominant # => G
117
+
118
+ # Access by Roman numeral or symbol
119
+ c_major[:I] # => Tonic (C)
120
+ c_major[:V] # => Dominant (G)
121
+
122
+ # Get pitch values from notes
123
+ pitch = c_major.tonic.pitch # => 60
124
+
125
+ # Navigate with octaves
126
+ note = c_major[2].octave(1) # E in octave 1
127
+ pitch_with_octave = note.pitch # => 76
128
+
129
+ # Chromatic operations - sharp and flat
130
+ c_sharp = c_major.tonic.sharp # => C# (chromatic, +1 semitone)
131
+ c_flat = c_major.tonic.flat # => Cb (chromatic, -1 semitone)
132
+
133
+ # Navigate by semitones
134
+ fifth_up = c_major.tonic.sharp(7) # => G (+7 semitones = perfect fifth)
135
+ third_up = c_major.tonic.sharp(4) # => E (+4 semitones = major third)
136
+
137
+ # Frequency calculation
138
+ frequency = c_major.tonic.frequency # => 261.63 Hz (middle C at A=440)
139
+
140
+ # Greek modes (church modes)
141
+ d_dorian = tuning.dorian[62] # D Dorian (minor with major 6th)
142
+ e_phrygian = tuning.phrygian[64] # E Phrygian (minor with minor 2nd)
143
+ f_lydian = tuning.lydian[65] # F Lydian (major with augmented 4th)
144
+ g_mixolydian = tuning.mixolydian[67] # G Mixolydian (major with minor 7th)
145
+ b_locrian = tuning.locrian[71] # B Locrian (diminished 5th)
146
+
147
+ # Access notes in Greek modes by function
148
+ d_dorian.tonic.pitch # => 62 (D)
149
+ d_dorian[:vi].pitch # => 71 (B - the major 6th characteristic of Dorian)
150
+
151
+ e_phrygian[:ii].pitch # => 65 (F - the minor 2nd characteristic of Phrygian)
152
+
153
+ f_lydian[:IV].pitch # => 71 (B - the augmented 4th characteristic of Lydian)
154
+
155
+ g_mixolydian[:VII].pitch # => 77 (F - the minor 7th characteristic of Mixolydian)
156
+
157
+ b_locrian[:v].pitch # => 77 (F - the diminished 5th characteristic of Locrian)
158
+
159
+ # Pentatonic and blues scales
160
+ c_pent_maj = tuning.pentatonic_major[60] # C major pentatonic
161
+ a_pent_min = tuning.pentatonic_minor[69] # A minor pentatonic
162
+ a_blues = tuning.blues[69] # A blues scale
163
+ a_blues[:blue].pitch # => 75 (Eb - the blue note)
164
+
165
+ # Symmetric scales
166
+ c_whole = tuning.whole_tone[60] # C whole tone
167
+ c_dim_hw = tuning.diminished_hw[60] # C diminished (half-whole)
168
+ c_dim_wh = tuning.diminished_wh[60] # C diminished (whole-half)
169
+
170
+ # Melodic minor modes
171
+ c_mel_min = tuning.minor_melodic[60] # C melodic minor
172
+ g_altered = tuning.altered[67] # G altered (for G7alt chords)
173
+ f_lyd_dom = tuning.lydian_dominant[65] # F lydian dominant (F7#11)
174
+
175
+ # Ethnic scales
176
+ e_phry_dom = tuning.phrygian_dominant[64] # E Phrygian dominant (flamenco)
177
+ a_hung_min = tuning.hungarian_minor[69] # A Hungarian minor
178
+
179
+ # Bebop scales (8 notes for smooth eighth-note lines)
180
+ g_bebop = tuning.bebop_dominant[67] # G bebop dominant
181
+ g_bebop[7].pitch # => 78 (F# - the chromatic passing tone)
182
+
183
+ # Create chords from scale degrees
184
+ i_chord = c_major.tonic.chord # C major triad [C, E, G]
185
+ ii_chord = c_major.supertonic.chord # D minor triad [D, F, A]
186
+ v_chord = c_major.dominant.chord # G major triad [G, B, D]
187
+
188
+ # Create extended chords
189
+ i_seventh = c_major.tonic.chord :seventh # C major 7th [C, E, G, B]
190
+ v_ninth = c_major.dominant.chord :ninth # G 9th chord
191
+
192
+ # Access chord tones by name
193
+ root = i_chord.root # => C (NoteInScale)
194
+ third = i_chord.third # => E (NoteInScale)
195
+ fifth = i_chord.fifth # => G (NoteInScale)
196
+
197
+ # Get chord pitches
198
+ pitches = i_chord.pitches # => [60, 64, 67] (C, E, G)
199
+ notes = i_chord.notes # Array of ChordGradeNote structs
200
+
201
+ # Chord features
202
+ i_chord.quality # => :major
203
+ i_chord.size # => :triad
204
+
205
+ # Navigate between chord qualities
206
+ minor_chord = i_chord.with_quality(:minor) # C minor [C, Eb, G]
207
+ diminished = i_chord.with_quality(:diminished) # C diminished [C, Eb, Gb]
208
+
209
+ # Change chord extensions
210
+ seventh_chord = i_chord.with_size(:seventh) # C major 7th
211
+ ninth_chord = i_chord.with_size(:ninth) # C major 9th
212
+
213
+ # Voicing modifications - move specific tones to different octaves
214
+ voiced = i_chord.move(root: -1, fifth: 1) # Root down, fifth up
215
+
216
+ # Duplicate tones in other octaves
217
+ doubled = i_chord.duplicate(root: -2, third: [-1, 1]) # Root 2 down, third 1 down and 1 up
218
+
219
+ # Transpose entire chord
220
+ lower = i_chord.octave(-1) # Move chord down one octave
221
+ ```
222
+
223
+ ### Chord-Scale Navigation
224
+
225
+ MusaDSL provides methods to explore the relationship between chords and scales,
226
+ enabling harmonic analysis and discovery of functional contexts.
227
+
228
+ #### Checking if a Scale Contains a Chord
229
+
230
+ ```ruby
231
+ c_major = Scales.et12[440.0].major[60]
232
+ g7 = c_major.dominant.chord :seventh
233
+
234
+ c_major.contains_chord?(g7) # => true
235
+ c_major.degree_of_chord(g7) # => 4 (V degree, 0-based)
236
+
237
+ # Non-diatonic chords return false/nil
238
+ cm = c_major.tonic.chord.with_quality(:minor) # C minor (Eb not in C major)
239
+ c_major.contains_chord?(cm) # => false
240
+ c_major.degree_of_chord(cm) # => nil
241
+ ```
242
+
243
+ #### Creating a Chord in a Different Scale Context
244
+
245
+ ```ruby
246
+ # Get the same chord but with a different scale as context
247
+ g_mixolydian = Scales.et12[440.0].mixolydian[67]
248
+ g7_in_mixolydian = g_mixolydian.chord_on(g7)
249
+ g7_in_mixolydian.scale # => G Mixolydian scale
250
+ g_mixolydian.degree_of_chord(g7_in_mixolydian) # => 0 (I degree)
251
+ ```
252
+
253
+ #### Finding Scales That Contain a Chord
254
+
255
+ ```ruby
256
+ g_triad = c_major.dominant.chord # G-B-D
257
+
258
+ # Search in diatonic scales
259
+ g_triad.in_scales(family: :diatonic)
260
+ # => [Chord in C major (V), Chord in G major (I), Chord in D major (IV), ...]
261
+
262
+ # Search using metadata filters
263
+ g_triad.in_scales(family: :greek_modes, brightness: -1..1)
264
+
265
+ # Search in all scale types
266
+ g_triad.in_scales
267
+
268
+ # Each result has its scale context
269
+ g_triad.in_scales(family: :diatonic).each do |chord|
270
+ scale = chord.scale
271
+ degree = scale.degree_of_chord(chord)
272
+ puts "#{scale.kind.class.id} rooted on #{scale.root_pitch}: degree #{degree}"
273
+ end
274
+ # Output:
275
+ # major rooted on 60: degree 4
276
+ # major rooted on 67: degree 0
277
+ # major rooted on 62: degree 3
278
+ ```
279
+
280
+ #### Low-Level Navigation Methods
281
+
282
+ ```ruby
283
+ tuning = Scales.et12[440.0]
284
+
285
+ # Search at ScaleKind level
286
+ tuning.major.scales_containing(g_triad)
287
+
288
+ # Search at ScaleSystemTuning level with metadata filters
289
+ tuning.chords_of(g7, family: :diatonic, roots: 60..71)
290
+ ```
291
+
292
+ ### Scale Kind Metadata
293
+
294
+ Scale kinds in MusaDSL have a three-layer metadata system that provides
295
+ both automatic structural information and extensible custom properties.
296
+
297
+ #### Metadata Layers
298
+
299
+ 1. **Intrinsic metadata**: Automatically derived from scale structure
300
+ - `id`: Scale kind identifier
301
+ - `grades`: Number of scale degrees
302
+ - `pitches`: Array of pitch offsets from root
303
+ - `intervals`: Intervals between consecutive degrees
304
+ - `has_leading_tone`: Whether scale has pitch 11 (semitone below octave)
305
+ - `has_tritone`: Whether scale contains tritone interval (pitch 6)
306
+ - `symmetric`: Type of symmetry if any (`:equal`, `:palindrome`, `:repeating`)
307
+
308
+ 2. **Base metadata**: Defined by musa-dsl library
309
+ - `family`: Scale family (`:diatonic`, `:greek_modes`, `:pentatonic`, etc.)
310
+ - `brightness`: Relative brightness (-3 to +3, major = 0)
311
+ - `character`: Array of descriptive tags
312
+ - `parent`: Parent scale and degree for modes (e.g., `{ scale: :major, degree: 2 }`)
313
+
314
+ 3. **Custom metadata**: Added by users at runtime
315
+
316
+ #### Accessing Metadata
317
+
318
+ ```ruby
319
+ tuning = Scales.et12[440.0]
320
+ major_class = tuning.major.class
321
+
322
+ # Get combined metadata (intrinsic < base < custom)
323
+ major_class.metadata
324
+ # => { id: :major, grades: 7, pitches: [0,2,4,5,7,9,11], family: :diatonic, ... }
325
+
326
+ # Get specific layer
327
+ major_class.intrinsic_metadata # Structure-derived only
328
+ major_class.base_metadata # Library-defined only
329
+ major_class.custom_metadata # User-added only
330
+
331
+ # Query helpers
332
+ major_class.has_metadata?(:family) # => true
333
+ major_class.has_metadata?(:family, :diatonic) # => true
334
+ major_class.has_metadata?(:character, :bright) # => true (array inclusion)
335
+ major_class.metadata_value(:brightness) # => 0
336
+ ```
337
+
338
+ #### Extending Metadata
339
+
340
+ Users can add custom metadata to any scale kind:
341
+
342
+ ```ruby
343
+ # Extend a specific scale kind class
344
+ tuning.dorian.class.extend_metadata(
345
+ my_mood: :nostalgic,
346
+ suitable_for: [:jazz, :fusion],
347
+ personal_rating: 5
348
+ )
349
+
350
+ # Or use the convenience method with scale ID
351
+ Scales.extend_metadata(:dorian, my_mood: :nostalgic)
352
+
353
+ # Multiple calls merge metadata
354
+ Scales.extend_metadata(:phrygian, mood: :dark)
355
+ Scales.extend_metadata(:phrygian, origin: :spanish) # Merges with previous
356
+
357
+ # Reset custom metadata if needed
358
+ tuning.dorian.class.reset_custom_metadata
359
+ ```
360
+
361
+ #### Custom Metadata Overrides
362
+
363
+ Custom metadata takes precedence over base metadata:
364
+
365
+ ```ruby
366
+ # Override library-defined family
367
+ Scales.extend_metadata(:dorian, family: :my_custom_category)
368
+ tuning.dorian.class.metadata[:family] # => :my_custom_category
369
+ ```
370
+
371
+ #### Brightness Scale Reference
372
+
373
+ | Value | Meaning | Examples |
374
+ |-------|---------|----------|
375
+ | +3 | Very bright | Lydian augmented |
376
+ | +2 | Bright | Lydian |
377
+ | +1 | Slightly bright | Mixolydian, Lydian dominant |
378
+ | 0 | Neutral (reference) | Major (Ionian) |
379
+ | -1 | Slightly dark | Dorian |
380
+ | -2 | Dark | Minor harmonic, Phrygian |
381
+ | -3 | Very dark | Locrian, Natural minor |
382
+
383
+ #### Scale Families
384
+
385
+ - `:diatonic` - Major, minor natural, minor harmonic
386
+ - `:greek_modes` - Dorian, Phrygian, Lydian, Mixolydian, Locrian
387
+ - `:melodic_minor_modes` - Melodic minor and its modes
388
+ - `:pentatonic` - Pentatonic major/minor
389
+ - `:blues` - Blues scales
390
+ - `:bebop` - Bebop scales
391
+ - `:symmetric` - Whole tone, diminished
392
+ - `:ethnic` - Hungarian, Spanish, Neapolitan, etc.
393
+ - `:chromatic` - Chromatic scale
394
+
395
+ ### Searching Scale Kinds
396
+
397
+ The `scale_kinds` method on ScaleSystemTuning allows searching and filtering
398
+ scale kinds by their metadata properties.
399
+
400
+ #### Basic Usage
401
+
402
+ ```ruby
403
+ tuning = Scales.et12[440.0]
404
+
405
+ # Get all scale kinds
406
+ tuning.scale_kinds
407
+ # => [major_kind, minor_kind, dorian_kind, ...]
408
+
409
+ # Filter by family
410
+ tuning.scale_kinds(family: :diatonic)
411
+ # => [major_kind, minor_kind, minor_harmonic_kind, harmonic_major_kind]
412
+
413
+ # Filter by brightness range
414
+ tuning.scale_kinds(brightness: -1..1)
415
+
416
+ # Filter by character
417
+ tuning.scale_kinds(character: :jazz)
418
+ ```
419
+
420
+ #### Custom Filtering with Blocks
421
+
422
+ Use a block for complex filtering based on any metadata:
423
+
424
+ ```ruby
425
+ # Scales with leading tone
426
+ tuning.scale_kinds { |klass| klass.intrinsic_metadata[:has_leading_tone] }
427
+
428
+ # Combine criteria and block
429
+ tuning.scale_kinds(family: :greek_modes) { |klass| klass.metadata[:brightness]&.negative? }
430
+ # => [dorian_kind, phrygian_kind, locrian_kind]
431
+ ```
432
+
433
+ #### Integration with Chord Search
434
+
435
+ The `chords_of` method uses the same metadata filtering:
436
+
437
+ ```ruby
438
+ g7 = tuning.major[60].dominant.chord(:seventh)
439
+
440
+ # Find G7 in diatonic scales
441
+ tuning.chords_of(g7, family: :diatonic)
442
+
443
+ # Find G7 in scales with specific brightness
444
+ tuning.chords_of(g7, brightness: -1..1)
445
+ ```
446
+
447
+ ## Defining Custom Scale Systems, Scale Kinds, and Chord Definitions
448
+
449
+ The framework is extensible, allowing users to define custom tuning systems, scale types, and chord structures:
450
+
451
+ **Custom Scale Systems:**
452
+
453
+ Users can create custom tuning systems by subclassing `Musa::Scales::ScaleSystem` and implementing:
454
+ - `.id` - Unique symbol identifier
455
+ - `.notes_in_octave` - Number of notes per octave
456
+ - `.part_of_tone_size` - Size of smallest pitch unit
457
+ - `.intervals` - Hash mapping interval names to semitone offsets
458
+ - `.frequency_of_pitch(pitch, root_pitch, a_frequency)` - Pitch to frequency conversion
459
+
460
+ After defining, register with `Musa::Scales::Scales.register(CustomScaleSystem, default: false)`
461
+
462
+ **Custom Scale Kinds:**
463
+
464
+ Users can define new scale types by subclassing `Musa::Scales::ScaleKind` and implementing:
465
+ - `.id` - Unique symbol identifier (e.g., `:dorian`, `:pentatonic`)
466
+ - `.pitches` - Array defining scale structure with functions and pitch offsets
467
+ - `.chromatic?` - Whether this is the chromatic scale (optional, default: false)
468
+ - `.grades` - Number of grades per octave (optional, default: pitches.length)
469
+
470
+ After defining, register with `YourScaleSystem.register(CustomScaleKind)`
471
+
472
+ **Custom Chord Definitions:**
473
+
474
+ Users can register new chord types using `Musa::Chords::ChordDefinition.register`:
475
+ - `name` - Symbol identifier (e.g., `:sus4`, `:add9`)
476
+ - `offsets:` - Hash defining semitone intervals from root (e.g., `{ root: 0, fourth: 5, fifth: 7 }`)
477
+ - `**features` - Chord characteristics like `quality:` and `size:`
478
+
479
+ **Examples of custom definitions:**
480
+
481
+ ```ruby
482
+ require 'musa-dsl'
483
+
484
+ include Musa::Scales
485
+ include Musa::Chords
486
+
487
+ # Example 1: Define a custom Hirajoshi scale (Japanese pentatonic)
488
+ class HirajoshiScaleKind < ScaleKind
489
+ class << self
490
+ def id
491
+ :hirajoshi
492
+ end
493
+
494
+ def pitches
495
+ [{ functions: [:I, :_1, :tonic], pitch: 0 },
496
+ { functions: [:II, :_2], pitch: 2 },
497
+ { functions: [:III, :_3], pitch: 3 },
498
+ { functions: [:V, :_4], pitch: 7 },
499
+ { functions: [:VI, :_5], pitch: 8 }]
500
+ end
501
+
502
+ def grades
503
+ 5 # 5 notes per octave
504
+ end
505
+ end
506
+ end
507
+
508
+ # Register the new scale kind with the 12-tone system
509
+ Scales.et12.register(HirajoshiScaleKind)
510
+
511
+ # Use the new scale kind
512
+ tuning = Scales.default_system.default_tuning
513
+ c_hirajoshi = tuning[:hirajoshi][60] # C Hirajoshi
514
+ puts c_hirajoshi[0].pitch # => 60 (C)
515
+ puts c_hirajoshi[1].pitch # => 62 (D)
516
+ puts c_hirajoshi[2].pitch # => 63 (Eb)
517
+
518
+ # Example 2: Register a custom chord definition (sus4)
519
+ Musa::Chords::ChordDefinition.register :sus4,
520
+ quality: :suspended,
521
+ size: :triad,
522
+ offsets: { root: 0, fourth: 5, fifth: 7 }
523
+
524
+ # Use the custom chord definition
525
+ c_major = tuning.major[60]
526
+ # To use custom chords, access via NoteInScale#chord with the definition name
527
+ # or create manually using the definition
528
+
529
+ # Example 3: Register a custom chord definition (add9)
530
+ Musa::Chords::ChordDefinition.register :add9,
531
+ quality: :major,
532
+ size: :extended,
533
+ offsets: { root: 0, third: 4, fifth: 7, ninth: 14 }
534
+ ```
535
+
536
+ ## API Reference
537
+
538
+ **Complete API documentation:**
539
+ - [Musa::Scales](https://rubydoc.info/gems/musa-dsl/Musa/Scales) - Scale systems and tuning
540
+ - [Musa::Chords](https://rubydoc.info/gems/musa-dsl/Musa/Chords) - Chord structures and navigation
541
+
542
+ **Source code:** `lib/music/`
543
+
544
+