Mercurial > hg > mep
view MidiPlayer.java @ 23:9fc8683b8fed
Fixed GUI button presses, had their background change as feedback and added ability to record responses for a probe tone at the end of a sequence
author | JShulver |
---|---|
date | Wed, 07 Nov 2012 18:22:01 +0000 |
parents | 5c54f5213f3d |
children | 7102e646b223 |
line wrap: on
line source
/*============================================================================= * File: MidiPlayer.java * Author: Marcus Pearce <m.pearce@gold.ac.uk> * Created: <2007-02-14 12:13:56 marcusp> * Time-stamp: <2011-11-15 16:52:06 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; import java.util.Arrays; 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, int deviceNumber) { File midiFile = new File(path); midiDevice = deviceNumber; // 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())); //if the event does not have a velocity of zero (i.e. switching a note off) if(message.getMessage()[2] != 0) { ons.add(ticksToMicroseconds(event.getTick())); //THEN we can add the note } } } } 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())); } //if we have not found a note off, else if (message instanceof ShortMessage && ((ShortMessage)message).getCommand() == ShortMessage.NOTE_ON && message.getMessage()[2] == 0) { //but it is a note on with a velocity of zero offs.add(ticksToMicroseconds(event.getTick())); //add it as an off signal } } } 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); System.out.println("Min: " + min); if (tatum < 0) tatum = dur; else if (min < tatum) tatum = min; } //tatum = sequence.getResolution()*1000; return tatum; } /* return length of sequence in microseconds */ public long getLength() { return sequence.getMicrosecondLength(); } /* play the midi file */ public void play() { //System.out.println("MidiPlayer.play: run sequencer.start()."); sequencer.start(); } public void stop() { if (synthesizer != null) { synthesizer.close(); } if (sequencer != null) { sequencer.close(); } sequencer = null; synthesizer = null; } }