Java实现音频播放暂停循环时触发mark/reset not supported异常
问题排查与修复方案
核心错误点
- 重复弹出文件选择框:按钮点击回调中连续两次调用
jfc.showOpenDialog(this),第一次选择完成进入判断分支后,又触发了第二次文件选择弹窗,必须连续选两次才能走后续逻辑。 mark/reset not supported异常来源:- 手动创建
FileInputStream传入AudioSystem.getAudioInputStream(),普通FileInputStream不支持流标记/回退操作,Java Sound API解析音频头时需要回退流位置,直接触发IO异常; resetAudioStream方法错误使用class.getResource()加载本地磁盘绝对路径文件,该方法仅用于读取类路径下的资源文件,读取外部文件会触发路径解析错误。
- 手动创建
- Clip API调用顺序错误:代码先执行
clip.start()再执行clip.open(myvoice),Clip必须先完成音频流加载、打开资源后才能调用播放方法,顺序颠倒无法正常发声。 - UI线程阻塞:控制台输入的死循环直接写在Swing事件回调线程中,会直接卡住整个UI界面,所有界面事件都无法响应。
- 原生格式限制:
javax.sound.sampled包默认仅支持WAV、AIFF、AU三种未压缩PCM音频格式,你配置的mp3、m4a、flac等格式默认无法被解析,播放会抛出格式不支持异常。
修复后可运行代码
import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; import javax.sound.sampled.*; import java.awt.*; import java.awt.event.*; import java.io.File; import java.io.IOException; import java.util.Scanner; public class changeyouvoice extends Frame implements WindowListener, ActionListener { JButton upload; JFileChooser jfc; Long currentFrame; Clip clip; String status; // 保存选中的音频文件引用,供重置流时使用 File selectedAudioFile; public static void main(String[] args) { changeyouvoice myWindow = new changeyouvoice(); myWindow.setSize(350, 100); myWindow.setVisible(true); } public changeyouvoice() { jfc = new JFileChooser(); setLayout(new FlowLayout()); addWindowListener(this); upload = new JButton("Upload"); add(upload); upload.addActionListener(this); } public void actionPerformed(ActionEvent e) { if (e.getSource() != upload) { return; } jfc.setDialogTitle("Select your audio file"); jfc.setAcceptAllFileFilterUsed(false); // 测试阶段先只保留wav格式支持,如需播放mp3需额外引入音频解码依赖 FileNameExtensionFilter filter = new FileNameExtensionFilter("WAV Audio", "wav"); jfc.setFileFilter(filter); // 只调用一次文件选择,去掉重复的弹窗逻辑 int returnValue = jfc.showOpenDialog(this); if (returnValue != JFileChooser.APPROVE_OPTION) { return; } selectedAudioFile = jfc.getSelectedFile(); new Thread(() -> { try { // 直接传入File对象获取音频流,避免mark/reset异常 AudioInputStream myvoice = AudioSystem.getAudioInputStream(selectedAudioFile); clip = AudioSystem.getClip(); // 修正调用顺序:先open加载音频,再设置循环、启动播放 clip.open(myvoice); clip.loop(Clip.LOOP_CONTINUOUSLY); clip.start(); status = "play"; Scanner sc = new Scanner(System.in); while (true) { System.out.println("1. pause"); System.out.println("2. resume"); System.out.println("3. restart"); System.out.println("4. stop"); System.out.println("5. Jump to specific time"); int c = sc.nextInt(); switch (c) { case 1: pause(); break; case 2: resumeAudio(); break; case 3: restart(); break; case 4: stop(); break; case 5: System.out.println("Enter time (" + 0 + ", " + clip.getMicrosecondLength() + ")"); long c1 = sc.nextLong(); jump(c1); break; } if (c == 4) break; } sc.close(); } catch (Exception ex) { System.out.println("Error with playing sound."); ex.printStackTrace(); } }).start(); // 控制台逻辑放到独立子线程,避免卡Swing UI } // 暂停音频方法 public void pause() { if ("paused".equals(status)) { System.out.println("audio is already paused"); return; } this.currentFrame = this.clip.getMicrosecondPosition(); clip.stop(); status = "paused"; } // 恢复音频方法 public void resumeAudio() throws UnsupportedAudioFileException, IOException, LineUnavailableException { if ("play".equals(status)) { System.out.println("Audio is already being played"); return; } clip.close(); resetAudioStream(); clip.setMicrosecondPosition(currentFrame); clip.start(); status = "play"; } // 重启音频方法 public void restart() throws IOException, LineUnavailableException, UnsupportedAudioFileException { clip.stop(); clip.close(); resetAudioStream(); currentFrame = 0L; clip.setMicrosecondPosition(0); clip.start(); status = "play"; } // 停止音频方法 public void stop() throws UnsupportedAudioFileException, IOException, LineUnavailableException { currentFrame = 0L; clip.stop(); clip.close(); } // 跳转到指定时间点方法 public void jump(long c) throws UnsupportedAudioFileException, IOException, LineUnavailableException { if (c > 0 && c < clip.getMicrosecondLength()) { clip.stop(); clip.close(); resetAudioStream(); currentFrame = c; clip.setMicrosecondPosition(c); clip.start(); status = "play"; } } // 重置音频流方法 public void resetAudioStream() throws UnsupportedAudioFileException, IOException, LineUnavailableException { // 直接基于本地文件生成音频流,不要用getResource加载外部文件 AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(selectedAudioFile); clip.open(audioInputStream); clip.loop(Clip.LOOP_CONTINUOUSLY); } public void windowClosing(WindowEvent e) { if (clip != null && clip.isOpen()) { clip.close(); } dispose(); System.exit(0); } public void windowOpened(WindowEvent e) {} public void windowActivated(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowDeactivated(WindowEvent e) {} public void windowClosed(WindowEvent e) {} }
补充说明
- 修复后的代码默认仅支持WAV格式播放,如果需要播放MP3等压缩格式,需要引入第三方音频解码库扩展Java Sound的格式支持,否则会抛出
UnsupportedAudioFileException。 - 字符串判断时把常量放在前面(比如
"paused".equals(status))可以避免status为null时触发空指针异常。 - 窗口关闭时增加了Clip资源释放逻辑,避免音频资源泄漏。
内容的提问来源于stack exchange,提问作者Vladimir George
相关产品推荐
相关产品推荐

