Class: Musa::MusicXML::Builder::Internal::Measure Private

Inherits:
Object
  • Object
show all
Extended by:
Extension::AttributeBuilder
Includes:
Extension::With, Helper::ToXML
Defined in:
lib/musa-dsl/musicxml/builder/measure.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Measure container for musical content.

Measure represents a single measure (bar) of music, containing musical elements in chronological order: attributes, notes, rests, backup/forward commands, and directions (dynamics, tempo markings, etc.).

Element Order

Elements within a measure follow MusicXML's sequential model:

  1. Attributes (key, time, clef, divisions) - typically in first measure
  2. Directions (tempo, dynamics) - before or between notes
  3. Notes/Rests - musical content
  4. Backup/Forward - timeline navigation for multiple voices/staves

Multiple Voices and Staves

For piano (grand staff) or polyphonic notation, use backup to rewind the timeline:

measure do
  # Right hand (treble clef)
  pitch 'C', octave: 5, duration: 4, type: 'quarter', staff: 1
  pitch 'D', octave: 5, duration: 4, type: 'quarter', staff: 1

  backup 8  # Rewind to start of measure

  # Left hand (bass clef)
  pitch 'C', octave: 3, duration: 8, type: 'half', staff: 2
end

Divisions

The divisions attribute sets timing resolution (divisions per quarter note). Higher values allow finer rhythmic subdivisions:

  • divisions: 1 → quarter, half, whole only
  • divisions: 2 → adds eighths
  • divisions: 4 → adds sixteenths
  • divisions: 8 → adds thirty-seconds
  • divisions: 16 → allows complex tuplets

Duration is calculated as: duration = (note_type_value * divisions) / beat_type

The measure the per-method examples are written against

measure = Measure.new(1, divisions: 4)

Examples:

Simple measure with quarter notes

measure = Measure.new(1, divisions: 2) do
  attributes do
    key fifths: 0  # C major
    time beats: 4, beat_type: 4
    clef sign: 'G', line: 2
  end

  pitch 'C', octave: 4, duration: 2, type: 'quarter'
  pitch 'D', octave: 4, duration: 2, type: 'quarter'
  pitch 'E', octave: 4, duration: 2, type: 'quarter'
  pitch 'F', octave: 4, duration: 2, type: 'quarter'
end

xml = measure.to_xml.string

xml.lines.first.strip                     # => "<measure number=\"1\">"
xml.scan(/<step>(\w)<\/step>/).flatten    # => ["C", "D", "E", "F"]

# `divisions` came from the constructor, the rest from the block:
xml.lines.map(&:strip).grep(/divisions|fifths|beats|beat-type/)
# => ["<divisions>2</divisions>", "<fifths>0</fifths>",
#     "<beats>4</beats>", "<beat-type>4</beat-type>"]

Measure with dynamics and tempo

measure = Measure.new(2, divisions: 4) do
  metronome beat_unit: 'quarter', per_minute: 120

  direction do
    dynamics 'p'
    wedge 'crescendo'
  end

  pitch 'C', octave: 4, duration: 4, type: 'quarter'
  pitch 'D', octave: 4, duration: 4, type: 'quarter'

  direction do
    wedge 'stop'
    dynamics 'f'
  end
end

xml = measure.to_xml.string

# `metronome` at measure level becomes a <direction> of its own, so
# what comes out is three directions around the two notes:
xml.scan(/<(direction|note)[ >]/).flatten
# => ["direction", "direction", "note", "note", "direction"]

# and the marks, in the order they were written:
xml.scan(/<wedge type="(\w+)"|<dynamics>\s*<(\w+) \/>/).map { |a, b| a || b }
# => ["p", "crescendo", "stop", "f"]

xml.lines.map(&:strip).grep(/beat-unit|per-minute/)
# => ["<beat-unit>quarter</beat-unit>", "<per-minute>120</per-minute>"]

