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.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/.version +6 -0
- data/.yardopts +7 -0
- 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 +233 -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 +180 -1
- data/lib/musa-dsl/generative/generative-grammar.rb +359 -0
- data/lib/musa-dsl/generative/markov.rb +133 -3
- data/lib/musa-dsl/generative/rules.rb +258 -4
- data/lib/musa-dsl/generative/variatio.rb +217 -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 +108 -1
- data/lib/musa-dsl/midi/midi-voices.rb +265 -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 +308 -2
- data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +315 -0
- data/lib/musa-dsl/music/scales.rb +957 -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 +48 -0
- data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +41 -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 +45 -7
- data/lib/musa-dsl/series/queue-serie.rb +65 -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 +1 -1
- data/lib/musa-dsl.rb +132 -25
- data/musa-dsl.gemspec +12 -10
- metadata +87 -8
|
@@ -6,9 +6,53 @@ module Musa
|
|
|
6
6
|
module MusicXML
|
|
7
7
|
module Builder
|
|
8
8
|
module Internal
|
|
9
|
+
# Key signature specification.
|
|
10
|
+
#
|
|
11
|
+
# Key represents a key signature in terms of the circle of fifths and mode.
|
|
12
|
+
# For multi-staff parts (like piano), the number attribute specifies which
|
|
13
|
+
# staff the key signature applies to.
|
|
14
|
+
#
|
|
15
|
+
# ## Circle of Fifths
|
|
16
|
+
#
|
|
17
|
+
# The `fifths` attribute uses the circle of fifths representation:
|
|
18
|
+
#
|
|
19
|
+
# - **Negative** values: flats (C♭ major = -7, F major = -1)
|
|
20
|
+
# - **Zero**: C major / A minor
|
|
21
|
+
# - **Positive** values: sharps (G major = +1, C♯ major = +7)
|
|
22
|
+
#
|
|
23
|
+
# Common keys:
|
|
24
|
+
#
|
|
25
|
+
# - -7: C♭, -6: G♭, -5: D♭, -4: A♭, -3: E♭, -2: B♭, -1: F
|
|
26
|
+
# - 0: C (major) / A (minor)
|
|
27
|
+
# - +1: G, +2: D, +3: A, +4: E, +5: B, +6: F♯, +7: C♯
|
|
28
|
+
#
|
|
29
|
+
# @example C major
|
|
30
|
+
# Key.new(fifths: 0)
|
|
31
|
+
#
|
|
32
|
+
# @example D major (2 sharps)
|
|
33
|
+
# Key.new(fifths: 2, mode: 'major')
|
|
34
|
+
#
|
|
35
|
+
# @example B♭ minor (5 flats)
|
|
36
|
+
# Key.new(fifths: -5, mode: 'minor')
|
|
37
|
+
#
|
|
38
|
+
# @example Piano - different keys per staff
|
|
39
|
+
# Key.new(1, fifths: 0) # Treble clef: C major
|
|
40
|
+
# Key.new(2, fifths: 0) # Bass clef: C major
|
|
9
41
|
class Key
|
|
10
42
|
include Helper::ToXML
|
|
11
43
|
|
|
44
|
+
# Creates a key signature.
|
|
45
|
+
#
|
|
46
|
+
# @param number [Integer, nil] staff number (for multi-staff parts)
|
|
47
|
+
# @param cancel [Integer, nil] number of accidentals to cancel from previous key
|
|
48
|
+
# @param fifths [Integer] circle of fifths position (-7 to +7)
|
|
49
|
+
# @param mode [String, nil] 'major' or 'minor'
|
|
50
|
+
#
|
|
51
|
+
# @example G major
|
|
52
|
+
# Key.new(fifths: 1, mode: 'major')
|
|
53
|
+
#
|
|
54
|
+
# @example E minor
|
|
55
|
+
# Key.new(fifths: 1, mode: 'minor')
|
|
12
56
|
def initialize(number = nil, cancel: nil, fifths:, mode: nil)
|
|
13
57
|
@number = number
|
|
14
58
|
|
|
@@ -17,9 +61,30 @@ module Musa
|
|
|
17
61
|
@mode = mode
|
|
18
62
|
end
|
|
19
63
|
|
|
64
|
+
# Staff number (for multi-staff instruments).
|
|
65
|
+
# @return [Integer, nil]
|
|
20
66
|
attr_reader :number
|
|
21
|
-
attr_accessor :cancel, :fifths, :mode
|
|
22
67
|
|
|
68
|
+
# Number of accidentals to cancel.
|
|
69
|
+
# @return [Integer, nil]
|
|
70
|
+
attr_accessor :cancel
|
|
71
|
+
|
|
72
|
+
# Circle of fifths position (-7 to +7).
|
|
73
|
+
# @return [Integer]
|
|
74
|
+
attr_accessor :fifths
|
|
75
|
+
|
|
76
|
+
# Mode ('major' or 'minor').
|
|
77
|
+
# @return [String, nil]
|
|
78
|
+
attr_accessor :mode
|
|
79
|
+
|
|
80
|
+
# Generates the key signature XML element.
|
|
81
|
+
#
|
|
82
|
+
# @param io [IO] output stream
|
|
83
|
+
# @param indent [Integer] indentation level
|
|
84
|
+
# @param tabs [String] tab string
|
|
85
|
+
# @return [void]
|
|
86
|
+
#
|
|
87
|
+
# @api private
|
|
23
88
|
def _to_xml(io, indent:, tabs:)
|
|
24
89
|
io ||= StringIO.new
|
|
25
90
|
indent ||= 0
|
|
@@ -30,7 +95,7 @@ module Musa
|
|
|
30
95
|
|
|
31
96
|
io.puts "#{tabs}\t<cancel>#{@cancel}</cancel>" if @cancel
|
|
32
97
|
io.puts "#{tabs}\t<fifths>#{@fifths.to_i}</fifths>"
|
|
33
|
-
io.puts "#{tabs}\t<mode>#{@mode}</mode>"
|
|
98
|
+
io.puts "#{tabs}\t<mode>#{@mode}</mode>" if @mode
|
|
34
99
|
|
|
35
100
|
io.puts "#{tabs}</key>"
|
|
36
101
|
|
|
@@ -38,9 +103,83 @@ module Musa
|
|
|
38
103
|
end
|
|
39
104
|
end
|
|
40
105
|
|
|
106
|
+
# Time signature specification.
|
|
107
|
+
#
|
|
108
|
+
# Time represents time signatures in MusicXML. It supports simple meters
|
|
109
|
+
# (4/4, 3/4), compound meters (6/8), complex meters (5/4), and compound
|
|
110
|
+
# time signatures with multiple beat groups. Also supports unmeasured time
|
|
111
|
+
# (senza misura) for cadenzas and free-form sections.
|
|
112
|
+
#
|
|
113
|
+
# For multi-staff parts (like piano), the number attribute specifies which
|
|
114
|
+
# staff the time signature applies to.
|
|
115
|
+
#
|
|
116
|
+
# ## Simple Time Signatures
|
|
117
|
+
#
|
|
118
|
+
# Most common meters use a single beats/beat_type pair:
|
|
119
|
+
#
|
|
120
|
+
# - 4/4 (common time): beats=4, beat_type=4
|
|
121
|
+
# - 3/4 (waltz): beats=3, beat_type=4
|
|
122
|
+
# - 6/8 (compound): beats=6, beat_type=8
|
|
123
|
+
# - 2/2 (cut time): beats=2, beat_type=2
|
|
124
|
+
#
|
|
125
|
+
# ## Compound Time Signatures
|
|
126
|
+
#
|
|
127
|
+
# Some meters combine multiple beat groups (e.g., 3+2+3/8):
|
|
128
|
+
#
|
|
129
|
+
# Time.new do |t|
|
|
130
|
+
# t.add_beats beats: 3, beat_type: 8
|
|
131
|
+
# t.add_beats beats: 2, beat_type: 8
|
|
132
|
+
# t.add_beats beats: 3, beat_type: 8
|
|
133
|
+
# end
|
|
134
|
+
#
|
|
135
|
+
# ## Senza Misura (Unmeasured Time)
|
|
136
|
+
#
|
|
137
|
+
# For cadenzas and free-form sections without strict meter:
|
|
138
|
+
#
|
|
139
|
+
# Time.new(senza_misura: '')
|
|
140
|
+
#
|
|
141
|
+
# @example Common time (4/4)
|
|
142
|
+
# Time.new(beats: 4, beat_type: 4)
|
|
143
|
+
#
|
|
144
|
+
# @example Waltz (3/4)
|
|
145
|
+
# Time.new(beats: 3, beat_type: 4)
|
|
146
|
+
#
|
|
147
|
+
# @example Compound meter (6/8)
|
|
148
|
+
# Time.new(beats: 6, beat_type: 8)
|
|
149
|
+
#
|
|
150
|
+
# @example Complex meter (5/4)
|
|
151
|
+
# Time.new(beats: 5, beat_type: 4)
|
|
152
|
+
#
|
|
153
|
+
# @example Compound signature (3+2+3/8)
|
|
154
|
+
# time = Time.new
|
|
155
|
+
# time.add_beats(beats: 3, beat_type: 8)
|
|
156
|
+
# time.add_beats(beats: 2, beat_type: 8)
|
|
157
|
+
# time.add_beats(beats: 3, beat_type: 8)
|
|
158
|
+
#
|
|
159
|
+
# @example Piano - different time per staff
|
|
160
|
+
# Time.new(1, beats: 4, beat_type: 4) # Treble: 4/4
|
|
161
|
+
# Time.new(2, beats: 3, beat_type: 4) # Bass: 3/4
|
|
162
|
+
#
|
|
163
|
+
# @example Cadenza (unmeasured)
|
|
164
|
+
# Time.new(senza_misura: '')
|
|
41
165
|
class Time
|
|
42
166
|
include Helper::ToXML
|
|
43
167
|
|
|
168
|
+
# Creates a time signature.
|
|
169
|
+
#
|
|
170
|
+
# @param number [Integer, nil] staff number (for multi-staff parts)
|
|
171
|
+
# @param senza_misura [String, nil] unmeasured time indicator (typically empty string)
|
|
172
|
+
# @param beats [Integer, nil] time signature numerator (beats per measure)
|
|
173
|
+
# @param beat_type [Integer, nil] time signature denominator (note value per beat)
|
|
174
|
+
#
|
|
175
|
+
# @example 4/4 time
|
|
176
|
+
# Time.new(beats: 4, beat_type: 4)
|
|
177
|
+
#
|
|
178
|
+
# @example 6/8 time
|
|
179
|
+
# Time.new(beats: 6, beat_type: 8)
|
|
180
|
+
#
|
|
181
|
+
# @example Unmeasured time
|
|
182
|
+
# Time.new(senza_misura: '')
|
|
44
183
|
def initialize(number = nil, senza_misura: nil, beats: nil, beat_type: nil)
|
|
45
184
|
@number = number
|
|
46
185
|
|
|
@@ -50,14 +189,44 @@ module Musa
|
|
|
50
189
|
add_beats beats: beats, beat_type: beat_type if beats && beat_type
|
|
51
190
|
end
|
|
52
191
|
|
|
192
|
+
# Staff number (for multi-staff instruments).
|
|
193
|
+
# @return [Integer, nil]
|
|
53
194
|
attr_reader :number
|
|
195
|
+
|
|
196
|
+
# Senza misura indicator for unmeasured time.
|
|
197
|
+
# @return [String, nil]
|
|
54
198
|
attr_accessor :senza_misura
|
|
199
|
+
|
|
200
|
+
# Array of beat groups for compound time signatures.
|
|
201
|
+
# @return [Array<Hash>]
|
|
55
202
|
attr_reader :beats
|
|
56
203
|
|
|
204
|
+
# Adds a beat group to the time signature.
|
|
205
|
+
#
|
|
206
|
+
# Used for compound time signatures that combine multiple beat groups
|
|
207
|
+
# (e.g., 3+2+3/8 or 2+2+3/4).
|
|
208
|
+
#
|
|
209
|
+
# @param beats [Integer] numerator for this beat group
|
|
210
|
+
# @param beat_type [Integer] denominator for this beat group
|
|
211
|
+
# @return [void]
|
|
212
|
+
#
|
|
213
|
+
# @example Complex meter (3+2+3/8)
|
|
214
|
+
# time = Time.new
|
|
215
|
+
# time.add_beats(beats: 3, beat_type: 8)
|
|
216
|
+
# time.add_beats(beats: 2, beat_type: 8)
|
|
217
|
+
# time.add_beats(beats: 3, beat_type: 8)
|
|
57
218
|
def add_beats(beats:, beat_type:)
|
|
58
219
|
@beats << { beats: beats, beat_type: beat_type }
|
|
59
220
|
end
|
|
60
221
|
|
|
222
|
+
# Generates the time signature XML element.
|
|
223
|
+
#
|
|
224
|
+
# @param io [IO] output stream
|
|
225
|
+
# @param indent [Integer] indentation level
|
|
226
|
+
# @param tabs [String] tab string
|
|
227
|
+
# @return [void]
|
|
228
|
+
#
|
|
229
|
+
# @api private
|
|
61
230
|
def _to_xml(io, indent:, tabs:)
|
|
62
231
|
io.puts "#{tabs}<time#{" number=\"#{@number.to_i}\"" if @number}>"
|
|
63
232
|
|
|
@@ -71,9 +240,102 @@ module Musa
|
|
|
71
240
|
end
|
|
72
241
|
end
|
|
73
242
|
|
|
243
|
+
# Clef specification.
|
|
244
|
+
#
|
|
245
|
+
# Clef represents musical clefs that determine the pitch range displayed
|
|
246
|
+
# on a staff. MusicXML supports standard clefs (treble, bass, alto, tenor),
|
|
247
|
+
# percussion clefs, and tablature clefs.
|
|
248
|
+
#
|
|
249
|
+
# For multi-staff parts (like piano), the number attribute specifies which
|
|
250
|
+
# staff the clef applies to.
|
|
251
|
+
#
|
|
252
|
+
# ## Common Clefs
|
|
253
|
+
#
|
|
254
|
+
# Standard clefs are defined by a sign and staff line number:
|
|
255
|
+
#
|
|
256
|
+
# - **Treble (G clef)**: sign='G', line=2
|
|
257
|
+
# - **Bass (F clef)**: sign='F', line=4
|
|
258
|
+
# - **Alto (C clef)**: sign='C', line=3
|
|
259
|
+
# - **Tenor (C clef)**: sign='C', line=4
|
|
260
|
+
# - **Percussion**: sign='percussion'
|
|
261
|
+
# - **Tablature**: sign='TAB'
|
|
262
|
+
#
|
|
263
|
+
# ## Clef Signs
|
|
264
|
+
#
|
|
265
|
+
# The sign parameter determines the clef type:
|
|
266
|
+
#
|
|
267
|
+
# - **G**: Treble clef family
|
|
268
|
+
# - **F**: Bass clef family
|
|
269
|
+
# - **C**: Alto/tenor clef family (movable C clef)
|
|
270
|
+
# - **percussion**: For unpitched percussion
|
|
271
|
+
# - **TAB**: For guitar/bass tablature
|
|
272
|
+
#
|
|
273
|
+
# ## Staff Lines
|
|
274
|
+
#
|
|
275
|
+
# The line parameter indicates which staff line the clef sign sits on:
|
|
276
|
+
#
|
|
277
|
+
# - Lines are numbered 1-5 from bottom to top
|
|
278
|
+
# - Treble clef (G) typically on line 2
|
|
279
|
+
# - Bass clef (F) typically on line 4
|
|
280
|
+
# - Alto clef (C) on line 3, Tenor clef (C) on line 4
|
|
281
|
+
#
|
|
282
|
+
# ## Octave Transposition
|
|
283
|
+
#
|
|
284
|
+
# The octave_change parameter transposes notation by octaves:
|
|
285
|
+
#
|
|
286
|
+
# - **-2**: 15ma basso (two octaves down)
|
|
287
|
+
# - **-1**: 8va basso (one octave down)
|
|
288
|
+
# - **0**: No transposition (default)
|
|
289
|
+
# - **+1**: 8va alta (one octave up)
|
|
290
|
+
# - **+2**: 15ma alta (two octaves up)
|
|
291
|
+
#
|
|
292
|
+
# Common for tenor voice (treble clef 8va basso) and piccolo (treble 8va alta).
|
|
293
|
+
#
|
|
294
|
+
# @example Treble clef
|
|
295
|
+
# Clef.new(sign: 'G', line: 2)
|
|
296
|
+
#
|
|
297
|
+
# @example Bass clef
|
|
298
|
+
# Clef.new(sign: 'F', line: 4)
|
|
299
|
+
#
|
|
300
|
+
# @example Alto clef
|
|
301
|
+
# Clef.new(sign: 'C', line: 3)
|
|
302
|
+
#
|
|
303
|
+
# @example Tenor clef
|
|
304
|
+
# Clef.new(sign: 'C', line: 4)
|
|
305
|
+
#
|
|
306
|
+
# @example Tenor voice (treble 8va basso)
|
|
307
|
+
# Clef.new(sign: 'G', line: 2, octave_change: -1)
|
|
308
|
+
#
|
|
309
|
+
# @example Piccolo (treble 8va alta)
|
|
310
|
+
# Clef.new(sign: 'G', line: 2, octave_change: 1)
|
|
311
|
+
#
|
|
312
|
+
# @example Piano - different clefs per staff
|
|
313
|
+
# Clef.new(1, sign: 'G', line: 2) # Treble clef (right hand)
|
|
314
|
+
# Clef.new(2, sign: 'F', line: 4) # Bass clef (left hand)
|
|
315
|
+
#
|
|
316
|
+
# @example Percussion
|
|
317
|
+
# Clef.new(sign: 'percussion')
|
|
318
|
+
#
|
|
319
|
+
# @example Guitar tablature
|
|
320
|
+
# Clef.new(sign: 'TAB')
|
|
74
321
|
class Clef
|
|
75
322
|
include Helper::ToXML
|
|
76
323
|
|
|
324
|
+
# Creates a clef.
|
|
325
|
+
#
|
|
326
|
+
# @param number [Integer, nil] staff number (for multi-staff parts)
|
|
327
|
+
# @param sign [String] clef sign: 'G', 'F', 'C', 'percussion', 'TAB'
|
|
328
|
+
# @param line [Integer] staff line number (1-5, bottom to top)
|
|
329
|
+
# @param octave_change [Integer, nil] octave transposition (-2, -1, 0, +1, +2)
|
|
330
|
+
#
|
|
331
|
+
# @example Treble clef
|
|
332
|
+
# Clef.new(sign: 'G', line: 2)
|
|
333
|
+
#
|
|
334
|
+
# @example Bass clef
|
|
335
|
+
# Clef.new(sign: 'F', line: 4)
|
|
336
|
+
#
|
|
337
|
+
# @example Tenor voice (treble 8va basso)
|
|
338
|
+
# Clef.new(sign: 'G', line: 2, octave_change: -1)
|
|
77
339
|
def initialize(number = nil, sign:, line:, octave_change: nil)
|
|
78
340
|
@number = number
|
|
79
341
|
@sign = sign
|
|
@@ -81,9 +343,30 @@ module Musa
|
|
|
81
343
|
@octave_change = octave_change
|
|
82
344
|
end
|
|
83
345
|
|
|
346
|
+
# Staff number (for multi-staff instruments).
|
|
347
|
+
# @return [Integer, nil]
|
|
84
348
|
attr_reader :number
|
|
85
|
-
attr_accessor :sign, :line, :octave_change
|
|
86
349
|
|
|
350
|
+
# Clef sign (G, F, C, percussion, TAB).
|
|
351
|
+
# @return [String]
|
|
352
|
+
attr_accessor :sign
|
|
353
|
+
|
|
354
|
+
# Staff line number (1-5).
|
|
355
|
+
# @return [Integer]
|
|
356
|
+
attr_accessor :line
|
|
357
|
+
|
|
358
|
+
# Octave transposition (-2 to +2).
|
|
359
|
+
# @return [Integer, nil]
|
|
360
|
+
attr_accessor :octave_change
|
|
361
|
+
|
|
362
|
+
# Generates the clef XML element.
|
|
363
|
+
#
|
|
364
|
+
# @param io [IO] output stream
|
|
365
|
+
# @param indent [Integer] indentation level
|
|
366
|
+
# @param tabs [String] tab string
|
|
367
|
+
# @return [void]
|
|
368
|
+
#
|
|
369
|
+
# @api private
|
|
87
370
|
def _to_xml(io, indent:, tabs:)
|
|
88
371
|
io.puts "#{tabs}<clef#{" number=\"#{@number.to_i}\"" if @number}>"
|
|
89
372
|
|
|
@@ -95,12 +378,131 @@ module Musa
|
|
|
95
378
|
end
|
|
96
379
|
end
|
|
97
380
|
|
|
381
|
+
# Musical attributes container.
|
|
382
|
+
#
|
|
383
|
+
# Attributes represents the `<attributes>` element in MusicXML, which
|
|
384
|
+
# contains key signatures, time signatures, clefs, and timing divisions.
|
|
385
|
+
# This element typically appears at the beginning of a measure to establish
|
|
386
|
+
# or change the musical context.
|
|
387
|
+
#
|
|
388
|
+
# ## Timing Resolution (Divisions)
|
|
389
|
+
#
|
|
390
|
+
# The divisions parameter sets the timing resolution for note durations
|
|
391
|
+
# in the measure. It represents how many divisions per quarter note:
|
|
392
|
+
#
|
|
393
|
+
# - **divisions=1**: Quarter note = 1 unit (limited precision)
|
|
394
|
+
# - **divisions=2**: Eighth notes possible
|
|
395
|
+
# - **divisions=4**: Sixteenth notes possible
|
|
396
|
+
# - **divisions=8**: Thirty-second notes possible
|
|
397
|
+
# - **divisions=24**: Common choice (supports triplets and quintuplets)
|
|
398
|
+
#
|
|
399
|
+
# Note durations are expressed as multiples of this division unit.
|
|
400
|
+
#
|
|
401
|
+
# ## Single-Staff vs Multi-Staff
|
|
402
|
+
#
|
|
403
|
+
# For single-staff instruments (violin, flute), one key/time/clef suffices.
|
|
404
|
+
# For multi-staff instruments (piano, organ, harp), different attributes
|
|
405
|
+
# can be specified per staff using the staff number parameter.
|
|
406
|
+
#
|
|
407
|
+
# The `<staves>` element is automatically generated based on the maximum
|
|
408
|
+
# number of keys, times, or clefs defined.
|
|
409
|
+
#
|
|
410
|
+
# ## Usage Styles
|
|
411
|
+
#
|
|
412
|
+
# Two equivalent approaches:
|
|
413
|
+
#
|
|
414
|
+
# **Constructor parameters** (convenient for simple cases):
|
|
415
|
+
#
|
|
416
|
+
# Attributes.new(
|
|
417
|
+
# divisions: 4,
|
|
418
|
+
# key_fifths: 0,
|
|
419
|
+
# time_beats: 4, time_beat_type: 4,
|
|
420
|
+
# clef_sign: 'G', clef_line: 2
|
|
421
|
+
# )
|
|
422
|
+
#
|
|
423
|
+
# **DSL with explicit elements** (flexible for multi-staff):
|
|
424
|
+
#
|
|
425
|
+
# Attributes.new do
|
|
426
|
+
# divisions 4
|
|
427
|
+
# key fifths: 0
|
|
428
|
+
# time beats: 4, beat_type: 4
|
|
429
|
+
# clef sign: 'G', line: 2
|
|
430
|
+
# end
|
|
431
|
+
#
|
|
432
|
+
# @example Simple single-staff attributes
|
|
433
|
+
# Attributes.new(
|
|
434
|
+
# divisions: 4,
|
|
435
|
+
# key_fifths: 1, # G major
|
|
436
|
+
# time_beats: 4, time_beat_type: 4,
|
|
437
|
+
# clef_sign: 'G', clef_line: 2
|
|
438
|
+
# )
|
|
439
|
+
#
|
|
440
|
+
# @example Piano with different keys per staff
|
|
441
|
+
# Attributes.new do
|
|
442
|
+
# divisions 4
|
|
443
|
+
# key 1, fifths: 0 # Treble: C major
|
|
444
|
+
# key 2, fifths: -1 # Bass: F major
|
|
445
|
+
# time beats: 3, beat_type: 4
|
|
446
|
+
# clef 1, sign: 'G', line: 2
|
|
447
|
+
# clef 2, sign: 'F', line: 4
|
|
448
|
+
# end
|
|
449
|
+
#
|
|
450
|
+
# @example Change key signature mid-score
|
|
451
|
+
# Attributes.new do
|
|
452
|
+
# key cancel: 2, fifths: -1 # From D major (2♯) to F major (1♭)
|
|
453
|
+
# end
|
|
454
|
+
#
|
|
455
|
+
# @example High timing resolution for complex rhythms
|
|
456
|
+
# Attributes.new(divisions: 24) # Supports triplets, quintuplets
|
|
457
|
+
#
|
|
458
|
+
# @see Key Key signature class
|
|
459
|
+
# @see Time Time signature class
|
|
460
|
+
# @see Clef Clef class
|
|
98
461
|
class Attributes
|
|
99
462
|
extend Musa::Extension::AttributeBuilder
|
|
100
463
|
include Musa::Extension::With
|
|
101
464
|
|
|
102
465
|
include Helper::ToXML
|
|
103
466
|
|
|
467
|
+
# Creates a musical attributes container.
|
|
468
|
+
#
|
|
469
|
+
# @param divisions [Integer, nil] timing resolution (divisions per quarter note)
|
|
470
|
+
# @param key_cancel [Integer, nil] accidentals to cancel from previous key
|
|
471
|
+
# @param key_fifths [Integer, nil] key signature (circle of fifths: -7 to +7)
|
|
472
|
+
# @param key_mode [String, nil] mode ('major' or 'minor')
|
|
473
|
+
# @param time_senza_misura [Boolean, nil] unmeasured time
|
|
474
|
+
# @param time_beats [Integer, nil] time signature numerator
|
|
475
|
+
# @param time_beat_type [Integer, nil] time signature denominator
|
|
476
|
+
# @param clef_sign [String, nil] clef sign ('G', 'F', 'C', 'percussion', 'TAB')
|
|
477
|
+
# @param clef_line [Integer, nil] clef staff line (1-5)
|
|
478
|
+
# @param clef_octave_change [Integer, nil] clef octave transposition
|
|
479
|
+
# @yield Optional DSL block for adding elements explicitly
|
|
480
|
+
#
|
|
481
|
+
# @example Constructor parameters approach
|
|
482
|
+
# Attributes.new(
|
|
483
|
+
# divisions: 4,
|
|
484
|
+
# key_fifths: 2, # D major
|
|
485
|
+
# time_beats: 6, time_beat_type: 8,
|
|
486
|
+
# clef_sign: 'G', clef_line: 2
|
|
487
|
+
# )
|
|
488
|
+
#
|
|
489
|
+
# @example DSL block approach
|
|
490
|
+
# Attributes.new do
|
|
491
|
+
# divisions 4
|
|
492
|
+
# key fifths: -3, mode: 'minor' # C minor
|
|
493
|
+
# time beats: 4, beat_type: 4
|
|
494
|
+
# clef sign: 'F', line: 4
|
|
495
|
+
# end
|
|
496
|
+
#
|
|
497
|
+
# @example Multi-staff piano
|
|
498
|
+
# Attributes.new do
|
|
499
|
+
# divisions 8
|
|
500
|
+
# key 1, fifths: 0
|
|
501
|
+
# key 2, fifths: 0
|
|
502
|
+
# time beats: 3, beat_type: 4
|
|
503
|
+
# clef 1, sign: 'G', line: 2
|
|
504
|
+
# clef 2, sign: 'F', line: 4
|
|
505
|
+
# end
|
|
104
506
|
def initialize(divisions: nil,
|
|
105
507
|
key_cancel: nil, key_fifths: nil, key_mode: nil,
|
|
106
508
|
time_senza_misura: nil, time_beats: nil, time_beat_type: nil,
|
|
@@ -120,12 +522,90 @@ module Musa
|
|
|
120
522
|
with &block if block_given?
|
|
121
523
|
end
|
|
122
524
|
|
|
525
|
+
# Timing divisions builder/setter.
|
|
526
|
+
#
|
|
527
|
+
# Sets or updates the divisions per quarter note. Higher values provide
|
|
528
|
+
# finer timing resolution for complex rhythms.
|
|
529
|
+
#
|
|
530
|
+
# @overload divisions(value)
|
|
531
|
+
# Sets divisions via DSL
|
|
532
|
+
# @param value [Integer] divisions per quarter note
|
|
533
|
+
# @overload divisions=(value)
|
|
534
|
+
# Sets divisions via assignment
|
|
535
|
+
# @param value [Integer] divisions per quarter note
|
|
536
|
+
#
|
|
537
|
+
# @example
|
|
538
|
+
# attributes.divisions 24 # High resolution for triplets
|
|
123
539
|
attr_simple_builder :divisions
|
|
124
540
|
|
|
541
|
+
# Adds a key signature.
|
|
542
|
+
#
|
|
543
|
+
# Multiple keys can be added for multi-staff parts where each staff
|
|
544
|
+
# has a different key signature.
|
|
545
|
+
#
|
|
546
|
+
# @option cancel [Integer, nil] accidentals to cancel
|
|
547
|
+
# @option fifths [Integer] circle of fifths position (-7 to +7)
|
|
548
|
+
# @option mode [String, nil] 'major' or 'minor'
|
|
549
|
+
# @yield Optional DSL block for configuring the key
|
|
550
|
+
# @return [Key] the created key signature
|
|
551
|
+
#
|
|
552
|
+
# @example Single key
|
|
553
|
+
# attributes.add_key(fifths: 2) # D major
|
|
554
|
+
#
|
|
555
|
+
# @example Multi-staff with different keys
|
|
556
|
+
# attributes.add_key(1, fifths: 0) # Treble: C major
|
|
557
|
+
# attributes.add_key(2, fifths: -1) # Bass: F major
|
|
125
558
|
attr_complex_adder_to_array :key, Key
|
|
559
|
+
|
|
560
|
+
# Adds a time signature.
|
|
561
|
+
#
|
|
562
|
+
# Multiple time signatures can be added for multi-staff parts where
|
|
563
|
+
# each staff has a different meter (polyrhythm).
|
|
564
|
+
#
|
|
565
|
+
# @option senza_misura [Boolean, nil] unmeasured time
|
|
566
|
+
# @option beats [Integer, nil] time signature numerator
|
|
567
|
+
# @option beat_type [Integer, nil] time signature denominator
|
|
568
|
+
# @yield Optional DSL block for configuring the time signature
|
|
569
|
+
# @return [Time] the created time signature
|
|
570
|
+
#
|
|
571
|
+
# @example Single time signature
|
|
572
|
+
# attributes.add_time(beats: 4, beat_type: 4)
|
|
573
|
+
#
|
|
574
|
+
# @example Polyrhythm (different meters per staff)
|
|
575
|
+
# attributes.add_time(1, beats: 3, beat_type: 4) # Treble: 3/4
|
|
576
|
+
# attributes.add_time(2, beats: 6, beat_type: 8) # Bass: 6/8
|
|
126
577
|
attr_complex_adder_to_array :time, Time
|
|
578
|
+
|
|
579
|
+
# Adds a clef.
|
|
580
|
+
#
|
|
581
|
+
# Multiple clefs are needed for multi-staff parts (piano, organ, harp).
|
|
582
|
+
#
|
|
583
|
+
# @option sign [String] clef sign ('G', 'F', 'C', 'percussion', 'TAB')
|
|
584
|
+
# @option line [Integer] staff line (1-5)
|
|
585
|
+
# @option octave_change [Integer, nil] octave transposition
|
|
586
|
+
# @yield Optional DSL block for configuring the clef
|
|
587
|
+
# @return [Clef] the created clef
|
|
588
|
+
#
|
|
589
|
+
# @example Single clef
|
|
590
|
+
# attributes.add_clef(sign: 'G', line: 2)
|
|
591
|
+
#
|
|
592
|
+
# @example Piano (two staves)
|
|
593
|
+
# attributes.add_clef(1, sign: 'G', line: 2) # Treble
|
|
594
|
+
# attributes.add_clef(2, sign: 'F', line: 4) # Bass
|
|
127
595
|
attr_complex_adder_to_array :clef, Clef
|
|
128
596
|
|
|
597
|
+
# Generates the attributes XML element.
|
|
598
|
+
#
|
|
599
|
+
# Automatically determines the number of staves based on the maximum
|
|
600
|
+
# count of keys, times, or clefs. Outputs divisions, keys, times,
|
|
601
|
+
# staves count (if > 1), and clefs.
|
|
602
|
+
#
|
|
603
|
+
# @param io [IO] output stream
|
|
604
|
+
# @param indent [Integer] indentation level
|
|
605
|
+
# @param tabs [String] tab string
|
|
606
|
+
# @return [void]
|
|
607
|
+
#
|
|
608
|
+
# @api private
|
|
129
609
|
def _to_xml(io, indent:, tabs:)
|
|
130
610
|
io.puts "#{tabs}<attributes>"
|
|
131
611
|
|