import socket
import subprocess
import websocket
import time
import os
import threading
import json
import pyaudio
import requests
import hashlib
import base64
from audioplayer import AudioPlayer
import numpy as np
from runner import set_global_var, get_global_var
device_status = {}
def listen_devices():
try:
# 检测设备连接状态
result = subprocess.check_output("adb devices", shell=True).decode()
current_devices = set(line.split('\t')[0] for line in result.splitlines()[1:] if line)
# 检测新连接设备
for dev in current_devices - set(device_status.keys()):
print(f"[设备已连接] {dev}")
device_status[dev] = "connected"
# 检测断开设备
for dev in set(device_status.keys()) - current_devices:
print(f"[设备已断开连接] {dev}")
del device_status[dev]
time.sleep(1)
except Exception as e:
print(f"设备监控错误: {e}")
def pcm_to_utf8(pcm_data: bytearray) -> str:
"""将16位PCM音频数据转为UTF-8字符串"""
def validate_pcm(data: bytearray) -> bool:
"""验证PCM数据有效性"""
return len(data) % 2 == 0 # 16位PCM需为偶数长度
if not validate_pcm(pcm_data):
raise ValueError("无效的PCM数据长度,16位PCM需为偶数长度")
try:
# 转为16位有符号整数数组(小端序)
samples = np.frombuffer(pcm_data, dtype='<i2')
# 标准化到0-255范围
normalized = ((samples - samples.min()) * (255 / (samples.max() - samples.min()))).astype(np.uint8)
# 转换为UTF-8字符串
return bytes(normalized).decode('utf-8', errors='replace')
except Exception as e:
raise RuntimeError(f"转换失败: {str(e)}")
# 打印前32字节的十六进制表示
def parse_packets(buffer):
"""解析接收到的数据包"""
# 解析数据包
end_marker = b'\n\n'
while buffer.find(end_marker) != -1:
packet_bytes = buffer[:buffer.find(end_marker) + len(end_marker)]
buffer = buffer[buffer.find(end_marker) + len(end_marker):]
try:
json_bytes = packet_bytes[:-len(end_marker)]
json_str = json_bytes.decode('utf-8')
packet = json.loads(json_str)
# 处理数据包
packet_type = packet.get("type")
if packet_type == "recording":
audio_data = base64.b64decode(packet.get("data", ""))
print('audio_data ', audio_data)
return audio_data
elif packet_type in ["startRecorder", "stopRecord"]:
pass
# command_callback(packet_type)
else:
print(f"未知数据包类型: {packet_type}")
except json.JSONDecodeError as e:
print(f"JSON解析错误: {e}")
except Exception as e:
print(f"数据包处理错误: {e}")
def start_server(port=35000):
adb_path = "adb.exe"
os.system(f"adb forward tcp:{port} tcp:30000")
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(('localhost', port))
#s.bind(('0.0.0.0', port))
#s.listen(5)
print(f"服务器已启动,正在监听端口 {port}...")
while True:
threading.Thread(target=listen_devices).start()
#client_socket, addr = s.accept()
#print(f"接收到来自 {addr} 的连接")
buffer = bytearray()
try:
while True:
data = s.recv(4096)
if data == b'':
pass
else:
print('data', data)
#buffer.extend(data)
if not data:
print("连接断开")
break
buffer.extend(data)
hex_preview = parse_packets(buffer)
handle_audio_chunk(hex_preview)
print('hex_preview',hex_preview)
'''if data==b'':
pass
else:
if len(data) > 0:
hex_preview = ' '.join(f'{b:02x}' for b in data[:32])
print(f"前32字节: {hex_preview}...")
#handle_audio_chunk(hex_preview)
# 调试用:将PCM转为UTF-8
if len(data) < 1024:
try:
utf8_data = pcm_to_utf8(data)
print(f"UTF-8预览: {utf8_data[:30]}...")
#handle_audio_chunk(utf8_data)
except:
pass'''
except Exception as e:
print(f"接收音频数据异常: {e}")
# 全局配置信息
# 播放时是否停止收音
stop_recording_when_playing = True
# 打断播放的语音指令
stop_playing_words = ["别说了", "停止", "停下"]
# 说话人id
voice_speaker_id = 5199
# 语音活动检测-静音时间长度,超过这个时间视为停止说话
vad_silent_time = 1.5
# 语音合成参数
tts_params = {"lan": "zh", "cuid": "test-1234", "ctp": 1, "pdt":993, "spd":5, "pit": 5,"aue": 3}
# 语音识别开始指令参数
asr_params = {
"type": "START",
"data": {
"dev_pid": 1912,
"dev_key": "com.baidu.open",
"format": "pcm",
"sample": 16000,
"cuid": "my_test_dev",
"type": 1,
"asr_type": 1,
"need_mid": False,
"need_session_finish": True
}
}
# 全局状态变量
ws_running = False
ws_object = None
recorder_running = False
sound_play_list = []
current_audio_player = None
chat_running = False
current_query = ''
last_asr_time = 0
def ws_send_start_command(ws):
message = json.dumps(asr_params)
ws.send_text(message)
def ws_send_stop_command(ws):
# 发送数据
msg_data = {
"type": "FINISH",
}
message = json.dumps(msg_data)
ws.send_text(message)
def on_ws_message(ws, message):
global current_query, last_asr_time
data = json.loads(message)
cmd_type = data.get("type")
if cmd_type == 'MID_TEXT':
mid_text = data.get("result")
set_global_var("voicebot.asr.mid_text", mid_text)
last_asr_time = time.time()
# print("voicebot.asr.mid_text:", mid_text)
elif cmd_type == "FIN_TEXT":
query = data.get("result")
# print("asr result:", query)
set_global_var("voicebot.asr.result", query)
last_asr_time = time.time()
if query and len(query) > 0:
current_query += query
set_global_var("voicebot.chat.query", current_query)
if ws_running == False:
ws.close()
def on_ws_close(ws, close_status_code, close_msg):
print("websocket closed:", close_status_code, close_msg)
def on_ws_error(ws, error):
print(f"websocket Error: {error}")
ws.close()
def on_ws_open(ws):
print("websocket connection opened:", ws)
ws_send_start_command(ws)
def check_chat(query:str):
# for word in stop_playing_words:
# if word in query:
# stop_sound_player()
# return False
# if query in stop_playing_words:
# stop_sound_player()
# return False
if is_playing_or_chatting():
return False
return True
def stop_sound_player():
global chat_running
if current_audio_player:
current_audio_player.stop()
if len(sound_play_list) > 0:
sound_play_list.clear()
chat_running = False
def run_chat(query:str):
global chat_running
chat_running = True
set_global_var("voicebot.chat.query", query)
params = {"query": query}
params['username'] = get_global_var("voicebot.username")
params['password'] = get_global_var("voicebot.password")
response = requests.post("http://127.0.0.1:8010/chat", json=params, stream=True)
total_reply = ''
buffer = ''
for line in response.iter_lines():
if line and chat_running:
text = line.decode('utf-8')
data = json.loads(text[5:])
content = data.get("content")
buffer += content
buffer = extract_play_text(buffer)
total_reply += content
set_global_var("voicebot.chat.reply", total_reply)
# print(content, end='', flush=True)
chat_running = False
buffer = buffer.strip()
if len(buffer) > 0:
add_play_text(buffer)
time.sleep(1)
set_global_var("voicebot.chat.query", None)
set_global_var("voicebot.chat.reply", None)
#提取播放文本
def extract_play_text(total_text:str):
separators = ",;。!?:,.!?\n"
last_start_pos = 0
min_sentence_length = 4
for i in range(0, len(total_text)):
if total_text[i] in separators and i - last_start_pos >= min_sentence_length:
text = total_text[last_start_pos: i + 1]
last_start_pos = i + 1
add_play_text(text.strip())
return total_text[last_start_pos:]
#添加播放文本
def add_play_text(text:str):
# print("add play text:", text)
if len(text) > 1:
sound_play_list.append({"text": text, "mp3_file": None})
# 语音合成 下载声音文件
def download_sound_file(text:str, speaker:int=None):
if speaker is None:
speaker = voice_speaker_id
# print("tts create:", text)
mp3_path = "sounds/" + str(speaker)
if not os.path.exists(mp3_path):
os.mkdir(mp3_path)
mp3_file = mp3_path + "/" + hashlib.md5(text.encode('utf-8')).hexdigest() + ".mp3"
if os.path.exists(mp3_file):
return mp3_file
params = tts_params
params['per'] = speaker
params['text'] = text
url = "http://25.83.75.1:8088/Others/tts/text2audio/json"
response = requests.post(url, json=params)
data = response.json()
if data['success'] == False:
binary_array = json.loads(data['message']['message'])
binary_data = bytes(binary_array)
string_data = binary_data.decode('utf-8', errors='replace')
data = json.loads(string_data)
return "sounds/tts-failed.mp3"
else:
b64_string = data['result'].get('data')
mp3_data = base64.b64decode(b64_string)
with open(mp3_file, 'wb') as file:
file.write(mp3_data)
return mp3_file
#开始聊天
def is_playing_or_chatting():
return len(sound_play_list) > 0
#播放下一个声音
def play_next_sound():
global sound_play_list, current_audio_player
item = sound_play_list[0]
mp3_file = item.get("mp3_file")
if mp3_file:
player = AudioPlayer(mp3_file)
current_audio_player = player
try:
player.play(block=True)
except Exception as e:
print("player exception:" + e)
current_audio_player = None
# print("remained sound:", len(sound_play_list))
if len(sound_play_list) > 0:
sound_play_list.pop(0)
#运行websocket
def run_websocket():
global ws_running, ws_object
ws_running = True
uri = "ws://25.83.75.1:8088/Others/asr/realtime_asr?sn=voicebot"
ws = websocket.WebSocketApp(uri, on_message=on_ws_message, on_close=on_ws_close, on_error=on_ws_error)
ws_object = ws
ws.on_open = on_ws_open
ws.run_forever()
ws_running = False
# print("websocket end")
#开始记录
def start_recorder(chuck_size:int=2560):
audio = pyaudio.PyAudio()
try:
stream = audio.open(format=pyaudio.paInt16, channels=1, rate=16000, input=True, frames_per_buffer=chuck_size)
return audio, stream
except:
print("打开麦克风失败")
return None, None
#获得不发音的时间
def get_silent_chunk(duration:float=0.16):
sample_rate = 16000 # 采样率
num_samples = int(sample_rate * duration) # 计算样本数
silent_data = np.zeros(num_samples, dtype=np.int16)
silent_bytes = silent_data.tobytes()
return silent_bytes
#处理音频块
def handle_audio_chunk(chunk_data:bytes):
# 接受外部是否收音的要求
recording = get_global_var("voicebot.recording")
if ws_object and ws_object.sock and ws_object.sock.connected:
if recording == False or (stop_recording_when_playing and is_playing_or_chatting()):
# print("ignor audio chunk:", sound_play_list, chat_running)
ws_object.send_bytes(get_silent_chunk())
else:
ws_object.send_bytes(chunk_data)
#运行录音机
def run_recorder(audio=None, stream=None, chuck_size=2560):
global recorder_running
recorder_running = True
set_global_var("voicebot.recording", True)
while recorder_running:
chunk_data = stream.read(chuck_size)
print('chunk_data)',chunk_data)
handle_audio_chunk(chunk_data)
stream.stop_stream()
stream.close()
audio.terminate()
# print("recorder end")
#运行检查
def run_check():
global ws_running, recorder_running, current_query
set_global_var("voicebot.running", True)
while ws_running and recorder_running:
time.sleep(1)
if get_global_var("voicebot.running") == False:
break
if len(current_query) > 0 and last_asr_time > 0 and time.time() - last_asr_time > vad_silent_time:
t = threading.Thread(target=run_chat, args=(current_query,))
t.start()
current_query = ''
ws_running = recorder_running = False
set_global_var("voicebot.running", False)
# print("语音助手已经停止")
#运行播放机
def run_player():
while ws_running and recorder_running:
time.sleep(0.1)
if len(sound_play_list) > 0:
play_next_sound()
def run_tts():
while ws_running and recorder_running:
time.sleep(0.1)
for item in sound_play_list:
if item.get("mp3_file") is None:
item['mp3_file'] = download_sound_file(item['text'])
def run():
active_threads = threading.enumerate()
# 打印每个活跃线程的信息
for t in active_threads:
if t.name == 'voicebot-runner':
return "语音助手已经在运行中了"
audio, stream = start_recorder()
if audio is None or stream is None:
return {"error": "语音助手开启失败,无法访问麦克风"}
t = threading.Thread(target=run_websocket)
t.daemon = True
t.start()
t=threading.Thread(target=start_server())
t.daemon = True
t.start()
t = threading.Thread(target=run_check, name='voicebot-runner')
t.daemon = True
t.start()
t = threading.Thread(target=run_tts)
t.daemon = True
t.start()
t = threading.Thread(target=run_player)
t.daemon = True
t.start()
return "执行成功"
if __name__ == "__main__":
#run()
start_server() 把这个TTS的功能融入到第一个脚本里面生成新脚本,并且修改安卓的代码package com.example.demoapplication;
import android.Manifest;
import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Build;
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.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
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 {
private static final String TAG = "AudioRecorder";
private Button startRecordButton;
private Button stopRecordButton;
private Button uploadButton;
// 音频录制相关
private AudioRecord audioRecord;
private static final int SAMPLE_RATE = 44100; // 音频采样率
private static final int BUFFER_SIZE;
// 静态代码块用于初始化缓冲区大小
static {
int minBufferSize = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) {
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; // 客户端Socket连接
private volatile BufferedWriter socketWriter; // Socket写入流
// 文本转语音(TTS)相关变量
private TextToSpeech ttsEngine;
private boolean isTtsInitialized = false;
// 主线程消息处理器,用于UI更新
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();
break;
case 0x13: // 数据发送成功
// 减少Toast频率,避免刷屏
if (Math.random() < 0.1) { // 10%概率显示
Toast.makeText(MainActivity.this, "录音数据已发送", Toast.LENGTH_SHORT).show();
}
break;
case 0x14: // 停止录音
Toast.makeText(MainActivity.this, "停止录音", Toast.LENGTH_SHORT).show();
break;
case 0x15: // 控制指令
Toast.makeText(MainActivity.this, "收到控制指令:" + msg.obj.toString(), Toast.LENGTH_SHORT).show();
break;
case 0x16: // 错误消息
Toast.makeText(MainActivity.this, "错误: " + msg.obj.toString(), Toast.LENGTH_LONG).show();
break;
case 0x17: // 网络状态
Toast.makeText(MainActivity.this, "网络: " + msg.obj.toString(), Toast.LENGTH_SHORT).show();
break;
}
}
};
/**
* Activity创建时调用,进行初始化操作。
* @param savedInstanceState 保存的状态数据
*/
@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); // 启动服务器,端口30000
}
/**
* 初始化UI视图组件
*/
private void initViews() {
startRecordButton = findViewById(R.id.startRecordButton);
stopRecordButton = findViewById(R.id.stopRecordButton);
uploadButton = findViewById(R.id.uploadButton);
stopRecordButton.setEnabled(false);
uploadButton.setEnabled(false);
}
/**
* 设置按钮点击事件监听器
*/
private void setupClickListeners() {
startRecordButton.setOnClickListener(v -> startRecording());
stopRecordButton.setOnClickListener(v -> stopRecording());
uploadButton.setOnClickListener(v -> uploadRecording());
}
/**
* 检查录音权限并请求必要权限
*/
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() || audioRecord != null) {
sendErrorMessage("录音已在进行中");
return;
}
// 检查网络连接
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 初始化失败");
}
// 开始录音
audioRecord.startRecording();
isRecording.set(true);
// 更新按钮状态
startRecordButton.setEnabled(false);
stopRecordButton.setEnabled(true);
uploadButton.setEnabled(false);
// 创建定时任务发送音频数据
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(this::uploadAudioData, 0, 100, TimeUnit.MILLISECONDS); // 提高发送频率
handler.sendEmptyMessage(0x12); // 发送开始录音的消息
// 发送开始录音控制指令
sendControlPacket("startRecorder");
// 播放TTS提示音
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);
uploadButton.setEnabled(true);
handler.sendEmptyMessage(0x14); // 发送停止录音的消息
// 发送停止录音控制指令
sendControlPacket("stopRecor");
// 播放TTS提示音
playTts("停止录音");
}
/**
* 使用TTS播放指定文本
* @param text 要播放的文本内容
*/
private void playTts(String text) {
if (isTtsInitialized) {
// 使用系统TTS播放
ttsEngine.speak(text, TextToSpeech.QUEUE_FLUSH, null);
Log.i(TAG, "播放TTS: " + text);
} else {
Log.w(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) {
Log.w(TAG, "无法发送音频数据: 录音未进行或客户端未连接");
return;
}
byte[] buffer = new byte[BUFFER_SIZE];
try {
int bytesRead = audioRecord.read(buffer, 0, BUFFER_SIZE);
if (bytesRead > 0) {
// 创建JSON数据包
JSONObject json = new JSONObject();
json.put("type", "recording");
json.put("data", Base64.encodeToString(buffer, 0, bytesRead, Base64.NO_WRAP)); // 使用NO_WRAP避免换行符
// 发送数据
synchronized (this) {
if (socketWriter != null) {
socketWriter.write(json.toString());
socketWriter.write("\n\n"); // 添加双换行作为结束标识
socketWriter.flush();
}
}
handler.sendEmptyMessage(0x13); // 发送录音数据的消息
}
} catch (Exception e) {
Log.e(TAG, "发送音频数据失败", e);
sendErrorMessage("发送音频数据失败: " + e.getMessage());
}
}
/**
* TTS初始化回调方法
* @param status 初始化状态
*/
@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;
Log.i(TAG, "TTS初始化成功,语言设置为中文");
}
} else {
Log.e(TAG, "TTS初始化失败");
}
}
/**
* 发送控制指令包
* @param type 控制指令类型
*/
private void sendControlPacket(String type) {
if (clientSocket == null || clientSocket.isClosed() || socketWriter == null) {
sendErrorMessage("无法发送控制指令: 客户端未连接");
return;
}
try {
JSONObject packet = new JSONObject();
packet.put("type", type);
packet.put("data", JSONObject.NULL);
synchronized (this) {
if (socketWriter != null) {
socketWriter.write(packet.toString());
socketWriter.write("\n\n"); // 双换行作为结束标识
socketWriter.flush();
}
}
Log.i(TAG, "控制指令发送成功: " + type);
} catch (Exception e) {
Log.e(TAG, "发送控制指令失败", e);
sendErrorMessage("发送控制指令失败: " + e.getMessage());
}
}
/**
* 发送错误消息
* @param message 错误信息
*/
private void sendErrorMessage(String message) {
Message msg = handler.obtainMessage(0x16, message);
handler.sendMessage(msg);
}
/**
* 发送网络状态消息
* @param message 网络状态信息
*/
private void sendNetworkMessage(String message) {
Message msg = handler.obtainMessage(0x17, message);
handler.sendMessage(msg);
}
/**
* 上传录音文件(当前模式下无实际作用)
*/
private void uploadRecording() {
Toast.makeText(this, "该模式下无需上传文件,已实时发送", Toast.LENGTH_SHORT).show();
}
/**
* 启动服务器监听
* @param port 监听端口号
*/
private void startServer(int port) {
executorService.execute(() -> {
try {
serverSocket = new ServerSocket(port);
Log.i(TAG, "服务器启动,监听端口: " + port);
sendNetworkMessage("服务器启动");
while (isServerRunning) {
try {
Socket socket = serverSocket.accept();
clientSocket = socket;
// 创建输出流
synchronized (this) {
socketWriter = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
}
handler.sendEmptyMessage(0x11); // 发送客户端连接成功的消息
Log.i(TAG, "客户端已连接: " + socket.getInetAddress());
sendNetworkMessage("客户端已连接");
// 启动双向通信处理
executorService.execute(() -> startCommunication(socket));
} catch (IOException e) {
if (isServerRunning) {
Log.e(TAG, "接受连接失败", e);
sendErrorMessage("接受连接失败: " + e.getMessage());
}
}
}
} catch (IOException e) {
Log.e(TAG, "服务器启动失败", e);
runOnUiThread(() -> Toast.makeText(MainActivity.this,
"服务器启动失败: " + e.getMessage(), Toast.LENGTH_LONG).show());
} finally {
closeServerSocket();
}
});
}
/**
* 开始与客户端的通信
* @param socket 客户端Socket连接
*/
private void startCommunication(Socket socket) {
try (java.io.BufferedReader reader = new java.io.BufferedReader(
new java.io.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);
runOnUiThread(() -> Toast.makeText(MainActivity.this,
"通信中断: " + e.getMessage(), Toast.LENGTH_SHORT).show());
}
} finally {
closeSocket(socket);
}
}
/**
* 处理接收到的数据包
* @param jsonObject 接收到的JSON数据包
*/
private void handleReceivedPacket(JSONObject jsonObject) {
try {
String type = jsonObject.getString("type");
Object data = jsonObject.opt("data");
// 发送消息到主线程进行显示
Message msg = handler.obtainMessage(0x15, type + ": " + data);
handler.sendMessage(msg);
Log.i(TAG, "收到控制指令: " + type);
// 根据不同类型执行不同操作
switch (type) {
case "start_recording":
runOnUiThread(this::startRecording);
break;
case "stop_recording":
runOnUiThread(this::stopRecording);
break;
case "ping":
sendResponse("pong");
break;
}
} catch (JSONException e) {
Log.e(TAG, "处理数据包失败", e);
}
}
/**
* 发送响应给客户端
* @param responseType 响应类型
*/
private void sendResponse(String responseType) {
if (clientSocket == null || clientSocket.isClosed() || socketWriter == null) return;
try {
JSONObject response = new JSONObject();
response.put("type", responseType);
response.put("data", "");
synchronized (this) {
if (socketWriter != null) {
socketWriter.write(response.toString());
socketWriter.write("\n\n");
socketWriter.flush();
}
}
Log.i(TAG, "发送响应: " + responseType);
} catch (Exception e) {
Log.e(TAG, "发送响应失败", e);
}
}
/**
* 关闭指定的Socket连接
* @param socket 要关闭的Socket
*/
private void closeSocket(Socket socket) {
try {
if (socket != null && !socket.isClosed()) {
socket.close();
}
} catch (IOException e) {
Log.w(TAG, "关闭Socket失败", e);
}
// 如果是当前客户端Socket,重置引用
if (socket == clientSocket) {
clientSocket = null;
synchronized (this) {
socketWriter = null;
}
sendNetworkMessage("客户端断开连接");
}
}
/**
* 关闭服务器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;
// 关闭TTS引擎
if (ttsEngine != null) {
ttsEngine.stop();
ttsEngine.shutdown();
}
// 关闭所有资源
closeServerSocket();
closeSocket(clientSocket);
executorService.shutdownNow();
releaseAudioResources();
Log.i(TAG, "应用已销毁");
sendNetworkMessage("服务已停止");
}
/**
* 权限请求结果回调
* @param requestCode 请求码
* @param permissions 请求的权限数组
* @param grantResults 权限授予结果
*/
@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();
}
}
}
}
最新发布