Technical Note of "Split/Merge AudioVisual" 3
Artist: Kenji Kojima Technical Note 1, 2, 3   This note is under CC License BY-NC-SA 3.0

index@kenjikojima.com


Please consider making a donation to support Kenji Kojima's works
Converting from a midi file to musical notes
1) Binary midi file is converted to human readable midi data.
2) Musical notes of a midi data is converted to numbers.
We take these processes. We have "music1" midi file on the desktop. We put midi data from the file into a variable "midData".

	
put URL ("binfile:" & specialFolderPath("Desktop") & "/music1.mid" ) into midData

But we cannot read the data, then have to convert it to human readable midi data. We use function "smf2Slg (see the last part of this page).


put smf2Slg(midData, true ) into slg  
  -- variable "slg" contains human readable midi data. 
  
  
----------// the result of slg (human readable midi data) //---------------
%MIDIFILE
@MThd
@FORMAT 0
@TIMEBASE 61440
@END
;---------------------------------------
@MTrk 1
001|1|000 @TEMPO 0
001|1|000  1:PROG  0    
001|1|000  1:A#4   127     0|060 
001|1|060  1:F-1   127     0|120 
001|1|180  1:C8    127     0|120 
001|1|300  1:B6    127     0|060 
001|1|360  1:F#2   127     0|060 
001|1|420  1:C#4   127     0|120 
001|1|540  1:B8    127     0|060 
001|1|600  1:G#7   127     0|120 
001|1|720  1:A6    127     0|060 
001|1|780  1:G#4   127     0|060 
001|1|840  1:E3    127     0|120 
001|1|960  1:A1    127     0|060 
001|1|0001020  1:D#9   127     0|060 
001|1|0001080  1:G1    127     0|120 
001|1|0001200  1:C#8   127     0|120 
001|1|0001320  1:G#7   127     0|060 
001|1|0001380  1:F#5   127     0|060 
001|1|0001440  1:A#7   127     0|060 
001|1|0001500  1:A#7   127     0|120 
001|1|0001620  1:C9    127     0|060 
001|1|0001680  1:B3    127     0|060 
001|1|0001740  1:A#5   127     0|120 
001|1|0001860  1:A8    127     0|120 
001|1|0001980  1:A#8   127     0|060 
001|1|0002040  1:E9    127     0|120 
001|1|0002160  1:C2    127     0|120 
001|1|0002280  1:D4    127     0|120 
001|1|0002400  1:D#7   127     0|060 
001|1|0002460  1:G8    127     0|060 
001|1|0002520  1:B0    127     0|060 
001|1|0002580  1:F0    127     0|060 
001|1|0002640  1:F#7   127     0|060 
001|1|0002700  1:F#8   127     0|120 
001|1|0002820  1:E6    127     0|060 
001|1|0002880  1:A3    127     0|060 
001|1|0002940  1:F#2   127     0|120 
001|1|0003060  1:A2    127     0|060 
001|1|0003120  1:F5    127     0|120 
001|1|0003240  1:G0    127     0|120 
001|1|0003360  1:D#3   127     0|060 
001|1|0003420  1:G#-1  127     0|060 
001|1|0003480  1:F6    127     0|120 
001|1|0003600  1:C6    127     0|060 
001|1|0003660  1:E2    127     0|120 
001|1|0003780  1:D#0   127     0|120 
001|1|0003900  1:G3    127     0|060 
001|1|0003960  1:B2    127     0|120 
001|1|0004080  1:F5    127     0|120 
@END

--------------// END of the result //----------------

Back to musical note numbers
A musical note starts from line 10 "001|1|000 1:A#4 127 0|060 " to 1 line before "@END". The word 2 of line 10 "1:A#4" is the note is "in A sharp 4" that means 2 octaves under "A sharp 6". C4 is the middle C and the musical number is 60. The last word of line 10 "0|060" is an eighth note that is an even number. The last word of line 11 "0|120" is a quarter note that is an odd number.

We need a global noteArray and functions "noteCharToNoteNum" and "noteToColumnNumber" converting from a musical note character to a musical note number.

global noteArray
command createNoteArray
   put "" into noteArray
      repeat with i= 0 to 127
      if i mod 12 = 11 then
         put i &cr after NoteArray
      else
         put i & tab after noteArray
      end if
   end repeat
   split noteArray by row
end createNoteArray

function noteCharToNoteNum pNoteChar
   if char 2 of pNoteChar is "#" then
      put char 1 to 2 of pNoteChar into tNoteName
      delete char 1 to 2 of pNoteChar
   else
      put char 1 of pNoteChar into tNoteName
      delete char 1 of pNoteChar
   end if
   put pNoteChar +2 into tRowNum
   put noteArray[tRowNum] into tRowList
   set the itemDel to tab
   return item noteToColumnNumber(tNoteName) of tRowList 
end noteCharToNoteNum

function noteToColumnNumber pNoteName
  switch pNoteName
  case "C"
    return 1
    break
  case "C#"
    return 2
    break
  case "D"
    return 3
    break
  case "D#"
    return 4
    break
  case "E"
    return 5
    break
  case "F"
    return 6
    break
  case "F#"
    return 7
    break
  case "G"
    return 8
    break
  case "G#"
    return 9
    break
  case "A"
    return 10
    break
  case "A#"
    return 11
    break
  case "B"
    return 12
    break
  end switch
end noteToColumnNumber

You can get a musical number from a musical note character. We have to two times of them and distinguish the note length eighth or quarter for RGB value.
put noteCharToNoteNum("A#4")   -- returns 70 
put noteCharToNoteNum("F-1")   -- returns 5

function eighthOrQuarter pNoteNum, pLength   -- pNoteNum is noteCharToNoteNum()
  switch pLength
  case "0|060"  -- even 
    return pNoteNum * 2
    break
  case "0|120"  -- odd
    return pNoteNum * 2 + 1
    break
  end switch
end eighthOrQuarter


function midDataToNoteNum slg
   repeat for each line tLine in slg
      put word -1 of tLine into tNoteLength
      if char 1 to 2 of tNoteLength is "0|" then
         put char 3 to -1 of word 2 of tLine into tNoteChar
         put noteCharToNoteNum(tNoteChar) into noteNum
         put eighthOrQuarter(noteNum, tNoteLength) & space after RGBValue
      end if
   end repeat
   return RGBValue
end midDataToNoteNum

-- you can get RGB values from slg
-- slg is smf2Slg(midData, true)
put midDataToNoteNum(slg)

-- it returns 
140 11 217 190 84 123 238 209 186 136 105 66 246 63 219 208 156 212 213 240 118 
165 235 236 249 73 125 198 230 46 34 204 229 176 114 85 90 155 39 102 16 179 168 
81 31 110 95 155 



Function smf2Slg
You should copy and paste under here onto a card script editor.
----------------// function smf2Slg by UDI //----------------
--smf2Slg midData, conciseList
--  midData: smf binary data
--  conciseList: true(omit some controls) / false(full convert)
--return slg text data

local lMidData

