Class: Musa::MusicXML::Builder::ScorePartwise

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

Overview

Main entry point for creating MusicXML scores.

ScorePartwise represents the root <score-partwise> element of a MusicXML 3.0 document. It contains metadata (work info, creators, rights), part definitions, and the actual musical content organized by parts and measures.

Structure

A ScorePartwise document contains:

  • Metadata: work title/number, movement title/number, creators, rights
  • Part List: part and part-group declarations with names/abbreviations
  • Parts: actual musical content (measures with notes, dynamics, etc.)

Usage Patterns

Constructor Style

Set properties via constructor parameters and use add_* methods:

score = ScorePartwise.new(
  work_title: "Symphony No. 1",
  creators: { composer: "Composer Name" }
)
part = score.add_part(:p1, name: "Violin")
measure = part.add_measure(divisions: 2)

DSL Style

Use blocks with method names as setters/builders:

score = ScorePartwise.new do
  work_title "Symphony No. 1"
  creators composer: "Composer Name"
  part :p1, name: "Violin" do
    measure do
      # measure content
    end
  end
end

Part Groups

Parts can be organized into groups (for orchestral sections, etc.):

score.add_group 1, type: 'start', name: "Strings"
score.add_part :p1, name: "Violin I"
score.add_part :p2, name: "Violin II"
score.add_group 1, type: 'stop'

XML Output

Generate MusicXML 3.0 compliant XML:

File.open('score.xml', 'w') do |f|
  score.to_xml(f)
end

# Or get as string:
xml_string = score.to_xml.string

Examples:

Complete score with two parts

score = ScorePartwise.new do
  work_title "Duet"
  creators composer: "J. Composer"
  encoding_date DateTime.new(2024, 1, 1)

  part :p1, name: "Flute" do
    measure do
      attributes do
        divisions 4
        key fifths: 1  # G major
        time beats: 3, beat_type: 4
        clef sign: 'G', line: 2
      end
      pitch 'G', octave: 4, duration: 4, type: 'quarter'
      pitch 'A', octave: 4, duration: 4, type: 'quarter'
      pitch 'B', octave: 4, duration: 4, type: 'quarter'
    end
  end

  part :p2, name: "Piano" do
    measure do
      attributes do
        divisions 4
        key fifths: 1
        time beats: 3, beat_type: 4
        clef sign: 'G', line: 2
      end
      pitch 'D', octave: 4, duration: 12, type: 'half', dots: 1
    end
  end
end

See Also:

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(work_number: nil, work_title: nil, movement_number: nil, movement_title: nil, encoding_date: nil, creators: nil, rights: nil) { ... } ⇒ ScorePartwise

Creates a new MusicXML score.

Examples:

With metadata in constructor

ScorePartwise.new(
  work_title: "Sonata in C",
  work_number: 1,
  movement_title: "Allegro",
  creators: { composer: "Mozart", arranger: "Smith" },
  rights: { lyrics: "Public Domain" }
)

With DSL block

ScorePartwise.new do
  work_title "Sonata in C"
  creators composer: "Mozart"
  part :p1, name: "Piano" do
    # ...
  end
end

Parameters:

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

    opus or catalog number

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

    title of the work

  • movement_number (Integer, String, nil) (defaults to: nil)

    movement number

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

    movement title

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

    encoding date (default: now)

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

    creators by type (e.g., composer: "Name")

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

    rights by type (e.g., lyrics: "Name")

Yields:

  • Optional DSL block for building score structure



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
# File 'lib/musa-dsl/musicxml/builder/score-partwise.rb', line 146

def initialize(work_number: nil, work_title: nil,
               movement_number: nil, movement_title: nil,
               encoding_date: nil,
               creators: nil,
               rights: nil,
               &block)

  @work_number = work_number
  @work_title = work_title
  @movement_number = movement_number
  @movement_title = movement_title

  @encoding_date = encoding_date || DateTime.now

  @creators = []
  @rights = []

  self.creators **creators if creators
  self.rights **rights if rights

  @groups_and_parts = []
  @parts = {}

  with &block if block_given?
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 complete MusicXML document structure.

Creates a MusicXML 3.0 Partwise document with:

  • XML declaration and DOCTYPE
  • Work and movement metadata
  • Identification section (creators, rights, encoding info)
  • Part list (part and group declarations)
  • Part content (measures with notes)

The encoding section automatically includes:

  • Encoding date (from @encoding_date)
  • Software attribution: "MusaDSL: MusicXML output formatter"

