# smf.rb: Written by Tadayoshi Funaba 1998-2003
# $Id: smf.rb,v 1.29 2003-08-12 05:08:00+09 tadf Exp $

module SMF

  class Sequence

    class ReadError < StandardError; end

    class RS

      class PO

	def self.u2s(u, w) u -= 2**w if u > 2**(w-1)-1; u end

	def initialize(str) @str, @index = str, 0 end
	def rem() @str.length - @index end
	def eof? () rem <= 0 end
	def skip(n) @index += n end

	def getn(n)
	  raise EOFError if rem < n
	  s = @str[@index, n]
	  skip(n)
	  s
	end

	def getc
	  raise EOFError if rem < 1
	  c = @str[@index]
	  skip(1)
	  c
	end

	def getl
	  v = 0
	  begin
	    v <<= 7
	    c = getc
	    v |= c & 0x7f
	  end until (c & 0x80).zero?
	  v
	end

	def geti(n)
	  u = 0
	  n.times do
	    c = getc
	    u <<= 8
	    u += c
	  end
	  u
	end

	def geti16() geti(2) end
	def geti24() geti(3) end
	def geti32() geti(4) end

	def to_s() @str end

      end

      def initialize(s, cb) @s, @cb = s, cb end

      def read_header(s)
	rs = RS::PO.new(s)
	format = rs.geti16
	ntrks = rs.geti16
	div1 = rs.getc
	div2 = rs.getc
	if (div1 & 0x80) == 0
	  smpte = nil
	  division = div1 << 8 | div2
	else
	  smpte = 0x100 - div1
	  division = div2
	end
	@cb.header(format, ntrks, division, smpte)
      end

      def read_meta(meta_type, data)
	case meta_type
	when 0x0
	  rs = RS::PO.new(data)
	  num = rs.geti16
	  @cb.sequencenumber(num)
	when 0x1..0xf
	  case meta_type
	  when 0x1; @cb.generalpurposetext(data)
	  when 0x2; @cb.copyrightnotice(data)
	  when 0x3; @cb.trackname(data)
	  when 0x4; @cb.instrumentname(data)
	  when 0x5; @cb.lyric(data)
	  when 0x6; @cb.marker(data)
	  when 0x7; @cb.cuepoint(data)
	  when 0x8; @cb.programname(data)
	  when 0x9; @cb.devicename(data)
	  when 0xa; @cb.text0a(data)
	  when 0xb; @cb.text0b(data)
	  when 0xc; @cb.text0c(data)
	  when 0xd; @cb.text0d(data)
	  when 0xe; @cb.text0e(data)
	  when 0xf; @cb.text0f(data)
	  end
	when 0x20
	  rs = RS::PO.new(data)
	  ch = rs.getc
	  @cb.channelprefix(ch)
	when 0x21
	  rs = RS::PO.new(data)
	  num = rs.getc
	  @cb.midiport(num)
	when 0x2f
	  @cb.endoftrack
	when 0x51
	  rs = RS::PO.new(data)
	  tempo = rs.geti24
	  @cb.settempo(tempo)
	when 0x54
	  rs = RS::PO.new(data)
	  hr = rs.getc
	  mn = rs.getc
	  se = rs.getc
	  fr = rs.getc
	  ff = rs.getc
	  @cb.smpteoffset(hr, mn, se, fr, ff)
	when 0x58
	  rs = RS::PO.new(data)
	  nn = rs.getc
	  dd = rs.getc
	  cc = rs.getc
	  bb = rs.getc
	  @cb.timesignature(nn, dd, cc, bb)
	when 0x59
	  rs = RS::PO.new(data)
	  sf = rs.getc
	  mi = rs.getc
	  sf = RS::PO.u2s(sf, 8)
	  @cb.keysignature(sf, mi)
	when 0x7f
	  @cb.sequencerspecific(data)
	else
	  @cb.unknownmeta(meta_type, data)
	end
      end

      def read_track(s)
	@cb.track_start
	rs = RS::PO.new(s)
	running = 0
	until rs.eof?
	  @cb.delta(rs.getl)
	  stat = rs.getc
	  if (stat & 0x80) == 0
	    rs.skip(-1)
	    stat = running
	  else
	    case stat
	    when 0x80..0xef; running = stat
	    when 0xf0..0xf7; running = 0
	    end
	  end
	  case stat
	  when 0x80..0x8f
	    @cb.noteoff(stat & 0xf, rs.getc, rs.getc)
	  when 0x90..0x9f
	    @cb.noteon(stat & 0xf, rs.getc, rs.getc)
	  when 0xa0..0xaf
	    @cb.polyphonickeypressure(stat & 0xf, rs.getc, rs.getc)
	  when 0xb0..0xbf
	    c = rs.getc
	    if c < 0x78
	      @cb.controlchange(stat & 0xf, c, rs.getc)
	    else
	      @cb.channelmodemessage(stat & 0xf, c, rs.getc)
	    end
	  when 0xc0..0xcf
	    @cb.programchange(stat & 0xf, rs.getc)
	  when 0xd0..0xdf
	    @cb.channelpressure(stat & 0xf, rs.getc)
	  when 0xe0..0xef
	    lsb = rs.getc
	    msb = rs.getc
	    val = (lsb | msb << 7) - 0x2000
	    @cb.pitchbendchange(stat & 0xf, val)
	  when 0xf0, 0xf7
	    len = rs.getl
	    data = rs.getn(len)
	    if stat == 0xf0
	      @cb.exclusivef0(data)
	    else
	      @cb.exclusivef7(data)
	    end
	  when 0xff
	    meta_type = rs.getc
	    len = rs.getl
	    data = rs.getn(len)
	    read_meta(meta_type, data)
	  else
	    until rs.eof?
	      unless (rs.getc & 0x80) == 0
		rs.skip(-1)
		break
	      end
	    end
	  end
	end
	@cb.track_end
      end

      private :read_header, :read_meta, :read_track

      def get_from_macbin
	begin
	  if @s[0] == 0 and @s[74] == 0 and @s[82] == 0 and @s[65,4] == 'Midi'
	    @s[128,@s[83,4].unpack('N')[0]]
	  end
	rescue
	end
      end

      def get_from_rmid
	begin
	  if @s[0,4] == 'RIFF' and @s[8,4] == 'RMID'
	    @s[20,@s[16,4].unpack('V')[0]]
	  end
	rescue
	end
      end

      private :get_from_macbin, :get_from_rmid

      def read
	begin
	  rs = RS::PO.new(get_from_macbin || get_from_rmid || @s)
	  ckid = rs.getn(4)
	  unless ckid == 'MThd'
	    @cb.error('not a SMF')
	  end
	  rs.skip(-4)
	  until rs.eof?
	    ckid = rs.getn(4)
	    leng = rs.geti32
	    body = rs.getn(leng)
	    case ckid
	    when 'MThd'
	      read_header(body)
	    when 'MTrk'
	      read_track(body)
	    else
	      @cb.unknownchunk(ckid, body)
	    end
	  end
	rescue EOFError
	  @cb.error('unexpected EOF')
	end
	@cb.result
      end

    end

    class WS

      class PO

	def self.s2u(s, w) s += 2**w if s < 0; s end

#	def initialize() @str = '' end
	def initialize() @arr = [] end

#	def puts(s) @str << s end
#	def putc(c) @str << c end
	def puts(s) @arr << s     end
	def putc(c) @arr << c.chr end

	def putl(v)
	  s = ''
	  begin
	    s << (v & 0x7f | 0x80)
	    v >>= 7
	  end until v.zero?
	  s[0] &= 0x7f
	  s.reverse!
	  puts(s)
	end

	def puti(n, u)
	  n.times do |i|
	    putc((u >> (n - i - 1) * 8) & 0xff)
	  end
	end

	def puti16(u) puti(2, u) end
	def puti24(u) puti(3, u) end
	def puti32(u) puti(4, u) end

#	def to_s() @str end
	def to_s() @arr.join end

      end

      def initialize(o, cb)
	@o, @cb = o, cb
      end

      def read_meta(ev)
	case ev
	when SequenceNumber
	  @cb.sequencenumber(ev.num)
	when Text
	  case ev
	  when GeneralPurposeText; @cb.generalpurposetext(ev.text)
	  when CopyrightNotice;    @cb.copyrightnotice(ev.text)
	  when TrackName;          @cb.trackname(ev.text)
	  when InstrumentName;     @cb.instrumentname(ev.text)
	  when Lyric;              @cb.lyric(ev.text)
	  when Marker;             @cb.marker(ev.text)
	  when CuePoint;           @cb.cuepoint(ev.text)
	  when ProgramName;        @cb.programname(ev.text)
	  when DeviceName;         @cb.devicename(ev.text)
	  when Text0A;             @cb.text0a(ev.text)
	  when Text0B;             @cb.text0b(ev.text)
	  when Text0C;             @cb.text0c(ev.text)
	  when Text0D;             @cb.text0d(ev.text)
	  when Text0E;             @cb.text0e(ev.text)
	  when Text0F;             @cb.text0f(ev.text)
	  end
	when ChannelPrefix
	  @cb.channelprefix(ev.ch)
	when MIDIPort
	  @cb.midiport(ev.num)
	when EndOfTrack
	  @cb.endoftrack
	when SetTempo
	  @cb.settempo(ev.tempo)
	when SMPTEOffset
	  @cb.smpteoffset(ev.hr, ev.mn, ev.se, ev.fr, ev.ff)
	when TimeSignature
	  @cb.timesignature(ev.nn, ev.dd, ev.cc, ev.bb)
	when KeySignature
	  @cb.keysignature(ev.sf, ev.mi)
	when SequencerSpecific
	  @cb.sequencerspecific(ev.data)
	else
	  @cb.unknownmeta(ev.meta_type, ev.data)
	end
      end

      def read_track(tr)
	@cb.track_start
	offset = 0
	tr.each do |ev|
	  @cb.delta(ev.offset - offset)
	  case ev
	  when NoteOff
	    @cb.noteoff(ev.ch, ev.note, ev.vel)
	  when NoteOn
	    @cb.noteon(ev.ch, ev.note, ev.vel)
	  when PolyphonicKeyPressure
	    @cb.polyphonickeypressure(ev.ch, ev.note, ev.val)
	  when ControlChange
	    @cb.controlchange(ev.ch, ev.num, ev.val)
	  when ProgramChange
	    @cb.programchange(ev.ch, ev.num)
	  when ChannelPressure
	    @cb.channelpressure(ev.ch, ev.val)
	  when PitchBendChange
	    @cb.pitchbendchange(ev.ch, ev.val)
	  when ChannelModeMessage
	    @cb.channelmodemessage(ev.ch, ev.num, ev.val)
	  when ExclusiveF0
	    @cb.exclusivef0(ev.data)
	  when ExclusiveF7
	    @cb.exclusivef7(ev.data)
	  when Meta
	    read_meta(ev)
	  end
	  offset = ev.offset
	end
	@cb.track_end
      end

      private :read_meta, :read_track

      def read
	@cb.header(@o.format, @o.ntrks, @o.division, @o.smpte)
	@o.each do |tr|
	  case tr
	  when Track
	    read_track(tr)
	  end
	end
	@cb.result
      end

    end

    class XSCallback

      def header(format, ntrks, division, smpte) end

      def track_start() end
      def track_end() end

      def unknownchunk(ckid, body) end

      def delta(delta) end

      def noteoff(ch, note, vel) end
      def noteon(ch, note, vel) end
      def polyphonickeypressure(ch, note, val) end
      def controlchange(ch, num, val) end
      def programchange(ch, num) end
      def channelpressure(ch, val) end
      def pitchbendchange(ch, val) end
      def channelmodemessage(ch, num, val) end

      def exclusivef0(data) end
      def exclusivef7(data) end

      def sequencenumber(num) end

      def generalpurposetext(text) end
      def copyrightnotice(text) end
      def trackname(text) end
      def instrumentname(text) end
      def lyric(text) end
      def marker(text) end
      def cuepoint(text) end
      def programname(text) end
      def devicename(text) end
      def text0a(text) end
      def text0b(text) end
      def text0c(text) end
      def text0d(text) end
      def text0e(text) end
      def text0f(text) end

      def channelprefix(ch) end
      def midiport(num) end
      def endoftrack() end
      def settempo(tempo) end
      def smpteoffset(hr, mn, se, fr, ff) end
      def timesignature(nn, dd, cc, bb) end
      def keysignature(sf, mi) end
      def sequencerspecific(data) end

      def unknownmeta(meta_type, data) end

      def result() end

      def error(mesg) end
      def warn(mesg) end

    end

    class Decode < XSCallback

      def header(format, ntrks, division, smpte)
	@sq = Sequence.new(format, division, smpte)
      end

      def track_start
	@sq << (@tr = Track.new)
	@offset = 0
      end

      def delta(delta) @offset += delta end

      def noteoff(ch, note, vel)
	@tr << NoteOff.new(@offset, ch, note, vel)
      end

      def noteon(ch, note, vel)
	@tr << NoteOn.new(@offset, ch, note, vel)
      end

      def polyphonickeypressure(ch, note, val)
	@tr << PolyphonicKeyPressure.new(@offset, ch, note, val)
      end

      def controlchange(ch, num, val)
	@tr << ControlChange.new(@offset, ch, num, val)
      end

      def programchange(ch, num)
	@tr << ProgramChange.new(@offset, ch, num)
      end

      def channelpressure(ch, val)
	@tr << ChannelPressure.new(@offset, ch, val)
      end

      def pitchbendchange(ch, val)
	@tr << PitchBendChange.new(@offset, ch, val)
      end

      def channelmodemessage(ch, num, val)
	@tr << ChannelModeMessage.new(@offset, ch, num, val)
      end

      def exclusivef0(data)
	@tr << ExclusiveF0.new(@offset, data)
      end

      def exclusivef7(data)
	@tr << ExclusiveF7.new(@offset, data)
      end

      def sequencenumber(num)
	@tr << SequenceNumber.new(@offset, num)
      end

      def generalpurposetext(text)
	@tr << GeneralPurposeText.new(@offset, text)
      end

      def copyrightnotice(text)
	@tr << CopyrightNotice.new(@offset, text)
      end

      def trackname(text)
	@tr << TrackName.new(@offset, text)
      end

      def instrumentname(text)
	@tr << InstrumentName.new(@offset, text)
      end

      def lyric(text)
	@tr << Lyric.new(@offset, text)
      end

      def marker(text)
	@tr << Marker.new(@offset, text)
      end

      def cuepoint(text)
	@tr << CuePoint.new(@offset, text)
      end

      def programname(text)
	@tr << ProgramName.new(@offset, text)
      end

      def devicename(text)
	@tr << DeviceName.new(@offset, text)
      end

      def text0a(text)
	@tr << Text0A.new(@offset, text)
      end

      def text0b(text)
	@tr << Text0B.new(@offset, text)
      end

      def text0c(text)
	@tr << Text0C.new(@offset, text)
      end

      def text0d(text)
	@tr << Text0D.new(@offset, text)
      end

      def text0e(text)
	@tr << Text0E.new(@offset, text)
      end

      def text0f(text)
	@tr << Text0F.new(@offset, text)
      end

      def channelprefix(ch)
	@tr << ChannelPrefix.new(@offset, ch)
      end

      def midiport(num)
	@tr << MIDIPort.new(@offset, num)
      end

      def endoftrack
	@tr << EndOfTrack.new(@offset)
      end

      def settempo(tempo)
	@tr << SetTempo.new(@offset, tempo)
      end

      def smpteoffset(hr, mn, se, fr, ff)
	@tr << SMPTEOffset.new(@offset, hr, mn, se, fr, ff)
      end

      def timesignature(nn, dd, cc, bb)
	@tr << TimeSignature.new(@offset, nn, dd, cc, bb)
      end

      def keysignature(sf, mi)
	@tr << KeySignature.new(@offset, sf, mi)
      end

      def sequencerspecific(data)
	@tr << SequencerSpecific.new(@offset, data)
      end

      def unknownmeta(meta_type, data)
	@tr << UnknownMeta.new(@offset, meta_type, data)
      end

      def result() @sq end

      def error(mesg) raise ReadError, mesg end

    end

    class Encode < XSCallback

      def header(format, ntrks, division, smpte)
	@ws = WS::PO.new
	# ntrks:1 (if format=0), ntrks:1/2**16-1 (otherwise)
	@ws.puts('MThd')
	@ws.puti32(6)
	@ws.puti16(format)
	@ws.puti16(ntrks)
	if smpte
	  div1 = 0x100 - smpte
	  div2 = division
	else
	  div1 = division >> 8
	  div2 = division & 0xff
	end
	@ws.putc(div1)
	@ws.putc(div2)
      end

      def track_start
	@ev = WS::PO.new
	@running = 0
	@cieot = false
      end

      def track_end
	@ws.puts('MTrk')
	unless @cieot
	  delta(0)
	  endoftrack
	end
	ev = @ev.to_s
	# ev.length:0/2**32-1
	@ws.puti32(ev.length)
	@ws.puts(ev)
      end

      def de
	# @delta:0/2**28-1
	@ev.putl(@delta)
      end

      def sb(stat)
	@ev.putc(stat) unless stat == @running
	case stat
	when 0x80..0xef; @running = stat
	when 0xf0..0xf7; @running = 0
	end
      end

      def db(data)
	@ev.putc(data & 0x7f)
      end

      private :de, :sb, :db

      def delta(delta) @delta = delta end

      def noteoff(ch, note, vel)
	de
	sb(ch | 0x80)
	db(note)
	db(vel)
      end

      def noteon(ch, note, vel)
	de
	sb(ch | 0x90)
	db(note)
	db(vel)
      end

      def polyphonickeypressure(ch, note, val)
	de
	sb(ch | 0xa0)
	db(note)
	db(val)
      end

      def controlchange(ch, num, val)
	de
	sb(ch | 0xb0)
	db(num)
	db(val)
      end

      def programchange(ch, num)
	de
	sb(ch | 0xc0)
	db(num)
      end

      def channelpressure(ch, val)
	de
	sb(ch | 0xd0)
	db(val)
      end

      def pitchbendchange(ch, val)
	de
	sb(ch | 0xe0)
	val += 0x2000
	lsb =  val       & 0x7f
	msb = (val >> 7) & 0x7f
	db(lsb)
	db(msb)
      end

      def channelmodemessage(ch, num, val)
	de
	sb(ch | 0xb0)
	db(num)
	db(val)
      end

      def exclusivef0(data)
	de
	@ev.putc(0xf0)
	@ev.putl(data.length)
	@ev.puts(data)
      end

      def exclusivef7(data)
	de
	@ev.putc(0xf7)
	@ev.putl(data.length)
	@ev.puts(data)
      end

      def sequencenumber(num)
	de
	@ev.putc(0xff)
	@ev.putc(0x0)
	@ev.putl(2)
	@ev.puti16(num)
      end

      def generalpurposetext(text)
	de
	@ev.putc(0xff)
	@ev.putc(0x1)
	@ev.putl(text.length)
	@ev.puts(text)
      end

      def copyrightnotice(text)
	de
	@ev.putc(0xff)
	@ev.putc(0x2)
	@ev.putl(text.length)
	@ev.puts(text)
      end

      def trackname(text)
	de
	@ev.putc(0xff)
	@ev.putc(0x3)
	@ev.putl(text.length)
	@ev.puts(text)
      end

      def instrumentname(text)
	de
	@ev.putc(0xff)
	@ev.putc(0x4)
	@ev.putl(text.length)
	@ev.puts(text)
      end

      def lyric(text)
	de
	@ev.putc(0xff)
	@ev.putc(0x5)
	@ev.putl(text.length)
	@ev.puts(text)
      end

      def marker(text)
	de
	@ev.putc(0xff)
	@ev.putc(0x6)
	@ev.putl(text.length)
	@ev.puts(text)
      end

      def cuepoint(text)
	de
	@ev.putc(0xff)
	@ev.putc(0x7)
	@ev.putl(text.length)
	@ev.puts(text)
      end

      def programname(text)
	de
	@ev.putc(0xff)
	@ev.putc(0x8)
	@ev.putl(text.length)
	@ev.puts(text)
      end

      def devicename(text)
	de
	@ev.putc(0xff)
	@ev.putc(0x9)
	@ev.putl(text.length)
	@ev.puts(text)
      end

      def text0a(text)
	de
	@ev.putc(0xff)
	@ev.putc(0xa)
	@ev.putl(text.length)
	@ev.puts(text)
      end

      def text0b(text)
	de
	@ev.putc(0xff)
	@ev.putc(0xb)
	@ev.putl(text.length)
	@ev.puts(text)
      end

      def text0c(text)
	de
	@ev.putc(0xff)
	@ev.putc(0xc)
	@ev.putl(text.length)
	@ev.puts(text)
      end

      def text0d(text)
	de
	@ev.putc(0xff)
	@ev.putc(0xd)
	@ev.putl(text.length)
	@ev.puts(text)
      end

      def text0e(text)
	de
	@ev.putc(0xff)
	@ev.putc(0xe)
	@ev.putl(text.length)
	@ev.puts(text)
      end

      def text0f(text)
	de
	@ev.putc(0xff)
	@ev.putc(0xf)
	@ev.putl(text.length)
	@ev.puts(text)
      end

      def channelprefix(ch)
	de
	@ev.putc(0xff)
	@ev.putc(0x20)
	@ev.putl(1)
	@ev.putc(ch)
      end

      def midiport(num)
	de
	@ev.putc(0xff)
	@ev.putc(0x21)
	@ev.putl(1)
	@ev.putc(num)
      end

      def endoftrack
	de
	@ev.putc(0xff)
	@ev.putc(0x2f)
	@ev.putl(0)
	@cieot = true
      end

      def settempo(tempo)
	de
	@ev.putc(0xff)
	@ev.putc(0x51)
	@ev.putl(3)
	@ev.puti24(tempo)
      end

      def smpteoffset(hr, mn, se, fr, ff)
	de
	@ev.putc(0xff)
	@ev.putc(0x54)
	@ev.putl(5)
	@ev.putc(hr)
	@ev.putc(mn)
	@ev.putc(se)
	@ev.putc(fr)
	@ev.putc(ff)
      end

      def timesignature(nn, dd, cc, bb)
	de
	@ev.putc(0xff)
	@ev.putc(0x58)
	@ev.putl(4)
	@ev.putc(nn)
	@ev.putc(dd)
	@ev.putc(cc)
	@ev.putc(bb)
      end

      def keysignature(sf, mi)
	sf = WS::PO.s2u(sf, 8)
	de
	@ev.putc(0xff)
	@ev.putc(0x59)
	@ev.putl(2)
	@ev.putc(sf)
	@ev.putc(mi)
      end

      def sequencerspecific(data)
	de
	@ev.putc(0xff)
	@ev.putc(0x7f)
	@ev.putl(data.length)
	@ev.puts(data)
      end

      def unknownmeta(meta_type, data)
	de
	@ev.putc(0xff)
	@ev.putc(meta_type)
	@ev.putl(data.length)
	@ev.puts(data)
      end

      def result() @ws.to_s end

      def error(mesg) raise ReadError, mesg end

    end

    class << self

      def decode(s)
	RS.new(s, Decode.new).read
      end

      def decodeio(io)
	decode(io.binmode.read)
      end

      def decodefile(fn)
	open(fn) do |io|
	  decodeio(io)
	end
      end

    end

    def initialize(format=1, division=96, smpte=nil)
      # format:0/2, division:1/2**16-1
      @format, @division, @smpte, @arr = format, division, smpte, []
    end

    attr_accessor :format, :division, :smpte

    def length() @arr.length end

    alias_method :size, :length

    def ntrks() @arr.compact.length end

    def << (tr)
      @arr << tr
      self
    end

    def >> (tr)
      @arr.reject!{|x| x.object_id == tr.object_id}
      self
    end

    def push(tr) @arr.push(tr) end
    def pop() @arr.pop end

    def unshift(tr) @arr.unshift(tr) end
    def shift() @arr.shift end

    def []  (n)     @arr[n]      end
    def []= (n, tr) @arr[n] = tr end

    def each
      arr = @arr.compact
      arr.each do |tr|
	yield tr
      end
      self
    end

    def encode
      WS.new(self, Encode.new).read
    end

    def encodeio(io)
      io.binmode.write(encode)
    end

    def encodefile(fn)
      open(fn, 'w') do |io|
	encodeio(io)
      end
    end

  end

  class Track

    def initialize
      @arr = []
    end

    def length() @arr.length end

    alias_method :size, :length

    def nevts() @arr.compact.length end

    def << (ev)
      @arr << ev
      self
    end

    def >> (ev)
      @arr.reject!{|x| x.object_id == ev.object_id}
      self
    end

    def push(ev) @arr.push(ev) end
    def pop() @arr.pop end

    def unshift(ev) @arr.unshift(ev) end
    def shift() @arr.shift end

    def []  (n)     @arr[n]      end
    def []= (n, tr) @arr[n] = tr end

    def stable_sort(xs)
      i = -1
      xs.collect!{|x| [x, i += 1]}
      xs.sort!
      xs.collect!{|x| x[0]}
    end

    private :stable_sort

    def each
      arr = @arr.compact
      stable_sort(arr)
      arr.each do |ev|
	yield ev
      end
      self
    end

  end

  class Event

    include Comparable

    def initialize(offset)
      @offset = offset
    end

    attr_accessor :offset

    def <=>(other) self.offset <=> other.offset end

  end

  class MIDIMessage < Event; end

  class ChannelMessage < MIDIMessage

    def initialize(offset, ch)
      super(offset)
      # ch:0/2**4-1
      @ch = ch
    end

    attr_accessor :ch

  end

  class VoiceMessage < ChannelMessage; end

  class NoteOff < VoiceMessage

    def initialize(offset, ch, note, vel)
      super(offset, ch)
      # note:0/2**7-1, vel:0/2**7-1
      @note, @vel = note, vel
    end

    attr_accessor :note, :vel

  end

  class NoteOn < VoiceMessage

    def initialize(offset, ch, note, vel)
      super(offset, ch)
      # note:0/2**7-1, vel:0/2**7-1
      @note, @vel = note, vel
    end

    attr_accessor :note, :vel

  end

  class PolyphonicKeyPressure < VoiceMessage

    def initialize(offset, ch, note, val)
      super(offset, ch)
      # note:0/2**7-1, val:0/2**7-1
      @note, @val = note, val
    end

    attr_accessor :note, :val

  end

  class ControlChange < VoiceMessage

    def initialize(offset, ch, num, val)
      super(offset, ch)
      # num:0/119, val:0/2**7-1
      @num, @val = num, val
    end

    attr_accessor :num, :val

  end

  class ProgramChange < VoiceMessage

    def initialize(offset, ch, num)
      super(offset, ch)
      # num:0/2**7-1
      @num = num
    end

    attr_accessor :num

  end

  class ChannelPressure < VoiceMessage

    def initialize(offset, ch, val)
      super(offset, ch)
      # val:0/2**7-1
      @val = val
    end

    attr_accessor :val

  end

  class PitchBendChange < VoiceMessage

    def initialize(offset, ch, val)
      super(offset, ch)
      # val:-2**13/2**13-1
      @val = val
    end

    attr_accessor :val

  end

  class ChannelModeMessage < ChannelMessage

    def initialize(offset, ch, num, val)
      super(offset, ch)
      # num:120/2**7-1, val:0/2**7-1
      @num, @val = num, val
    end

    attr_accessor :num, :val

  end

  class SystemMessage < Event; end
  class Exclusive < SystemMessage; end

  class ExclusiveF0 < Exclusive

    def initialize(offset, data)
      super(offset)
      # data.length:0/2**28-1
      @data = data
    end

    attr_accessor :data

  end

  class ExclusiveF7 < Exclusive

    def initialize(offset, data)
      super(offset)
      # data.length:0/2**28-1
      @data = data
    end

    attr_accessor :data

  end

  class Meta < Event

    def initialize(offset, meta_type)
      super(offset)
      # meta_type:0/2**7-1
      @meta_type = meta_type
    end

    attr :meta_type

  end

  class SequenceNumber < Meta

    def initialize(offset, num)
      super(offset, 0x0)
      # num:0/2**16-1
      @num = num
    end

    attr_accessor :num

  end

  class Text < Meta

    def initialize(offset, meta_type, text)
      super(offset, meta_type)
      # meta_type:0/2**4-1, text.length:0/2**28-1
      @text = text
    end

    attr_accessor :text

  end

  class GeneralPurposeText < Text

    def initialize(offset, text)
      super(offset, 0x1, text)
    end

  end

  Text01 = GeneralPurposeText

  class CopyrightNotice < Text

    def initialize(offset, text)
      super(offset, 0x2, text)
    end

  end

  Text02 = CopyrightNotice

  class TrackName < Text

    def initialize(offset, text)
      super(offset, 0x3, text)
    end

  end

  SequenceName = TrackName
  Text03 = TrackName

  class InstrumentName < Text

    def initialize(offset, text)
      super(offset, 0x4, text)
    end

  end

  Text04 = InstrumentName

  class Lyric < Text

    def initialize(offset, text)
      super(offset, 0x5, text)
    end

  end

  Text05 = Lyric

  class Marker < Text

    def initialize(offset, text)
      super(offset, 0x6, text)
    end

  end

  Text06 = Marker

  class CuePoint < Text

    def initialize(offset, text)
      super(offset, 0x7, text)
    end

  end

  Text07 = CuePoint

  class ProgramName < Text

    def initialize(offset, text)
      super(offset, 0x8, text)
    end

  end

  Text08 = ProgramName

  class DeviceName < Text

    def initialize(offset, text)
      super(offset, 0x9, text)
    end

  end

  Text09 = DeviceName

  class Text0A < Text

    def initialize(offset, text)
      super(offset, 0xa, text)
    end

  end

  class Text0B < Text

    def initialize(offset, text)
      super(offset, 0xb, text)
    end

  end

  class Text0C < Text

    def initialize(offset, text)
      super(offset, 0xc, text)
    end

  end

  class Text0D < Text

    def initialize(offset, text)
      super(offset, 0xd, text)
    end

  end

  class Text0E < Text

    def initialize(offset, text)
      super(offset, 0xe, text)
    end

  end

  class Text0F < Text

    def initialize(offset, text)
      super(offset, 0xf, text)
    end

  end

  class ChannelPrefix < Meta

    def initialize(offset, ch)
      super(offset, 0x20)
      # ch:0/2**4-1
      @ch = ch
    end

    attr_accessor :ch

  end

  class MIDIPort < Meta

    def initialize(offset, num)
      super(offset, 0x21)
      # num:0/2**8-1
      @num = num
    end

    attr_accessor :num

  end

  class EndOfTrack < Meta

    def initialize(offset)
      super(offset, 0x2f)
    end

  end

  class SetTempo < Meta

    def initialize(offset, tempo)
      super(offset, 0x51)
      # tempo:1/2**24-1
      @tempo = tempo
    end

    attr_accessor :tempo

  end

  class SMPTEOffset < Meta

    def initialize(offset, hr, mn, se, fr, ff)
      super(offset, 0x54)
      # hr:0/119, mn:0/59, se:0/59, fr:0/29, ff:0/99
      @hr, @mn, @se, @fr, @ff = hr, mn, se, fr, ff
    end

    attr_accessor :hr, :mn, :se, :fr, :ff

  end

  class TimeSignature < Meta

    def initialize(offset, nn, dd, cc, bb)
      super(offset, 0x58)
      # nn,cc,bb:1/2**8-1, dd:0/2**8-1
      @nn, @dd, @cc, @bb = nn, dd, cc, bb
    end

    attr_accessor :nn, :dd, :cc, :bb

  end

  class KeySignature < Meta

    def initialize(offset, sf, mi)
      super(offset, 0x59)
      # sf:-2**7/2**7-1, mi:0/1
      @sf, @mi = sf, mi
    end

    attr_accessor :sf, :mi

  end

  class SequencerSpecific < Meta

    def initialize(offset, data)
      super(offset, 0x7f)
      # data.length:0/2**28-1
      @data = data
    end

    attr_accessor :data

  end

end
