import javax.sound.midi.MidiChannel; import javax.sound.midi.MidiSystem; import javax.sound.midi.Synthesizer; import javax.sound.midi.Sequence; import javax.sound.midi.Sequencer; import java.io.File; /** * Plays one or more notes through Midi. If you find yourself wanting to use lots of individual * sound files for a scenario, such as emulating an instrument for example, you may find using * midi a better choice. It has several advantages over using lots of wav files: * * However, Midi is not always suited to the task - the sounds aren't always the same on each * computer (since they're generated through the sound card and not in advance) and the quality * won't be as good as a recorded sound. If you're looking for special effects, a backing track * or in game sounds then chances are you'll be better off using normal sound files. * * @author Michael Berry (mjrb4) * @version 12/02/09 */ public class MidiPlayer { /** The default instrument to use if one isn't specified in the constructor.*/ public static int DEFAULT_INSTRUMENT = 4; /** The channel used to play the Midi notes. */ private MidiChannel channel; /** A sequencer used to play Midi files. */ private static Sequencer sequencer; /** * Try to set the seqencer up. */ { try { sequencer = MidiSystem.getSequencer(); } catch(Exception ex) { ex.printStackTrace(); } } /** * Create a new MidiPlayer object with a default * instrument specified by DEFAULT_INSTRUMENT - * usually a piano sound. */ public MidiPlayer() { channel = getChannel(DEFAULT_INSTRUMENT); } /** * Create a new MidiPlayer object with a specified * instrument. * @param instrument the instrument to use */ public MidiPlayer(int instrument) { channel = getChannel(instrument); } /** * Change the instrument this MidiPlayer uses. * @param instrument the instrument to change to */ public void setInstrument(int instrument) { channel.programChange(instrument); } /** * Get the instrument the MidiPlayer is using * at present. * @return the instrument in use. */ public int getInstrument() { return channel.getProgram(); } /** * Converts a string description of the key * to the number used by the MidiPlayer. * @throws RuntimeException if the key name is invalid. */ public int getNumber(String key) { try { String note = new Character(key.charAt(0)).toString(); boolean accidental = false; if(key.charAt(1)=='b') { note += "b"; accidental = true; } else if(key.charAt(1)=='#') { note += "#"; accidental = true; } int offset = 1; if(accidental) offset = 2; int number = Integer.parseInt(key.substring(offset)); int midiNum = (number+1)*12; midiNum += getOffset(note); return midiNum; } catch(Exception ex) { throw new RuntimeException(key + " is an invalid key name..."); } } /** * Set how far each note is (relatively in semitones) above C. */ private int getOffset(String note) { if(note.equalsIgnoreCase("C")) return 0; if(note.equalsIgnoreCase("C#")) return 1; if(note.equalsIgnoreCase("Db")) return 1; if(note.equalsIgnoreCase("D")) return 2; if(note.equalsIgnoreCase("D#")) return 3; if(note.equalsIgnoreCase("Eb")) return 3; if(note.equalsIgnoreCase("E")) return 4; if(note.equalsIgnoreCase("E#")) return 5; if(note.equalsIgnoreCase("F")) return 5; if(note.equalsIgnoreCase("F#")) return 6; if(note.equalsIgnoreCase("Gb")) return 6; if(note.equalsIgnoreCase("G")) return 7; if(note.equalsIgnoreCase("G#")) return 8; if(note.equalsIgnoreCase("Ab")) return 8; if(note.equalsIgnoreCase("A")) return 9; if(note.equalsIgnoreCase("A#")) return 10; if(note.equalsIgnoreCase("Bb")) return 10; if(note.equalsIgnoreCase("B")) return 11; if(note.equalsIgnoreCase("Cb")) return 11; else throw new RuntimeException(); } /** * Play a note - this method doesn't turn the note * off after a specified period of time, the release * method must be called to do that. * @param note the note to play */ public void play(final int note) { channel.noteOn(note, 50); } /** * Release a note that was previously played. If this * note isn't on already, this method will do nothing. */ public void release(final int note) { channel.noteOff(note, 50); } /** * Play a note for a certain amount of time. * @param note the integer value for the note to play * @param length the length to play the note (ms). */ public void play(final int note, final int length) { new Thread() { public void run() { channel.noteOn(note, 50); try { Thread.sleep(length); } catch(InterruptedException ex) {} finally { channel.noteOff(note, 50); } channel.noteOff(note, 50); } }.start(); } /** * Release all notes smoothly. This can be called in the World.stopped() * method to ensure no notes are playing when the scenario has been * stopped or reset. */ public void turnAllOff() { try { channel.allNotesOff(); //This would turn cut notes of immediately and suddenly: //channel.allSoundOff(); sequencer.stop(); sequencer.close(); } catch(Exception ex) {} } /** * Get the MidiChannel object that this MidiPlayer class is using. * If you want to do some more advanced work with the midi channel, you * can use this method to get the MidiChannel object and then work with * it directly. The API for the MidiChannel class is available online * as part of the javax.sound.midi package.
* Examples of why you might want to use this - adjusting the speed * notes are played / released with, adding sustain, adding pitch * bend, soloing / muting individual channels - all are fairly advanced * features and as such are not included in this class as standard (to * keep things simple and avoid clutter.) * @return the MidiChannel object behind this MidiPlayer. */ public MidiChannel getMidiChannel() { return channel; } /** * Play a Midi file. */ public static void playMidiFile(String fileName) { try { Sequence sequence = MidiSystem.getSequence(new File(fileName)); sequencer.open(); sequencer.setSequence(sequence); sequencer.start(); } catch(Exception ex) { ex.printStackTrace(); } } /** * Stop playing a Midi file. */ public static void stopMidiFile() { sequencer.stop(); } /** * Internal method to get the channel from the synthesizer in use. * @param instrument the instrument to load initially. */ private MidiChannel getChannel(int instrument) { try { Synthesizer synthesizer = MidiSystem.getSynthesizer(); synthesizer.open(); for (int i=0; i