stretto 0.6.1
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.
- data/CHANGELOG.markdown +0 -0
 - data/README.markdown +67 -0
 - data/Rakefile +9 -0
 - data/lib/stretto.rb +14 -0
 - data/lib/stretto/grammar/channel_pressure_grammar.treetop +9 -0
 - data/lib/stretto/grammar/chord_grammar.treetop +60 -0
 - data/lib/stretto/grammar/controller_change_grammar.treetop +9 -0
 - data/lib/stretto/grammar/duration_grammar.treetop +60 -0
 - data/lib/stretto/grammar/grammar_helper.rb +6 -0
 - data/lib/stretto/grammar/harmonic_chord_grammar.treetop +15 -0
 - data/lib/stretto/grammar/harmony_grammar.treetop +15 -0
 - data/lib/stretto/grammar/instrument_grammar.treetop +9 -0
 - data/lib/stretto/grammar/key_signature_grammar.treetop +10 -0
 - data/lib/stretto/grammar/layer_change_grammar.treetop +9 -0
 - data/lib/stretto/grammar/measure_grammar.treetop +7 -0
 - data/lib/stretto/grammar/note_grammar.treetop +32 -0
 - data/lib/stretto/grammar/pitch_bend_grammar.treetop +7 -0
 - data/lib/stretto/grammar/polyphonic_pressure_grammar.treetop +9 -0
 - data/lib/stretto/grammar/rest_grammar.treetop +9 -0
 - data/lib/stretto/grammar/stretto_grammar.treetop +62 -0
 - data/lib/stretto/grammar/tempo_grammar.treetop +9 -0
 - data/lib/stretto/grammar/timing_grammar.treetop +9 -0
 - data/lib/stretto/grammar/tokens/attack_decay_token.rb +42 -0
 - data/lib/stretto/grammar/tokens/chord_token.rb +67 -0
 - data/lib/stretto/grammar/tokens/controller_change_token.rb +26 -0
 - data/lib/stretto/grammar/tokens/duration_token.rb +55 -0
 - data/lib/stretto/grammar/tokens/harmonic_chord_token.rb +25 -0
 - data/lib/stretto/grammar/tokens/harmony_with_melody_token.rb +66 -0
 - data/lib/stretto/grammar/tokens/hash_token.rb +15 -0
 - data/lib/stretto/grammar/tokens/key_signature_token.rb +30 -0
 - data/lib/stretto/grammar/tokens/measure_token.rb +21 -0
 - data/lib/stretto/grammar/tokens/modifier_token.rb +99 -0
 - data/lib/stretto/grammar/tokens/note_string_token.rb +106 -0
 - data/lib/stretto/grammar/tokens/note_token.rb +25 -0
 - data/lib/stretto/grammar/tokens/pattern_token.rb +26 -0
 - data/lib/stretto/grammar/tokens/polyphonic_pressure_token.rb +28 -0
 - data/lib/stretto/grammar/tokens/rest_token.rb +21 -0
 - data/lib/stretto/grammar/tokens/value_token.rb +28 -0
 - data/lib/stretto/grammar/tokens/variable_definition_token.rb +30 -0
 - data/lib/stretto/grammar/value_grammar.treetop +45 -0
 - data/lib/stretto/grammar/variable_grammar.treetop +10 -0
 - data/lib/stretto/grammar/voice_change_grammar.treetop +9 -0
 - data/lib/stretto/music_elements/channel_pressure.rb +44 -0
 - data/lib/stretto/music_elements/chord.rb +194 -0
 - data/lib/stretto/music_elements/controller_change.rb +49 -0
 - data/lib/stretto/music_elements/harmonic_chord.rb +56 -0
 - data/lib/stretto/music_elements/harmony.rb +37 -0
 - data/lib/stretto/music_elements/instrument.rb +57 -0
 - data/lib/stretto/music_elements/key_signature.rb +110 -0
 - data/lib/stretto/music_elements/layer.rb +22 -0
 - data/lib/stretto/music_elements/layer_change.rb +39 -0
 - data/lib/stretto/music_elements/measure.rb +38 -0
 - data/lib/stretto/music_elements/melody.rb +72 -0
 - data/lib/stretto/music_elements/modifiers/attack_decay.rb +55 -0
 - data/lib/stretto/music_elements/modifiers/chord_intervals.rb +44 -0
 - data/lib/stretto/music_elements/modifiers/duration.rb +180 -0
 - data/lib/stretto/music_elements/modifiers/value.rb +172 -0
 - data/lib/stretto/music_elements/modifiers/variables.rb +365 -0
 - data/lib/stretto/music_elements/music_element.rb +88 -0
 - data/lib/stretto/music_elements/note.rb +282 -0
 - data/lib/stretto/music_elements/pattern.rb +100 -0
 - data/lib/stretto/music_elements/pitch_bend.rb +46 -0
 - data/lib/stretto/music_elements/polyphonic_pressure.rb +62 -0
 - data/lib/stretto/music_elements/rest.rb +26 -0
 - data/lib/stretto/music_elements/tempo.rb +42 -0
 - data/lib/stretto/music_elements/timing.rb +33 -0
 - data/lib/stretto/music_elements/variable.rb +49 -0
 - data/lib/stretto/music_elements/voice.rb +49 -0
 - data/lib/stretto/music_elements/voice_change.rb +39 -0
 - data/lib/stretto/parsers/exceptions.rb +11 -0
 - data/lib/stretto/parsers/parser.rb +89 -0
 - data/lib/stretto/renderers/midiator/player.rb +200 -0
 - data/lib/stretto/util/jfugue_format_parser.rb +20 -0
 - data/lib/stretto/util/node.rb +5 -0
 - data/lib/stretto/util/utils.rb +41 -0
 - data/lib/stretto/version.rb +3 -0
 - metadata +203 -0
 
