Browse Source

add voice manager & test

devab
MTing 4 weeks ago
parent
commit
0bb087a5ec
  1. 6
      app/src/main/AndroidManifest.xml
  2. 223
      app/src/main/assets/voices/IntegrationGuide.md
  3. 72
      app/src/main/assets/voices/README.md
  4. BIN
      app/src/main/assets/voices/close_ab_gate.wav
  5. BIN
      app/src/main/assets/voices/course_sign_success.wav
  6. BIN
      app/src/main/assets/voices/face_recognition.wav
  7. BIN
      app/src/main/assets/voices/inquire_order.wav
  8. BIN
      app/src/main/assets/voices/one_person_entry.wav
  9. BIN
      app/src/main/assets/voices/order_expired.wav
  10. BIN
      app/src/main/assets/voices/select_order_confirm.wav
  11. 0
      app/src/main/assets/voices/test_shell_commands.sh
  12. BIN
      app/src/main/assets/voices/verification_success_entry.wav
  13. BIN
      app/src/main/assets/voices/verification_success_leave.wav
  14. 26
      app/src/main/java/com/ouxuan/oxface/DebugActivity.java
  15. 145
      app/src/main/java/com/ouxuan/oxface/device/voice/AudioPlayer.java
  16. 101
      app/src/main/java/com/ouxuan/oxface/device/voice/VoiceConfigManager.java
  17. 118
      app/src/main/java/com/ouxuan/oxface/device/voice/VoicePlayerManager.java
  18. 133
      app/src/main/java/com/ouxuan/oxface/device/voice/VoicePlayerUsageExample.java
  19. 134
      app/src/main/java/com/ouxuan/oxface/device/voice/VoiceResourceManager.java
  20. 142
      app/src/main/java/com/ouxuan/oxface/device/voice/VoiceTestActivity.java
  21. 46
      app/src/main/java/com/ouxuan/oxface/device/voice/VoiceType.java
  22. 31
      app/src/main/res/layout/activity_debug.xml
  23. 198
      app/src/main/res/layout/activity_voice_test.xml

6
app/src/main/AndroidManifest.xml

@ -139,6 +139,12 @@
android:name=".abgate.ABGateTestActivity"
android:exported="true"
android:theme="@style/Theme.OxFaceLogin" />
<!-- 语音模块测试Activity -->
<activity
android:name=".device.voice.VoiceTestActivity"
android:exported="false"
android:theme="@style/Theme.OxFaceLogin" />
</application>
</manifest>

223
app/src/main/assets/voices/IntegrationGuide.md

