class Musicality::NoteFIFO

Attributes

duration[R]
notes[R]

Public Class Methods

new(initial_notes = []) click to toggle source
# File lib/musicality/composition/sequencing/note_fifo.rb, line 5
def initialize initial_notes = []
  @notes = []
  @duration = 0
  add_notes(initial_notes) if initial_notes.any?
end

Public Instance Methods

add_note(note) click to toggle source
# File lib/musicality/composition/sequencing/note_fifo.rb, line 15
def add_note note
  if note.duration <= 0
    raise ArgumentError, "note have non-positive duration: #{note}"
  end
  @notes.push note
  @duration += note.duration
end
add_notes(notes) click to toggle source
# File lib/musicality/composition/sequencing/note_fifo.rb, line 23
def add_notes notes
  nonpositive = notes.select {|x| x.duration <= 0}
  if nonpositive.any?
    raise ArgumentError, "one or more notes have non-positive duration: #{notes}"
  end
  @notes += notes
  @duration += notes.inject(0) {|sum, note| sum + note.duration }
end
empty?() click to toggle source
# File lib/musicality/composition/sequencing/note_fifo.rb, line 11
def empty?
  @notes.empty?
end
remove_notes(target_duration) click to toggle source

Return a sequence of notes with total duration equal to the given target duration, and remove the same notes from the accumulator. Any notes beyond the given target duration are left in the accumulator. Split a note into two tied notes if needed.

# File lib/musicality/composition/sequencing/note_fifo.rb, line 35
def remove_notes target_duration
  raise ArgumentError, "negative target duration #{target_duration}" if target_duration < 0

  if target_duration > duration
    raise ArgumentError, "target duration #{target_duration} is greater than duration of accumulated notes #{duration}"
  end

  removed_notes = if target_duration == 0
    []
  elsif target_duration == duration
    notes.shift(notes.size)
  else
    dur_so_far = 0.to_r
    num_notes_taking = 0
    @notes.each_with_index do |note, idx|
      dur_so_far += note.duration
      num_notes_taking += 1
      break if dur_so_far >= target_duration
    end

    notes_taking = notes.shift(num_notes_taking)
    excess_dur = dur_so_far - target_duration

    if excess_dur > 0
      @notes.unshift(notes_taking[-1].resize(excess_dur))
      notes_taking[-1] = notes_taking[-1].resize(notes_taking[-1].duration - excess_dur)
      notes_taking[-1].pitches.each do |pitch|
        notes_taking[-1].links[pitch] = Link::Tie.new
      end
    end
    notes_taking
  end

  @duration = @duration - target_duration
  return removed_notes
end