| 
         @@ -0,0 +1,49 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), 'music_element')
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Stretto
         
     | 
| 
      
 4 
     | 
    
         
            +
              module MusicElements
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                # Represents a MIDI controller event.
         
     | 
| 
      
 7 
     | 
    
         
            +
                #
         
     | 
| 
      
 8 
     | 
    
         
            +
                # MIDI specification defines about 100 controller events, that are used by the
         
     | 
| 
      
 9 
     | 
    
         
            +
                # instruments or synthesizer to perform a range of effects and control settings.
         
     | 
| 
      
 10 
     | 
    
         
            +
                #
         
     | 
| 
      
 11 
     | 
    
         
            +
                # JFugue defines the most common ones (See {Variables::CONTROLLER_VARIABLES}),
         
     | 
| 
      
 12 
     | 
    
         
            +
                # and provides a shortcut for mixed (coarse and fine values) controllers.
         
     | 
| 
      
 13 
     | 
    
         
            +
                #
         
     | 
| 
      
 14 
     | 
    
         
            +
                # The syntax for a controller change is X_controller_=_value_, where controller is the
         
     | 
| 
      
 15 
     | 
    
         
            +
                # number of the controller event, and value is the value that is going to be set.
         
     | 
| 
      
 16 
     | 
    
         
            +
                class ControllerChange < MusicElement
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  attr_reader :controller, :value
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def initialize(string_or_options, pattern = nil)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    token = case string_or_options
         
     | 
| 
      
 22 
     | 
    
         
            +
                      when String then Stretto::Parser.parse_controller_change!(string_or_options)
         
     | 
| 
      
 23 
     | 
    
         
            +
                      else string_or_options
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
                    super(token[:text_value], pattern)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @original_controller = token[:controller]
         
     | 
| 
      
 27 
     | 
    
         
            +
                    @original_value      = token[:value]
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  def controller
         
     | 
| 
      
 31 
     | 
    
         
            +
                    @controller || @original_controller.to_i(@pattern)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  def value
         
     | 
| 
      
 35 
     | 
    
         
            +
                    @value || @original_value.to_i(@pattern)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  private
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    # @private
         
     | 
| 
      
 41 
     | 
    
         
            +
                    def substitute_variables!
         
     | 
| 
      
 42 
     | 
    
         
            +
                      @controller = @original_controller.to_i(@pattern)
         
     | 
| 
      
 43 
     | 
    
         
            +
                      @value      = @original_value.to_i(@pattern)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
              end
         
     | 
| 
      
 49 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,56 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), 'music_element')
         
     | 
| 
      
 2 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), 'chord')
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Stretto
         
     | 
| 
      
 5 
     | 
    
         
            +
              module MusicElements
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                # Represents a non-regular chord (see {Chord})
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # A harmonic chord can be specied by joining notes, or even chords, together with
         
     | 
| 
      
 10 
     | 
    
         
            +
                # the <tt>+</tt> symbol. For example, the chord <tt>C+E+G</tt> is the same as the chord
         
     | 
| 
      
 11 
     | 
    
         
            +
                # +Cmaj+, and the chord <tt>C+D+E</tt> is an irregular chord (not included in the
         
     | 
