Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java midi volume control won't work

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

like image 778
Cazra Avatar asked Jun 09 '12 23:06

Cazra


2 Answers

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.

like image 163
Nievinski Avatar answered Sep 28 '22 06:09

Nievinski


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();
    }
}
like image 32
Andrew Thompson Avatar answered Sep 28 '22 06:09

Andrew Thompson