Class: HeadMusic::Time::MeterMap

Inherits:
Object
  • Object
show all
Includes:
EventMapSupport
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 Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

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

Returns a new instance of MeterMap.



31
32
33
34
35
# File 'lib/head_music/time/meter_map.rb', line 31

def initialize(starting_meter: nil, starting_position: nil)
  starting_meter = HeadMusic::Rudiment::Meter.get(starting_meter || "4/4")
  starting_position ||= MusicalPosition.new
  @events = [MeterEvent.new(starting_position, starting_meter)]
end

Instance Attribute Details

#eventsArray<MeterEvent> (readonly)

Returns all meter events in chronological order.

Returns:

  • all meter events in chronological order



29
30
31
# File 'lib/head_music/time/meter_map.rb', line 29

def events
  @events
end

Instance Method Details

#add_change(position, meter_or_identifier) ⇒ Object



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

def add_change(position, meter_or_identifier)
  remove_change(position)
  meter = meter_or_identifier.is_a?(HeadMusic::Rudiment::Meter) ? meter_or_identifier : HeadMusic::Rudiment::Meter.get(meter_or_identifier)
  event = MeterEvent.new(position, meter)
  @events << event
  sort_events!
  event
end

#clear_changesObject



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

def clear_changes
  @events = [@events.first]
end

#compare_positions(first_position, second_position) ⇒ Object (private) Originally defined in module EventMapSupport

#each_segment(from_position, to_position) {|current_pos, to_position, current_meter| ... } ⇒ Object

Yields:

  • (current_pos, to_position, current_meter)


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/head_music/time/meter_map.rb', line 63

def each_segment(from_position, to_position)
  relevant_events = @events.select do |event|
    compare_positions(event.position, to_position) < 0
  end

  current_pos = from_position
  current_meter = meter_at(from_position)

  relevant_events.each do |event|
    next if compare_positions(event.position, from_position) <= 0

    yield current_pos, event.position, current_meter
    current_pos = event.position
    current_meter = event.meter
  end

  yield current_pos, to_position, current_meter
end

#meter_at(position) ⇒ Object



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

def meter_at(position)
  active_event = @events.reverse.find do |event|
    compare_positions(event.position, position) <= 0
  end
  active_event&.meter || @events.first.meter
end

#position_tuple(position) ⇒ Object (private) Originally defined in module EventMapSupport

#positions_equal?(first_position, second_position) ⇒ Boolean (private) Originally defined in module EventMapSupport

Returns:

#remove_change(position) ⇒ Object



46
47
48
49
50
# File 'lib/head_music/time/meter_map.rb', line 46

def remove_change(position)
  @events.reject! do |event|
    event != @events.first && positions_equal?(event.position, position)
  end
end

#sort_events!Object (private) Originally defined in module EventMapSupport