www.jpct.net

jPCT - a 3d engine for Java => Support => Topic started by: AGP on February 03, 2009, 05:48:13 pm

Title: Midi Volume (Not jPCT But Java)
Post by: AGP on February 03, 2009, 05:48:13 pm
I've read everything on the subject that I could find by searching Google. Figured somebody here might know. I cannot, for the life of me, change the volume of the midi playback in my program. The following is my code. The last part (before the call to start()) is my attempt at discovering which Synthesizer is attached to the Sequencer. And the very last nested loop was an attempt to change everyone's volume. By the way, if my test for synthesizer.getDefaultSoundbank() == null is valid, I'm using the software synthesizer on my computer.

Code: [Select]
        private void playMidi(String fileName) {
try {
     midiPlayer = MidiSystem.getSequencer(true);
     Synthesizer synthesizer = MidiSystem.getSynthesizer();
     midiPlayer.open();
     synthesizer.open();
     Transmitter transmitter = midiPlayer.getTransmitter();

     if (synthesizer.getDefaultSoundbank() == null) {//MEANING "IF USING HARDWARE (NOT JRE) SYNTHESIZER"
transmitter.setReceiver(MidiSystem.getReceiver());//THIS IS RIGHT FOR HARDWARE
System.out.println("Hardware synthesizer!");
     }
     else {
transmitter.setReceiver(synthesizer.getReceiver());//SYNTHESIZER.GETRECEIVER() IS RIGHT FOR SOFTWARE

System.out.println("Software synthesizer in use!");
     }

     midiPlayer.setSequence(MidiSystem.getSequence(new File(fileName)));

     ShortMessage volumeMessage = new ShortMessage();
     int numberOfChannels = synthesizer.getChannels().length;
     for (int i = 0; i < numberOfChannels; i++) {
volumeMessage.setMessage(ShortMessage.CONTROL_CHANGE, i, 7, 5); //7 IS MASTER VOLUME CONTROLLER, 5 IS VOLUME BETWEEN 0-127
transmitter.getReceiver().send(volumeMessage, -1);
     }
System.out.println("Is the sequencer an instance of Synthesizer? "+(midiPlayer instanceof Synthesizer));

     MidiDevice device = MidiSystem.getMidiDevice(midiPlayer.getDeviceInfo());
     java.util.List<Transmitter> transmitters = device.getTransmitters();
     Transmitter[] transmitterArray = new Transmitter[transmitters.size()];
     transmitterArray = transmitters.toArray(transmitterArray);

for (int y = 0; y < transmitterArray.length; y++) {
for (int x = 0; x < 16; x++) {
volumeMessage.setMessage(ShortMessage.CONTROL_CHANGE, y, 7, 5); //7 IS MASTER VOLUME CONTROLLER, 5 IS VOLUME BETWEEN 0-127
transmitterArray[y].getReceiver().send(volumeMessage, -1);
}
}

     MidiChannel[] channels = synthesizer.getChannels();
     for (int i = 0; i < channels.length; i++){
channels[i].controlChange(7, 5);
System.out.println("Can we lower the volume? "+(channels[i].getController(7)!=0));}

     midiPlayer.start();
}
catch (Exception e) {System.out.println("Midi File Error: "+e.getMessage());}
Title: Re: Midi Volume (Not jPCT But Java)
Post by: paulscode on February 03, 2009, 09:14:34 pm
I have been using the following method for changing MIDI volume, which seems to work for me:

Code: [Select]
/**
 * Resets playback volume to the correct level.
 */
    public void resetGain()
    {
        // make sure the value for gain is valid (between 0 and 1)
        if( gain < 0.0f )
            gain = 0.0f;
        if( gain > 1.0f )
            gain = 1.0f;
       
        int midiVolume = (int) ( gain * SoundSystemConfig.getMasterGain()
                                 * 127.0f );
        if( synthesizer != null )
        {
            javax.sound.midi.MidiChannel[] channels = synthesizer.getChannels();
            for( int c = 0; channels != null && c < channels.length; c++ )
            {
                channels[c].controlChange( CHANGE_VOLUME, midiVolume );
            }
        }
        else if( sequencer != null && sequencer instanceof Synthesizer )
        {
            synthesizer = (Synthesizer) sequencer;
            javax.sound.midi.MidiChannel[] channels = synthesizer.getChannels();
            for( int c = 0; channels != null && c < channels.length; c++ )
            {
                channels[c].controlChange( CHANGE_VOLUME, midiVolume );
            }
        }
        else
        {
            try
            {
                Receiver receiver = MidiSystem.getReceiver();
                ShortMessage volumeMessage= new ShortMessage();
                for( int c = 0; c < 16; c++ )
                {
                    volumeMessage.setMessage( ShortMessage.CONTROL_CHANGE, c,
                                              CHANGE_VOLUME, midiVolume );
                    receiver.send( volumeMessage, -1 );
                }
            }
            catch( Exception e )
            {
                errorMessage( "Error resetting gain for MIDI source" );
                printStackTrace( e );
            }
        }
    }

In this code, 'gain' is a float between 0.0f and 1.0f, 'sequencer' was created from MidiSystem.getSequencer(), and 'synthesizer' was created from MidiSystem.getSynthesizer() if 'sequencer' was not an instance of Synthesizer.  For reference, see the 'MidiChannel' class in the source code (http://www.paulscode.com/source/SoundSystem/SoundSystemSource.zip) for SoundSystem.
Title: Re: Midi Volume (Not jPCT But Java)
Post by: AGP on February 03, 2009, 10:07:21 pm
Have a look at mine. It's no different than yours as far as I can tell.

An observation: wouldn't the following code (your code) always yield true for the first one (or false for both, but never true for the second)? You would have to flip that test to make sure either can be true.
Code: [Select]
        if( synthesizer != null ) {
...
        }
        else if( sequencer != null && sequencer instanceof Synthesizer){
...
        }
Title: Re: Midi Volume (Not jPCT But Java)
Post by: AGP on February 03, 2009, 10:31:48 pm
By the way, I assume your CHANGE_VOLUME is 7. If not what is it?
Title: Re: Midi Volume (Not jPCT But Java)
Post by: paulscode on February 03, 2009, 10:47:43 pm
An observation: wouldn't the following code (your code) always yield true for the first one (or false for both, but never true for the second)? You would have to flip that test to make sure either can be true.
Code: [Select]
        if( synthesizer != null ) {
...
        }
        else if( sequencer != null && sequencer instanceof Synthesizer){
...
        }

No, because as I mentioned, in my code, 'synthesizer' was only instantiated if sequencer was not an instance of Synthesizer.  So in the case that sequencer is an instance of Synthesizer, the first would yield false, and the second would yield true.  I could of course remove that 'else if' by calling synthesizer = (Synthesizer) sequencer; earlier in the code when checking if sequencer is an instance of synthesizer.
Title: Re: Midi Volume (Not jPCT But Java)
Post by: paulscode on February 03, 2009, 10:55:19 pm
Actually, upon closer examination, I think your problem might be coming from the fact that you are sending the volume change messages BEFORE you started the sequence.  Generally, when the sequence begins, default volume messages are sent, so you need to change the volume after starting the sequence.

I am running some tests, so I'll update you if I can get the volume changes to work properly.
Title: Re: Midi Volume (Not jPCT But Java)
Post by: paulscode on February 04, 2009, 12:08:21 am
Ok, I altered your playMidi method slightly, and it seems to be working.  One important thing to note - volume changes for MIDI are not a linear function, and they can be overwritten if volume events are written into the sequence by the composer.  Also, depending on the MIDI file and synthesizer, a volume of "0" does not necessarily mean completely silent.  That being said, at least it is usually possible to reduce the volume down to an acceptable level.  Here is the altered playMidi method (in a simple application), which works fine on my computer:

Code: [Select]
import java.io.IOException;
import java.net.URL;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Synthesizer;
import javax.sound.midi.Transmitter;

public class MIDIVolumeProblem
{
    public static void main(String[] args)
    {
        new MIDIVolumeProblem();
    }

    public MIDIVolumeProblem()
    {
        playMidi( "beethoven.mid", 0 );
       
        try
        {
            Thread.sleep( 60000 );
        }
        catch( InterruptedException ie )
        {}
    }
   
    private void playMidi( String filename, int midiVolume )
    {
        int CHANGE_VOLUME = 7;
        Sequencer midiPlayer = null;
        Synthesizer synthesizer = null;
        Sequence sequence = null;
       
        URL midiFile = getClass().getClassLoader().getResource(
                                                              "beethoven.mid" );
       
        if( midiFile == null )
        {
            System.err.println( "Unable to load Midi file." );
            return;
        }
        try
        {
            sequence = MidiSystem.getSequence( midiFile );
            midiPlayer = MidiSystem.getSequencer();
            midiPlayer.open();
            midiPlayer.setSequence( sequence );
        }
        catch( IOException ioe )
        {
            System.err.println( "Input failed while reading from MIDI file." );
            ioe.printStackTrace();
            return;
        }
        catch( InvalidMidiDataException imde )
        {
            System.err.println( "Invalid MIDI data encountered, or not a MIDI "
                                + "file." );
            imde.printStackTrace();
            return;
        }
        catch( MidiUnavailableException mue )
        {
            System.err.println( "MIDI unavailable, or MIDI device is already "
                                + "in use." );
            mue.printStackTrace();
            return;
        }
        catch( Exception e )
        {
            System.err.println( "Problem loading MIDI file." );
            e.printStackTrace();
            return;
        }
       
        if( midiPlayer instanceof Synthesizer )
        {
            synthesizer = (Synthesizer) midiPlayer;
        }
        else
        {
            try
            {
                synthesizer = MidiSystem.getSynthesizer();
                synthesizer.open();
                Receiver receiver = synthesizer.getReceiver();
                Transmitter transmitter = midiPlayer.getTransmitter();
                transmitter.setReceiver( receiver );
            }
            catch( MidiUnavailableException mue )
            {
                System.err.println( "MIDI unavailable, or MIDI device is "
                                    + "already in use." );
                mue.printStackTrace();
                return;
            }
            catch( Exception e )
            {
                System.err.println( "Problem initializing the MIDI "
                                    + "synthesizer." );
                e.printStackTrace();
                return;
            }
        }
       
        midiPlayer.start();
       
        // wait a couple of seconds so you can hear the volume change:
        // TODO: remove this
        try
        {
            Thread.sleep( 2000 );
        }
        catch( InterruptedException ie )
        {}
       
        javax.sound.midi.MidiChannel[] channels = synthesizer.getChannels();
        for( int c = 0; channels != null && c < channels.length; c++ )
        {
            channels[c].controlChange( CHANGE_VOLUME, midiVolume );
        }
    }
}

Oh, and I should mention, if you use a program (such as Cakewalk) to compose or edit your MIDI files, if you set the default volume for each channel very low, then you should have more control over the volume in your program (I haven't tested that yet myself, but that's the advice I got from someone else).  Also, possible values for midVolume can actually go up to 255 (anything over 127 is an increase in the channel's volume, while values lower than 127 decrease the volume).
Title: Re: Midi Volume (Not jPCT But Java)
Post by: AGP on February 04, 2009, 02:03:21 am
Thanks for your help, pal, but as yours hasn't worked for me either, I'm going to assume that it's my hardware (or maybe fact that I'm running the Windows 7 beta). But moving the start() call up was a good suggestion.
Title: Re: Midi Volume (Not jPCT But Java)
Post by: paulscode on February 04, 2009, 03:04:12 am
Hm, that is odd.  Have you tried using different midi files to see if volume changes on any of them?

I went ahead and added back in the checking for default soundbank code.  It doesn't seem to be required on my computer, but who knows - it could be required on some computers.  This way also seems to work fine on my computer:

Code: [Select]
    private void playMidi( String filename, int midiVolume )
    {
        int CHANGE_VOLUME = 7;
        Sequencer midiPlayer = null;
        Synthesizer synthesizer = null;
        Sequence sequence = null;
       
        URL midiFile = getClass().getClassLoader().getResource(
                                                              "beethoven.mid" );
       
        if( midiFile == null )
        {
            System.err.println( "Unable to load Midi file." );
            return;
        }
        try
        {
            sequence = MidiSystem.getSequence( midiFile );
            midiPlayer = MidiSystem.getSequencer();
            midiPlayer.open();
            midiPlayer.setSequence( sequence );
        }
        catch( IOException ioe )
        {
            System.err.println( "Input failed while reading from MIDI file." );
            ioe.printStackTrace();
            return;
        }
        catch( InvalidMidiDataException imde )
        {
            System.err.println( "Invalid MIDI data encountered, or not a MIDI "
                                + "file." );
            imde.printStackTrace();
            return;
        }
        catch( MidiUnavailableException mue )
        {
            System.err.println( "MIDI unavailable, or MIDI device is already "
                                + "in use." );
            mue.printStackTrace();
            return;
        }
        catch( Exception e )
        {
            System.err.println( "Problem loading MIDI file." );
            e.printStackTrace();
            return;
        }
       
        if( midiPlayer instanceof Synthesizer )
        {
            synthesizer = (Synthesizer) midiPlayer;
        }
        else
        {
            try
            {
                synthesizer = MidiSystem.getSynthesizer();
                synthesizer.open();
                if( synthesizer.getDefaultSoundbank() == null )
                {
                    midiPlayer.getTransmitter().setReceiver(
                                                     MidiSystem.getReceiver() );

                }
                else
                {
                    midiPlayer.getTransmitter().setReceiver(
                                                    synthesizer.getReceiver() );
                }
            }
            catch( MidiUnavailableException mue )
            {
                System.err.println( "MIDI unavailable, or MIDI device is "
                                    + "already in use." );
                mue.printStackTrace();
                return;
            }
            catch( Exception e )
            {
                System.err.println( "Problem initializing the MIDI "
                                    + "synthesizer." );
                e.printStackTrace();
                return;
            }
        }
       
        midiPlayer.start();
       
        try
        {
            Thread.sleep( 2000 );
        }
        catch( InterruptedException ie )
        {}
       
        if( synthesizer.getDefaultSoundbank() == null )
        {
            try
            {
                ShortMessage volumeMessage = new ShortMessage();
                for( int i = 0; i < 16; i++ )
                {
                    volumeMessage.setMessage( ShortMessage.CONTROL_CHANGE, i,
                                              CHANGE_VOLUME, midiVolume );
                    MidiSystem.getReceiver().send( volumeMessage, -1 );
                }
            }
            catch( InvalidMidiDataException imde )
            {
                System.err.println( "Invalid MIDI data encountered, or not a"
                                    + "MIDI file." );
                imde.printStackTrace();
                return;
            }
            catch( MidiUnavailableException mue )
            {
                System.err.println( "MIDI unavailable, or MIDI device is "
                                    + "already in use." );
                mue.printStackTrace();
                return;
            }
        }
        else
        {
            javax.sound.midi.MidiChannel[] channels = synthesizer.getChannels();

            for( int c = 0; channels != null && c < channels.length; c++ )
            {
                channels[c].controlChange( CHANGE_VOLUME, midiVolume );
            }
        }
    }

Sorry I couldn't be of any more help.
Title: Re: Midi Volume (Not jPCT But Java)
Post by: paulscode on February 04, 2009, 04:52:34 am
I would like to see if this volume problem exists on a lot of computers, so I created a test applet:

MIDI Volume Test (http://www.paulscode.com/source/MIDI/MIDIApplet/)

This applet uses the same code as in my last post for changing midi volume.  Use the up and down arrows to change the volume (you might have to go down a ways before you hear a change).

If any of you all have the time, would you please run this applet, and let me know if the volume changes correctly for you?  Please mention your operating system and hardware.  Thanks in advance!
Title: Re: Midi Volume (Not jPCT But Java)
Post by: AGP on February 04, 2009, 05:45:33 am
About the soundbank test, that's the whole point: it works in software on most computers, but hardware on most Windows JREs. The point is to work on all computers. And your applet didn't work on my laptop (the computer I'm currently using since I'm not home), whose sound chip I can't identify since Windows 7 only tells me "High Definition Audio Device" and it's a Microsoft driver.
Title: Re: Midi Volume (Not jPCT But Java)
Post by: AGP on February 04, 2009, 05:46:41 am
To clarify: the applet ran fine, but the volume (whose value changed) didn't change.
Title: Re: Midi Volume (Not jPCT But Java)
Post by: paulscode on February 04, 2009, 11:37:32 am
About the soundbank test, that's the whole point: it works in software on most computers, but hardware on most Windows JREs. The point is to work on all computers. And your applet didn't work on my laptop (the computer I'm currently using since I'm not home), whose sound chip I can't identify since Windows 7 only tells me "High Definition Audio Device" and it's a Microsoft driver.
That is strange.  I am running Windows XP and Windows Vista on the three test machines I used to test MIDI so far, with various sound cards - RealTec Ac'97, ESS Meistro, and SigmaTel High Definition Audio Device, and volume changes work fine for all of them in both applets and applications (to clarify, volume changes and becomes quiet/loud, but doesn't necessarily become completely silent with a volume message of 0).
I'm wondering if it is a Windows 7 issue, as you mentioned.  I don't have that OS to test it here.  When I have more time later, I am going to create a more verbose test program to post on various forums, and I will try it out on more of my test machines as well.  I'm glad you brought this to my attention - If possible, I would like to figure out a solution that works for all systems.
Title: Re: Midi Volume (Not jPCT But Java)
Post by: C3R14L.K1L4 on February 04, 2009, 10:19:08 pm
If any of you all have the time, would you please run this applet, and let me know if the volume changes correctly for you?  Please mention your operating system and hardware.  Thanks in advance!

Volume change works, but when set to '0' I still hear music.
Using kX Driver on a Audigy2. The same on my laptop with a integrated Conexant High Definition Audio chip. Both pcs with XP SP2/SP3.
Title: Re: Midi Volume (Not jPCT But Java)
Post by: EgonOlsen on February 05, 2009, 01:58:34 pm
Volume change works, but when set to '0' I still hear music.
Using kX Driver on a Audigy2. The same on my laptop with a integrated Conexant High Definition Audio chip. Both pcs with XP SP2/SP3.
The same here (Vista Ultimate, Java 6, RealTek HD Audio (onboard)).
Title: Re: Midi Volume (Not jPCT But Java)
Post by: paulscode on February 05, 2009, 11:41:23 pm
Ok, I made the applet more more verbose, so I can hopefully track down if there is a specific setup that results in volume changes not working:

MIDI Applet (http://www.paulscode.com/source/MIDI/MIDIApplet/)

Those of you who have already posted information from the last applet, if you have a chance, could you please run the applet again and grab the output.  If you had tested the applet on multiple systems, could you please let me know which output went with which system (assuming output was different for each system)?  I am most interested in the last half of output, since the first part will probably be the same for everyone running a Microsoft OS (I would like to see what Windows 7 shows for MIDI devices, though).

If anyone else wants to help out who hasn't already, please run the applet and use the arrow keys to adjust the volume all the way to 0, then back up to 127.  Please post the output, and include your operating system and hardware and a brief description of the results.  Possible results are:
1) MIDI did not play.
2) MIDI played, but volume never changed.
3) Volume changed, but did not become silent at volume=0.
or
4) Everything worked correctly.

