Mega Code Archive

 
Categories / Java by API / Javax Sound Midi
 

New MidiEvent(MidiMessage message, long tick)

/*  * Copyright (c) 2004 David Flanagan.  All rights reserved.  * This code is from the book Java Examples in a Nutshell, 3nd Edition.  * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.  * You may study, use, and modify it for any non-commercial purpose,  * including teaching and use in open-source projects.  * You may distribute it non-commercially as long as you retain this notice.  * For a commercial use license, or to purchase the book,   * please visit http://www.davidflanagan.com/javaexamples3.  */ import java.io.File; import java.io.IOException; import javax.sound.midi.InvalidMidiDataException; import javax.sound.midi.MetaEventListener; import javax.sound.midi.MetaMessage; import javax.sound.midi.MidiEvent; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Sequence; import javax.sound.midi.Sequencer; import javax.sound.midi.ShortMessage; import javax.sound.midi.Synthesizer; import javax.sound.midi.Track; public class Main {   // These are some MIDI constants from the spec. They aren't defined   // for us in javax.sound.midi.   public static final int DAMPER_PEDAL = 64;   public static final int DAMPER_ON = 127;   public static final int DAMPER_OFF = 0;   public static final int END_OF_TRACK = 47;   public static void main(String[] args) throws MidiUnavailableException, InvalidMidiDataException,       IOException {     int instrument = 0;     int tempo = 120;     String filename = null;     // Parse the options     // -i <instrument number> default 0, a piano. Allowed values: 0-127     // -t <beats per minute> default tempo is 120 quarter notes per minute     // -o <filename> save to a midi file instead of playing     int a = 0;     while (a < args.length) {       if (args[a].equals("-i")) {         instrument = Integer.parseInt(args[a + 1]);         a += 2;       } else if (args[a].equals("-t")) {         tempo = Integer.parseInt(args[a + 1]);         a += 2;       } else if (args[a].equals("-o")) {         filename = args[a + 1];         a += 2;       } else         break;     }     char[] notes = args[a].toCharArray();     // 16 ticks per quarter note.     Sequence sequence = new Sequence(Sequence.PPQ, 16);     // Add the specified notes to the track     addTrack(sequence, instrument, tempo, notes);     if (filename == null) { // no filename, so play the notes       // Set up the Sequencer and Synthesizer objects       Sequencer sequencer = MidiSystem.getSequencer();       sequencer.open();       Synthesizer synthesizer = MidiSystem.getSynthesizer();       synthesizer.open();       sequencer.getTransmitter().setReceiver(synthesizer.getReceiver());       // Specify the sequence to play, and the tempo to play it at       sequencer.setSequence(sequence);       sequencer.setTempoInBPM(tempo);       // Let us know when it is done playing       sequencer.addMetaEventListener(new MetaEventListener() {         public void meta(MetaMessage m) {           // A message of this type is automatically sent           // when we reach the end of the track           if (m.getType() == END_OF_TRACK)             System.exit(0);         }       });       // And start playing now.       sequencer.start();     } else { // A file name was specified, so save the notes       int[] allowedTypes = MidiSystem.getMidiFileTypes(sequence);       if (allowedTypes.length == 0) {         System.err.println("No supported MIDI file types.");       } else {         MidiSystem.write(sequence, allowedTypes[0], new File(filename));         System.exit(0);       }     }   }   static final int[] offsets = { // add these amounts to the base value   // A B C D E F G       -4, -2, 0, 1, 3, 5, 7 };   /*    * This method parses the specified char[] of notes into a Track. The musical    * notation is the following: A-G: A named note; Add b for flat and # for    * sharp. +: Move up one octave. Persists. -: Move down one octave. Persists.    * /1: Notes are whole notes. Persists 'till changed /2: Half notes /4:    * Quarter notes /n: N can also be, 8, 16, 32, 64. s: Toggle sustain pedal on    * or off (initially off)    *  >: Louder. Persists <: Softer. Persists .: Rest. Length depends on current    * length setting Space: Play the previous note or notes; notes not separated    * by spaces are played at the same time    */   public static void addTrack(Sequence s, int instrument, int tempo, char[] notes)       throws InvalidMidiDataException {     Track track = s.createTrack(); // Begin with a new track     // Set the instrument on channel 0     ShortMessage sm = new ShortMessage();     sm.setMessage(ShortMessage.PROGRAM_CHANGE, 0, instrument, 0);     track.add(new MidiEvent(sm, 0));     int n = 0; // current character in notes[] array     int t = 0; // time in ticks for the composition     // These values persist and apply to all notes 'till changed     int notelength = 16; // default to quarter notes     int velocity = 64; // default to middle volume     int basekey = 60; // 60 is middle C. Adjusted up and down by octave     boolean sustain = false; // is the sustain pedal depressed?     int numnotes = 0; // How many notes in current chord?     while (n < notes.length) {       char c = notes[n++];       if (c == '+')         basekey += 12; // increase octave       else if (c == '-')         basekey -= 12; // decrease octave       else if (c == '>')         velocity += 16; // increase volume;       else if (c == '<')         velocity -= 16; // decrease volume;       else if (c == '/') {         char d = notes[n++];         if (d == '2')           notelength = 32; // half note         else if (d == '4')           notelength = 16; // quarter note         else if (d == '8')           notelength = 8; // eighth note         else if (d == '3' && notes[n++] == '2')           notelength = 2;         else if (d == '6' && notes[n++] == '4')           notelength = 1;         else if (d == '1') {           if (n < notes.length && notes[n] == '6')             notelength = 4; // 1/16th note           else             notelength = 64; // whole note         }       } else if (c == 's') {         sustain = !sustain;         // Change the sustain setting for channel 0         ShortMessage m = new ShortMessage();         m             .setMessage(ShortMessage.CONTROL_CHANGE, 0, DAMPER_PEDAL, sustain ? DAMPER_ON                 : DAMPER_OFF);         track.add(new MidiEvent(m, t));       } else if (c >= 'A' && c <= 'G') {         int key = basekey + offsets[c - 'A'];         if (n < notes.length) {           if (notes[n] == 'b') { // flat             key--;             n++;           } else if (notes[n] == '#') { // sharp             key++;             n++;           }         }         addNote(track, t, notelength, key, velocity);         numnotes++;       } else if (c == ' ') {         // Spaces separate groups of notes played at the same time.         // But we ignore them unless they follow a note or notes.         if (numnotes > 0) {           t += notelength;           numnotes = 0;         }       } else if (c == '.') {         // Rests are like spaces in that they force any previous         // note to be output (since they are never part of chords)         if (numnotes > 0) {           t += notelength;           numnotes = 0;         }         // Now add additional rest time         t += notelength;       }     }   }   // A convenience method to add a note to the track on channel 0   public static void addNote(Track track, int startTick, int tickLength, int key, int velocity)       throws InvalidMidiDataException {     ShortMessage on = new ShortMessage();     on.setMessage(ShortMessage.NOTE_ON, 0, key, velocity);     ShortMessage off = new ShortMessage();     off.setMessage(ShortMessage.NOTE_OFF, 0, key, velocity);     track.add(new MidiEvent(on, startTick));     track.add(new MidiEvent(off, startTick + tickLength));   } }