@ -0,0 +1,223 @@
# 语音模块集成指南
## 1. 在OXFaceOnlineActivity中集成语音模块
### 1.1 初始化语音播放器
在OXFaceOnlineActivity的onCreate方法中初始化语音播放器:
```java
public class OXFaceOnlineActivity extends BaseActivity {
private VoicePlayerManager voicePlayerManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_oxface_online);
// 初始化语音播放管理器
initVoicePlayer();
// ... 其他初始化代码
}
/**
* 初始化语音播放器
*/
private void initVoicePlayer() {
try {
voicePlayerManager = VoicePlayerManager.getInstance();
voicePlayerManager.initialize(this);
// 设置播放监听器(可选)
voicePlayerManager.getAudioPlayer().setOnVoicePlayListener(new AudioPlayer.OnVoicePlayListener() {
@Override
public void onPlayStart(String filePath) {
LogManager.logInfo(TAG, "语音开始播放: " + filePath);
}
@Override
public void onPlayComplete(String filePath) {
LogManager.logInfo(TAG, "语音播放完成: " + filePath);
}
@Override
public void onPlayError(String filePath, String error) {
LogManager.logError(TAG, "语音播放错误: " + filePath + ", 错误信息: " + error);
}
});
LogManager.logInfo(TAG, "语音播放器初始化成功");
} catch (Exception e) {
LogManager.logError(TAG, "语音播放器初始化失败", e);
}
}
}
```
### 1.2 在关键流程中添加语音提示
在人脸识别和核销流程的关键节点添加语音提示:
```java
// 在人脸识别开始时
private void startFaceRecognition() {
if (voicePlayerManager != null) {
voicePlayerManager.playVoice(VoiceType.FACE_RECOGNITION);
}
// ... 其他人脸识别逻辑
}
// 在订单核销成功时(进场)
private void onVerificationSuccessEntry() {
if (voicePlayerManager != null) {
voicePlayerManager.playVoice(VoiceType.VERIFICATION_SUCCESS_ENTRY);
}
// ... 其他核销成功逻辑
}
// 在订单核销成功时(离场)
private void onVerificationSuccessLeave() {
if (voicePlayerManager != null) {
voicePlayerManager.playVoice(VoiceType.VERIFICATION_SUCCESS_LEAVE);
}
// ... 其他核销成功逻辑
}
// 在检测到多人时
private void onMultiplePeopleDetected() {
if (voicePlayerManager != null) {
voicePlayerManager.playVoice(VoiceType.ONE_PERSON_ENTRY);
}
// ... 其他多人检测逻辑
}
```
### 1.3 在Activity销毁时释放资源
```java
@Override
protected void onDestroy() {
super.onDestroy();
// 释放语音播放器资源
if (voicePlayerManager != null) {
voicePlayerManager.release();
voicePlayerManager = null;
}
// ... 其他资源释放代码
}
```
## 2. 在门禁控制模块中集成语音模块
### 2.1 在GateABController中添加语音提示
```java
public class GateABController {
private VoicePlayerManager voicePlayerManager;
public void initialize(Context context) {
// ... 其他初始化代码
// 获取语音播放器实例
voicePlayerManager = VoicePlayerManager.getInstance();
LogManager.logInfo(TAG, "GateABController初始化完成");
}
/**
* 开门操作
*/
public void openGateAB(GateControlCallback callback) {
LogManager.logInfo(TAG, "开始执行AB门开门操作");
// 播放开门提示语音
if (voicePlayerManager != null) {
// 根据场景判断播放进场还是离场语音
boolean isLeaveScene = VenueSceneUtils.isLeaveScene(context);
if (isLeaveScene) {
voicePlayerManager.playVoice(VoiceType.VERIFICATION_SUCCESS_LEAVE);
} else {
voicePlayerManager.playVoice(VoiceType.VERIFICATION_SUCCESS_ENTRY);
}
}
// ... 其他开门逻辑
}
/**
* AB门状态异常时播放提示
*/
public void onGateStateAbnormal() {
if (voicePlayerManager != null) {
voicePlayerManager.playVoice(VoiceType.CLOSE_AB_GATE);
}
}
}
```
## 3. 配置管理
### 3.1 启用/禁用语音播放
```java
// 启用语音播放
VoiceConfigManager configManager = voicePlayerManager.getConfigManager();
configManager.setVoiceEnabled(true);
// 禁用语音播放
configManager.setVoiceEnabled(false);
```
### 3.2 调整语音音量
```java
// 设置音量(0.0-1.0)
VoiceConfigManager configManager = voicePlayerManager.getConfigManager();
configManager.setVoiceVolume(0.8f);
```
## 4. 最佳实践
### 4.1 避免语音重叠播放
```java
// 在播放新语音前停止当前播放
if (voicePlayerManager != null) {
voicePlayerManager.stopCurrentVoice();
voicePlayerManager.playVoice(VoiceType.FACE_RECOGNITION);
}
```
### 4.2 根据场景选择合适的语音
```java
// 根据进场/离场场景选择语音
boolean isLeaveScene = VenueSceneUtils.isLeaveScene(context);
if (isLeaveScene) {
voicePlayerManager.playVoice(VoiceType.VERIFICATION_SUCCESS_LEAVE);
} else {
voicePlayerManager.playVoice(VoiceType.VERIFICATION_SUCCESS_ENTRY);
}
```
### 4.3 错误处理
```java
// 检查语音播放器是否初始化成功
if (voicePlayerManager != null && voicePlayerManager.getConfigManager().isVoiceEnabled()) {
voicePlayerManager.playVoice(VoiceType.FACE_RECOGNITION);
} else {
LogManager.logWarning(TAG, "语音播放器未初始化或已禁用");
}
```
## 5. 测试建议
1. 确保所有语音文件都已放置在assets/voices目录中
2. 测试不同场景下的语音播放效果
3. 测试语音开关功能
4. 测试语音音量调节功能
5. 测试异常情况下的错误处理

72
app/src/main/assets/voices/README.md