I also posted this on several forums so I can get a variety of hardware, operating systems, and browsers.  I will update you if I notice any patterns.

Thanks again!
Title: Re: Midi Volume (Not jPCT But Java)
Post by: C3R14L.K1L4 on February 06, 2009, 12:02:18 am
Nice, you corrected the volume change ;) Now I don't have to press down/up 128 times ;D
Anyway, here's the debug from my 'regular' machine:

Code: [Select]
12 MIDI Devices:
    kX Control SB0244 10k2 [a000]
    kX Uart SB0244 10k2 [a000]
    kX Uart2 SB0244 10k2 [a000]
    Microsoft MIDI Mapper
    Microsoft GS Wavetable SW Synth
    kX Control SB0244 10k2 [a000]
    kX Synth SB0244 10k2 [a000]
    kX Synth2 SB0244 10k2 [a000]
    kX Uart SB0244 10k2 [a000]
    kX Uart2 SB0244 10k2 [a000]
    Real Time Sequencer
    Java Sound Synthesizer
Using 'Real Time Sequencer' as the Sequencer.
Variable 'sequencer' is NOT an instance of Synthesizer
Using 'Java Sound Synthesizer' as the Synthesizer.
Method 'getDefaultSoundbank()' did NOT return 'null'.

And from my laptop:

