2012-05-04 9 views
0

私は最近開発中のゲームでmidisを演奏する際に奇妙なバグに遭遇しました。私は奇妙な音を出さずにミディを演奏していたので、私のMIDIコードはうまくいきました。今度はミディアムを演奏するたびに、彼らはすっごく響き、エコー、そして大きな音を出します。Java midiの一貫性のない再生品質

私は非常に長い間、私のMIDIプレイヤーのコードに触れていないので、最近のJavaのアップデートが私のコードにあったバグを全面的に公開している可能性があるのだろうかと思います。 Javaの私のバージョンには、私が気づいていない何らかのミディバグがありますか?

私がゲームの外でプレイすると、midisは正常に聞こえます。

私はJava 6、update 31、build 1.6.0_31-b05を実行しています。 SSCCEは問題を再現します(少なくとも私のJVMで再現します)。

import java.awt.*; 
import java.awt.event.*; 
import javax.swing.*; 
import java.io.*; 
import javax.sound.midi.*; 
import java.net.URL; 

public class MidiSSCCE extends JFrame 
{ 

    public MidiSSCCE() 
    { 
     super("Sound problem SSCCE"); 
     this.setSize(200,100); 

     // instantiate main window panel 

     JPanel screenP = new SSCCEPanel(this); 
     this.add(screenP); 

     // finishing touches on Game window 

     this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     this.setVisible(true); 

     System.out.println("Game Window successfully created!!!"); 
    } 

    public static void main(String[] args) 
    { 
     MidiSSCCE gui = new MidiSSCCE();   
    } 
} 


/** 
* SSCCEPanel is the JPanel that manages the example's timer, painting, and logic. 
**/ 

class SSCCEPanel extends JPanel 
{ 
    public Frame parentFrame; 
    private Timer timer; 
    public int logicLoops; 
    public double prevFPS; 
    boolean timerReady; 

    // The MidiPlayer object is used by the example to play the midi. 

    public MidiPlayer midiPlayer; 

    public SSCCEPanel(Frame parent) 
    { 
     super(true); 
     parentFrame = parent; 
     this.setFocusable(true); 

     Toolkit.getDefaultToolkit().sync(); 
     logicLoops = 0; 

     midiPlayer = new MidiPlayer(); 

     TimerListener timerListener = new TimerListener(); 
     prevFPS = 0; 
     timerReady = true; 
     timer = new Timer(0,timerListener); 
     this.setFPS(60); 
     timer.start(); 
    } 

    /** 
    * setFPS() 
    * Preconditions: fps is a quantity of frames per second 
    * Postconditions: Sets the timer's refresh rate so that it 
    *  fires fps times per second. 
    **/ 

    public void setFPS(int fps) 
    { 
     int mspf = (int) (1000.0 /fps + 0.5); 
     timer.setDelay(mspf); 
    } 


    /** 
    * This is the JPanel's timer listener. It runs the example's logic and repaint 
    * methods each time it gets a timer signal. 
    **/ 

    private class TimerListener implements ActionListener 
    { 
     long startTime = System.currentTimeMillis(); 
     long lastTime = this.startTime; 
     int ticks = 0; 

     public void actionPerformed(ActionEvent e) 
     { 
      Object source = e.getSource(); 
      if(source == timer) 
      { 
       // perform a loop through the game's logic and repaint. 

       synchronized(this) 
       { 
        if(timerReady) 
        { 
         timerReady = false; 
         runSSCCELogic(); 
         repaint(); 
         timerReady = true; 
        } 
       } 

       // Logic for Frames per Second counter 

       this.ticks++; 

       long currentTime = System.currentTimeMillis(); 

       if(currentTime - startTime >= 500) 
       { 
        prevFPS = 1000.0 * ticks/(1.0*currentTime - startTime); 
        System.out.println(prevFPS); 
        startTime = currentTime; 
        ticks = 0; 
       } 

       lastTime = currentTime; 
      } 
     } 
    } 


    /** 
    * repaints the SSCCE. 
    * This just shows the current FPS. 
    **/ 

    public void paintComponent(Graphics g) 
    { 
      super.paintComponent(g); 

      Graphics2D g2D = (Graphics2D) g; 
      double roundedFPS = Math.round(prevFPS*10)/10.0; 

      g2D.setColor(new Color(0x000000)); 
      g2D.drawString("FPS: " + roundedFPS, 20,20); 
      g.dispose(); 
    } 

