#! /usr/bin/env ruby # play-spkr.rb: Written by Tadayoshi Funaba 2005,2006 # $Id: play-spkr.rb,v 1.4 2006-11-10 21:57:06+09 tadf Exp $ require 'smf' require 'smf/toy/tempomap' require 'gopt' include SMF module SMF class DevSpkr < File case RUBY_PLATFORM when /freebsd/ SPKRTONE = 0x80085301 else raise 'unknown system' end def emit_tone(freq, duration) ioctl(SPKRTONE, [freq, duration].pack('i*')) end end class Sequence class PlaySpkr < XSCallback FREQ = [] (0..127).each do |n| FREQ << 440 * 2**((n-69.0)/12) end def initialize(tm) @tm = tm end def header(format, ntrks, division, tc) @sp = DevSpkr.open('/dev/speaker', 'w') end def track_start @loffset = 0 @offset = 0 @noteon = nil end def delta(delta) @offset += delta end def noteoff(ch, note, vel) return if ch == 9 # GM perc. if @noteon && @noteon[0] == note start = @noteon[1] @noteon = nil if start > @loffset s = @tm.offset2elapse(@loffset) e = @tm.offset2elapse(start) @sp.emit_tone(0, ((e - s) * 100).round) end s = @tm.offset2elapse(start) e = @tm.offset2elapse(@offset) @sp.emit_tone(FREQ[note], ((e - s) * 100).round) @loffset = @offset end end def noteon(ch, note, vel) return if ch == 9 # GM perc. @noteon ||= [note, @offset] end def result() @sp.close end end def play j = join tm = TempoMap.new(j) WS.new(j, PlaySpkr.new(tm)).read end end end def usage warn 'usage: play-spkr [input]' exit 1 end usage unless $*.size >= 0 && $*.size <= 1 file = $*.shift file = nil if file == '-' sq = unless file then Sequence.read($stdin) else Sequence.load(file) end sq.play