function smf2Slg midData, conciseList 
   put midData into lMidData
   put "" into slg
   put ( conciseList is true ) into conciseList
   -- header
   put "%MIDIFILE" &return& "@MThd" &return after slg
   delete char 1 to 8 of lMidData -- 'MThd' & headerSize(4)
   get binaryDecode( "S", lMidData, aNum )
   if aNum <> 0 then
      put "@FORMAT 1" &return after slg
   else
      put "@FORMAT 0" &return after slg
   end if
   delete char 1 to 4 of lMidData -- format(2) & trackCount(2)
   get binaryDecode( "S", lMidData, timeBase )
   delete char 1 to 2 of lMidData
   put "@TIMEBASE" && timeBase &return after slg
   put "@END" &return after slg
   --
   put 0 into trackNum
   put 0 into absT
   put 4 into beatA
   put 4 into beatB
   put timeBase into oneBeat
   put oneBeat * beatA into barBeat
   --
 
   repeat -- track
      set the cursor to watch
      if lMidData is "" then exit repeat
      put 0 into absT
      put "" into aSlg
      add 1 to trackNum
      put ";---------------------------------------" &return after aSlg
      put "@MTrk" && trackNum &return after aSlg
      delete char 1 to 8 of lMidData -- 'MTrk' & trackSize(4)
      put 0 into cursorCount
            
      repeat -- event
         if lMidData is "" then exit repeat
         
         if the short name of the target <> "mergeAudioVisual" then
            add 1 to cursorCount
            if ( cursorCount = 40 ) then
               set the cursor to busy
               put 0 into cursorCount
            end if
         end if
         
         add getDeltaTime() to absT
         --
         put aByte into oldByte
         get binaryDecode( "C", lMidData, aByte )
         
         if ( aByte >= 128 ) then
            put ( aByte div 16 ) into aEvnt
            put ( aByte mod 16 ) +1 into aPart
            delete first char of lMidData
         else -- running status
            put oldByte into aByte
         end if
         --
         if ( aEvnt = 9 ) then -- $9x note on --
            get binaryDecode( "CC", lMidData, aPitch, aVal )
            delete char 1 to 2 of lMidData
            --
            if ( aVal > 0 ) then
               put absT2barT( absT, oneBeat, barBeat ) after aSlg
               put rightFormatStr( aPart, 2, "  " ) & ":" after aSlg
               put leftFormatStr( pitch2Name( aPitch ), 6, "      " ) after aSlg
               put leftFormatStr( aVal, 8, "        " ) after aSlg
               put "_" & aPitch &","& absT & return after aSlg
            else -- note off
               put lineOffset( ( "_" & aPitch ), aSlg ) into lineNum --2002.12.10
               put item 2 of ( last word of line lineNum of aSlg ) into onTime
               put absT2barT2( absT - onTime, oneBeat, barBeat ) into last word of line lineNum of aSlg
            end if
            next repeat
         end if
         if ( aEvnt = 8 ) then -- $8x note off --
            get binaryDecode( "C", lMidData, aPitch )
            delete char 1 to 2 of lMidData -- pitch(1) & offVelocity(1)
            --
            put lineOffset( ( "_" & aPitch ), aSlg ) into lineNum --2002.12.10
            put item 2 of ( last word of line lineNum of aSlg ) into onTime
            put absT2barT2( absT - onTime, oneBeat, barBeat ) into last word of line lineNum of aSlg
            next repeat
         end if
         if ( aEvnt = 10 ) then -- $Ax Poly Key Pressure --
            if ( conciseList ) then
               delete char 1 to 2 of lMidData
            else
               get binaryDecode( "CC", lMidData, aCtrlN, aVal )
               delete char 1 to 2 of lMidData
               --
               put absT2barT( absT, oneBeat, barBeat ) after aSlg
               put rightFormatStr( aPart, 2, "  " ) & ":POLY  " after aSlg
               put leftFormatStr( aCtrlN, 6, "      " ) after aSlg
               put leftFormatStr( aVal, 3, "   " ) &return after aSlg
            end if
            next repeat
         end if
         if ( aEvnt = 11 ) then -- $Bx control --
            if ( conciseList ) then
               delete char 1 to 2 of lMidData
            else
               get binaryDecode( "CC", lMidData, aCtrlN, aVal )
               delete char 1 to 2 of lMidData
               --
               if ( aCtrlN < 120 ) then
                  put absT2barT( absT, oneBeat, barBeat ) after aSlg
                  put rightFormatStr( aPart, 2, "  " ) & ":CONT  " after aSlg
                  put leftFormatStr( aCtrlN, 6, "      " ) after aSlg
                  put leftFormatStr( aVal, 3, "   " ) &return after aSlg
               end if
            end if
            next repeat
         end if
         if ( aEvnt = 12 ) then -- $Cx program change --
            get binaryDecode( "C", lMidData, aVal )
            delete first char of lMidData
            --
            put absT2barT( absT, oneBeat, barBeat ) after aSlg
            put rightFormatStr( aPart, 2, "  " ) & ":PROG  " after aSlg
            put leftFormatStr( aVal, 5, "     " ) & return after aSlg
            next repeat
         end if
         if ( aEvnt = 13 ) then -- $Dx Mono Key Pressurer --
            if ( conciseList ) then
               delete first char of lMidData
            else
               get binaryDecode( "C", lMidData, aVal )
               delete first char of lMidData
               --
               put absT2barT( absT, oneBeat, barBeat ) after aSlg
               put rightFormatStr( aPart, 2, "  " ) & ":MONO  " after aSlg
               put leftFormatStr( aVal, 5, "     " ) & return after aSlg
            end if
            next repeat
         end if
         if ( aEvnt = 14 ) then -- $Ex pitchbend --
            if ( conciseList ) then
               delete char 1 to 2 of lMidData
            else
               get binaryDecode( "CC", lMidData, aNum1, aNum2 )
               delete char 1 to 2 of lMidData
               --
               put absT2barT( absT, oneBeat, barBeat ) after aSlg
               put rightFormatStr( aPart, 2, "  " ) & ":PITCH " after aSlg
               put leftFormatStr( ( aNum2 *128 + aNum1 ) - 8192, 5, "     " ) & return after aSlg
            end if
            next repeat
         end if
         if ( aByte = 255 ) then -- $FF meta event --
            get binaryDecode( "C", lMidData, metaNum )
            delete first char of lMidData
            if ( metaNum > 0 ) and ( metaNum < 32 ) then -- $FF01-FF1F TextEvent --
               get item metaNum of "TXT,COPYRIGHT,SEQ_NAME,INSTRUMENT,LYRIC,MARKER,CUE"
               put absT2barT( absT, oneBeat, barBeat ) & getTextEvent( "@"&it&" " ) &return after aSlg
               next repeat
            end if
            if ( metaNum = 81 ) then -- $FF51 tempo --
               delete first char of lMidData --len
               put char 1 to 3 of lMidData into aStr
               delete char 1 to 3 of lMidData
               --
               put absT2barT( absT, oneBeat, barBeat ) after aSlg
               get binaryDecode( "I", numToChar(0) & aStr, aNum )
               put "@TEMPO " & ( 60000000 div aNum ) & return after aSlg
               next repeat
            end if
            if ( metaNum = 88 ) then -- $FF58 timesign --
               delete first char of lMidData --len
               get binaryDecode( "CC", lMidData, beatA, beatB )
               delete char 1 to 4 of lMidData -- sign(2) & clock(2)
               put 2^beatB into beatB
               --
               put 4 * timeBase div beatB into oneBeat
               put oneBeat * beatA into barBeat
               --
               put absT2barT( absT, oneBeat, barBeat ) after aSlg
               put "@BEAT " & beatA &"/" & beatB &return after aSlg
               next repeat
            end if
            if ( metaNum = 47 ) then -- $FF2F end of track --
               delete first char of lMidData --len
               --
               put "@END" &return after aSlg
               exit repeat
            end if
            if ( conciseList is FALSE ) then
               if ( metaNum = 0 ) then -- $FF00 seq no. --
                  delete first char of lMidData --len
                  put char 1 to 2 of lMidData into aStr
                  delete char 1 to 2 of lMidData
                  --
                  put absT2barT( absT, oneBeat, barBeat ) after aSlg
                  get binaryDecode( "S", aStr, aNum )
                  put "@SEQ_NO " & aNum & return after aSlg
                  next repeat
               end if
               if ( metaNum = 84 ) then -- $FF54 SMPTE --
                  delete first char of lMidData --len
                  put absT2barT( absT, oneBeat, barBeat ) after aSlg
                  put "@SMPTE" after aSlg
                  repeat 5
                     get binaryDecode( "C", lMidData, aNum )
                     put " " & aNum after aSlg
                     delete first char of lMidData
                  end repeat
                  put return after aSlg
                  next repeat
               end if
               if ( metaNum = 89 ) then -- $FF59 keysign --
                  delete first char of lMidData --len
                  get binaryDecode( "CC", lMidData, sf, mi )
                  delete char 1 to 2 of lMidData
                  --
                  put absT2barT( absT, oneBeat, barBeat ) after aSlg
                  put "@KEY " after aSlg
                  if mi = 0 then
                     if sf >= 0 then
                        put ( item sf+1 of "C,G,D,A,E,B,F" ) & " Major" after aSlg
                     else
                        put ( item abs(sf)+1 of "C,F,B,E,A,D,G" )  & " Major" after aSlg
                     end if
                  else
                     if sf >= 0 then
                        put ( item sf+1 of "A,E,B,F,C,G,D" ) & " Minor" after aSlg
                     else
                        put ( item abs(sf)+1 of "A,D,G,C,F,B,E" )  & " Minor" after aSlg
                     end if
                  end if
                  put return after aSlg
                  next repeat
               end if
               if ( metaNum = 32 ) then -- $FF20 channel prefix --
                  delete first char of lMidData -- len
                  get binaryDecode( "C", lMidData, aNum )
                  delete first char of lMidData
                  --
                  put absT2barT( absT, oneBeat, barBeat ) after aSlg
                  put "@CH_PREFIX " & aNum & return after aSlg
                  next repeat
               end if
               if ( metaNum = 33 ) then -- $FF21 MIDI port --
                  delete first char of lMidData --len
                  get binaryDecode( "C", lMidData, aNum )
                  delete first char of lMidData
                  --
                  put absT2barT( absT, oneBeat, barBeat ) after aSlg
                  put "@MIDI_PORT " & aNum & return after aSlg
                  next repeat
               end if
            end if --conciseList is FALSE
            -- other meta event
            get binaryDecode( "C", lMidData, aLen )
            delete char 1 to aLen +1 of lMidData
            next repeat
         end if --aByte = 255/ $FF meta event --
         if ( aByte = 240 ) then -- $F0 Sytem exclusive --
            put absT2barT( absT, oneBeat, barBeat ) & "@F0 " & getSysEx() & "@EE" &return after aSlg
            next repeat
         end if
         if ( aByte = 247 ) then -- $F7 Sytem exclusive --
            put absT2barT( absT, oneBeat, barBeat ) & "@F7 " & getSysEx() & "@EE" &return after aSlg
            next repeat
         end if
      end repeat
      put aSlg after slg
      
   end repeat
   delete last char of slg
   return slg
