Contents
Articles
Behaviors
Books
Director News
Director Web Sites
FAQ
Games
Mailing Lists
News Groups
Project Examples
Reviews
Software
Tools
Useful Web Sites
Utilities
Xtras

Don't miss these
Vector Shape - Create Square Wave
StarMenu Xtra
Simple Sprite Mover Behavior
Cross-platform scripting differences: Sorted Lists
Director 7 Lingo Dictionary
Home, End, Page Up, Page Down with Text Members
Volume Control Dial
Macromedia Director 7 Lingo training CD
Image Mover
Set Quality of a Flash Cast member
 

 

 

Behavior MIDI Class

Added on 3/25/2004

 

Compatibilities:
D6_5 D7 D8 D8_5 D9 Mac Parent PC

This item has not yet been rated

Author: fluxus

Parent Script for reading, writing, analyzing, modifying, and creating standard MIDI files (*.mid, *.rmi) of type 0 or 1.

Download PC Source
--/****************************************************************************
--Software: MIDI CLASS
--Version:  1.0
--Date:     2003/12/23
--Author:   Valentin Schmidt
--Contact:  fluxus@freenet.de
--License:  Freeware
--
--You may use and modify this software as you wish.
--****************************************************************************/


property pTracks          --array of tracks, where each track is array of message strings
property pTimebase        --timebase = ticks per frame (quarter note)
property pTempo           --tempo as integer (0 for unknown)
property pTempoMsgNum     --position of tempo event in track 0

property pLF
property pCR

--/****************************************************************************
--*                                                                           *
--*                              Public methods                               *
--*                                                                           *
--****************************************************************************/

-----------------------------------------------------------------
-- creates (or resets to) new empty MIDI song
-----------------------------------------------------------------
on new (me,timebase)
  if voidP(timebase) then timebase=480
  me.pTempo = 0 --125000 = 120 bpm
  me.pTimebase = timebase
  me.pTracks = []
  
  pLF=chr(10) --

  pCR=chr(13) --

  
  return me
end

-----------------------------------------------------------------
-- sets tempo by replacing set tempo msg in track 0 (or adding new track 0)
-----------------------------------------------------------------
on setTempo (me,tempo)
  tempo = integer(tempo)
  if (NOT voidP(me.pTempoMsgNum)) then
    me.pTracks[1][me.pTempoMsgNum] = "0 Tempo "&tempo
  else
    pTempoTrack = ["0 TimeSig 4/4 24 8","0 Tempo "&tempo,"0 Meta TrkEnd"]
    --array_unshift(me.pTracks, pTempoTrack)
    
    tmp=[pTempoTrack]
    repeat with i = 1 to count(me.pTracks)
      tmp.add(me.pTracks[i])
    end repeat
    me.pTracks=tmp
    
    me.pTempoMsgNum = 1
  end if
  me.pTempo = tempo
end

-----------------------------------------------------------------
-- returns tempo (0 if not set)
-----------------------------------------------------------------
on getTempo (me)
  return me.pTempo
end

-----------------------------------------------------------------
-- sets tempo corresponding to given bpm
-----------------------------------------------------------------
on setBpm (me,bpm)
  tempo = integer(60000000/bpm)
  me.setTempo(tempo)
end

-----------------------------------------------------------------
-- returns bpm corresponding to tempo
-----------------------------------------------------------------
on getBpm (me)
  if (me.pTempo<>0) then return 60000000/me.pTempo
  else return 0
end

-----------------------------------------------------------------
-- sets timebase
-----------------------------------------------------------------
on setTimebase (me,tb)
  me.pTimebase = tb
end

-----------------------------------------------------------------
-- returns timebase
-----------------------------------------------------------------
on getTimebase (me)
  return me.pTimebase
end

-----------------------------------------------------------------
-- adds new track, returns new track count
-----------------------------------------------------------------
on newTrack (me)
  tracks=me.pTracks
  tracks.add([])
  me.pTracks=tracks
  return count(me.pTracks)
end

-----------------------------------------------------------------
-- returns track tn as array of msg strings
-----------------------------------------------------------------
on getTrack (me,tn)
  return me.pTracks[tn]
end

-----------------------------------------------------------------
-- returns number of messages of track tn
-----------------------------------------------------------------
on getMsgCount (me,tn)
  return count(me.pTracks[tn])
