/******************************************************************************/
// Play all the notes in the MIDI range in all available MIDI channels on the
// system, printing note-channel information to the screen for every note.
//
// Roy Vanegas: rvanegas@hunter.cuny.edu
/******************************************************************************/

import javax.sound.midi.*;

public class PlayEntireRangeOfMIDINotes
{
   public static void main( String[] args )
   {
      /**
       * The maximum MIDI value for both note and velocity is 128.  Thus, we 
       * can create one constant with a value of 128 and use this value for both
       * note events (as a cap when we iterate over all MIDI notes) and velocity
       * events (to simply create a constant velocity of 64).
       */      
      final int MAX_MIDI_VALUE = 128;
      int note;
      final int VELOCITY = (MAX_MIDI_VALUE / 2);
      
      int channel = 0;

      try
      {
         // Acquire the default synthesizer, then open it.
         Synthesizer synth = MidiSystem.getSynthesizer();
         synth.open();
         
         /**
          * Method getChannels in interface Synthesizer obtains the set of MIDI
          * channels controlled by this synthesizer.
          */
         MidiChannel[] midiChannels = synth.getChannels();

         /**
          * Let's cycle through all possible 128 MIDI notes, starting with 0, 
          * which is C-1 (middle C is 60).  As we iterate over the MIDI scale, 
          * we'll do the following.
          *
          * 1. Calculate a number between 0 and 15 for the MIDI channel, of 
          *    which there are usually 16.
          * 2. Turn on the MIDI note, which is also our loop counter, with the
          *    volume, or velocity, associated with the channel variable in our
          *    array of MIDI channels.
          * 3. To allow our note to last, we'll put the current thread to sleep 
          *    for 100 milliseconds.
          * 4. For the purposes of printing the correct note to the screen, 
          *    we'll calculate a modulo value for the current note that is 
          *    playing.  For example, every multiple of 12 is a C note, every
          *    multiple of 13 is a C#/Db note, etc.
          * 5. We call the switch case for every note whose modulo we have 
          *    calculated and print the correct note to the screen.
          */
         for( int midiNote = 0; midiNote < MAX_MIDI_VALUE; midiNote++ )
         {
            channel = (midiNote % 16);

            midiChannels[ channel ].noteOn( midiNote, VELOCITY );

            Thread.sleep( 100 );    

            note = (midiNote % 12);
	     
            switch( note )
            {
               case 0:
                  System.out.print( "c     " );
                  break; 
               case 1:
                  System.out.print( "c#/db " );
                  break;
               case 2:
                  System.out.print( "d     " );
                  break;
               case 3:
                  System.out.print( "d#/eb " );
                  break;
               case 4:
                  System.out.print( "e     " );
                  break;
               case 5:
                  System.out.print( "f     " );
                  break;
               case 6:
                  System.out.print( "f#/gb " );
                  break;
               case 7:
                  System.out.print( "g     " );
                  break;
               case 8:
                  System.out.print( "g#/ab " );
                  break;
               case 9:
                  System.out.print( "a     " );
                  break;
               case 10:
                  System.out.print( "a#/bb " );
                  break;
               case 11:
                  System.out.print( "b     " );
                  break;
               default:
                  break;
            }
            
            System.out.println( " on MIDI channel\t" + channel );
         }

         /**
          * We've exited the "for" loop and should have one last note playing.  
          * To ensure that the note rings and doesn't halt abruptly, we put the 
          * current thread to sleep for a short while.
          */
         Thread.sleep( 2000 ); // Let the last note ring
         synth.close();
         System.out.println();
      }
      catch( MidiUnavailableException e ){}
      catch( InterruptedException ie ){}

   System.exit( 0 );
   }
}
