2012-05-04 3 views
0

최근에 개발중인 게임에서 미드 놀기와 함께 이상한 버그가 발생했습니다. 나는 미디 코드가 이상하게 들리지 않으면 서 미디를 연주했기 때문에 내 미디 코드가 잘 작동한다고 생각했습니다. 이제는 미디를 연주 할 때마다, 그들은 모두 귀찮고, 에코 -와, 큰 소리로 들립니다.Java midi의 일관되지 않은 재생 품질

아주 오랫동안 내 midi 플레이어 코드를 건드리지 않았으므로 최신 자바 업데이트가 내 코드에있는 버그를 노출했을 가능성이 있는지 궁금합니다. 아니면 Java 버전에 midi 버그가 있습니다.

내가 게임 밖에서 연주 할 때마다 미드 사운드가 괜찮습니다.

Java 6, 업데이트 31, 빌드 1.6.0_31-b05를 실행 중입니다. 그것은 문제가 내 JRE 빌드 함께 밝혀

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

나는 또한 미디 볼륨 컨트롤을 작동시키는 데 문제가 있었지만이 질문의 주제가 아니기 때문에 추측 하건데 그 문제에 대한 새로운 질문을 시작하겠습니다 ... – Cazra

0

: 여기에 (그것은 적어도 내 JVM에 재현) 문제를 재현 SSCCE입니다. 앤드류가 나를 연결 한 간단한 예제를 실행 해 보았습니다.

import javax.sound.midi.*; 
import java.net.URL; 

class PlayMidi { 

    public static void main(String[] args) throws Exception { 
      URL url = new URL("http://www.vgmusic.com/music/computer/microsoft/windows/touhou_6_stage3_boss.mid"); 

      Sequence sequence = MidiSystem.getSequence(url); 
      Sequencer sequencer = MidiSystem.getSequencer(); 

      sequencer.open(); 
      sequencer.setSequence(sequence); 

      sequencer.start(); 
    } 
} 

그리고 여전히 미디의 음질은 나아지지 않았습니다. 내 SSCCE와 Java 6_31 및 Java 6_32를 사용하여 위의 최소 예제를 모두 실행했습니다.

결론적으로 이것은 Java 6_31 및 Java 6_32에 고유 한 문제입니다. 따라서 Sun/Oracle이 다음 JRE 빌드를 릴리스 할 때까지 운이 좋지 않을 것입니다.이 JRE 빌드가이 문제를 해결할 것으로 기대됩니다.

편집 :

난 그냥 자바 6_31 자신의 컴퓨터에서 테스트 이것을 설치되어있는 친구가 있었다. 그는 내 Java 예제를 실행했을 때 사운드 품질에 어떤 차이가 있는지 알지 못했습니다. 따라서 문제는 Java의 버그가 아니라 내 시스템과 관련이있을 수 있습니다. 그러나, 다른 친구가 방금 그것을 테스트하고 내가 겪고있는 동일한 문제가 발생했습니다.

결론적으로이 문제는 6/31 이전의 Java 버전, 일부 시스템의 사운드 장치 또는 둘 모두의 조합에 내재되어 있습니다. 이 문제는 네이티브 java에서 더 이상 추구 할만한 가치가 없을 것입니다.

+0

신경 쓰지 마라. 내 컴퓨터의 문제를 해결하는 코드 수정 프로그램을 발견했습니다. (다른 사람과 아직 테스트하지 않았습니다.) 제 대답을보십시오. – Cazra

2

MIDI의 음질은 사운드를 생성하는 신스에 따라 다릅니다. 그것은 당신의 코드와 관련이 없어야합니다.

사운드 카드와 관련된 문제 일 가능성이 가장 높지만, 그게 항상 사운드를 생성하는 것은 아닙니다. 요즘 특히 그렇습니다. Windows에서는 모든 것을 수행하는 Microsoft의 소프트웨어 신디사이저가 있습니다. 어쨌든 이것은 당신의 코드와 관련이 없습니다.

+0

감사합니다, 이것은 내가 도착할 결론 인 것 같습니다. 나는 몇몇 친구들이 내 SSCCE를 여러 가지 결과로 테스트 해 보았습니다. 일부 들어, 미디 소리 괜찮지 만, 일부는 내 같은 문제가 있습니다. – Cazra

관련 문제