    /** 
    * runSSCCEELogic() 
    * This is where the run-time logic for the SSCCE example is. 
    * All it does is load and play a midi called "mymidi.mid" which is located in the same directory. 
    **/ 

    public void runSSCCELogic() 
    { 
     if(logicLoops == 1) 
     { 
      midiPlayer.load("http://www.vgmusic.com/music/computer/microsoft/windows/touhou_6_stage3_boss.mid"); 
      midiPlayer.play(true); 
     } 

     logicLoops++; 
    } 
} 



/** 
* MidiPlayer 
* 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 
     { 
      seqr = MidiSystem.getSequencer(); 
      synth = MidiSystem.getSynthesizer(); 
     } 
     catch(Exception e) 
     { 
      System.out.println("MIDI error: It appears your system doesn't have a MIDI device or your device is not working."); 
     } 
    } 

    /** 
    * MidiPlayer(String fileName) 
    * Constructor that also loads an initial midi file. 
    * Preconditions: fileName is the name of the midi file to be loaded. 
    * Postconditions: The MidiPlayer is created and loaded with the midi specified by fileName. 
    **/ 

    public MidiPlayer(String fileName) 
    { 
     this(); 
     load(fileName); 
    } 


    // 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) 
    { 
     this.unload(); 
     try 
     { 
      URL midiURL = new URL(fileName); 
     // midiFile = new File(fileName); 
      seq = MidiSystem.getSequence(midiURL); 

      seqr.open(); 
      synth.open(); 

      System.out.println("MidiDeviceInfo: "); 
      for(MidiDevice.Info info : MidiSystem.getMidiDeviceInfo()) 
      { 
       System.out.println("\t" + info); 
      } 
      System.out.println(); 

      if(synth.getDefaultSoundbank() == null) 
      { 
       receiver = MidiSystem.getReceiver(); 
       usingHardwareSoundbank = true; 
       System.out.println("using hardware soundbank"); 
      } 
      else 
      { 
       receiver = synth.getReceiver(); 
       usingHardwareSoundbank = false; 
       System.out.println("using default software soundbank:" + synth.getDefaultSoundbank()); 
      } 
      seqr.getTransmitter().setReceiver(receiver); 

      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: Unexplained error occured while loading midi."); 
     } 
    } 

    /** 
    * unload() 
    * Unloads the current midi from the MidiPlayer and releases its resources from memory. 
    **/ 

    public void unload() 
    { 
     this.stop(); 
     seqr.close(); 
     midiFile = null; 
     loaded = false; 
    } 

    // OTHER METHODS 

    /** 
    * setMidiID(String id) 
    * associates a String ID with the current midi. 
    * Preconditions: id is the ID we are associating with the current midi. 
    **/ 

    public void setMidiID(String id) 
    { 
     midiID = id; 
    } 

    /** 
    * getMidiID(String id) 
    * 
    **/ 

    public String getMidiID() 
    { 
     return new String(midiID); 
    } 

    /** 
    * 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) 
    { 
     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; channels != null && c < channels.length; c++) 
       { 
        channels[c].controlChange(7, (int)(vol*127)); 
       } 
      } 
     } 
     catch (Exception e) 
     { 
      e.printStackTrace(); 
     } 
    } 

} 
+0

1)私の質問のほとんどが私の問題を解決した答えはありませんでした。 2)完了! – Cazra

答えて

0

私の問題を解決したと思われるMidiPlayerコードに変更を加えました。私は、midiをロードするメソッドの代わりに、サウンドバンクとトランスミッタのレシーバをコンストラクタにロードするコードを移動しました。それはもはや本当に騒々しくて悲鳴を上げることはありません。

package gameEngine; 

    import javax.sound.midi.*; 
    import java.io.File; 
    import java.io.IOException; 
    import java.net.URL; 

    /** 
    * MidiPlayer 
    * author: Stephen Lindberg 
    * Last modified: Oct 14, 2011 
    * 
    * A class that allows midi files to be loaded and played. 
    **/ 

    public 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; 
    private float defaultTempo; 

    // CONSTRUCTORS 

    public MidiPlayer() 
    { 
     loaded = false; 
     try 
     { 
      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 receiver. This will be used for changing volume. 

      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: It appears your system doesn't have a MIDI device or your device is not working."); 
     } 
    } 

    /** 
    * MidiPlayer(String fileName) 
    * Constructor that also loads an initial midi file. 
    * Preconditions: fileName is the name of the midi file to be loaded. 
    * Postconditions: The MidiPlayer is created and loaded with the midi specified by fileName. 
    **/ 

    public MidiPlayer(String fileName) 
    { 
     this(); 
     load(fileName); 
    } 


    // 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) 
    { 
     this.unload(); 
     try 
     { 
      URL midiURL = getClass().getClassLoader().getResource(fileName); 
      seq = MidiSystem.getSequence(midiURL); 

      seqr.open(); 
      synth.open(); 

      // load our sequence into the sequencer. 

      seqr.setSequence(seq); 
      loaded = true; 
      defaultTempo = seqr.getTempoInBPM(); 
     } 
     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: Unexplained error occured while loading midi."); 
     } 
    } 

    /** 
    * 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 

    /** 
    * setMidiID(String id) 
    * associates a String ID with the current midi. 
    * Preconditions: id is the ID we are associating with the current midi. 
    **/ 

    public void setMidiID(String id) 
    { 
     midiID = id; 
    } 

    /** 
    * getMidiID(String id) 
    * 
    **/ 

    public String getMidiID() 
    { 
     return new String(midiID); 
    } 

    /** 
    * 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(); 
    } 



    /** 
    * getTempo() 
    * Returns the current tempo of the MidiPlayer in BPM (Beats per Minute). 
    **/ 

    public float getTempo() 
    { 
     return seqr.getTempoInBPM(); 
    } 

    /** 
    * 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); 

    } 

    /** 
    * resetTempo() 
    * Resets the MidiPlayer's tempo the the initial tempo of its current midi. 
    **/ 

    public void resetTempo() 
    { 
     this.changeTempo(this.defaultTempo); 
    } 

    /** 
    * changeTempo(float bpm) 
    * Changes the MidiPlayer's current tempo. 
    * Preconditions: bpm is the MidiPlayer's new tempo in BPM (Beats per Minute). 
    * Postconditions: The MidiPlayer's current tempo is set to bpm BPM. 
    **/ 

    public void changeTempo(float bpm) 
    { 
     double lengthCoeff = bpm/seqr.getTempoInBPM(); 

     seqr.setLoopStartPoint((long) (seqr.getLoopStartPoint()*lengthCoeff)); 
     seqr.setLoopEndPoint((long) (seqr.getLoopEndPoint()*lengthCoeff)); 

     seqr.setTempoInBPM(bpm); 
    } 


    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(); 
     } 
    } 

    } 