Parameters:

  • io (IO)

    output stream to write XML to

  • indent (Integer)

    current indentation level

  • tabs (String)

    precomputed tab string for current indent



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/musa-dsl/musicxml/builder/score-partwise.rb', line 342

def _to_xml(io, indent:, tabs:)
  io.puts "#{tabs}<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
  io.puts "#{tabs}<!DOCTYPE score-partwise PUBLIC \"-//Recordare//DTD MusicXML 3.0 Partwise//EN\" \"http://www.musicxml.org/dtds/partwise.dtd\">"
  io.puts "#{tabs}<score-partwise version=\"3.0\">"

  # Work section (optional)
  if @work_number || @work_title
    io.puts"#{tabs}\t<work>"
    io.puts"#{tabs}\t\t<work-number>#{@work_number.to_i}</work-number>" if @work_number
    io.puts"#{tabs}\t\t<work-title>#{@work_title}</work-title>" if @work_title
    io.puts"#{tabs}\t</work>"
  end

  # Movement metadata (optional)
  io.puts"#{tabs}\t<movement-number>#{@movement_number.to_i}</movement-number>" if @movement_number
  io.puts"#{tabs}\t<movement-title>#{@movement_title}</movement-title>" if @movement_title

  # Identification section (required)
  io.puts "#{tabs}\t<identification>"

  @creators.each do |creator|
    creator.to_xml(io, indent: indent + 2)
  end

  @rights.each do |rights|
    rights.to_xml(io, indent: indent + 2)
  end

  io.puts "#{tabs}\t\t<encoding>"
  io.puts"#{tabs}\t\t\t<encoding-date>#{@encoding_date.strftime("%Y-%m-%d")}</encoding-date>"
  io.puts"#{tabs}\t\t\t<software>MusaDSL: MusicXML output formatter</software>"
  io.puts "#{tabs}\t\t</encoding>"

  io.puts "#{tabs}\t</identification>"

  # Part list section (required)
  io.puts "#{tabs}\t<part-list>"
  @groups_and_parts.each do |group_or_part|
    group_or_part.header_to_xml(io, indent: indent + 2)
  end
  io.puts "#{tabs}\t</part-list>"

  # Parts content (measures with notes)
  @parts.each_value do |part|
    part.to_xml(io, indent: indent + 1)
  end

  io.puts "#{tabs}</score-partwise>"
end

#creators(hash) ⇒ Object #add_creator(type, name) ⇒ Object

Adds creator information (single or multiple).

Creators specify who created various aspects of the work.

Examples:

DSL style

score.creators composer: "Mozart", lyricist: "Da Ponte"

Method style

score.add_creator :composer, "Mozart"
score.add_creator :lyricist, "Da Ponte"

Overloads:

  • #creators(hash) ⇒ Object

    Adds multiple creators via hash (DSL style)

    Parameters:

    • hash (Hash{Symbol => String})

      creators by type

  • #add_creator(type, name) ⇒ Object

    Adds single creator entry (method style)

    Parameters:

    • type (Symbol, String)

      creator type (e.g., :composer, :lyricist, :arranger)

    • name (String)

      creator name



260
# File 'lib/musa-dsl/musicxml/builder/score-partwise.rb', line 260

attr_tuple_adder_to_array :creator, Internal::Creator

#encoding_date(value) ⇒ Object #encoding_date=(value) ⇒ Object

Encoding date builder/setter.

Overloads:

  • #encoding_date(value) ⇒ Object

    Sets encoding date via DSL

    Parameters:

    • value (DateTime)

      encoding date

  • #encoding_date=(value) ⇒ Object

    Sets encoding date via assignment

    Parameters:

    • value (DateTime)

      encoding date



220
# File 'lib/musa-dsl/musicxml/builder/score-partwise.rb', line 220

attr_simple_builder :encoding_date

#groupInternal::PartGroup

Adds a part group to organize parts.

Part groups bracket multiple parts together (e.g., string section, choir). Groups are defined by matching start/stop pairs with the same number.

Examples:

Bracketing string section

score.add_group 1, type: 'start', name: "Strings", symbol: 'bracket'
score.add_part :p1, name: "Violin I"
score.add_part :p2, name: "Violin II"
score.add_part :p3, name: "Viola"
score.add_group 1, type: 'stop'

Nested groups

score.add_group 1, type: 'start', name: "Orchestra"
score.add_group 2, type: 'start', name: "Woodwinds"
score.add_part :p1, name: "Flute"
score.add_part :p2, name: "Oboe"
score.add_group 2, type: 'stop'
score.add_group 1, type: 'stop'

