musa-dsl 0.30.2 → 0.40.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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.version +6 -0
  4. data/.yardopts +7 -0
  5. data/README.md +227 -6
  6. data/docs/README.md +83 -0
  7. data/docs/api-reference.md +86 -0
  8. data/docs/getting-started/quick-start.md +93 -0
  9. data/docs/getting-started/tutorial.md +58 -0
  10. data/docs/subsystems/core-extensions.md +316 -0
  11. data/docs/subsystems/datasets.md +465 -0
  12. data/docs/subsystems/generative.md +290 -0
  13. data/docs/subsystems/matrix.md +63 -0
  14. data/docs/subsystems/midi.md +123 -0
  15. data/docs/subsystems/music.md +233 -0
  16. data/docs/subsystems/musicxml-builder.md +264 -0
  17. data/docs/subsystems/neumas.md +71 -0
  18. data/docs/subsystems/repl.md +135 -0
  19. data/docs/subsystems/sequencer.md +98 -0
  20. data/docs/subsystems/series.md +302 -0
  21. data/docs/subsystems/transcription.md +152 -0
  22. data/docs/subsystems/transport.md +177 -0
  23. data/lib/musa-dsl/core-ext/array-explode-ranges.rb +68 -0
  24. data/lib/musa-dsl/core-ext/arrayfy.rb +110 -0
  25. data/lib/musa-dsl/core-ext/attribute-builder.rb +91 -30
  26. data/lib/musa-dsl/core-ext/deep-copy.rb +125 -2
  27. data/lib/musa-dsl/core-ext/dynamic-proxy.rb +78 -0
  28. data/lib/musa-dsl/core-ext/extension.rb +53 -0
  29. data/lib/musa-dsl/core-ext/hashify.rb +162 -1
  30. data/lib/musa-dsl/core-ext/inspect-nice.rb +154 -0
  31. data/lib/musa-dsl/core-ext/smart-proc-binder.rb +117 -0
  32. data/lib/musa-dsl/core-ext/with.rb +114 -0
  33. data/lib/musa-dsl/datasets/dataset.rb +109 -0
  34. data/lib/musa-dsl/datasets/delta-d.rb +78 -0
  35. data/lib/musa-dsl/datasets/e.rb +186 -2
  36. data/lib/musa-dsl/datasets/gdv.rb +279 -2
  37. data/lib/musa-dsl/datasets/gdvd.rb +201 -0
  38. data/lib/musa-dsl/datasets/helper.rb +75 -0
  39. data/lib/musa-dsl/datasets/p.rb +177 -2
  40. data/lib/musa-dsl/datasets/packed-v.rb +91 -0
  41. data/lib/musa-dsl/datasets/pdv.rb +136 -1
  42. data/lib/musa-dsl/datasets/ps.rb +134 -4
  43. data/lib/musa-dsl/datasets/score/queriable.rb +143 -1
  44. data/lib/musa-dsl/datasets/score/render.rb +105 -1
  45. data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +138 -1
  46. data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +111 -0
  47. data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +200 -1
  48. data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +145 -1
  49. data/lib/musa-dsl/datasets/score.rb +279 -0
  50. data/lib/musa-dsl/datasets/v.rb +88 -0
  51. data/lib/musa-dsl/generative/darwin.rb +180 -1
  52. data/lib/musa-dsl/generative/generative-grammar.rb +359 -0
  53. data/lib/musa-dsl/generative/markov.rb +133 -3
  54. data/lib/musa-dsl/generative/rules.rb +258 -4
  55. data/lib/musa-dsl/generative/variatio.rb +217 -2
  56. data/lib/musa-dsl/logger/logger.rb +267 -2
  57. data/lib/musa-dsl/matrix/matrix.rb +256 -10
  58. data/lib/musa-dsl/midi/midi-recorder.rb +108 -1
  59. data/lib/musa-dsl/midi/midi-voices.rb +265 -4
  60. data/lib/musa-dsl/music/chord-definition.rb +233 -1
  61. data/lib/musa-dsl/music/chord-definitions.rb +33 -6
  62. data/lib/musa-dsl/music/chords.rb +308 -2
  63. data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +315 -0
  64. data/lib/musa-dsl/music/scales.rb +957 -40
  65. data/lib/musa-dsl/musicxml/builder/attributes.rb +483 -3
  66. data/lib/musa-dsl/musicxml/builder/backup-forward.rb +166 -1
  67. data/lib/musa-dsl/musicxml/builder/direction.rb +243 -0
  68. data/lib/musa-dsl/musicxml/builder/helper.rb +240 -0
  69. data/lib/musa-dsl/musicxml/builder/measure.rb +284 -0
  70. data/lib/musa-dsl/musicxml/builder/note-complexities.rb +324 -8
  71. data/lib/musa-dsl/musicxml/builder/note.rb +285 -0
  72. data/lib/musa-dsl/musicxml/builder/part-group.rb +108 -1
  73. data/lib/musa-dsl/musicxml/builder/part.rb +139 -0
  74. data/lib/musa-dsl/musicxml/builder/pitched-note.rb +124 -0
  75. data/lib/musa-dsl/musicxml/builder/rest.rb +93 -0
  76. data/lib/musa-dsl/musicxml/builder/score-partwise.rb +276 -0
  77. data/lib/musa-dsl/musicxml/builder/typed-text.rb +62 -1
  78. data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +83 -0
  79. data/lib/musa-dsl/neumalang/neumalang.rb +675 -0
  80. data/lib/musa-dsl/neumas/array-to-neumas.rb +149 -0
  81. data/lib/musa-dsl/neumas/neuma-decoder.rb +253 -0
  82. data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +142 -2
  83. data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +82 -0
  84. data/lib/musa-dsl/neumas/neumas.rb +67 -0
  85. data/lib/musa-dsl/neumas/string-to-neumas.rb +233 -1
  86. data/lib/musa-dsl/repl/repl.rb +550 -0
  87. data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +118 -2
  88. data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +149 -2
  89. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +296 -0
  90. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +88 -2
  91. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +161 -0
  92. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +263 -0
  93. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +173 -1
  94. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +177 -0
  95. data/lib/musa-dsl/sequencer/base-sequencer.rb +710 -10
  96. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +210 -0
  97. data/lib/musa-dsl/sequencer/timeslots.rb +79 -0
  98. data/lib/musa-dsl/series/array-to-serie.rb +37 -1
  99. data/lib/musa-dsl/series/base-series.rb +843 -5
  100. data/lib/musa-dsl/series/buffer-serie.rb +48 -0
  101. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +41 -0
  102. data/lib/musa-dsl/series/main-serie-constructors.rb +398 -2
  103. data/lib/musa-dsl/series/main-serie-operations.rb +538 -16
  104. data/lib/musa-dsl/series/proxy-serie.rb +67 -0
  105. data/lib/musa-dsl/series/quantizer-serie.rb +45 -7
  106. data/lib/musa-dsl/series/queue-serie.rb +65 -0
  107. data/lib/musa-dsl/series/series-composer.rb +701 -0
  108. data/lib/musa-dsl/series/timed-serie.rb +473 -28
  109. data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +404 -1
  110. data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +118 -0
  111. data/lib/musa-dsl/transcription/from-gdv.rb +84 -1
  112. data/lib/musa-dsl/transcription/transcription.rb +265 -0
  113. data/lib/musa-dsl/transport/clock.rb +125 -0
  114. data/lib/musa-dsl/transport/dummy-clock.rb +89 -2
  115. data/lib/musa-dsl/transport/external-tick-clock.rb +91 -0
  116. data/lib/musa-dsl/transport/input-midi-clock.rb +133 -1
  117. data/lib/musa-dsl/transport/timer-clock.rb +183 -1
  118. data/lib/musa-dsl/transport/timer.rb +83 -0
  119. data/lib/musa-dsl/transport/transport.rb +318 -0
  120. data/lib/musa-dsl/version.rb +1 -1
  121. data/lib/musa-dsl.rb +132 -25
  122. data/musa-dsl.gemspec +12 -10
  123. metadata +87 -8
