#! /usr/bin/env ruby # play-oss3.rb: Written by Tadayoshi Funaba 1999-2006 # $Id: play-oss3.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 Sequence class Timer def initialize() @start = Time.now end def elapse() Time.now - @start end end class Play < XSCallback def initialize(tm, num) @tm, @num = tm, num end def header(format, ntrks, division, tc) @mo = open("/dev/midi%02d" % @num, 'w') end def track_start() @offset = 0 end def delta(delta) @timer ||= Timer.new if delta.nonzero? @offset += delta e = @tm.offset2elapse(@offset) - @timer.elapse if e > 0 sleep(e.to_f) end end end def noteoff(ch, note, vel) @mo.putc(ch | 0x80) @mo.putc(note) @mo.putc(vel) @mo.flush end def noteon(ch, note, vel) @mo.putc(ch | 0x90) @mo.putc(note) @mo.putc(vel) @mo.flush end def polyphonickeypressure(ch, note, val) @mo.putc(ch | 0xa0) @mo.putc(note) @mo.putc(val) @mo.flush end def controlchange(ch, num, val) @mo.putc(ch | 0xb0) @mo.putc(num) @mo.putc(val) @mo.flush end def programchange(ch, num) @mo.putc(ch | 0xc0) @mo.putc(num) @mo.flush end def channelpressure(ch, val) @mo.putc(ch | 0xd0) @mo.putc(val) @mo.flush end def pitchbendchange(ch, val) @mo.putc(ch | 0xe0) val += 0x2000 lsb = val & 0x7f msb = (val >> 7) & 0x7f @mo.putc(lsb) @mo.putc(msb) @mo.flush end def channelmodemessage(ch, num, val) controlchange(ch, num, val) end private :channelmodemessage def allsoundoff(ch) channelmodemessage(ch, 0x78, 0) end def resetallcontrollers(ch) channelmodemessage(ch, 0x79, 0) end def localcontrol(ch, val) channelmodemessage(ch, 0x7a, val) end def allnotesoff(ch) channelmodemessage(ch, 0x7b, 0) end def omnioff(ch) channelmodemessage(ch, 0x7c, 0) end def omnion(ch) channelmodemessage(ch, 0x7d, 0) end def monomode(ch, val) channelmodemessage(ch, 0x7e, val) end def polymode(ch) channelmodemessage(ch, 0x7f, 0) end def exclusivefx(data) data.each_byte do |x| @mo.putc(x) end @mo.flush end private :exclusivefx def exclusivef0(data) @mo.putc(0xf0) exclusivefx(data) @mo.flush end def exclusivef7(data) exclusivefx(data) end def result() @mo.close end end def play(num=0) j = join tm = TempoMap.new(j) WS.new(j, Play.new(tm, num)).read end end end def usage warn 'usage: play-oss3 [-d num] [input]' exit 1 end usage unless opt = Gopt.gopt('d:') usage unless $*.size >= 0 && $*.size <= 1 file = $*.shift file = nil if file == '-' num = (opt[:d] || '0').to_i sq = unless file then Sequence.read($stdin) else Sequence.load(file) end sq.play(num)