#! /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
