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.

Time and duration processing for MusicXML export.

This module provides helper methods for converting musical durations to MusicXML note types, dots, and tuplet ratios. Handles the complex mathematics of decomposing arbitrary rational durations into standard notation elements.

Duration Representation

Durations are Rational numbers where 1 = one beat (typically quarter note).

  • 1r = quarter note
  • 1/2r = eighth note
  • 3/2r = dotted quarter
  • 1/3r = eighth note triplet

Decomposition Process

  1. Decompose: Break duration into sum of simple durations (powers of 2)
  2. Integrate: Combine consecutive halves into dotted notes
  3. Type & Dots: Determine note type and dot count
  4. Tuplet Ratio: Calculate tuplet modification if needed

Instance Method Summary collapse

Instance Method Details

#to_mxml(beats_per_bar, ticks_per_beat, 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.

Examples:

Simple piano score

score = Musa::Datasets::Score.new
score.at(1r, add: { pitch: 60, duration: 1.0 }.extend(Musa::Datasets::PDV))

mxml = score.to_mxml(
  4, 24,
  bpm: 120,
  title: 'Invention',
  creators: { composer: 'J.S. Bach' },
  parts: { piano: { name: 'Piano', clefs: { g: 2, f: 4 } } }
)

String quartet

score = Musa::Datasets::Score.new
score.at(1r, add: { instrument: :vln1, pitch: 67, duration: 1.0 }.extend(Musa::Datasets::PDV))
score.at(1r, add: { instrument: :vln2, pitch: 64, duration: 1.0 }.extend(Musa::Datasets::PDV))
score.at(1r, add: { instrument: :vla, pitch: 60, duration: 1.0 }.extend(Musa::Datasets::PDV))
score.at(1r, add: { instrument: :vc, pitch: 48, duration: 1.0 }.extend(Musa::Datasets::PDV))

mxml = score.to_mxml(
  4, 24,
  parts: {
    vln1: { name: 'Violin I', abbreviation: 'Vln. I', clefs: { g: 2 } },
    vln2: { name: 'Violin II', abbreviation: 'Vln. II', clefs: { g: 2 } },
    vla: { name: 'Viola', abbreviation: 'Vla.', clefs: { c: 3 } },
    vc: { name: 'Cello', abbreviation: 'Vc.', clefs: { f: 4 } }
  }
)

Export to file

score = Musa::Datasets::Score.new
score.at(1r, add: { pitch: 60, duration: 1.0 }.extend(Musa::Datasets::PDV))

mxml = score.to_mxml(4, 24, parts: { piano: { name: 'Piano' } })
File.write('output.musicxml', mxml.to_xml.string)

Parameters:

  • beats_per_bar (Integer)

    time signature numerator (e.g., 4 for 4/4)

  • ticks_per_beat (Integer)

    resolution per beat (typically 24)

  • bpm (Integer) (defaults to: nil)

    tempo in beats per minute (default: 90)

  • title (String) (defaults to: nil)

    work title (default: 'Untitled')

  • creators (Hash{Symbol => String}) (defaults to: nil)

    creator roles and names (default: { composer: 'Unknown' })

  • encoding_date (DateTime, nil) (defaults to: nil)

    encoding date for metadata

  • parts (Hash{Symbol => Hash})

    part definitions Each part: { name: String, abbreviation: String, clefs: Hash } Clefs: { clef_sign: line_number } (e.g., { g: 2, f: 4 } for piano)

  • logger (Musa::Logger::Logger, nil) (defaults to: nil)

    logger for debugging

  • do_log (Boolean, nil) (defaults to: nil)

    enable logging output

Returns:



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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
# File 'lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb', line 140

def to_mxml(beats_per_bar, ticks_per_beat,
            bpm: nil,
            title: nil,
            creators: nil,
            encoding_date: nil,
            parts:,
            logger: nil,
            do_log: nil)

  bpm ||= 90
  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 |_|
            _.divisions ticks_per_beat

            i = 0
            (part_info&.[](:clefs) || { g: 2 }).each_pair do |clef, line|
              i += 1
              _.clef i, sign: clef.upcase, line: line
              _.time i, beats: beats_per_bar, beat_type: 4
            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],
              beats_per_bar * ticks_per_beat,
              (parts.size > 1 ? part_id : nil),
              logger, do_log
  end

  mxml
end