| 
      
 12 
     | 
    
         
            +
                # standard named chords) which will have the notes +C+, +D+ and +E+. Note that as
         
     | 
| 
      
 13 
     | 
    
         
            +
                # the notes are specified by their note notation, the default octave is 5, instead of
         
     | 
| 
      
 14 
     | 
    
         
            +
                # 3 as the normal chords.
         
     | 
| 
      
 15 
     | 
    
         
            +
                class HarmonicChord < Chord
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def initialize(string_or_options, pattern = nil)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    token = case string_or_options
         
     | 
| 
      
 19 
     | 
    
         
            +
                      when String then Stretto::Parser.parse_harmonic_chord!(string_or_options)
         
     | 
| 
      
 20 
     | 
    
         
            +
                      else string_or_options
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
                    super(token, pattern)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    @notes = normalize_notes(@notes)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  # @return (see Chord#elements)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  def elements
         
     | 
| 
      
 28 
     | 
    
         
            +
                    notes
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  # @return (see Chord#elements)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  def duration
         
     | 
| 
      
 33 
     | 
    
         
            +
                    @notes.map(&:duration).max
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  # @private
         
     | 
| 
      
 37 
     | 
    
         
            +
                  def substitute_variables!
         
     | 
| 
      
 38 
     | 
    
         
            +
                    @duration        = @notes.map(&:duration).max
         
     | 
| 
      
 39 
     | 
    
         
            +
                    @notes.each{ |note| note.pattern = @pattern }
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  # Builds the +@notes+ instance variable, flattening the notes
         
     | 
| 
      
 43 
     | 
    
         
            +
                  # and the notes in a chord into a single array of elements
         
     | 
| 
      
 44 
     | 
    
         
            +
                  def normalize_notes(base_notes)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    base_notes.inject([]) do |notes, element|
         
     | 
| 
      
 46 
     | 
    
         
            +
                      element.pattern = @pattern
         
     | 
| 
      
 47 
     | 
    
         
            +
                      case element
         
     | 
| 
      
 48 
     | 
    
         
            +
                        when Note   then notes << element
         
     | 
| 
      
 49 
     | 
    
         
            +
                        when Chord  then notes + element.notes
         
     | 
| 
      
 50 
     | 
    
         
            +
                      end
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end.uniq
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), 'music_element')
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Stretto
         
     | 
| 
      
 4 
     | 
    
         
            +
              module MusicElements
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                # Represents a group of notes, chords and rests of different lengths
         
     | 
| 
      
 7 
     | 
    
         
            +
                # that should play together.
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # It is similar to a HarmonicChord (see {HarmonicChord} with the difference
         
     | 
| 
      
 10 
     | 
    
         
            +
                # that it can also include rests and melodies (see {Melody})
         
     | 
| 
      
 11 
     | 
    
         
            +
                class Harmony < MusicElement
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  attr_accessor :elements
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  def initialize(string_or_options, pattern = nil)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    token = case string_or_options
         
     | 
| 
      
 17 
     | 
    
         
            +
                      when String then Stretto::Parser.parse_harmony!(string_or_options)
         
     | 
| 
      
 18 
     | 
    
         
            +
                      else string_or_options
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
                    super(token[:text_value], pattern)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    @elements = token[:music_elements]
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  # @return (see HarmonicChord#duration)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  def duration
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @elements.map(&:duration).max
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  # @return (see HarmonicChord#pattern=)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  def pattern=(pattern)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    @elements.each { |element| element.pattern = pattern }
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,57 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), 'music_element')
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Stretto
         
     | 
| 
      
 4 
     | 
    
         
            +
              module MusicElements
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                # Represent an instrument change by the MIDI specification.
         
     | 
| 
      
 7 
     | 
    
         
            +
                #
         
     | 
| 
      
 8 
     | 
    
         
            +
                # The sound played depends on the soundbank installed by
         
     | 
| 
      
 9 
     | 
    
         
            +
                # the synthesizer, the MIDI standard defines 128 standard instruments
         
     | 
| 
      
 10 
     | 
    
         
            +
                # (see {Variables::INSTRUMENT_VARIABLES}) that are reflected by
         
     | 
| 
      
 11 
     | 
    
         
            +
                # JFugue with predefined variables.
         
     | 
| 
      
 12 
     | 
    
         
            +
                class Instrument < MusicElement
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  MAX_INSTRUMENT_VALUE = 127
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  # Returns an instrument with value 0 (piano)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  def self.default_instrument(pattern = nil)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    params = {
         
     | 