@ -0,0 +1,72 @@
# 语音文件说明
## 语音文件列表
请将以下wav格式的语音文件放置在本目录中:
1. `select_order_confirm.wav` - 请选择订单确认核销
2. `inquire_order.wav` - 正在查询您的订单 --去除
3. `face_recognition.wav` - 正在进行人脸识别
4. `close_ab_gate.wav` - 请关闭ab门
5. `verification_success_entry.wav` - 核销成功请推门 (进场-识别成功)
6. `verification_success_leave.wav` - 离场请推门 (离场-识别成功)
7. `one_person_entry.wav` - 请1人进场进行核销
8. `order_expired.wav` - 订单超期需要扫码补缴
9. `course_sign_success.wav` - 课程签到成功
## 文件格式要求
- 格式:WAV
- 编码:PCM
- 采样率:推荐16kHz或22.05kHz
- 位深度:16位
- 声道:单声道
## 使用说明
语音模块会自动从assets目录复制文件到应用内部存储,然后进行播放。
请确保所有语音文件都已放置在本目录中,否则对应的语音提示将无法播放。
## 在项目中使用语音模块
1. 初始化语音播放管理器:
```java
VoicePlayerManager voicePlayerManager = VoicePlayerManager.getInstance();
voicePlayerManager.initialize(context);
```
2. 播放指定语音:
```java
// 播放人脸识别语音
voicePlayerManager.playVoice(VoiceType.FACE_RECOGNITION);
// 播放核销成功语音(进场)
voicePlayerManager.playVoice(VoiceType.VERIFICATION_SUCCESS_ENTRY);
```
3. 配置语音参数:
```java
VoiceConfigManager configManager = voicePlayerManager.getConfigManager();
configManager.setVoiceEnabled(true); // 启用语音
configManager.setVoiceVolume(0.8f); // 设置音量
```
## 集成到人脸识别流程
语音模块已设计为可轻松集成到OXFaceOnlineActivity中:
1. 在onCreate中初始化语音播放器
2. 在关键流程节点调用相应的语音播放方法
3. 在onDestroy中释放资源
## 测试语音模块
项目中包含VoiceTestActivity测试界面,可以用来测试所有语音文件的播放效果。
## 注意事项
1. 语音文件必须为WAV格式,否则可能无法正常播放
2. 建议在应用启动时预加载所有语音文件以提升响应速度
3. 可通过VoiceConfigManager配置语音开关和音量
4. 语音播放为异步操作,不会阻塞主线程
5. 详细的集成指南请参考IntegrationGuide.md文件

BIN
app/src/main/assets/voices/close_ab_gate.wav

BIN
app/src/main/assets/voices/course_sign_success.wav

BIN
app/src/main/assets/voices/face_recognition.wav

BIN
app/src/main/assets/voices/inquire_order.wav

BIN
app/src/main/assets/voices/one_person_entry.wav

BIN
app/src/main/assets/voices/order_expired.wav

BIN
app/src/main/assets/voices/select_order_confirm.wav

0
app/src/main/assets/test_shell_commands.sh → app/src/main/assets/voices/test_shell_commands.sh

BIN
app/src/main/assets/voices/verification_success_entry.wav

BIN
app/src/main/assets/voices/verification_success_leave.wav

26
app/src/main/java/com/ouxuan/oxface/DebugActivity.java

@ -264,6 +264,32 @@ public class DebugActivity extends Activity {
testGateAB485PeopleNum();
}
});
// 语音测试按钮
Button btnTestVoice = findViewById(R.id.btnTestVoice);
btnTestVoice.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
testVoiceFunction();
}
});
}
/**
* 测试语音功能
*/
private void testVoiceFunction() {
logMessage("启动语音测试...");
try {
Intent intent = new Intent(this, com.ouxuan.oxface.device.voice.VoiceTestActivity.class);
startActivity(intent);
logMessage("已启动语音测试界面");
showToast("已启动语音测试界面");
} catch (Exception e) {
Log.e(TAG, "启动语音测试失败", e);
logMessage("启动语音测试失败: " + e.getMessage());
showToast("启动语音测试失败");
}
}
/**

145
app/src/main/java/com/ouxuan/oxface/device/voice/AudioPlayer.java

@ -0,0 +1,145 @@
package com.ouxuan.oxface.device.voice;
import android.media.MediaPlayer;
import android.media.AudioManager;
import com.ouxuan.oxface.utils.LogManager;
/**
* 音频播放器
* 负责实际的音频文件播放
*/
public class AudioPlayer {
private static final String TAG = "AudioPlayer";
private MediaPlayer mediaPlayer;
private OnVoicePlayListener playListener;
private String currentPlayingFile;
public interface OnVoicePlayListener {
void onPlayStart(String filePath);
void onPlayComplete(String filePath);
void onPlayError(String filePath, String error);
}
/**
* 设置播放监听器
*/
public void setOnVoicePlayListener(OnVoicePlayListener listener) {
this.playListener = listener;
}
/**
* 播放音频文件
*/
public void play(String filePath) {
try {
// 如果正在播放其他文件先停止
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
stop();
}
currentPlayingFile = filePath;
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(filePath);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
LogManager.logInfo(TAG, "开始播放音频: " + filePath);
if (playListener != null) {
playListener.onPlayStart(filePath);
}
}
});
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
LogManager.logInfo(TAG, "音频播放完成: " + filePath);
if (playListener != null) {
playListener.onPlayComplete(filePath);
}
releaseMediaPlayer();
}
});
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
String errorMsg = "播放错误: what=" + what + ", extra=" + extra;
LogManager.logError(TAG, "音频播放失败: " + filePath + ", " + errorMsg);
if (playListener != null) {
playListener.onPlayError(filePath, errorMsg);
}
releaseMediaPlayer();
return true;
}
});
} catch (Exception e) {
String errorMsg = "播放异常: " + e.getMessage();
LogManager.logError(TAG, "音频播放异常: " + filePath + ", " + errorMsg, e);
if (playListener != null) {
playListener.onPlayError(filePath, errorMsg);
}
releaseMediaPlayer();
}
}
/**
* 停止当前播放
*/
public void stop() {
if (mediaPlayer != null) {
try {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
LogManager.logInfo(TAG, "音频播放已停止: " + currentPlayingFile);
}
} catch (Exception e) {
LogManager.logError(TAG, "停止音频播放时发生异常", e);
}
}
releaseMediaPlayer();
}
/**
* 释放MediaPlayer资源
*/
private void releaseMediaPlayer() {
if (mediaPlayer != null) {
try {
mediaPlayer.release();
} catch (Exception e) {
LogManager.logError(TAG, "释放MediaPlayer资源时发生异常", e);
}
mediaPlayer = null;
}
currentPlayingFile = null;
}
/**
* 释放所有资源
*/
public void release() {
releaseMediaPlayer();
}
/**
* 检查是否正在播放
*/
public boolean isPlaying() {
return mediaPlayer != null && mediaPlayer.isPlaying();
}
/**
* 获取当前播放的文件路径
*/
public String getCurrentPlayingFile() {
return currentPlayingFile;
}
}