Returns:



317
318
319
320
321
# File 'lib/musa-dsl/musicxml/builder/score-partwise.rb', line 317

attr_complex_adder_to_custom :group do |*parameters, **key_parameters|
  Internal::PartGroup.new(*parameters, **key_parameters).tap do |group|
    @groups_and_parts << group
  end
end

#movement_number(value) ⇒ Object #movement_number=(value) ⇒ Object

Movement number builder/setter.

Overloads:

  • #movement_number(value) ⇒ Object

    Sets movement number via DSL

    Parameters:

    • value (Integer, String)

      movement number

  • #movement_number=(value) ⇒ Object

    Sets movement number via assignment

    Parameters:

    • value (Integer, String)

      movement number



210
# File 'lib/musa-dsl/musicxml/builder/score-partwise.rb', line 210

attr_simple_builder :movement_number

#movement_title(value) ⇒ Object #movement_title=(value) ⇒ Object

Movement title builder/setter.

Overloads:

  • #movement_title(value) ⇒ Object

    Sets movement title via DSL

    Parameters:

    • value (String)

      movement title

  • #movement_title=(value) ⇒ Object

    Sets movement title via assignment

    Parameters:

    • value (String)

      movement title



200
# File 'lib/musa-dsl/musicxml/builder/score-partwise.rb', line 200

attr_simple_builder :movement_title

#part { ... } ⇒ Internal::Part

Adds a part to the score.

Parts represent individual instruments or voices in the score. Each part contains measures with musical content.

Examples:

DSL style

score.part :p1, name: "Violin I", abbreviation: "Vln. I" do
  measure do
    pitch 'A', octave: 4, duration: 4, type: 'quarter'
  end
end

Method style

part = score.add_part(:p1, name: "Violin I", abbreviation: "Vln. I")
measure = part.add_measure
measure.add_pitch step: 'A', octave: 4, duration: 4, type: 'quarter'

Yields:

  • Optional DSL block for defining measures

Returns:



283
284
285
286
287
288
# File 'lib/musa-dsl/musicxml/builder/score-partwise.rb', line 283

attr_complex_adder_to_custom :part, variable: :@parts do |id, name:, abbreviation: nil, &block|
  Internal::Part.new(id, name: name, abbreviation: abbreviation, &block).tap do |part|
    @parts[id] = part
    @groups_and_parts << part
  end
end

#rights(hash) ⇒ Object #add_rights(type, name) ⇒ Object

Adds rights information (single or multiple).

Rights specify copyright, licensing, or attribution information.

Examples:

DSL style

score.rights lyrics: "John Doe", music: "Jane Smith"

Method style

score.add_rights :lyrics, "John Doe"
score.add_rights :music, "Jane Smith"

Overloads:

  • #rights(hash) ⇒ Object

    Adds multiple rights via hash (DSL style)

    Parameters:

    • hash (Hash{Symbol => String})

      rights by type

  • #add_rights(type, name) ⇒ Object

    Adds single rights entry (method style)

    Parameters:

    • type (Symbol, String)

      rights type (e.g., :lyrics, :arrangement)

    • name (String)

      rights holder name



240
# File 'lib/musa-dsl/musicxml/builder/score-partwise.rb', line 240

attr_tuple_adder_to_array :rights, Internal::Rights, plural: :rights

#to_xml(io = nil, indent: nil) ⇒ IO, StringIO Originally defined in module Internal::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

File.open('output.xml', 'w') do |f|
  element.to_xml(f)
end

Getting XML as string

xml_string = element.to_xml.string

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

#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.

#work_number(value) ⇒ Object #work_number=(value) ⇒ Object

Work number builder/setter.

Overloads:

  • #work_number(value) ⇒ Object

    Sets work number via DSL

    Parameters:

    • value (Integer)

      work number

  • #work_number=(value) ⇒ Object

    Sets work number via assignment

    Parameters:

    • value (Integer)

      work number



190
# File 'lib/musa-dsl/musicxml/builder/score-partwise.rb', line 190

attr_simple_builder :work_number

#work_title(value) ⇒ Object #work_title=(value) ⇒ Object

Work title builder/setter.

Overloads:

  • #work_title(value) ⇒ Object

    Sets work title via DSL

    Parameters:

    • value (String)

      work title

  • #work_title=(value) ⇒ Object

    Sets work title via assignment

    Parameters:

    • value (String)

      work title



180
# File 'lib/musa-dsl/musicxml/builder/score-partwise.rb', line 180

attr_simple_builder :work_title