end smf2Slg

function getTextEvent tagStr
  get binaryDecode( "C", lMidData, aLen )
  delete first char of lMidData
  put char 1 to aLen of lMidData into aStr
  delete char 1 to aLen of lMidData
  --
  if last char of aStr is numToChar(0) then delete last char of aStr
  if last char of aStr is numToChar(0) then delete last char of aStr
  return tagStr "e& aStr "e
end getTextEvent

function getSysEx
  get binaryDecode( "C", lMidData, aLen )
  delete first char of lMidData
  put char 1 to aLen of lMidData into tgData
  delete char 1 to aLen of lMidData
  put "" into aStr
  put "" into aHex
  repeat for each char aChar in tgData
    get binaryDecode( "H2", aChar, aHex )
    put aHex & " " after aStr
  end repeat
  return aStr
end getSysEx

function getDeltaTime
   put 0 into aSum
   repeat
      get binaryDecode( "C", lMidData, aNum )
      delete first char of lMidData
      if aNum > 127 then
         put aSum *128 + ( aNum - 128 ) into aSum
      else
         return aSum *128 + aNum
      end if
   end repeat
end getDeltaTime

function absT2barT absT, oneBeat, barBeat
  put rightFormatStr( absT div barBeat +1, 3, "000" ) & "|" into barT
  put ( absT mod barBeat ) div oneBeat +1 & "|" after barT
  put rightFormatStr( ( absT mod oneBeat ), 3, "000" ) & " " after barT
  return barT
