You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java实现音频播放暂停循环时触发mark/reset not supported异常

问题排查与修复方案

核心错误点

  • 重复弹出文件选择框:按钮点击回调中连续两次调用jfc.showOpenDialog(this),第一次选择完成进入判断分支后,又触发了第二次文件选择弹窗,必须连续选两次才能走后续逻辑。
  • mark/reset not supported异常来源:
    1. 手动创建FileInputStream传入AudioSystem.getAudioInputStream(),普通FileInputStream不支持流标记/回退操作,Java Sound API解析音频头时需要回退流位置,直接触发IO异常;
    2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 07:33:23