class EasyAudioSequencer::Sequencer

Public Class Methods

new(stream: EasyAudio::EasyStream.new(amp: 0.8, frame_size: 4096, latency: 12.0), bpm: 120) click to toggle source
# File lib/easy_audio_sequencer/sequencer.rb, line 6
def initialize(stream: EasyAudio::EasyStream.new(amp: 0.8, frame_size: 4096, latency: 12.0), bpm: 120)
  srand
  @stream = stream
  @stream.fn = method(:next_frame)
  @scene = nil
  @scenes = {}
  @rendered_scenes = {}
  @keyframes = {}
  @tracks = []
  @bpm = bpm.to_f
  @sample = 0
  @kf = 0
  @samples_per_bar = (@stream.sample_rate * 60) / @bpm
end

Public Instance Methods

add_scene(name = nil, tracks) click to toggle source
# File lib/easy_audio_sequencer/sequencer.rb, line 37
def add_scene(name = nil, tracks)
  @scenes[name.to_s || 'default'] = tracks.map do |track|
    track.map {|t| Sound === t ? t : t ? Sound.new(&t) : nil }
  end
end
calculate_frame(name, i) click to toggle source
# File lib/easy_audio_sequencer/sequencer.rb, line 43
def calculate_frame(name, i)
  @rendered_scenes[name] ||= {}
  total = 0.0
  @scenes[name].each do |track|
    q = @samples_per_bar.to_i / track.length
    n = (i / q).to_i
    step = (i.to_f / @stream.sample_rate) % 1.0
    total += track[n] ? track[n].next_frame(i % q, step) : 0.0
  end
  @rendered_scenes[name][i] = total
end
next_frame() click to toggle source
# File lib/easy_audio_sequencer/sequencer.rb, line 21
def next_frame
  if @keyframes[@kf.to_i]
    @scene = @keyframes[@kf.to_i]
  end
  @kf += 1

  if @rendered_scenes[@scene] && @rendered_scenes[@scene][@sample.to_i]
    result = @rendered_scenes[@scene][@sample.to_i]
  else
    result = calculate_frame(@scene, @sample.to_i)
  end

  @sample = (@sample + 1) % @samples_per_bar.to_i
  result
end
play(scenes: ['16:default']) click to toggle source
# File lib/easy_audio_sequencer/sequencer.rb, line 73
def play(scenes: ['16:default'])
  @sample = 0
  @kf = 0
  @keyframes = {}

  last_frame = 0
  scenes.each do |scene|
    bars, name = *scene.split(':')
    @keyframes[last_frame] = name.to_s
    last_frame += (bars.to_i * @samples_per_bar).to_i
  end

  puts "Rendering scenes in background..."
  Thread.new { render_scenes }

  puts "Starting audio..."
  @stream.start
  sleep(last_frame.to_f / @stream.sample_rate)
  sleep 0.1 while @kf < last_frame
end
render_scenes() click to toggle source
# File lib/easy_audio_sequencer/sequencer.rb, line 55
def render_scenes
  @rendered_scenes = {}
  @keyframes.values.uniq.each do |name|
    reader, writer = IO.pipe
    fork do
      reader.close
      data = @samples_per_bar.to_i.times.map do |i|
        calculate_frame(name, i)
      end
      writer.puts(Marshal.dump(data))
    end

    writer.close
    data = Marshal.load(reader.read)
    @rendered_scenes[name] = data
  end
end