See Also:

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(number, divisions: nil, key_cancel: nil, key_fifths: nil, key_mode: nil, time_senza_misura: nil, time_beats: nil, time_beat_type: nil, clef_sign: nil, clef_line: nil, clef_octave_change: nil) { ... } ⇒ Measure

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Creates a new measure.

Examples:

First measure with all attributes

Measure.new(1,
  divisions: 4,
  key_fifths: 2,  # D major
  time_beats: 3, time_beat_type: 4,
  clef_sign: 'G', clef_line: 2
)

Measure with DSL block

Measure.new(2) do
  pitch 'E', octave: 4, duration: 4, type: 'quarter'
  rest duration: 4, type: 'quarter'
end

Parameters:

  • number (Integer)

    measure number (automatically assigned by Part)

  • divisions (Integer, nil) (defaults to: nil)

    divisions per quarter note (timing resolution)

  • key_cancel (Integer, nil) (defaults to: nil)

    key cancellation

  • key_fifths (Integer, nil) (defaults to: nil)

    key signature (-7 to +7, circle of fifths)

  • key_mode (String, nil) (defaults to: nil)

    mode ('major' or 'minor')

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

    unmeasured time

  • time_beats (Integer, nil) (defaults to: nil)

    time signature numerator

  • time_beat_type (Integer, nil) (defaults to: nil)

    time signature denominator

  • clef_sign (String, nil) (defaults to: nil)

    clef sign ('G', 'F', 'C')

  • clef_line (Integer, nil) (defaults to: nil)

    clef line number

  • clef_octave_change (Integer, nil) (defaults to: nil)

    octave transposition

Yields:

  • Optional DSL block for adding measure content



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 155

def initialize(number, divisions: nil,
               key_cancel: nil, key_fifths: nil, key_mode: nil,
               time_senza_misura: nil, time_beats: nil, time_beat_type: nil,
               clef_sign: nil, clef_line: nil, clef_octave_change: nil,
               &block)

  @number = number
  @elements = []

  @attributes = []

  if divisions ||
      key_cancel || key_fifths || key_mode ||
      time_senza_misura || time_beats || time_beat_type ||
      clef_sign || clef_line || clef_octave_change

    add_attributes divisions: divisions,
                   key_cancel: key_cancel, key_fifths: key_fifths, key_mode: key_mode,
                   time_senza_misura: time_senza_misura, time_beats: time_beats, time_beat_type: time_beat_type,
                   clef_sign: clef_sign, clef_line: clef_line, clef_octave_change: clef_octave_change
  end

  with &block if block_given?
end

Instance Attribute Details

#elementsArray<Object> (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Ordered list of elements in this measure.

Returns:

  • (Array<Object>)

    notes, rests, attributes, directions, etc.



186
187
188
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 186

def elements
  @elements
end

#numberInteger

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Measure number.

Returns:

  • (Integer)


182
183
184
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 182

def number
  @number
end

Class Method Details

.attr_complex_adder_to_array(name, klass, plural: nil, variable: nil) ⇒ Object Originally defined in module Extension::AttributeBuilder

Creates methods for adding complex objects (with multiple parameters) to an array.

Supports both positional and keyword arguments when creating instances.

Parameters:

  • name (Symbol)

    singular name.

  • klass (Class)

    class to instantiate.

  • plural (Symbol, nil) (defaults to: nil)

    plural name.

  • variable (Symbol, nil) (defaults to: nil)

    instance variable name.

.attr_complex_adder_to_custom(name, plural: nil, variable: nil) { ... } ⇒ Object Originally defined in module Extension::AttributeBuilder

Creates methods for adding complex objects with custom construction logic.

The block receives parameters and should construct and add the object.

Parameters:

  • name (Symbol)

    singular name.

  • plural (Symbol, nil) (defaults to: nil)

    plural name.

  • variable (Symbol, nil) (defaults to: nil)

    instance variable name.

Yields:

  • Constructor block executed in instance context.

.attr_complex_builder(name, klass, variable: nil, first_parameter: nil) ⇒ Object Originally defined in module Extension::AttributeBuilder

Creates a getter/setter DSL method for complex objects with multiple parameters.

