Hi there, I'm writing a simple MIDI player to teach myself how to use the javax.sound.midi package. So far, I have a basic program that can play, pause, stop, open a new MIDI sequence, and display file information (name and length in minutes:seconds). I'm having a hard time getting the volume control to work, though. I have googled extensively on this. The conventional wisdom seems to be that you control MIDI volume by instantiating and opening a Sequencer, instantiating and opening a Synthesizer, binding the Synthesizer to the Sequencer via a Transmitter/Receiver pair, setting your Sequence, starting your Sequencer, and collecting the MidiChannels via a call to the Synthesizer, then doing a controlChange of controller #7 (major volume control) on each channel. It's easier to see what's going on by looking at the code; this method should turn all channels on the synthesizer down to zero:
The effect of this code is that the volume of the playing synthesizer is indeed lowered, but not silenced. This is driving me up the wall; I'm not a MIDI guru, so I don't know what I'm missing, and reading the specs isn't helping. Some caveats:
1) Apparently, MIDI controller 39 is another volume controller (the least significant 7 bits of the volume control for the channel) but when you reset controller 7, you also zero out controller 39.
2) I have tried rearranging the instructions in the above code, to see if I was doing something in the wrong order. Nothing helped.
3) I tried setting controller 7 to -1, thinking that maybe MIDI works differently on my machine than everywhere else in the universe. It raised the volume to the maximum value, an earsplitting 127.
4) It's almost as if there are two sound-generating elements here, the Synthesizer I'm using and something else which the Synthesizer can't touch. Calls to setMute() and allNotesOff() don't work, either.
5) Another way to make this work is to go into the Sequence and change the volume of every single MidiMessage individually. I won't do this, it's too slow. I want my volume to change in real time, as the user drags the volume slider up and down.
6) Another caveat: I did the obvious check. I added the following code, which checks each channel's controller #7, to the above:
...and each channel is indeed set to zero.
Any ideas? Thanks! -Greg [ December 31, 2004: Message edited by: Greg Donahue ]
vi veri veniversum vivus vici
posted 14 years ago
Okay, I figured it out.
I looked at the MidiDevices obtained from calling MidiSystem.getMidiDeviceInfo(), and observed that they were all closed. So I knew right away that something was fishy. I guess that calling MidiSystem.getSequencer() and MidiSystem.getSynthesizer() doesn't give you the default objects for those MIDI devices.
To correct the problem, I had to set the sequencer and synthesizer to the default sequencer and synthesizer in the MidiDevice.Info list, rather than using MidiSystem.getSequencer() and MidiSystem.getSynthesizer(). It looks like:
...and that did the trick. Now my volume works perfectly. -Greg
Upon investigation I concluded that for me (on Mac OS X, java 1.6.0_33-b03-424-10M3720) MidiSystem.getSynthesizer did *NOT* return the same synthesizer that the sequencer was by default connected to.
The sequencer is already connected to *A* synthesizer. If you do a getSynthesizer and connect it up then your sequencer will drive *TWO* synthesizers. You can test this by calling sequencer.getTransmitters and looping through the list (of transmitters open and in use).
I suggest that this is why setting volume to zero reduces the volume but doesn't shut it off completely!
Of course different java environments might behave differently.
1 (the one I used)
Don't use the synthesizer object because you can't get to it (but see solution 2). Don't use Channels. To control volume use:
This is of course suitable for both software and hardware synth so should simplify the code.
2 (not tested)
Use getSynthesizer and connect transmitter and receiver as lots of sample code tells you, BUT
do something like: seq.getTransmitters ().iterator ().next ().close ()
to shut down the default synthesizer started for you.
Then with any luck you can get the channels and the sample code might work.