class Musicality::Score

Constants

DEFAULT_START_DYNAMIC

Attributes

auditions[RW]
composer[W]
key_changes[RW]
parts[RW]
program[RW]
sections[RW]
start_key[RW]
title[W]

Public Class Methods

new(parts: {}) { |self| ... } click to toggle source
# File lib/musicality/notation/model/score.rb, line 32
def initialize parts: {}, program: [], title: nil, composer: nil, sections: {}, start_key: Keys::C_MAJOR, key_changes: {}, auditions: []
  @parts = parts
  @program = program
  @title = title
  @composer = composer
  @sections = sections
  @start_key = start_key
  @key_changes = key_changes
  @auditions = auditions
  yield(self) if block_given?
end

Private Class Methods

parse_numeric(str) click to toggle source
# File lib/musicality/notation/model/score.rb, line 208
def self.parse_numeric str
  if str.include? "."
    str.to_f
  elsif str.include? "/"
    str.to_r
  else
    str.to_i
  end
end
parse_numeric_range(str) click to toggle source
# File lib/musicality/notation/model/score.rb, line 218
def self.parse_numeric_range str
  result = str.match /(\d+([\.\/]\d+)?)([\.]{2,3})(\d+([\.\/]\d+)?)/
  raise ArgumentError, "string #{str} is not a numeric range" if result.nil?

  dots = result.values_at(3)
  l_num = parse_numeric(result.values_at(1)[0])
  r_num = parse_numeric(result.values_at(4)[0])
  Range.new l_num, r_num, dots.size == 3
end

Public Instance Methods

==(other) click to toggle source
# File lib/musicality/notation/model/score.rb, line 53
def ==(other)
  return self.class == other.class &&
    @parts == other.parts && @program == other.program &&
    @program == other.program && @sections == other.sections &&
    @title == other.title && @composer == other.composer &&
    @start_key == other.start_key && @key_changes == other.key_changes
end
audition(part_name, program, &block) click to toggle source
# File lib/musicality/composition/dsl/score_methods.rb, line 60
def audition part_name, program, &block
  audition = Audition.new(part_name, program)
  audition.instance_eval(&block)
  @auditions.push audition
end
check_methods() click to toggle source
# File lib/musicality/notation/model/score.rb, line 45
def check_methods
  [:check_program, :check_parts, :check_start_key, :check_key_changes ]
end
clone() click to toggle source
# File lib/musicality/notation/model/score.rb, line 49
def clone
  Marshal.load(Marshal.dump(self))
end
collated?() click to toggle source
# File lib/musicality/notation/model/score.rb, line 65
def collated?
  @program.size == 1 && @program[0].first == 0 && @program[0].last == duration
end
composer(value = nil) click to toggle source
# File lib/musicality/notation/model/score.rb, line 24
def composer value = nil
  if value.nil?
    return @composer
  else
    @composer = value
  end
end
duration() click to toggle source
# File lib/musicality/notation/model/score.rb, line 61
def duration
  @parts.map {|name,part| part.duration }.max || 0.to_r
end
dynamic_change(new_dynamic, transition_dur: 0, offset: 0) click to toggle source
# File lib/musicality/composition/dsl/score_methods.rb, line 51
def dynamic_change new_dynamic, transition_dur: 0, offset: 0
  if transition_dur == 0
    change = (transition_dur == 0) ? Change::Immediate.new(new_tempo) : Change::Gradual.linear(new_tempo, transition_dur)
    parts.values.each do |part|
      part.tempo_changes[self.duration + offset] = change
    end
  end
end
notes(part_notes) click to toggle source
# File lib/musicality/composition/dsl/score_methods.rb, line 23
def notes part_notes
  raise ArgumentError, "No part notes given" if part_notes.empty?

  durs_uniq = part_notes.values.map do |notes|
    notes.map {|n| n.duration }.inject(0,:+)
  end.uniq
  raise DurationMismatchError, "New part note durations do not all match" if durs_uniq.size > 1
  dur = durs_uniq.first

  a = starting_part_dur = self.duration
  part_notes.each do |part,notes|
    unless parts.has_key? part
      parts[part] = Part.new DEFAULT_START_DYNAMIC
      if starting_part_dur > 0
        parts[part].notes.push Note.new(starting_part_dur)
      end
    end
    parts[part].notes += notes
  end
  (parts.keys - part_notes.keys).each do |part|
    parts[part].notes.push Note.new(dur)
  end

  b = self.duration
  program.push a...b
  a...b
end
repeat(arg) click to toggle source
# File lib/musicality/composition/dsl/score_methods.rb, line 11
def repeat arg
  case arg
  when Range
    program.push arg
  when String,Symbol
    program.push @sections.fetch(arg)
  else
    raise ArgumentError, "Arg is not a Range, String, or Symbol"
  end
end
section(title, &block) click to toggle source
# File lib/musicality/composition/dsl/score_methods.rb, line 4
def section title, &block
  a = duration
  self.instance_eval(&block)
  b = duration
  @sections[title] = a...b
end
title(value = nil) click to toggle source
# File lib/musicality/notation/model/score.rb, line 16
def title value = nil
  if value.nil?
    return @title
  else
    @title = value
  end
end
validatables() click to toggle source
# File lib/musicality/notation/model/score.rb, line 44
def validatables; @parts.values; end

Private Instance Methods

check_key_changes() click to toggle source
# File lib/musicality/notation/model/score.rb, line 189
def check_key_changes
  badtypes = @key_changes.select {|k,v| !v.end_value.is_a?(Key) }
  if badtypes.any?
    raise TypeError, "Found key change values that are not Key objects: #{badtypes}"
  end

  badoffsets = @key_changes.select {|k,v| k != k.to_i }
  if badoffsets.any?
    raise NonIntegerError, "Found key changes at non-integer offsets: #{badoffsets}"
  end

  nonzero_duration = @key_changes.select {|k,v| !v.is_a?(Change::Immediate) }
  if nonzero_duration.any?
    raise NonZeroError, "Found key changes that are not immediate: #{nonzero_duration}"
  end
end
check_parts() click to toggle source
# File lib/musicality/notation/model/score.rb, line 176
def check_parts
  non_parts = @parts.values.select {|x| !x.is_a?(Part) }
  if non_parts.any?
    raise TypeError, "Non-Part part value(s) found: #{non_parts}"
  end
end
check_program() click to toggle source
# File lib/musicality/notation/model/score.rb, line 159
def check_program
  non_ranges = @program.select {|x| !x.is_a?(Range) }
  if non_ranges.any?
    raise TypeError, "Non-Range program element(s) found: #{non_ranges}"
  end

  non_increasing = @program.select {|seg| seg.first >= seg.last }
  if non_increasing.any?
    raise NonIncreasingError, "Non-increasing program range(s) found: #{non_increasing}"
  end

  negative = @program.select {|seg| seg.first < 0 || seg.last < 0 }
  if negative.any?
    raise NegativeError, "Program range(s) with negative value(s) found: #{negative}"
  end
end
check_start_key() click to toggle source
# File lib/musicality/notation/model/score.rb, line 183
def check_start_key
  unless @start_key.is_a? Key
    raise TypeError, "Start key #{@start_key} is not a Key object"
  end
end