| 
      
 19 
     | 
    
         
            +
                      :text_value => '',
         
     | 
| 
      
 20 
     | 
    
         
            +
                      :value => Value.new(Value::NumericValue.new(0))
         
     | 
| 
      
 21 
     | 
    
         
            +
                    }
         
     | 
| 
      
 22 
     | 
    
         
            +
                    new(params, pattern)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  def initialize(string_or_options, pattern = nil)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    token = case string_or_options
         
     | 
| 
      
 27 
     | 
    
         
            +
                      when String then Stretto::Parser.parse_instrument!(string_or_options)
         
     | 
| 
      
 28 
     | 
    
         
            +
                      else string_or_options
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
      
 30 
     | 
    
         
            +
                    super(token[:text_value], pattern )
         
     | 
| 
      
 31 
     | 
    
         
            +
                    @original_value = token[:value]
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  # Sets and validates value (0...127)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  def value=(value)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    if value < 0 or value > MAX_INSTRUMENT_VALUE
         
     | 
| 
      
 37 
     | 
    
         
            +
                      raise Exceptions::ValueOutOfBoundsException.new("Instrument value should be in range 0..#{MAX_INSTRUMENT_VALUE}")
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
                    @value = value
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  # @return [Number] Returns or calculates the value for the
         
     | 
| 
      
 43 
     | 
    
         
            +
                  #   instrument
         
     | 
| 
      
 44 
     | 
    
         
            +
                  def value
         
     | 
| 
      
 45 
     | 
    
         
            +
                    @value || @original_value.to_i(@pattern)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  private
         
     | 
| 
      
 49 
     | 
    
         
            +
                  
         
     | 
| 
      
 50 
     | 
    
         
            +
                    def substitute_variables!
         
     | 
| 
      
 51 
     | 
    
         
            +
                      self.value = value
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,110 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), 'music_element')
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Stretto
         
     | 
| 
      
 4 
     | 
    
         
            +
              module MusicElements
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                # A key signature indicates the channel to play in the indicated key or scale.
         
     | 
| 
      
 7 
     | 
    
         
            +
                #
         
     | 
| 
      
 8 
     | 
    
         
            +
                # This increases or decreases note values for certain notes, according to music theory
         
     | 
| 
      
 9 
     | 
    
         
            +
                # (see http://en.wikipedia.org/wiki/Key_signature). The key signature is specified by the
         
     | 
| 
      
 10 
     | 
    
         
            +
                # letter +K+, followed by a key (for example +C+, +D#+ or +Eb+) and the scale (+maj+ or
         
     | 
| 
      
 11 
     | 
    
         
            +
                # +min+)
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # Note that notes and chords specified with a pitch value will not be affected. That is,
         
     | 
| 
      
 14 
     | 
    
         
            +
                # given the following pattern "KGmaj F [65]", the first note will raise its pitch to
         
     | 
| 
      
 15 
     | 
    
         
            +
                # 66 (due to the sharp accidental in the F notes), while the second note will keep its
         
     | 
| 
      
 16 
     | 
    
         
            +
                # given value of 65
         
     | 
| 
      
 17 
     | 
    
         
            +
                class KeySignature < MusicElement
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  attr_reader :key, :scale
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def initialize(string_or_options, pattern = nil)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    token = case string_or_options
         
     | 
| 
      
 23 
     | 
    
         
            +
                      when String then Stretto::Parser.parse_key_signature!(string_or_options)
         
     | 
| 
      
 24 
     | 
    
         
            +
                      else string_or_options
         
     | 
| 
      
 25 
     | 
    
         
            +
                    end
         
     | 
| 
      
 26 
     | 
    
         
            +
                    super(token[:text_value], pattern)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    @key = normalize_keysig(token[:key])
         
     | 
| 
      
 28 
     | 
    
         
            +
                    @scale = SCALES[token[:scale].downcase]
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  # @return [Number] +1, 0 or -1, if the note key has a flat, none or sharp accidental
         
     | 
| 
      
 32 
     | 
    
         
            +
                  #   respectively for the given +note_key+
         
     | 