end

-----------------------------------------------------------------
-- adds message to end of track tn
-----------------------------------------------------------------
on addMsg (me,tn, msgStr, ttype) --0:absolute, 1:delta
  if voidP(ttype) then ttype=0
  
  track = me.pTracks[tn]
  
  if (ttype=1) then
    last = me._getTime(track.getLast())
    msg = explode(" ",msgStr)
    dt = integer(msg[1])
    msg[1] = last + dt
    msgStr = implode(" ",msg)
  end if
  
  track.add(msgStr)
  me.pTracks[tn] = track
end

-----------------------------------------------------------------
-- adds message at adequate position of track n (slower than addMsg)
-----------------------------------------------------------------
on insertMsg (me,tn,msgStr)
  time = me._getTime(msgStr)
  track = me.pTracks[tn]
  mc = count(track)
  
  repeat with i=1 to mc
    t = me._getTime(track[i])
    if (t>=time) then exit repeat
  end repeat
  array_splice(me.pTracks[tn], i, 0, msgStr)
end

-----------------------------------------------------------------
-- returns message number mn of track tn
-----------------------------------------------------------------
on getMsg (me,tn,mn)
  return me.pTracks[tn][mn]
end

-----------------------------------------------------------------
-- deletes message number mn of track tn
-----------------------------------------------------------------
on deleteMsg (me,tn,mn)
  array_splice(me.pTracks[tn], mn, 1)
end

-----------------------------------------------------------------
-- deletes track tn
-----------------------------------------------------------------
on deleteTrack (me,tn)
  array_splice(me.pTracks, tn, 1)
  return count(me.pTracks)
end

-----------------------------------------------------------------
-- deletes track tn
-----------------------------------------------------------------
on getTrackCount (me)
  return count(me.pTracks)
end

-----------------------------------------------------------------
-- deletes all tracks except track tn (and track 1 which contains tempo info)
-----------------------------------------------------------------
on soloTrack (me,tn)
  if (tn=1) then me.pTracks = [me.pTracks[1]]
  else me.pTracks = [me.pTracks[1],me.pTracks[tn]]
end

-----------------------------------------------------------------
-- transposes song by dn half tone steps
-----------------------------------------------------------------
on transpose (me,dn)
  tc = count(me.pTracks)
  repeat with i = 1 to tc
    transposeTrack(i,dn)
  end repeat
end

-----------------------------------------------------------------
-- transposes track tn by dn half tone steps
-----------------------------------------------------------------
on transposeTrack (me,tn, dn)
  track = me.pTracks[tn]
  mc = count(track)
  repeat with i = 1 to mc
    msg = explode(" ",track[i])
    if (msg[2] = "On" OR msg[2] = "Off") then
      do(msg[4]) -- n
      n = max(0,min(127,n+dn))
      msg[4] = "n=n"
      track[i] = join(" ",msg)
    end if
  end repeat
  me.pTracks[tn] = track
end

-----------------------------------------------------------------
-- import whole MIDI song as text (mf2t-format)
-----------------------------------------------------------------
on importTxt (me,txt)
  if NOT (txt starts "MFile") then me._err(">>> no valid MIDI text!")
  
  txt = trim(txt)
  -- make unix text format
  if (txt contains pCR)  AND NOT (txt contains pLF) then -- MAC
    txt = str_replace(pCR,pLF,txt)
  else -- PC?
    txt = str_replace(pCR,"",txt)
  end if
  txt = txt&pLF-- makes things easier
  
  the itemdelimiter=pLF
  headerStr = txt.item[1]
  header = explode(" ",headerStr) --"MFile type tc pTimebase"
  --me.pType = header[2]
  me.pTimebase = header[4]
  me.pTempo = 0
  
  trackstrings = explode("MTrk"&pLF,txt)
  
  trackstrings.deleteAt(1)
  tracks = []
  
  repeat with trackstr in trackstrings
    track = explode(pLF,trackstr)
    track.deleteAt(track.count)
    track.deleteAt(track.count)
    if (track[1]="TimestampType=Delta") then--delta
      track.deleteAt(1)
      track = me._delta2Absolute(track)
    end if
    
    tracks.add(track)
  end repeat
  me.pTracks = tracks
  me._findTempo()
