class Musicality::Score::Tempo

Tempo-based score with meter, bar lines, and a fixed pulse (beat).

Offsets and durations are based on note duration, but note duration is determined by the tempo, which can change.

Tempo values are in beats-per-minute.

Attributes

meter_changes[RW]
start_meter[RW]
start_tempo[RW]
tempo_changes[RW]

Public Class Methods

new(start_tempo, tempo_changes: {}) click to toggle source

See Score#initialize for remaining kwargs

Calls superclass method Musicality::Score::new
# File lib/musicality/notation/model/score.rb, line 82
def initialize start_tempo, tempo_changes: {}, start_meter: Meters::FOUR_FOUR, meter_changes: {}, parts: {}, program: [], title: nil, composer: nil, sections: {}, start_key: Keys::C_MAJOR, key_changes: {}, auditions: []
  @start_tempo = start_tempo
  @tempo_changes = tempo_changes
  @start_meter = start_meter
  @meter_changes = meter_changes

  super(parts: parts, program: program, title: title, composer: composer, sections: sections, start_key: start_key, key_changes: key_changes, auditions: auditions)
end

Public Instance Methods

==(other) click to toggle source
Calls superclass method Musicality::Score#==
# File lib/musicality/notation/model/score.rb, line 99
def ==(other)
  return super(other) &&
  @start_tempo == other.start_tempo &&
  @tempo_changes == other.tempo_changes
  @start_meter == other.start_meter &&
  @meter_changes == other.meter_changes
end
beat_durations() click to toggle source
# File lib/musicality/notation/conversion/score_conversion.rb, line 11
def beat_durations
  bdurs = @meter_changes.map do |offset,change_or_meter|
    if change_or_meter.is_a? Meter
      [ offset, change_or_meter.beat_duration ]
    else
      [ offset, change_or_meter.end_value.beat_duration ]
    end
  end.sort
  
  if bdurs.empty? || bdurs[0][0] != 0
    bdurs.unshift([0.to_r,@start_meter.beat_duration])
  end

  return Hash[ bdurs ]
end
check_methods() click to toggle source
Calls superclass method Musicality::Score#check_methods
# File lib/musicality/notation/model/score.rb, line 91
def check_methods
  super() + [:check_start_tempo, :check_tempo_changes, :check_start_meter, :check_meter_changes]
end
key_change(new_key, offset: 0) click to toggle source
# File lib/musicality/composition/dsl/score_methods.rb, line 79
def key_change new_key, offset: 0
  key_changes[self.duration + offset] = Change::Immediate.new(new_key)
end
measure_duration(note_offset = self.duration) click to toggle source

Returns the measure duration of the most recent meter duration since the given note offset, or of the start meter if there are no meter changes.

# File lib/musicality/notation/model/score.rb, line 109
def measure_duration note_offset = self.duration
  if meter_changes.any?
    candidates = meter_changes.select {|noff,change| noff <= note_offset }
    candidates.max[1].end_value.measure_duration
  else
    start_meter.measure_duration
  end
end
meter_change(new_meter, offset: 0) click to toggle source
# File lib/musicality/composition/dsl/score_methods.rb, line 75
def meter_change new_meter, offset: 0
  meter_changes[self.duration + offset] = Change::Immediate.new(new_meter)
end
tempo_change(new_tempo, transition_dur: 0, offset: 0) click to toggle source
# File lib/musicality/composition/dsl/score_methods.rb, line 67
def tempo_change new_tempo, transition_dur: 0, offset: 0
  if transition_dur == 0
    tempo_changes[self.duration + offset] = Change::Immediate.new(new_tempo)
  else
    tempo_changes[self.duration + offset] = Change::Gradual.linear(new_tempo, transition_dur)
  end
end
to_lilypond(selected_parts = @parts.keys) click to toggle source

See ScoreEngraver#make_lilypond for details on supported keyword args

# File lib/musicality/printing/lilypond/score_engraving.rb, line 6
def to_lilypond selected_parts = @parts.keys
  ScoreEngraver.new(self).make_lilypond(selected_parts)
end
to_midi_seq(tempo_sample_rate, **kwargs) click to toggle source
# File lib/musicality/performance/midi/score_sequencing.rb, line 11
def to_midi_seq tempo_sample_rate, **kwargs
  to_timed(tempo_sample_rate).to_midi_seq(**kwargs)
end
to_osc(tempo_sample_rate, base_fpath, **kwargs) click to toggle source
# File lib/musicality/performance/supercollider/score_conducting.rb, line 11
def to_osc tempo_sample_rate, base_fpath, **kwargs
  to_timed(tempo_sample_rate).to_osc(base_fpath, **kwargs)
end
to_timed(tempo_sample_rate) click to toggle source

Convert to timed score by converting note-based offsets and durations to time-based. This eliminates the use of meters and tempos.

# File lib/musicality/notation/conversion/score_conversion.rb, line 7
def to_timed tempo_sample_rate
  ScoreConverter.new(self, tempo_sample_rate).convert_score
end
validatables() click to toggle source
Calls superclass method Musicality::Score#validatables
# File lib/musicality/notation/model/score.rb, line 95
def validatables
  super() + [ @start_meter ] + @meter_changes.values.map {|v| v.end_value}
end

Private Instance Methods

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

  nonzero_duration = @meter_changes.select {|k,v| !v.is_a?(Change::Immediate) }
  if nonzero_duration.any?
    raise NonZeroError, "Found meter changes that are not immediate: #{nonzero_duration}"
  end
end
check_start_meter() click to toggle source
# File lib/musicality/notation/model/score.rb, line 138
def check_start_meter
  unless @start_meter.is_a? Meter
    raise TypeError, "Start meter #{@start_meter} is not a Meter object"
  end
end
check_start_tempo() click to toggle source
# File lib/musicality/notation/model/score.rb, line 120
def check_start_tempo
  if @start_tempo <= 0
    raise NonPositiveError, "Start tempo (#{@start_tempo}) is not positive"
  end
end
check_tempo_changes() click to toggle source
# File lib/musicality/notation/model/score.rb, line 126
def check_tempo_changes
  badtypes = @tempo_changes.select {|k,v| !v.end_value.is_a?(Numeric) }
  if badtypes.any?
    raise TypeError, "Found non-numeric tempo change values: #{badtypes}"
  end

  badvalues = @tempo_changes.select {|k,v| v.end_value <= 0 }
  if badvalues.any?
    raise NonPositiveError, "Found non-positive tempo changes values: #{badvalues}"
  end
end