Code: [Select]
4 MIDI Devices:
    Microsoft MIDI Mapper
    Microsoft GS Wavetable SW Synth
    Real Time Sequencer
    Java Sound Synthesizer
Using 'Real Time Sequencer' as the Sequencer.
Variable 'sequencer' is NOT an instance of Synthesizer
Using 'Java Sound Synthesizer' as the Synthesizer.
Method 'getDefaultSoundbank()' did NOT return 'null'.

Edit: added jdk version:

Code: [Select]
java version "1.6.0_11"
Java(TM) SE Runtime Environment (build 1.6.0_11-b03)
Java HotSpot(TM) Client VM (build 11.0-b16, mixed mode, sharing)
Title: Re: Midi Volume (Not jPCT But Java)
Post by: AGP on February 06, 2009, 03:08:01 am
I guess the volume does change on my laptop (now that the keyboard input was improved I can tell the difference). It's just a matter of how much, and the difference isn't huge on my computer.

4 MIDI Devices:
    Microsoft MIDI Mapper
    Microsoft GS Wavetable Synth
    Real Time Sequencer
    Java Sound Synthesizer
Using 'Real Time Sequencer' as the Sequencer.
Variable 'sequencer' is NOT an instance of Synthesizer
Using 'Java Sound Synthesizer' as the Synthesizer.
Method 'getDefaultSoundbank()' did NOT return 'null'.
Title: Re: Midi Volume (Not jPCT But Java)
Post by: paulscode on February 06, 2009, 03:55:38 am
I have gotten a good number of responses on various forums, and I am beginning to see a distinct pattern.  In cases where getDefaultSoundbank() does not return null (i.e. software-synthesis mode) then volume does not become silent at zero.  In cases where getDefaultSoundbank() does return null (i.e. hardware-synthesis mode) then volume becomes silent at zero.  This theory is further supported by some test cases I've done here.  When I run the test as an application, they all report that getDefaultSoundbank() does not return null, and volume does not become silent at zero.  On my laptop running Vista, the applet also reports not null for getDefaultSoundbank(), and volume does not become silent at zero.  For all my other test machines, the applet reports that getDefaultSoundbank() does return null, and volume becomes silent at zero (the same machines where volume did not become silent at zero when running as an application).