end absT2barT

function absT2barT2 absT, oneBeat, barBeat
  put absT div oneBeat & "|" into barT
  put rightFormatStr( ( absT mod oneBeat ), 3, "000" ) & " " after barT
  return barT
end absT2barT2

function rightFormatStr tgData, nDigit, fillChars
  get nDigit - length( tgData )
  return ( char 1 to it of fillChars ) & tgData
end rightFormatStr

function leftFormatStr tgData, nDigit, fillChars
  return char 1 to nDigit of ( tgData & fillChars )
end leftFormatStr

function pitch2Name aPitch
  put item ( aPitch mod 12 ) +1 of "C,C#,D,D#,E,F,F#,G,G#,A,A#,B" into aName
  return aName & ( aPitch div 12 ) -1
end pitch2Name

----------------// end smf2Slg //----------------


----------------// slg2Pmd //----------------
--slg2Pmd slgData
--  slgData: 'SLG' text format midi events for SC.EXE
--return pmd text data

local lSlgPmdTempo, lSlgPmdInst, lSlgPmdTimeSign, lSlgPmdCopyStr, lSlgPmdSeqName, lSlgPmdInfoStr
local lSlgPmdBarTime, lSlgPmdBase, lSlgPmdWTime

function slg2Pmd slg
   set the cursor to watch
   --
   getMusicInfo slg  -- lSlgPmdBase, lSlgPmdTempo, lSlgPmdBarTime, lSlgPmdTimeSign
   put "//pmd,1.0," & lSlgPmdTempo & return into notes
   put "//s " & lSlgPmdTimeSign & return after notes
   getTextInfo slg -- lSlgPmdCopyStr, lSlgPmdSeqName, lSlgPmdInfoStr
   if ( lSlgPmdCopyStr <> "" ) then
      put "//c " & lSlgPmdCopyStr & return after notes
   end if
   if ( lSlgPmdSeqName <> "" ) then
      put "//n " & lSlgPmdSeqName & return after notes
   end if
   if ( lSlgPmdInfoStr <> "" ) then
      repeat for each line theLine in lSlgPmdInfoStr
         put "//i " & theLine & return after notes
      end repeat
   end if
   put return after notes
   --
   put UxGetFoundLines( slg, ":", "S" ) into slg
   --
   put UxGetFoundLines( slg, "PITCH", "S" , "", false ) into slg
   put UxGetFoundLines( slg, "MONO", "S" , "", false ) into slg
   put UxGetFoundLines( slg, ":CONT  0", "S" ) into bankLines
   put UxGetFoundLines( slg, "CONT", "S" , "", false ) into slg
   sort lines of slg
   --
   put 1 into pMin
   put 16 into pMax
   repeat with aPart = pMin to pMax
      set the cursor to watch
      put getBank( bankLines, aPart ) into bankOfs
      put pickPartData( slg, aPart ) into slg1 --lSlgPmdInst & except "PROG"
      if ( slg1 = "" ) then
         next repeat
      end if
      add bankOfs to lSlgPmdInst
      put "$" & lSlgPmdInst & "V8" && "// (" & aPart & ") " into pInfo
      --    put "$" & lSlgPmdInst & "V8" && "// (" & aPart & ") " & UxGetInst( lSlgPmdInst ) into pInfo
      put pInfo & slg2play( slg1 ) &return after notes
      
   end repeat
   
   return notes
