class Music::Transcription::MeasureScore

Attributes

meter_changes[RW]
start_meter[RW]

Public Class Methods

new(start_meter, start_tempo, meter_changes: {}) { |self| ... } click to toggle source
Calls superclass method
# File lib/music-transcription/model/measure_score.rb, line 7
def initialize start_meter, start_tempo, meter_changes: {}, tempo_changes: {}, parts: {}, program: Program.new
  @start_meter = start_meter
  @meter_changes = meter_changes
  
  super(start_tempo, tempo_changes: tempo_changes,
        program: program, parts: parts)
  yield(self) if block_given?
end
unpack(packing) click to toggle source
# File lib/music-transcription/packing/measure_score_packing.rb, line 16
def self.unpack packing
  unpacked_start_meter = Meter.parse(packing["start_meter"])
  unpacked_mcs = Hash[ packing["meter_changes"].map do |k,v|
    v = v.clone
    v[0] = Meter.parse(v[0])
    [k, Change.from_ary(v) ]
  end ]
  
  note_score = NoteScore.unpack(packing)
  
  new(unpacked_start_meter, note_score.start_tempo,
    meter_changes: unpacked_mcs, tempo_changes: note_score.tempo_changes,
    program: note_score.program, parts: note_score.parts
  )
end
valid_tempo_types() click to toggle source
# File lib/music-transcription/model/measure_score.rb, line 25
def self.valid_tempo_types
  NoteScore.valid_tempo_types + [ Tempo::BPM ]
end

Public Instance Methods

==(other) click to toggle source
Calls superclass method
# File lib/music-transcription/model/measure_score.rb, line 56
def ==(other)
  return super(other) && @start_meter == other.start_meter &&
    @meter_changes == other.meter_changes
end
beat_duration_at(moff) click to toggle source
# File lib/music-transcription/conversion/measure_score_conversion.rb, line 93
def beat_duration_at moff
  beat_durations.select {|k,v| k <= moff }.max[1]
end
beat_durations() click to toggle source
# File lib/music-transcription/conversion/measure_score_conversion.rb, line 126
def beat_durations
  bdurs = @meter_changes.map do |offset,change|
    [ offset, change.value.beat_duration ]
  end.sort
  
  if bdurs.empty? || bdurs[0][0] != 0
    bdurs.unshift([0,@start_meter.beat_duration])
  end

  return bdurs
end
check_meterchange_durs() click to toggle source
# File lib/music-transcription/model/measure_score.rb, line 49
def check_meterchange_durs
  nonzero_duration = @meter_changes.select {|k,v| !v.is_a?(Change::Immediate) }
  if nonzero_duration.any?
    raise NonZeroError, "meter changes #{nonzero_duration} are not immediate"
  end
end
check_meterchange_offsets() click to toggle source
# File lib/music-transcription/model/measure_score.rb, line 42
def check_meterchange_offsets
  badoffsets = @meter_changes.select {|k,v| k != k.to_i }
  if badoffsets.any?
    raise NonIntegerError, "meter changes #{badoffsets} have non-integer offsets"
  end
end
check_meterchange_types() click to toggle source
# File lib/music-transcription/model/measure_score.rb, line 35
def check_meterchange_types
  badtypes = @meter_changes.select {|k,v| !v.value.is_a?(Meter) }
  if badtypes.any?
    raise TypeError, "meter change values #{nonmeter_values} are not Meter objects"
  end
end
check_methods() click to toggle source
Calls superclass method
# File lib/music-transcription/model/measure_score.rb, line 16
def check_methods
  super() + [:check_startmeter_type, :check_meterchange_types,
             :check_meterchange_durs, :check_meterchange_offsets]
end
check_startmeter_type() click to toggle source
# File lib/music-transcription/model/measure_score.rb, line 29
def check_startmeter_type
  unless @start_meter.is_a? Meter
    raise TypeError, "start meter #{@start_meter} is not a Meter object"
  end
end
convert_parts(mnoff_map = self.measure_note_map) click to toggle source
# File lib/music-transcription/conversion/measure_score_conversion.rb, line 31
def convert_parts mnoff_map = self.measure_note_map
  Hash[ @parts.map do |name,part|
    new_dcs = Hash[ part.dynamic_changes.map do |moff,change|
      noff = mnoff_map[moff]
      noff2 = mnoff_map[moff + change.duration]
      [noff, change.resize(noff2-noff)]
    end ]
    new_notes = part.notes.map {|n| n.clone }
    [name, Part.new(part.start_dynamic,
      notes: new_notes, dynamic_changes: new_dcs)]
  end ]
end
convert_program(mnoff_map = self.measure_note_map) click to toggle source
# File lib/music-transcription/conversion/measure_score_conversion.rb, line 44
def convert_program mnoff_map = self.measure_note_map
  Program.new(
    @program.segments.map do |seg|
      mnoff_map[seg.first]...mnoff_map[seg.last]
    end
  )