end

-----------------------------------------------------------------
-- imports track as text (mf2t-format)
-----------------------------------------------------------------
on importTrackTxt (me,txt, tn)
  txt = trim(txt)
  -- make unix text format
  if (txt contains pCR) AND NOT (txt contains pLF) then -- MAC
    txt = str_replace(pCR,pLF,txt)
  else -- maybe PC, 0D 0A?
    txt = str_replace(pCR,"",txt)
  end if
  track = explode(pLF,txt)
  
  if (track[1]="MTrk") then track.deleteAt(1)
  if (track.getLast()="TrkEnd") then track.deleteAt(track.count)
  
  if (track[1]="TimestampType=Delta") then --delta
    track.deleteAt(1)
    track = me._delta2Absolute(track)
  end if
  
  if voidP(tn) then tn=count(me.pTracks)  
  me.pTracks[tn] = track
  if (tn=0) then me._findTempo()
end

-----------------------------------------------------------------
-- returns MIDI song as text
-----------------------------------------------------------------
on getTxt (me,ttype,ret) --0:absolute, 1:delta
  if voidP(ttype) then ttype=0
  if voidP(ret) then ret=pLF
  
  timebase = me.pTimebase
  tracks = me.pTracks
  tc = count(me.pTracks)
  type=integer(tc>1)
  str =  "MFile" && type && tc && timebase &ret
  repeat with i=1 to tc
    str=str& me.getTrackTxt(i,ttype,ret)
  end repeat
  return str
end

-----------------------------------------------------------------
-- returns track as text
-----------------------------------------------------------------
on getTrackTxt (me,tn,ttype,ret) --0:absolute, 1:delta
  if voidP(ttype) then ttype=0
  if voidP(ret) then ret=pLF
  
  track = me.pTracks[tn]
  str = "MTrk" &ret
  if (ttype=1) then --time as delta
    str=str& "TimestampType=Delta" &ret
    last = 0
    repeat with msg in track
      t=integer(msg.word[1])
      dt=t-last
      delete word 1 of msg
      str=str& dt && msg &ret
      last = t
    end repeat
  else
    repeat with msg in track
      str=str& msg &ret
    end repeat
  end if
  str=str& "TrkEnd" &ret
  return str
end

-----------------------------------------------------------------
-- imports Standard MIDI File (typ 0 or 1) (and RMID)
-- (if optional parameter tn set, only track tn is imported)
-----------------------------------------------------------------
on importMid (me,smf_path,tn)
  song = readBinFile(smf_path) -- Standard MIDI File, typ 0 or 1, or RMID
  if (offset("MThd",song)>1) then delete char 1 to offset("MThd",song)-1 of song -- get rid of RMID header
  header = song.char[1..14]
  if NOT (header starts "MThd"&chr(0)&chr(0)&chr(0)&chr(6)) then me._err(">>> wrong MIDI-header!")
  type = ord(header.char[10])
  if (type>1) then me._err(">>> only SMF Typ 0 and 1 supported!")
  --trackCnt = ord(header[10])*256 + ord(header[11]) --ignore
  timebase = ord(header.char[13])*256 + ord(header.char[14])
  --me.pType = type
  me.pTimebase = timebase
  me.pTempo = 0 -- maybe (hopefully!) overwritten by _parseTrack
  trackstrings = explode("MTrk",song)
  trackstrings.deleteAt(1)
  tracks = []
  tsc = count(trackstrings)
  if (NOT voidP(tn)) then
    if (tn>=tsc) then me._err(">>> SMF has less tracks than "&tn&"!")
    tracks.add(me._parseTrack(trackstrings[tn],tn))
  else
    repeat with i = 1 to tsc
      tracks.add(me._parseTrack(trackstrings[i],i))
    end repeat
  end if
  me.pTracks = tracks
end

