Module: Musa::Datasets::Score::ToMXML Private
- Included in:
- Musa::Datasets::Score
- Defined in:
- lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb,
lib/musa-dsl/datasets/score/to-mxml/process-ps.rb,
lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb,
lib/musa-dsl/datasets/score/to-mxml/process-time.rb
Overview
This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.
MusicXML export for scores.
ToMXML provides conversion of Musa::Datasets::Score objects to MusicXML format, suitable for import into notation software like MuseScore, Finale, or Sibelius.
Conversion Process
-
Creates MusicXML structure with metadata (title, creators, etc.)
-
Defines parts (instruments) with clefs and time signatures
-
Divides score into measures (bars)
-
Processes events in each measure:
-
Fills gaps with rests
Event Types Supported
- PDV (Pitch/Duration/Velocity): Converted to notes or rests
- PS (Pitch Series): Converted to dynamics markings
Multi-part Scores
Scores can contain multiple instruments, differentiated by the :instrument attribute. Each part is rendered separately.
Time Representation
Positions and durations are counted in BARS, and they are 1-based: bar
1 starts at position 1r, bar 2 at 2r, and a duration of 1r lasts a
whole bar whatever the meter. That is what the sequencer counts, and this
follows it.
A figure's NAME, on the other hand, is a fraction of a whole note, which is
a different thing: a bar measures beats_per_bar / beat_type whole notes.
In 4/4 the two coincide -- a bar is a whole note -- which is why one number
served for both for a long time and why nothing outside 4/4 came out right
(issue #70). So:
- in 4/4,
duration: 1/4ris a quarter note; - in 3/4,
duration: 1/4ris a quarter of a bar, which is written as a dotted eighth, and a quarter note isduration: 1/3r; - in 6/8,
duration: 1/6ris a beat, which is written as an eighth.
The conversion happens here, at the only place that has to name figures. Nothing else in the library needs to know about whole notes.
Instance Method Summary collapse
-
#to_mxml(beats_per_bar, ticks_per_beat, beat_type: nil, bpm: nil, title: nil, creators: nil, encoding_date: nil, parts:, logger: nil, do_log: nil) ⇒ Musa::MusicXML::Builder::ScorePartwise
Converts score to MusicXML.
Instance Method Details
#to_mxml(beats_per_bar, ticks_per_beat, beat_type: nil, bpm: nil, title: nil, creators: nil, encoding_date: nil, parts:, logger: nil, do_log: nil) ⇒ Musa::MusicXML::Builder::ScorePartwise
Converts score to MusicXML.
Creates complete MusicXML document with metadata, parts, measures, notes, rests, and dynamics markings.
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb', line 161 def to_mxml(, ticks_per_beat, beat_type: nil, bpm: nil, title: nil, creators: nil, encoding_date: nil, parts:, logger: nil, do_log: nil) beat_type ||= 4 bpm ||= 90 # THE TWO UNITS. A duration in this library is a fraction of a BAR -- # `1r` is a bar, and that is what the sequencer counts. The name a # duration is written with is a fraction of a WHOLE NOTE. A bar measures # `beats_per_bar / beat_type` whole notes, and the two readings coincide # only in 4/4, which is why they were the same number for so long and # why nothing outside 4/4 came out right (issue #70). # # Everything about time here is in bars; `bar_duration` is what the # notation side multiplies by, and nothing else. = Rational(, beat_type) # A beat is `4 / beat_type` quarters, so a quarter holds this many of the # sequencer's ticks. It has to be a whole number of them: MusicXML has no # way to say "half a division". divisions_per_quarter = Rational(ticks_per_beat * beat_type, 4) raise ArgumentError, "A quarter note is #{divisions_per_quarter} ticks in #{}/#{beat_type} " \ "with #{ticks_per_beat} ticks per beat, and MusicXML divisions must be whole. " \ "Use a ticks_per_beat that is a multiple of #{Rational(4, beat_type).numerator}." \ unless divisions_per_quarter.denominator == 1 divisions_per_quarter = divisions_per_quarter.to_i title ||= 'Untitled' creators ||= { composer: 'Unknown' } if logger.nil? logger = Musa::Logger::Logger.new logger.debug! if do_log end do_log ||= nil mxml = Musa::MusicXML::Builder::ScorePartwise.new do |_| _.work_title title _.creators **creators _.encoding_date encoding_date if encoding_date parts.each_pair do |id, part_info| _.part id, name: part_info&.[](:name), abbreviation: part_info&.[](:abbreviation) do |_| _.measure do |_| _.attributes do |_| # MusicXML counts divisions per QUARTER NOTE, and a beat is a # quarter only when beat_type is 4. In 6/8 with 24 ticks to the # beat there are 48 of them in a quarter, not 24. _.divisions divisions_per_quarter i = 0 (part_info&.[](:clefs) || { g: 2 }).each_pair do |clef, line| i += 1 _.clef i, sign: clef.upcase, line: line _.time i, beats: , beat_type: beat_type end end _.metronome placement: 'above', beat_unit: 'quarter', per_minute: bpm end end end end if do_log logger.debug "" logger.debug"score.to_mxml log:" logger.debug"------------------" end parts.each_key do |part_id| fill_part mxml.parts[part_id], * ticks_per_beat, , (parts.size > 1 ? part_id : nil), logger, do_log end mxml end |