| 
      
 33 
     | 
    
         
            +
                  def modifier_for(note_key)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    MODIFIERS[@scale][@key][note_key]
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  private
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    MODIFIERS = {
         
     | 
| 
      
 40 
     | 
    
         
            +
                      :major => {
         
     | 
| 
      
 41 
     | 
    
         
            +
                        'C'   => {},
         
     | 
| 
      
 42 
     | 
    
         
            +
                        'G'   => { 'F' => +1 },
         
     | 
| 
      
 43 
     | 
    
         
            +
                        'D'   => { 'F' => +1, 'C' => +1 },
         
     | 
| 
      
 44 
     | 
    
         
            +
                        'A'   => { 'F' => +1, 'C' => +1, 'G' => +1 },
         
     | 
| 
      
 45 
     | 
    
         
            +
                        'E'   => { 'F' => +1, 'C' => +1, 'G' => +1, 'D' => +1 },
         
     | 
| 
      
 46 
     | 
    
         
            +
                        'B'   => { 'F' => +1, 'C' => +1, 'G' => +1, 'D' => +1, 'A' => +1 },
         
     | 
| 
      
 47 
     | 
    
         
            +
                        'F#'  => { 'F' => +1, 'C' => +1, 'G' => +1, 'D' => +1, 'A' => +1, 'E' => +1},
         
     | 
| 
      
 48 
     | 
    
         
            +
                        'C#'  => { 'F' => +1, 'C' => +1, 'G' => +1, 'D' => +1, 'A' => +1, 'E' => +1, 'B' => +1 },
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                        'F'   => { 'B' => -1 },
         
     | 
| 
      
 51 
     | 
    
         
            +
                        'Bb'  => { 'B' => -1, 'E' => -1 },
         
     | 
| 
      
 52 
     | 
    
         
            +
                        'Eb'  => { 'B' => -1, 'E' => -1, 'A' => -1 },
         
     | 
| 
      
 53 
     | 
    
         
            +
                        'Ab'  => { 'B' => -1, 'E' => -1, 'A' => -1, 'D' => -1 },
         
     | 
| 
      
 54 
     | 
    
         
            +
                        'Db'  => { 'B' => -1, 'E' => -1, 'A' => -1, 'D' => -1, 'G' => -1 },
         
     | 
| 
      
 55 
     | 
    
         
            +
                        'Gb'  => { 'B' => -1, 'E' => -1, 'A' => -1, 'D' => -1, 'G' => -1, 'C' => -1 },
         
     | 
| 
      
 56 
     | 
    
         
            +
                        'Cb'  => { 'B' => -1, 'E' => -1, 'A' => -1, 'D' => -1, 'G' => -1, 'C' => -1, 'F' => -1},
         
     | 
| 
      
 57 
     | 
    
         
            +
                      },
         
     | 
| 
      
 58 
     | 
    
         
            +
                      :minor => {
         
     | 
| 
      
 59 
     | 
    
         
            +
                        'A'   => {},
         
     | 
| 
      
 60 
     | 
    
         
            +
                        'E'   => { 'F' => +1 },
         
     | 
| 
      
 61 
     | 
    
         
            +
                        'B'   => { 'F' => +1, 'C' => +1 },
         
     | 
| 
      
 62 
     | 
    
         
            +
                        'F#'  => { 'F' => +1, 'C' => +1, 'G' => +1 },
         
     | 
| 
      
 63 
     | 
    
         
            +
                        'C#'  => { 'F' => +1, 'C' => +1, 'G' => +1, 'D' => +1 },
         
     | 
| 
      
 64 
     | 
    
         
            +
                        'G#'  => { 'F' => +1, 'C' => +1, 'G' => +1, 'D' => +1, 'A' => +1 },
         
     | 
| 
      
 65 
     | 
    
         
            +
                        'D#'  => { 'F' => +1, 'C' => +1, 'G' => +1, 'D' => +1, 'A' => +1, 'E' => +1},
         
     | 
| 
      
 66 
     | 
    
         
            +
                        'A#'  => { 'F' => +1, 'C' => +1, 'G' => +1, 'D' => +1, 'A' => +1, 'E' => +1, 'B' => +1 },
         
     | 
| 
      
 67 
     | 
    
         
            +
                        'D'   => { 'B' => -1 },
         
     | 
| 
      
 68 
     | 
    
         
            +
                        'G'   => { 'B' => -1, 'E' => -1 },
         
     | 
| 
      
 69 
     | 
    
         
            +
                        'C'   => { 'B' => -1, 'E' => -1, 'A' => -1 },
         
     | 
| 
      
 70 
     | 
    
         
            +
                        'F'   => { 'B' => -1, 'E' => -1, 'A' => -1, 'D' => -1 },
         
     | 
| 
      
 71 
     | 
    
         
            +
                        'Bb'  => { 'B' => -1, 'E' => -1, 'A' => -1, 'D' => -1, 'G' => -1 },
         
     | 
| 
      
 72 
     | 
    
         
            +
                        'Eb'  => { 'B' => -1, 'E' => -1, 'A' => -1, 'D' => -1, 'G' => -1, 'C' => -1 },
         
     | 
| 
      
 73 
     | 
    
         
            +
                        'Ab'  => { 'B' => -1, 'E' => -1, 'A' => -1, 'D' => -1, 'G' => -1, 'C' => -1, 'F' => -1},
         
     | 
| 
      
 74 
     | 
    
         
            +
                      }
         
     | 
| 
      
 75 
     | 
    
         
            +
                    }
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    # ALIASES IN MAJOR SCALE
         
     | 
