Coverage Report - net.cscott.sdr.sound.MidiDemo
 
Classes in this File Line Coverage Branch Coverage Complexity
MidiDemo
0%
0/35
0%
0/14
5.5
MidiDemo$1
0%
0/6
0%
0/4
5.5
 
 1  
 package net.cscott.sdr.sound;
 2  
 /*
 3  
  *        Derived from LoopingMidiPlayer15.java, from jsresources.org
 4  
  *      Requires JDK1.5 or later.
 5  
  */
 6  
 
 7  
 /* Original file copyright (c) 1999 - 2001 by Matthias Pfisterer
 8  
  * All rights reserved.
 9  
  *
 10  
  * Redistribution and use in source and binary forms, with or without
 11  
  * modification, are permitted provided that the following conditions
 12  
  * are met:
 13  
  *
 14  
  * - Redistributions of source code must retain the above copyright notice,
 15  
  *   this list of conditions and the following disclaimer.
 16  
  * - Redistributions in binary form must reproduce the above copyright
 17  
  *   notice, this list of conditions and the following disclaimer in the
 18  
  *   documentation and/or other materials provided with the distribution.
 19  
  *
 20  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 21  
  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 22  
  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 23  
  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 24  
  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 25  
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 26  
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 27  
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 28  
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 29  
  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 30  
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 31  
  * OF THE POSSIBILITY OF SUCH DAMAGE.
 32  
  */
 33  
 
 34  
 /* for our test files, tempo is 120bpm, and resolution is 384 PPQ.  This
 35  
  * means that a midi tick is 1/768s, which is more than adequate resolution
 36  
  * for (say) 15-30fps animation. */
 37  
 
 38  
 import java.io.IOException;
 39  
 
 40  
 import javax.sound.midi.InvalidMidiDataException;
 41  
 import javax.sound.midi.MetaEventListener;
 42  
 import javax.sound.midi.MetaMessage;
 43  
 import javax.sound.midi.MidiSystem;
 44  
 import javax.sound.midi.MidiUnavailableException;
 45  
 import javax.sound.midi.Receiver;
 46  
 import javax.sound.midi.Sequence;
 47  
 import javax.sound.midi.Sequencer;
 48  
 import javax.sound.midi.ShortMessage;
 49  
 import javax.sound.midi.Soundbank;
 50  
 import javax.sound.midi.Synthesizer;
 51  
 import javax.sound.midi.Transmitter;
 52  
 
 53  
 
 54  
 
 55  
 
 56  
 /**        <titleabbrev>MidiDemo</titleabbrev>
 57  
         <title>Looping a MIDI file (JDK1.5 and later)</title>
 58  
 
 59  
         <formalpara><title>Purpose</title>
 60  
 
 61  
         <para>Loops a MIDI file using the new looping methods in the
 62  
         Sequencer of the JDK1.5.</para></formalpara>
 63  
 
 64  
         <formalpara><title>Usage</title>
 65  
         <para>
 66  
         <cmdsynopsis>
 67  
         <command>java MidiDemo</command>
 68  
         <arg choice="plain"><replaceable>midifile</replaceable></arg>
 69  
         </cmdsynopsis>
 70  
         </para></formalpara>
 71  
 
 72  
         <formalpara><title>Parameters</title>
 73  
         <variablelist>
 74  
         <varlistentry>
 75  
         <term><option><replaceable>midifile</replaceable></option></term>
 76  
         <listitem><para>the name of the MIDI file that should be
 77  
         played</para></listitem>
 78  
         </varlistentry>
 79  
         </variablelist>
 80  
         </formalpara>
 81  
 
 82  
         <formalpara><title>Bugs, limitations</title>
 83  
 
 84  
         <para>This program always uses the default Sequencer and the default
 85  
         Synthesizer to play on. For using non-default sequencers,
 86  
         synthesizers or to play on an external MIDI port, see
 87  
         <olink targetdoc="MidiPlayer"
 88  
         targetptr="MidiPlayer">MidiPlayer</olink>.</para>
 89  
 
 90  
         <para>This program requires the JDK 1.5. For looping using the JDK
 91  
         1.4 or earlier, see <olink targetdoc="LoopingMidiPlayer14"
 92  
         targetptr="LoopingMidiPlayer14">LoopingMidiPlayer14</olink>.</para>
 93  
 
 94  
         </formalpara>
 95  
 
 96  
         <formalpara><title>Source code</title>
 97  
         <para>
 98  
         <ulink url="MidiDemo.java.html">MidiDemo.java</ulink>
 99  
         </para>
 100  
         </formalpara>
 101  
 
 102  
 */
 103  0
 public class MidiDemo
 104  
 {
 105  
     public static void main(String[] args)
 106  
         throws MidiUnavailableException, InvalidMidiDataException, IOException
 107  
     {
 108  
         final Sequencer sequencer;
 109  
         final Synthesizer synthesizer;
 110  
 
 111  
         /* Use high-quality soundbank. */
 112  0
         Soundbank soundbank = MidiSystem.getSoundbank
 113  
             (MidiDemo.class.getClassLoader().getResource
 114  
              ("net/cscott/sdr/sound/soundbank-deluxe.gm"));
 115  
         //soundbank=null;
 116  
 
 117  
         /* We read in the MIDI file to a Sequence object.  This object
 118  
          * is set at the Sequencer later.
 119  
          */
 120  0
         Sequence sequence = MidiSystem.getSequence
 121  
             (MidiDemo.class.getClassLoader().getResource
 122  
              ("net/cscott/sdr/sound/saturday-night.midi"));
 123  
 
 124  
         // print out some info about timing resolution.
 125  0
         System.out.println("Division type: "+sequence.getDivisionType());
 126  0
         System.out.println("Resolution: "+sequence.getResolution());
 127  
         assert sequence.getDivisionType()==Sequence.PPQ :
 128  0
             "don't know how to sync non-PPQ tracks";
 129  0
         @SuppressWarnings("unused") int ticksPerBeat = sequence.getResolution();
 130  
 
 131  
         /* Now, we need a Sequencer to play the sequence.  Here, we
 132  
          * simply request the default sequencer without an implicitly
 133  
          * connected synthesizer
 134  
          */
 135  0
         sequencer = MidiSystem.getSequencer(false);
 136  
 
 137  
         /* The Sequencer is still a dead object.  We have to open() it
 138  
          * to become live.  This is necessary to allocate some
 139  
          * ressources in the native part.
 140  
          */
 141  0
         sequencer.open();
 142  
 
 143  
         /* Next step is to tell the Sequencer which Sequence it has to
 144  
          * play. In this case, we set it as the Sequence object
 145  
          * created above.
 146  
          */
 147  0
         sequencer.setSequence(sequence);
 148  0
         System.out.println("Initial tempo: "+sequencer.getTempoInMPQ()+" MPQ");
 149  
         // Mississippi Sawyer is *half note*=120; Java doesn't seem to grok
 150  
         // this.  Something like the following is needed for these cases:
 151  
         // sequencer.setTempoFactor(2f);
 152  
 
 153  
         /* We try to get the default synthesizer, open() it and chain
 154  
          * it to the sequencer with a Transmitter-Receiver pair.
 155  
          */
 156  0
         synthesizer = MidiSystem.getSynthesizer();
 157  0
         synthesizer.open();
 158  0
         boolean loaded = false;
 159  0
         if (soundbank!=null)
 160  0
             loaded = synthesizer.loadAllInstruments(soundbank);
 161  0
         System.out.println("Instruments "+(loaded?"":"not ")+"loaded.");
 162  0
         Receiver        synthReceiver = synthesizer.getReceiver();
 163  0
         Transmitter        seqTransmitter = sequencer.getTransmitter();
 164  0
         seqTransmitter.setReceiver(synthReceiver);
 165  
 
 166  
         /* To free system resources, it is recommended to close the
 167  
          * synthesizer and sequencer properly.
 168  
          *
 169  
          * To accomplish this, we register a Listener to the
 170  
          * Sequencer. It is called when there are "meta" events. Meta
 171  
          * event 47 is end of track.
 172  
          *
 173  
          * Thanks to Espen Riskedal for finding this trick.
 174  
          */
 175  0
         sequencer.addMetaEventListener(new MetaEventListener()
 176  0
             {
 177  
                 public void meta(MetaMessage event)
 178  
                 {
 179  0
                     if (event.getType() == 47)
 180  
                         {
 181  0
                             sequencer.close();
 182  0
                             if (synthesizer != null)
 183  
                                 {
 184  0
                                     synthesizer.close();
 185  
                                 }
 186  
                             //System.exit(0);
 187  
                         }
 188  0
                 }
 189  
             });
 190  
 
 191  
         /* Here, we set the loop points to loop over the whole
 192  
          * sequence. Setting the loop end point to -1 means using the
 193  
          * last tick of the sequence as end point of the loop.
 194  
          *
 195  
          * Furthermore, we set the number of loops to loop infinitely.
 196  
          */
 197  0
         sequencer.setLoopStartPoint(0);
 198  0
         sequencer.setLoopEndPoint(-1);
 199  0
         sequencer.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
 200  
 
 201  
         /* Now, we can start over.
 202  
          */
 203  0
         sequencer.start();
 204  
 
 205  
         /* WORKAROUND for BUG in jdk 1.6 (at least): loop control has an
 206  
          * off-by-one error and doesn't play the (usually note off) messages
 207  
          * in the last tick of the song; it just jumps to tick 0 and plays
 208  
          * the note-on messages there.  Workaround this by manually kludging
 209  
          * in ALL_NOTE_OFF messages on all channels one tick before the end
 210  
          * of the song.  Sigh. */
 211  0
         for (int i=0; i<16; i++) {
 212  0
             javax.sound.midi.ShortMessage notesOff =
 213  
                 new javax.sound.midi.ShortMessage();
 214  0
             notesOff.setMessage(ShortMessage.CONTROL_CHANGE+i,123,0);
 215  0
             for (javax.sound.midi.Track t : sequence.getTracks()) {
 216  0
                 long end = (t.ticks()<=0)?0L:(t.ticks()-1);
 217  0
                 t.add(new javax.sound.midi.MidiEvent(notesOff, end));
 218  
             }
 219  
         }
 220  
 
 221  
         /* check timer */
 222  0
         OldMidiTimer mt = new OldMidiTimer(sequencer);
 223  
         while(true) {
 224  0
             System.out.println(mt.getTime());
 225  
             try {
 226  0
                 Thread.sleep(1000);
 227  0
             } catch (InterruptedException ie) { /* ignore */ }
 228  
         }
 229  
     }
 230  
 }
 231  
 
 232  
 
 233  
 
 234  
 /*** MidiDemo.java ***/
 235