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.
- checksums.yaml +4 -4
- data/.gitignore +5 -1
- data/.version +6 -0
- data/.yardopts +7 -0
- data/Gemfile +0 -1
- data/README.md +227 -6
- data/docs/README.md +83 -0
- data/docs/api-reference.md +86 -0
- data/docs/getting-started/quick-start.md +93 -0
- data/docs/getting-started/tutorial.md +58 -0
- data/docs/subsystems/core-extensions.md +316 -0
- data/docs/subsystems/datasets.md +465 -0
- data/docs/subsystems/generative.md +290 -0
- data/docs/subsystems/matrix.md +63 -0
- data/docs/subsystems/midi.md +123 -0
- data/docs/subsystems/music.md +544 -0
- data/docs/subsystems/musicxml-builder.md +264 -0
- data/docs/subsystems/neumas.md +71 -0
- data/docs/subsystems/repl.md +135 -0
- data/docs/subsystems/sequencer.md +98 -0
- data/docs/subsystems/series.md +302 -0
- data/docs/subsystems/transcription.md +152 -0
- data/docs/subsystems/transport.md +177 -0
- data/lib/musa-dsl/core-ext/array-explode-ranges.rb +68 -0
- data/lib/musa-dsl/core-ext/arrayfy.rb +110 -0
- data/lib/musa-dsl/core-ext/attribute-builder.rb +91 -30
- data/lib/musa-dsl/core-ext/deep-copy.rb +125 -2
- data/lib/musa-dsl/core-ext/dynamic-proxy.rb +78 -0
- data/lib/musa-dsl/core-ext/extension.rb +53 -0
- data/lib/musa-dsl/core-ext/hashify.rb +162 -1
- data/lib/musa-dsl/core-ext/inspect-nice.rb +154 -0
- data/lib/musa-dsl/core-ext/smart-proc-binder.rb +117 -0
- data/lib/musa-dsl/core-ext/with.rb +114 -0
- data/lib/musa-dsl/datasets/dataset.rb +109 -0
- data/lib/musa-dsl/datasets/delta-d.rb +78 -0
- data/lib/musa-dsl/datasets/e.rb +186 -2
- data/lib/musa-dsl/datasets/gdv.rb +279 -2
- data/lib/musa-dsl/datasets/gdvd.rb +201 -0
- data/lib/musa-dsl/datasets/helper.rb +75 -0
- data/lib/musa-dsl/datasets/p.rb +177 -2
- data/lib/musa-dsl/datasets/packed-v.rb +91 -0
- data/lib/musa-dsl/datasets/pdv.rb +136 -1
- data/lib/musa-dsl/datasets/ps.rb +134 -4
- data/lib/musa-dsl/datasets/score/queriable.rb +143 -1
- data/lib/musa-dsl/datasets/score/render.rb +105 -1
- data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +138 -1
- data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +111 -0
- data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +200 -1
- data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +145 -1
- data/lib/musa-dsl/datasets/score.rb +279 -0
- data/lib/musa-dsl/datasets/v.rb +88 -0
- data/lib/musa-dsl/generative/darwin.rb +215 -1
- data/lib/musa-dsl/generative/generative-grammar.rb +387 -0
- data/lib/musa-dsl/generative/markov.rb +135 -3
- data/lib/musa-dsl/generative/rules.rb +312 -4
- data/lib/musa-dsl/generative/variatio.rb +286 -2
- data/lib/musa-dsl/logger/logger.rb +267 -2
- data/lib/musa-dsl/matrix/matrix.rb +256 -10
- data/lib/musa-dsl/midi/midi-recorder.rb +113 -2
- data/lib/musa-dsl/midi/midi-voices.rb +275 -4
- data/lib/musa-dsl/music/chord-definition.rb +233 -1
- data/lib/musa-dsl/music/chord-definitions.rb +33 -6
- data/lib/musa-dsl/music/chords.rb +353 -2
- data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +70 -206
- data/lib/musa-dsl/music/scale_kinds/bebop/bebop_dominant_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/bebop/bebop_major_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/bebop/bebop_minor_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/blues/blues_major_scale_kind.rb +100 -0
- data/lib/musa-dsl/music/scale_kinds/blues/blues_scale_kind.rb +99 -0
- data/lib/musa-dsl/music/scale_kinds/chromatic_scale_kind.rb +79 -0
- data/lib/musa-dsl/music/scale_kinds/ethnic/double_harmonic_scale_kind.rb +102 -0
- data/lib/musa-dsl/music/scale_kinds/ethnic/hungarian_minor_scale_kind.rb +102 -0
- data/lib/musa-dsl/music/scale_kinds/ethnic/neapolitan_major_scale_kind.rb +102 -0
- data/lib/musa-dsl/music/scale_kinds/ethnic/neapolitan_minor_scale_kind.rb +101 -0
- data/lib/musa-dsl/music/scale_kinds/ethnic/phrygian_dominant_scale_kind.rb +103 -0
- data/lib/musa-dsl/music/scale_kinds/harmonic_major/harmonic_major_scale_kind.rb +104 -0
- data/lib/musa-dsl/music/scale_kinds/major_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/altered_scale_kind.rb +106 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/dorian_b2_scale_kind.rb +104 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/locrian_sharp2_scale_kind.rb +103 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/lydian_augmented_scale_kind.rb +103 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/lydian_dominant_scale_kind.rb +106 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/melodic_minor_scale_kind.rb +104 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/mixolydian_b6_scale_kind.rb +103 -0
- data/lib/musa-dsl/music/scale_kinds/minor_harmonic_scale_kind.rb +125 -0
- data/lib/musa-dsl/music/scale_kinds/minor_natural_scale_kind.rb +123 -0
- data/lib/musa-dsl/music/scale_kinds/modes/dorian_scale_kind.rb +111 -0
- data/lib/musa-dsl/music/scale_kinds/modes/locrian_scale_kind.rb +114 -0
- data/lib/musa-dsl/music/scale_kinds/modes/lydian_scale_kind.rb +111 -0
- data/lib/musa-dsl/music/scale_kinds/modes/mixolydian_scale_kind.rb +111 -0
- data/lib/musa-dsl/music/scale_kinds/modes/phrygian_scale_kind.rb +111 -0
- data/lib/musa-dsl/music/scale_kinds/pentatonic/pentatonic_major_scale_kind.rb +93 -0
- data/lib/musa-dsl/music/scale_kinds/pentatonic/pentatonic_minor_scale_kind.rb +99 -0
- data/lib/musa-dsl/music/scale_kinds/symmetric/diminished_hw_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/symmetric/diminished_wh_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/symmetric/whole_tone_scale_kind.rb +99 -0
- data/lib/musa-dsl/music/scale_systems/equally_tempered_12_tone_scale_system.rb +80 -0
- data/lib/musa-dsl/music/scale_systems/twelve_semitones_scale_system.rb +60 -0
- data/lib/musa-dsl/music/scales.rb +1384 -40
- data/lib/musa-dsl/musicxml/builder/attributes.rb +483 -3
- data/lib/musa-dsl/musicxml/builder/backup-forward.rb +166 -1
- data/lib/musa-dsl/musicxml/builder/direction.rb +243 -0
- data/lib/musa-dsl/musicxml/builder/helper.rb +240 -0
- data/lib/musa-dsl/musicxml/builder/measure.rb +284 -0
- data/lib/musa-dsl/musicxml/builder/note-complexities.rb +324 -8
- data/lib/musa-dsl/musicxml/builder/note.rb +285 -0
- data/lib/musa-dsl/musicxml/builder/part-group.rb +108 -1
- data/lib/musa-dsl/musicxml/builder/part.rb +139 -0
- data/lib/musa-dsl/musicxml/builder/pitched-note.rb +124 -0
- data/lib/musa-dsl/musicxml/builder/rest.rb +93 -0
- data/lib/musa-dsl/musicxml/builder/score-partwise.rb +276 -0
- data/lib/musa-dsl/musicxml/builder/typed-text.rb +62 -1
- data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +83 -0
- data/lib/musa-dsl/neumalang/neumalang.rb +675 -0
- data/lib/musa-dsl/neumas/array-to-neumas.rb +149 -0
- data/lib/musa-dsl/neumas/neuma-decoder.rb +253 -0
- data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +142 -2
- data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +82 -0
- data/lib/musa-dsl/neumas/neumas.rb +67 -0
- data/lib/musa-dsl/neumas/string-to-neumas.rb +233 -1
- data/lib/musa-dsl/repl/repl.rb +550 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +118 -2
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +149 -2
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +296 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +88 -2
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +161 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +263 -0
- data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +173 -1
- data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +177 -0
- data/lib/musa-dsl/sequencer/base-sequencer.rb +710 -10
- data/lib/musa-dsl/sequencer/sequencer-dsl.rb +210 -0
- data/lib/musa-dsl/sequencer/timeslots.rb +79 -0
- data/lib/musa-dsl/series/array-to-serie.rb +37 -1
- data/lib/musa-dsl/series/base-series.rb +843 -5
- data/lib/musa-dsl/series/buffer-serie.rb +54 -0
- data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +64 -0
- data/lib/musa-dsl/series/main-serie-constructors.rb +398 -2
- data/lib/musa-dsl/series/main-serie-operations.rb +538 -16
- data/lib/musa-dsl/series/proxy-serie.rb +67 -0
- data/lib/musa-dsl/series/quantizer-serie.rb +57 -7
- data/lib/musa-dsl/series/queue-serie.rb +78 -0
- data/lib/musa-dsl/series/series-composer.rb +701 -0
- data/lib/musa-dsl/series/timed-serie.rb +473 -28
- data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +404 -1
- data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +118 -0
- data/lib/musa-dsl/transcription/from-gdv.rb +84 -1
- data/lib/musa-dsl/transcription/transcription.rb +265 -0
- data/lib/musa-dsl/transport/clock.rb +125 -0
- data/lib/musa-dsl/transport/dummy-clock.rb +89 -2
- data/lib/musa-dsl/transport/external-tick-clock.rb +91 -0
- data/lib/musa-dsl/transport/input-midi-clock.rb +133 -1
- data/lib/musa-dsl/transport/timer-clock.rb +183 -1
- data/lib/musa-dsl/transport/timer.rb +83 -0
- data/lib/musa-dsl/transport/transport.rb +318 -0
- data/lib/musa-dsl/version.rb +2 -1
- data/lib/musa-dsl.rb +132 -25
- data/musa-dsl.gemspec +25 -18
- 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
|
+
|