Of course I will keep looking for exceptions to this theory as more reports come in (and I haven't had anyone report yet that volume didn't change for them at all), but I can make a deduction based on the current information:
When running in hardware-synthesis mode, volume changes work properly.  The code that runs in this case is:
Code: [Select]
                Receiver receiver = MidiSystem.getReceiver();
                ShortMessage volumeMessage= new ShortMessage();
                for( int c = 0; c < 16; c++ )
                {
                    volumeMessage.setMessage( ShortMessage.CONTROL_CHANGE, c,
                                              CHANGE_VOLUME, midiVolume );
                    receiver.send( volumeMessage, -1 );
                }

On the other hand, when running in software-synthesis mode, volume changes do not work properly.  The code that runs in this case is:
Code: [Select]
            javax.sound.midi.MidiChannel[] channels = synthesizer.getChannels();
            for( int c = 0; channels != null && c < channels.length; c++ )
            {
                channels[c].controlChange( CHANGE_VOLUME, midiVolume );
            }

It would appear that using the ShortMessages method works better for changing the volume than using the synthesizer channels method.  I've tried mixing and matching the two methods, but the volume does not change at all if using the wrong method with the wrong synthesis mode.  I've also tried using both modes at the same time for both methods, and that doesn't cause any problems, but it doesn't correct the software-synthesis mode volume problem either.  I'll keep looking into this, and post some questions on various forums.  If my theory is right, then at least we have a little bit better idea on where to focus.

Oh, and we also know that the problem of volume not changing at all is not common, and since your laptop is running Windows 7, we know it is probably not an issue Windows 7.
Title: Re: Midi Volume (Not jPCT But Java)
Post by: AGP on February 06, 2009, 07:28:48 am
To clarify, the volume change worked. The contrast between 0 and 127 may not have been huge, but it was noticeable (now that the keyboard input was improved). And thanks again for showing that.
Title: Re: Midi Volume (Not jPCT But Java)
Post by: paulscode on February 06, 2009, 11:25:30 pm
I thought I would mention that I also tried this for software-synthesis mode:

Code: [Select]
                ShortMessage volumeMessage = new ShortMessage();
                for( int i = 0; i < 16; i++ )
                {
                    volumeMessage.setMessage( ShortMessage.CONTROL_CHANGE, i,
                                              CHANGE_VOLUME, midiVolume );
                    synthesizer.getReceiver().send( volumeMessage, -1 );
                }

This behaves exactly the same way as the synthesizer getChannels method (i.e. volume changes, but doesn't become silent at zero).

Doesn't really help much, but at least it's one more thing to cross off the list.  I haven't gotten any useful responses from the various forums I've posted on yet.