Class: Webmidi::SMF::TempoMap
- Inherits:
-
Object
- Object
- Webmidi::SMF::TempoMap
- Defined in:
- lib/webmidi/smf/tempo_map.rb
Constant Summary collapse
- DEFAULT_TEMPO =
500_000
Instance Attribute Summary collapse
-
#entries ⇒ Object
readonly
Returns the value of attribute entries.
-
#ppqn ⇒ Object
readonly
Returns the value of attribute ppqn.
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(entries = [], ppqn:) ⇒ TempoMap
constructor
A new instance of TempoMap.
- #seconds_to_ticks(seconds) ⇒ Object
- #tempo_at(ticks) ⇒ Object
- #ticks_to_seconds(ticks) ⇒ Object
Constructor Details
#initialize(entries = [], ppqn:) ⇒ TempoMap
Returns a new instance of TempoMap.
24 25 26 27 28 29 30 31 |
# File 'lib/webmidi/smf/tempo_map.rb', line 24 def initialize(entries = [], ppqn:) raise InvalidSMFError, "PPQN must be positive" unless ppqn.is_a?(Integer) && ppqn.positive? @ppqn = ppqn @entries = entries.map { |entry| normalize_entry(entry) }.sort_by { |entry| entry[:tick] } @entries.unshift({tick: 0, tempo: DEFAULT_TEMPO}) if @entries.empty? || @entries.first[:tick] != 0 freeze_entries! end |
Instance Attribute Details
#entries ⇒ Object (readonly)
Returns the value of attribute entries.
8 9 10 |
# File 'lib/webmidi/smf/tempo_map.rb', line 8 def entries @entries end |
#ppqn ⇒ Object (readonly)
Returns the value of attribute ppqn.
8 9 10 |
# File 'lib/webmidi/smf/tempo_map.rb', line 8 def ppqn @ppqn end |
Class Method Details
.from_sequence(sequence) ⇒ Object
10 11 12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/webmidi/smf/tempo_map.rb', line 10 def self.from_sequence(sequence) tempo_events = [] sequence.each do |track| tick = 0 track.each do |event| tick += event.delta_time if event.is_a?(MetaEvent) && event.type == MetaEvent::META_TYPES[:tempo] tempo_events << {tick: tick, tempo: event.tempo} end end end new(tempo_events, ppqn: sequence.ppqn) end |
Instance Method Details
#seconds_to_ticks(seconds) ⇒ Object
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/webmidi/smf/tempo_map.rb', line 53 def seconds_to_ticks(seconds) validate_non_negative_number!(seconds, "Seconds") remaining = seconds.to_f current_tick = 0 @entries.each_with_index do |entry, index| next_tick = (index + 1 < @entries.size) ? @entries[index + 1][:tick] : nil segment_ticks = next_tick ? next_tick - current_tick : nil seconds_per_tick = entry[:tempo] / 1_000_000.0 / @ppqn if segment_ticks.nil? return current_tick + (remaining / seconds_per_tick).round end segment_seconds = segment_ticks * seconds_per_tick return current_tick + (remaining / seconds_per_tick).round if remaining <= segment_seconds remaining -= segment_seconds current_tick = next_tick end end |
#tempo_at(ticks) ⇒ Object
75 76 77 78 |
# File 'lib/webmidi/smf/tempo_map.rb', line 75 def tempo_at(ticks) validate_non_negative_number!(ticks, "Ticks") @entries.rfind { |entry| entry[:tick] <= ticks }[:tempo] end |
#ticks_to_seconds(ticks) ⇒ Object
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/webmidi/smf/tempo_map.rb', line 33 def ticks_to_seconds(ticks) validate_non_negative_number!(ticks, "Ticks") seconds = 0.0 current_tick = 0 @entries.each_with_index do |entry, index| next_tick = if index + 1 < @entries.size [@entries[index + 1][:tick], ticks].min else ticks end break if current_tick >= ticks seconds += ticks_segment_to_seconds(next_tick - current_tick, entry[:tempo]) current_tick = next_tick end seconds end |