Mercurial > hg > semantic-sia
changeset 37:cb7f70e10a0c
New
author | stevenh |
---|---|
date | Tue, 26 Feb 2013 13:30:46 +0000 |
parents | 029968a13f8b |
children | 1f09e7ded739 |
files | src/org/qmul/eecs/c4dm/sia/midi/MidiParser.java |
diffstat | 1 files changed, 462 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/qmul/eecs/c4dm/sia/midi/MidiParser.java Tue Feb 26 13:30:46 2013 +0000 @@ -0,0 +1,462 @@ +package org.qmul.eecs.c4dm.sia.midi; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + +import javax.sound.midi.InvalidMidiDataException; +import javax.sound.midi.MetaMessage; +import javax.sound.midi.MidiEvent; +import javax.sound.midi.MidiMessage; +import javax.sound.midi.MidiSystem; +import javax.sound.midi.Sequence; +import javax.sound.midi.ShortMessage; +import javax.sound.midi.SysexMessage; +import javax.sound.midi.Track; + +import org.qmul.eecs.c4dm.sia.model.Datapoint; +import org.qmul.eecs.c4dm.sia.model.DimensionValue; +import org.qmul.eecs.c4dm.sia.rdf.Namespaces; + +import com.hp.hpl.jena.ontology.OntClass; +import com.hp.hpl.jena.ontology.OntModel; +import com.hp.hpl.jena.ontology.OntResource; +import com.hp.hpl.jena.rdf.model.AnonId; +import com.hp.hpl.jena.rdf.model.ModelFactory; +import com.hp.hpl.jena.rdf.model.NodeIterator; +import com.hp.hpl.jena.rdf.model.Property; +import com.hp.hpl.jena.rdf.model.RDFNode; +import com.hp.hpl.jena.rdf.model.Resource; +import com.hp.hpl.jena.rdf.model.Statement; +import com.hp.hpl.jena.rdf.model.StmtIterator; +import com.hp.hpl.jena.util.iterator.ExtendedIterator; +import com.hp.hpl.jena.vocabulary.RDF; +import com.sun.media.sound.MidiUtils; + +public class MidiParser { + + public static final int TIME_DIMENSION = 1; + public static final int PITCH_DIMENSION = 2; + public static final int CHANNEL_DIMENSION = 3; + + // The ontology loaded as dataset + private static final String ontology = "file:src/rdf/siaDatapointOntology.n3"; + + // The final output file + private static final String finalModelFileName = "src/rdf/midiModel"; + + public static void main(String[] args) + { + // First create a Jena ontology model + OntModel ontModel = ModelFactory + .createOntologyModel(); // OntModelSpec.OWL_MEM + + // Then read the data from the file into the ontology model + ontModel.read(ontology, "N3"); + + // Set the value of SIA_NS_URI as the namespace for 'sia' found in + // the ontology. This must be done before we use any of the sia.model classes. + Namespaces.SIA_NS_URI = ontModel.getNsPrefixURI("sia"); + OntClass datapointClass = ontModel.getOntClass(Datapoint.RESOURCE_URI); + Resource datapointResource = ontModel.getOntResource(datapointClass); + Property siaDimValProperty = ontModel.createProperty(DimensionValue.PROPERTY_URI); + Property siaDimensionProperty = ontModel.createProperty(DimensionValue.DIMENSION_URI); + Property siaValueProperty = ontModel.createProperty(DimensionValue.VALUE_URI); + + String midiFileName = "/Volumes/USB DISK/portable/ewerts/Cantata_16_no_5-mids/score.mid"; + + File midiFile1 = new File(midiFileName); + + Sequence sequence = null; + try { + sequence = MidiSystem.getSequence(midiFile1); + } catch (InvalidMidiDataException e) { + e.printStackTrace(); + System.exit(1); + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); + } + + MidiUtils.TempoCache tempoCache = new MidiUtils.TempoCache(sequence); + + Track[] tracks = sequence.getTracks(); + int numTracks = tracks.length; + float divisionType = sequence.getDivisionType(); + List<Datapoint> datapoints = new ArrayList<Datapoint>(); + + HashMap<Integer, Integer> trackDimensionMap = getTrackToDimensionIndexMap(sequence); + + if (trackDimensionMap.isEmpty()) + { + try { + throw new Exception("Couldn't find any track to dimension mappings"); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + + long maxTick = 0; + + for (int trackIdx = 0; trackIdx < numTracks; trackIdx++) + { + System.out.println("Track " + trackIdx + ":"); + int numEvents = tracks[trackIdx].size(); + for (int eventIdx = 0; eventIdx < numEvents; eventIdx++) + { + MidiEvent event = tracks[trackIdx].get(eventIdx); + long tick = event.getTick(); + MidiMessage midiMessage = event.getMessage(); + + int midiMessageLength = midiMessage.getLength(); + byte[] messageBytes = midiMessage.getMessage(); + int status = midiMessage.getStatus(); + System.out.println("tick = " + tick + " midiMessageLength = " + midiMessageLength + " status byte = " + status); + + int s = status & 0x80; + if (s == 0x80) + { + System.out.println("STATUS MESSAGE (s = " + s + ")"); + } + else + { + System.out.println("not a status message (s = " + s + ")"); + } + + // Determine the type of this message (short, sysex or meta) + if (midiMessage instanceof ShortMessage) + { + System.out.print("ShortMessage "); + + // Determine which command is being issued + ShortMessage shortMessage = (ShortMessage)midiMessage; + int messageCommand = shortMessage.getCommand(); + int channel = shortMessage.getChannel(); + int data1 = shortMessage.getData1(); + int data2 = shortMessage.getData2(); + + if (messageCommand == ShortMessage.ACTIVE_SENSING) + { + System.out.print("ignoring ACTIVE_SENSING"); + } + else if (messageCommand == ShortMessage.CHANNEL_PRESSURE) + { + System.out.print("ignoring CHANNEL_PRESSURE"); + } + else if (messageCommand == ShortMessage.CONTINUE) + { + System.out.print("ignoring CONTINUE"); + } + else if (messageCommand == ShortMessage.CONTROL_CHANGE) + { + System.out.print("ignoring CONTROL_CHANGE"); + } + else if (messageCommand == ShortMessage.END_OF_EXCLUSIVE) + { + System.out.print("ignoring END_OF_EXCLUSIVE"); + } + else if (messageCommand == ShortMessage.MIDI_TIME_CODE) + { + System.out.print("ignoring MIDI_TIME_CODE"); + } + else if (messageCommand == ShortMessage.NOTE_OFF) + { + System.out.println("NOTE_OFF"); + long microsecs = MidiUtils.tick2microsecond(sequence, tick, tempoCache); + System.out.println(" microsecs = " + microsecs); + } + else if (messageCommand == ShortMessage.NOTE_ON) + { + if (tick > maxTick) + { + maxTick = tick; + } + System.out.println("NOTE_ON"); + long microsecs = MidiUtils.tick2microsecond(sequence, tick, tempoCache); + System.out.println(" microsecs = " + microsecs); + + DimensionValue timeDimVal = new DimensionValue(); + timeDimVal.setDimension(TIME_DIMENSION); + timeDimVal.setValue(tick); + + DimensionValue dimVal = new DimensionValue(); + dimVal.setDimension(trackDimensionMap.get(trackIdx)); + dimVal.setValue(data1); + + Datapoint datapoint = new Datapoint(); + Vector<DimensionValue> dimVals = new Vector<DimensionValue>(); + dimVals.add(timeDimVal); + dimVals.add(dimVal); + + datapoint.setDimensionValues(dimVals); + datapoints.add(datapoint); + + // RDF + Resource datapointBnode = ontModel.createResource(AnonId.create()); + ontModel.add(datapointBnode, RDF.type, datapointResource); + + Resource timeDimValBnode = ontModel.createResource(AnonId.create()); + Resource pitchDimValBnode = ontModel.createResource(AnonId.create()); + + ontModel.add(datapointBnode, siaDimValProperty, timeDimValBnode); + ontModel.addLiteral(timeDimValBnode, siaDimensionProperty, TIME_DIMENSION); + ontModel.addLiteral(timeDimValBnode, siaValueProperty, tick); + + ontModel.add(datapointBnode, siaDimValProperty, pitchDimValBnode); + ontModel.addLiteral(pitchDimValBnode, siaDimensionProperty, trackDimensionMap.get(trackIdx).intValue()); + ontModel.addLiteral(pitchDimValBnode, siaValueProperty, data1); + } + else if (messageCommand == ShortMessage.PITCH_BEND) + { + System.out.print("ignoring PITCH_BEND"); + } + else if (messageCommand == ShortMessage.POLY_PRESSURE) + { + System.out.print("ignoring POLY_PRESSURE"); + } + else if (messageCommand == ShortMessage.PROGRAM_CHANGE) + { + System.out.print("ignoring PROGRAM_CHANGE"); + } + else if (messageCommand == ShortMessage.SONG_POSITION_POINTER) + { + System.out.print("ignoring SONG_POSITION_POINTER"); + } + else + { + System.out.print("unrecognised midi message command (" + messageCommand + ")"); + } + System.out.print(", channel " + channel + ", data1 = [" + data1 + "], data2 = [" + data2 + "]"); + System.out.println(); + } + else if (midiMessage instanceof MetaMessage) + { + System.out.println("MetaMessage"); + + MetaMessage metaMessage = (MetaMessage)midiMessage; + byte[] metaMessageData = metaMessage.getData(); + int metaMessageLength = metaMessage.getLength(); + int metaMessageType = metaMessage.getType(); + System.out.println("metaMessageType = " + metaMessageType + ", metaMessageLength = " + metaMessageLength); + + // Determine message type + if (metaMessageType == 81) + { + if (divisionType == Sequence.PPQ) + { + // Do nothing - we've dealt with PPQ tempo data elsewhere + } + else + { + try { + throw new Exception("Not yet implemented SMPTE tempo metadata"); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + } + + for (int dataIdx = 0; dataIdx < metaMessageData.length; dataIdx++) + { + System.out.println("\tmetaMessageData[" + dataIdx + "] = " + (metaMessageType == 81 ? metaMessageData[dataIdx] : (char)metaMessageData[dataIdx])); + } + + } + else if (midiMessage instanceof SysexMessage) + { + // We can safely ignore these messages + System.out.println("ignoring SysexMessage"); + } + else + { + System.out.println("Unknown MidiMessage type (" + midiMessage.getClass().toString() + ")"); + } + + for (int byteIdx = 0; byteIdx < midiMessageLength; byteIdx++) + { + byte messageByte = messageBytes[byteIdx]; + System.out.println("\tbyte[" + byteIdx + "] = " + messageByte); + } + } + } + + // Data integrity start - set any 'missing' dimensions to zero value + // Note the highest dimension for later use + Collection<Integer> values = trackDimensionMap.values(); + Iterator<Integer> valuesIter = values.iterator(); + int maxDimension = 0; + while (valuesIter.hasNext()) + { + Integer value = valuesIter.next(); + if (value > maxDimension) + { + maxDimension = value; + } + } + + // Iterate over all datapoints + ExtendedIterator<? extends OntResource> datapointIter = datapointClass.listInstances(); + OntResource datapointIndividual; + OntModel ontModelTemp = ModelFactory + .createOntologyModel(); // OntModelSpec.OWL_MEM + + while (datapointIter.hasNext()) + { + datapointIndividual = datapointIter.next(); + + // Find all Dimension Values for this datapoint + NodeIterator dimValIter = datapointIndividual.listPropertyValues(siaDimValProperty); + + // Create a hashmap of dimensions for this datapoint + HashMap<Integer, Boolean> dimensions = new HashMap<>(); + while (dimValIter.hasNext()) + { + RDFNode dimVal = dimValIter.next(); + NodeIterator dims = ontModel.listObjectsOfProperty(dimVal.asResource(), siaDimensionProperty); + while (dims.hasNext()) + { + int dim = dims.next().asLiteral().getInt(); + dimensions.put(dim, true); + System.out.println(dim); + } + } + + for (int i = 2; i <= maxDimension; i++) + { + if (!dimensions.containsKey(i)) + { + Resource zeroValDimValBnode = ontModel.createResource(AnonId.create()); + + Resource dpResource = datapointIndividual.asResource(); + ontModelTemp.add(dpResource, siaDimValProperty, zeroValDimValBnode); + ontModelTemp.addLiteral(zeroValDimValBnode, siaDimensionProperty, i); + ontModelTemp.addLiteral(zeroValDimValBnode, siaValueProperty, 0); + } + } + + } + ontModel.add(ontModelTemp); + // Data integrity end + + System.out.println("done"); + + // Print out what we've got now + System.out.println("------------------"); + StmtIterator stmtIterator = ontModel.listStatements(); + printStmts(stmtIterator); + + // Write rdf to file + File outFileRdf = new File(finalModelFileName + ".rdf"); + File outFileN3 = new File(finalModelFileName + ".n3"); + FileOutputStream outFileOutputStreamRdf = null; + FileOutputStream outFileOutputStreamN3 = null; + + // RDF/XML version + try { + outFileOutputStreamRdf = new FileOutputStream(outFileRdf); + ontModel.writeAll(outFileOutputStreamRdf, "RDF/XML", null); + } catch (FileNotFoundException e) { + System.out.println("Unable to write to file: " + + outFileRdf.getAbsolutePath()); + e.printStackTrace(); + System.exit(1); + } + + try { + outFileOutputStreamRdf.close(); + } catch (IOException e1) { + e1.printStackTrace(); + System.exit(1); + } + + // N3 version + try { + outFileOutputStreamN3 = new FileOutputStream(outFileN3); + ontModel.writeAll(outFileOutputStreamN3, "N3", null); + } catch (FileNotFoundException e) { + System.out.println("Unable to write to file: " + + outFileN3.getAbsolutePath()); + e.printStackTrace(); + System.exit(1); + } + + try { + outFileOutputStreamN3.close(); + } catch (IOException e1) { + e1.printStackTrace(); + System.exit(1); + } + + System.out.println("Model written to files: " + + outFileRdf.getAbsolutePath() + " and " + outFileN3.getAbsolutePath()); + + System.out.println("max tick: " + maxTick); + + } + + private static HashMap<Integer, Integer> getTrackToDimensionIndexMap(Sequence sequence) { + + int numTracks = sequence.getTracks().length; + int siaDatapointDimension = 2; // Dimension 1 is reserved for time + + HashMap<Integer, Integer> trackDimensionMap = new HashMap<Integer, Integer>(); + Track[] tracks = sequence.getTracks(); + + for (int trackIdx = 0; trackIdx < numTracks; trackIdx++) + { + boolean trackIsAudible = false; + int numEvents = tracks[trackIdx].size(); + + for (int eventIdx = 0; eventIdx < numEvents; eventIdx++) + { + MidiEvent event = tracks[trackIdx].get(eventIdx); + MidiMessage midiMessage = event.getMessage(); + + // Determine the type of this message (short, sysex or meta) + if (midiMessage instanceof ShortMessage) + { + // Determine which command is being issued + ShortMessage shortMessage = (ShortMessage)midiMessage; + int messageCommand = shortMessage.getCommand(); + + if (messageCommand == ShortMessage.NOTE_ON) + { + trackIsAudible = true; + break; + } + } + } + + if (trackIsAudible) + { + trackDimensionMap.put(trackIdx, siaDatapointDimension); + siaDatapointDimension++; + } + } + + return trackDimensionMap; + } + + private static void printStmts(StmtIterator iter) { + Statement statement; + + while (iter.hasNext()) { + statement = iter.nextStatement(); + System.out.println(" | <" + statement.getSubject() + "> | <" + + statement.getPredicate() + "> | <" + + statement.getObject() + "> | "); + } + + // And an empty line to make it pretty + System.out.println(); + } +}