end slg2Pmd

function slg2play slg
  put false into inChode
  put "" into pitchName
  put "" into stepName
  put 0 into currBar
  put lSlgPmdBarTime into nextTop
  replace "|" with "," in slg --RR
  replace ":" with "," in slg --RR
  put ( ( item 1 of slg ) -1 ) *lSlgPmdBarTime + ( ( item 2 of slg ) -1 ) *lSlgPmdBase + ( item 3 of ( word 1 of slg ) ) into nextTime
  put ( item 1 of ( last line of slg ) )+0 into maxBar
  --
  repeat
    put line 1 of slg into aLine
    --
    put nextTime into currTime
    put ( ( item 1 of ( line 2 of slg ) ) -1 ) *lSlgPmdBarTime + ( ( item 2 of ( line 2 of slg ) ) -1 ) *lSlgPmdBase + ( item 3 of ( word 1 of ( line 2 of slg ) ) ) into nextTime
    --
    put currBar into oldBar
    put ( item 1 of aLine )+0 into currBar
    if ( currBar <> oldBar ) then
      put ( currBar -1 ) *lSlgPmdBarTime into currTop
      put currTop +lSlgPmdBarTime into nextTop
      --
      repeat with b = ( oldBar +1 ) to ( currBar -1 )
        put return & "/" & b && "R" & step2name( lSlgPmdBarTime, lSlgPmdWTime ) after theNotes
      end repeat
      put return & "/" & currBar after theNotes
      if ( currTime > currTop ) then
        put " R" & step2name( currTime - currTop, lSlgPmdWTime ) after theNotes
      end if
      put "*" into oldStepName --reset stepName
    end if
    --
    if ( nextTime < 0 ) then put nextTop into nextTime
    --
    put item 2 of ( word 2 of aLine ) into pitchName --SC
    --
    put min( nextTime - currTime, nextTop - currTime ) into stepTime
    put ( ( item 1 of ( word 4 of aLine ) ) * lSlgPmdBase ) + ( item 2 of ( word 4 of aLine ) ) into gateTime --SC
    --
    if ( nextTime = currTime ) then -- is chode
      if ( inChode is false ) then
        put true into inChode
        put " (" after theNotes
      end if
      put " " & pitchName & gate2Name( gateTime, lSlgPmdWTime ) after theNotes
    else -- not chode
      put step2name( stepTime, lSlgPmdWTime ) into stepName
      put ( gateTime > stepTime ) or ( stepTime > gateTime *1.2 ) into existGap
      if ( inChode ) then
        put false into inChode
        if ( existGap ) then
          put " " & pitchName & gate2Name( gateTime, lSlgPmdWTime ) & " )" after theNotes
        else
          put " " & pitchName & stepName & " )" after theNotes
        end if
        if ( stepName <> oldStepName ) then
          put  stepName after theNotes
        end if
      else -- not inChode
        if ( existGap ) then
          put " ( " & pitchName & gate2Name( gateTime, lSlgPmdWTime ) & " )" after theNotes
        else
          put " " & pitchName after theNotes
        end if
        if ( stepName <> oldStepName ) then
          put stepName after theNotes
        end if
      end if
      put stepName into oldStepName
    end if
    --
    delete line 1 of slg
    if slg is "" then
      exit repeat
    end if
  end repeat
  if ( inChode ) then
    put false into inChode
    put " )" & step2name( stepTime, lSlgPmdWTime ) after theNotes
  end if
  --
  put gateTime - ( nextTop - currTime ) into restTime
  repeat
    if ( restTime > lSlgPmdBarTime ) then
      add 1 to currBar
      put return & "/" & currBar & " R" & gate2name( lSlgPmdBarTime, lSlgPmdWTime ) after theNotes
      subtract lSlgPmdBarTime from restTime
    else
      if restTime > ( lSlgPmdBase /4 ) then
        add 1 to currBar
        put return & "/" & currBar & " R" & gate2name( restTime, lSlgPmdWTime ) after theNotes
      end if
      exit repeat
    end if
  end repeat
  put return & "/ Re" & return after theNotes
  return theNotes