| 
      
 78 
     | 
    
         
            +
                    MODIFIERS[:major]['Fb'] = MODIFIERS[:major]['E' ]
         
     | 
| 
      
 79 
     | 
    
         
            +
                    MODIFIERS[:major]['Cb'] = MODIFIERS[:major]['B' ]
         
     | 
| 
      
 80 
     | 
    
         
            +
                    MODIFIERS[:major]['Gb'] = MODIFIERS[:major]['F#']
         
     | 
| 
      
 81 
     | 
    
         
            +
                    MODIFIERS[:major]['Db'] = MODIFIERS[:major]['C#']
         
     | 
| 
      
 82 
     | 
    
         
            +
                    MODIFIERS[:major]['B#'] = MODIFIERS[:major]['C' ]
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                    # EQUIVALENCES FROM MAJOR SCALE TO MINOR SCALE
         
     | 
| 
      
 85 
     | 
    
         
            +
                    MODIFIERS[:major]['G#'] = MODIFIERS[:major]['Ab'] = MODIFIERS[:minor]['F' ]
         
     | 
| 
      
 86 
     | 
    
         
            +
                    MODIFIERS[:major]['D#'] = MODIFIERS[:major]['Eb'] = MODIFIERS[:minor]['C' ]
         
     | 
| 
      
 87 
     | 
    
         
            +
                    MODIFIERS[:major]['A#'] = MODIFIERS[:major]['Bb'] = MODIFIERS[:minor]['G' ]
         
     | 
| 
      
 88 
     | 
    
         
            +
                    MODIFIERS[:major]['E#'] = MODIFIERS[:major]['F' ] = MODIFIERS[:minor]['D' ]
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                    # ALIASES IN MINOR SCALE
         
     | 
| 
      
 91 
     | 
    
         
            +
                    MODIFIERS[:minor]['B#'] = MODIFIERS[:minor]['C' ]
         
     | 
| 
      
 92 
     | 
    
         
            +
                    MODIFIERS[:minor]['E#'] = MODIFIERS[:minor]['F' ]
         
     | 
| 
      
 93 
     | 
    
         
            +
                    MODIFIERS[:minor]['A#'] = MODIFIERS[:minor]['Bb']
         
     | 
| 
      
 94 
     | 
    
         
            +
                    MODIFIERS[:minor]['D#'] = MODIFIERS[:minor]['Eb']
         
     | 
| 
      
 95 
     | 
    
         
            +
                    MODIFIERS[:minor]['G#'] = MODIFIERS[:minor]['Ab']
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                    # EQUIVALENCES FROM MINOR SCALE TO MAJOR SCALE
         
     | 
| 
      
 98 
     | 
    
         
            +
                    MODIFIERS[:minor]['Db'] = MODIFIERS[:minor]['C#'] = MODIFIERS[:major]['E' ]
         
     | 
| 
      
 99 
     | 
    
         
            +
                    MODIFIERS[:minor]['Gb'] = MODIFIERS[:minor]['F#'] = MODIFIERS[:major]['A' ]
         
     | 
| 
      
 100 
     | 
    
         
            +
                    MODIFIERS[:minor]['Cb'] = MODIFIERS[:minor]['B' ] = MODIFIERS[:major]['D' ]
         
     | 
| 
      
 101 
     | 
    
         
            +
                    MODIFIERS[:minor]['Fb'] = MODIFIERS[:minor]['E' ] = MODIFIERS[:major]['G' ] 
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                    SCALES = { 'maj' => :major, 'min' => :minor }
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                    def normalize_keysig(string)
         
     | 
| 
      
 106 
     | 
    
         
            +
                      string.downcase.sub(/\w/){ |initial| initial.upcase }
         
     | 
| 
      
 107 
     | 
    
         
            +
                    end
         
     | 
| 
      
 108 
     | 
    
         
            +
                end
         
     | 
| 
      
 109 
     | 
    
         
            +
              end
         
     | 