+0

私はMIDIボリュームコントロールを動作させるのにも問題を抱えていましたが、それはこの質問の対象ではありませんので、私が推測する問題について新しい質問を開始します... – Cazra

0

私のJREビルドに問題があることが判明しました。 Andrewが私にリンクしたmidisを演奏するためのこの簡単な例を試してみました:

そしてまだMIDIの音質は向上しませんでした。 SSCCEとJava 6_31とJava 6_32を使用した上記の最小限の例の両方を実行しました。

結論として、これはJava 6_31とJava 6_32に固有の問題です。だから、私はSun/Oracleがこの問題を解決したうえで次のJREビルドをリリースするまで私は運がないと思う。

EDIT:

私はちょうどジャワ6_31は、自分のマシン上でのテストにこれをインストールしている友人を持っていました。彼は私のJavaのサンプルを実行してJavaの外部でmidiをプレイしたとき音質の違いに気付かなかった。だから、問題はJavaのバグではなく、私のマシンとは特に関係している可能性があります。しかし、別の友人がそれをテストしただけで、同じ問題が発生していました。

結論として、この問題は、6/31を過ぎたJavaバージョンに固有のものか、一部のマシンのサウンドデバイス、あるいはその両方の組み合わせのいずれかです。この問題は、おそらくネイティブjavaでそれ以上追求する価値はありません。

+0

は心配しないでください。私は自分のマシン上の問題を解決するコードの修正を見つけました。 (他の人とまだそれをテストしていない)私の受け入れられた答えを見てください。 – Cazra

2

MIDIの音質は、サウンドを生成するシンセに依存します。あなたのコードとは関係がありません。

ほとんどの場合、それはサウンドカードの問題ですが、それはいつもサウンドを生成するものではありません。 Windowsでは、すべてのことを行うMicrosoftのソフトウェアシンセがあります。いずれにせよ、これはあなたのコードとは関係ありません。

+0

ありがとう、これは私が到着している結論であるようです。私はいくつかの友人にSSCCEをテストしてもらいました。いくつかは、ミディはうまく聞こえるが、いくつかは同じ問題を抱えている。 – Cazra

関連する問題