end slg2play

function step2Name stepTime, wTime
  put false into cntne
  get stepTime div wTime
  put "" into theResult
  if ( it > 0 ) then
    put char 1 to it of "wwwwwwww" into theResult
    subtract it * wTime from stepTime
    put true into cntne
  end if
  put wTime div 2 into wTime
  put "hqestxx" into steps
  repeat with x = 1 to 7
    if ( stepTime = wTime *2/3 ) then
      return theResult & char x of steps & "3"
    end if
    if ( stepTime >= wTime ) then
      if cntne then
        put "." after theResult
      else
        put char x of steps after theResult
      end if
      subtract wTime from stepTime
      put true into cntne
    else
      put false into cntne
    end if
    put wTime div 2 into wTime
  end repeat
  --
  if theResult is "" then
    return "x"
  end if
  return theResult
end step2Name

function gate2Name gateTime, wTime
  put "" into whole
  put "" into theResult
  get gateTime div wTime
  if ( it > 1 ) then
    put char 1 to it of "wwwwwwww" into whole
    subtract it * wTime from gateTime
  end if
  --
  put "whqestx" into steps
  repeat with x = 1 to 7
    if ( gateTime *1.2 >= wTime ) then
      put char x of steps after theResult
      subtract wTime from gateTime
      if ( the number of chars of theResult ) = 2 then
        exit repeat
      end if
    end if
    put wTime div 2 into wTime
  end repeat
  --
  if ( theResult is "" ) then
    put "x" into theResult
  end if
  return whole & theResult
end gate2Name

on getMusicInfo slg
  get last line of UxGetFoundLines( slg, "@TIMEBASE", "S" )
  if word 1 of it is "Error" then
    errDialog it
    exit to HyperCard
  end if
  if ( it <> empty ) then put last word of it into timeBase
  else put 480 into timeBase
  --
  get last line of UxGetFoundLines( slg, "@BEAT", "S" )
  if word 1 of it is "Error" then
    errDialog it
    exit to HyperCard
  end if
  put last word of it into lSlgPmdTimeSign
  if ( it <> empty ) then
    replace "/" with "," in it --RR
    put item 1 of ( last word of it ) into beatA
    put item 2 of ( last word of it ) into beatB
  else
    put 4 into beatA
    put 4 into beatB
  end if
  --
  put "" into tempoItems
  get UxGetFoundLines( slg, "@TEMPO", "S" )
  if ( word 1 of it is "Error" ) then
    errDialog it
    exit to HyperCard
  end if
  if ( it <> empty ) then
    put the number of lines of it into maxL
    put 0 into aNum
    repeat with L = 1 to maxL
      add ( last word of line L of it ) to aNum
    end repeat
    put aNum div ( maxL * 10 ) * 10 into lSlgPmdTempo
  else
    put 100 into lSlgPmdTempo
  end if
  --
  put timeBase * 4 div beatB into lSlgPmdBase -- 1 beat time
  put lSlgPmdBase * beatA into lSlgPmdBarTime -- 1 bar time
  put lSlgPmdBase * beatB into lSlgPmdWTime --'W' time (lSlgPmdBase *4 *beatB div4 )
end getMusicInfo

on getTextInfo slg -- lSlgPmdCopyStr, lSlgPmdSeqName, lSlgPmdInfoStr
  put UxGetFoundLines( slg, "@COPYRIGHT", "S" ) into cpLineStr
  if cpLineStr <> "" then put the value of word 3 of cpLineStr into lSlgPmdCopyStr
  else put "" into lSlgPmdCopyStr
  put UxGetFoundLines( slg, "@SEQ_NAME", "S" ) into seqName
  if ( seqName <> "" ) then
    put word 3 of seqName into lSlgPmdSeqName
    try
      put the value of lSlgPmdSeqName into lSlgPmdSeqName
    end try
  else
    put "" into lSlgPmdSeqName
  end if
  put UxGetFoundLines( slg, "@TXT", "S" ) into infoLinesStr
  put "" into lSlgPmdInfoStr
  repeat for each line theLine in infoLinesStr
    put word 3 of theLine into aStr
    try
      put the value of aStr into aStr
    end try
    put aStr & return after lSlgPmdInfoStr
  end repeat
end getTextInfo

function pickPartData slg, aPart
  put UxGetFoundLines( slg, " " & aPart & ":", "S" ) into slg
  if ( slg is "" ) then return ""
  --
  put 1 into lSlgPmdInst
  if ( aPart = 10 ) then
    put 16385 into lSlgPmdInst
  else
    put offset( "PROG", slg ) into aOfs
    if ( aOfs > 0 ) then
      put ( char aOfs +6 to aOfs +9 of slg ) +1 into lSlgPmdInst
    end if
  end if
  return UxGetFoundLines( slg, "PROG", "S" , "", false )
end pickPartData

function getBank slg, aPart
  if ( the number of chars of aPart ) = 1 then
    put " " before aPart
  end if
  put line 1 of UxGetFoundLines( slg, aPart & ":", "S" ) into bankLine
  if bankLine is "" then
    return 0
  end if
  put ( char 26 to 27 of bankLine )+0 into bank
  if bank mod 2 <> 0 then
    add 1 to bank
  end if
  return bank * 128
end getBank


--- XCMD substitute ----

-- UxGetFoundLines( SourceStr, findStr [ , resultForm ] [ , case ] [, condi ] )
-- 2002.12.10
function UxGetFoundLines sourceStr, findStr, resultForm, caseSence, condi
  put "" into returnStr
  if ( resultForm is "s" ) then -- strings result ----------
    if ( condi is NOT false ) then -- exist lines
      repeat for each lines theLine in sourceStr
        if ( theLine contains findStr ) then put ( theLine & return ) after returnStr
      end repeat
    else -- condi=false -- NOT exist lines
      repeat for each line theLine in sourceStr
        if NOT ( theLine contains findStr ) then put ( theLine & return ) after returnStr
      end repeat
    end if
  else -- line number result ----------------------------------------
    if ( condi is NOT false ) then -- exist lines
      put 0 into lineCount
      repeat for each line theLine in sourceStr
        add 1 to lineCount
        if ( theLine contains findStr ) then put ( lineCount & "," ) after returnStr
      end repeat
    else -- condi=false -- NOT exist lines
      put 0 into lineCount
      repeat for each line theLine in sourceStr
        add 1 to lineCount
        if NOT ( theLine contains findStr ) then put ( lineCount & "," ) after returnStr
      end repeat
    end if
  end if
  return returnStr
end UxGetFoundLines


-- UxGetInst( instNumber )
-- Requestment btn "Inst" --
function UxGetInst instNumber
  send ( "return getInstName( " & instNumber & ")" ) to btn "Inst"
  return the result
end UxGetInst

----------------// END slg2Pmd //----------------





Technical Note 1, 2, 3