101
app/src/main/java/com/ouxuan/oxface/device/voice/VoiceConfigManager.java

@ -0,0 +1,101 @@
package com.ouxuan.oxface.device.voice;
import android.content.Context;
import android.content.SharedPreferences;
import com.ouxuan.oxface.utils.LogManager;
/**
* 语音配置管理器
* 负责管理语音播放的相关配置
*/
public class VoiceConfigManager {
private static final String TAG = "VoiceConfigManager";
private static final String PREF_NAME = "voice_config";
private static final String KEY_VOICE_ENABLED = "voice_enabled";
private static final String KEY_VOICE_VOLUME = "voice_volume";
private static final String KEY_VOICE_SPEED = "voice_speed";
private SharedPreferences sharedPreferences;
public VoiceConfigManager(Context context) {
sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
LogManager.logInfo(TAG, "VoiceConfigManager初始化完成");
}
/**
* 检查语音播放是否启用
*/
public boolean isVoiceEnabled() {
boolean enabled = sharedPreferences.getBoolean(KEY_VOICE_ENABLED, true);
LogManager.logDebug(TAG, "语音播放状态: " + (enabled ? "启用" : "禁用"));
return enabled;
}
/**
* 设置语音播放启用状态
*/
public void setVoiceEnabled(boolean enabled) {
sharedPreferences.edit().putBoolean(KEY_VOICE_ENABLED, enabled).apply();
LogManager.logInfo(TAG, "语音播放状态已设置为: " + (enabled ? "启用" : "禁用"));
}
/**
* 获取语音音量
*/
public float getVoiceVolume() {
float volume = sharedPreferences.getFloat(KEY_VOICE_VOLUME, 1.0f);
LogManager.logDebug(TAG, "语音音量: " + volume);
return volume;
}
/**
* 设置语音音量
*/
public void setVoiceVolume(float volume) {
// 限制音量范围在0.0-1.0之间
volume = Math.max(0.0f, Math.min(1.0f, volume));
sharedPreferences.edit().putFloat(KEY_VOICE_VOLUME, volume).apply();
LogManager.logInfo(TAG, "语音音量已设置为: " + volume);
}
/**
* 获取语音播放速度
*/
public float getVoiceSpeed() {
float speed = sharedPreferences.getFloat(KEY_VOICE_SPEED, 1.0f);
LogManager.logDebug(TAG, "语音播放速度: " + speed);
return speed;
}
/**
* 设置语音播放速度
*/
public void setVoiceSpeed(float speed) {
// 限制速度范围在0.5-2.0之间
speed = Math.max(0.5f, Math.min(2.0f, speed));
sharedPreferences.edit().putFloat(KEY_VOICE_SPEED, speed).apply();
LogManager.logInfo(TAG, "语音播放速度已设置为: " + speed);
}
/**
* 重置为默认配置
*/
public void resetToDefault() {
sharedPreferences.edit()
.putBoolean(KEY_VOICE_ENABLED, true)
.putFloat(KEY_VOICE_VOLUME, 1.0f)
.putFloat(KEY_VOICE_SPEED, 1.0f)
.apply();
LogManager.logInfo(TAG, "语音配置已重置为默认值");
}
/**
* 获取所有配置信息
*/
public String getAllConfigInfo() {
return "语音配置信息: " +
"启用=" + isVoiceEnabled() +
", 音量=" + getVoiceVolume() +
", 速度=" + getVoiceSpeed();
}
}

118
app/src/main/java/com/ouxuan/oxface/device/voice/VoicePlayerManager.java

