diff MidiPlayer.java @ 0:4031cbb02f08

Initial import. Ignore-this: 87317e384f22bde48db996355191fa5f
author Marcus Pearce <m.pearce@gold.ac.uk>
date Tue, 18 May 2010 11:37:10 +0100
parents
children 93ed757b9871
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MidiPlayer.java	Tue May 18 11:37:10 2010 +0100
@@ -0,0 +1,304 @@
+/*=============================================================================
+ * File:       MidiPlayer.java
+ * Author:     Marcus Pearce <m.pearce@gold.ac.uk>
+ * Created:    <2007-02-14 12:13:56 marcusp>
+ * Time-stamp: <2008-10-20 16:52:05 marcusp>
+ *=============================================================================
+ */
+
+/* 
+ * Based on: 
+ *   http://www.jsresources.org/examples/SimpleMidiPlayer.html
+ *   http://www.jsresources.org/examples/DumpSequence.html
+ */ 
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList; 
+
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.MidiDevice; 
+import javax.sound.midi.InvalidMidiDataException;
+import javax.sound.midi.MidiUnavailableException;
+import javax.sound.midi.MetaEventListener;
+import javax.sound.midi.Sequence;
+import javax.sound.midi.Sequencer;
+import javax.sound.midi.Synthesizer;
+import javax.sound.midi.MidiChannel;
+import javax.sound.midi.Receiver;
+import javax.sound.midi.Transmitter;
+import javax.sound.midi.Track;
+import javax.sound.midi.MidiEvent;
+import javax.sound.midi.MidiMessage;
+import javax.sound.midi.ShortMessage;
+import javax.sound.midi.MetaMessage;
+import javax.sound.midi.SysexMessage;
+import javax.sound.midi.MetaMessage;
+
+public class MidiPlayer { 
+
+    /* variables */ 
+    private Sequencer   sequencer = null;
+    private Synthesizer synthesizer = null;
+    private Sequence sequence = null;
+    private ArrayList onsets, offsets, pitches = null; 
+    private int midiDevice = 0; /* 4 for usb midi device */ 
+
+    /* accessors */ 
+    public Sequencer getSequencer() { return sequencer; }
+
+    /* Constructor */ 
+    public MidiPlayer(String path) { 
+        File midiFile = new File(path);
+
+        // Get sequence 
+        try { sequence = MidiSystem.getSequence(midiFile); }
+        catch (InvalidMidiDataException e) {
+            e.printStackTrace();
+            System.exit(1);
+        }
+        catch (IOException e) {
+            e.printStackTrace();
+            System.exit(1);
+        }
+        // Get sequencer  
+        try { sequencer = MidiSystem.getSequencer(); }
+        catch (MidiUnavailableException e) {
+            e.printStackTrace();
+            System.exit(1);
+        }
+        //sequencer.setTempoInBPM(bpm); 
+        // Workaround bug in JDK 
+//         sequencer.addMetaEventListener(new MetaEventListener() {
+//                 public void meta(MetaMessage event) {
+//                     if (event.getType() == 47) {
+//                         sequencer.close();
+//                         if (synthesizer != null) {
+//                             synthesizer.close();
+//                         }
+//                     }
+//                 }
+//             });
+        // Open sequencer 
+        try { sequencer.open(); }
+        catch (MidiUnavailableException e) {
+            e.printStackTrace();
+            System.exit(1);
+        }
+        // Assign sequence to sequencer 
+        try { sequencer.setSequence(sequence); }
+        catch (InvalidMidiDataException e) {
+            e.printStackTrace();
+            System.exit(1);
+        }
+        // Set up MIDI output for sequence 
+        try {
+            MidiDevice.Info msinfo[] = MidiSystem.getMidiDeviceInfo(); 
+            for(int i = 0; i < msinfo.length; i++) { 
+                MidiDevice.Info m = msinfo[i]; 
+//                 System.out.println("Name: " + m.getName() + 
+//                                    "; Vendor: " + m.getVendor() + 
+//                                    "; Version: " + m.getVersion()); 
+            }
+            // synthesizer = MidiSystem.getSynthesizer();
+            MidiDevice synth = MidiSystem.getMidiDevice(msinfo[midiDevice]); 
+
+            // Change the patch 
+            //             MidiChannel channels[] = synthesizer.getChannels(); 
+            //             for (int i = 0; i < channels.length; i++) 
+            //                 channels[i].programChange(65);
+
+            synth.open();
+            Receiver synthReceiver = synth.getReceiver();
+            Transmitter	seqTransmitter = sequencer.getTransmitter();
+            seqTransmitter.setReceiver(synthReceiver);
+
+        }
+        catch (MidiUnavailableException e) {
+            e.printStackTrace();
+        }
+
+        // compute data from the MIDI file 
+        onsets = computeOnsets(); 
+        offsets = computeOffsets(); 
+        pitches = computePitches(); 
+
+        //String divisionType; 
+        //if (sequence.getDivisionType() == sequence.PPQ)
+        //    divisionType = "ppq"; 
+        //else 
+        //    divisionType = "smpte"; 
+        //System.out.println("division type = " + divisionType + 
+        //                   "; resolution = " + sequence.getResolution()); 
+    }
+
+    /* number of microseconds per MIDI tick */ 
+    private double microsecondsPerTick() { 
+        double seqTickLength = (float)sequence.getTickLength(); 
+        double seqMicrosecondLength = (float)sequence.getMicrosecondLength(); 
+        double microsecondsPerTick = seqMicrosecondLength / seqTickLength; 
+        //System.out.println("seqTickLength = " + seqTickLength); 
+        //System.out.println("seqMicrosecondLength = " + seqMicrosecondLength); 
+        //System.out.println("microsecondsPerTick = " + microsecondsPerTick); 
+        return microsecondsPerTick; 
+    }
+    
+    private long ticksToMicroseconds(long tick) { 
+        double microsecondsPerTick = microsecondsPerTick(); 
+        double seconds = (double)tick * microsecondsPerTick; 
+        
+        return (long)(10 * Math.floor(seconds * 0.1));
+    }
+
+    /* compute a list of note onset times (microseconds) */ 
+    private ArrayList computeOnsets() {
+        ArrayList ons = new ArrayList(); 
+        Track[]	tracks = sequence.getTracks();
+        
+        for (int nTrack = 0; nTrack < tracks.length; nTrack++) {
+            Track track = tracks[nTrack];
+            for (int nEvent = 0; nEvent < track.size(); nEvent++) {
+                MidiEvent event = track.get(nEvent);
+                MidiMessage message = event.getMessage();
+                if (message instanceof ShortMessage && 
+                    ((ShortMessage)message).getCommand() == ShortMessage.NOTE_ON) {
+//                               System.out.println("onset in ticks = " + event.getTick()+ 
+//                                                  "; onset in microseconds = " + 
+//                                                  ticksToMicroseconds(event.getTick()));
+                              ons.add(ticksToMicroseconds(event.getTick()));
+                }
+            }
+        } 
+        return ons; 
+    }
+
+    /* compute a list of note offset times (microseconds) */ 
+    private ArrayList computeOffsets() {
+        ArrayList offs = new ArrayList(); 
+        Track[]	tracks = sequence.getTracks();
+        
+        for (int nTrack = 0; nTrack < tracks.length; nTrack++) {
+            Track track = tracks[nTrack];
+            for (int nEvent = 0; nEvent < track.size(); nEvent++) {
+                MidiEvent event = track.get(nEvent);
+                MidiMessage message = event.getMessage();
+                if (message instanceof ShortMessage && 
+                    ((ShortMessage)message).getCommand() == ShortMessage.NOTE_OFF) {
+                    //System.out.println("offset in ticks = " + event.getTick()+ 
+                    //                   "; microsecondsPerTick = " + microsecondsPerTick +
+                    //                   "; offset in microseconds = " + 
+                    //                   (float)event.getTick() * microsecondsPerTick);
+                    offs.add(ticksToMicroseconds(event.getTick()));
+                }
+            }
+        }
+        return offs; 
+    }
+
+    /* compute a list of note pitches */ 
+    private ArrayList computePitches() {
+        ArrayList pit = new ArrayList(); 
+        Track[]	tracks = sequence.getTracks();
+        
+        for (int nTrack = 0; nTrack < tracks.length; nTrack++) {
+            Track track = tracks[nTrack];
+            for (int nEvent = 0; nEvent < track.size(); nEvent++) {
+                MidiEvent event = track.get(nEvent);
+                MidiMessage message = event.getMessage();
+                if (message instanceof ShortMessage && 
+                    ((ShortMessage)message).getCommand() == ShortMessage.NOTE_ON)
+                    pit.add(((ShortMessage)message).getData1()); 
+            }
+        } 
+        return pit; 
+    }
+
+    /* return a list of note onset times (milliseconds) */ 
+    public ArrayList getOnsets() { return onsets; } 
+
+    /* return a list of note offset times (milliseconds) */ 
+    public ArrayList getOffsets() { return offsets; } 
+
+    /* return a list of note pitches */ 
+    public ArrayList getPitches() { return pitches; } 
+    
+    /* return a list of note durations (microseconds) */ 
+    public ArrayList getDurations() { 
+        Object[] ons = onsets.toArray(); 
+        Object[] offs = offsets.toArray(); 
+        
+        ArrayList durations = new ArrayList(); 
+        
+        for(int i = 0; i < ons.length; i++) { 
+            durations.add(((Long)offs[i]).longValue() - 
+                          ((Long)ons[i]).longValue());
+        } 
+        return durations; 
+    } 
+
+    /* return a list of inter-onset intervals (microseconds) */ 
+    public ArrayList getInterOnsetIntervals() { 
+        Object[] ons = onsets.toArray(); 
+
+        ArrayList iois = new ArrayList(); 
+        long firstIOI = 0; 
+        iois.add(firstIOI); // IOI of first note is zero 
+        
+        for(int i = 1; i < ons.length; i++) { 
+            iois.add(((Long)ons[i]).longValue() - 
+                     ((Long)ons[i-1]).longValue());
+        } 
+        return iois; 
+    } 
+
+    /* return the length of a rest preceding a note */ 
+    public ArrayList getDeltaST() { 
+        Object[] durs = getDurations().toArray(); 
+        Object[] iois = getInterOnsetIntervals().toArray(); 
+        
+        ArrayList deltaST = new ArrayList(); 
+        long firstDeltaST = 0; 
+        deltaST.add(firstDeltaST); // deltaST of first note is zero 
+
+        for(int i = 1; i < durs.length; i++) { 
+            deltaST.add(((Long)durs[i-1]).longValue() - 
+                        ((Long)iois[i]).longValue());
+        }
+        return deltaST; 
+    } 
+    
+    /* return the tatum of the midi file */ 
+    public long getTatum() { 
+        Object[] durs = getDurations().toArray(); 
+        Object[] rests = getDeltaST().toArray(); 
+        long tatum = -1; 
+
+        for(int i = 0; i < durs.length; i++) { 
+            long dur = ((Long)durs[i]).longValue(); 
+            long rest = ((Long)rests[i]).longValue(); 
+            long min = dur; 
+            if (rest != 0)
+                min = Math.min(dur, rest);
+            
+            if (tatum < 0)
+                tatum = dur; 
+            else if (min < tatum) 
+                tatum = min; 
+        }
+        return tatum; 
+    }
+
+    /* return length of sequence in microseconds */ 
+    public long getLength() { 
+        return sequence.getMicrosecondLength(); 
+    } 
+
+    /* play the midi file */ 
+    public void play() { 
+        sequencer.start();
+    } 
+
+    public void stop() { 
+        sequencer.close(); 
+    } 
+}