@@ -0,0 +1,233 @@
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
+ - `:major` - Major scale (Ionian mode)
24
+ - `:minor` - Natural minor scale (Aeolian mode)
25
+ - `:minor_harmonic` - Harmonic minor scale (raised 7th degree)
26
+ - `:chromatic` - Chromatic scale (all 12 semitones)
27
+
28
+ ## Chords System
29
+
30
+ The **Chords** module provides chord structures with scale context:
31
+
32
+ **Architecture:**
33
+ - **Chord** (`Musa::Chords::Chord`): Instantiated chord with root note and scale context
34
+ - **ChordDefinition** (`Musa::Chords::ChordDefinition`): Abstract chord structure definition (quality, size, intervals)
35
+
36
+ **Features:**
37
+ - Access chord tones by name (root, third, fifth, seventh, etc.)
38
+ - Voicing modifications (move, duplicate, octave)
39
+ - Navigate between related chords (change quality, add extensions)
40
+ - Extract pitches and notes
41
+
42
+ **Usage examples:**
43
+
44
+ ```ruby
45
+ require 'musa-dsl'
46
+
47
+ include Musa::Scales
48
+ include Musa::Chords
49
+
50
+ # Access default system and tuning
51
+ tuning = Scales.default_system.default_tuning # A=440Hz
52
+
53
+ # Create scales using available scale kinds
54
+ c_major = tuning.major[60] # C major (tonic pitch 60)
55
+ d_minor = tuning.minor[62] # D minor (natural)
56
+ e_harmonic = tuning.minor_harmonic[64] # E harmonic minor
57
+ chromatic = tuning.chromatic[60] # C chromatic
58
+
59
+ # Alternative access methods
60
+ c_major = Scales.et12[440.0].major[60] # Explicit system and frequency
61
+
62
+ # Access notes by grade (0-based) or function
63
+ tonic = c_major[0] # => C (grade 0)
64
+ mediant = c_major[2] # => E (grade 2)
65
+ dominant = c_major[4] # => G (grade 4)
66
+
67
+ # Access by function name
68
+ tonic = c_major.tonic # => C
69
+ supertonic = c_major.supertonic # => D
70
+ mediant = c_major.mediant # => E
71
+ subdominant = c_major.subdominant # => F
72
+ dominant = c_major.dominant # => G
73
+
74
+ # Access by Roman numeral or symbol
75
+ c_major[:I] # => Tonic (C)
76
+ c_major[:V] # => Dominant (G)
77
+
78
+ # Get pitch values from notes
79
+ pitch = c_major.tonic.pitch # => 60
80
+
81
+ # Navigate with octaves
82
+ note = c_major[2].octave(1) # E in octave 1
83
+ pitch_with_octave = note.pitch # => 76
84
+
85
+ # Chromatic operations - sharp and flat
86
+ c_sharp = c_major.tonic.sharp # => C# (chromatic, +1 semitone)
87
+ c_flat = c_major.tonic.flat # => Cb (chromatic, -1 semitone)
88
+
89
+ # Navigate by semitones
90
+ fifth_up = c_major.tonic.sharp(7) # => G (+7 semitones = perfect fifth)
91
+ third_up = c_major.tonic.sharp(4) # => E (+4 semitones = major third)
92
+
93
+ # Frequency calculation
94
+ frequency = c_major.tonic.frequency # => 261.63 Hz (middle C at A=440)
95
+
96
+ # Create chords from scale degrees
97
+ i_chord = c_major.tonic.chord # C major triad [C, E, G]
98
+ ii_chord = c_major.supertonic.chord # D minor triad [D, F, A]
99
+ v_chord = c_major.dominant.chord # G major triad [G, B, D]
100
+
101
+ # Create extended chords
102
+ i_seventh = c_major.tonic.chord :seventh # C major 7th [C, E, G, B]
103
+ v_ninth = c_major.dominant.chord :ninth # G 9th chord
104
+
105
+ # Access chord tones by name
106
+ root = i_chord.root # => C (NoteInScale)
107
+ third = i_chord.third # => E (NoteInScale)
108
+ fifth = i_chord.fifth # => G (NoteInScale)
109
+
110
+ # Get chord pitches
111
+ pitches = i_chord.pitches # => [60, 64, 67] (C, E, G)
112
+ notes = i_chord.notes # Array of ChordGradeNote structs
113
+
114
+ # Chord features
115
+ i_chord.quality # => :major
116
+ i_chord.size # => :triad
117
+
118
+ # Navigate between chord qualities
119
+ minor_chord = i_chord.with_quality(:minor) # C minor [C, Eb, G]
120
+ diminished = i_chord.with_quality(:diminished) # C diminished [C, Eb, Gb]
121
+
122
+ # Change chord extensions
123
+ seventh_chord = i_chord.with_size(:seventh) # C major 7th
124
+ ninth_chord = i_chord.with_size(:ninth) # C major 9th
125
+
126
+ # Voicing modifications - move specific tones to different octaves
127
+ voiced = i_chord.move(root: -1, fifth: 1) # Root down, fifth up
128
+
129
+ # Duplicate tones in other octaves
130
+ doubled = i_chord.duplicate(root: -2, third: [-1, 1]) # Root 2 down, third 1 down and 1 up
131
+
132
+ # Transpose entire chord
133
+ lower = i_chord.octave(-1) # Move chord down one octave
134
+ ```
135
+
136
+ ## Defining Custom Scale Systems, Scale Kinds, and Chord Definitions
137
+
138
+ The framework is extensible, allowing users to define custom tuning systems, scale types, and chord structures:
139
+
140
+ **Custom Scale Systems:**
141
+
142
+ Users can create custom tuning systems by subclassing `Musa::Scales::ScaleSystem` and implementing:
143
+ - `.id` - Unique symbol identifier
144
+ - `.notes_in_octave` - Number of notes per octave
145
+ - `.part_of_tone_size` - Size of smallest pitch unit
146
+ - `.intervals` - Hash mapping interval names to semitone offsets
147
+ - `.frequency_of_pitch(pitch, root_pitch, a_frequency)` - Pitch to frequency conversion
148
+
149
+ After defining, register with `Musa::Scales::Scales.register(CustomScaleSystem, default: false)`
150
+
151
+ **Custom Scale Kinds:**
152
+
153
+ Users can define new scale types by subclassing `Musa::Scales::ScaleKind` and implementing:
154
+ - `.id` - Unique symbol identifier (e.g., `:dorian`, `:pentatonic`)
155
+ - `.pitches` - Array defining scale structure with functions and pitch offsets
156
+ - `.chromatic?` - Whether this is the chromatic scale (optional, default: false)
157
+ - `.grades` - Number of grades per octave (optional, default: pitches.length)
158
+
159
+ After defining, register with `YourScaleSystem.register(CustomScaleKind)`
160
+
161
+ **Custom Chord Definitions:**
162
+
163
+ Users can register new chord types using `Musa::Chords::ChordDefinition.register`:
164
+ - `name` - Symbol identifier (e.g., `:sus4`, `:add9`)
165
+ - `offsets:` - Hash defining semitone intervals from root (e.g., `{ root: 0, fourth: 5, fifth: 7 }`)
166
+ - `**features` - Chord characteristics like `quality:` and `size:`
167
+
168
+ **Examples of custom definitions:**
169
+
170
+ ```ruby
171
+ require 'musa-dsl'
172
+
173
+ include Musa::Scales
174
+ include Musa::Chords
175
+
176
+ # Example 1: Define a custom pentatonic scale kind for the 12-tone system
177
+ class PentatonicMajorScaleKind < ScaleKind
178
+ class << self
179
+ def id
180
+ :pentatonic_major
181
+ end
182
+
183
+ def pitches
184
+ [{ functions: [:I, :_1, :tonic], pitch: 0 },
185
+ { functions: [:II, :_2], pitch: 2 },
186
+ { functions: [:III, :_3], pitch: 4 },
187
+ { functions: [:V, :_5], pitch: 7 },
188
+ { functions: [:VI, :_6], pitch: 9 }]
189
+ end
190
+
191
+ def grades
192
+ 5 # 5 notes per octave
193
+ end
194
+ end
195
+ end
196
+
197
+ # Register the new scale kind with the 12-tone system
198
+ Scales.et12.register(PentatonicMajorScaleKind)
199
+
200
+ # Use the new scale kind
201
+ tuning = Scales.default_system.default_tuning
202
+ c_pentatonic = tuning[:pentatonic_major][60] # C pentatonic major
203
+ puts c_pentatonic[0].pitch # => 60 (C)
204
+ puts c_pentatonic[1].pitch # => 62 (D)
205
+ puts c_pentatonic[2].pitch # => 64 (E)
206
+
207
+ # Example 2: Register a custom chord definition (sus4)
208
+ Musa::Chords::ChordDefinition.register :sus4,
209
+ quality: :suspended,
210
+ size: :triad,
211
+ offsets: { root: 0, fourth: 5, fifth: 7 }
212
+
213
+ # Use the custom chord definition
214
+ c_major = tuning.major[60]
215
+ # To use custom chords, access via NoteInScale#chord with the definition name
216
+ # or create manually using the definition
217
+
218
+ # Example 3: Register a custom chord definition (add9)
219
+ Musa::Chords::ChordDefinition.register :add9,
220
+ quality: :major,
221
+ size: :extended,
222
+ offsets: { root: 0, third: 4, fifth: 7, ninth: 14 }
223
+ ```
224
+
225
+ ## API Reference
226
+
227
+ **Complete API documentation:**
228
+ - [Musa::Scales](https://rubydoc.info/gems/musa-dsl/Musa/Scales) - Scale systems and tuning
229
+ - [Musa::Chords](https://rubydoc.info/gems/musa-dsl/Musa/Chords) - Chord structures and navigation
230
+
231
+ **Source code:** `lib/music/`
232
+
233
+
@@ -0,0 +1,264 @@
1
+ # MusicXML Builder - Music Notation Export
2
+
3
+ Comprehensive builder for generating MusicXML 3.0 files compatible with music notation software (Finale, Sibelius, MuseScore, Dorico, etc.). MusicXML is the standard open format for exchanging digital sheet music between applications.
4
+
5
+ ## Root Class: ScorePartwise
6
+
7
+ The entry point for creating MusicXML documents is `Musa::MusicXML::Builder::ScorePartwise`, which represents the `<score-partwise>` root element. It organizes music by parts (instruments/voices) and measures.
8
+
9
+ **Structure:**
10
+ - **Metadata**: work info, movement info, creators, rights, encoding date
11
+ - **Part List**: part definitions with names and abbreviations
12
+ - **Parts**: musical content organized by measures
13
+
14
+ ## Key Features
15
+
16
+ **Multiple staves:**
17
+ Use `staff:` parameter to specify which staff (1, 2, etc.) for grand staff notation (piano, harp, organ, etc.).
18
+ ```ruby
19
+ pitch 'C', octave: 3, staff: 2 # Note in staff 2 (bass clef)
20
+ ```
21
+
22
+ **Multiple voices:**
23
+ Use `voice:` parameter for polyphonic notation within a single staff (independent melodic lines).
24
+ ```ruby
25
+ pitch 'C', octave: 4, voice: 1 # Voice 1
26
+ pitch 'E', octave: 3, voice: 2 # Voice 2 (simultaneous)
27
+ ```
28
+
29
+ **Backup/Forward:**
30
+ Navigate timeline within measures to layer voices. `backup(duration)` returns to an earlier point, `forward(duration)` skips ahead.
31
+ ```ruby
32
+ pitch 'C', octave: 4, duration: 4
33
+ backup 4 # Return to beginning
34
+ pitch 'E', octave: 3, duration: 4 # Play simultaneously
35
+ ```
36
+
37
+ **Divisions:**
38
+ Set rhythmic precision as divisions per quarter note in measure attributes. Higher values allow smaller note values.
39
+ ```ruby
40
+ attributes do
41
+ divisions 4 # 4 divisions per quarter (allows 16th notes)
42
+ end
43
+ ```
44
+
45
+ **Alterations:**
46
+ Use `alter:` parameter for accidentals: `-1` for flat, `1` for sharp, `2` for double sharp, etc.
47
+ ```ruby
48
+ pitch 'F', octave: 4, alter: 1 # F# (sharp)
49
+ pitch 'B', octave: 4, alter: -1 # Bb (flat)
50
+ ```
51
+
52
+ **Articulations:**
53
+ Add slurs, dots, and other articulations via parameters.
54
+ ```ruby
55
+ pitch 'C', octave: 4, slur: 'start' # Begin slur
56
+ pitch 'D', octave: 4, slur: 'stop' # End slur
57
+ pitch 'E', octave: 4, dots: 1 # Dotted note
58
+ ```
59
+
60
+ **Dynamics:**
61
+ Add dynamic markings using `direction` blocks with `dynamics` method. Supported: `pp`, `p`, `mp`, `mf`, `f`, `ff`, `fff`, etc.
62
+ ```ruby
63
+ direction do
64
+ dynamics 'f' # Forte
65
+ end
66
+ ```
67
+
68
+ **Wedges:**
69
+ Add crescendo/diminuendo markings with `wedge` in direction blocks.
70
+ ```ruby
71
+ direction do
72
+ wedge 'crescendo' # Start crescendo
73
+ end
74
+ # ... notes ...
75
+ direction wedge: 'stop' # End crescendo
76
+ ```
77
+
78
+ **Metronome:**
79
+ Add tempo markings with `metronome` in measures.
80
+ ```ruby
81
+ metronome beat_unit: 'quarter', per_minute: 120
82
+ ```
83
+
84
+ **Rests:**
85
+ Use `rest` method instead of `pitch` for rest notation.
86
+ ```ruby
87
+ rest duration: 2, type: 'quarter'
88
+ ```
89
+
90
+ ## Two Usage Modes
91
+
92
+ **Constructor Style (Method Calls):**
93
+
94
+ Use constructor parameters and `add_*` methods for programmatic building:
95
+
96
+ ```ruby
97
+ require 'musa-dsl'
98
+
99
+ # Create score with metadata
100
+ score = Musa::MusicXML::Builder::ScorePartwise.new(
101
+ work_title: "Piano Piece",
102
+ creators: { composer: "Your Name" },
103
+ encoding_date: DateTime.new(2024, 1, 1)
104
+ )
105
+
106
+ # Add parts using add_* methods
107
+ part = score.add_part(:p1, name: "Piano", abbreviation: "Pno.")
108
+
109
+ # Add measures and attributes
110
+ measure = part.add_measure(divisions: 4)
111
+
112
+ # Add attributes (key, time, clef, etc.)
113
+ measure.attributes.last.add_key(1, fifths: 0) # C major
114
+ measure.attributes.last.add_time(1, beats: 4, beat_type: 4)
115
+ measure.attributes.last.add_clef(1, sign: 'G', line: 2)
116
+
117
+ # Add notes
118
+ measure.add_pitch(step: 'C', octave: 4, duration: 4, type: 'quarter')
119
+ measure.add_pitch(step: 'E', octave: 4, duration: 4, type: 'quarter')
120
+ measure.add_pitch(step: 'G', octave: 4, duration: 4, type: 'quarter')
121
+ measure.add_pitch(step: 'C', octave: 5, duration: 4, type: 'quarter')
122
+
123
+ # Export to file
124
+ File.write("score.musicxml", score.to_xml.string)
125
+ ```
126
+
127
+ **DSL Style (Blocks):**
128
+
129
+ Use blocks with method names as setters/builders for more readable, declarative code:
130
+
131
+ ```ruby
132
+ require 'musa-dsl'
133
+
134
+ score = Musa::MusicXML::Builder::ScorePartwise.new do
135
+ work_title "Piano Piece"
136
+ creators composer: "Your Name"
137
+ encoding_date DateTime.new(2024, 1, 1)
138
+
139
+ part :p1, name: "Piano", abbreviation: "Pno." do
140
+ measure do
141
+ attributes do
142
+ divisions 4
143
+ key 1, fifths: 0 # C major
144
+ time 1, beats: 4, beat_type: 4
145
+ clef 1, sign: 'G', line: 2
146
+ end
147
+
148
+ pitch 'C', octave: 4, duration: 4, type: 'quarter'
149
+ pitch 'E', octave: 4, duration: 4, type: 'quarter'
150
+ pitch 'G', octave: 4, duration: 4, type: 'quarter'
151
+ pitch 'C', octave: 5, duration: 4, type: 'quarter'
152
+ end
153
+ end
154
+ end
155
+
156
+ File.write("score.musicxml", score.to_xml.string)
157
+ ```
158
+
159
+ **Sophisticated Example - Piano Score with Multiple Features:**
160
+
161
+ ```ruby
162
+ require 'musa-dsl'
163
+
164
+ score = Musa::MusicXML::Builder::ScorePartwise.new do
165
+ work_title "Étude in D Major"
166
+ work_number 1
167
+ creators composer: "Example Composer"
168
+ encoding_date DateTime.now
169
+
170
+ part :p1, name: "Piano" do
171
+ # Measure 1 - Setup and opening with two staves
172
+ measure do
173
+ attributes do
174
+ divisions 2 # 2 divisions per quarter note
175
+
176
+ # Treble clef (staff 1)
177
+ key 1, fifths: 2 # D major (2 sharps)
178
+ clef 1, sign: 'G', line: 2
179
+ time 1, beats: 4, beat_type: 4
180
+
181
+ # Bass clef (staff 2)
182
+ key 2, fifths: 2
183
+ clef 2, sign: 'F', line: 4
184
+ time 2, beats: 4, beat_type: 4
185
+ end
186
+
187
+ # Tempo marking
188
+ metronome beat_unit: 'quarter', per_minute: 120
189
+
190
+ # Right hand melody (staff 1)
191
+ pitch 'D', octave: 4, duration: 4, type: 'half', slur: 'start'
192
+ pitch 'E', octave: 4, duration: 4, type: 'half', slur: 'stop'
193
+
194
+ # Return to beginning for left hand (staff 2)
195
+ backup 8
196
+
197
+ # Left hand accompaniment (staff 2)
198
+ pitch 'D', octave: 3, duration: 8, type: 'whole', staff: 2
199
+ end
200
+
201
+ # Measure 2 - Two voices in treble clef
202
+ measure do
203
+ # Voice 1
204
+ pitch 'F#', octave: 4, duration: 2, type: 'quarter', alter: 1, voice: 1
205
+ pitch 'G', octave: 4, duration: 2, type: 'quarter', voice: 1
206
+ pitch 'A', octave: 4, duration: 2, type: 'quarter', voice: 1
207
+ pitch 'B', octave: 4, duration: 2, type: 'quarter', voice: 1
208
+
209
+ # Return to beginning for voice 2
210
+ backup 8
211
+
212
+ # Voice 2 (inner voice)
213
+ pitch 'A', octave: 3, duration: 3, type: 'quarter', dots: 1, voice: 2
214
+ pitch 'B', octave: 3, duration: 1, type: 'eighth', voice: 2
215
+ pitch 'C#', octave: 4, duration: 3, type: 'quarter', dots: 1, alter: 1, voice: 2
216
+ pitch 'D', octave: 4, duration: 1, type: 'eighth', voice: 2
217
+
218
+ # Return for left hand
219
+ backup 8
220
+
221
+ # Left hand (staff 2)
222
+ pitch 'A', octave: 2, duration: 8, type: 'whole', staff: 2
223
+ end
224
+
225
+ # Measure 3 - Dynamics and articulations
226
+ measure do
227
+ # Dynamic marking
228
+ direction do
229
+ dynamics 'pp'
230
+ wedge 'crescendo'
231
+ end
232
+
233
+ # Notes with crescendo
234
+ pitch 'C#', octave: 5, duration: 1, type: 'eighth', alter: 1
235
+ pitch 'D', octave: 5, duration: 1, type: 'eighth'
236
+ pitch 'E', octave: 5, duration: 1, type: 'eighth'
237
+ pitch 'F#', octave: 5, duration: 1, type: 'eighth', alter: 1
238
+
239
+ pitch 'G', octave: 5, duration: 1, type: 'eighth'
240
+ pitch 'A', octave: 5, duration: 1, type: 'eighth'
241
+ pitch 'B', octave: 5, duration: 1, type: 'eighth'
242
+ pitch 'C#', octave: 6, duration: 1, type: 'eighth', alter: 1
243
+
244
+ # End of crescendo, forte
245
+ direction wedge: 'stop', dynamics: 'f'
246
+ end
247
+ end
248
+ end
249
+
250
+ # Export to file
251
+ File.write("etude.musicxml", score.to_xml.string)
252
+
253
+ # Or write directly to IO
254
+ File.open("etude.musicxml", 'w') { |f| score.to_xml(f) }
255
+ ```
256
+
257
+ ## API Reference
258
+
259
+ **Complete API documentation:**
260
+ - [Musa::MusicXML::Builder](https://rubydoc.info/gems/musa-dsl/Musa/MusicXML/Builder) - MusicXML score generation
261
+
262
+ **Source code:** `lib/musicxml/builder/`
263
+
264
+
@@ -0,0 +1,71 @@
1
+ # Neumas & Neumalang - Musical Notation
2
+
3
+ Neumas provide a compact text-based notation system for musical composition. Neumalang is the parser that converts this notation to structured musical data.
4
+
5
+ ```ruby
6
+ require 'musa-dsl'
7
+ require 'midi-communications'
8
+
9
+ # To play the song, decode neumas to GDV and convert to PDV
10
+ include Musa::All
11
+
12
+ using Musa::Extension::Neumas
13
+
14
+ # Neuma notation requires parentheses around each neuma element
15
+ # Parsed using Musa::Neumalang::Neumalang.parse()
16
+
17
+ # Complete example with durations and dynamics (parallel voices using |)
18
+ song = "(0 1 mf) (+2 1 mp) (+4 2 p) (+5 1/2 mf) (+7 1 f)" | # Voice 1: melody with varied dynamics
19
+ "(+7 2 p) (+5 1 mp) (+7 1 mf) (+9 1/2 f) (+12 2 ff)" # Voice 2: harmony with crescendo
20
+
21
+ # Wrap parallel structure in serie
22
+ song_serie = S(song)
23
+
24
+ # Create decoder with a scale
25
+ scale = Scales.et12[440.0].major[60]
26
+ decoder = Decoders::NeumaDecoder.new(scale, base_duration: 1r)
27
+
28
+ # Setup sequencer with clock and transport
29
+ output = MIDICommunications::Output.gets
30
+
31
+ clock = TimerClock.new(bpm: 120, ticks_per_beat: 24)
32
+ transport = Transport.new(clock, 4, 24)
33
+
34
+ voices = MIDIVoices.new(sequencer: transport.sequencer, output: output, channels: [0, 1])
35
+
36
+ # Play both voices simultaneously - sequencer handles parallel structure automatically
37
+ transport.sequencer.with do
38
+ at 1 do
39
+ play song_serie, decoder: decoder, mode: :neumalang do |gdv|
40
+ # Convert GDV to PDV for MIDI output
41
+ pdv = gdv.to_pdv(scale)
42
+
43
+ # Use voice based on channel assignment (sequencer maintains voice separation)
44
+ voice_index = gdv[:channel] || 0
45
+ voices.voices[voice_index].note pitch: pdv[:pitch],
46
+ velocity: pdv[:velocity],
47
+ duration: pdv[:duration]
48
+ end
49
+ end
50
+ end
51
+
52
+ transport.start
53
+ ```
54
+
55
+ **Notation syntax:**
56
+ - `(0)`, `(+2)`, `(-1)` - Absolute/relative pitch steps (in parentheses)
57
+ - `o0`, `o1`, `o-1` - Octave specification
58
+ - `1`, `2`, `1/2`, `1/4` - Duration (whole, double, half, quarter)
59
+ - `ppp`, `pp`, `p`, `mp`, `mf`, `f`, `ff`, `fff` - Dynamics (velocity)
60
+ - `+f`, `+ff`, `-p`, `-pp` - Relative dynamics (louder/softer)
61
+ - `|` operator - Parallel voices (polyphonic structure)
62
+
63
+ ## API Reference
64
+
65
+ **Complete API documentation:**
66
+ - [Musa::Neumas](https://rubydoc.info/gems/musa-dsl/Musa/Neumas) - Musical notation data structures
67
+ - [Musa::Neumalang](https://rubydoc.info/gems/musa-dsl/Musa/Neumalang) - Notation parser and interpreter
68
+
69
+ **Source code:** `lib/neumas/` and `lib/neumalang/`
70
+
71
+
@@ -0,0 +1,135 @@
1
+ # REPL - Live Coding Infrastructure
2
+
3
+ The REPL (Read-Eval-Print Loop) provides a TCP-based server for live coding, enabling real-time code evaluation and interactive composition. It acts as a bridge between code editors (via MusaLCE clients) and the running Musa DSL environment.
4
+
5
+ **Architecture:**
6
+ ```
7
+ Editor → MusaLCE Client → TCP (port 1327) → REPL Server → DSL Context
8
+
9
+ Results/Errors
10
+ ```
11
+
12
+ **Available MusaLCE Clients:**
13
+ - **MusaLCEClientForVSCode**: Visual Studio Code extension
14
+ - **MusaLCEClientForAtom**: Atom editor plugin
15
+ - **MusaLCEforBitwig**: Bitwig Studio integration
16
+ - **MusaLCEforLive**: Ableton Live integration
17
+
18
+ ## Communication Protocol
19
+
20
+ The REPL uses a line-based protocol over TCP (default port: 1327).
21
+
22
+ **Client to Server:**
23
+ - `#path` - Start path block (optional, to inject file path context)
24
+ - *file path* - Path to the user's file being edited
25
+ - `#begin` - Start code block
26
+ - *code lines* - Ruby code to execute
27
+ - `#end` - Execute accumulated code block
28
+
29
+ **Server to Client:**
30
+ - `//echo` - Start echo block (code about to be executed)
31
+ - `//error` - Start error block
32
+ - `//backtrace` - Start backtrace section within error block
33
+ - `//end` - End current block
34
+ - *regular lines* - Output from code execution (puts, etc.)
35
+
36
+ **Example Session:**
37
+ ```
38
+ Client → Server:
39
+ #path
40
+ /Users/me/composition.rb
41
+ #begin
42
+ puts "Starting composition..."
43
+ at 1 do
44
+ note pitch: 60, duration: 1r
45
+ end
46
+ #end
47
+
48
+ Server → Client:
49
+ //echo
50
+ puts "Starting composition..."
51
+ at 1 do
52
+ note pitch: 60, duration: 1r
53
+ end
54
+ //end
55
+ Starting composition...
56
+ ```
57
+
58
+ ## Server Setup
59
+
60
+ **Basic REPL Server:**
61
+
62
+ ```ruby
63
+ require 'musa-dsl'
64
+ include Musa::All
65
+
66
+ # Create sequencer and transport
67
+ clock = TimerClock.new(bpm: 120, ticks_per_beat: 24)
68
+ transport = Transport.new(clock, 4, 24)
69
+
70
+ # Start REPL server bound to sequencer context
71
+ # The REPL will execute code in the sequencer's DSL context
72
+ transport.sequencer.with do
73
+ # DSL methods available in REPL
74
+ def note(pitch:, duration:)
75
+ puts "Playing pitch #{pitch} for #{duration} bars"
76
+ end
77
+
78
+ # Create REPL server (port 1327 by default)
79
+ @repl = Musa::REPL::REPL.new(binding)
80
+ end
81
+
82
+ # Start playback (REPL runs in background thread)
83
+ transport.start
84
+ ```
85
+
86
+ **File Path Injection:**
87
+
88
+ When a client sends a file path via `#path`, the REPL injects it as `@user_pathname` (Pathname object). This enables relative requires based on the editor's current file location:
89
+
90
+ ```ruby
91
+ # In REPL context, clients can use:
92
+ require_relative @user_pathname.dirname / 'my_helpers'
93
+ ```
94
+
95
+ ## Integration with Sequencer
96
+
97
+ The REPL automatically hooks into sequencer error handling to report async errors during playback:
98
+
99
+ ```ruby
100
+ require 'musa-dsl'
101
+ include Musa::All
102
+
103
+ clock = TimerClock.new(bpm: 120, ticks_per_beat: 24)
104
+ transport = Transport.new(clock, 4, 24)
105
+
106
+ transport.sequencer.with do
107
+ # If an error occurs during sequencer execution,
108
+ # REPL clients receive formatted error messages
109
+
110
+ at 1 do
111
+ raise "This error will be sent to REPL client"
112
+ end
113
+
114
+ @repl = Musa::REPL::REPL.new(binding)
115
+ end
116
+
117
+ transport.start
118
+ ```
119
+
120
+ ## Use Cases
121
+
122
+ - **Live coding performances**: Real-time code evaluation during performances
123
+ - **Interactive composition**: Develop compositions interactively with immediate feedback
124
+ - **DAW synchronization**: Control Musa DSL from within Bitwig or Ableton Live
125
+ - **Remote composition control**: Send commands to running compositions over network
126
+ - **Educational workshops**: Live demonstrations with instant code execution
127
+
128
+ ## API Reference
129
+
130
+ **Complete API documentation:**
131
+ - [Musa::REPL](https://rubydoc.info/gems/musa-dsl/Musa/REPL) - Live coding server and protocol
132
+
133
+ **Source code:** `lib/repl/`
134
+
135
+