Advertisement
glitchdetector

MIDI Parser

Oct 10th, 2018
231
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 7.35 KB | None | 0 0
  1. -- Usage:
  2. -- midi = midiParser( filePath )
  3.  
  4. local midiParser = function( _path )
  5.  
  6.   local ret = {}
  7.  
  8.   ------------------
  9.   -- prepare file --
  10.   ------------------
  11.  
  12.   if _path == nil then
  13.     error( 'path is not defined' )
  14.   end
  15.  
  16.   local file = io.open( _path, 'rb' )
  17.   if file == nil then
  18.         error( 'not found: ' .. _path )
  19.     end
  20.  
  21.   local midi = file:read( '*all' )
  22.   midi = string.gsub( midi, '\r\n', '\n' )
  23.  
  24.   --------------------
  25.   -- some functions --
  26.   --------------------
  27.  
  28.   local byteArray = function( _start, _length )
  29.     local retArray = {}
  30.     for i = 1, _length do
  31.       retArray[ i ] = string.byte( midi, i + _start - 1 )
  32.     end
  33.     return retArray
  34.   end
  35.  
  36.   local bytesToNumber = function( _start, _length )
  37.     local retNumber = 0
  38.     for i = 1, _length do
  39.       retNumber = retNumber + string.byte( midi, i + _start - 1 ) * math.pow( 256, _length - i )
  40.     end
  41.     return retNumber
  42.   end
  43.  
  44.   local vlq = function( _start ) -- Variable-length quantity
  45.     local retNumber = 0
  46.     local head = 0
  47.     local byte = 0
  48.     repeat
  49.       byte = string.byte( midi, _start + head )
  50.       retNumber = retNumber * 128 + ( byte - math.floor( byte / 128 ) * 128 )
  51.       head = head + 1
  52.     until math.floor( byte / 128 ) ~= 1
  53.     return retNumber, head
  54.   end
  55.  
  56.   local isSameTable = function( _a, _b )
  57.     for i, v in ipairs( _a ) do
  58.       if _a[ i ] ~= _b[ i ] then
  59.         return false
  60.       end
  61.     end
  62.     return true
  63.   end
  64.  
  65.   ------------------
  66.   -- check format --
  67.   ------------------
  68.  
  69.   local head = 1
  70.  
  71.   if not isSameTable( byteArray( head, 4 ), { 77, 84, 104, 100 } ) then
  72.     error( 'input file seems not to be a .mid file' )
  73.   end
  74.   head = head + 4 -- header chunk magic number
  75.   head = head + 4 -- header chunk length
  76.  
  77.   ret.format = bytesToNumber( head, 2 )
  78.  
  79.   if not ( ret.format == 0 or ret.format == 1 ) then
  80.     error( 'not supported such format of .mid' )
  81.   end
  82.   head = head + 2 -- format
  83.  
  84.   head = head + 2 -- trackCount
  85.  
  86.   ret.timebase = bytesToNumber( head, 2 )
  87.   head = head + 2 -- timeBase
  88.  
  89.   ------------------------
  90.   -- fight against .mid --
  91.   ------------------------
  92.  
  93.   ret.tracks = {}
  94.  
  95.   while head < string.len( midi ) do
  96.  
  97.     if not isSameTable( byteArray( head, 4 ), { 77, 84, 114, 107 } ) then -- if chunk is not track chunk
  98.  
  99.       head = head + 4 -- unknown chunk magic number
  100.       head = head + 4 + bytesToNumber( head, 4 ) -- chunk length + chunk data
  101.  
  102.     else
  103.  
  104.       head = head + 4 -- track chunk magic number
  105.  
  106.       local chunkLength = bytesToNumber( head, 4 )
  107.       head = head + 4 -- chunk length
  108.       local chunkStart = head
  109.  
  110.       local track = {}
  111.       track.messages = {}
  112.       table.insert( ret.tracks, track )
  113.  
  114.       local status = 0
  115.       while head < chunkStart + chunkLength do
  116.  
  117.         local deltaTime, deltaHead = vlq( head ) -- timing
  118.         head = head + deltaHead
  119.  
  120.         local tempStatus = byteArray( head, 1 )[ 1 ]
  121.  
  122.         if math.floor( tempStatus / 128 ) == 1 then -- event, running status
  123.           head = head + 1
  124.           status = tempStatus
  125.         end
  126.  
  127.         local type = math.floor( status / 16 )
  128.         local channel = status - type * 16
  129.  
  130.         if type == 8 then -- note off
  131.           local data = byteArray( head, 2 )
  132.           head = head + 2
  133.  
  134.           table.insert( track.messages, {
  135.             time = deltaTime,
  136.             type = 'off',
  137.             channel = channel,
  138.             number = data[ 1 ],
  139.             velocity = data[ 2 ]
  140.           } )
  141.  
  142.         elseif type == 9 then -- note on
  143.           local data = byteArray( head, 2 )
  144.           head = head + 2
  145.  
  146.           table.insert( track.messages, {
  147.             time = deltaTime,
  148.             type = 'on',
  149.             channel = channel,
  150.             number = data[ 1 ],
  151.             velocity = data[ 2 ]
  152.           } )
  153.  
  154.         elseif type == 10 then -- polyphonic keypressure
  155.           head = head + 2
  156.  
  157.         elseif type == 11 then -- control change
  158.           head = head + 2
  159.  
  160.         elseif type == 12 then -- program change
  161.           head = head + 1
  162.  
  163.         elseif type == 13 then -- channel pressure
  164.           head = head + 1
  165.  
  166.         elseif type == 14 then -- pitch bend
  167.           head = head + 2
  168.  
  169.         elseif status == 255 then -- meta event
  170.           local metaType = byteArray( head, 1 )[ 1 ]
  171.           head = head + 1
  172.           local metaLength, metaHead = vlq( head )
  173.  
  174.           if metaType == 3 then -- track name
  175.             head = head + metaHead
  176.             track.name = string.sub( midi, head, head + metaLength - 1 )
  177.             head = head + metaLength
  178.  
  179.             table.insert( track.messages, {
  180.               time = deltaTime,
  181.               type = 'meta',
  182.               meta = 'Track Name',
  183.               text = track.name
  184.             } )
  185.  
  186.           elseif metaType == 4 then -- instrument name
  187.             head = head + metaHead
  188.             track.instrument = string.sub( midi, head, head + metaLength - 1 )
  189.             head = head + metaLength
  190.  
  191.             table.insert( track.messages, {
  192.               time = deltaTime,
  193.               type = 'meta',
  194.               meta = 'Instrument Name',
  195.               text = track.instrument
  196.             } )
  197.  
  198.           elseif metaType == 5 then -- lyric
  199.             head = head + metaHead
  200.             track.lyric = string.sub( midi, head, head + metaLength - 1 )
  201.             head = head + metaLength
  202.  
  203.             table.insert( track.messages, {
  204.               time = deltaTime,
  205.               type = 'meta',
  206.               meta = 'Lyric',
  207.               text = track.lyric
  208.             } )
  209.  
  210.           elseif metaType == 47 then -- end of track
  211.             head = head + 1
  212.  
  213.             table.insert( track.messages, {
  214.               time = deltaTime,
  215.               type = 'meta',
  216.               meta = 'End of Track'
  217.             } )
  218.  
  219.             break
  220.  
  221.           elseif metaType == 81 then -- tempo
  222.             head = head + 1
  223.  
  224.             local micros = bytesToNumber( head, 3 )
  225.             head = head + 3
  226.  
  227.             table.insert( track.messages, {
  228.               time = deltaTime,
  229.               type = 'meta',
  230.               meta = 'Set Tempo',
  231.               tempo = micros
  232.             } )
  233.  
  234.           elseif metaType == 88 then -- time signature
  235.             head = head + 1
  236.  
  237.             local sig = byteArray( head, 4 )
  238.             head = head + 4
  239.  
  240.             table.insert( track.messages, {
  241.               time = deltaTime,
  242.               type = 'meta',
  243.               meta = 'Time Signature',
  244.               signature = sig
  245.             } )
  246.  
  247.           elseif metaType == 89 then -- key signature
  248.             head = head + 1
  249.  
  250.             local sig = byteArray( head, 2 )
  251.             head = head + 2
  252.  
  253.             table.insert( track.messages, {
  254.               time = deltaTime,
  255.               type = 'meta',
  256.               meta = 'Key Signature',
  257.               signature = sig
  258.             } )
  259.  
  260.           else -- comment
  261.             head = head + metaHead
  262.             local text = string.sub( midi, head, head + metaLength - 1 )
  263.             head = head + metaLength
  264.  
  265.             table.insert( track.messages, {
  266.               time = deltaTime,
  267.               type = 'meta',
  268.               meta = 'Unknown Text: ' .. event[ 2 ],
  269.               text = text
  270.             } )
  271.  
  272.           end
  273.  
  274.         end
  275.  
  276.       end
  277.  
  278.     end
  279.  
  280.   end
  281.  
  282.   return ret
  283.  
  284. end
  285.  
  286. return midiParser
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement