annotate src/org/qmul/eecs/c4dm/sia/midi/MidiParser.java @ 46:fa9030705e93

organised imports now uses three dimensions - one for time, one for pitch, one for channel number removed the Data integrity code section - no longer necessary stores the final model in an external TDB database, as well as writing to N3 and RDF/XML files
author stevenh
date Tue, 02 Apr 2013 22:47:21 +0100
parents cb7f70e10a0c
children 39106212a3c6
rev   line source
stevenh@37 1 package org.qmul.eecs.c4dm.sia.midi;
stevenh@37 2
stevenh@37 3 import java.io.File;
stevenh@37 4 import java.io.FileNotFoundException;
stevenh@37 5 import java.io.FileOutputStream;
stevenh@37 6 import java.io.IOException;
stevenh@37 7 import java.util.ArrayList;
stevenh@37 8 import java.util.Collection;
stevenh@37 9 import java.util.HashMap;
stevenh@37 10 import java.util.Iterator;
stevenh@37 11 import java.util.List;
stevenh@37 12 import java.util.Vector;
stevenh@37 13
stevenh@37 14 import javax.sound.midi.InvalidMidiDataException;
stevenh@37 15 import javax.sound.midi.MetaMessage;
stevenh@37 16 import javax.sound.midi.MidiEvent;
stevenh@37 17 import javax.sound.midi.MidiMessage;
stevenh@37 18 import javax.sound.midi.MidiSystem;
stevenh@37 19 import javax.sound.midi.Sequence;
stevenh@37 20 import javax.sound.midi.ShortMessage;
stevenh@37 21 import javax.sound.midi.SysexMessage;
stevenh@37 22 import javax.sound.midi.Track;
stevenh@37 23
stevenh@46 24 import org.qmul.eecs.c4dm.sia.SiaMain;
stevenh@37 25 import org.qmul.eecs.c4dm.sia.model.Datapoint;
stevenh@37 26 import org.qmul.eecs.c4dm.sia.model.DimensionValue;
stevenh@37 27 import org.qmul.eecs.c4dm.sia.rdf.Namespaces;
stevenh@37 28
stevenh@37 29 import com.hp.hpl.jena.ontology.OntClass;
stevenh@37 30 import com.hp.hpl.jena.ontology.OntModel;
stevenh@37 31 import com.hp.hpl.jena.ontology.OntResource;
stevenh@46 32 import com.hp.hpl.jena.query.Dataset;
stevenh@46 33 import com.hp.hpl.jena.query.ReadWrite;
stevenh@37 34 import com.hp.hpl.jena.rdf.model.AnonId;
stevenh@37 35 import com.hp.hpl.jena.rdf.model.ModelFactory;
stevenh@37 36 import com.hp.hpl.jena.rdf.model.NodeIterator;
stevenh@37 37 import com.hp.hpl.jena.rdf.model.Property;
stevenh@37 38 import com.hp.hpl.jena.rdf.model.RDFNode;
stevenh@37 39 import com.hp.hpl.jena.rdf.model.Resource;
stevenh@37 40 import com.hp.hpl.jena.rdf.model.Statement;
stevenh@37 41 import com.hp.hpl.jena.rdf.model.StmtIterator;
stevenh@46 42 import com.hp.hpl.jena.tdb.TDBFactory;
stevenh@37 43 import com.hp.hpl.jena.util.iterator.ExtendedIterator;
stevenh@37 44 import com.hp.hpl.jena.vocabulary.RDF;
stevenh@37 45 import com.sun.media.sound.MidiUtils;
stevenh@37 46
stevenh@37 47 public class MidiParser {
stevenh@37 48
stevenh@37 49 public static final int TIME_DIMENSION = 1;
stevenh@37 50 public static final int PITCH_DIMENSION = 2;
stevenh@37 51 public static final int CHANNEL_DIMENSION = 3;
stevenh@37 52
stevenh@37 53 // The ontology loaded as dataset
stevenh@37 54 private static final String ontology = "file:src/rdf/siaDatapointOntology.n3";
stevenh@37 55
stevenh@37 56 // The final output file
stevenh@37 57 private static final String finalModelFileName = "src/rdf/midiModel";
stevenh@37 58
stevenh@46 59 // The input midi file
stevenh@46 60 private static final String midiFileName = "/Volumes/USB_DISK/portable/ewerts/Cantata_16_no_5-mids/score.mid";
stevenh@46 61
stevenh@37 62 public static void main(String[] args)
stevenh@37 63 {
stevenh@37 64 // First create a Jena ontology model
stevenh@37 65 OntModel ontModel = ModelFactory
stevenh@37 66 .createOntologyModel(); // OntModelSpec.OWL_MEM
stevenh@37 67
stevenh@37 68 // Then read the data from the file into the ontology model
stevenh@37 69 ontModel.read(ontology, "N3");
stevenh@37 70
stevenh@37 71 OntClass datapointClass = ontModel.getOntClass(Datapoint.RESOURCE_URI);
stevenh@37 72 Resource datapointResource = ontModel.getOntResource(datapointClass);
stevenh@37 73 Property siaDimValProperty = ontModel.createProperty(DimensionValue.PROPERTY_URI);
stevenh@37 74 Property siaDimensionProperty = ontModel.createProperty(DimensionValue.DIMENSION_URI);
stevenh@37 75 Property siaValueProperty = ontModel.createProperty(DimensionValue.VALUE_URI);
stevenh@37 76
stevenh@37 77 File midiFile1 = new File(midiFileName);
stevenh@37 78
stevenh@37 79 Sequence sequence = null;
stevenh@37 80 try {
stevenh@37 81 sequence = MidiSystem.getSequence(midiFile1);
stevenh@37 82 } catch (InvalidMidiDataException e) {
stevenh@37 83 e.printStackTrace();
stevenh@37 84 System.exit(1);
stevenh@37 85 } catch (IOException e) {
stevenh@37 86 e.printStackTrace();
stevenh@37 87 System.exit(1);
stevenh@37 88 }
stevenh@37 89
stevenh@37 90 MidiUtils.TempoCache tempoCache = new MidiUtils.TempoCache(sequence);
stevenh@37 91
stevenh@37 92 Track[] tracks = sequence.getTracks();
stevenh@37 93 int numTracks = tracks.length;
stevenh@37 94 float divisionType = sequence.getDivisionType();
stevenh@37 95 List<Datapoint> datapoints = new ArrayList<Datapoint>();
stevenh@37 96
stevenh@37 97 HashMap<Integer, Integer> trackDimensionMap = getTrackToDimensionIndexMap(sequence);
stevenh@37 98
stevenh@37 99 if (trackDimensionMap.isEmpty())
stevenh@37 100 {
stevenh@37 101 try {
stevenh@37 102 throw new Exception("Couldn't find any track to dimension mappings");
stevenh@37 103 } catch (Exception e) {
stevenh@37 104 e.printStackTrace();
stevenh@37 105 System.exit(1);
stevenh@37 106 }
stevenh@37 107 }
stevenh@37 108
stevenh@37 109 long maxTick = 0;
stevenh@37 110
stevenh@37 111 for (int trackIdx = 0; trackIdx < numTracks; trackIdx++)
stevenh@37 112 {
stevenh@37 113 System.out.println("Track " + trackIdx + ":");
stevenh@37 114 int numEvents = tracks[trackIdx].size();
stevenh@37 115 for (int eventIdx = 0; eventIdx < numEvents; eventIdx++)
stevenh@37 116 {
stevenh@37 117 MidiEvent event = tracks[trackIdx].get(eventIdx);
stevenh@37 118 long tick = event.getTick();
stevenh@37 119 MidiMessage midiMessage = event.getMessage();
stevenh@37 120
stevenh@37 121 int midiMessageLength = midiMessage.getLength();
stevenh@37 122 byte[] messageBytes = midiMessage.getMessage();
stevenh@37 123 int status = midiMessage.getStatus();
stevenh@37 124 System.out.println("tick = " + tick + " midiMessageLength = " + midiMessageLength + " status byte = " + status);
stevenh@37 125
stevenh@37 126 int s = status & 0x80;
stevenh@37 127 if (s == 0x80)
stevenh@37 128 {
stevenh@37 129 System.out.println("STATUS MESSAGE (s = " + s + ")");
stevenh@37 130 }
stevenh@37 131 else
stevenh@37 132 {
stevenh@37 133 System.out.println("not a status message (s = " + s + ")");
stevenh@37 134 }
stevenh@37 135
stevenh@37 136 // Determine the type of this message (short, sysex or meta)
stevenh@37 137 if (midiMessage instanceof ShortMessage)
stevenh@37 138 {
stevenh@37 139 System.out.print("ShortMessage ");
stevenh@37 140
stevenh@37 141 // Determine which command is being issued
stevenh@37 142 ShortMessage shortMessage = (ShortMessage)midiMessage;
stevenh@37 143 int messageCommand = shortMessage.getCommand();
stevenh@37 144 int channel = shortMessage.getChannel();
stevenh@37 145 int data1 = shortMessage.getData1();
stevenh@37 146 int data2 = shortMessage.getData2();
stevenh@37 147
stevenh@37 148 if (messageCommand == ShortMessage.ACTIVE_SENSING)
stevenh@37 149 {
stevenh@37 150 System.out.print("ignoring ACTIVE_SENSING");
stevenh@37 151 }
stevenh@37 152 else if (messageCommand == ShortMessage.CHANNEL_PRESSURE)
stevenh@37 153 {
stevenh@37 154 System.out.print("ignoring CHANNEL_PRESSURE");
stevenh@37 155 }
stevenh@37 156 else if (messageCommand == ShortMessage.CONTINUE)
stevenh@37 157 {
stevenh@37 158 System.out.print("ignoring CONTINUE");
stevenh@37 159 }
stevenh@37 160 else if (messageCommand == ShortMessage.CONTROL_CHANGE)
stevenh@37 161 {
stevenh@37 162 System.out.print("ignoring CONTROL_CHANGE");
stevenh@37 163 }
stevenh@37 164 else if (messageCommand == ShortMessage.END_OF_EXCLUSIVE)
stevenh@37 165 {
stevenh@37 166 System.out.print("ignoring END_OF_EXCLUSIVE");
stevenh@37 167 }
stevenh@37 168 else if (messageCommand == ShortMessage.MIDI_TIME_CODE)
stevenh@37 169 {
stevenh@37 170 System.out.print("ignoring MIDI_TIME_CODE");
stevenh@37 171 }
stevenh@37 172 else if (messageCommand == ShortMessage.NOTE_OFF)
stevenh@37 173 {
stevenh@37 174 System.out.println("NOTE_OFF");
stevenh@37 175 long microsecs = MidiUtils.tick2microsecond(sequence, tick, tempoCache);
stevenh@37 176 System.out.println(" microsecs = " + microsecs);
stevenh@37 177 }
stevenh@37 178 else if (messageCommand == ShortMessage.NOTE_ON)
stevenh@37 179 {
stevenh@37 180 if (tick > maxTick)
stevenh@37 181 {
stevenh@37 182 maxTick = tick;
stevenh@37 183 }
stevenh@37 184 System.out.println("NOTE_ON");
stevenh@46 185 double microsecs = MidiUtils.tick2microsecond(sequence, tick, tempoCache);
stevenh@46 186 double secs = microsecs/1000000;
stevenh@46 187 System.out.println(" microsecs = " + secs);
stevenh@37 188
stevenh@37 189 DimensionValue timeDimVal = new DimensionValue();
stevenh@37 190 timeDimVal.setDimension(TIME_DIMENSION);
stevenh@46 191 timeDimVal.setValue(secs);
stevenh@37 192
stevenh@46 193 DimensionValue pitchDimVal = new DimensionValue();
stevenh@46 194 pitchDimVal.setDimension(PITCH_DIMENSION);
stevenh@46 195 pitchDimVal.setValue(data1);
stevenh@37 196
stevenh@46 197 DimensionValue channelDimVal = new DimensionValue();
stevenh@46 198 channelDimVal.setDimension(CHANNEL_DIMENSION);
stevenh@46 199 channelDimVal.setValue(trackDimensionMap.get(trackIdx));
stevenh@46 200
stevenh@37 201 Datapoint datapoint = new Datapoint();
stevenh@37 202 Vector<DimensionValue> dimVals = new Vector<DimensionValue>();
stevenh@37 203 dimVals.add(timeDimVal);
stevenh@46 204 dimVals.add(pitchDimVal);
stevenh@46 205 dimVals.add(channelDimVal);
stevenh@37 206
stevenh@37 207 datapoint.setDimensionValues(dimVals);
stevenh@37 208 datapoints.add(datapoint);
stevenh@37 209
stevenh@37 210 // RDF
stevenh@37 211 Resource datapointBnode = ontModel.createResource(AnonId.create());
stevenh@37 212 ontModel.add(datapointBnode, RDF.type, datapointResource);
stevenh@37 213
stevenh@37 214 Resource timeDimValBnode = ontModel.createResource(AnonId.create());
stevenh@37 215 Resource pitchDimValBnode = ontModel.createResource(AnonId.create());
stevenh@46 216 Resource channelDimValBnode = ontModel.createResource(AnonId.create());
stevenh@37 217
stevenh@37 218 ontModel.add(datapointBnode, siaDimValProperty, timeDimValBnode);
stevenh@37 219 ontModel.addLiteral(timeDimValBnode, siaDimensionProperty, TIME_DIMENSION);
stevenh@46 220 ontModel.addLiteral(timeDimValBnode, siaValueProperty, secs);
stevenh@37 221
stevenh@37 222 ontModel.add(datapointBnode, siaDimValProperty, pitchDimValBnode);
stevenh@46 223 ontModel.addLiteral(pitchDimValBnode, siaDimensionProperty, PITCH_DIMENSION);
stevenh@37 224 ontModel.addLiteral(pitchDimValBnode, siaValueProperty, data1);
stevenh@46 225
stevenh@46 226 ontModel.add(datapointBnode, siaDimValProperty, channelDimValBnode);
stevenh@46 227 ontModel.addLiteral(channelDimValBnode, siaDimensionProperty, CHANNEL_DIMENSION);
stevenh@46 228 ontModel.addLiteral(channelDimValBnode, siaValueProperty, trackDimensionMap.get(trackIdx).intValue());
stevenh@46 229
stevenh@37 230 }
stevenh@37 231 else if (messageCommand == ShortMessage.PITCH_BEND)
stevenh@37 232 {
stevenh@37 233 System.out.print("ignoring PITCH_BEND");
stevenh@37 234 }
stevenh@37 235 else if (messageCommand == ShortMessage.POLY_PRESSURE)
stevenh@37 236 {
stevenh@37 237 System.out.print("ignoring POLY_PRESSURE");
stevenh@37 238 }
stevenh@37 239 else if (messageCommand == ShortMessage.PROGRAM_CHANGE)
stevenh@37 240 {
stevenh@37 241 System.out.print("ignoring PROGRAM_CHANGE");
stevenh@37 242 }
stevenh@37 243 else if (messageCommand == ShortMessage.SONG_POSITION_POINTER)
stevenh@37 244 {
stevenh@37 245 System.out.print("ignoring SONG_POSITION_POINTER");
stevenh@37 246 }
stevenh@37 247 else
stevenh@37 248 {
stevenh@37 249 System.out.print("unrecognised midi message command (" + messageCommand + ")");
stevenh@37 250 }
stevenh@37 251 System.out.print(", channel " + channel + ", data1 = [" + data1 + "], data2 = [" + data2 + "]");
stevenh@37 252 System.out.println();
stevenh@37 253 }
stevenh@37 254 else if (midiMessage instanceof MetaMessage)
stevenh@37 255 {
stevenh@37 256 System.out.println("MetaMessage");
stevenh@37 257
stevenh@37 258 MetaMessage metaMessage = (MetaMessage)midiMessage;
stevenh@37 259 byte[] metaMessageData = metaMessage.getData();
stevenh@37 260 int metaMessageLength = metaMessage.getLength();
stevenh@37 261 int metaMessageType = metaMessage.getType();
stevenh@37 262 System.out.println("metaMessageType = " + metaMessageType + ", metaMessageLength = " + metaMessageLength);
stevenh@37 263
stevenh@37 264 // Determine message type
stevenh@37 265 if (metaMessageType == 81)
stevenh@37 266 {
stevenh@37 267 if (divisionType == Sequence.PPQ)
stevenh@37 268 {
stevenh@37 269 // Do nothing - we've dealt with PPQ tempo data elsewhere
stevenh@37 270 }
stevenh@37 271 else
stevenh@37 272 {
stevenh@37 273 try {
stevenh@37 274 throw new Exception("Not yet implemented SMPTE tempo metadata");
stevenh@37 275 } catch (Exception e) {
stevenh@37 276 e.printStackTrace();
stevenh@37 277 System.exit(1);
stevenh@37 278 }
stevenh@37 279 }
stevenh@37 280 }
stevenh@37 281
stevenh@37 282 for (int dataIdx = 0; dataIdx < metaMessageData.length; dataIdx++)
stevenh@37 283 {
stevenh@37 284 System.out.println("\tmetaMessageData[" + dataIdx + "] = " + (metaMessageType == 81 ? metaMessageData[dataIdx] : (char)metaMessageData[dataIdx]));
stevenh@37 285 }
stevenh@37 286
stevenh@37 287 }
stevenh@37 288 else if (midiMessage instanceof SysexMessage)
stevenh@37 289 {
stevenh@37 290 // We can safely ignore these messages
stevenh@37 291 System.out.println("ignoring SysexMessage");
stevenh@37 292 }
stevenh@37 293 else
stevenh@37 294 {
stevenh@37 295 System.out.println("Unknown MidiMessage type (" + midiMessage.getClass().toString() + ")");
stevenh@37 296 }
stevenh@37 297
stevenh@37 298 for (int byteIdx = 0; byteIdx < midiMessageLength; byteIdx++)
stevenh@37 299 {
stevenh@37 300 byte messageByte = messageBytes[byteIdx];
stevenh@37 301 System.out.println("\tbyte[" + byteIdx + "] = " + messageByte);
stevenh@37 302 }
stevenh@37 303 }
stevenh@37 304 }
stevenh@37 305
stevenh@37 306 System.out.println("done");
stevenh@37 307
stevenh@37 308 // Print out what we've got now
stevenh@37 309 System.out.println("------------------");
stevenh@37 310 StmtIterator stmtIterator = ontModel.listStatements();
stevenh@37 311 printStmts(stmtIterator);
stevenh@37 312
stevenh@46 313 // TODO write rdf to file
stevenh@37 314 File outFileRdf = new File(finalModelFileName + ".rdf");
stevenh@37 315 File outFileN3 = new File(finalModelFileName + ".n3");
stevenh@37 316 FileOutputStream outFileOutputStreamRdf = null;
stevenh@37 317 FileOutputStream outFileOutputStreamN3 = null;
stevenh@37 318
stevenh@37 319 // RDF/XML version
stevenh@37 320 try {
stevenh@37 321 outFileOutputStreamRdf = new FileOutputStream(outFileRdf);
stevenh@37 322 ontModel.writeAll(outFileOutputStreamRdf, "RDF/XML", null);
stevenh@37 323 } catch (FileNotFoundException e) {
stevenh@37 324 System.out.println("Unable to write to file: "
stevenh@37 325 + outFileRdf.getAbsolutePath());
stevenh@37 326 e.printStackTrace();
stevenh@37 327 System.exit(1);
stevenh@37 328 }
stevenh@37 329
stevenh@37 330 try {
stevenh@37 331 outFileOutputStreamRdf.close();
stevenh@37 332 } catch (IOException e1) {
stevenh@37 333 e1.printStackTrace();
stevenh@37 334 System.exit(1);
stevenh@37 335 }
stevenh@37 336
stevenh@37 337 // N3 version
stevenh@37 338 try {
stevenh@37 339 outFileOutputStreamN3 = new FileOutputStream(outFileN3);
stevenh@37 340 ontModel.writeAll(outFileOutputStreamN3, "N3", null);
stevenh@37 341 } catch (FileNotFoundException e) {
stevenh@37 342 System.out.println("Unable to write to file: "
stevenh@37 343 + outFileN3.getAbsolutePath());
stevenh@37 344 e.printStackTrace();
stevenh@37 345 System.exit(1);
stevenh@37 346 }
stevenh@37 347
stevenh@37 348 try {
stevenh@37 349 outFileOutputStreamN3.close();
stevenh@37 350 } catch (IOException e1) {
stevenh@37 351 e1.printStackTrace();
stevenh@37 352 System.exit(1);
stevenh@37 353 }
stevenh@37 354
stevenh@37 355 System.out.println("Model written to files: "
stevenh@37 356 + outFileRdf.getAbsolutePath() + " and " + outFileN3.getAbsolutePath());
stevenh@37 357
stevenh@46 358 // Obtain a dataset context
stevenh@46 359 Dataset dataset = TDBFactory.assembleDataset(SiaMain.assemblerFile);
stevenh@46 360 dataset.begin(ReadWrite.WRITE) ;
stevenh@46 361 try {
stevenh@46 362 dataset.replaceNamedModel(SiaMain.graph, ontModel);
stevenh@46 363 dataset.commit();
stevenh@46 364 System.out.println("dataset.commit() done");
stevenh@46 365 } finally {
stevenh@46 366 dataset.end();
stevenh@46 367 System.out.println("dataset.end() done");
stevenh@46 368 }
stevenh@46 369 dataset.close();
stevenh@46 370 System.out.println("dataset.close() done");
stevenh@46 371
stevenh@46 372 System.out.println("max tick: " + maxTick);
stevenh@46 373 System.out.println("Number of Datapoints (n) = " + datapoints.size());
stevenh@37 374
stevenh@37 375 }
stevenh@37 376
stevenh@37 377 private static HashMap<Integer, Integer> getTrackToDimensionIndexMap(Sequence sequence) {
stevenh@37 378
stevenh@37 379 int numTracks = sequence.getTracks().length;
stevenh@37 380 int siaDatapointDimension = 2; // Dimension 1 is reserved for time
stevenh@37 381
stevenh@37 382 HashMap<Integer, Integer> trackDimensionMap = new HashMap<Integer, Integer>();
stevenh@37 383 Track[] tracks = sequence.getTracks();
stevenh@37 384
stevenh@37 385 for (int trackIdx = 0; trackIdx < numTracks; trackIdx++)
stevenh@37 386 {
stevenh@37 387 boolean trackIsAudible = false;
stevenh@37 388 int numEvents = tracks[trackIdx].size();
stevenh@37 389
stevenh@37 390 for (int eventIdx = 0; eventIdx < numEvents; eventIdx++)
stevenh@37 391 {
stevenh@37 392 MidiEvent event = tracks[trackIdx].get(eventIdx);
stevenh@37 393 MidiMessage midiMessage = event.getMessage();
stevenh@37 394
stevenh@37 395 // Determine the type of this message (short, sysex or meta)
stevenh@37 396 if (midiMessage instanceof ShortMessage)
stevenh@37 397 {
stevenh@37 398 // Determine which command is being issued
stevenh@37 399 ShortMessage shortMessage = (ShortMessage)midiMessage;
stevenh@37 400 int messageCommand = shortMessage.getCommand();
stevenh@37 401
stevenh@37 402 if (messageCommand == ShortMessage.NOTE_ON)
stevenh@37 403 {
stevenh@37 404 trackIsAudible = true;
stevenh@37 405 break;
stevenh@37 406 }
stevenh@37 407 }
stevenh@37 408 }
stevenh@37 409
stevenh@37 410 if (trackIsAudible)
stevenh@37 411 {
stevenh@37 412 trackDimensionMap.put(trackIdx, siaDatapointDimension);
stevenh@37 413 siaDatapointDimension++;
stevenh@37 414 }
stevenh@37 415 }
stevenh@37 416
stevenh@37 417 return trackDimensionMap;
stevenh@37 418 }
stevenh@37 419
stevenh@37 420 private static void printStmts(StmtIterator iter) {
stevenh@37 421 Statement statement;
stevenh@37 422
stevenh@37 423 while (iter.hasNext()) {
stevenh@37 424 statement = iter.nextStatement();
stevenh@37 425 System.out.println(" | <" + statement.getSubject() + "> | <"
stevenh@37 426 + statement.getPredicate() + "> | <"
stevenh@37 427 + statement.getObject() + "> | ");
stevenh@37 428 }
stevenh@37 429
stevenh@37 430 // And an empty line to make it pretty
stevenh@37 431 System.out.println();
stevenh@37 432 }
stevenh@37 433 }