view 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 source
/*=============================================================================
 * 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(); 
    } 
}