将重复功能添加到简单的MIDI播放器

时间:2013-06-23 20:12:08

标签: java multithreading swing midi swingworker

我正在尝试在自定义MIDI播放器上实现重复功能,但我无法实现重复功能。以下是我正在使用的课程:

NotePlayer - 使用Java的MIDI包播放MIDI音符。

GuitarTunerGUI

  • NotePlayer类的接口。
  • 为每个吉他弦提供六个JButton,一个用于选择所需调音的JComboBox和一个用于切换重复功能的JCheckBox。
  • 提供toggleRepeat()来切换repeatEnabled字段,GuitarTunerGUI类的私有字段。

我创建了一个SwingWorker,负责在单独的线程中播放MIDI音符。这解决了在播放音符时保持GUI响应的问题。 但是,当启用重复并且用户按下多个按钮时会出现问题。

当用户按下六个JButton中的一个时,监听器执行以下操作:

public void actionPerformed(ActionEvent event) {
    // The note param is a private field of the listener object
    MusicianWorker clapton = new MusicianWorker(note);
    clapton.execute();
}

execute方法执行以下操作:

protected Void doInBackground() throws Exception {
    do {
        NotePlayer.playNote(thisNote);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException ex) {
            System.out.println(ex.getMessage());
        }
    } while (repeatEnabled);

    return null;
}

当用户按下多个按钮而不切换重复时,会出现问题。例如,当按顺序按下“A”按钮和“E”按钮时,会创建两个线程,并且“A”和“E”音符重复播放,直到repeatEnabled被切换了。当用户推送JButton时,我需要首先确定当前是否正在执行任何工作线程,如果是,则在播放指定的注释之前杀死这些线程。提前感谢您的时间和反馈。

2 个答案:

答案 0 :(得分:0)

您需要维护员工之间的共享状态。引入新的布尔变量“播放”。在执行之前检查播放标志是否设置为true,执行后再将其设置为false。

答案 1 :(得分:0)

您提供的代码非常棒,只需稍微调整一下即可。当您创建SwingWorker时,您应该在实例变量中跟踪它(如果您想要在某个时间点播放多个音符,可能在List中)。然后,在播放新音符之前,检查最后一个音符是否已完成,如果没有,则取消它。

取消是否会对您的MusicianWorker产生任何影响取决于您。工作线程将被中断,这意味着如果Thread.sleep方法正在运行,它将提前终止 - 您必须检查您的文档以查看它对NotePlayer会产生什么影响。

最后,您似乎根本不需要使用SwingWorker,因为您的后台任务没有与UI交互。您可能需要调查Executors

您可以尝试这样的事情:

public class AlbertHall {
  private final ExecutorService es = Executors.newSingleThreadExecutor();
  // No longer a local variable in the listener
  private Future<Void> clapton; // ... or maybe a Collection of Futures

  private class Listener implements ActionListener {
    private final Note note;

    public Listener(Note note) {
      this.note = note;
    }

    public void actionPerformed(ActionEvent event) {
      // Watch out, Clapton may finish after you have asked if he is done
      // but before you call cancel
      if (clapton != null && !clapton.isDone()) clapton.cancel(true);

      // You may need to have a wait loop here if Clapton takes a while 
      // to leave the stage

      // Next note
      clapton = es.submit(new MusicianWorker(note));
    }
  }

  static class MusicianWorker implements Runnable {
    private final Note note;

    public MusicianWorker(Note note) {
      this.note = note;
    }

    public void run() {
      boolean cancelRequested = false;
      do {
        NotePlayer.playNote(thisNote);
        try {
          Thread.sleep(3000);
        } catch (InterruptedException ex) {
          // Looks like we got cancelled
          cancelRequested = true;
        }
      } while (repeatEnabled && !cancelRequested);
    }
  }
}