唐浩
1 天以前 3d631dbadf98f3f77c6e955cb36219217ef73ab5
app/src/main/java/com/ziang/driver/utils/AudioRecoderUtils.java
@@ -10,10 +10,49 @@
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
import cn.sinata.xldutils.utils.TimeUtils;
public class AudioRecoderUtils {
    // --- 单例实例 ---
    private static AudioRecoderUtils instance; // 静态变量,持有唯一实例
    // 私有化构造函数,防止外部直接创建新实例
    private AudioRecoderUtils() {
        // 默认保存路径为/sdcard/record/下
        this(Environment.getExternalStorageDirectory() + "/record/");
    }
    // 私有化带 filePath 的构造函数
    private AudioRecoderUtils(String filePath) {
        File path = new File(filePath);
        if (!path.exists())
            path.mkdirs();
        this.FolderPath = filePath;
    }
    // --- 获取单例的公共静态方法 ---
    // 使用 synchronized 确保线程安全,防止多线程环境下创建多个实例
    public static synchronized AudioRecoderUtils getInstance() {
        if (instance == null) {
            instance = new AudioRecoderUtils();
        }
        return instance;
    }
    // 如果需要指定文件路径,可以提供另一个获取实例的方法
    public static synchronized AudioRecoderUtils getInstance(String filePath) {
        if (instance == null) {
            instance = new AudioRecoderUtils(filePath);
        }
        // 如果实例已存在但路径不同,这里通常不修改路径。
        // 若要支持动态路径,需要更复杂的逻辑,或者在外部处理路径逻辑。
        return instance;
    }
    // --- 单例修改结束 ---
    //文件路径
    private String filePath;
@@ -27,130 +66,13 @@
    private OnAudioStatusUpdateListener audioStatusUpdateListener;
    private MediaPlayer mMediaPlayer;
    /**
     * 文件存储默认sdcard/record
     */
    public AudioRecoderUtils() {
        //默认保存路径为/sdcard/record/下
        this(Environment.getExternalStorageDirectory() + "/record/");
    }
    public AudioRecoderUtils(String filePath) {
        File path = new File(filePath);
        if (!path.exists())
            path.mkdirs();
        this.FolderPath = filePath;
    }
    // --- 新增:播放队列和状态变量 ---
    private Queue<String> playQueue = new LinkedList<>(); // 存储待播放的音频文件路径
    private boolean isPlayingQueue = false; // 标记是否正在播放队列中的音频
    // --- 新增结束 ---
    private long startTime;
    private long endTime;
    /**
     * 开始录音 使用amr格式
     * 录音文件
     *
     * @return
     */
    public void startRecord() {
        // 开始录音
        /* ①Initial:实例化MediaRecorder对象 */
        if (mMediaRecorder == null)
            mMediaRecorder = new MediaRecorder();
        try {
            /* ②setAudioSource/setVedioSource */
            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置麦克风
            /* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */
            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);
            /*
             * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式
             * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB)
             */
            mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
            filePath = FolderPath + TimeUtils.getCurrentTime() + ".caf";
            /* ③准备 */
            mMediaRecorder.setOutputFile(filePath);
            mMediaRecorder.setMaxDuration(MAX_LENGTH);
            mMediaRecorder.prepare();
            /* ④开始 */
            mMediaRecorder.start();
            // AudioRecord audioRecord.
            /* 获取开始时间* */
            startTime = System.currentTimeMillis();
            updateMicStatus();
            Log.e("fan", "startTime" + startTime);
        } catch (IllegalStateException e) {
            Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage());
        } catch (IOException e) {
            Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage());
        }
    }
    /**
     * 停止录音
     */
    public long stopRecord() {
        if (mMediaRecorder == null)
            return 0L;
        endTime = System.currentTimeMillis();
        //有一些网友反应在5.0以上在调用stop的时候会报错,翻阅了一下谷歌文档发现上面确实写的有可能会报错的情况,捕获异常清理一下就行了,感谢大家反馈!
        try {
            mMediaRecorder.stop();
            mMediaRecorder.reset();
            mMediaRecorder.release();
            mMediaRecorder = null;
            audioStatusUpdateListener.onStop(filePath);
            filePath = "";
        } catch (RuntimeException e) {
            if (mMediaRecorder != null) {
                mMediaRecorder.reset();
                mMediaRecorder.release();
                mMediaRecorder = null;
            }
            File file = new File(filePath);
            if (file.exists())
                file.delete();
            filePath = "";
        }
        return endTime - startTime;
    }
    public String getFilePath() {
        return filePath;
    }
    /**
     * 取消录音
     */
    public void cancelRecord() {
        try {
            mMediaRecorder.stop();
            mMediaRecorder.reset();
            mMediaRecorder.release();
            mMediaRecorder = null;
        } catch (RuntimeException e) {
            mMediaRecorder.reset();
            mMediaRecorder.release();
            mMediaRecorder = null;
        }
        File file = new File(filePath);
        if (file.exists())
            file.delete();
        filePath = "";
    }
    private final Handler mHandler = new Handler();
    private Runnable mUpdateMicStatusTimer = new Runnable() {
@@ -163,9 +85,7 @@
    private int BASE = 1;
    private int SPACE = 100;// 间隔取样时间
    public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener audioStatusUpdateListener) {
        this.audioStatusUpdateListener = audioStatusUpdateListener;
    }
    /**
     * 更新麦克状态
@@ -206,80 +126,150 @@
        public void onFinishPlay();
    }
    //开始播放录音
    public void startplayMusic(Context context, String filePath) {
        if (mMediaPlayer != null) {
            stopPlayMusic();
        }
        mMediaPlayer = new MediaPlayer();
        try {
            Log.e("mmp", "path:" + filePath);
            mMediaPlayer.setDataSource(context, Uri.parse(filePath));
            mMediaPlayer.prepareAsync();
            mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                    // 装载完毕回调
                    mMediaPlayer.start();
                    if (audioStatusUpdateListener != null)
                        audioStatusUpdateListener.onStartPlay();
                }
            });
            mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    if (audioStatusUpdateListener != null)
                        audioStatusUpdateListener.onFinishPlay();
                    stopPlayMusic();
//                    BaseEvent baseEvent = new BaseEvent(BaseEvent.SEND_AODIO_PLAY_OVER);
//                    baseEvent.setMsg(filePath);
//                    EventBus.getDefault().post(baseEvent);
                }
            });
        // 将新的音频路径添加到播放队列
        playQueue.offer(filePath); // 将文件路径添加到队列尾部
        Log.e("AudioRecoderUtils", "Added to queue: " + filePath + ", Queue size: " + playQueue.size());
        } catch (Exception e) {
            e.printStackTrace();
        // 如果当前没有在播放,则开始播放队列中的第一个
        if (!isPlayingQueue) {
            playNextInQueue(context);
        }
    }
    //结束录音播放
    public void stopPlayMusic() {
        if (mMediaPlayer == null)
            return;
        try {
            mMediaPlayer.stop();
            mMediaPlayer.release();
            if (audioStatusUpdateListener != null)
    // --- 新增:播放队列中的下一个音频 ---
    private void playNextInQueue(Context context) {
        if (playQueue.isEmpty()) {
            isPlayingQueue = false;
            Log.d(TAG, "playNextInQueue: Play queue finished.");
            if (audioStatusUpdateListener != null) {
                audioStatusUpdateListener.onFinishPlay();
            }
            return;
        }
        String currentFilePath = playQueue.poll();
        Log.d(TAG, "playNextInQueue: Now attempting to play from queue: " + currentFilePath);
        // 确保每次播放前,旧的 MediaPlayer 都已经被彻底释放
        // 这行虽然在前面已经有,但此处是核心逻辑,再次强调其重要性
        if (mMediaPlayer != null) {
            stopPlayMusic(); // 确保旧的播放器已完全释放
        }
        mMediaPlayer = new MediaPlayer();
        isPlayingQueue = true; // 标记正在播放队列中的音频
        try {
            mMediaPlayer.setDataSource(context, Uri.parse(currentFilePath));
            Log.d(TAG, "playNextInQueue: Set data source for: " + currentFilePath + ", preparing async.");
            mMediaPlayer.prepareAsync();
            mMediaPlayer.setOnPreparedListener(mp -> {
                Log.d(TAG, "onPrepared: MediaPlayer prepared for " + currentFilePath + ", starting playback.");
                mp.start(); // 这里使用 mp,它是 MediaPlayer 实例本身
                if (audioStatusUpdateListener != null)
                    audioStatusUpdateListener.onStartPlay();
            });
            mMediaPlayer.setOnCompletionListener(mp -> {
                Log.d(TAG, "onCompletion: Playback finished for " + currentFilePath + ", moving to next.");
                // 注意:onFinishPlay() 应该在整个队列播放完毕时才触发,而不是单个文件
                stopPlayMusic(); // 播放完成后释放当前播放器
                playNextInQueue(context); // 递归调用,播放队列中的下一个
            });
            mMediaPlayer.setOnErrorListener((mp, what, extra) -> {
                Log.e(TAG, "onError: MediaPlayer error occurred: what=" + what + ", extra=" + extra + " for " + currentFilePath);
                // 错误发生时,也要通知UI(如果需要),并尝试播放下一个
                if (audioStatusUpdateListener != null)
                    audioStatusUpdateListener.onFinishPlay(); // 通知当前文件播放失败
                stopPlayMusic(); // 释放当前播放器
                playNextInQueue(context); // 继续播放队列中的下一个
                return true; // 表示已处理错误
            });
        } catch (IOException e) {
            // IOException 通常是文件不存在、无法读取或URL无效
            Log.e(TAG, "IOException during MediaPlayer setup for " + currentFilePath, e);
            // 捕获异常后,也要尝试播放下一个
            if (audioStatusUpdateListener != null)
                audioStatusUpdateListener.onFinishPlay(); // 通知播放失败
            stopPlayMusic(); // 释放当前播放器
            playNextInQueue(context); // 继续播放队列中的下一个
        } catch (IllegalArgumentException e) {
            // IllegalArgumentException 可能是URI格式不正确等
            Log.e(TAG, "IllegalArgumentException during MediaPlayer setup for " + currentFilePath, e);
            if (audioStatusUpdateListener != null)
                audioStatusUpdateListener.onFinishPlay(); // 通知播放失败
            stopPlayMusic();
            playNextInQueue(context);
        } catch (SecurityException e) {
            // SecurityException 可能是权限问题
            Log.e(TAG, "SecurityException during MediaPlayer setup for " + currentFilePath, e);
            if (audioStatusUpdateListener != null)
                audioStatusUpdateListener.onFinishPlay(); // 通知播放失败
            stopPlayMusic();
            playNextInQueue(context);
        } catch (IllegalStateException e) {
            // IllegalStateException 可能是 MediaPlayer 状态不正确
            Log.e(TAG, "IllegalStateException during MediaPlayer setup for " + currentFilePath, e);
            if (audioStatusUpdateListener != null)
                audioStatusUpdateListener.onFinishPlay(); // 通知播放失败
            stopPlayMusic();
            playNextInQueue(context);
        } catch (Exception e) { // 最后捕获所有其他未知异常
            Log.e(TAG, "Generic Exception during MediaPlayer setup for " + currentFilePath, e);
            e.printStackTrace();
            if (audioStatusUpdateListener != null)
                audioStatusUpdateListener.onFinishPlay(); // 通知播放失败
            stopPlayMusic();
            playNextInQueue(context);
        }
    }
    // --- 新增结束 ---
    public void stopPlayMusic() {
//        playQueue.clear();
        isPlayingQueue = false;
        if (mMediaPlayer == null) {
            Log.d(TAG, "stopPlayMusic: MediaPlayer is null, nothing to stop.");
            return;
        }
        try {
            // 检查 MediaPlayer 是否处于可停止的状态
            // 如果正在准备或处于错误状态,直接 release 可能更安全
            if (mMediaPlayer.isPlaying()) {
                mMediaPlayer.stop(); // 停止当前播放
                Log.d(TAG, "stopPlayMusic: Stopped active MediaPlayer.");
            }
            mMediaPlayer.release(); // 释放资源
            mMediaPlayer = null; // 置为 null,防止内存泄漏和引用旧对象
            Log.d(TAG, "stopPlayMusic: MediaPlayer released and set to null.");
        } catch (IllegalStateException e) {
            // 捕获 MediaPlayer 状态异常,例如在 prepareAsync() 期间调用 stop()
            Log.e(TAG, "stopPlayMusic: IllegalStateException during stop/release.", e);
            // 即使异常,也要尝试释放并置null,防止后续问题
            if (mMediaPlayer != null) {
                mMediaPlayer.release();
                mMediaPlayer = null;
            }
        } catch (Exception e) {
            // 捕获其他未知异常
            Log.e(TAG, "stopPlayMusic: General Exception during stop/release.", e);
            if (mMediaPlayer != null) {
                mMediaPlayer.release();
                mMediaPlayer = null;
            }
        }
    }
    /**
     * 获取文件长度
     *
     * @param context
     * @param filePath
     * @return
     */
    public int getMusicTime(Context context, String filePath) {
        if (mMediaPlayer != null) {
            stopPlayMusic();
        }
        mMediaPlayer = new MediaPlayer();
        try {
            mMediaPlayer.setDataSource(context, Uri.parse(filePath));
            mMediaPlayer.prepare();
            int duration = mMediaPlayer.getDuration();
            if (0 != duration) {
                mMediaPlayer.release();
                return duration / 1000;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0;
    // 新增设置监听器的方法
    public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener audioStatusUpdateListener) {
        this.audioStatusUpdateListener = audioStatusUpdateListener;
    }
}