Storing Midi Information


The Midi Sequences Loader

MidisLoader stores sequences as a collection of MidiInfo objects in a HashMap, keyed by their names. The name and filename for a sequence are obtained from an information file loaded when MidisLoader is created. The file is assumed to be in the subdirectory Sounds/.

MidisLoader allows a specified sequence to be played, stopped, resumed, and looped. A SoundsWatcher can be attached to the sequencer and not to a sequence. MidisLoader deliberately offers almost the same interface as ClipsLoader (see Figure 8-2), though it has some internal differences.

MidisLoader was designed to have one Sequencer object for playing all the sequences, which avoids the overhead of supporting multiple sequencers. Consequently, one sequence will play at a time. This contrasts with ClipsLoader, where multiple clips can be playing concurrently since multiple Clip objects are created by ClipsLoader. A reference to the sequencer is passed to each MidiInfo object, thereby giving them the responsibility for playing, stopping, resuming and looping their sequences.

The MidisLoader initializes the sequencer using initSequencer( ) and loads the information file:

     // globals     private Sequencer sequencer;     private HashMap midisMap;     private MidiInfo currentMidi = null;           // reference to currently playing MidiInfo object     public MidisLoader( )     { midisMap = new HashMap( );       initSequencer( );     }     public MidisLoader(String soundsFnm)     { midisMap = new HashMap( );       initSequencer( );       loadSoundsFile(soundsFnm);     }

The simpler versions of the constructor allow the loader to be created without an information file.


initSequencer( ) is similar to the version in PlayMidi.java in Chapter 7. loadSoundsFile( ) is similar to the same named method in ClipsLoader since it parses the information file, assuming each line contains a name and filename. For example, midisInfo.txt used by LoadersTests is

     // midis     baa bsheep.mid     farmer farmerinthedell.mid     mary maryhadalittlelamb.mid     mcdonald mcdonald.mid

The name can be any string. The file may contain blank lines and comment lines beginning with //.


After a line's name and filename have been extracted, load( ) is called:

     public void load(String name, String fnm)     // create a MidiInfo object, and store it under name     {       if (midisMap.containsKey(name))         System.out.println( "Error: " + name + "already stored");       else if (sequencer == null)         System.out.println( "No sequencer for: " + name);       else {         midisMap.put(name, new MidiInfo(name, fnm, sequencer) );         System.out.println("-- " + name + "/" + fnm);       }     }

This creates a MidiInfo object for the sequence and stores it in the midisMap HashMap. The last MidiInfo constructor argument is the sequencer.

Playing Sequences

Playing a sequence is a matter of looking up the specified name in midisMap and calling its play( ) method. A slight complication is that one sequence will play at a time, a restriction included in the loader design to reduce processing overheads. play( ) only plays the requested tune if no sequence is playing; a reference to that sequence is stored in the currentMidi global:

     public void play(String name, boolean toLoop)     // play (perhaps loop) the sequence     {        MidiInfo mi = (MidiInfo) midisMap.get(name);        if (mi == null)          System.out.println( "Error: " + name + "not stored");        else {          if (currentMidi != null)            System.out.println("Sorry, " + currentMidi.getName( ) + " already playing");          else {           currentMidi = mi;  // store a reference to playing midi           mi.play(toLoop);   // pass play request to MidiInfo object          }        }     }

Playing is prohibited if currentMidi is not null, which means that a sequence is playing.

Pausing and Resuming Sequences

Pausing and resuming is handled by passing the tasks to the playing MidiInfo object:

     public void pause( )     { if (currentMidi != null)         currentMidi.pause( );       else         System.out.println( "No music to pause");     }         public void resume( )     { if (currentMidi != null)     currentMidi.resume( );       else         System.out.println("No music to resume");     }

Stopping Sequences

Stopping a sequence uses the same delegation strategy as pausing and resuming. The stop( ) method in MidisInfo will trigger an end-of-track metaevent in the sequencer, which is handled by MidisLoader's meta( ) method:

     public void stop( )     { if (currentMidi != null)         currentMidi.stop( );  // this will cause an end-of-track event       System.out.println("No music playing");     }     public void meta(MetaMessage meta)     {       if (meta.getType( ) == END_OF_TRACK) {         String name = currentMidi.getName( );         boolean hasLooped = currentMidi.tryLooping( );  // music still looping?         if (!hasLooped)   // no it's finished           currentMidi = null;         if (watcher != null) {   // tell the watcher           if (hasLooped)        // the music is playing again             watcher.atSequenceEnd(name, SoundsWatcher.REPLAYED);           else                  // the music has finished             watcher.atSequenceEnd(name, SoundsWatcher.STOPPED);         }       }     } // end of meta( )

The code in meta( ) only deals with an end-of-track metaevent. These end-of-track events are triggered by a MidiInfo object when its sequence reaches its end or is stopped. However, a sequence at its end may be looping, which is checked by calling tryLooping( ) in MidiInfo. If there is a watcher, that watcher is notified of the status of the sequence.

Closing Sequences

As LoadersTests terminates, it calls close( ) in MidisLoader to release the sequencer:

     public void close( )     {       stop( );    // stop the playing sequence       if (sequencer != null) {         if (sequencer.isRunning( ))           sequencer.stop( );         sequencer.removeMetaEventListener(this);         sequencer.close( );         sequencer = null;       }     }



Killer Game Programming in Java
Killer Game Programming in Java
ISBN: 0596007302
EAN: 2147483647
Year: 2006
Pages: 340

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net