Supports optional first_parameter that's automatically prepended when constructing.

Parameters:

  • name (Symbol)

    attribute name.

  • klass (Class)

    class to instantiate.

  • variable (Symbol, nil) (defaults to: nil)

    instance variable name.

  • first_parameter (Object, nil) (defaults to: nil)

    parameter automatically prepended to constructor.

.attr_simple_builder(name, klass = nil, variable: nil) ⇒ Object Originally defined in module Extension::AttributeBuilder

Creates a simple getter/setter DSL method for a single value.

Parameters:

  • name (Symbol)

    attribute name.

  • klass (Class, nil) (defaults to: nil)

    class to instantiate (nil = use value as-is).

  • variable (Symbol, nil) (defaults to: nil)

    instance variable name.

.attr_tuple_adder_to_array(name, klass, plural: nil, variable: nil) ⇒ Object Originally defined in module Extension::AttributeBuilder

Creates methods for adding id/value tuples to an array collection.

Similar to attr_tuple_adder_to_hash but stores items in an array instead of hash. Useful when order matters or duplicates are allowed.

Parameters:

  • name (Symbol)

    singular name for the item.

  • klass (Class)

    class to instantiate.

  • plural (Symbol, nil) (defaults to: nil)

    plural name.

  • variable (Symbol, nil) (defaults to: nil)

    instance variable name.

.attr_tuple_adder_to_hash(name, klass, plural: nil, variable: nil) ⇒ Object Originally defined in module Extension::AttributeBuilder

Creates methods for adding id/value tuples to a hash collection.

Generates:

  • add_#{name}(id, parameter) → creates instance and adds to hash
  • #{plural}(**parameters) → batch add or retrieve hash

Parameters:

  • name (Symbol)

    singular name for the item.

  • klass (Class)

    class to instantiate (receives id, parameter).

  • plural (Symbol, nil) (defaults to: nil)

    plural name (defaults to name + 's').

  • variable (Symbol, nil) (defaults to: nil)

    instance variable name (defaults to '@' + plural).

.attr_tuple_builder(name, klass, variable: nil) ⇒ Object Originally defined in module Extension::AttributeBuilder

Creates a getter/setter DSL method for a single id/value tuple.

Parameters:

  • name (Symbol)

    attribute name.

  • klass (Class)

    class to instantiate.

  • variable (Symbol, nil) (defaults to: nil)

    instance variable name.

Instance Method Details

#_to_xml(io, indent:, tabs:) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Generates the measure XML element with all contained elements.

Outputs elements in the order they were added, which must follow MusicXML's element ordering rules (attributes, then notes/directions/backup/forward).

Parameters:

  • io (IO)

    output stream

  • indent (Integer)

    indentation level

  • tabs (String)

    tab string



436
437
438
439
440
441
442
443
444
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 436

def _to_xml(io, indent:, tabs:)
  io.puts "#{tabs}<measure number=\"#{@number.to_i}\">"

  @elements.each do |element|
    element.to_xml(io, indent: indent + 1)
  end

  io.puts "#{tabs}</measure>"
end

#attributes { ... } ⇒ Attributes

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds musical attributes to the measure.

Attributes define key signature, time signature, clef, and timing divisions. Typically appear at the start of the first measure or when they change.

Examples:

Via DSL block

measure.attributes do
  divisions 4
  key fifths: 1  # G major
  time beats: 3, beat_type: 4
  clef sign: 'G', line: 2
end

Yields:

  • Optional DSL block for adding keys, times, clefs

Returns:



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 213

attr_complex_adder_to_custom :attributes, plural: :attributes, variable: :@attributes do
| divisions: nil,
    key_cancel: nil, key_fifths: nil, key_mode: nil,
    time_senza_misura: nil, time_beats: nil, time_beat_type: nil,
    clef_sign: nil, clef_line: nil, clef_octave_change: nil,
    &block |

  Attributes.new(divisions: divisions,
                 key_cancel: key_cancel, key_fifths: key_fifths, key_mode: key_mode,
                 time_senza_misura: time_senza_misura, time_beats: time_beats, time_beat_type: time_beat_type,
                 clef_sign: clef_sign, clef_line: clef_line, clef_octave_change: clef_octave_change, &block) \
        .tap do |attributes|

    @attributes << attributes
    @elements << attributes
  end
