I've been trying to get midi volume control to work in a MidiPlayer class for a very long time now. I've searched for examples for accomplishing this here on stackoverflow and all over the Internet, but nothing I try ever seems to work. The volume stays the same! It doesn't change like I want it to.
I'm running Java 1.6.0_32 on Windows 7 professional.
Here! Have an SSCCE:
import java.io.*;
import javax.sound.midi.*;
import java.net.URL;
public class MidiSSCCE {
public static void main(String[] args)
{
// creates the midi player and sets up its sequencer and synthesizer.
MidiPlayer midiP = new MidiPlayer();
double volume = 1.0;
// loads a midi from a url into the sequencer, but doesn't start playing it yet.
midiP.load("http://www.vgmusic.com/music/computer/microsoft/windows/touhou_6_stage3_boss.mid",true);
// set the midi to loop indefinitely.
midiP.loop(-1);
// start playing the midi.
midiP.play(true);
// while loop changes the volume of the midi while it is playing.
while(true) {
midiP.setVolume(volume);
try { Thread.sleep(300); } catch(Exception e) {}
volume -= 0.1;
if(volume < 0) volume += 1.0;
}
}
}
/**
* MidiPlayer
* author: Stephen Lindberg
* Last modified: Oct 14, 2011
*
* A class that allows midi files to be loaded and played.
**/
class MidiPlayer {
private Sequence seq;
private Sequencer seqr;
private Synthesizer synth;
private Receiver receiver;
private File midiFile;
private String midiID;
private boolean loaded;
private boolean usingHardwareSoundbank;
// CONSTRUCTORS
public MidiPlayer() {
loaded = false;
try {
// obtain the sequencer and synthesizer.
seqr = MidiSystem.getSequencer();
synth = MidiSystem.getSynthesizer();
// print the user's midi device info
System.out.println("Setting up Midi Player...");
System.out.println("MidiDeviceInfo: ");
for(MidiDevice.Info info : MidiSystem.getMidiDeviceInfo()) {
System.out.println("\t" + info.getName() + ": " +info.getDescription());
}
System.out.println();
// obtain the soundbank and receiver.
Soundbank soundbank = synth.getDefaultSoundbank();
if(soundbank == null) {
receiver = MidiSystem.getReceiver();
usingHardwareSoundbank = true;
System.out.println("using hardware soundbank");
}
else {
synth.loadAllInstruments(soundbank);
receiver = synth.getReceiver();
usingHardwareSoundbank = false;
System.out.println("using default software soundbank:" + soundbank);
}
seqr.getTransmitter().setReceiver(receiver);
}
catch(Exception e) {
System.out.println("MIDI error: I just don't know what went wrong! 6_9");
}
}
// DATA METHODS
/**
* load(String fileName)
* loads a midi file into this MidiPlayer.
* Preconditions: fileName is the name of the midi file to be loaded.
* Postconditions: fileName is loaded and is ready to be played.
**/
public void load(String fileName, boolean isOnline) {
this.unload();
try {
URL midiURL;
if(isOnline) midiURL = new URL(fileName);
else midiURL = getClass().getClassLoader().getResource(fileName);
seq = MidiSystem.getSequence(midiURL);
seqr.open();
synth.open();
// load our sequence into the sequencer.
seqr.setSequence(seq);
loaded = true;
}
catch(IOException ioe) {
System.out.println("MIDI error: Problem occured while reading " + midiFile.getName() + ".");
}
catch(InvalidMidiDataException imde) {
System.out.println("MIDI error: " + midiFile.getName() + " is not a valid MIDI file or is unreadable.");
}
catch(Exception e) {
System.out.println("MIDI error: I just don't know what went wrong! 6_9");
}
}
/**
* unload()
* Unloads the current midi from the MidiPlayer and releases its resources from memory.
**/
public void unload() {
this.stop();
seqr.close();
synth.close();
midiFile = null;
loaded = false;
}
// OTHER METHODS
/**
* play(boolean reset)
* plays the currently loaded midi.
* Preconditions: reset tells our midi whether or nor to begin playing from the start of the midi file's current loop start point.
* Postconditions: If reset is true, then the loaded midi begins playing from its loop start point (default 0).
* If reset is false, then the loaded midi resumes playing from its current position.
**/
public void play(boolean reset) {
if(reset) seqr.setTickPosition(seqr.getLoopStartPoint());
seqr.start();
}
/**
* stop()
* Pauses the current midi if it was playing.
**/
public void stop() {
if(seqr.isOpen()) seqr.stop();
}
/**
* isRunning()
* Returns true if the current midi is playing. Returns false otherwise.
**/
public boolean isRunning() {
return seqr.isRunning();
}
/**
* loop(int times)
* Sets the current midi to loop from start to finish a specific number of times.
* Preconditions: times is the number of times we want our midi to loop.
* Postconditions: The current midi is set to loop times times.
* If times = -1, the current midi will be set to loop infinitely.
**/
public void loop(int times)
{
loop(times,0,-1);
}
/**
* loop(int times)
* Sets the current midi to loop from a specified start point to a specified end point a specific number of times.
* Preconditions: times is the number of times we want our midi to loop.
* start is our loop's start point in ticks.
* end is our loop's end point in ticks.
* Postconditions: The current midi is set to loop from tick start to tick end times times.
* If times = -1, the current midi will be set to loop infinitely.
**/
public void loop(int times, long start, long end) {
if(start < 0) start = 0;
if(end > seqr.getSequence().getTickLength() || end <= 0) end = seqr.getSequence().getTickLength();
if(start >= end && end != -1) start = end-1;
seqr.setLoopStartPoint(start);
seqr.setLoopEndPoint(end);
if(times == -1) seqr.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
else seqr.setLoopCount(times);
}
public void setVolume(double vol) {
System.out.println("Midi volume change request: " + vol);
try {
if(usingHardwareSoundbank) {
ShortMessage volumeMessage = new ShortMessage();
for ( int i = 0; i < 16; i++ ) {
volumeMessage.setMessage( ShortMessage.CONTROL_CHANGE, i, 7, (int)(vol*127) );
receiver.send( volumeMessage, -1 );
}
}
else {
MidiChannel[] channels = synth.getChannels();
for( int c = 0; c < channels.length; c++ ) {
if(channels[c] != null) channels[c].controlChange( 7, (int)( vol*127) );
}
}
}
catch ( Exception e ) {
e.printStackTrace();
}
}
}
I've tried examples at the following sources with no success:
http://www.java2s.com/Code/Java/Development-Class/SettingtheVolumeofPlayingMidiAudio.htm
How to controll the MIDI channel's volume
https://forums.oracle.com/forums/thread.jspa?messageID=5389030
MIDI Song with CC
http://www.codezealot.org/archives/27
http://www.exampledepot.com/egs/javax.sound.midi/Volume.html
I'm struggling with these very issues on controlling sound and i've found a code to change volume that works. Unfurtunatly i was unable to understand it, but here is something in your code that is differente from the one i've seen. Maybe it can helps you. Try change the line
seqr = MidiSystem.getSequencer();
for
seqr = MidiSystem.getSequencer(false);
Maybe it helps you, I believe that using the "false" the sequencer will connect to the receiver, and not to the synthesizer, then when you send the message to the receiver to set volume it will work.
public void setVolume(double vol) {
System.out.println("Midi volume change request: " + vol);
try {
if(usingHardwareSoundbank) {
ShortMessage volumeMessage = new ShortMessage();
for ( int i = 0; i < 16; i++ ) {
volumeMessage.setMessage( ShortMessage.CONTROL_CHANGE, i, 7, (int)(vol*127) );
receiver.send( volumeMessage, -1 );
}
}
else {
MidiChannel[] channels = synth.getChannels();
for( int c = 0; c < channels.length; c++ ) {
if(channels[c] != null) channels[c].controlChange( 7, (int)( vol*127) );
}
}
// Very important!
seqr.setSequence(seq);
}
catch ( Exception e ) {
e.printStackTrace();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With