@ -0,0 +1,118 @@
package com.ouxuan.oxface.device.voice;
import android.content.Context;
import android.media.MediaPlayer;
import android.os.Handler;
import android.os.Looper;
import com.ouxuan.oxface.utils.LogManager;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 语音播放管理器
* 负责管理预录制语音文件的播放
*/
public class VoicePlayerManager {
private static final String TAG = "VoicePlayerManager";
private static volatile VoicePlayerManager instance;
private Context context;
private AudioPlayer audioPlayer;
private VoiceResourceManager resourceManager;
private VoiceConfigManager configManager;
private ExecutorService executorService;
private Handler mainHandler;
private VoicePlayerManager() {
executorService = Executors.newSingleThreadExecutor();
mainHandler = new Handler(Looper.getMainLooper());
}
/**
* 获取单例实例
*/
public static VoicePlayerManager getInstance() {
if (instance == null) {
synchronized (VoicePlayerManager.class) {
if (instance == null) {
instance = new VoicePlayerManager();
}
}
}
return instance;
}
/**
* 初始化语音播放管理器
*/
public void initialize(Context context) {
this.context = context.getApplicationContext();
this.audioPlayer = new AudioPlayer();
this.resourceManager = new VoiceResourceManager(context);
this.configManager = new VoiceConfigManager(context);
LogManager.logInfo(TAG, "VoicePlayerManager初始化完成");
}
/**
* 播放指定语音
*/
public void playVoice(VoiceType voiceType) {
if (!configManager.isVoiceEnabled()) {
LogManager.logDebug(TAG, "语音播放已禁用,跳过播放: " + voiceType.getFileName());
return;
}
String filePath = resourceManager.getVoiceFilePath(voiceType);
if (filePath != null) {
LogManager.logInfo(TAG, "播放语音: " + voiceType.getFileName());
audioPlayer.play(filePath);
} else {
LogManager.logWarning(TAG, "未找到语音文件: " + voiceType.getFileName());
}
}
/**
* 停止当前播放
*/
public void stopCurrentVoice() {
if (audioPlayer != null) {
audioPlayer.stop();
}
}
/**
* 释放资源
*/
public void release() {
if (audioPlayer != null) {
audioPlayer.release();
}
if (executorService != null && !executorService.isShutdown()) {
executorService.shutdown();
}
instance = null;
LogManager.logInfo(TAG, "VoicePlayerManager资源已释放");
}
/**
* 获取音频播放器实例供外部使用
*/
public AudioPlayer getAudioPlayer() {
return audioPlayer;
}
/**
* 获取资源配置管理器
*/
public VoiceResourceManager getResourceManager() {
return resourceManager;
}
/**
* 获取配置管理器
*/
public VoiceConfigManager getConfigManager() {
return configManager;
}
}

133
app/src/main/java/com/ouxuan/oxface/device/voice/VoicePlayerUsageExample.java

@ -0,0 +1,133 @@
package com.ouxuan.oxface.device.voice;
import android.content.Context;
import com.ouxuan.oxface.utils.LogManager;
/**
* 语音播放使用示例
* 展示如何在项目中使用语音播放模块
*/
public class VoicePlayerUsageExample {
private static final String TAG = "VoicePlayerUsageExample";
private VoicePlayerManager voicePlayerManager;
private Context context;
public VoicePlayerUsageExample(Context context) {
this.context = context;
}
/**
* 初始化语音播放器
*/
public void initializeVoicePlayer() {
try {
voicePlayerManager = VoicePlayerManager.getInstance();
voicePlayerManager.initialize(context);
// 设置播放监听器
voicePlayerManager.getAudioPlayer().setOnVoicePlayListener(new AudioPlayer.OnVoicePlayListener() {
@Override
public void onPlayStart(String filePath) {
LogManager.logInfo(TAG, "语音开始播放: " + filePath);
}
@Override
public void onPlayComplete(String filePath) {
LogManager.logInfo(TAG, "语音播放完成: " + filePath);
}
@Override
public void onPlayError(String filePath, String error) {
LogManager.logError(TAG, "语音播放错误: " + filePath + ", 错误信息: " + error);
}
});
LogManager.logInfo(TAG, "语音播放器初始化成功");
} catch (Exception e) {
LogManager.logError(TAG, "语音播放器初始化失败", e);
}
}
/**
* 演示各种语音播放场景
*/
public void demonstrateVoicePlayback() {
if (voicePlayerManager == null) {
LogManager.logError(TAG, "语音播放器未初始化");
return;
}
// 1. 订单相关语音
voicePlayerManager.playVoice(VoiceType.SELECT_ORDER_CONFIRM);
// 2. 人脸识别相关语音
voicePlayerManager.playVoice(VoiceType.FACE_RECOGNITION);
// 3. 门禁相关语音
voicePlayerManager.playVoice(VoiceType.CLOSE_AB_GATE);
// 4. 核销成功相关语音根据场景选择
// 进场场景
voicePlayerManager.playVoice(VoiceType.VERIFICATION_SUCCESS_ENTRY);
// 离场场景
// voicePlayerManager.playVoice(VoiceType.VERIFICATION_SUCCESS_LEAVE);
// 5. 其他场景语音
voicePlayerManager.playVoice(VoiceType.ONE_PERSON_ENTRY);
voicePlayerManager.playVoice(VoiceType.ORDER_EXPIRED);
voicePlayerManager.playVoice(VoiceType.COURSE_SIGN_SUCCESS);
}
/**
* 配置语音播放参数
*/
public void configureVoicePlayback() {
if (voicePlayerManager == null) {
LogManager.logError(TAG, "语音播放器未初始化");
return;
}
// 获取配置管理器
VoiceConfigManager configManager = voicePlayerManager.getConfigManager();
// 启用语音播放
configManager.setVoiceEnabled(true);
// 设置音量
configManager.setVoiceVolume(0.8f);
// 设置播放速度
configManager.setVoiceSpeed(1.0f);
LogManager.logInfo(TAG, "语音播放配置已完成: " + configManager.getAllConfigInfo());
}
/**
* 预加载所有语音文件
*/
public void preloadAllVoices() {
if (voicePlayerManager == null) {
LogManager.logError(TAG, "语音播放器未初始化");
return;
}
// 获取资源管理器并预加载所有语音文件
VoiceResourceManager resourceManager = voicePlayerManager.getResourceManager();
resourceManager.preloadAllVoices();
LogManager.logInfo(TAG, "所有语音文件预加载完成");
}
/**
* 释放语音播放器资源
*/
public void releaseVoicePlayer() {
if (voicePlayerManager != null) {
voicePlayerManager.release();
voicePlayerManager = null;
LogManager.logInfo(TAG, "语音播放器资源已释放");
}
}
}