end

#backupBackup

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Rewinds the musical timeline.

Backup moves the current time position backward by the specified duration, allowing multiple voices or staves to be layered in the same time span.

Examples:

Piano grand staff

Measure.new(1, divisions: 2) do
  pitch 'C', octave: 5, duration: 8, type: 'half', staff: 1
  backup 8  # Rewind to start
  pitch 'C', octave: 3, duration: 8, type: 'half', staff: 2
end

Returns:

  • (Backup)

    the created backup element

See Also:



281
282
283
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 281

attr_complex_adder_to_custom :backup do |duration|
  Backup.new(duration).tap { |backup| @elements << backup }
end

#bracketDirection

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds bracket notation.

Returns:

  • (Direction)

    direction containing bracket



387
388
389
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 387

attr_complex_adder_to_custom(:bracket) {
|*p, placement: nil, offset: nil, **kp, &b|
direction(placement: placement, offset: offset) { bracket *p, **kp, &b } }

#dashesDirection

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds dashed line.

Returns:

  • (Direction)

    direction containing dashes



396
397
398
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 396

attr_complex_adder_to_custom(:dashes) {
|*p, placement: nil, offset: nil, **kp, &b|
direction(placement: placement, offset: offset) { dashes *p, **kp, &b } }

#direction { ... } ⇒ Direction

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds a direction element (dynamics, tempo, expressions).

Directions contain non-note musical instructions like dynamics (p, f), tempo markings, wedges (crescendo/diminuendo), pedal marks, etc.

Examples:

Dynamics with crescendo

measure.direction do
  dynamics 'p'
  wedge 'crescendo'
end

Yields:

  • Optional DSL block for direction content

Returns:

See Also:



317
318
319
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 317

attr_complex_adder_to_custom :direction do |*parameters, **key_parameters, &block|
  Direction.new(*parameters, **key_parameters, &block).tap { |direction| @elements << direction }
end

#dynamicsDirection

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds dynamics (p, pp, f, ff, etc.).

Examples:

measure.dynamics 'pp'
measure.dynamics ['mf', 'sf']  # Multiple dynamics

Returns:

  • (Direction)

    direction containing dynamics



364
365
366
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 364

attr_complex_adder_to_custom(:dynamics) {
|*p, placement: nil, offset: nil, **kp, &b|
direction(placement: placement, offset: offset) { dynamics *p, **kp, &b } }

#forwardForward

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Advances the musical timeline.

Forward moves the current time position forward without sounding, creating rests or gaps in the timeline.

Examples:

Skip to beat 3

Measure.new(1, divisions: 2) do
  pitch 'C', octave: 4, duration: 2, type: 'quarter'
  forward 4  # Skip 2 beats
  pitch 'D', octave: 4, duration: 2, type: 'quarter'
end

Returns:

  • (Forward)

    the created forward element



298
299
300
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 298

attr_complex_adder_to_custom :forward do |duration, voice: nil, staff: nil|
  Forward.new(duration, voice: voice, staff: staff).tap { |forward| @elements << forward }
end

#metronome { ... } ⇒ Direction

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds a metronome (tempo) marking.

Examples:

measure.metronome beat_unit: 'quarter', per_minute: 120

Yields:

  • Optional block

Returns:

  • (Direction)

    direction containing metronome



338
339
340
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 338

attr_complex_adder_to_custom(:metronome) {
|*p, placement: nil, offset: nil, **kp, &b|
direction(placement: placement, offset: offset) { metronome *p, **kp, &b } }

#octave_shiftDirection

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds octave shift (8va/8vb).

Examples:

measure.octave_shift 'up', size: 8

Returns:

  • (Direction)

    direction containing octave_shift



