StdAudio类使我的JFrame无法显示

时间:2015-06-28 14:12:57

标签: java multithreading swing event-dispatch-thread

我正在尝试编写一个播放音乐和弦的程序。我想添加一个窗口,显示一个进度条,显示和弦的播放时间以及完成的次数。为了演奏和弦,我一直在使用StdAudio class的略微修改版本。到目前为止,当我要求和弦播放时,我会运行以下代码。

public static void playNotes(double[] frequencies, double duration, double amplitude)
{
    PlayAudioGUI g = new PlayAudioGUI(duration);
    g.run();

    amp = amplitude;
    ArrayList<double[]> chord = new ArrayList<double[]>();
    for(double freq : frequencies) {
        double[] note = StdAudio.tone(freq, duration);
        chord.add(note);
    }

    double[] chordCombined = new double[chord.get(0).length];
    for (int i = 0; i < chordCombined.length; i++) {
        for (double[] note : chord) {
            chordCombined[i] += note[i];
        }
        chordCombined[i] /= chord.size();
    }
    StdAudio.play(chordCombined);
}

我之前从未尝试过多线程,所以我不知道我做错了什么。当我运行代码时,它在播放和弦时显示一个空窗口,然后正确显示窗口。我希望它能够在播放音频的同时显示窗口。

这是我的窗口类的代码。

import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.Timer;

public class PlayAudioGUI implements Runnable {
    private JFrame window;
    private JProgressBar prog;
    private double duration;
    private Timer t;

    class TimerListener implements ActionListener {
        // This runs every few milliseconds, depending on the delay set below
        public void actionPerformed(ActionEvent event) {
            prog.setValue(prog.getValue() + 1);
            // Stop the timer and hide the window when the progress bar
            // completes
            if (prog.getValue() == prog.getMaximum()) {
                t.stop();
                window.setVisible(false);
            }
        }
    }

    public PlayAudioGUI(double duration) {
        this.window = new JFrame("Playing audio...");
        this.duration = duration;
    }

    @Override
    public void run() {
        // Setting up gridbag layout. I will add more components later.
        Container pane = this.window.getContentPane();
        pane.setLayout(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        c.gridx = 0;
        c.gridy = 0;
        c.insets = new Insets(30, 30, 30, 30);

        // Display the approximate duration
        String clippedDuration;
        if (Double.toString(duration).length() > 5) {
            clippedDuration = Double.toString(duration).substring(0, 4);
        } else {
            clippedDuration = Double.toString(duration);
        }
        String message = "Playing audio for " + clippedDuration + " seconds";
        pane.add(new JLabel(message), c);

        // Make a progressbar
        c.gridy = 1;
        this.prog = new JProgressBar();
        this.prog.setMinimum(0);
        this.prog.setMaximum(250);
        pane.add(this.prog, c);

        // More window management stuff
        this.window.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        this.window.pack();
        this.window.setVisible(true);

        // Set up the timer
        ActionListener listener = new TimerListener();
        final int DELAY = (int) (4 * this.duration); // This works, I did the
                                                        // math :)
        t = new Timer(DELAY, listener);
        t.start();
    }
}

感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

建议:

  • 新的依赖对话框窗口应该是一个对话框,例如JDialog,而不是创建整个单独应用程序的新JFrame。
  • 你知道你应该在后台线程中创建你的声音,而你的空白屏幕只是由这个问题引起的,但我看到代码中没有创建线程 - 为什么?
  • 我自己,我不会使用Swing Timer,而是轮询数据,而是在SwingWorker中完成所有操作,在SwingWorker的doInBackground方法中我会更新其进度状态,并且我会添加一个PropertyChangeListener到SwingWorker并监控这种状态。
  • 另外,您几乎不会想要创建一个Runnable类,然后调用它的run()方法。如果您正在创建Runnable以允许它在后台线程中运行,那么您可能会将其放入Thread中,然后在Thread上调用start()。由于上面的代码应该在Swing事件线程上运行,如果没有从这个线程调用它,它应该通过SwingUtilities.invokeLater(myRunnable);
  • 排队。
  • 我不确定如何从StdAudio库中获取进度值。如果有方法,则使用它通过其setProgress(...)方法设置SwingWorker的进度状态。如果没有,那么你可以猜测,或者你可能会更好地使用不确定的进度条。我相信JProgressBar有一个名为setIndeterminate(true)的方法可以用于此。