134
app/src/main/java/com/ouxuan/oxface/device/voice/VoiceResourceManager.java

@ -0,0 +1,134 @@
package com.ouxuan.oxface.device.voice;
import android.content.Context;
import android.os.Environment;
import com.ouxuan.oxface.utils.LogManager;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
* 语音资源管理器
* 负责管理语音文件资源的加载和路径获取
*/
public class VoiceResourceManager {
private static final String TAG = "VoiceResourceManager";
private static final String VOICE_ASSETS_DIR = "voices";
private static final String VOICE_STORAGE_DIR = "oxface_voices";
private Context context;
private Map<VoiceType, String> voicePathMap;
public VoiceResourceManager(Context context) {
this.context = context.getApplicationContext();
this.voicePathMap = new HashMap<>();
initVoiceResources();
}
/**
* 初始化语音资源
*/
private void initVoiceResources() {
LogManager.logInfo(TAG, "初始化语音资源");
// 为每种语音类型初始化文件路径
for (VoiceType voiceType : VoiceType.values()) {
String filePath = getInternalStoragePath(voiceType.getFileName());
voicePathMap.put(voiceType, filePath);
LogManager.logDebug(TAG, "语音资源映射: " + voiceType.name() + " -> " + filePath);
}
LogManager.logInfo(TAG, "语音资源初始化完成,共" + voicePathMap.size() + "个语音文件");
}
/**
* 获取语音文件路径
*/
public String getVoiceFilePath(VoiceType voiceType) {
String filePath = voicePathMap.get(voiceType);
if (filePath == null) {
LogManager.logWarning(TAG, "未找到语音类型对应的文件路径: " + voiceType.name());
return null;
}
// 检查文件是否存在
File voiceFile = new File(filePath);
if (!voiceFile.exists()) {
LogManager.logWarning(TAG, "语音文件不存在,尝试从assets复制: " + filePath);
if (!copyAssetToInternalStorage(voiceType.getFileName())) {
LogManager.logError(TAG, "从assets复制语音文件失败: " + voiceType.getFileName());
return null;
}
}
return filePath;
}
/**
* 获取内部存储路径
*/
private String getInternalStoragePath(String fileName) {
File voiceDir = new File(context.getFilesDir(), VOICE_STORAGE_DIR);
if (!voiceDir.exists()) {
voiceDir.mkdirs();
}
return new File(voiceDir, fileName).getAbsolutePath();
}
/**
* 从assets复制文件到内部存储
*/
private boolean copyAssetToInternalStorage(String fileName) {
String assetPath = VOICE_ASSETS_DIR + "/" + fileName;
String internalPath = getInternalStoragePath(fileName);
try {
InputStream inputStream = context.getAssets().open(assetPath);
FileOutputStream outputStream = new FileOutputStream(internalPath);
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
inputStream.close();
outputStream.close();
LogManager.logInfo(TAG, "语音文件复制成功: " + assetPath + " -> " + internalPath);
return true;
} catch (IOException e) {
LogManager.logError(TAG, "复制语音文件失败: " + assetPath, e);
return false;
}
}
/**
* 预加载所有语音文件
*/
public void preloadAllVoices() {
LogManager.logInfo(TAG, "开始预加载所有语音文件");
int successCount = 0;
int failCount = 0;
for (VoiceType voiceType : VoiceType.values()) {
if (getVoiceFilePath(voiceType) != null) {
successCount++;
} else {
failCount++;
}
}
LogManager.logInfo(TAG, "语音文件预加载完成 - 成功: " + successCount + ", 失败: " + failCount);
}
/**
* 获取语音文件存储目录
*/
public String getVoiceStorageDirectory() {
return new File(context.getFilesDir(), VOICE_STORAGE_DIR).getAbsolutePath();
}
}