421
422
423
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 421

attr_complex_adder_to_custom(:octave_shift) {
|*p, placement: nil, offset: nil, **kp, &b|
direction(placement: placement, offset: offset) { octave_shift *p, **kp, &b } }

#pedalDirection

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds pedal marking.

Examples:

measure.pedal 'start', line: true

Returns:



377
378
379
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 377

attr_complex_adder_to_custom(:pedal) {
|*p, placement: nil, offset: nil, **kp, &b|
direction(placement: placement, offset: offset) { pedal *p, **kp, &b } }

#pitchPitchedNote

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds a pitched note.

Examples:

measure.pitch 'C', octave: 4, duration: 4, type: 'quarter'
measure.pitch step: 'E', octave: 4, duration: 2, type: 'eighth', dots: 1

Returns:

See Also:



240
241
242
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 240

attr_complex_adder_to_custom :pitch do | *parameters, **key_parameters |
  PitchedNote.new(*parameters, **key_parameters).tap { |note| @elements << note }
end

#restRest

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds a rest.

Examples:

measure.rest duration: 4, type: 'quarter'
measure.rest duration: 8, type: 'half', measure: true  # whole measure rest

Returns:

  • (Rest)

    the created rest

See Also:



253
254
255
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 253

attr_complex_adder_to_custom :rest do | *parameters, **key_parameters |
  Rest.new(*parameters, **key_parameters).tap { |rest| @elements << rest }
end

#to_xml(io = nil, indent: nil) ⇒ IO, StringIO Originally defined in module Helper::ToXML

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Converts the object to MusicXML format.

This method sets up the IO stream and indentation, then delegates to the private _to_xml method for actual XML generation.

Examples:

Writing to file

element = Measure.new(1, divisions: 2)
File.open('output.xml', 'w') do |f|
  element.to_xml(f)
end

Getting XML as string

xml_string = element.to_xml.string
xml_string.start_with?('<measure')  # => true

Parameters:

  • io (IO, StringIO, nil) (defaults to: nil)

    output stream (creates StringIO if nil)

  • indent (Integer, nil) (defaults to: nil)

    indentation level (default: 0)

Returns:

  • (IO, StringIO)

    the io parameter, containing the XML output

#unpitchedUnpitchedNote

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds an unpitched note (for percussion).

Returns:

See Also:



262
263
264
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 262

attr_complex_adder_to_custom :unpitched do | *parameters, **key_parameters |
  UnpitchedNote.new(*parameters, **key_parameters).tap { |unpitched| @elements << unpitched }
end

#wedgeDirection

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds a wedge (crescendo/diminuendo).

Examples:

measure.wedge 'crescendo', niente: true

Returns:



351
352
353
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 351

attr_complex_adder_to_custom(:wedge) {
|*p, placement: nil, offset: nil, **kp, &b|
direction(placement: placement, offset: offset) { wedge *p, **kp, &b } }

#with(*value_parameters, keep_block_context: nil, **key_parameters, &block) ⇒ Object Originally defined in module Extension::With

Note:

The _ parameter is special: when present, it signals "keep caller's context" and receives self (the object) as its value.

Note:

Uses SmartProcBinder internally to handle parameter matching.

Executes a block with flexible context and parameter handling.

Parameters:

  • value_parameters (Array)

    positional parameters to pass to block.

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

    explicit control of context switching:

    • true: always keep caller's context
    • false: always use object's context
    • nil: auto-detect based on _ parameter
  • key_parameters (Hash)

    keyword parameters to pass to block.

  • block (Proc)

    block to execute.

Returns:

  • (Object)

    result of block execution.

#wordsDirection

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds text annotation.

Examples:

measure.words "rit.", placement: 'above'

Returns:



408
409
410
# File 'lib/musa-dsl/musicxml/builder/measure.rb', line 408

attr_complex_adder_to_custom(:words) {
|*p, placement: nil, offset: nil, **kp, &b|
direction(placement: placement, offset: offset) { words *p, **kp, &b } }