如何将 JSlider 设置为音频文件的持续时间?

How to set a JSlider to the duration of an audio file?

这个事件是否可以在 JSlider 中实现?另外,能否将滑块移动到音频文件中的某个点?为了详细说明,用户可以将滑块拖动到 JSlider 中的任何点,这会导致播放 "rewind" 或 "skip"。

在这种情况下,你google。不,我是认真的。

我使用了 java wav player adding pause and continue. and How do I get a sound file's total time in Java? 的答案并将它们组合成一个解决方案,允许 JSlider 显示当前播放位置并更改当前播放位置。

尝试解决此类问题时要记住的重要一点是 - 您不太可能找到完全匹配的解决方案,您将不得不调整一些想法来满足您的需求。

虽然下面的 "might" 看起来像是一个成熟的系统,但它只是一个演示 - 它需要大量额外的工作和管理才能使其灵活和健壮 - 这是我要留给你的搞清楚。

核心功能如下...

AudioInputStream ais = null;
try {
    File file = new File(...);
    ais = AudioSystem.getAudioInputStream(file);
    format = ais.getFormat();
    frameCount = ais.getFrameLength();
    duration = ((double) frameCount) / format.getFrameRate();

    clip = AudioSystem.getClip();
    clip.open(ais);
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException ex) {
    ex.printStackTrace();
}

这基本上是加载一个音频文件,获取 frameCount 并使用它来计算持续时间(以秒为单位)。然后它创建一个 Clip 可以用来播放音频文件。

从那里开始,重置只是在播放音频时监视 framePosition(我使用 Swing Timer)并更新状态。

JSlider 被用户更改时,这只是将 framePosition 设置为所需的帧

例如...

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineEvent.Type;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JSlider slider = new JSlider(0, 100);
        private long frameCount;
        private double duration;

        private AudioFormat format;
        private Clip clip;

        private JLabel currentFrame;
        private JLabel currentDuration;

        private boolean playing = false;

        private Timer playTimer;

        private boolean ignoreStateChange = false;

        public TestPane() {
            AudioInputStream ais = null;
            try {
                File file = new File(System.getProperty("user.home") + "/Library/Application Support/Steam/Steam.AppBundle/Steam/Contents/MacOS/friends/voice_hang_up.wav");
                ais = AudioSystem.getAudioInputStream(file);
                format = ais.getFormat();
                frameCount = ais.getFrameLength();
                duration = ((double) frameCount) / format.getFrameRate();

                clip = AudioSystem.getClip();
                clip.open(ais);
            } catch (UnsupportedAudioFileException | IOException | LineUnavailableException ex) {
                ex.printStackTrace();
            }
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            setLayout(new GridBagLayout());
            add(slider, gbc);
            slider.setValue(0);

            add(new JLabel("Total Frames: " + frameCount), gbc);
            add(new JLabel("Total Duration: " + duration), gbc);

            currentFrame = new JLabel("Current frame: 0");
            currentDuration = new JLabel("Current duration: 0");

            add(currentFrame, gbc);
            add(currentDuration, gbc);

            JButton action = new JButton("Play");
            action.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (!playing) {
                        int frame = getDesiredFrame();
                        if (frame >= frameCount) {
                            frame = 0;
                        }
                        clip.setFramePosition(frame);
                        clip.start();
                        action.setText("Stop");
                        playing = true;
                        playTimer.start();
                    } else {
                        clip.stop();
                        action.setText("Play");
                        playing = false;
                        playTimer.stop();
                    }
                }
            });

            clip.addLineListener(new LineListener() {
                @Override
                public void update(LineEvent event) {
                    if (event.getType().equals(Type.STOP)
                                    || event.getType().equals(Type.CLOSE)) {
                        action.setText("Play");                     
                        playing = false;
                        playTimer.stop();
                        updateState();
                    }
                }
            });

            add(action, gbc);

            playTimer = new Timer(100, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    updateState();
                }
            });

            Timer delayedUpdate = new Timer(250, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    int frame = getDesiredFrame();
                    clip.setFramePosition(frame);

                    double time = getCurrentTime();

                    currentFrame.setText("Current frame: " + frame);
                    currentDuration.setText("Current duration: " + time);

                }
            });
            delayedUpdate.setRepeats(false);
            slider.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {
                    if (ignoreStateChange) {
                        return;
                    }
                    delayedUpdate.restart();
                }
            });
        }

        public void updateState() {
            ignoreStateChange = true;
            int frame = clip.getFramePosition();
            int progress = (int) (((double) frame / (double) frameCount) * 100);
            slider.setValue(progress);
            currentFrame.setText("Current frame: " + getDesiredFrame());
            currentDuration.setText("Current duration: " + getCurrentTime());
            ignoreStateChange = false;
        }

        public double getCurrentTime() {
            int currentFrame = clip.getFramePosition();
            double time = (double) currentFrame / format.getFrameRate();
            return time;
        }

        public int getDesiredFrame() {
            int progress = slider.getValue();
            double frame = ((double) frameCount * ((double) progress / 100.0));
            return (int) frame;
        }

    }

}