package com.example.demoapplication; // 定义包名
// 导入Android音频相关的类
import android.media.AudioTrack;
import android.Manifest;
import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.MediaRecorder;
// 导入Android系统组件相关类
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
// 导入语音合成相关类
import android.speech.tts.TextToSpeech;
// 导入Android工具类
import android.util.Base64;
import android.util.Log;
// 导入Android UI组件
import android.widget.Button;
import android.widget.Toast;
// 导入Android兼容库组件
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
// 导入JSON处理库
import org.json.JSONException;
import org.json.JSONObject;
// 导入IO流相关类
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
// 导入网络通信相关类
import java.net.ServerSocket;
import java.net.Socket;
// 导入数据结构和并发工具
import java.util.LinkedList;
import java.util.Locale;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 主活动类,实现音频录制、播放、网络通信和TTS功能
*/
public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener {
private static final String TAG = "AudioRecorder"; // 日志标签
// 按钮成员变量
private Button startRecordButton, stopRecordButton;
private Button playSoundButton, pauseSoundButton, stopSoundButton, resumeSoundButton, clearSoundsButton;
// 音频录制对象
private AudioRecord audioRecord;
// 音频采样率常量
private static final int SAMPLE_RATE = 16000;
// 缓冲区大小常量
private static final int BUFFER_SIZE;
// 静态代码块初始化缓冲区大小
static {
// 获取最小缓冲区大小
int minBufferSize = AudioRecord.getMinBufferSize(
SAMPLE_RATE,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT
);
// 取较大值作为实际使用的缓冲区大小
BUFFER_SIZE = Math.max(minBufferSize, 4096);
}
// 调度执行器
private ScheduledExecutorService scheduler;
// 录音状态标志
private AtomicBoolean isRecording = new AtomicBoolean(false);
// 权限请求码
private static final int PERMISSION_REQUEST_CODE = 1;
// 线程池服务
private final ExecutorService executorService = Executors.newCachedThreadPool();
// 服务器套接字
private ServerSocket serverSocket;
// 服务器运行状态
private volatile boolean isServerRunning = true;
// 客户端套接字
private volatile Socket clientSocket;
// 套接字写入器
private volatile BufferedWriter socketWriter;
// 文语转换引擎
private TextToSpeech ttsEngine;
// TTS初始化状态
private boolean isTtsInitialized = false;
// 播放队列相关变量
private final Queue<byte[]> playbackQueue = new LinkedList<>();
private final Queue<byte[]> pausedQueue = new LinkedList<>();
private final AtomicBoolean isPlaying = new AtomicBoolean(false);
private final AtomicBoolean isPaused = new AtomicBoolean(false);
private volatile boolean isPlaybackThreadActive = false;
// ... existing code ...
// 对象锁用于同步访问
private final Object audioTrackLock = new Object();
private final Object playbackQueueLock = new Object();
private final Object recordingQueueLock = new Object();
// 新增的成员变量 - 音频播放器
private AudioTrack audioTrack; // 新增的成员变量
// ... existing code ...
// 录制音频数据队列
private final Queue<byte[]> recordingQueue = new LinkedList<>();
// 主线程消息处理器
private final Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 0x11: // 客户端连接
Toast.makeText(MainActivity.this, "客户端已连接", Toast.LENGTH_SHORT).show();
break;
case 0x12: // 开始录音
Toast.makeText(MainActivity.this, "开始录音", Toast.LENGTH_SHORT).show();
sendJsonPacket("startRecorder", null);
playTts("开始录音");
break;
case 0x14: // 停止录音
Toast.makeText(MainActivity.this, "停止录音", Toast.LENGTH_SHORT).show();
sendJsonPacket("stopRecorder", null);
playTts("停止录音");
break;
case 0x16: // 错误消息
Toast.makeText(MainActivity.this, "错误: " + msg.obj, Toast.LENGTH_LONG).show();
break;
case 0x17: // 播放完成
Toast.makeText(MainActivity.this, "播放完成", Toast.LENGTH_SHORT).show();
isPlaying.set(false);
isPlaybackThreadActive = false;
updatePlayButtonsState();
break;
case 0x18: // 添加到播放队列
Toast.makeText(MainActivity.this, "已添加到播放队列", Toast.LENGTH_SHORT).show();
break;
case 0x19: // 更新播放按钮状态
updatePlayButtonsState();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // 调用父类onCreate方法
setContentView(R.layout.activity_main); // 设置布局文件
// 初始化TTS引擎
ttsEngine = new TextToSpeech(this, this);
// 初始化视图组件
initViews();
// 设置点击监听器
setupClickListeners();
// 检查权限
checkPermissions();
// 启动服务器(端口30000)
startServer(30000);
// 启动套接字监听
startSocketListener();
}
// 初始化视图组件
private void initViews() {
// 绑定按钮控件
startRecordButton = findViewById(R.id.startRecordButton);
stopRecordButton = findViewById(R.id.stopRecordButton);
playSoundButton = findViewById(R.id.playSoundButton);
pauseSoundButton = findViewById(R.id.pauseSoundButton);
stopSoundButton = findViewById(R.id.stopSoundButton);
resumeSoundButton = findViewById(R.id.resumeSoundButton);
clearSoundsButton = findViewById(R.id.clearSoundsButton);
// 初始化按钮状态
stopRecordButton.setEnabled(false);
pauseSoundButton.setEnabled(false);
stopSoundButton.setEnabled(false);
resumeSoundButton.setEnabled(false);
}
// 设置点击事件监听器
private void setupClickListeners() {
// 录音控制
startRecordButton.setOnClickListener(v -> startRecording());
stopRecordButton.setOnClickListener(v -> stopRecording());
// 播放控制
playSoundButton.setOnClickListener(v -> playNextInQueue());
pauseSoundButton.setOnClickListener(v -> pausePlayback());
stopSoundButton.setOnClickListener(v -> stopPlayback());
resumeSoundButton.setOnClickListener(v -> resumePlayback());
clearSoundsButton.setOnClickListener(v -> {
clearPlaybackQueue();
handler.sendEmptyMessage(0x19);
});
}
// ==================== 录音功能 ====================
// 开始录音
private void startRecording() {
// 检查录音权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
sendErrorMessage("没有录音权限");
return;
}
// 如果已经在录音则先释放资源
if (isRecording.get()) {
releaseAudioResources();
}
try {
// 创建AudioRecord对象
audioRecord = new AudioRecord(
MediaRecorder.AudioSource.MIC,
SAMPLE_RATE,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
BUFFER_SIZE
);
// 检查初始化状态
if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
throw new IllegalStateException("AudioRecord 初始化失败");
}
// 开始录音
audioRecord.startRecording();
isRecording.set(true);
startRecordButton.setEnabled(false);
stopRecordButton.setEnabled(true);
updatePlayButtonsState();
// 关闭之前的调度器
if (scheduler != null && !scheduler.isShutdown()) {
scheduler.shutdownNow();
}
// 创建新的调度器
scheduler = Executors.newSingleThreadScheduledExecutor();
// 每100毫秒采集一次音频数据
scheduler.scheduleAtFixedRate(this::captureAudioData, 0, 100, TimeUnit.MILLISECONDS);
handler.sendEmptyMessage(0x12);
} catch (Exception e) {
Log.e(TAG, "录音启动失败", e);
sendErrorMessage("录音启动失败: " + e.getMessage());
releaseAudioResources();
}
}
// 停止录音
private void stopRecording() {
if (!isRecording.get()) return;
isRecording.set(false);
releaseAudioResources();
stopRecordButton.setEnabled(false);
startRecordButton.setEnabled(true);
updatePlayButtonsState();
handler.sendEmptyMessage(0x14);
}
// 采集音频数据
private void captureAudioData() {
if (!isRecording.get() || audioRecord == null) return;
byte[] buffer = new byte[BUFFER_SIZE];
try {
int bytesRead = audioRecord.read(buffer, 0, BUFFER_SIZE);
if (bytesRead > 0) {
synchronized (recordingQueueLock) {
recordingQueue.offer(buffer.clone());
}
String base64Data = Base64.encodeToString(buffer, Base64.DEFAULT);
sendJsonPacket("recording", base64Data);
}
} catch (Exception e) {
Log.e(TAG, "音频采集失败", e);
}
}
// ==================== 录音功能结束 ====================
// ==================== 播放队列管理 ====================
// 将声音数据添加到播放队列
private void addSoundToQueue(byte[] soundData) {
synchronized (playbackQueueLock) {
playbackQueue.offer(soundData);
}
handler.sendEmptyMessage(0x18);
// 如果没有在播放且没有激活的播放线程,则开始播放
if (!isPlaying.get() && !isPlaybackThreadActive) {
handler.post(this::playNextInQueue);
}
}
// 播放下一个队列中的音频
private void playNextInQueue() {
if (isPlaybackThreadActive) {
return;
}
isPlaybackThreadActive = true;
new Thread(() -> {
try {
while (!playbackQueue.isEmpty() && !isPaused.get()) {
byte[] soundData;
synchronized (playbackQueueLock) {
soundData = playbackQueue.poll();
}
if (soundData != null) {
playSoundDirectly(soundData);
}
}
if (!isPaused.get()) {
isPlaying.set(false);
isPlaybackThreadActive = false;
handler.sendEmptyMessage(0x17);
}
} catch (Exception e) {
Log.e(TAG, "播放队列处理失败", e);
sendErrorMessage("播放失败: " + e.getMessage());
}
}).start();
}
// 直接播放音频
private void playSoundDirectly(byte[] soundData) {
if (soundData == null || soundData.length == 0) {
return;
}
try {
isPlaying.set(true);
isPaused.set(false);
synchronized (audioTrackLock) {
int bufferSize = AudioTrack.getMinBufferSize(
SAMPLE_RATE,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT
);
if (audioTrack != null) {
audioTrack.release();
}
audioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC,
SAMPLE_RATE,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize,
AudioTrack.MODE_STREAM
);
if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
throw new IllegalStateException("AudioTrack 初始化失败");
}
audioTrack.play();
ByteArrayInputStream inputStream = new ByteArrayInputStream(soundData);
byte[] buffer = new byte[bufferSize];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1 && !isPaused.get()) {
audioTrack.write(buffer, 0, bytesRead);
}
audioTrack.stop();
audioTrack.release();
audioTrack = null;
if (!isPaused.get()) {
runOnUiThread(this::updatePlayButtonsState);
}
}
} catch (Exception e) {
Log.e(TAG, "音频播放失败", e);
sendErrorMessage("播放失败: " + e.getMessage());
}
}
// 暂停播放
private void pausePlayback() {
if (!isPlaying.get() || isPaused.get()) {
return;
}
isPaused.set(true);
synchronized (audioTrackLock) {
if (audioTrack != null) {
audioTrack.pause();
}
}
// 将当前播放的数据移到暂停队列
byte[] currentData = null;
synchronized (playbackQueueLock) {
if (!playbackQueue.isEmpty()) {
currentData = playbackQueue.poll();
}
}
if (currentData != null) {
synchronized (playbackQueueLock) {
pausedQueue.offer(currentData);
}
}
updatePlayButtonsState();
}
// 恢复播放
private void resumePlayback() {
if (!isPaused.get()) {
return;
}
isPaused.set(false);
// 将暂停的数据移回播放队列
byte[] pausedData = null;
synchronized (playbackQueueLock) {
if (!pausedQueue.isEmpty()) {
pausedData = pausedQueue.poll();
}
}
if (pausedData != null) {
synchronized (playbackQueueLock) {
playbackQueue.offer(pausedData);
}
}
synchronized (audioTrackLock) {
if (audioTrack != null && audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) {
audioTrack.play();
}
}
if (!isPlaybackThreadActive) {
new Thread(() -> {
isPlaybackThreadActive = true;
try {
while (!playbackQueue.isEmpty() && !isPaused.get()) {
byte[] soundData;
synchronized (playbackQueueLock) {
soundData = playbackQueue.poll();
}
if (soundData != null) {
playSoundDirectly(soundData);
}
}
if (!isPaused.get()) {
isPlaying.set(false);
isPlaybackThreadActive = false;
handler.sendEmptyMessage(0x17);
}
} catch (Exception e) {
Log.e(TAG, "恢复播放失败", e);
sendErrorMessage("恢复播放失败: " + e.getMessage());
}
}).start();
}
updatePlayButtonsState();
}
// 停止播放
private void stopPlayback() {
if (!isPlaying.get() && !isPaused.get()) {
return;
}
isPlaying.set(false);
isPaused.set(false);
synchronized (playbackQueueLock) {
playbackQueue.clear();
if (!pausedQueue.isEmpty()) {
pausedQueue.clear();
}
}
synchronized (audioTrackLock) {
if (audioTrack != null) {
try {
audioTrack.stop();
audioTrack.release();
} catch (Exception e) {
Log.e(TAG, "停止音频播放失败", e);
} finally {
audioTrack = null;
}
}
}
updatePlayButtonsState();
handler.sendEmptyMessage(0x19);
}
// 清空播放队列
private void clearPlaybackQueue() {
synchronized (playbackQueueLock) {
playbackQueue.clear();
pausedQueue.clear();
}
}
// ==================== 播放队列管理结束 ====================
// ==================== 辅助方法 ====================
// 更新播放按钮状态
private void updatePlayButtonsState() {
runOnUiThread(() -> {
boolean hasRecordings = !playbackQueue.isEmpty() || !pausedQueue.isEmpty();
boolean isPlayingState = isPlaying.get() && !isPaused.get();
boolean isPausedState = isPaused.get();
playSoundButton.setEnabled(hasRecordings && !isPlayingState);
pauseSoundButton.setEnabled(isPlayingState);
stopSoundButton.setEnabled(isPlaying.get() || isPausedState);
resumeSoundButton.setEnabled(isPausedState);
clearSoundsButton.setEnabled(hasRecordings);
});
}
// 播放文本转语音
private void playTts(String text) {
if (isTtsInitialized) {
ttsEngine.speak(text, TextToSpeech.QUEUE_FLUSH, null);
}
}
// 释放音频资源
private void releaseAudioResources() {
if (audioRecord != null) {
try {
if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
audioRecord.stop();
}
audioRecord.release();
} catch (IllegalStateException e) {
Log.e(TAG, "停止录音失败", e);
} finally {
audioRecord = null;
}
}
if (scheduler != null) {
try {
scheduler.shutdownNow();
if (!scheduler.awaitTermination(500, TimeUnit.MILLISECONDS)) {
Log.w(TAG, "录音线程池未正常关闭");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
scheduler = null;
}
}
}
// 发送JSON数据包
private void sendJsonPacket(String type, Object data) {
if (clientSocket == null || clientSocket.isClosed() || socketWriter == null) {
return;
}
try {
JSONObject packet = new JSONObject();
packet.put("type", type);
if (data != null) {
packet.put("data", data);
}
synchronized (this) {
if (socketWriter != null) {
socketWriter.write(packet.toString());
socketWriter.write("\n\n");
socketWriter.flush();
}
}
} catch (Exception e) {
Log.e(TAG, "发送数据包失败: " + type, e);
}
}
// 发送错误消息
private void sendErrorMessage(String message) {
handler.obtainMessage(0x16, message).sendToTarget();
}
// 检查权限
private void checkPermissions() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.RECORD_AUDIO},
PERMISSION_REQUEST_CODE
);
}
}
// 启动服务器
private void startServer(int port) {
executorService.execute(() -> {
try {
serverSocket = new ServerSocket(port);
Log.i(TAG, "服务器启动: " + port);
while (isServerRunning) {
try {
Socket socket = serverSocket.accept();
clientSocket = socket;
synchronized (this) {
socketWriter = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream(), "UTF-8")
);
}
handler.sendEmptyMessage(0x11);
} catch (IOException e) {
if (isServerRunning) Log.e(TAG, "接受连接失败", e);
}
}
} catch (IOException e) {
Log.e(TAG, "服务器启动失败", e);
runOnUiThread(() -> Toast.makeText(
this,
"服务器启动失败: " + e.getMessage(),
Toast.LENGTH_LONG
).show());
} finally {
closeServerSocket();
}
});
}
// 启动套接字监听
private void startSocketListener() {
executorService.execute(() -> {
while (true) {
if (clientSocket != null && !clientSocket.isClosed()) {
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream(), "UTF-8")
);
StringBuilder packetBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
if (line.isEmpty()) {
if (packetBuilder.length() > 0) {
String packet = packetBuilder.toString();
Log.d(TAG, "收到数据包: " + packet);
try {
JSONObject command = new JSONObject(packet);
String type = command.getString("type");
switch (type) {
case "playSound":
String base64Sound = command.optString("data");
if (base64Sound != null && !base64Sound.isEmpty()) {
byte[] soundData = Base64.decode(base64Sound, Base64.DEFAULT);
addSoundToQueue(soundData);
handler.sendEmptyMessage(0x18);
}
break;
case "pauseSound":
pausePlayback();
break;
case "stopSound":
stopPlayback();
break;
case "resumeSound":
resumePlayback();
break;
case "clearSounds":
clearPlaybackQueue();
handler.sendEmptyMessage(0x19);
break;
}
} catch (JSONException e) {
Log.e(TAG, "JSON解析失败", e);
}
packetBuilder.setLength(0);
}
} else {
packetBuilder.append(line);
}
}
} catch (IOException e) {
Log.e(TAG, "Socket读取失败", e);
}
} else {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
});
}
// 关闭服务器套接字
private void closeServerSocket() {
try {
if (serverSocket != null && !serverSocket.isClosed()) {
serverSocket.close();
}
} catch (IOException e) {
Log.w(TAG, "关闭ServerSocket失败", e);
}
}
// TTS初始化回调
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
int result = ttsEngine.setLanguage(Locale.CHINESE);
if (result == TextToSpeech.LANG_MISSING_DATA ||
result == TextToSpeech.LANG_NOT_SUPPORTED) {
Log.e(TAG, "TTS语言不支持中文");
} else {
isTtsInitialized = true;
}
}
}
// 销毁活动时清理资源
@Override
protected void onDestroy() {
super.onDestroy();
isServerRunning = false;
if (ttsEngine != null) {
ttsEngine.stop();
ttsEngine.shutdown();
}
closeServerSocket();
closeSocket(clientSocket);
stopRecording();
executorService.shutdown();
try {
if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
releaseAudioResources();
}
// 关闭套接字
private void closeSocket(Socket socket) {
try {
if (socket != null && !socket.isClosed()) {
socket.close();
}
} catch (IOException e) {
Log.w(TAG, "关闭Socket失败", e);
}
if (socket == clientSocket) {
clientSocket = null;
synchronized (this) {
socketWriter = null;
}
}
}
}
我需要修改当前代码常按开始录音就会录音,松开按钮就会结束录音,类似于微信那种功能包括修改xml文件