end
convert_tempo_changes(tempo_class, mnoff_map = self.measure_note_map) click to toggle source
# File lib/music-transcription/conversion/measure_score_conversion.rb, line 52
def convert_tempo_changes tempo_class, mnoff_map = self.measure_note_map
  tcs = {}
  bdurs = beat_durations
  
  @tempo_changes.each do |moff,change|
    bdur = bdurs.select {|x,y| x <= moff}.max[1]
    tempo = change.value
    
    case change
    when Change::Immediate
      tcs[mnoff_map[moff]] = Change::Immediate.new(tempo.convert(tempo_class,bdur))
    when Change::Gradual
      start_moff, end_moff = moff, moff + change.duration
      start_noff, end_noff = mnoff_map[start_moff], mnoff_map[end_moff]
      dur = end_noff - start_noff
      cur_noff, cur_bdur = start_noff, bdur

      more_bdurs = bdurs.select {|x,y| x > moff && x < end_moff }
      if more_bdurs.any?
        more_bdurs.each do |next_moff, next_bdur|
          next_noff = mnoff_map[next_moff]
          tcs[cur_noff] = Change::Partial.new(
            tempo.convert(tempo_class, cur_bdur), dur,
            cur_noff - start_noff, next_noff - start_noff)
          cur_noff, cur_bdur = next_noff, next_bdur
        end
        tcs[cur_noff] = Change::Partial.new(
          tempo.convert(tempo_class, cur_bdur), dur,
          cur_noff - start_noff, dur)
      else
        tcs[start_noff] = Change::Gradual.new(
          tempo.convert(tempo_class, cur_bdur), end_noff - start_noff)
      end
    when Change::Partial
      raise NotImplementedError, "No support yet for converting partial tempo changes."
    end
  end
  
  return tcs
end
measure_durations() click to toggle source
# File lib/music-transcription/conversion/measure_score_conversion.rb, line 138
def measure_durations
  mdurs = @meter_changes.map do |offset,change|
    [ offset, change.value.measure_duration ]
  end.sort
  
  if mdurs.empty? || mdurs[0][0] != 0
    mdurs.unshift([0,@start_meter.measure_duration])
  end

  return Hash[ mdurs ]
end
measure_note_map() click to toggle source
# File lib/music-transcription/conversion/measure_score_conversion.rb, line 27
def measure_note_map
  Conversion::measure_note_map(measure_offsets,measure_durations)
end
measure_offsets() click to toggle source
# File lib/music-transcription/conversion/measure_score_conversion.rb, line 97
def measure_offsets
  moffs = Set.new([0.to_r])
  
  @tempo_changes.each do |moff,change|
    moffs.add(moff)
    if change.duration > 0
      moffs.add(moff + change.duration)
    end
  end
  
  @meter_changes.keys.each {|moff| moffs.add(moff) }
  
  @parts.values.each do |part|
    part.dynamic_changes.each do |moff,change|
      moffs.add(moff)
      if change.duration > 0
        moffs.add(moff + change.duration)
      end
    end
  end
  
  @program.segments.each do |seg|
    moffs.add(seg.first)
    moffs.add(seg.last)
  end
  
  return moffs.sort
end
pack() click to toggle source
Calls superclass method
# File lib/music-transcription/packing/measure_score_packing.rb, line 5
def pack
  hash = super()
  hash["start_meter"] = start_meter.to_s
  hash["meter_changes"] = Hash[ meter_changes.map do |offset,change|
    a = change.pack
    a[0] = a[0].to_s
    [offset,a]
  end ]
  return hash
end
to_note_score(tempo_class = Tempo::QNPM) click to toggle source

Convert to NoteScore object by first converting measure-based offsets to note-based offsets, and eliminating the use of meters. Also, tempo is to non-BPM tempo.

# File lib/music-transcription/conversion/measure_score_conversion.rb, line 8
def to_note_score tempo_class = Tempo::QNPM
  unless valid?
    raise NotValidError, "Current MeasureScore is invalid, so it can not be \
                          converted to a NoteScore. Validation errors: #{self.errors}"
  end
  
  unless NoteScore.valid_tempo_types.include? tempo_class
    raise TypeError, "The desired tempo class #{tempo_class} is not valid for a NoteScore."
  end
  
  mnoff_map = self.measure_note_map
  parts = convert_parts(mnoff_map)
  prog = convert_program(mnoff_map)
  tcs = convert_tempo_changes(tempo_class, mnoff_map)
  start_tempo = @start_tempo.convert(tempo_class,@start_meter.beat_duration)
  
  NoteScore.new(start_tempo, parts: parts, program: prog, tempo_changes: tcs)
end
validatables() click to toggle source
Calls superclass method
# File lib/music-transcription/model/measure_score.rb, line 21
def validatables
  super() + [ @start_meter ] + @meter_changes.values.map {|v| v.value}
end