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
|
@@ -7,9 +7,26 @@ module Musa
|
|
|
7
7
|
module MusicXML
|
|
8
8
|
module Builder
|
|
9
9
|
module Internal
|
|
10
|
+
# Notation element helper.
|
|
11
|
+
#
|
|
12
|
+
# Private helper class for creating MusicXML notation elements with
|
|
13
|
+
# type attributes and optional content. Used internally for elements
|
|
14
|
+
# like slurs, which can be simple (type: 'start'/'stop') or complex
|
|
15
|
+
# (with additional attributes like placement, bezier curves, etc.).
|
|
16
|
+
#
|
|
17
|
+
# @api private
|
|
10
18
|
class Notation
|
|
11
19
|
include Helper::ToXML
|
|
12
20
|
|
|
21
|
+
# Creates a Notation from various input formats.
|
|
22
|
+
#
|
|
23
|
+
# @param tag [String] XML element tag name
|
|
24
|
+
# @param value_or_attributes [Symbol, String, Hash, nil] type value or hash of attributes
|
|
25
|
+
# @param true_value [String, nil] value to use when type is true
|
|
26
|
+
# @param false_value [String, nil] value to use when type is false
|
|
27
|
+
# @return [Notation, nil]
|
|
28
|
+
#
|
|
29
|
+
# @api private
|
|
13
30
|
def self.create(tag, value_or_attributes, true_value = nil, false_value = nil)
|
|
14
31
|
case value_or_attributes
|
|
15
32
|
when Hash
|
|
@@ -28,6 +45,16 @@ module Musa
|
|
|
28
45
|
end
|
|
29
46
|
end
|
|
30
47
|
|
|
48
|
+
# Creates a notation element.
|
|
49
|
+
#
|
|
50
|
+
# @param tag [String] XML element tag name
|
|
51
|
+
# @param type [Symbol, String, Boolean] type attribute value
|
|
52
|
+
# @param content [String, nil] element text content
|
|
53
|
+
# @param true_value [String, nil] value to use when type is true
|
|
54
|
+
# @param false_value [String, nil] value to use when type is false
|
|
55
|
+
# @param attributes [Hash] additional XML attributes
|
|
56
|
+
#
|
|
57
|
+
# @api private
|
|
31
58
|
def initialize(tag, type, content = nil, true_value = nil, false_value = nil, **attributes)
|
|
32
59
|
@tag = tag
|
|
33
60
|
|
|
@@ -43,6 +70,14 @@ module Musa
|
|
|
43
70
|
@attributes = attributes
|
|
44
71
|
end
|
|
45
72
|
|
|
73
|
+
# Generates the notation XML element.
|
|
74
|
+
#
|
|
75
|
+
# @param io [IO] output stream
|
|
76
|
+
# @param indent [Integer] indentation level
|
|
77
|
+
# @param tabs [String] tab string
|
|
78
|
+
# @return [void]
|
|
79
|
+
#
|
|
80
|
+
# @api private
|
|
46
81
|
def _to_xml(io, indent:, tabs:)
|
|
47
82
|
io.print "#{tabs}\t<#{ @tag } "
|
|
48
83
|
|
|
@@ -62,6 +97,108 @@ module Musa
|
|
|
62
97
|
|
|
63
98
|
private_constant :Notation
|
|
64
99
|
|
|
100
|
+
# Abstract base class for all note types.
|
|
101
|
+
#
|
|
102
|
+
# Note is the foundation for pitched notes, rests, and unpitched percussion notes.
|
|
103
|
+
# It provides comprehensive support for musical notation including:
|
|
104
|
+
#
|
|
105
|
+
# ## Core Note Properties
|
|
106
|
+
# - **Duration and Type**: Timing (duration in divisions, type: whole/half/quarter/etc.)
|
|
107
|
+
# - **Voice and Staff**: Multi-voice and multi-staff support
|
|
108
|
+
# - **Dots**: Dotted and double-dotted notes
|
|
109
|
+
# - **Grace Notes**: Ornamental notes without duration
|
|
110
|
+
# - **Cue Notes**: Smaller notes for reference
|
|
111
|
+
# - **Chords**: Notes that sound simultaneously
|
|
112
|
+
#
|
|
113
|
+
# ## Notations
|
|
114
|
+
# The `<notations>` element groups musical symbols attached to notes:
|
|
115
|
+
#
|
|
116
|
+
# ### Basic Notations
|
|
117
|
+
# - **Ties/Tied**: Visual ties connecting notes (tie) vs sustained sound (tied)
|
|
118
|
+
# - **Slurs**: Phrase markings
|
|
119
|
+
# - **Tuplets**: Irregular rhythmic groupings (triplets, quintuplets, etc.)
|
|
120
|
+
# - **Dynamics**: Volume markings (pp, p, mp, mf, f, ff, etc.)
|
|
121
|
+
# - **Fermata**: Pause/hold symbols
|
|
122
|
+
# - **Accidental Marks**: Sharp, flat, natural annotations
|
|
123
|
+
# - **Arpeggiate**: Rolled chord indication
|
|
124
|
+
# - **Glissando/Slide**: Pitch glide
|
|
125
|
+
#
|
|
126
|
+
# ### Articulations
|
|
127
|
+
# Attack and release characteristics:
|
|
128
|
+
# - **accent**: Emphasis (>)
|
|
129
|
+
# - **staccato**: Short, detached (•)
|
|
130
|
+
# - **tenuto**: Full value (−)
|
|
131
|
+
# - **staccatissimo**: Very short (▼)
|
|
132
|
+
# - **spiccato**: Bouncing bow
|
|
133
|
+
# - **strong_accent**: Forceful (^)
|
|
134
|
+
# - **detached_legato**: Portato
|
|
135
|
+
# - **breath_mark**: Breath pause
|
|
136
|
+
# - **caesura**: Railroad tracks (caesura)
|
|
137
|
+
# - Plus: doit, falloff, plop, scoop, stress, unstress
|
|
138
|
+
#
|
|
139
|
+
# ### Ornaments
|
|
140
|
+
# Melodic decorations:
|
|
141
|
+
# - **trill_mark**: Rapid alternation with upper neighbor
|
|
142
|
+
# - **mordent**: Single alternation with lower neighbor
|
|
143
|
+
# - **inverted_mordent**: Single alternation with upper neighbor
|
|
144
|
+
# - **turn**: Four-note figure around main note
|
|
145
|
+
# - **inverted_turn**: Inverted turn figure
|
|
146
|
+
# - **delayed_turn**: Turn after main note
|
|
147
|
+
# - **shake**: Extended trill
|
|
148
|
+
# - **tremolo**: Rapid repetition (single) or alternation (start/stop)
|
|
149
|
+
# - **schleifer**: Slide ornament
|
|
150
|
+
# - **wavy_line**: Trill extension
|
|
151
|
+
#
|
|
152
|
+
# ### Technical Markings
|
|
153
|
+
# Performance technique indicators:
|
|
154
|
+
#
|
|
155
|
+
# **String Instruments**:
|
|
156
|
+
# - **fingering**: Finger numbers
|
|
157
|
+
# - **up_bow/down_bow**: Bowing direction (↑/↓)
|
|
158
|
+
# - **harmonic**: Natural or artificial harmonics
|
|
159
|
+
# - **open_string**: Open string indication (○)
|
|
160
|
+
# - **stopped**: Stopped note (+)
|
|
161
|
+
# - **snap_pizzicato**: Bartók pizzicato
|
|
162
|
+
# - **thumb_position**: Cello thumb position
|
|
163
|
+
# - **string**: String number
|
|
164
|
+
# - **hammer_on/pull_off**: Legato technique
|
|
165
|
+
#
|
|
166
|
+
# **Wind Instruments**:
|
|
167
|
+
# - **double_tongue/triple_tongue**: Tonguing technique
|
|
168
|
+
# - **fingernails**: Use fingernails
|
|
169
|
+
# - **hole**: Woodwind fingering holes
|
|
170
|
+
#
|
|
171
|
+
# **Guitar/Fretted**:
|
|
172
|
+
# - **fret**: Fret number
|
|
173
|
+
# - **bend**: String bend
|
|
174
|
+
# - **tap**: Tapping technique
|
|
175
|
+
# - **pluck**: Plucking style
|
|
176
|
+
#
|
|
177
|
+
# **Other**:
|
|
178
|
+
# - **arrow**: Directional arrow
|
|
179
|
+
# - **handbell**: Handbell technique (damp, echo, gyro, etc.)
|
|
180
|
+
# - **heel/toe**: Organ pedal technique
|
|
181
|
+
#
|
|
182
|
+
# ## Hierarchy
|
|
183
|
+
#
|
|
184
|
+
# Note is an abstract base class with three concrete subclasses:
|
|
185
|
+
# - {PitchedNote}: Notes with specific pitch (step, octave, alteration)
|
|
186
|
+
# - {Rest}: Silences with duration
|
|
187
|
+
# - {UnpitchedNote}: Percussion notes without specific pitch
|
|
188
|
+
#
|
|
189
|
+
# ## Usage
|
|
190
|
+
#
|
|
191
|
+
# Note is not used directly—use {PitchedNote}, {Rest}, or {UnpitchedNote}.
|
|
192
|
+
# Notes are typically added via {Measure} convenience methods:
|
|
193
|
+
# - {Measure#add_pitch} / {Measure#pitch}
|
|
194
|
+
# - {Measure#add_rest} / {Measure#rest}
|
|
195
|
+
# - {Measure#add_unpitched} / {Measure#unpitched}
|
|
196
|
+
#
|
|
197
|
+
# @abstract Subclass and override {#specific_to_xml} to implement.
|
|
198
|
+
# @see PitchedNote Pitched notes with step/octave
|
|
199
|
+
# @see Rest Rests and measure rests
|
|
200
|
+
# @see UnpitchedNote Unpitched percussion
|
|
201
|
+
# @see Measure Container for notes
|
|
65
202
|
class Note
|
|
66
203
|
extend Musa::Extension::AttributeBuilder
|
|
67
204
|
include Musa::Extension::With
|
|
@@ -69,6 +206,127 @@ module Musa
|
|
|
69
206
|
include Helper
|
|
70
207
|
include ToXML
|
|
71
208
|
|
|
209
|
+
# Creates a note (abstract base constructor).
|
|
210
|
+
#
|
|
211
|
+
# This constructor is called by subclasses ({PitchedNote}, {Rest}, {UnpitchedNote}).
|
|
212
|
+
# The extensive parameter list supports all MusicXML notation features.
|
|
213
|
+
#
|
|
214
|
+
# ## Parameter Categories
|
|
215
|
+
#
|
|
216
|
+
# ### Core Note Properties
|
|
217
|
+
# @param grace [Boolean, nil] grace note (ornamental, no time value)
|
|
218
|
+
# @param cue [Boolean, nil] cue note (smaller, reference indication)
|
|
219
|
+
# @param chord [Boolean, nil] note is part of a chord (sounds with previous note)
|
|
220
|
+
# @param duration [Integer, nil] duration in division units
|
|
221
|
+
# @param type [String, nil] note type: 'whole', 'half', 'quarter', 'eighth', '16th', etc.
|
|
222
|
+
# @param dots [Integer, nil] number of augmentation dots (1 or 2)
|
|
223
|
+
# @param voice [Integer, nil] voice number for polyphonic music
|
|
224
|
+
# @param staff [Integer, nil] staff number for multi-staff instruments
|
|
225
|
+
# @param tie_start [Boolean, nil] start a tie to next note
|
|
226
|
+
# @param tie_stop [Boolean, nil] stop a tie from previous note
|
|
227
|
+
# @param accidental [String, nil] visual accidental: 'sharp', 'flat', 'natural', etc.
|
|
228
|
+
# @param stem [String, nil] stem direction: 'up', 'down', 'double', 'none'
|
|
229
|
+
# @param pizzicato [Boolean, nil] pizzicato attribute on note element
|
|
230
|
+
#
|
|
231
|
+
# ### Rhythm Modification
|
|
232
|
+
# @param time_modification [TimeModification, Hash, nil] tuplet ratio (e.g., 3:2 for triplets)
|
|
233
|
+
# @param notehead [Notehead, Hash, nil] notehead style and properties
|
|
234
|
+
#
|
|
235
|
+
# ### Basic Notations
|
|
236
|
+
# @param tied [String, nil] tied notation: 'start', 'stop', 'continue'
|
|
237
|
+
# @param tuplet [Tuplet, Hash, nil] tuplet bracket/number notation
|
|
238
|
+
# @param slur [String, Hash, nil] slur: 'start', 'stop', 'continue' or hash with attributes
|
|
239
|
+
# @param dynamics [String, Array<String>, nil] dynamics: 'pp', 'p', 'mp', 'mf', 'f', 'ff', etc.
|
|
240
|
+
# @param fermata [Boolean, String, nil] fermata: true, 'upright', 'inverted'
|
|
241
|
+
# @param accidental_mark [String, nil] accidental in notations: 'sharp', 'flat', 'natural'
|
|
242
|
+
# @param arpeggiate [Boolean, String, nil] arpeggio: true, 'up', 'down'
|
|
243
|
+
# @param non_arpeggiate [String, nil] non-arpeggio: 'top', 'bottom'
|
|
244
|
+
# @param glissando [String, nil] glissando: 'start', 'stop'
|
|
245
|
+
# @param slide [String, nil] slide: 'start', 'stop'
|
|
246
|
+
#
|
|
247
|
+
# ### Articulations
|
|
248
|
+
# @param accent [Boolean, nil] accent mark (>)
|
|
249
|
+
# @param staccato [Boolean, nil] staccato (•)
|
|
250
|
+
# @param tenuto [Boolean, nil] tenuto (−)
|
|
251
|
+
# @param staccatissimo [Boolean, nil] staccatissimo (▼)
|
|
252
|
+
# @param spiccato [Boolean, nil] spiccato
|
|
253
|
+
# @param strong_accent [Boolean, String, nil] strong accent (^): true, 'up', 'down'
|
|
254
|
+
# @param detached_legato [Boolean, nil] detached legato (portato)
|
|
255
|
+
# @param breath_mark [Boolean, String, nil] breath mark: true, 'comma', 'tick'
|
|
256
|
+
# @param caesura [Boolean, nil] caesura (railroad tracks)
|
|
257
|
+
# @param doit [Boolean, nil] doit
|
|
258
|
+
# @param falloff [Boolean, nil] falloff
|
|
259
|
+
# @param plop [Boolean, nil] plop
|
|
260
|
+
# @param scoop [Boolean, nil] scoop
|
|
261
|
+
# @param stress [Boolean, nil] stress
|
|
262
|
+
# @param unstress [Boolean, nil] unstress
|
|
263
|
+
# @param other_articulation [String, nil] custom articulation text
|
|
264
|
+
#
|
|
265
|
+
# ### Ornaments
|
|
266
|
+
# @param trill_mark [Boolean, nil] trill
|
|
267
|
+
# @param mordent [Boolean, nil] mordent (lower neighbor)
|
|
268
|
+
# @param inverted_mordent [Boolean, nil] inverted mordent (upper neighbor)
|
|
269
|
+
# @param turn [Boolean, nil] turn
|
|
270
|
+
# @param inverted_turn [Boolean, nil] inverted turn
|
|
271
|
+
# @param delayed_turn [Boolean, nil] delayed turn
|
|
272
|
+
# @param delayed_inverted_turn [Boolean, nil] delayed inverted turn
|
|
273
|
+
# @param shake [Boolean, nil] shake
|
|
274
|
+
# @param tremolo [String, nil] tremolo: 'single', 'start', 'stop'
|
|
275
|
+
# @param schleifer [Boolean, nil] schleifer
|
|
276
|
+
# @param wavy_line [Boolean, nil] wavy line (trill extension)
|
|
277
|
+
# @param vertical_turn [Boolean, nil] vertical turn
|
|
278
|
+
# @param other_ornament [Boolean, nil] custom ornament
|
|
279
|
+
# @param ornament_accidental_mark [String, nil] ornament accidental: 'sharp', 'flat', 'natural'
|
|
280
|
+
#
|
|
281
|
+
# ### String Technique
|
|
282
|
+
# @param fingering [Fingering, Hash, nil] fingering indication
|
|
283
|
+
# @param up_bow [Boolean, nil] up bow (↑)
|
|
284
|
+
# @param down_bow [Boolean, nil] down bow (↓)
|
|
285
|
+
# @param harmonic [Harmonic, Hash, nil] harmonic
|
|
286
|
+
# @param open_string [Boolean, nil] open string (○)
|
|
287
|
+
# @param stopped [Boolean, nil] stopped note (+)
|
|
288
|
+
# @param snap_pizzicato [Boolean, nil] Bartók pizzicato
|
|
289
|
+
# @param thumb_position [Boolean, nil] cello thumb position
|
|
290
|
+
# @param string [Integer, nil] string number
|
|
291
|
+
# @param hammer_on [String, nil] hammer-on: 'start', 'stop'
|
|
292
|
+
# @param pull_off [String, nil] pull-off: 'start', 'stop'
|
|
293
|
+
#
|
|
294
|
+
# ### Wind Technique
|
|
295
|
+
# @param double_tongue [Boolean, nil] double tonguing
|
|
296
|
+
# @param triple_tongue [Boolean, nil] triple tonguing
|
|
297
|
+
# @param fingernails [Boolean, nil] use fingernails
|
|
298
|
+
# @param hole [Hole, Hash, nil] woodwind fingering hole
|
|
299
|
+
#
|
|
300
|
+
# ### Fretted Instrument Technique
|
|
301
|
+
# @param fret [Integer, nil] fret number
|
|
302
|
+
# @param bend [Bend, Hash, nil] string bend
|
|
303
|
+
# @param tap [String, nil] tapping
|
|
304
|
+
# @param pluck [String, nil] plucking technique
|
|
305
|
+
#
|
|
306
|
+
# ### Other Technical
|
|
307
|
+
# @param arrow [Arrow, Hash, nil] arrow indication
|
|
308
|
+
# @param handbell [String, nil] handbell technique: 'damp', 'echo', 'gyro', etc.
|
|
309
|
+
# @param heel [Boolean, nil] heel (organ pedal)
|
|
310
|
+
# @param toe [Boolean, nil] toe (organ pedal)
|
|
311
|
+
# @param other_technical [String, nil] custom technical text
|
|
312
|
+
#
|
|
313
|
+
# @yield Optional DSL block for setting properties
|
|
314
|
+
#
|
|
315
|
+
# @example Basic quarter note with staccato
|
|
316
|
+
# PitchedNote.new('C', octave: 4, duration: 2, type: 'quarter', staccato: true)
|
|
317
|
+
#
|
|
318
|
+
# @example Dotted eighth with slur start
|
|
319
|
+
# PitchedNote.new('D', octave: 5, duration: 3, type: 'eighth', dots: 1, slur: 'start')
|
|
320
|
+
#
|
|
321
|
+
# @example Grace note with accent
|
|
322
|
+
# PitchedNote.new('E', octave: 5, grace: true, type: 'eighth', accent: true)
|
|
323
|
+
#
|
|
324
|
+
# @example Multi-voice with fermata
|
|
325
|
+
# PitchedNote.new('G', octave: 4, duration: 4, type: 'half', voice: 2, fermata: true)
|
|
326
|
+
#
|
|
327
|
+
# For detailed parameter documentation, see {NoteComplexities::PARAMETERS}
|
|
328
|
+
#
|
|
329
|
+
# @api private (called by subclasses)
|
|
72
330
|
def initialize(*_rest,
|
|
73
331
|
pizzicato: nil, # true
|
|
74
332
|
# main content
|
|
@@ -351,6 +609,18 @@ module Musa
|
|
|
351
609
|
attr_simple_builder :triple_tongue
|
|
352
610
|
attr_simple_builder :up_bow
|
|
353
611
|
|
|
612
|
+
# Generates the note XML element.
|
|
613
|
+
#
|
|
614
|
+
# Outputs a complete `<note>` element with all sub-elements in MusicXML order:
|
|
615
|
+
# grace, cue, chord, pitch/rest/unpitched, duration, tie, voice, type, dots,
|
|
616
|
+
# accidental, time_modification, stem, notehead, staff, notations.
|
|
617
|
+
#
|
|
618
|
+
# @param io [IO] output stream
|
|
619
|
+
# @param indent [Integer] indentation level
|
|
620
|
+
# @param tabs [String] tab string
|
|
621
|
+
# @return [void]
|
|
622
|
+
#
|
|
623
|
+
# @api private
|
|
354
624
|
def _to_xml(io, indent:, tabs:)
|
|
355
625
|
io.puts "#{tabs}<note#{" pizzicato=\"yes\"" if @pizzicato}>"
|
|
356
626
|
|
|
@@ -488,8 +758,23 @@ module Musa
|
|
|
488
758
|
|
|
489
759
|
private
|
|
490
760
|
|
|
761
|
+
# Outputs note-type-specific XML content.
|
|
762
|
+
#
|
|
763
|
+
# Abstract method overridden by subclasses to output pitch, rest, or unpitched elements.
|
|
764
|
+
# Called during XML generation between chord and duration elements.
|
|
765
|
+
#
|
|
766
|
+
# @param io [IO] output stream
|
|
767
|
+
# @param indent [Integer] indentation level
|
|
768
|
+
# @return [void]
|
|
769
|
+
#
|
|
770
|
+
# @abstract Subclasses must implement this method
|
|
771
|
+
# @api private
|
|
491
772
|
def specific_to_xml(io, indent:); end
|
|
492
773
|
|
|
774
|
+
# Checks if any notation elements are present.
|
|
775
|
+
#
|
|
776
|
+
# @return [Boolean] true if any notations should be output
|
|
777
|
+
# @api private
|
|
493
778
|
def _notations
|
|
494
779
|
@accidental_mark ||
|
|
495
780
|
@arpeggiate ||
|
|
@@ -4,10 +4,83 @@ module Musa
|
|
|
4
4
|
module MusicXML
|
|
5
5
|
module Builder
|
|
6
6
|
module Internal
|
|
7
|
+
# Part group for bracketing multiple parts together.
|
|
8
|
+
#
|
|
9
|
+
# PartGroup represents the `<part-group>` element in the MusicXML part-list
|
|
10
|
+
# section. Part groups visually bracket or brace related parts together
|
|
11
|
+
# (e.g., string sections, choir SATB, piano grand staff).
|
|
12
|
+
#
|
|
13
|
+
# ## Usage
|
|
14
|
+
#
|
|
15
|
+
# Part groups are defined by matching start/stop pairs with the same number:
|
|
16
|
+
#
|
|
17
|
+
# <part-group number="1" type="start">
|
|
18
|
+
# <group-name>Strings</group-name>
|
|
19
|
+
# <group-symbol>bracket</group-symbol>
|
|
20
|
+
# </part-group>
|
|
21
|
+
# <score-part id="p1">...</score-part>
|
|
22
|
+
# <score-part id="p2">...</score-part>
|
|
23
|
+
# <part-group number="1" type="stop" />
|
|
24
|
+
#
|
|
25
|
+
# ## Nesting
|
|
26
|
+
#
|
|
27
|
+
# Groups can be nested using different numbers:
|
|
28
|
+
#
|
|
29
|
+
# <part-group number="1" type="start" name="Orchestra" />
|
|
30
|
+
# <part-group number="2" type="start" name="Strings" />
|
|
31
|
+
# <score-part id="vln1" />
|
|
32
|
+
# <score-part id="vln2" />
|
|
33
|
+
# <part-group number="2" type="stop" />
|
|
34
|
+
# <part-group number="1" type="stop" />
|
|
35
|
+
#
|
|
36
|
+
# ## Symbols
|
|
37
|
+
#
|
|
38
|
+
# Common bracket symbols:
|
|
39
|
+
# - **bracket**: Standard square bracket
|
|
40
|
+
# - **brace**: Curly brace (for piano, organ)
|
|
41
|
+
# - **line**: Simple vertical line
|
|
42
|
+
# - **square**: Square bracket (rare)
|
|
43
|
+
#
|
|
44
|
+
# @example String quartet grouping
|
|
45
|
+
# group_start = PartGroup.new(1,
|
|
46
|
+
# type: 'start',
|
|
47
|
+
# name: "String Quartet",
|
|
48
|
+
# symbol: 'bracket'
|
|
49
|
+
# )
|
|
50
|
+
# # ... add parts vln1, vln2, vla, vlc ...
|
|
51
|
+
# group_stop = PartGroup.new(1, type: 'stop')
|
|
52
|
+
#
|
|
53
|
+
# @example Piano grand staff
|
|
54
|
+
# PartGroup.new(1,
|
|
55
|
+
# type: 'start',
|
|
56
|
+
# symbol: 'brace',
|
|
57
|
+
# group_barline: true
|
|
58
|
+
# )
|
|
59
|
+
# # ... add parts for right hand and left hand ...
|
|
60
|
+
# PartGroup.new(1, type: 'stop')
|
|
7
61
|
class PartGroup
|
|
8
62
|
include Helper
|
|
9
63
|
include Helper::HeaderToXML
|
|
10
64
|
|
|
65
|
+
# Creates a part group declaration.
|
|
66
|
+
#
|
|
67
|
+
# @param number [Integer, nil] group number for matching start/stop pairs
|
|
68
|
+
# @param type [String] 'start' or 'stop'
|
|
69
|
+
# @param name [String, nil] group name displayed on bracket
|
|
70
|
+
# @param abbreviation [String, nil] abbreviated group name
|
|
71
|
+
# @param symbol [String, nil] bracket type: 'bracket', 'brace', 'line', 'square'
|
|
72
|
+
# @param group_barline [Boolean, String, nil] whether barlines connect across group
|
|
73
|
+
# @param group_time [Boolean, String, nil] whether time signatures are shared
|
|
74
|
+
#
|
|
75
|
+
# @example Start a bracket group
|
|
76
|
+
# PartGroup.new(1,
|
|
77
|
+
# type: 'start',
|
|
78
|
+
# name: "Woodwinds",
|
|
79
|
+
# symbol: 'bracket'
|
|
80
|
+
# )
|
|
81
|
+
#
|
|
82
|
+
# @example Stop a group
|
|
83
|
+
# PartGroup.new(1, type: 'stop')
|
|
11
84
|
def initialize(number = nil, # number
|
|
12
85
|
type:,
|
|
13
86
|
name: nil,
|
|
@@ -24,8 +97,42 @@ module Musa
|
|
|
24
97
|
@group_time = group_time
|
|
25
98
|
end
|
|
26
99
|
|
|
27
|
-
|
|
100
|
+
# Group number (for matching start/stop pairs).
|
|
101
|
+
# @return [Integer, nil]
|
|
102
|
+
attr_accessor :number
|
|
28
103
|
|
|
104
|
+
# Type: 'start' or 'stop'.
|
|
105
|
+
# @return [String]
|
|
106
|
+
attr_accessor :type
|
|
107
|
+
|
|
108
|
+
# Group name displayed on bracket.
|
|
109
|
+
# @return [String, nil]
|
|
110
|
+
attr_accessor :name
|
|
111
|
+
|
|
112
|
+
# Abbreviated group name.
|
|
113
|
+
# @return [String, nil]
|
|
114
|
+
attr_accessor :abbreviation
|
|
115
|
+
|
|
116
|
+
# Bracket symbol type.
|
|
117
|
+
# @return [String, nil]
|
|
118
|
+
attr_accessor :symbol
|
|
119
|
+
|
|
120
|
+
# Whether barlines connect across the group.
|
|
121
|
+
# @return [Boolean, String, nil]
|
|
122
|
+
attr_accessor :group_barline
|
|
123
|
+
|
|
124
|
+
# Whether time signatures are shared.
|
|
125
|
+
# @return [Boolean, String, nil]
|
|
126
|
+
attr_accessor :group_time
|
|
127
|
+
|
|
128
|
+
# Generates the part-group XML element for the part-list section.
|
|
129
|
+
#
|
|
130
|
+
# @param io [IO] output stream
|
|
131
|
+
# @param indent [Integer] indentation level
|
|
132
|
+
# @param tabs [String] tab string
|
|
133
|
+
# @return [void]
|
|
134
|
+
#
|
|
135
|
+
# @api private
|
|
29
136
|
def _header_to_xml(io, indent:, tabs:)
|
|
30
137
|
io.puts "#{tabs}<part-group#{ decode_bool_or_string_attribute(@number&.to_i, 'number') } type=\"#{@type}\">"
|
|
31
138
|
|
|
@@ -8,6 +8,43 @@ module Musa
|
|
|
8
8
|
module MusicXML
|
|
9
9
|
module Builder
|
|
10
10
|
module Internal
|
|
11
|
+
# Individual part (instrument/voice) in a score.
|
|
12
|
+
#
|
|
13
|
+
# Part represents a single instrument or voice in the score, containing
|
|
14
|
+
# a sequence of measures with musical content. Each part has a unique
|
|
15
|
+
# identifier, full name, and optional abbreviation.
|
|
16
|
+
#
|
|
17
|
+
# ## Structure
|
|
18
|
+
#
|
|
19
|
+
# A part contains:
|
|
20
|
+
# - **Header**: Declared in `<part-list>` with `<score-part>`
|
|
21
|
+
# - **Content**: Sequence of `<measure>` elements with notes, dynamics, etc.
|
|
22
|
+
#
|
|
23
|
+
# ## Usage
|
|
24
|
+
#
|
|
25
|
+
# Parts are typically created via {Musa::MusicXML::Builder::ScorePartwise#part} or {Musa::MusicXML::Builder::ScorePartwise#add_part}.
|
|
26
|
+
# Measures are added sequentially, automatically numbered starting from 1.
|
|
27
|
+
#
|
|
28
|
+
# @example Creating a part with measures
|
|
29
|
+
# part = Part.new(:p1, name: "Violin I", abbreviation: "Vln. I") do
|
|
30
|
+
# measure do
|
|
31
|
+
# attributes do
|
|
32
|
+
# divisions 4
|
|
33
|
+
# key fifths: 1 # G major
|
|
34
|
+
# time beats: 4, beat_type: 4
|
|
35
|
+
# clef sign: 'G', line: 2
|
|
36
|
+
# end
|
|
37
|
+
# pitch 'D', octave: 5, duration: 4, type: 'quarter'
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
# measure do
|
|
41
|
+
# pitch 'E', octave: 5, duration: 4, type: 'quarter'
|
|
42
|
+
# pitch 'F', octave: 5, duration: 4, type: 'quarter', alter: 1
|
|
43
|
+
# end
|
|
44
|
+
# end
|
|
45
|
+
#
|
|
46
|
+
# @see Measure Measure implementation
|
|
47
|
+
# @see ScorePartwise#part Main way to create parts
|
|
11
48
|
class Part
|
|
12
49
|
extend Musa::Extension::AttributeBuilder
|
|
13
50
|
include Musa::Extension::With
|
|
@@ -15,6 +52,31 @@ module Musa
|
|
|
15
52
|
include Helper::HeaderToXML
|
|
16
53
|
include Helper::ToXML
|
|
17
54
|
|
|
55
|
+
# Creates a new part.
|
|
56
|
+
#
|
|
57
|
+
# @param id [Symbol, String] unique part identifier (referenced in `<part>` elements)
|
|
58
|
+
# @param name [String] full part name (e.g., "Violin I", "Piano")
|
|
59
|
+
# @param abbreviation [String, nil] abbreviated name for subsequent systems
|
|
60
|
+
# @param first_measure_attributes [Hash] optional attributes for auto-created first measure
|
|
61
|
+
# @yield Optional DSL block for adding measures
|
|
62
|
+
#
|
|
63
|
+
# @example With abbreviation
|
|
64
|
+
# Part.new(:p1, name: "Violoncello", abbreviation: "Vc.")
|
|
65
|
+
#
|
|
66
|
+
# @example With first measure attributes
|
|
67
|
+
# Part.new(:p1,
|
|
68
|
+
# name: "Flute",
|
|
69
|
+
# divisions: 4,
|
|
70
|
+
# key_fifths: 0,
|
|
71
|
+
# time_beats: 3, time_beat_type: 4
|
|
72
|
+
# )
|
|
73
|
+
#
|
|
74
|
+
# @example With DSL block
|
|
75
|
+
# Part.new(:p1, name: "Clarinet") do
|
|
76
|
+
# measure do
|
|
77
|
+
# # measure content
|
|
78
|
+
# end
|
|
79
|
+
# end
|
|
18
80
|
def initialize(id, name:, abbreviation: nil, **first_measure_attributes, &block)
|
|
19
81
|
@id = id
|
|
20
82
|
@name = name
|
|
@@ -29,10 +91,67 @@ module Musa
|
|
|
29
91
|
with &block if block_given?
|
|
30
92
|
end
|
|
31
93
|
|
|
94
|
+
# Part ID builder/setter.
|
|
95
|
+
#
|
|
96
|
+
# @overload id(value)
|
|
97
|
+
# Sets part ID via DSL
|
|
98
|
+
# @param value [Symbol, String] part identifier
|
|
99
|
+
# @overload id=(value)
|
|
100
|
+
# Sets part ID via assignment
|
|
101
|
+
# @param value [Symbol, String] part identifier
|
|
32
102
|
attr_simple_builder :id
|
|
103
|
+
|
|
104
|
+
# Part name builder/setter.
|
|
105
|
+
#
|
|
106
|
+
# @overload name(value)
|
|
107
|
+
# Sets part name via DSL
|
|
108
|
+
# @param value [String] part name
|
|
109
|
+
# @overload name=(value)
|
|
110
|
+
# Sets part name via assignment
|
|
111
|
+
# @param value [String] part name
|
|
33
112
|
attr_simple_builder :name
|
|
113
|
+
|
|
114
|
+
# Part abbreviation builder/setter.
|
|
115
|
+
#
|
|
116
|
+
# @overload abbreviation(value)
|
|
117
|
+
# Sets abbreviation via DSL
|
|
118
|
+
# @param value [String] abbreviated name
|
|
119
|
+
# @overload abbreviation=(value)
|
|
120
|
+
# Sets abbreviation via assignment
|
|
121
|
+
# @param value [String] abbreviated name
|
|
34
122
|
attr_simple_builder :abbreviation
|
|
35
123
|
|
|
124
|
+
# Adds a measure to the part.
|
|
125
|
+
#
|
|
126
|
+
# Measures are automatically numbered sequentially starting from 1.
|
|
127
|
+
# The first measure typically contains attributes (key, time, clef, divisions).
|
|
128
|
+
#
|
|
129
|
+
# @option divisions [Integer, nil] divisions per quarter note (timing resolution)
|
|
130
|
+
# @option key_cancel [Integer, nil] key cancellation
|
|
131
|
+
# @option key_fifths [Integer, nil] key signature (circle of fifths: -7 to +7)
|
|
132
|
+
# @option key_mode [String, nil] mode (major/minor)
|
|
133
|
+
# @option time_senza_misura [Boolean, nil] unmeasured time
|
|
134
|
+
# @option time_beats [Integer, nil] time signature numerator
|
|
135
|
+
# @option time_beat_type [Integer, nil] time signature denominator
|
|
136
|
+
# @option clef_sign [String, nil] clef sign (G/F/C)
|
|
137
|
+
# @option clef_line [Integer, nil] clef line number
|
|
138
|
+
# @option clef_octave_change [Integer, nil] octave transposition
|
|
139
|
+
# @yield Optional DSL block for measure content
|
|
140
|
+
# @return [Measure] the created measure
|
|
141
|
+
#
|
|
142
|
+
# @example First measure with attributes
|
|
143
|
+
# part.add_measure(
|
|
144
|
+
# divisions: 4,
|
|
145
|
+
# key_fifths: 2, # D major
|
|
146
|
+
# time_beats: 4, time_beat_type: 4,
|
|
147
|
+
# clef_sign: 'G', clef_line: 2
|
|
148
|
+
# )
|
|
149
|
+
#
|
|
150
|
+
# @example Measure with DSL block
|
|
151
|
+
# part.measure do
|
|
152
|
+
# pitch 'C', octave: 4, duration: 4, type: 'quarter'
|
|
153
|
+
# rest duration: 4, type: 'quarter'
|
|
154
|
+
# end
|
|
36
155
|
attr_complex_adder_to_custom :measure, variable: :@measures do
|
|
37
156
|
| divisions: nil,
|
|
38
157
|
key_cancel: nil, key_fifths: nil, key_mode: nil,
|
|
@@ -46,6 +165,16 @@ module Musa
|
|
|
46
165
|
clef_sign: clef_sign, clef_line: clef_line, clef_octave_change: clef_octave_change).tap { |measure| @measures << measure }
|
|
47
166
|
end
|
|
48
167
|
|
|
168
|
+
# Generates the part declaration for the part-list section.
|
|
169
|
+
#
|
|
170
|
+
# Creates a `<score-part>` element with part name and optional abbreviation.
|
|
171
|
+
#
|
|
172
|
+
# @param io [IO] output stream
|
|
173
|
+
# @param indent [Integer] indentation level
|
|
174
|
+
# @param tabs [String] tab string
|
|
175
|
+
# @return [void]
|
|
176
|
+
#
|
|
177
|
+
# @api private
|
|
49
178
|
def _header_to_xml(io, indent:, tabs:)
|
|
50
179
|
io.puts "#{tabs}<score-part id=\"#{@id}\">"
|
|
51
180
|
io.puts "#{tabs}\t<part-name>#{@name}</part-name>"
|
|
@@ -53,6 +182,16 @@ module Musa
|
|
|
53
182
|
io.puts "#{tabs}</score-part>"
|
|
54
183
|
end
|
|
55
184
|
|
|
185
|
+
# Generates the part content with all measures.
|
|
186
|
+
#
|
|
187
|
+
# Creates a `<part>` element containing all measures in sequence.
|
|
188
|
+
#
|
|
189
|
+
# @param io [IO] output stream
|
|
190
|
+
# @param indent [Integer] indentation level
|
|
191
|
+
# @param tabs [String] tab string
|
|
192
|
+
# @return [void]
|
|
193
|
+
#
|
|
194
|
+
# @api private
|
|
56
195
|
def _to_xml(io, indent:, tabs:)
|
|
57
196
|
io.puts "#{tabs}<part id=\"#{@id}\">"
|
|
58
197
|
@measures.each do |measure|
|