142
app/src/main/java/com/ouxuan/oxface/device/voice/VoiceTestActivity.java

@ -0,0 +1,142 @@
package com.ouxuan.oxface.device.voice;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import com.ouxuan.oxface.R;
import com.ouxuan.oxface.utils.LogManager;
/**
* 语音模块测试Activity
* 用于测试语音播放功能
*/
public class VoiceTestActivity extends AppCompatActivity {
private static final String TAG = "VoiceTestActivity";
private VoicePlayerManager voicePlayerManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_voice_test);
initVoicePlayer();
initViews();
LogManager.logInfo(TAG, "VoiceTestActivity创建");
}
/**
* 初始化语音播放器
*/
private void initVoicePlayer() {
try {
voicePlayerManager = VoicePlayerManager.getInstance();
voicePlayerManager.initialize(this);
// 设置播放监听器
voicePlayerManager.getAudioPlayer().setOnVoicePlayListener(new AudioPlayer.OnVoicePlayListener() {
@Override
public void onPlayStart(String filePath) {
LogManager.logInfo(TAG, "语音开始播放: " + filePath);
}
@Override
public void onPlayComplete(String filePath) {
LogManager.logInfo(TAG, "语音播放完成: " + filePath);
}
@Override
public void onPlayError(String filePath, String error) {
LogManager.logError(TAG, "语音播放错误: " + filePath + ", 错误信息: " + error);
}
});
LogManager.logInfo(TAG, "语音播放器初始化成功");
} catch (Exception e) {
LogManager.logError(TAG, "语音播放器初始化失败", e);
}
}
/**
* 初始化视图
*/
private void initViews() {
// 订单相关按钮
Button btnSelectOrder = findViewById(R.id.btn_select_order);
Button btnInquireOrder = findViewById(R.id.btn_inquire_order);
// 人脸识别相关按钮
Button btnFaceRecognition = findViewById(R.id.btn_face_recognition);
// 门禁相关按钮
Button btnCloseAbGate = findViewById(R.id.btn_close_ab_gate);
// 核销成功相关按钮
Button btnVerificationEntry = findViewById(R.id.btn_verification_entry);
Button btnVerificationLeave = findViewById(R.id.btn_verification_leave);
// 其他场景按钮
Button btnOnePerson = findViewById(R.id.btn_one_person);
Button btnOrderExpired = findViewById(R.id.btn_order_expired);
Button btnCourseSign = findViewById(R.id.btn_course_sign);
// 控制按钮
Button btnEnableVoice = findViewById(R.id.btn_enable_voice);
Button btnDisableVoice = findViewById(R.id.btn_disable_voice);
Button btnStopVoice = findViewById(R.id.btn_stop_voice);
// 设置点击事件
btnSelectOrder.setOnClickListener(v -> playVoice(VoiceType.SELECT_ORDER_CONFIRM));
btnInquireOrder.setOnClickListener(v -> playVoice(VoiceType.INQUIRE_ORDER));
btnFaceRecognition.setOnClickListener(v -> playVoice(VoiceType.FACE_RECOGNITION));
btnCloseAbGate.setOnClickListener(v -> playVoice(VoiceType.CLOSE_AB_GATE));
btnVerificationEntry.setOnClickListener(v -> playVoice(VoiceType.VERIFICATION_SUCCESS_ENTRY));
btnVerificationLeave.setOnClickListener(v -> playVoice(VoiceType.VERIFICATION_SUCCESS_LEAVE));
btnOnePerson.setOnClickListener(v -> playVoice(VoiceType.ONE_PERSON_ENTRY));
btnOrderExpired.setOnClickListener(v -> playVoice(VoiceType.ORDER_EXPIRED));
btnCourseSign.setOnClickListener(v -> playVoice(VoiceType.COURSE_SIGN_SUCCESS));
btnEnableVoice.setOnClickListener(v -> setVoiceEnabled(true));
btnDisableVoice.setOnClickListener(v -> setVoiceEnabled(false));
btnStopVoice.setOnClickListener(v -> stopCurrentVoice());
}
/**
* 播放指定语音
*/
private void playVoice(VoiceType voiceType) {
if (voicePlayerManager != null) {
voicePlayerManager.playVoice(voiceType);
}
}
/**
* 设置语音启用状态
*/
private void setVoiceEnabled(boolean enabled) {
if (voicePlayerManager != null) {
voicePlayerManager.getConfigManager().setVoiceEnabled(enabled);
LogManager.logInfo(TAG, "语音播放已" + (enabled ? "启用" : "禁用"));
}
}
/**
* 停止当前语音播放
*/
private void stopCurrentVoice() {
if (voicePlayerManager != null) {
voicePlayerManager.stopCurrentVoice();
LogManager.logInfo(TAG, "已停止当前语音播放");
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (voicePlayerManager != null) {
voicePlayerManager.release();
}
LogManager.logInfo(TAG, "VoiceTestActivity销毁");
}
}

