Skip to content

Instantly share code, notes, and snippets.

@hsk
Last active August 29, 2015 14:00
Show Gist options
  • Save hsk/11359120 to your computer and use it in GitHub Desktop.
Save hsk/11359120 to your computer and use it in GitHub Desktop.
functional midi file parser 疑似コード
// 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