如何将 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;
}
}
}
这个事件是否可以在 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;
}
}
}