| 
      
 110 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Stretto
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              # Wrapper for a layer: set of elements inside a voice
         
     | 
| 
      
 4 
     | 
    
         
            +
              #
         
     | 
| 
      
 5 
     | 
    
         
            +
              # Layers are not part of the MIDI specification, but are
         
     | 
| 
      
 6 
     | 
    
         
            +
              # a resource to separate multiple notes that should be played
         
     | 
| 
      
 7 
     | 
    
         
            +
              # together in a voice.
         
     | 
| 
      
 8 
     | 
    
         
            +
              #
         
     | 
| 
      
 9 
     | 
    
         
            +
              # This is most useful in the voice 9 (see {Voice}), which is
         
     | 
| 
      
 10 
     | 
    
         
            +
              # the percussion layer. Different parts of the percussion track
         
     | 
| 
      
 11 
     | 
    
         
            +
              # can be separated in different layers, and they all play together
         
     | 
| 
      
 12 
     | 
    
         
            +
              # in the same track.
         
     | 
| 
      
 13 
     | 
    
         
            +
              class Layer < Array
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                # @return [Array(MusicElement)] Its elements as an array
         
     | 
| 
      
 16 
     | 
    
         
            +
                def elements
         
     | 
| 
      
 17 
     | 
    
         
            +
                  to_a
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
              
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), 'music_element')
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Stretto
         
     | 
| 
      
 4 
     | 
    
         
            +
              module MusicElements
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                class LayerChange < MusicElement
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  MAX_LAYERS = 15
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  attr_reader :index
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def initialize(string_or_options, pattern = nil)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    token = case string_or_options
         
     | 
| 
      
 14 
     | 
    
         
            +
                      when String then Stretto::Parser.parse_layer_change!(string_or_options)
         
     | 
| 
      
 15 
     | 
    
         
            +
                      else string_or_options
         
     | 
| 
      
 16 
     | 
    
         
            +
                    end
         
     | 
| 
      
 17 
     | 
    
         
            +
                    super(token[:text_value], pattern)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    @original_value = token[:value]
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def index
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @index || @original_value.to_i(@pattern)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  def index=(index)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    if index < 0 or index > MAX_LAYERS
         
     | 
| 
      
 27 
     | 
    
         
            +
                      raise Exceptions::ValueOutOfBoundsException.new("Layer value should be in range 0..#{MAX_LAYERS}")
         
     | 
| 
      
 28 
     | 
    
         
            +
                    end
         
     | 
| 
      
 29 
     | 
    
         
            +
                    @index = index
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def substitute_variables!
         
     | 
| 
      
 33 
     | 
    
         
            +
                    self.index = index
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), 'music_element')
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Stretto
         
     | 
| 
      
 4 
     | 
    
         
            +
              module MusicElements
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                # Does not have effect in the playback, it represents a measure
         
     | 
| 
      
 7 
     | 
    
         
            +
                # for readability of the music texts
         
     | 
| 
      
 8 
     | 
    
         
            +
                class Measure < MusicElement
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def initialize(string_or_options, pattern = nil)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    token = case string_or_options
         
     | 
| 
      
 12 
     | 
    
         
            +
                      when String then Stretto::Parser.parse_measure!(string_or_options)
         
     | 
| 
      
 13 
     | 
    
         
            +
                      else string_or_options
         
     | 
| 
      
 14 
     | 
    
         
            +
                    end
         
     | 
| 
      
 15 
     | 
    
         
            +
                    super(token[:text_value], pattern)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                  
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # @private
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # TODO: Make tests for other elements inside ties, move this to MusicElement
         
     | 
| 
      
 20 
     | 
    
         
            +
                  def tied_elements
         
     | 
| 
      
 21 
     | 
    
         
            +
                    next_element = self.next
         
     | 
| 
      
 22 
     | 
    
         
            +
                    if next_element
         
     | 
| 
      
 23 
     | 
    
         
            +
                      next_element.tied_elements
         
     | 
| 
      
 24 
     | 
    
         
            +
                    else
         
     | 
| 
      
 25 
     | 
    
         
            +
                      []
         
     | 
| 
      
 26 
     | 
    
         
            +
                    end
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  # @private
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # TODO: Make tests for other elements inside ties, move this to MusicElement
         
     | 
| 
      
 31 
     | 
    
         
            +
                  def tied_duration
         
     | 
| 
      
 32 
     | 
    
         
            +
                    tied_elements.inject(0){|sum, element| sum + element.duration}
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,72 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), 'music_element')
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Stretto
         
     | 
