Class: HeadMusic::Time::MeterMap

Inherits:
Object
  • Object
show all
Defined in:
lib/head_music/time/meter_map.rb

Overview

Manages meter (time signature) changes along a musical timeline

A MeterMap maintains a sorted list of meter changes at specific musical positions, allowing you to determine which meter is active at any point and iterate through meter segments for musical position calculations.

Examples:

Basic usage

meter_map = HeadMusic::Time::MeterMap.new
meter_map.add_change(MusicalPosition.new(5, 1, 0, 0), "3/4")
meter_map.add_change(MusicalPosition.new(9, 1, 0, 0), "6/8")

meter = meter_map.meter_at(MusicalPosition.new(7, 1, 0, 0))
meter.to_s # => "3/4"

Iterating through segments

from = MusicalPosition.new(1, 1, 0, 0)
to = MusicalPosition.new(10, 1, 0, 0)
meter_map.each_segment(from, to) do |start_pos, end_pos, meter|
  # Process each meter segment
end

Instance Method Summary collapse

Constructor Details

#initialize(starting_meter: nil, starting_position: nil) ⇒ MeterMap

Returns a new instance of MeterMap.



26
27
28
29
30
31
32
33
# File 'lib/head_music/time/meter_map.rb', line 26

def initialize(starting_meter: nil, starting_position: nil)
  starting_meter = HeadMusic::Rudiment::Meter.get(starting_meter || "4/4")
  starting_position ||= MusicalPosition.new
  # The opening meter is not removable: a timeline with no meter at all
  # has no bars, so there is always one in force.
  @map = EventMap.new(removable_first_event: false)
  @map.add(starting_position, MeterEvent.new(starting_position, starting_meter))
end

Instance Method Details

#add_change(position, meter_or_identifier) ⇒ Object



40
41
42
43
# File 'lib/head_music/time/meter_map.rb', line 40

def add_change(position, meter_or_identifier)
  meter = meter_or_identifier.is_a?(HeadMusic::Rudiment::Meter) ? meter_or_identifier : HeadMusic::Rudiment::Meter.get(meter_or_identifier)
  MeterEvent.new(position, meter).tap { |event| @map.add(position, event) }
end

#change_at(position) ⇒ MeterEvent?

Returns the meter change starting exactly here.

Returns:

  • (MeterEvent, nil)

    the meter change starting exactly here



58
59
60
# File 'lib/head_music/time/meter_map.rb', line 58

def change_at(position)
  @map.change_at(position)&.value
end

#clear_changesObject



49
50
51
# File 'lib/head_music/time/meter_map.rb', line 49

def clear_changes
  @map.clear
end

#each_segment(from_position, to_position, &block) ⇒ Object



62
63
64
65
66
# File 'lib/head_music/time/meter_map.rb', line 62

def each_segment(from_position, to_position, &block)
  @map.each_segment(from_position, to_position) do |start_position, end_position, event|
    block.call(start_position, end_position, event&.meter || events.first.meter)
  end
end

#eventsArray<MeterEvent>

Returns all meter events in chronological order.

Returns:

  • (Array<MeterEvent>)

    all meter events in chronological order



36
37
38
# File 'lib/head_music/time/meter_map.rb', line 36

def events
  @map.values
end

#meter_at(position) ⇒ Object



53
54
55
# File 'lib/head_music/time/meter_map.rb', line 53

def meter_at(position)
  @map.at(position)&.meter || events.first.meter
end

#remove_change(position) ⇒ Object



45
46
47
# File 'lib/head_music/time/meter_map.rb', line 45

def remove_change(position)
  @map.remove(position)
end