class PicoTune::Phrase

Attributes

beats[R]
melodies[R]
name[R]
simultaneous_melodies[R]
subbeats[R]
tempo[R]
tune[RW]

Public Class Methods

new(name, tempo, beats, subbeats, melodies) click to toggle source
# File lib/picotune.rb, line 214
def initialize name, tempo, beats, subbeats, melodies
  @name = name
  @tempo = tempo.to_i
  @beats = beats.to_i
  @subbeats = subbeats.to_i
  @melodies = melodies
  @simultaneous_melodies = @melodies.count

  @melodies.each do |m|
    m.instrument.phrase = self
  end
end

Public Instance Methods

buffer() click to toggle source
# File lib/picotune.rb, line 239
def buffer
  @buffer ||= begin
    samples = Array.new(buffer_size) { PicoTune::Sample.new }

    @melodies.each do |melody|
      temp = Array.new(buffer_size) { PicoTune::Sample.new } if melody.instrument.reverb?
      sub_buffer_size = buffer_size / (@beats * @subbeats)
      last_step_number = -1
      carry_over = 0

      if melody.pattern.steps.count != @beats * @subbeats
        raise "Mismatch between Pattern \"#{melody.pattern.name}\", which has #{melody.pattern.steps.count} steps, and Phrase \"#{@name}\", which has #{@beats} beats and #{subbeats} subbeats (meaning any pattern it uses should have #{@beats * @subbeats} steps). Please check your pattern and phrase definitions to find the discrepancy!"
      end

      melody.pattern.steps.each_with_index do |note, step_number|
        unless note == '.'
          buffer_pointer = step_number * sub_buffer_size
          local_index = 0
          wave_index = 0
          length_offset = (1 - melody.instrument.length_value) * sub_buffer_size

          if step_number == last_step_number + 1
            local_index = carry_over
          end

          carry_over = 0

          while local_index + length_offset < sub_buffer_size || !wave_index.zero?
            current_sample = (temp ? temp : samples)[buffer_pointer + local_index] || PicoTune::Sample.new

            new_sample = melody.instrument.wave wave_index, note

            current_sample.add new_sample

            (temp ? temp : samples)[buffer_pointer + local_index] = current_sample

            wave_index += 1
            local_index += 1
            last_step_number = step_number
            carry_over += 1 if local_index + length_offset >= sub_buffer_size
            wave_index = 0 if wave_index >= melody.instrument.samples_per_wave(note)
          end

          if melody.instrument.reverb?
            i = 0
            while i < temp.size
              if i + melody.instrument.reverb_offset < temp.size
                verb_sample = temp[i + melody.instrument.reverb_offset]
                verb_sample.modify_left :+, temp[i].left * melody.instrument.decay
                verb_sample.modify_right :+, temp[i].right * melody.instrument.decay

                temp[i + melody.instrument.reverb_offset] = verb_sample
              end

              samples[i] = (samples[i] || PicoTune::Sample.new).add temp[i]
              
              i += 1
            end
          end
        end
      end
    end

    samples
  end
end
buffer_size() click to toggle source
# File lib/picotune.rb, line 235
def buffer_size
  (seconds_per_measure * PicoTune::SAMPLE_RATE).to_i
end
seconds_per_beat() click to toggle source
# File lib/picotune.rb, line 227
def seconds_per_beat
  60.0 / @tempo
end
seconds_per_measure() click to toggle source
# File lib/picotune.rb, line 231
def seconds_per_measure
  seconds_per_beat * @beats
end