Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
abstracted some stuff for dealing with the midi interface and generat…
Browse files Browse the repository at this point in the history
…ing notes
  • Loading branch information
Matt Lyon committed Oct 31, 2008
1 parent 634cabf commit 2c3e93d
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 20 deletions.
17 changes: 12 additions & 5 deletions examples/metronome.rb
Expand Up @@ -2,6 +2,7 @@

tempo = 96
# comment this out and choose another one below if you want
# a second metronome is set to run an octave and a half below at 8/8
signature = [4,4]
# signature = [3,4]
# signature = [5,4]
Expand All @@ -12,18 +13,24 @@
require 'rubygems'
require 'midiator'
require 'lib/ouroubourus'
require 'lib/interfaces/midiator'
require 'lib/sequencers/metronome'

@s = Ouroubourus::Scheduler.new :timekeeper => Ouroubourus::LocalTimer.new(tempo, :resolution => 80)
@midi = MIDIator::Interface.new
@midi.autodetect_driver
@midi.program_change 0, 115 # wood block
require 'ruby-debug'
Debugger.start

@s = Ouroubourus::Scheduler.new :timekeeper => Ouroubourus::LocalTimer.new(tempo)
@midi = Midiator.new
@midi.interface.autodetect_driver
@midi.interface.program_change 0, 115 # wood block
@metronome1 = Metronome.new :interface => @midi, :signature => signature
@metronome2 = Metronome.new :interface => @midi, :signature => [6,4], :basenote => 67
@metronome2 = Metronome.new :interface => @midi, :signature => [8,8], :basenote => 53
@s.subscribers << @metronome1
@s.subscribers << @metronome2

Signal.trap("INT") { @s.timekeeper.thread.exit!; "Interrupt caught, cancelling..." }

# debugger

@s.start
@s.timekeeper.thread.join
15 changes: 15 additions & 0 deletions lib/interfaces/midiator.rb
@@ -0,0 +1,15 @@
require 'midiator'

class Midiator

attr_accessor :interface

def initialize
@interface = MIDIator::Interface.new
end

def note(channel, pitch, velocity)
@interface.driver.note_on(pitch, channel, velocity)
end

end
1 change: 1 addition & 0 deletions lib/ouroubourus.rb
Expand Up @@ -6,5 +6,6 @@ module Ouroubourus

$:.unshift File.dirname(__FILE__)
%w(scheduler schedulable schedule
midi midi/note midi/generator
timekeeper/timekeeper timekeeper/local_timer
).each {|r| require "ouroubourus/#{r}.rb" }
5 changes: 5 additions & 0 deletions lib/ouroubourus/midi.rb
@@ -0,0 +1,5 @@
module Ouroubourus
module Midi

end
end
27 changes: 27 additions & 0 deletions lib/ouroubourus/midi/generator.rb
@@ -0,0 +1,27 @@
module Ouroubourus
module MIDI
module Generator

def play(notes)
[notes].flatten.each do |note|
if note.start.zero?
note_on(note.channel, note.pitch, note.velocity)
@schedule.in(note.duration, L{ note_off(note.channel, note.pitch)})
else
@schedule.in(note.start, L{ note_on(note.channel, note.pitch, note.velocity) })
@schedule.in(note.start + note.duration, L{ note_off(note.channel, note.pitch) })
end
end
end

def note_on(channel, pitch, velocity)
@interface.note(channel, pitch, velocity)
end

def note_off(channel, pitch)
note_on(channel, pitch, 0)
end

end
end
end
45 changes: 45 additions & 0 deletions lib/ouroubourus/midi/note.rb
@@ -0,0 +1,45 @@
module Ouroubourus
module MIDI
class Note

attr_reader :pitch, :velocity, :duration, :start, :channel

def initialize(p, vel, dur, chan=1, s=0)
self.pitch = p
self.velocity = vel
self.channel = chan
self.duration = dur
self.start = s
end

def pitch=(val)
@pitch = seven_bit(val)
end

def velocity=(val)
@velocity = seven_bit(val)
end

def channel=(val)
val = 1 if val < 1
val = 16 if val > 16
@channel = val
end

def duration=(val)
@duration = [1,val].max
end

def start=(val)
@start = [0,val].max
end

protected
def seven_bit(val)
val = 0 if val < 0
val = 127 if val > 127
val
end
end
end
end
2 changes: 1 addition & 1 deletion lib/ouroubourus/schedule.rb
Expand Up @@ -11,7 +11,7 @@ def initialize
def run(now)
@time = now
ready, @queue = @queue.partition {|pos, proc| pos <= now }
ready.each {|time, proc| Thread.start { proc.call(time) } }
ready.each {|time, proc| proc.call(time) }
end

def at(position, block)
Expand Down
2 changes: 1 addition & 1 deletion lib/ouroubourus/scheduler.rb
Expand Up @@ -10,7 +10,7 @@ def initialize(options={})

def start
return false unless timekeeper.kind_of?(Ouroubourus::Timekeeper)
timekeeper.start {|now| @subscribers.each{|s| Thread.start { s.run(now) } }}
timekeeper.start {|now| @subscribers.each{|s| s.run(now) }}
end

end
Expand Down
27 changes: 14 additions & 13 deletions lib/sequencers/metronome.rb
@@ -1,5 +1,6 @@
class Metronome
include Ouroubourus::Schedulable
include Ouroubourus::MIDI::Generator

attr_accessor :interface, :signature, :channel #, :schedule

Expand All @@ -9,30 +10,30 @@ def initialize(options={})
@interface = options[:interface]
@channel = options[:channel] || 1
@basenote = options[:basenote] || 72
@run = L{|now| tick(now) }
schedule.next 480, @run
end

def tick(now)
note = (now % bar_length) == 0 ? one : beat
@interface.driver.note_on(note[:pitch], @channel, note[:velocity])
@schedule.in(note[:duration], L{ @interface.driver.note_off(note[:pitch], @channel, 0) })
@schedule.next note_length, @run
@run = L do |now|
play(now % bar_length == 0 ? one : beat)
@schedule.next beat_length, @run
end
schedule.next beat_length, @run
end

def bar_length
@signature.first * note_length
@signature.first * beat_length
end

def note_length
def beat_length
4.0 / @signature.last * 480
end

def one
{:pitch => @basenote + 12, :velocity => 100, :duration => 180}
note(@basenote+12, 100, 180)
end

def beat
{:pitch => @basenote, :velocity => 60, :duration => 90}
note(@basenote, 60, 90)
end

def note(pitch, velocity, duration)
Ouroubourus::MIDI::Note.new(pitch, velocity, duration, @channel, 0)
end
end

0 comments on commit 2c3e93d

Please sign in to comment.