iB86/app/src/main/java/com/ismart/ib86/app/MainActivity.java
2025-08-18 15:26:44 +08:00

980 lines
36 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.ismart.ib86.app;
import android.Manifest;
import android.content.pm.PackageManager;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
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.view.KeyEvent;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.os.Build;
import android.view.View;
import android.util.Log;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.ismart.ib86.feature.serial.SerialPort.ASR5515Protocol;
import com.ismart.ib86.feature.gpio.GpioManager;
import com.ismart.ib86.feature.serial.SerialPort.ASR5515DeviceManager;
import com.ismart.ib86.common.utils.LogManager;
import com.ismart.ib86.feature.motor.MotorController;
import com.lhht.xiaozhi.models.websokcet.send.WebSocketSendMsgFactory;
import com.lhht.xiaozhi.settings.SettingsManager;
import com.lhht.xiaozhi.utils.DeviceUtils;
import com.lhht.xiaozhi.websocket.WebSocketManager;
import org.json.JSONObject;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import vip.inode.demo.opusaudiodemo.utils.OpusUtils;
public class MainActivity extends AppCompatActivity implements WebSocketManager.WebSocketListener {
private static final String TAG = "MainActivity";
private static final String DEVICE_PATH = "/dev/ttyS3";
private static final int HEAD_TOUCH_KEY = 285;
private static final int BAUD_RATE = 921600;
// 权限请求码
private static final int PERMISSION_REQUEST_CODE = 1001;
// 需要请求的权限
private static final String[] REQUIRED_PERMISSIONS = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.RECORD_AUDIO
};
private static final int BT_POWER_EN = 75;
private ASR5515DeviceManager deviceManager;
private MotorController motorController;
// 使用弱引用避免内存泄漏
private GpioManager gpioManager;
//xiaozhi start
private static final int SAMPLE_RATE = 16000;
private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
private static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
private static final int BUFFER_SIZE = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT);
private static final int OPUS_FRAME_SIZE = 960;
private static final int PLAY_BUFFER_SIZE = 65536;
private static final int OPUS_FRAME_DURATION = 60; // 60ms per frame
private static final long UI_UPDATE_TIMEOUT = 1500; // 1.5秒无声音就更新UI
private static final long MIC_ENABLE_DELAY = 1000; // UI更新1秒后开启麦克风
private static final long CHECK_INTERVAL = 50; // 检测频率提高到50ms
private AudioRecord audioRecord;
private AudioTrack audioTrack;
private ExecutorService executorService;
private ExecutorService audioExecutor;
private Handler mainHandler;
private WebSocketManager webSocketManager;
private OpusUtils opusUtils;
private long encoderHandle;
private long decoderHandle;
private short[] decodedBuffer;
private short[] recordBuffer;
private byte[] encodedBuffer;
private boolean hasStartedCall = false;
private boolean isRecording = false;
private volatile boolean isDestroyed = false;
private boolean isMuted = false;
private boolean isPlaying = false;
private String sessionId = ""; // 添加session_id字段
private long lastAudioDataTime = 0; // 新增:记录最后一次收到音频数据的时间
private boolean isCheckingPlaybackStatus = false;
//xiaozhi end
// HeadTouchManager removed
// UI控件
private ImageView ivFace;
private Button btnRotateForward;
private Button btnRotateReverse;
private SeekBar seekBarDutyCycle;
private TextView tvDutyCycleValue;
private int currentDutyCyclePercent = 30; // 默认占空比30%
private static final int PWM_PERIOD = 1000000; // 1ms周期
// 动画管理器
private RobotFaceAnimationManager animationManager;
private boolean isCalibrating = false;
private boolean isForwardCalibration = false;
private boolean isReverseCalibration = false;
private Runnable wearDetectionRunnable = new Runnable() {
@Override
public void run() {
// 发送佩戴检测请求
deviceManager.sendWearDetectionRequest();
// 2秒后再次执行
handler.postDelayed(this, 2000);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_robot_face);
// 初始化UI控件
ivFace = (ImageView) this.findViewById(R.id.iv_face);
// btnRotateForward = (Button) this.findViewById(R.id.btn_rotate_forward);
// btnRotateReverse = (Button) this.findViewById(R.id.btn_rotate_reverse);
// seekBarDutyCycle = (SeekBar) this.findViewById(R.id.seekbar_duty_cycle);
// tvDutyCycleValue = (TextView) this.findViewById(R.id.tv_duty_cycle_value);
// // 设置占空比调整监听器
// seekBarDutyCycle.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
// @Override
// public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// currentDutyCyclePercent = progress;
// tvDutyCycleValue.setText("占空比: " + progress + "%");
// }
//
// @Override
// public void onStartTrackingTouch(SeekBar seekBar) {}
//
// @Override
// public void onStopTrackingTouch(SeekBar seekBar) {}
// });
//
// // 设置正转按钮点击监听器
// btnRotateForward.setOnClickListener(v -> {
// if (motorController != null) {
// int dutyCycle = (int) (PWM_PERIOD * currentDutyCyclePercent / 100.0);
// motorController.rotateVerticalDown(PWM_PERIOD, 0); // 确保垂直向下PWM关闭
// motorController.rotateVerticalUp(PWM_PERIOD, dutyCycle);
// }
// });
//
// // 设置反转按钮点击监听器
// btnRotateReverse.setOnClickListener(v -> {
// if (motorController != null) {
// int dutyCycle = (int) (PWM_PERIOD * currentDutyCyclePercent / 100.0);
// motorController.rotateVerticalUp(PWM_PERIOD, 0); // 确保垂直向上PWM关闭
// motorController.rotateVerticalDown(PWM_PERIOD, dutyCycle);
// }
// });
// 初始化动画管理器
animationManager = new RobotFaceAnimationManager(this, ivFace);
// 预加载所有动画资源
new Thread(() -> {
// 预加载所有动画
for (int i = 0; i < 5; i++) {
animationManager.loadFaceAnimation(i);
}
// 创建可重复执行的Runnable
Runnable faceAnimationRunnable = new Runnable() {
@Override
public void run() {
playRandomFaceAnimation();
handler.postDelayed(this, 20000); // 20秒后再次执行
}
};
// 启动第一次动画切换
handler.post(faceAnimationRunnable);
}).start();
// 检查并请求必要的权限
checkAndRequestPermissions();
// 隐藏状态栏和导航栏
hideSystemBars();
// 初始化设备管理器
initDeviceManager();
// testMotorControl();
motorController.setVerticalSensorEnabled(true);
new Thread(() -> {
boolean direction = true;
try {
motorController.controlVerticalCalibration();
Thread.sleep(2000);
int max = motorController.getVerticalMaxAngle();
int min = motorController.getVerticalMinAngle();
int mid = max - min;
Log.d(TAG,"ver max:" + max + " ver min:"+min + " mid:"+ mid);
// while(true)
// {
// try {
// angleListener.rotateToAngle(direction, direction? 15 : 15 , direction ? 35 : 12);
// direction = !direction;
// Thread.sleep(200);
// }catch (InterruptedException e) {
// Log.e(TAG, "初始化延时失败", e);
// }
// }
} catch (InterruptedException e) {
Log.e(TAG, "初始化延时失败", e);
}
}).start();
//xiaozhi
initAudio();
// setupListeners();
initWebSocket();
}
private void initDeviceManager() {
//打开5515电源
gpioManager = GpioManager.getInstance();
gpioManager.exportGpio(BT_POWER_EN);
gpioManager.setDirection(BT_POWER_EN, GpioManager.DIRECTION_OUT);
gpioManager.setValue(BT_POWER_EN, GpioManager.VALUE_HIGH);
// 初始化电机控制器
motorController = MotorController.getInstance();
motorController.init();
LogManager.d(TAG, "电机控制器初始化完成");
deviceManager = new ASR5515DeviceManager(this, DEVICE_PATH, BAUD_RATE);
// 设置设备检查回调
deviceManager.setDeviceCheckListener(deviceInfo -> {
if (deviceInfo != null) {
LogManager.d(TAG, "设备检查成功: " + deviceInfo.toString());
} else {
LogManager.e(TAG, "设备检查失败");
}
});
// 设置设备控制回调
deviceManager.setDeviceControlListener(success -> {
if (success) {
LogManager.d(TAG, "设备重启成功");
} else {
LogManager.e(TAG, "设备重启失败");
}
});
// 设置日志控制回调
deviceManager.setLogControlListener(success -> {
if (success) {
LogManager.d(TAG, "日志控制设置成功");
} else {
LogManager.e(TAG, "日志控制设置失败");
}
});
// 设置蓝牙版本回调
deviceManager.setBtVersionListener(version -> {
if (version != null) {
LogManager.d(TAG, "蓝牙版本: " + version);
} else {
LogManager.e(TAG, "获取蓝牙版本失败");
}
});
// 设置蓝牙升级回调
deviceManager.setBtUpgradeListener(response ->
LogManager.d(TAG, "蓝牙升级: " + response.toString())
);
// 设置主机状态回调
deviceManager.setHostStatusListener(response -> LogManager.d(TAG, "主机状态: " + response.status));
deviceManager.setBootBinCheckListener(response -> LogManager.d(TAG, "Boot bin检查: " + response.toString()));
deviceManager.setCollectFreqSetListener(response -> LogManager.d(TAG, "采集频率设置: " + response.toString()));
// 设置心率血压血氧自动测量回调
deviceManager.setHrBpBoAutoMeasureListener(response -> LogManager.d(TAG, "HrBpBo自动测量: " + response.toString()));
// 设置心率血压血氧手动测量回调
deviceManager.setHrBpBoManualMeasureListener(response -> LogManager.d(TAG, "HrBpBo手动测量: " + response.toString()));
// 设置动态测量回调
deviceManager.setDynamicMeasureListener(response ->
LogManager.d(TAG, "动态测量: " + response.toString())
);
// 设置佩戴检测回调
deviceManager.setWearDetectionListener(response ->
LogManager.d(TAG, "佩戴检测: " + response.isWearing)
);
// 设置算法结果回调
deviceManager.setAlgoResultListener(response -> {
LogManager.d(TAG, "算法结果: " + response.toString());
// 在后台线程处理算法结果
new Thread(() -> {
// 根据类型处理不同的算法结果
if (response.type == ASR5515Protocol.AlgoResultResponse.TYPE_HEART_RATE) {
// 简化日志输出
LogManager.d(TAG, "HR: " + response.getHeartRate() +
" Conf: " + response.getConfidence() + "%");
} else if (response.type == ASR5515Protocol.AlgoResultResponse.TYPE_SPO2 &&
response.getSpo2() > 0) {
LogManager.d(TAG, "SpO2: " + response.getSpo2() + "%");
}
}).start();
});
//设置lead状态回调
deviceManager.setLeadStatusListener(response -> {
LogManager.d(TAG, "lead 状态: " + response.status);
if(response.status == 1)
{
deviceManager.switchEcgDataReport((byte) 0x01);
deviceManager.startGH3220Measure(ASR5515Protocol.GH3220MeasureType.ECG);
}
});
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
Log.d(TAG, "keyCode: "+ keyCode);
if (keyCode == HEAD_TOUCH_KEY) {
// 处理返回键按下事件
Log.d(TAG, "触摸了机器人的头");
return true; // 返回true表示事件已被处理
}
return super.onKeyDown(keyCode, event); // 让系统继续处理其他按键事件
}
private void startProtocolTests() {
LogManager.d(TAG, "开始协议测试...");
// handler.postDelayed(new Runnable() {
// @Override
// public void run() {
// deviceManager.checkDevice();
// }
// }, 1000);
//
// handler.postDelayed(new Runnable() {
// @Override
// public void run() {
// deviceManager.queryBtVersion();
// }
// }, 2000);
deviceManager.startGH3220Measure(ASR5515Protocol.GH3220MeasureType.ADT);
deviceManager.startGH3220Measure(ASR5515Protocol.GH3220MeasureType.LEAD);
// deviceManager.switchEcgDataReport((byte) 0x01);
// deviceManager.stopGH3220Measure(ASR5515Protocol.GH3220MeasureType.HR);
// deviceManager.stopGH3220Measure(ASR5515Protocol.GH3220MeasureType.SPO2);
// deviceManager.startGH3220Measure(ASR5515Protocol.GH3220MeasureType.ECG);
handler.postDelayed(new Runnable() {
@Override
public void run() {
// deviceManager.startGH3220Measure(ASR5515Protocol.GH3220MeasureType.LEAD);
}
}, 2000);
// deviceManager.sendWearDetectionRequest();
// handler.postDelayed(wearDetectionRunnable, 1000);
}
@Override
protected void onStop(){
super.onStop();
Log.d(TAG, "onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
// 移至后台线程执行耗时操作避免ANR
new Thread(() -> {
try {
// 关闭GPIO电源
if (gpioManager != null) {
try {
gpioManager.setValue(BT_POWER_EN, GpioManager.VALUE_LOW);
} catch (Exception e) {
LogManager.e(TAG, "关闭GPIO电源失败: " + e.getMessage());
}
gpioManager = null;
}
// 释放电机控制器资源
if (motorController != null) {
try {
motorController.release();
LogManager.d(TAG, "电机控制器资源已释放");
} catch (Exception e) {
LogManager.e(TAG, "释放电机控制器资源失败: " + e.getMessage());
}
}
// 停止所有测量
if (deviceManager != null) {
deviceManager.stopGH3220Measure(ASR5515Protocol.GH3220MeasureType.HR);
deviceManager.stopGH3220Measure(ASR5515Protocol.GH3220MeasureType.SPO2);
deviceManager.stopGH3220Measure(ASR5515Protocol.GH3220MeasureType.LEAD);
deviceManager.syncHostStatus(false);
deviceManager.close();
deviceManager = null;
}
} catch (Exception e) {
LogManager.e(TAG, "资源释放过程中发生错误: " + e.getMessage());
}
}).start();
isDestroyed = true;
hasStartedCall = false;
if (webSocketManager != null) {
try {
webSocketManager.sendMessage(WebSocketSendMsgFactory.getInstance().createEndMsg());
} catch (Exception e) {
Log.e(TAG, "发送结束消息失败", e);
}
webSocketManager.disconnect();
}
endCall();
if (encoderHandle != 0) {
opusUtils.destroyEncoder(encoderHandle);
encoderHandle = 0;
}
if (decoderHandle != 0) {
opusUtils.destroyDecoder(decoderHandle);
decoderHandle = 0;
}
executorService.shutdown();
audioExecutor.shutdown();
// 恢复音频模式
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
audioManager.setMode(AudioManager.MODE_NORMAL);
audioManager.setSpeakerphoneOn(false);
isCheckingPlaybackStatus = false;
// 移除所有Handler消息和回调避免内存泄漏
if (handler != null) {
handler.removeCallbacksAndMessages(null);
handler = null;
}
// 释放动画管理器资源
if (animationManager != null) {
animationManager.release();
animationManager = null;
}
}
private Handler handler = new Handler();
/**
* 随机播放一个表情动画
*/
private void playRandomFaceAnimation() {
// 随机选择一个表情
int selectedAnimation = (int)(Math.random() * 5);
LogManager.d(TAG, "随机选择表情动画: " + selectedAnimation);
// 加载选中的动画
if (animationManager != null) {
animationManager.loadFaceAnimation(selectedAnimation);
animationManager.setSpeed(0.4f);
} else {
LogManager.e(TAG, "动画管理器未初始化");
}
}
private void hideSystemBars() {
Window window = getWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
WindowInsetsController insetsController = window.getInsetsController();
if (insetsController != null) {
// 隐藏状态栏和导航栏 (API 30+)
insetsController.hide(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars());
insetsController.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
}
} else {
// 兼容旧版本 (API < 30)
View decorView = window.getDecorView();
int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(flags);
}
}
/**
* 测试电机控制功能
*/
private void testMotorControl() {
if (motorController == null) {
LogManager.e(TAG, "电机控制器未初始化");
return;
}
byte[] data = new byte[1];
motorController.sendCommand(MotorController.CMD_CYCLE_TEST, new byte[]{0x01});
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
hideSystemBars();
}
}
/**
* 检查并请求必要的权限
*/
private void checkAndRequestPermissions() {
// 检查是否已经有所需的权限
boolean allPermissionsGranted = true;
for (String permission : REQUIRED_PERMISSIONS) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
allPermissionsGranted = false;
break;
}
}
// 如果没有所有权限,则请求它们
if (!allPermissionsGranted) {
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, PERMISSION_REQUEST_CODE);
}
}
/**
* 处理权限请求结果
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
boolean allGranted = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allGranted = false;
break;
}
}
if (allGranted) {
// 所有权限都已授予,可以继续操作
LogManager.d(TAG, "所有必要权限已授予");
} else {
// 有些权限被拒绝,显示提示并可能影响功能
Toast.makeText(this, "需要所有请求的权限才能正常运行应用", Toast.LENGTH_LONG).show();
LogManager.e(TAG, "部分权限被拒绝,可能影响应用功能");
}
}
}
private void initAudio() {
executorService = Executors.newSingleThreadExecutor();
audioExecutor = Executors.newSingleThreadExecutor();
mainHandler = new Handler(Looper.getMainLooper());
// 初始化Opus编解码器
opusUtils = OpusUtils.getInstance();
// 使用OPUS_APPLICATION_VOIP模式设置比特率为32000
encoderHandle = opusUtils.createEncoder(SAMPLE_RATE, 1, 32000);
decoderHandle = opusUtils.createDecoder(SAMPLE_RATE, 1);
decodedBuffer = new short[OPUS_FRAME_SIZE];
recordBuffer = new short[OPUS_FRAME_SIZE];
encodedBuffer = new byte[OPUS_FRAME_SIZE * 2];
// 设置音频模式
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
audioManager.setSpeakerphoneOn(true);
// 初始化音频播放器
initAudioTrack();
}
private void initAudioTrack() {
try {
if (audioTrack != null) {
audioTrack.release();
}
audioTrack = new AudioTrack.Builder()
.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build())
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(AUDIO_FORMAT)
.setSampleRate(SAMPLE_RATE)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.build())
.setBufferSizeInBytes(PLAY_BUFFER_SIZE)
.setTransferMode(AudioTrack.MODE_STREAM)
.setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY)
.build();
audioTrack.play();
} catch (Exception e) {
Log.e(TAG, "创建AudioTrack失败", e);
}
}
private void initWebSocket() {
// 从MainActivity获取WebSocket配置
String deviceId = DeviceUtils.getMacFromAndroidId(this);
SettingsManager settingsManager = new SettingsManager(this);
String wsUrl = settingsManager.getWsUrl();
String token = settingsManager.getToken();
boolean enableToken = settingsManager.isTokenEnabled();
webSocketManager = new WebSocketManager("f0:4a:7b:5b:72:46");
webSocketManager.setListener(this);
// 连接WebSocket
try {
webSocketManager.connect(wsUrl, token, enableToken);
} catch (Exception e) {
Log.e(TAG, "WebSocket连接失败", e);
Toast.makeText(this, "连接失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
finish();
}
}
private void sendAudioData(short[] data, int size) {
if (webSocketManager != null && webSocketManager.isConnected() && !isMuted) {
try {
// 编码音频数据
int encodedSize = opusUtils.encode(encoderHandle, data, 0, encodedBuffer);
if (encodedSize > 0) {
byte[] encodedData = new byte[encodedSize];
System.arraycopy(encodedBuffer, 0, encodedData, 0, encodedSize);
// 检查是否全是静音数据
boolean isAllZero = true;
for (int i = 0; i < size && isAllZero; i++) {
if (data[i] != 0) {
isAllZero = false;
}
}
if (!isAllZero) {
webSocketManager.sendBinaryMessage(encodedData);
Log.d(TAG, "发送音频数据: " + encodedSize + " bytes");
// 更新波形图
// byte[] buffer = new byte[size * 2];
// for (int i = 0; i < size; i++) {
// buffer[i * 2] = (byte) (data[i] & 0xFF);
// buffer[i * 2 + 1] = (byte) ((data[i] >> 8) & 0xFF);
// }
// updateUserWaveform(buffer);
}
}
} catch (Exception e) {
Log.e(TAG, "发送音频数据失败", e);
}
}
}
private void interruptAiResponse() {
if (webSocketManager != null && webSocketManager.isConnected()) {
try {
webSocketManager.sendMessage(WebSocketSendMsgFactory.getInstance().createAbortMsg());
Log.d(TAG,"已打断AI回答");
// 停止当前音频播放
stopCurrentAudio();
} catch (Exception e) {
Log.e(TAG, "发送中断消息失败", e);
}
}
}
private void stopCurrentAudio() {
audioExecutor.execute(() -> {
try {
if (audioTrack != null && isPlaying) {
audioTrack.pause();
audioTrack.flush();
isPlaying = false;
// 重新初始化AudioTrack
initAudioTrack();
}
} catch (Exception e) {
Log.e(TAG, "停止音频播放失败", e);
}
});
}
private void startRecording() {
if (audioRecord == null) {
try {
audioRecord = new AudioRecord.Builder()
.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION)
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(AUDIO_FORMAT)
.setSampleRate(SAMPLE_RATE)
.setChannelMask(CHANNEL_CONFIG)
.build())
.setBufferSizeInBytes(BUFFER_SIZE)
.build();
if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "AudioRecord初始化失败");
return;
}
Log.d(TAG, "AudioRecord 初始化成功,缓冲区大小: " + BUFFER_SIZE);
} catch (Exception e) {
Log.e(TAG, "创建AudioRecord失败", e);
return;
}
}
executorService.execute(() -> {
try {
audioRecord.startRecording();
Log.d(TAG, "开始录音");
while (!isDestroyed) {
if (!isRecording || isMuted) {
// 如果不在录音状态或静音,暂停一下
Thread.sleep(100);
continue;
}
int read = audioRecord.read(recordBuffer, 0, OPUS_FRAME_SIZE);
if (read > 0) {
sendAudioData(recordBuffer, read);
} else if (read < 0) {
Log.e(TAG, "读取音频数据失败: " + read);
break;
}
// 控制采样率
Thread.sleep(OPUS_FRAME_DURATION);
}
} catch (Exception e) {
Log.e(TAG, "录音失败", e);
}
});
}
private void startCall() {
if (!webSocketManager.isConnected()) {
Log.d(TAG,"未连接");
return;
}
try {
// 发送开始通话消息
webSocketManager.sendMessage(WebSocketSendMsgFactory.getInstance().createStartMsg(SAMPLE_RATE));
// 开始录音
isRecording = true;
startRecording();
Log.d(TAG,"正在通话中...");
} catch (Exception e) {
Log.e(TAG, "开始通话失败", e);
}
}
private void endCall() {
isRecording = false;
if (audioRecord != null) {
audioRecord.stop();
audioRecord.release();
audioRecord = null;
}
if (audioTrack != null) {
audioTrack.stop();
audioTrack.release();
audioTrack = null;
}
// finish();
}
private void sendListenMessage() {
try {
webSocketManager.sendMessage(WebSocketSendMsgFactory.getInstance().createListenMsg(sessionId));
Log.d(TAG, "发送listen消息");
// 开始录音
isRecording = true;
startRecording();
Log.d(TAG,"正在通话中...");
} catch (Exception e) {
Log.e(TAG, "发送listen消息失败", e);
}
}
private void checkPlaybackStatus() {
if (audioTrack != null && isPlaying) {
long currentTime = System.currentTimeMillis();
long timeSinceLastAudio = currentTime - lastAudioDataTime;
// 如果超过1.5秒没有收到新的音频数据更新UI
if (timeSinceLastAudio > UI_UPDATE_TIMEOUT) {
isPlaying = false;
Log.d(TAG,"正在通话中...");
Log.d(TAG, "AI说话结束更新UI状态");
// 延迟1秒后开启麦克风
mainHandler.postDelayed(() -> {
if (!isPlaying) { // 再次检查是否还是非播放状态
isRecording = true;
Log.d("VoiceCall", "延迟开启麦克风");
}
}, MIC_ENABLE_DELAY);
isCheckingPlaybackStatus = false;
return;
}
// 继续检查,提高检查频率
mainHandler.postDelayed(() -> checkPlaybackStatus(), CHECK_INTERVAL);
} else {
isCheckingPlaybackStatus = false;
}
}
private void startPlaybackStatusCheck() {
isCheckingPlaybackStatus = true;
checkPlaybackStatus();
}
@Override
public void onConnected() {
Log.d(TAG, "连接成功");
runOnUiThread(() -> {
Toast.makeText(this, "连接成功", Toast.LENGTH_SHORT).show();
});
}
@Override
public void onDisconnected() {
Log.d(TAG, "连接断开");
runOnUiThread(() -> {
Toast.makeText(this, "连接已断开", Toast.LENGTH_SHORT).show();
endCall();
});
}
@Override
public void onError(String error) {
Log.e(TAG, "错误: " + error);
runOnUiThread(() -> {
Toast.makeText(this, "错误: " + error, Toast.LENGTH_SHORT).show();
});
}
@Override
public void onMessage(String message) {
try {
JSONObject jsonMessage = new JSONObject(message);
String type = jsonMessage.getString("type");
Log.d(TAG, "收到消息: " + message);
switch (type) {
case "stt":
// 处理语音识别结果
String recognizedText = jsonMessage.getString("text");
break;
case "tts":
break;
case "hello":
// 处理服务器的hello响应
if (!hasStartedCall) {
// 等待服务器返回session_id
if (jsonMessage.has("session_id")) {
sessionId = jsonMessage.getString("session_id");
hasStartedCall = true;
startCall();
}
}
break;
case "start":
// 收到start响应后发送listen消息
if (jsonMessage.has("session_id")) {
sessionId = jsonMessage.getString("session_id");
sendListenMessage();
}
break;
}
} catch (Exception e) {
Log.e(TAG, "处理消息失败", e);
}
}
@Override
public void onBinaryMessage(byte[] data) {
if (data == null || data.length == 0) return;
audioExecutor.execute(() -> {
try {
if (audioTrack == null || audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
initAudioTrack();
}
// 解码并播放音频数据
int decodedSamples = opusUtils.decode(decoderHandle, data, decodedBuffer);
if (decodedSamples > 0) {
byte[] pcmData = new byte[decodedSamples * 2];
for (int i = 0; i < decodedSamples; i++) {
short sample = decodedBuffer[i];
pcmData[i * 2] = (byte) (sample & 0xff);
pcmData[i * 2 + 1] = (byte) ((sample >> 8) & 0xff);
}
// 更新最后一次收到音频数据的时间
lastAudioDataTime = System.currentTimeMillis();
// 如果之前不是AI说话状态则立即切换状态并关闭麦克风
if (!isPlaying) {
isPlaying = true;
isRecording = false; // 立即关闭麦克风
Log.e(TAG,"AI正在说话...");
}
int written = audioTrack.write(pcmData, 0, pcmData.length, AudioTrack.WRITE_BLOCKING);
if (written < 0) {
Log.e(TAG, "音频播放失败: " + written);
}
// // 更新AI波形图
// float[] amplitudes = new float[decodedSamples];
// for (int i = 0; i < decodedSamples; i++) {
// amplitudes[i] = decodedBuffer[i] / 32768f;
// }
// updateAiWaveform(amplitudes);
// 确保状态检查在运行
if (!isCheckingPlaybackStatus) {
startPlaybackStatusCheck();
}
}
} catch (Exception e) {
Log.e(TAG, "处理音频数据失败", e);
}
});
}
}