简体   繁体   中英

Java Metronome Timer() inconsistent

public void playMet(){
        int tempo = Integer.parseInt(met_speed.getText());
        tempo = tempo/60;
        int delay = tempo*1000;
        new Timer(delay, new ActionListener(){

            public void actionPerformed(ActionEvent e){
                if(Play.isSelected()){
                    System.out.println("beep");
                    playSound("Click1.wav");
                }
            }
            }).start();
    }

Here's the code for my class. It grabs the value from the JTextField/60*1000 which is the milisecond value for when the beep should run. I tested it with only the System.out.println("beep"); line and it worked fine, but when I actually played a sound it delays or skips or doubles the sound.

playSound():

public void playSound(String filename){
        try
        {
            Clip clip = AudioSystem.getClip();
            clip.open(AudioSystem.getAudioInputStream(new File(filename)));
            clip.start();
        }
        catch (Exception exc)
        {
            exc.printStackTrace(System.out);
        }
    }

I'm not sure what's really going on here, any suggestions?

edit:

public void playMet(){
        int tempo = Integer.parseInt(met_speed.getText());
        tempo = tempo/60;
        int delay = tempo*1000;
        try
        {
            Clip clip = AudioSystem.getClip();
            clip.open(AudioSystem.getAudioInputStream(new File("Click1.wav")));
            clip.start();
        }
        catch (Exception exc)
        {
            exc.printStackTrace(System.out);
        }
        new Timer(delay, new ActionListener(){

            public void actionPerformed(ActionEvent e){
                if(Play.isSelected()){
                    System.out.println("beep");
                    clip.start();
                }
            }
            }).start();
    }

Edit 2: tried a different approach

    public void playMet(){
        int tempo = Integer.parseInt(met_speed.getText());
        tempo = tempo/60;
        int delay = tempo*1000; 

        if(Play.isSelected()){
        try
        {
             FileInputStream in = new FileInputStream(new File("Click1.wav"));

             AudioStream as = new AudioStream(in);
             AudioPlayer.player.start(as);

             Thread.sleep(tempo*1000);

         } catch (Exception e)
         {
             JOptionPane.showMessageDialog(null, e);
         }
        playMet();
        } 
        else
            System.out.println("not playing");
        }

Not it plays at the correct speed consistently, but it freezes and the play button can no loner be toggled.

If I remove the playMet() towards the end it works but only plays once. If I make it a while instead of if loop, it freezes like the code above.

Most of the work could be done ahead of time. Try constructing the clip before the timer starts, and just having

clip.start();

in the event handler. Try

public void playMet(){
    int tempo = Integer.parseInt(met_speed.getText());
    tempo = tempo/60;
    int delay = tempo*1000;
    try
    {
        final Clip clip = AudioSystem.getClip();
        clip.open(AudioSystem.getAudioInputStream(new File("Click1.wav")));
        new Timer(delay, new ActionListener(){

          public void actionPerformed(ActionEvent e){
            if(Play.isSelected()){
                System.out.println("beep");
                clip.start();
            }
          }
        }).start();
    }
    catch (Exception exc)
    {
        exc.printStackTrace(System.out);
    }

}

A Timer is LIMITED to the speed you give it, however if the code takes too long to execute, then the timer starts to be a little out of sync causing delays or sometimes it will speed up and execute more than one loop in quick succession.

The only real solution is to make the code inside your loop run faster, from my experience getting an audio clip and opening it are pretty expensive operations, so try to do those outside the loop.

EDIT: An example of how to open it outside the loop

public void playMet(){
    int tempo = Integer.parseInt(met_speed.getText());
    tempo = tempo/60;
    int delay = tempo*1000;
    try {
        Clip clip = AudioSystem.getClip();
        clip.open(AudioSystem.getAudioInputStream(new File(filename)));

    }
    catch (Exception exc) {
        exc.printStackTrace(System.out);
    }
    new Timer(delay, new ActionListener(){

        public void actionPerformed(ActionEvent e){
            if(Play.isSelected()){
                System.out.println("beep");
                clip.start();
            }
        }
        }).start();
    }

However I believe this forces you to declare the clip as final, another way to do it:

public Class {
private boolean firstTimeThrough;
public void playMet(){
    int tempo = Integer.parseInt(met_speed.getText());
    tempo = tempo/60;
    int delay = tempo*1000;
    firstTimeThrough = true;
    new Timer(delay, new ActionListener(){

        public void actionPerformed(ActionEvent e){
            if(Play.isSelected()){
                if(firstTimeThrough){
                try {
                   Clip clip = AudioSystem.getClip();
                   clip.open(AudioSystem.getAudioInputStream(new File(filename)));
                   firstTimeThrough = false;
                   }
                   catch (Exception exc) {
                   exc.printStackTrace(System.out);
                   }
                }
                System.out.println("beep");
                clip.start();
            }
        }
        }).start();
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM