package com.example.demoapplication;
import android.Manifest;
import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.speech.tts.TextToSpeech;
import android.util.Base64;
import android.util.Log;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Locale;
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;
public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener {
// 日志标签和UI组件定义
private static final String TAG = "AudioRecorder";
private Button startRecordButton, stopRecordButton;
private AudioRecord audioRecord;
// 音频采样率和缓冲区大小配置
private static final int SAMPLE_RATE = 16000;
private static final int BUFFER_SIZE;
static {
// 计算最小缓冲区大小并确保不小于4096字节
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;
// 文字转语音(TTS)引擎和播放器
private TextToSpeech ttsEngine;
private boolean isTtsInitialized = false;
private AudioTrack audioTrack;
// 控制是否已发送开始/结束提示
private boolean isStartMessageSent = false;
private boolean isStopMessageSent = false;
private boolean isTransmitStarted = false; // 新增标志位控制startTransmit只显示一次
private boolean isStopTransmitSent = false; // 新增标志位控制stopTransmit只显示一次
// 主线程消息处理器
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,但保留标志位用于其他用途
Toast.makeText(MainActivity.this, "开始录音", Toast.LENGTH_SHORT).show();
isStartMessageSent = true;
isStopMessageSent = false;
sendControlPacket("startRecorder"); // 发送开始录音指令给客户端
playTts("开始录音"); // 触发TTS播报"开始录音"
break;
case 0x13: // 数据发送
break;
case 0x14: // 停止录音
// 强制显示Toast,忽略标志位检查
Toast.makeText(MainActivity.this, "停止录音", Toast.LENGTH_SHORT).show();
isStopMessageSent = true;
isStartMessageSent = false;
sendControlPacket("stopRecorder"); // 发送停止录音指令给客户端
playTts("停止录音"); // 触发TTS播报"停止录音"
break;
case 0x15: // 控制指令
Toast.makeText(MainActivity.this, "收到指令: " + msg.obj, Toast.LENGTH_SHORT).show();
break;
case 0x16: // 错误
Toast.makeText(MainActivity.this, "错误: " + msg.obj, Toast.LENGTH_LONG).show();
break;
case 0x18: // TTS音频
handleTtsAudio((String) msg.obj);
break;
case 0x19: // 聊天开始
Toast.makeText(MainActivity.this, "聊天开始: " + msg.obj, Toast.LENGTH_SHORT).show();
break;
case 0x20: // 聊天回复
Toast.makeText(MainActivity.this, "回复: " + msg.obj, Toast.LENGTH_LONG).show();
break;
case 0x21: // 播放完成
Toast.makeText(MainActivity.this, "播放完成", Toast.LENGTH_SHORT).show();
break;
}
}
};
// Activity生命周期方法
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化TTS引擎
ttsEngine = new TextToSpeech(this, this);
initViews();
setupClickListeners();
checkPermissions();
startServer(30000);
}
// 初始化视图控件
private void initViews() {
startRecordButton = findViewById(R.id.startRecordButton);
stopRecordButton = findViewById(R.id.stopRecordButton);
stopRecordButton.setEnabled(false);
}
// 设置按钮点击事件监听器
private void setupClickListeners() {
startRecordButton.setOnClickListener(v -> startRecording());
stopRecordButton.setOnClickListener(v -> stopRecording());
}
// 检查录音权限
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 startRecording() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
sendErrorMessage("没有录音权限");
return;
}
if (isRecording.get()) {
// 允许重复启动:如果已经在录音,先释放资源
releaseAudioResources();
}
if (clientSocket == null || clientSocket.isClosed() || socketWriter == null) {
sendErrorMessage("客户端未连接");
return;
}
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 初始化失败");
}
// 启动录音,更新UI状态,启动定时上传任务
audioRecord.startRecording();
isRecording.set(true);
startRecordButton.setEnabled(false);
stopRecordButton.setEnabled(true);
// 如果已有调度器,先关闭再新建
if (scheduler != null) {
scheduler.shutdownNow();
}
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(this::uploadAudioData, 0, 100, TimeUnit.MILLISECONDS);
handler.sendEmptyMessage(0x12);
sendControlPacket("startRecorder");
playTts("开始录音");
} 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); // 允许再次开始录音
// 总是重置标志位以允许下次显示开始/停止提示
isStopMessageSent = false;
isStartMessageSent = false;
handler.sendEmptyMessage(0x14);
sendControlPacket("stopRecorder");
playTts("停止录音");
}
// 播放TTS语音
private void playTts(String text) {
if (isTtsInitialized) {
ttsEngine.speak(text, TextToSpeech.QUEUE_FLUSH, null);
Log.i(TAG, "播放TTS: " + text);
}
}
// 释放录音资源
private void releaseAudioResources() {
if (audioRecord != null) {
try {
if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
audioRecord.stop();
}
} catch (IllegalStateException e) {
Log.e(TAG, "停止录音失败", e);
}
audioRecord.release();
audioRecord = null;
}
if (scheduler != null) {
scheduler.shutdownNow();
scheduler = null;
}
}
// 上传音频数据到服务器
private void uploadAudioData() {
if (!isRecording.get() || clientSocket == null || clientSocket.isClosed() || socketWriter == null) {
return;
}
byte[] buffer = new byte[BUFFER_SIZE];
try {
int bytesRead = audioRecord.read(buffer, 0, BUFFER_SIZE);
if (bytesRead > 0) {
// 首次读取数据时发送开始指令,并且仅发送一次
if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING && !isTransmitStarted) {
sendControlPacket("startTransmit"); // 发送开始传输指令
playTts("开始传输"); // 触发TTS播报"开始传输"
audioRecord.startRecording(); // 确保录音已开始
isTransmitStarted = true; // 标记为已发送
}
// 在最后一次读取数据后发送停止传输指令
if (bytesRead < BUFFER_SIZE / 2 && isTransmitStarted) {
sendControlPacket("stopTransmit"); // 发送停止传输指令
playTts("停止传输"); // 触发TTS播报"停止传输"
isTransmitStarted = false; // 重置标志位以便下次使用
}
JSONObject json = new JSONObject();
json.put("type", "recording");
json.put("data", Base64.encodeToString(buffer, 0, bytesRead, Base64.NO_WRAP));
synchronized (this) {
if (socketWriter != null) {
socketWriter.write(json.toString());
socketWriter.write("\n\n");
socketWriter.flush();
}
}
}
} catch (Exception e) {
Log.e(TAG, "发送音频数据失败", e);
sendErrorMessage("发送音频数据失败: " + e.getMessage());
}
}
// 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;
}
}
}
// 发送控制指令包
private void sendControlPacket(String type) {
if (clientSocket == null || clientSocket.isClosed() || socketWriter == null) {
return;
}
try {
JSONObject packet = new JSONObject();
packet.put("type", type);
synchronized (this) {
if (socketWriter != null) {
socketWriter.write(packet.toString());
socketWriter.write("\n\n");
socketWriter.flush();
}
}
} catch (Exception e) {
Log.e(TAG, "发送控制指令失败", e);
}
}
// 发送错误信息到主线程
private void sendErrorMessage(String message) {
handler.obtainMessage(0x16, message).sendToTarget();
}
// 启动TCP服务器
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);
executorService.execute(() -> startCommunication(socket));
} 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 startCommunication(Socket socket) {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream(), "UTF-8"))) {
StringBuilder packetBuilder = new StringBuilder();
int c;
while ((c = reader.read()) != -1 && isServerRunning) {
char ch = (char) c;
packetBuilder.append(ch);
if (packetBuilder.length() >= 2 &&
packetBuilder.charAt(packetBuilder.length() - 2) == '\n' &&
packetBuilder.charAt(packetBuilder.length() - 1) == '\n') {
String packet = packetBuilder.toString().trim();
packetBuilder.setLength(0);
if (!packet.isEmpty()) {
try {
JSONObject jsonObject = new JSONObject(packet);
handleReceivedPacket(jsonObject);
} catch (JSONException e) {
Log.w(TAG, "JSON解析失败: " + packet, e);
}
}
}
}
} catch (IOException e) {
if (isServerRunning) {
Log.e(TAG, "通信中断", e);
}
} finally {
closeSocket(socket);
}
}
// 处理接收到的数据包
private void handleReceivedPacket(JSONObject jsonObject) {
try {
String type = jsonObject.getString("type");
Object data = jsonObject.opt("data");
Message msg;
switch (type) {
case "tts_audio":
msg = handler.obtainMessage(0x18, data.toString());
handler.sendMessage(msg);
break;
case "chat_start":
msg = handler.obtainMessage(0x19, jsonObject.getJSONObject("data").getString("query"));
handler.sendMessage(msg);
break;
case "chat_reply":
msg = handler.obtainMessage(0x20, jsonObject.getJSONObject("data").getString("reply"));
handler.sendMessage(msg);
break;
case "play_complete":
handler.sendEmptyMessage(0x21);
break;
default:
msg = handler.obtainMessage(0x15, type + ": " + data);
handler.sendMessage(msg);
break;
}
} catch (JSONException e) {
Log.e(TAG, "处理数据包失败", e);
}
}
// 处理TTS音频数据
private void handleTtsAudio(String base64Data) {
byte[] pcmData = Base64.decode(base64Data, Base64.DEFAULT);
playPcm(pcmData);
}
// 播放PCM音频
private void playPcm(byte[] pcmData) {
stopAudioPlayback();
int sampleRate = 16000;
int channelConfig = AudioFormat.CHANNEL_OUT_MONO;
int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
int bufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
audioTrack = new AudioTrack(
android.media.AudioManager.STREAM_MUSIC,
sampleRate,
channelConfig,
audioFormat,
bufferSize,
AudioTrack.MODE_STREAM);
audioTrack.play();
audioTrack.write(pcmData, 0, pcmData.length);
}
// 停止音频播放
private void stopAudioPlayback() {
if (audioTrack != null) {
if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
audioTrack.stop();
}
audioTrack.release();
audioTrack = null;
}
}
// 关闭指定的Socket连接
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;
}
}
}
// 关闭服务器Socket
private void closeServerSocket() {
try {
if (serverSocket != null && !serverSocket.isClosed()) {
serverSocket.close();
}
} catch (IOException e) {
Log.w(TAG, "关闭ServerSocket失败", e);
}
}
// Activity销毁时清理资源
@Override
protected void onDestroy() {
super.onDestroy();
isServerRunning = false;
if (ttsEngine != null) {
ttsEngine.stop();
ttsEngine.shutdown();
}
closeServerSocket();
closeSocket(clientSocket);
executorService.shutdownNow();
releaseAudioResources();
stopAudioPlayback();
}
// 权限请求结果处理
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "录音权限已授予", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "录音权限被拒绝", Toast.LENGTH_SHORT).show();
}
}
}
public boolean isStopTransmitSent() {
return isStopTransmitSent;
}
public void setStopTransmitSent(boolean stopTransmitSent) {
isStopTransmitSent = stopTransmitSent;
}
}
修改安卓
最新发布