46
app/src/main/java/com/ouxuan/oxface/device/voice/VoiceType.java

@ -0,0 +1,46 @@
package com.ouxuan.oxface.device.voice;
/**
* 语音类型枚举
* 定义所有可用的语音提示类型
*/
public enum VoiceType {
// 订单相关
SELECT_ORDER_CONFIRM("请选择订单确认核销", "select_order_confirm.wav"),
INQUIRE_ORDER("正在查询您的订单", "inquire_order.wav"),
// 人脸识别相关
FACE_RECOGNITION("正在进行人脸识别", "face_recognition.wav"),
// 门禁相关
CLOSE_AB_GATE("请关闭ab门", "close_ab_gate.wav"),
// 核销成功相关
VERIFICATION_SUCCESS_ENTRY("核销成功请推门", "verification_success_entry.wav"), // 进场-识别成功
VERIFICATION_SUCCESS_LEAVE("离场请推门", "verification_success_leave.wav"), // 离场-识别成功
// 人数相关
ONE_PERSON_ENTRY("请1人进场进行核销", "one_person_entry.wav"),
// 订单状态相关
ORDER_EXPIRED("订单超期需要扫码补缴", "order_expired.wav"),
// 课程相关
COURSE_SIGN_SUCCESS("课程签到成功", "course_sign_success.wav");
private final String description;
private final String fileName;
VoiceType(String description, String fileName) {
this.description = description;
this.fileName = fileName;
}
public String getDescription() {
return description;
}
public String getFileName() {
return fileName;
}
}

31
app/src/main/res/layout/activity_debug.xml

@ -287,6 +287,37 @@
</LinearLayout>
<!-- 第八行按钮 - 新增语音测试按钮 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="8dp">
<Button
android:id="@+id/btnTestVoice"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="语音测试"
android:layout_marginEnd="4dp"
android:textSize="12sp" />
<View
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp" />
<View
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="4dp" />
</LinearLayout>
</LinearLayout>
</ScrollView>

198
app/src/main/res/layout/activity_voice_test.xml

@ -0,0 +1,198 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- 标题 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="语音模块测试"
android:textSize="24sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginBottom="24dp" />
<!-- 订单相关 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="订单相关"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginBottom="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="16dp">
<Button
android:id="@+id/btn_select_order"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="选择订单"
android:layout_marginEnd="8dp" />
<Button
android:id="@+id/btn_inquire_order"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="查询订单"
android:layout_marginStart="8dp" />
</LinearLayout>
<!-- 人脸识别相关 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="人脸识别相关"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginBottom="8dp"
android:layout_marginTop="16dp" />
<Button
android:id="@+id/btn_face_recognition"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="人脸识别"
android:layout_marginBottom="16dp" />
<!-- 门禁相关 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="门禁相关"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginBottom="8dp" />
<Button
android:id="@+id/btn_close_ab_gate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="关闭AB门"
android:layout_marginBottom="16dp" />
<!-- 核销成功相关 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="核销成功相关"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginBottom="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="16dp">
<Button
android:id="@+id/btn_verification_entry"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="进场核销成功"
android:layout_marginEnd="8dp" />
<Button
android:id="@+id/btn_verification_leave"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="离场核销成功"
android:layout_marginStart="8dp" />
</LinearLayout>
<!-- 其他场景 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="其他场景"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginBottom="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="16dp">
<Button
android:id="@+id/btn_one_person"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="请1人进场"
android:layout_marginBottom="8dp" />
<Button
android:id="@+id/btn_order_expired"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="订单超期"
android:layout_marginBottom="8dp" />
<Button
android:id="@+id/btn_course_sign"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="课程签到成功" />
</LinearLayout>
<!-- 控制按钮 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="控制选项"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginBottom="8dp"
android:layout_marginTop="16dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="16dp">
<Button
android:id="@+id/btn_enable_voice"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="启用语音"
android:layout_marginEnd="8dp" />
<Button
android:id="@+id/btn_disable_voice"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="禁用语音"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" />
<Button
android:id="@+id/btn_stop_voice"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="停止播放"
android:layout_marginStart="8dp" />
</LinearLayout>
</LinearLayout>
</ScrollView>
Loading…
Cancel
Save