As you may know already Java supports the MIDI 1.0 spec in its core classes. It is still common for MIDI audio to be used in games because of small size and nostalgia. Nothing makes me smirk more than a really cheesy synthesized MIDI…
But anyway, as I was developing the MIDI support in my base game code I wanted to add volume control. This seems easy, and at first sight/Google its about 4 lines of code.
Setting up to play an MIDI:
// get all the default stuff Sequencer sequencer = MidiSystem.getSequencer(false); Synthesizer synthesizer = MidiSystem.getSynthesizer(); Receiver receiver = synthesizer.getReceiver(); // open the synth and sequencer and wire up the receiver and transmitter synthesizer.open(); sequencer.open(); sequencer.getTransmitter().setReceiver(receiver); // play the MIDI sequencer.setSequence(sequence); sequencer.start();
And to control the volume:
MidiChannel[] channels = synthesizer.getChannels(); for (int i = 0; i < channels.length; i++) { channels[i].controlChange(7, volume); }
Unfortunately it was not so easy. So I attempted to use the various code snippets around the web and nothing ever worked. So after some time of searching (like 2 months…) I finally found the . Read the first paragraph… well what do you know, I happen to be developing and testing on a windows box…
So after my long search I found out what the problem was, the JRE for Windows does not ship with a soundbank and by default the JRE will use the hardware soundbank. So you might ask, “What does it matter that the JRE uses the hardware soundbank instead of a packaged one, the volume control should be the same?” Unfortunately it’s not.
The process is basically the same except that we need to use the default receiver of the MidiSystem object instead of the default synthesizer’s receiver. And in addition to this we need to use the default receiver to modify the volume.
The modified code:
// get all the default stuff Sequencer sequencer = MidiSystem.getSequencer(false); Receiver receiver = MidiSystem.getReceiver(); // open the sequencer and wire up the receiver // and transmitter sequencer.open(); sequencer.getTransmitter().setReceiver(receiver); // play the MIDI sequencer.setSequence(sequence); sequencer.start();
And to control the volume:
ShortMessage volMessage = new ShortMessage(); for (int i = 0; i < 16; i++) { try { volMessage.setMessage(ShortMessage.CONTROL_CHANGE, i, 7, midiVolume); } catch (InvalidMidiDataException e) {} receiver.send(volMessage, -1); }
Hi I wrote the below code as explained by you.But the Volume controller is not working .
Could U please help me to find out the issue.
Looks like your code is good. I tried my own Midi class (which basically has the same code as yours) and it seemed to work…strange. Take a look at this modified code:
It works for me. It seems that you have to give the sequencer time to open before actually setting the volume in the case that Java uses the hardware synthesizer (I have it pausing for 1 second here but I tried 10 milliseconds and that seemed to work as well). I tested with a couple of midi’s that I had and it reminded me of another problem you might run into: midi files can contain the volume control messages! So make sure you midi is an midi that the volume can be changed on.
One more thing. If you are deploying a real app that uses Midi, I would recommend just packaging a soundbank with your application and load it up like:
This will allow you to use the default synthesizer instead of using the finicky hardware one.
Hi,
1.I tried as U have mentioned/Provided but even now the Volume is not changing.I tried adding the Thread also..But no vain.Can U please share me the Midi where the volume controller information is available.
2. I tried using the default synthesizer as provided by U and as below.
But I am getting the below exception.
javax.sound.midi.InvalidMidiDataException: cannot get soundbank from stream
at javax.sound.midi.MidiSystem.getSoundbank(Unknown Source)
at MidiSynthesizerSample.main(MidiSynthesizerSample.java:18)
Please HELP me to fix it.
1. Try your code on this midi file and see if it works. Unfortunately I’m not familiar with any methods to modify or inspect midi files so I can’t help you find out if the file you are using is the problem or not. The linked midi file is the one I tested with and it seems to work.
2. If you want to use the default synthesizer you must go out and download a soundbank from . Then you basically do everything the same:
You can also take a look at my implementation which seems to work for me here. Feel free to copy and modify. You use it like the following:
Hi,
I am able to Set the Volume Now. :). The initialization of the receiver was not correct in my code.Your code help me to find that out and fix it.
Thanks a lot for Your Help.
As U have mentioned before (give the sequencer time to open before actually setting the volume). How can I achieve it without putting the Sleep.
I have tried with
and also tried to see the infinite while loop of like below
But both are not working.
Awesome, I glad to hear that, no pun intended. Yes, as you point out a busy wait will probably not work, however a Thread.sleep(10) will (at least did for me).
If you didn’t want to do this then you may be able to listen for a “start” event, then set the volume. I have not done this myself you but if you want, you can check out how to listen for events . You can listen for either Meta events or control events.
If it exists, a start event would be a Meta event. If you look at my code you’ll see that I already listen for a “track end” event. It should be very similar to this. Once you receive the event, set the volume. I’m not sure what the event number is but you can reference to try to figure it out.
As far as my code is concerned, it seems like I must be doing enough before setting the volume so that I don’t have this problem. However, I have noticed that this problem will crop up from time to time.
Hope this helps
It worked :) Thanks a lot for the gr8 help which you have done.
No Thread.sleep and volume is set as it starts playing or initialized.
Hi,
Again a wired issue. At some time I get the below error
javax.sound.midi.MidiUnavailableException: MIDI OUT transmitter not available
while executing the line
this.sequencer.getTransmitter().setReceiver(receiver);
Any idea why is it happening?.
I can’t say that I have ever seen that error before. I’m sure you are already doing this, but make sure you call the close method on all the MIDI stuff when you are done; see my code again. This could happen if you played a bunch of MIDIs and didn’t close the resources. Do you have the same problem using my code?
Hi William,
:) I have closed all the object as very much similar to what U have done. In fact I restarted my system after that.Then also 1st time when I execute itself I am getting this error.I changed my JDK ver from 1.6 to 1.5 now its working.
Dono shld I be happy or not..:)
Thanks a lot for Ur help.
Regards,
Uvaraj S