-----------------------------------------------------------------
-- returns binary MIDI string
-----------------------------------------------------------------
on getMid (me)
  pTracks = me.pTracks
  tc = count(pTracks)
  type = integer(tc > 1)
  midStr = "MThd"&chr(0)&chr(0)&chr(0)&chr(6)&chr(0) & chr(type) & me._getBytes(tc,2) & me._getBytes(me.pTimebase,2)
  
  repeat with i = 1 to tc
    track = pTracks[i]
    mc = count(track)
    time = 0
    midStr=midStr& "MTrk"
    trackstart = length(midStr)
    
    last = ""
    
    repeat with j = 1 to mc
      lin = track[j]
      t = me._getTime(lin)
      dt = t - time
      time = t
      midStr=midStr& me._writeVarLen(dt)
      
      -- REPETITION: same event, same channel, omit first byte (smaller file size)
      str = me._getMsgStr(lin)
      start = ord(str.char[1])
      if (start=last AND start>=128 AND start<=239) then --239 --159
        delete char 1 of str
      end if
      
      last = start
      
      midStr=midStr& str
    end repeat
    trackLen = length(midStr) - trackstart
    midStr = midStr.char[1..trackstart] & me._getBytes(trackLen,4) & midStr.char[trackstart+1..length(midStr)] --???
  end repeat
  return midStr
end

-----------------------------------------------------------------
-- saves MIDI song as Standard MIDI File
-----------------------------------------------------------------
on saveMidFile (me,mid_path)
  if (count(me.pTracks)<1) then me._err("MIDI song has no tracks!")
  --writeFile(me.getMid(),mid_path)
  saveTxtFile(mid_path,me.getMid())
end

--/****************************************************************************
--*                                                                           *
--*                              Private methods                              *
--*                                                                           *
--****************************************************************************/

-----------------------------------------------------------------
-- returns time code of message string
-----------------------------------------------------------------
on _getTime (me,msgStr)
  return integer(msgStr.word[1]) --strtok(msgStr," "))
end

