唐浩
昨天 3d631dbadf98f3f77c6e955cb36219217ef73ab5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
package com.ziang.driver.utils;
 
import android.content.Context;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
 
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;
    //文件夹路径
    private String FolderPath;
 
    private MediaRecorder mMediaRecorder;
    private final String TAG = "fan";
    public static final int MAX_LENGTH = 1000 * 60;// 最大录音时长1000*60*10;
 
    private OnAudioStatusUpdateListener audioStatusUpdateListener;
    private MediaPlayer mMediaPlayer;
 
    // --- 新增:播放队列和状态变量 ---
    private Queue<String> playQueue = new LinkedList<>(); // 存储待播放的音频文件路径
    private boolean isPlayingQueue = false; // 标记是否正在播放队列中的音频
    // --- 新增结束 ---
 
    private long startTime;
    private long endTime;
 
    private final Handler mHandler = new Handler();
    private Runnable mUpdateMicStatusTimer = new Runnable() {
        public void run() {
            updateMicStatus();
        }
    };
 
 
    private int BASE = 1;
    private int SPACE = 100;// 间隔取样时间
 
 
 
    /**
     * 更新麦克状态
     */
    private void updateMicStatus() {
 
        if (mMediaRecorder != null) {
            double ratio = (double) mMediaRecorder.getMaxAmplitude() / BASE;
            double db = 0;// 分贝
            if (ratio > 1) {
                db = 20 * Math.log10(ratio);
                if (null != audioStatusUpdateListener) {
                    audioStatusUpdateListener.onUpdate(db, System.currentTimeMillis() - startTime);
                }
            }
            mHandler.postDelayed(mUpdateMicStatusTimer, SPACE);
        }
    }
 
    public interface OnAudioStatusUpdateListener {
        /**
         * 录音中...
         *
         * @param db   当前声音分贝
         * @param time 录音时长
         */
        public void onUpdate(double db, long time);
 
        /**
         * 停止录音
         *
         * @param filePath 保存路径
         */
        public void onStop(String filePath);
 
        public void onStartPlay();
 
        public void onFinishPlay();
    }
 
 
    public void startplayMusic(Context context, String filePath) {
        // 将新的音频路径添加到播放队列
        playQueue.offer(filePath); // 将文件路径添加到队列尾部
        Log.e("AudioRecoderUtils", "Added to queue: " + filePath + ", Queue size: " + playQueue.size());
 
        // 如果当前没有在播放,则开始播放队列中的第一个
        if (!isPlayingQueue) {
            playNextInQueue(context);
        }
    }
 
    // --- 新增:播放队列中的下一个音频 ---
    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;
            }
        }
    }
 
    // 新增设置监听器的方法
    public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener audioStatusUpdateListener) {
        this.audioStatusUpdateListener = audioStatusUpdateListener;
    }
 
 
}