| 
      
 4 
     | 
    
         
            +
              module MusicElements
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                # A set of elements that should play sequentially. The elements are separated
         
     | 
| 
      
 7 
     | 
    
         
            +
                # by underscores, for example <tt>C_D_E</tt>
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # A melody alone would be the equivalent to play the elements of it separately,
         
     | 
| 
      
 10 
     | 
    
         
            +
                # but it is useful to indicate explicitely a melody when used in harmonies
         
     | 
| 
      
 11 
     | 
    
         
            +
                # (see {Harmony}). For example, a harmony (C_D+E_Fmaj) will play at the same time
         
     | 
| 
      
 12 
     | 
    
         
            +
                # +C+ + +E+ for one quarter of a whole note, then +D+ and +Fmaj+ will play together. 
         
     | 
| 
      
 13 
     | 
    
         
            +
                class Melody < MusicElement
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  attr_reader :elements
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def initialize(array_or_hash, pattern = nil)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    options = _handle_initial_argument(array_or_hash)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    _verify_is_pattern(pattern)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    @elements = options[:elements]
         
     | 
| 
      
 21 
     | 
    
         
            +
                    super(options[:original_string], pattern)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  # Returns the sum of the duration of its elements
         
     | 
| 
      
 25 
     | 
    
         
            +
                  def duration
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @elements.map(&:duration).sum
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  # Adds an element to the melody, additionally setting its pattern
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # (see {MusicElement::pattern=)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  def <<(element)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    @elements << element
         
     | 
| 
      
 33 
     | 
    
         
            +
                    element.pattern = @pattern if @pattern
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  private
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                    # @private
         
     | 
| 
      
 39 
     | 
    
         
            +
                    # Builds the set of elements based on what the constructor is (a token,
         
     | 
| 
      
 40 
     | 
    
         
            +
                    # an array of elements or a single MusicElement (in which case, a melody
         
     | 
| 
      
 41 
     | 
    
         
            +
                    # with just one element is created.)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    def _handle_initial_argument(array_hash_or_music_element)
         
     | 
| 
      
 43 
     | 
    
         
            +
                      arg = array_hash_or_music_element
         
     | 
| 
      
 44 
     | 
    
         
            +
                      case arg
         
     | 
| 
      
 45 
     | 
    
         
            +
                        when Array
         
     | 
| 
      
 46 
     | 
    
         
            +
                          unless arg.present? and arg.all? { |element| element.kind_of?(MusicElement) }
         
     | 
| 
      
 47 
     | 
    
         
            +
                            raise ArgumentError.new("First argument should be either a MusicElement or an array of at least one MusicElement")
         
     | 
| 
      
 48 
     | 
    
         
            +
                          end
         
     | 
| 
      
 49 
     | 
    
         
            +
                          { :elements   => arg,
         
     | 
| 
      
 50 
     | 
    
         
            +
                            :original_string => arg.map(&:original_string).join('_')
         
     | 
| 
      
 51 
     | 
    
         
            +
                          }
         
     | 
| 
      
 52 
     | 
    
         
            +
                        when Hash
         
     | 
| 
      
 53 
     | 
    
         
            +
                          arg
         
     | 
| 
      
 54 
     | 
    
         
            +
                        when MusicElement
         
     | 
| 
      
 55 
     | 
    
         
            +
                          { :elements   => [arg],
         
     | 
| 
      
 56 
     | 
    
         
            +
                            :original_string => arg.original_string
         
     | 
| 
      
 57 
     | 
    
         
            +
                          }
         
     | 
| 
      
 58 
     | 
    
         
            +
                        else raise ArgumentError.new("First argument should be either a MusicElement or an array of MusicElements")
         
     | 
| 
      
 59 
     | 
    
         
            +
                      end
         
     | 
| 
      
 60 
     | 
    
         
            +
                    end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                    # @private
         
     | 
| 
      
 63 
     | 
    
         
            +
                    # Verifies that the second parameter in initializer is a {Pattern}
         
     | 
| 
      
 64 
     | 
    
         
            +
                    def _verify_is_pattern(pattern)
         
     | 
| 
      
 65 
     | 
    
         
            +
                      if pattern and !pattern.kind_of?(Pattern)
         
     | 
| 
      
 66 
     | 
    
         
            +
                        raise ArgumentError.new("Second argument should be a Pattern object or nil")
         
     | 
| 
      
 67 
     | 
    
         
            +
                      end
         
     | 
| 
      
 68 
     | 
    
         
            +
                    end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
            end
         
     |