-----------------------------------------------------------------
-- returns binary code for message string
-----------------------------------------------------------------
on _getMsgStr (me,lin){
  ch=VOID
  p=VOID
  n=VOID
  v=VOID
  c=VOID
  msg = explode(" ",lin)
  
  case (msg[2]) of
    "PrCh": -- 0x0C
      do(msg[3]) -- chan
      do(msg[4]) -- prog
      return chr(192+ch-1)&chr(p) --0xC0
      
    "On": -- 0x09
      do(msg[3]) -- chan
      do(msg[4]) -- note
      do(msg[5]) -- vel
      return chr(144+ch-1)&chr(n)&chr(v)--0x90
      
    "Off": -- 0x08
      do(msg[3]) -- chan
      do(msg[4]) -- note
      do(msg[5]) -- vel
      return chr(128+ch-1)&chr(n)&chr(v)--0x80
      
    "PoPr": -- 0x0A = PolyPressure
      do(msg[3]) -- chan
      do(msg[4]) -- note
      do(msg[5]) -- val
      return chr(160+ch-1)&chr(n)&chr(v)--0xA0
      
    "Par": -- 0x0B = ControllerChange
      do(msg[3]) -- chan
      do(msg[4]) -- controller
      do(msg[5]) -- val
      return chr(176+ch-1)&chr(c)&chr(v)--0xB0
      
    "ChPr": -- 0x0D = ChannelPressure
      do(msg[3]) -- chan
      do(msg[4]) -- val
      return chr(208+ch-1)&chr(v)--0xD0
      
    "Pb": -- 0x0E = PitchBend
      do(msg[3]) -- chan
      do(msg[4]) -- val (2 Bytes!)
      a = v mod 256
      b = 64 + (v - a)/128
      return chr(224+ch-1)&chr(a)&chr(b)--0xE0
      
      -- META EVENTS
    "Seqnr": -- 0x00 = sequence_number
      num = chr(msg[3])
      return chr(255)&chr(0)&chr(2)&num --"xFFx00x02"&num
      
    "Meta":
      type = msg[3]
      case (type) of
          -- 0x01: Meta Text
          -- 0x02: Meta Copyright
          -- 0x03: Meta TrackName ???SeqName???
          -- 0x04: Meta InstrumentName
          -- 0x05: Meta Lyrics
          -- 0x06: Meta Marker
          -- 0x07: Meta Cue
        "Text","Copyright","TrkName","InstrName","Lyric","Marker","Cue":
          texttypes = ["Text","Copyright","TrkName","InstrName","Lyric","Marker","Cue"]
          byte = chr(texttypes.getPos(type))
          start = offset(QUOTE,lin)+1
          ende = start+ offset(QUOTE,lin.char[start..length(lin)])-2
          txt = lin.char[start..ende]
          len = chr(length(txt))
          return chr(255)&byte&len&txt --"xFF"
          
        "TrkEnd": --0x2F
          return chr(255)&chr(47)&chr(0) --"xFFx2Fx00"
          
        "0x20": -- 0x20 = ChannelPrefix
          v = chr(msg[4])
          return chr(255)&chr(32)&chr(1)&v --"xFFx20x01"&v
          
        "0x21": -- 0x21 = ChannelPrefixOrPort
          v = chr(msg[4])
          return chr(255)&chr(33)&chr(01)&v --"xFFx21x01"&v
          
        otherwise:
          me._err(">>> unknown meta event:" && type)
          halt()
      end case
      
      
    "Tempo": -- 0x51
      tempo = me._getBytes(integer(msg[3]),3)
      return chr(255)&chr(81)&chr(3)&tempo --"xFFx51x03"&tempo
      
    "SMPTE": -- 0x54 = SMPTE offset
      h = chr(msg[3])
      m = chr(msg[4])
      s = chr(msg[5])
      f = chr(msg[6])
      fh = chr(msg[7])
      return chr(255)&chr(84)&chr(5) &h&m&s&f&fh --"xFFx54x05"
      
    "TimeSig": -- 0x58
      zt = explode("/",msg[3])
      z = chr(zt[1])
      t = chr(log(integer(zt[2]))/log(2))
      mc = chr(msg[4])
      c = chr(msg[5])
      return chr(255)&chr(88)&chr(4) &z&t&mc&c --"xFFx58x04"
      
    "KeySig": -- 0x59
      vz = chr(msg[3])
      g = chr(integer(msg[4]<>"major"))
      return chr(255)&chr(89)&chr(2) &vz&g --"xFFx59x02"
      
    "SeqSpec": -- 0x7F = Sequencer specific data (eg: 0 SeqSpec 00 00 41)
      cnt = count(msg)-2
      data = ""
      repeat with i = 1 to cnt
        data=data& me._hex2bin(msg[i+2])
      end repeat
      len = chr(length(data))
      return chr(255)&chr(127) &len&data --"xFFx7F"
      
    "SysEx": -- 0xF0 = SysEx
      start = offset("f0",lin)+3
      ende = start+offset("f7",lin.char[start..length(lin)-1])
      data = lin.char[start..ende]
      data = me._hex2bin(str_replace(" ","",data))
      len = chr(length(data))
      return chr(240) &len&data --"xF0"
      
    otherwise:
      me._err(">>> unknown event: "&msg[2])
      halt()
  end case
end

-----------------------------------------------------------------
-- converts binary track string to track (list of msg strings)
-----------------------------------------------------------------
on _parseTrack (me,binStr, tn)
  trackLen = length(binStr)
  p=4 +1
  time = 0
  track = []
  
  last=""
  
  repeat while (p    
    -- timedelta
    a=[p]
    dt = me._readVarLen(binStr,a)
    p=a[1]
    
    time=time+ dt
    
    byte = ord(binStr.char[p])
    --high = byte >> 4
    high = byte/16
    low = byte - high*16
    
    case (high) of
      8: --0x08: --Off
        chan = low+1
        note = ord(binStr.char[p+1])
        vel = ord(binStr.char[p+2])
        last = "Off"
        track.add(time && "Off ch="&chan&" n="¬e&" v="&vel)
        p=p+3
        
      9: --0x09: --On
        chan = low+1
        note = ord(binStr.char[p+1])
        vel = ord(binStr.char[p+2])
        last = "On"
        track.add(time && "On ch="&chan&" n="¬e&" v="&vel)
        p=p+3
        
      10: --0x0A: --PoPr = PolyPressure
        chan = low+1
        note = ord(binStr.char[p+1])
        val = ord(binStr.char[p+2])
        last = "PoPr"
        track.add(time && "PoPr ch="&chan&" n="¬e&" v="&val)
        p=p+3
        
      11: --0x0B: --Par = ControllerChange
        chan = low+1
        c = ord(binStr.char[p+1])
        val = ord(binStr.char[p+2])
        last = "Par"
        track.add(time && "Par ch="&chan&" c="&c&" v="&val)
        p=p+3
        
      12: --0x0C: --PrCh = ProgramChange
        chan = low+1
        prog = ord(binStr.char[p+1])
        last = "PrCh"
        track.add(time && "PrCh ch="&chan&" p="&prog)
        p=p+2
        
      13: --0x0D: --ChPr = ChannelPressure
        chan = low+1
        val = ord(binStr.char[p+1])
        last = "ChPr"
        track.add(time && "ChPr ch="&chan&" v="&val)
        p=p+2
        
      14: --0x0E: --Pb = PitchBend
        chan = low+1
        val = ord(binStr.char[p+1]) + (ord(binStr.char[p+2])-64)*128
        last = "Pb"
        track.add(time &&"Pb ch="&chan&" v="&val)
        p=p+3
        
      otherwise:
        case (byte) of
          255: --0xFF: -- Meta
            meta = ord(binStr.char[p+1])
            case (meta) of
              0: --0x00: -- sequence_number
                num = ord(binStr.char[p+2])
                track.add(time && "Seqnr "&num)
                p=p+2
                
              1,2,3,4,5,6,7:
                texttypes = ["Text","Copyright","TrkName","InstrName","Lyric","Marker","Cue"]
                type = texttypes[meta]
                len = ord(binStr.char[p+2])
                txt = binStr.char[p+3..p+3+len-1]
                
                track.add(time && "Meta "&type &"E&txt"E)
                p=p+len+3
                
              32: --0x20: -- ChannelPrefix
                chan = ord(binStr.char[p+3])
                if (chan<10) then chan = "0"&chan--???
                track.add(time && "Meta 0x20 "&chan)
                p=p+4
                
              33: --0x21: -- ChannelPrefixOrPort
                chan = ord(binStr.char[p+3])
                if (chan<10) then chan = "0"&chan--???
                track.add(time && "Meta 0x21 "&chan)
                p=p+4
                
              47: --0x2F: -- Meta TrkEnd
                track.add(time && "Meta TrkEnd")
                return track--ignore rest
                --p+=3
                
              81: --0x51: -- Tempo
                tempo = ord(binStr.char[p+3])*256*256 + ord(binStr.char[p+4])*256 + ord(binStr.char[p+5])
                track.add(time && "Tempo "&tempo)
                if (tn=0  AND  time=0) then
                  me.pTempo = tempo-- ???
                  me.pTempoMsgNum = count(track) - 1
                end if
                p=p+6
                
              84: --0x54: -- SMPTE offset
                h = ord(binStr.char[p+3])
                m = ord(binStr.char[p+4])
                s = ord(binStr.char[p+5])
                f = ord(binStr.char[p+6])
                fh = ord(binStr.char[p+7])
                track.add(time && "SMPTE"&&h&&m&&s&&f&&fh)
                p=p+8
                
              88: --0x58: -- TimeSig
                z = ord(binStr.char[p+3])
                t = integer(power(2,ord(binStr.char[p+4])))
                mc = ord(binStr.char[p+5])
                c = ord(binStr.char[p+6])
                track.add(time && "TimeSig "&z&"/"&t&&mc&&c)
                p=p+7
                
              89: --0x59: -- KeySig
                vz = ord(binStr.char[p+3])
                g = ["major","minor"][2-(ord(binStr.char[p+4])=0)]
                track.add(time && "KeySig" && vz && g)
                p=p+5
                
              127: --0x7F: -- Sequencer specific data (string or hexString???)
                len = ord(binStr.char[p+2])
                
                data=""
                repeat with i = p+3 to p+3+len-1
                  data=data& " "&int2hex(ord(binStr.char[i]))
                end repeat
                track.add(time && "SeqSpec"& data)
                p=p+len+3
                
              otherwise:
                me._err(">>> unknown meta event:" && time && byte && meta)
            end case -- Ende Meta
            
          240: --0xF0: -- SysEx
            len = ord(binStr.char[p+1])
            str = "f0"
            repeat with i=1 to len
              str=str && strtolower(int2hex(ord(binStr.char[p+2+i])))
            end repeat
            track.add(time && "SysEx "&str)
            p=p+len+2
            
          otherwise: --Repetition of last event?
            case (last) of
              "On","Off":
                note = ord(binStr.char[p])
                vel = ord(binStr.char[p+1])
                track.add(time && last && "ch="&chan&" n="¬e&" v="&vel)
                p=p+2
                
              "PoPr":
                note = ord(binStr.char[p+1])
                val = ord(binStr.char[p+2])
                track.add(time && "PoPr ch="&chan&" n="¬e&" v="&val)
                p=p+2
                
              "ChPr":
                val = ord(binStr.char[p])
                track.add(time && "ChPr ch="&chan&" v="&val)
                p=p+1
                
              "Par":
                c = ord(binStr.char[p])
                val = ord(binStr.char[p+1])
                track.add(time && "Par ch="&chan&" c="&c&" v="&val)
                p=p+2
                
              "Pb":
                val = ord(binStr.char[p])+ ord(binStr.char[p+1])*128
                track.add(time && "Pb ch="&chan&" v="&val)
                p=p+2
                
                --test
              "PrCh": --0x0C: --PrCh = ProgramChange
                prog = ord(binStr.char[p])
                track.add(time && "PrCh ch="&chan&" p="&prog)
                p=p+1
                
              otherwise:
                me._err(">>> unknown repetition:" && last) --???
            end case
        end case
    end case
  end repeat
  return track
end

-----------------------------------------------------------------
-- search track 0 for set tempo msg
-----------------------------------------------------------------
on _findTempo (me)
  track = me.pTracks[1]
  mc = count(track)
  repeat with i=1 to mc
    msg = explode(" ",track[i])
    if (integer(msg[1])>0) then exit repeat
    if (msg[2]="Tempo") then
      me.pTempo = msg[3]
      me.pTempoMsgNum = i
      exit repeat
    end if
  end repeat
end


--***************************************************************
-- UTILITIES
--***************************************************************

-----------------------------------------------------------------
-- hexstr to binstr
-----------------------------------------------------------------
on _hex2bin (me,hex_str)
  bin_str=""
  l=length(hex_str)/2-1
  repeat with i = 0 to l
    bin_str=bin_str& chr(hexdec(hex_str.char[i*2+1..i*2+2]))
  end repeat
  return bin_str
end

-----------------------------------------------------------------
-- int to bytes (length len)
-----------------------------------------------------------------
on _getBytes (me,n,len)
  str=""
  repeat with i = len-1 down to 0
    str=str& chr(floor(n/power(256,i)))
  end repeat
  return str
end

-----------------------------------------------------------------
-- variable length string to int (+repositioning)
-----------------------------------------------------------------
on _readVarLen (me,str,posArray) --&pos !!!
  pos=posArray[1]
  value=ord(str.char[pos])
  pos=pos +1
  if bitAnd(value,128) then
    value=bitAnd(value,127)
    repeat while TRUE
      c = ord(str.char[pos])
      pos=pos +1
      value = value*128 + bitAnd(c,127)
      if NOT bitAnd(c,128) then exit repeat
    end repeat
  end if
  posArray[1]=pos
  return (value)
end

-----------------------------------------------------------------
-- int to variable length string
-----------------------------------------------------------------
on _writeVarLen (me,value)
  buf = bitAnd(value,127)
  str=""
  repeat while (value/128>0)
    value=value/128
    buf=buf*256
    buf = bitOr(buf, bitOr(bitAnd(value,127),128))
  end repeat
  
  repeat while TRUE
    str=str& chr(buf mod 256)
    if bitAnd(buf,128) then buf=buf/256
    else exit repeat  
  end repeat
  return str
end

-----------------------------------------------------------------
-- converts all delta times in track to absolute times
-----------------------------------------------------------------
on _delta2Absolute (me,track)
  mc = count(track)
  last = 0
  repeat with i=1 to mc
    msg=track[i]
    t=last+integer(msg.word[1])
    delete word 1 of msg
    track[i]=t && msg
    last=t
  end repeat
  return track
end

-----------------------------------------------------------------
-- error message
-----------------------------------------------------------------
on _err (me,str)
  --die(str)
  cursor(-1)
  alert(str)  
  abort()
end

 


Contact

MMI
36 South Court Sq
Suite 300
Newnan, GA 30263
USA

Send e-mail