Last active
August 29, 2015 14:00
-
-
Save hsk/11359120 to your computer and use it in GitHub Desktop.
functional midi file parser 疑似コード
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// orignal code http://openpear.org/package/IO_MIDI/downloads | |
package midi | |
case class Midi(header:Header,tracks:List[Track]) | |
trait Chunk | |
case class Header(format:Int,numberOfTracks:Int,divisionFlag:Int,division:Int) extends Chunk | |
case class Track(List[Event]) extends Chunk | |
trait Event | |
class IO_MIDI { | |
def parse(mididata):Midi = { | |
val reader = new IO_Bit() | |
reader.input(mididata) | |
def parseChunks(header:Header,tracks:List[Tracks]):Midi = { | |
if (reader.hasNextData(4)) { | |
_parseChunk(reader) match { | |
case header:Header => parseChunks(header, tracks) | |
case track:Track => parseChunks(header, track::tracks) | |
} | |
} else { | |
Midi(header, tracks.rev) | |
} | |
} | |
parseChunks(null,List()) | |
} | |
def _parseChunk(reader:Reader):Chunk = { | |
val (offset, dummy) = reader.getOffset() | |
val typ = reader.getData(4) | |
val length = reader.getUI32BE() | |
val nextOffset = offset + 8 + length | |
val chunk = typ match { | |
case "MThd" => _parseChunkHeader(reader) | |
case "MTrk" => _parseChunkTrack(reader, nextOffset) | |
case _ => | |
throw new Exception("Unknown chunk (type="+typ+")") | |
} | |
val (doneOffset, dummy) = reader.getOffset() | |
if (doneOffset !== nextOffset) { | |
println("done:"+doneOffset+" next:"+nextOffset) | |
} | |
reader.setOffset(nextOffset, 0) | |
chunk | |
} | |
def _parseChunkHeader(reader:Reader):Header = { | |
Header( | |
format = reader.getUI16BE(), | |
numberOfTracks = reader.getUI16BE(), | |
divisionFlag = reader.getBits(1), | |
division = reader.getBits(15) | |
) | |
} | |
def _parseChunkTrack(reader:Reader, nextOffset:Int):Track = { | |
def parseEvent(events:List[Event], prev_status:Int):List[Event] = { | |
val (offset, dummy) = reader.getOffset() | |
if (offset >= nextOffset) { | |
return events // done | |
} | |
// delta time | |
val deltaTime = getVaribleLengthValue(reader) | |
// event | |
def readStatus(status:Int):Int = { | |
if (status < 0x80) { // running status | |
status = prev_status | |
reader.incrementOffset(-1, 0) // 1 byte back | |
readStatus(status) | |
} else { | |
status | |
} | |
} | |
val status = readStatus(reader.getUI8()) // status byte | |
val eventType = status >> 4 | |
val midiChannel = status & 0x0f | |
eventType match { | |
case 0x8 => // Note Off | |
NoteOff( | |
deltaTime = deltaTime, | |
eventType = eventType, | |
midiChannel = midiChannel, | |
noteNumber = reader.getUI8(), | |
velocity = reader.getUI8() | |
) | |
case 0x9 => // Note On | |
NoteOn( | |
deltaTime = deltaTime, | |
eventType = eventType, | |
midiChannel = midiChannel, | |
noteNumber = reader.getUI8(), | |
velocity = reader.getUI8() | |
) | |
case 0xA => // Note Aftertouch Event | |
NoteAftertouchEvent( | |
deltaTime = deltaTime, | |
eventType = eventType, | |
midiChannel = midiChannel, | |
noteNumber = reader.getUI8() | |
amount = reader.getUI8() | |
) | |
case 0xB => // Controller | |
Controller( | |
deltaTime = deltaTime, | |
eventType = eventType, | |
midiChannel = midiChannel, | |
controllerType = reader.getUI8(), | |
data = reader.getUI8() | |
) | |
case 0xC => // Program Change | |
ProgramChange( | |
deltaTime = deltaTime, | |
eventType = eventType, | |
midiChannel = midiChannel, | |
programNumber = reader.getUI8() | |
) | |
case 0xD => // Note Aftertouch Event | |
NoteAftertouchEvent0( | |
deltaTime = deltaTime, | |
eventType = eventType, | |
midiChannel = midiChannel, | |
amount = reader.getUI8() | |
) | |
case 0xE => // Pitch Bend Event | |
val value = | |
((reader.getUI8() & 0x7f) << 7) + | |
(reader.getUI8() & 0x7f) | |
PitchBendEvent( | |
deltaTime = deltaTime, | |
eventType = eventType, | |
midiChannel = midiChannel, | |
value = value - 0x2000 | |
) | |
case 0xF => // Meta Event of System Ex | |
midiChannel match { | |
case 0xF => // not midiChannel | |
val metaEventType = reader.getUI8() | |
val length = getVaribleLengthValue(reader) | |
NotMidiChannel( | |
deltaTime = deltaTime, | |
eventType = eventType, | |
metaEventType = metaEventType, | |
metaEventData = reader.getData(length) | |
) | |
case 0x0 => // System Ex | |
val length = getVaribleLengthValue(reader) | |
SystemEx( | |
deltaTime = deltaTime, | |
eventType = eventType, | |
systemEx = reader.getData(length) | |
) | |
case _ => | |
printf("unknown status=0x%02X\n", status); | |
printf("unknown EventType=0x%02X\n", eventType); | |
var_dump(chunks); | |
exit (0); | |
} | |
case _ => | |
printf("unknown EventType=0x%02X\n", eventType); | |
var_dump(chunks); | |
exit (0); | |
} | |
parseEvent(chunk::events,status) | |
} | |
Track(parseEvent(List(), 0).rev) | |
} | |
def getVaribleLengthValue(reader:Reader):Int = { | |
def f(n:Int):Int = { | |
val v = reader.getUI8() | |
if (v & 0x80) f((n << 7) + (v & 0x7f)) | |
else (n << 7) + v | |
} | |
f(0) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment