Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Usage:
- -- midi = midiParser( filePath )
- local midiParser = function( _path )
- local ret = {}
- ------------------
- -- prepare file --
- ------------------
- if _path == nil then
- error( 'path is not defined' )
- end
- local file = io.open( _path, 'rb' )
- if file == nil then
- error( 'not found: ' .. _path )
- end
- local midi = file:read( '*all' )
- midi = string.gsub( midi, '\r\n', '\n' )
- --------------------
- -- some functions --
- --------------------
- local byteArray = function( _start, _length )
- local retArray = {}
- for i = 1, _length do
- retArray[ i ] = string.byte( midi, i + _start - 1 )
- end
- return retArray
- end
- local bytesToNumber = function( _start, _length )
- local retNumber = 0
- for i = 1, _length do
- retNumber = retNumber + string.byte( midi, i + _start - 1 ) * math.pow( 256, _length - i )
- end
- return retNumber
- end
- local vlq = function( _start ) -- Variable-length quantity
- local retNumber = 0
- local head = 0
- local byte = 0
- repeat
- byte = string.byte( midi, _start + head )
- retNumber = retNumber * 128 + ( byte - math.floor( byte / 128 ) * 128 )
- head = head + 1
- until math.floor( byte / 128 ) ~= 1
- return retNumber, head
- end
- local isSameTable = function( _a, _b )
- for i, v in ipairs( _a ) do
- if _a[ i ] ~= _b[ i ] then
- return false
- end
- end
- return true
- end
- ------------------
- -- check format --
- ------------------
- local head = 1
- if not isSameTable( byteArray( head, 4 ), { 77, 84, 104, 100 } ) then
- error( 'input file seems not to be a .mid file' )
- end
- head = head + 4 -- header chunk magic number
- head = head + 4 -- header chunk length
- ret.format = bytesToNumber( head, 2 )
- if not ( ret.format == 0 or ret.format == 1 ) then
- error( 'not supported such format of .mid' )
- end
- head = head + 2 -- format
- head = head + 2 -- trackCount
- ret.timebase = bytesToNumber( head, 2 )
- head = head + 2 -- timeBase
- ------------------------
- -- fight against .mid --
- ------------------------
- ret.tracks = {}
- while head < string.len( midi ) do
- if not isSameTable( byteArray( head, 4 ), { 77, 84, 114, 107 } ) then -- if chunk is not track chunk
- head = head + 4 -- unknown chunk magic number
- head = head + 4 + bytesToNumber( head, 4 ) -- chunk length + chunk data
- else
- head = head + 4 -- track chunk magic number
- local chunkLength = bytesToNumber( head, 4 )
- head = head + 4 -- chunk length
- local chunkStart = head
- local track = {}
- track.messages = {}
- table.insert( ret.tracks, track )
- local status = 0
- while head < chunkStart + chunkLength do
- local deltaTime, deltaHead = vlq( head ) -- timing
- head = head + deltaHead
- local tempStatus = byteArray( head, 1 )[ 1 ]
- if math.floor( tempStatus / 128 ) == 1 then -- event, running status
- head = head + 1
- status = tempStatus
- end
- local type = math.floor( status / 16 )
- local channel = status - type * 16
- if type == 8 then -- note off
- local data = byteArray( head, 2 )
- head = head + 2
- table.insert( track.messages, {
- time = deltaTime,
- type = 'off',
- channel = channel,
- number = data[ 1 ],
- velocity = data[ 2 ]
- } )
- elseif type == 9 then -- note on
- local data = byteArray( head, 2 )
- head = head + 2
- table.insert( track.messages, {
- time = deltaTime,
- type = 'on',
- channel = channel,
- number = data[ 1 ],
- velocity = data[ 2 ]
- } )
- elseif type == 10 then -- polyphonic keypressure
- head = head + 2
- elseif type == 11 then -- control change
- head = head + 2
- elseif type == 12 then -- program change
- head = head + 1
- elseif type == 13 then -- channel pressure
- head = head + 1
- elseif type == 14 then -- pitch bend
- head = head + 2
- elseif status == 255 then -- meta event
- local metaType = byteArray( head, 1 )[ 1 ]
- head = head + 1
- local metaLength, metaHead = vlq( head )
- if metaType == 3 then -- track name
- head = head + metaHead
- track.name = string.sub( midi, head, head + metaLength - 1 )
- head = head + metaLength
- table.insert( track.messages, {
- time = deltaTime,
- type = 'meta',
- meta = 'Track Name',
- text = track.name
- } )
- elseif metaType == 4 then -- instrument name
- head = head + metaHead
- track.instrument = string.sub( midi, head, head + metaLength - 1 )
- head = head + metaLength
- table.insert( track.messages, {
- time = deltaTime,
- type = 'meta',
- meta = 'Instrument Name',
- text = track.instrument
- } )
- elseif metaType == 5 then -- lyric
- head = head + metaHead
- track.lyric = string.sub( midi, head, head + metaLength - 1 )
- head = head + metaLength
- table.insert( track.messages, {
- time = deltaTime,
- type = 'meta',
- meta = 'Lyric',
- text = track.lyric
- } )
- elseif metaType == 47 then -- end of track
- head = head + 1
- table.insert( track.messages, {
- time = deltaTime,
- type = 'meta',
- meta = 'End of Track'
- } )
- break
- elseif metaType == 81 then -- tempo
- head = head + 1
- local micros = bytesToNumber( head, 3 )
- head = head + 3
- table.insert( track.messages, {
- time = deltaTime,
- type = 'meta',
- meta = 'Set Tempo',
- tempo = micros
- } )
- elseif metaType == 88 then -- time signature
- head = head + 1
- local sig = byteArray( head, 4 )
- head = head + 4
- table.insert( track.messages, {
- time = deltaTime,
- type = 'meta',
- meta = 'Time Signature',
- signature = sig
- } )
- elseif metaType == 89 then -- key signature
- head = head + 1
- local sig = byteArray( head, 2 )
- head = head + 2
- table.insert( track.messages, {
- time = deltaTime,
- type = 'meta',
- meta = 'Key Signature',
- signature = sig
- } )
- else -- comment
- head = head + metaHead
- local text = string.sub( midi, head, head + metaLength - 1 )
- head = head + metaLength
- table.insert( track.messages, {
- time = deltaTime,
- type = 'meta',
- meta = 'Unknown Text: ' .. event[ 2 ],
- text = text
- } )
- end
- end
- end
- end
- end
- return ret
- end
- return midiParser
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement