(* smf2txt.sml *) (* Copyright (C) 2000 Daikoku Manabu *) (* Friday, 3 March 2000 *) exception FileName fun smfToText (infilename,outfilename) = let open TextIO Vector Int (* open files *) val _ = if infilename=outfilename then raise FileName else () val instr = BinIO.openIn infilename val outstr = openOut outfilename (* hexadecimal utilities *) fun left w = Word8.>> (w,0wx4) fun right w = Word8.andb (w,0wxf) fun isFlagTrue w = Word8.andb (w,0wx80) = 0wx80 fun delFlag w = Word8.andb (w,0wx7f) fun wtoi w = Word8.toInt w fun wtois w = toString (wtoi w) fun wtos w = str (chr (wtoi w)) fun wtoh w = let open String val hexdigit = "0123456789ABCDEF" in extract (hexdigit,wtoi (left w),SOME 1) ^ extract (hexdigit,wtoi (right w),SOME 1) ^ "H" end fun wvtos wv = let val len = length wv val s = ref "" val i = ref 0 in while (!i raise EOF fun readN n = let val v = ref #[] val i = ref 0 in while (!i"MTrk" then raise TrackHeader else ( outputL [mtrk,"\n", "length=",wvtois length,"\n", "[measure:beat:tick]\n"]; wvtoi length ) end fun longValue () = let val v = ref #[] val w = ref 0wx0 val continue = ref true in while !continue do ( w := readDown (); v := concat [!v,#[delFlag (!w)]]; if not (isFlagTrue (!w)) then continue := false else () ); wvtoi (!v) end fun hexDump length = let val count = ref 0 in while !count outputS "text " | 0wx2 => outputS "copyright " | 0wx3 => outputS "sequence/track name " | 0wx4 => outputS "instrument name " | 0wx5 => outputS "lyric " | 0wx6 => outputS "marker " | 0wx7 => outputS "cue point " | _ => outputL [wtoh eventtype," "]; outputS "\""; while !i=(w,0wxf6) fun outputStatus status = let val ls = left status val rs = right status in if Word8.<=(ls,0wxe) then ( outputL ["Ch ", toString ((wtoi rs)+1)," "]; case ls of 0wx8 => outputS "noteoff " | 0wx9 => outputS "noteon " | 0wxa => outputS "polyphonic key pressure " | 0wxb => outputS "control change " | 0wxc => outputS "program change " | 0wxd => outputS "channel key pressure " | 0wxe => outputS "pitch bend change " | _ => outputL [wtoh status," "] ) else outputL [wtoh status," "] end fun isLengthTwo status = let val ls = left status in Word8.<=(ls,0wxb) orelse ls=0wxe orelse status=0wxf2 end fun midiEventData (status,dataone) = let val ls = left status val rs = right status in outputStatus status; if isLengthTwo status then outputL [wtois dataone, " ", wtois (readDown ())," "] else outputL [wtois dataone, " "] end fun sysEx status = let val length = longValue () val i = ref 0 in outputL ["SysEx(",wtoh status,") "]; while !i endOfTrack () | 0wx51 => tempo () | 0wx54 => smpteOffset () | 0wx58 => timeSignature () | 0wx59 => keySignature () | 0wx7f => sequencerSpecific () | _ => unknownMetaEvent () end val running = ref 0wx0 fun midiEvent w = if isFlagTrue w then ( running := w; if isStatusOnly w then () else midiEventData (w,readDown ()) ) else ( if isStatusOnly (!running) then () else midiEventData (!running,w) ) fun event () = let val w = ref 0wx0 in deltaTime (longValue ()); w := readDown (); case !w of 0wxf0 => sysEx (!w) | 0wxf7 => sysEx (!w) | 0wxff => metaEvent () | _ => midiEvent (!w); outputS "\n" end fun track () = while !remain>0 do event () fun trackToText () = ( currenttime := 0; remain := trackHeader (); track () ) fun nTracksToText ntracks = let val i = ref 0 in while !i"MThd" then raise SMFHeader else ( outputL [mthd,"\n", "format=",wvtois format,"\n", "ntracks=",wvtois ntracks,"\n"]; setTimebase (); (wvtoi format,wvtoi ntracks) ) end fun smfToText2 () = let val (format,ntracks) = header () in case format of 0 => nTracksToText 1 | 1 => nTracksToText ntracks | _ => printL ["Format",toString format, " is not supported.\n"] end fun smfToText1 () = smfToText2 () handle SMFHeader => print "Illegal SMF header.\n" | Timebase => print "Absolute time is not supported.\n" | EOF => print "Unexpected EOF.\n" | TrackHeader => print "Illegal track header.\n" in smfToText1 (); BinIO.closeIn instr; closeOut outstr end