添加播放等待超时判断
This commit is contained in:
parent
e9eab6ae23
commit
724c5d51e0
13
app/src/main/java/com/zs/smarthuman/sherpa/TimeoutType.kt
Normal file
13
app/src/main/java/com/zs/smarthuman/sherpa/TimeoutType.kt
Normal file
@ -0,0 +1,13 @@
|
||||
package com.zs.smarthuman.sherpa
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lrs
|
||||
* @date: 2026/1/8 17:39
|
||||
*/
|
||||
enum class TimeoutType {
|
||||
IDLE_TIMEOUT, // 唤醒后全程没说话的闲时超时
|
||||
INVALID_SPEECH_TIMEOUT // 唤醒后一直无效说话的超时
|
||||
}
|
||||
|
||||
typealias OnTimeoutTip = (TimeoutType) -> Unit
|
||||
@ -2,17 +2,12 @@ package com.zs.smarthuman.sherpa
|
||||
|
||||
import android.content.res.AssetManager
|
||||
import com.blankj.utilcode.util.LogUtils
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.ArrayDeque
|
||||
|
||||
// 新增:超时类型枚举(区分两种超时)
|
||||
enum class TimeoutType {
|
||||
IDLE_TIMEOUT, // 唤醒后全程没说话的闲时超时
|
||||
INVALID_SPEECH_TIMEOUT // 唤醒后一直无效说话的超时
|
||||
}
|
||||
|
||||
// 新增:提示语回调(外部可通过此获取不同的提示语)
|
||||
typealias OnTimeoutTip = (TimeoutType) -> Unit
|
||||
|
||||
class VoiceController(
|
||||
assetManager: AssetManager,
|
||||
private val onWakeup: () -> Unit,
|
||||
@ -21,7 +16,6 @@ class VoiceController(
|
||||
maxRecordingSeconds: Int = 10,
|
||||
private val onStateChanged: ((VoiceState) -> Unit)? = null,
|
||||
private val stopBackendAudio: (() -> Unit)? = null,
|
||||
// 新增:超时提示语回调
|
||||
private val onTimeoutTip: OnTimeoutTip? = null
|
||||
) {
|
||||
|
||||
@ -120,6 +114,11 @@ class VoiceController(
|
||||
// ========== 补充:MIN_EFFECTIVE_SPEECH_RMS 常量(和VadManager对齐) ==========
|
||||
private val MIN_EFFECTIVE_SPEECH_RMS = 0.001f
|
||||
|
||||
|
||||
//播放等待超时
|
||||
private val PLAY_WAIT_TIMEOUT_MS = 3000L
|
||||
private var playWaitJob: Job? = null
|
||||
|
||||
/* ================= 音频入口 ================= */
|
||||
fun acceptAudio(samples: FloatArray) {
|
||||
cachePreBuffer(samples)
|
||||
@ -472,7 +471,12 @@ class VoiceController(
|
||||
}
|
||||
|
||||
fun onPlayStartBackend() {
|
||||
LogUtils.d(TAG, "🎶 播放后台音频 | 基线: $currentEnvBaseline")
|
||||
// 仅当上传完成(成功)且状态为 UPLOADING 时,才切换状态
|
||||
if (state != VoiceState.UPLOADING) {
|
||||
LogUtils.w(TAG, "🎶 非上传完成状态,禁止切换到 PLAYING_BACKEND | 当前状态: $state")
|
||||
return
|
||||
}
|
||||
LogUtils.d(TAG, "🎶 开始播放后台音频 | 基线: $currentEnvBaseline")
|
||||
state = VoiceState.PLAYING_BACKEND
|
||||
}
|
||||
|
||||
@ -485,13 +489,43 @@ class VoiceController(
|
||||
fun onUploadFinished(success: Boolean) {
|
||||
if (state != VoiceState.UPLOADING) return
|
||||
LogUtils.d(TAG, "📤 上传完成 | 成功: $success | 基线: $currentEnvBaseline")
|
||||
state = if (success) VoiceState.PLAYING_BACKEND
|
||||
else {
|
||||
|
||||
if (success) {
|
||||
// 上传成功:启动协程超时任务
|
||||
startPlayWaitTimer()
|
||||
} else {
|
||||
// 上传失败:取消超时任务,重置状态
|
||||
cancelPlayWaitTimer()
|
||||
speechEnableAtMs = System.currentTimeMillis() + SPEECH_COOLDOWN_MS
|
||||
VoiceState.WAIT_SPEECH_COOLDOWN
|
||||
state = VoiceState.WAIT_SPEECH_COOLDOWN
|
||||
}
|
||||
}
|
||||
|
||||
private fun startPlayWaitTimer() {
|
||||
// 先取消旧任务,避免重复
|
||||
cancelPlayWaitTimer()
|
||||
|
||||
// 启动协程超时任务(Dispatchers.Main保证状态修改在主线程)
|
||||
playWaitJob = GlobalScope.launch {
|
||||
delay(PLAY_WAIT_TIMEOUT_MS) // 挂起3秒,无线程阻塞
|
||||
LogUtils.w(TAG, "⏱ 播放等待超时(${PLAY_WAIT_TIMEOUT_MS}ms),自动重置状态")
|
||||
|
||||
// 超时后重置状态(加同步锁,避免多线程冲突)
|
||||
synchronized(this@VoiceController) {
|
||||
speechEnableAtMs = System.currentTimeMillis() + SPEECH_COOLDOWN_MS
|
||||
state = VoiceState.WAIT_SPEECH_COOLDOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ================= 替换:取消协程任务 =================
|
||||
private fun cancelPlayWaitTimer() {
|
||||
playWaitJob?.cancel() // 取消协程(挂起函数会立即停止)
|
||||
playWaitJob = null
|
||||
LogUtils.d(TAG, "🔄 播放等待协程已取消")
|
||||
}
|
||||
|
||||
|
||||
private fun resetToWaitSpeech() {
|
||||
LogUtils.d(TAG, "🔄 重置到等待说话 | 基线: $currentEnvBaseline | 已标记无效说话: $hasInvalidSpeech")
|
||||
val now = System.currentTimeMillis()
|
||||
@ -525,6 +559,7 @@ class VoiceController(
|
||||
currentTimeoutType = TimeoutType.IDLE_TIMEOUT
|
||||
LogUtils.d(TAG, "🔄 环境基线已重置 | 新基线: $currentEnvBaseline | 无效说话标记已重置")
|
||||
state = VoiceState.WAIT_WAKEUP
|
||||
cancelPlayWaitTimer()
|
||||
}
|
||||
|
||||
fun release() {
|
||||
@ -536,6 +571,7 @@ class VoiceController(
|
||||
// 核心新增:释放资源时重置标记
|
||||
hasInvalidSpeech = false
|
||||
currentTimeoutType = TimeoutType.IDLE_TIMEOUT
|
||||
cancelPlayWaitTimer()
|
||||
}
|
||||
|
||||
private fun cachePreBuffer(samples: FloatArray) {
|
||||
|
||||
@ -148,8 +148,11 @@ class MainActivity : BaseViewModelActivity<ActivityMainBinding, MainViewModel>()
|
||||
voiceController?.onUploadFinished(false)
|
||||
}
|
||||
|
||||
is ApiResult.Success<*> -> {
|
||||
Toaster.showShort("上传成功")
|
||||
is ApiResult.Success<String> -> {
|
||||
if (!TextUtils.isEmpty(it.data)){
|
||||
Toaster.showShort(it.data)
|
||||
}
|
||||
Toaster.showShort(it)
|
||||
voiceController?.onUploadFinished(true)
|
||||
}
|
||||
}
|
||||
@ -180,7 +183,7 @@ class MainActivity : BaseViewModelActivity<ActivityMainBinding, MainViewModel>()
|
||||
onWakeup = {
|
||||
Log.d("lrs", "当前状态: 唤醒成功wakeup")
|
||||
//每次唤醒前都要把前面的音频停掉
|
||||
UnityPlayerHolder.getInstance().cancelPCM()
|
||||
// UnityPlayerHolder.getInstance().cancelPCM()
|
||||
UnityPlayerHolder.getInstance()
|
||||
.sendVoiceToUnity(
|
||||
voiceInfo = mutableListOf<VoiceBeanResp>().apply {
|
||||
@ -200,12 +203,12 @@ class MainActivity : BaseViewModelActivity<ActivityMainBinding, MainViewModel>()
|
||||
1
|
||||
)
|
||||
// loadLocalJsonAndPlay()
|
||||
// val file = File(
|
||||
// getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)!!.getAbsolutePath(),
|
||||
// "xxx.wav"
|
||||
// )
|
||||
// AudioDebugUtil.saveFloatPcmAsWav(audio, file)
|
||||
// LogUtils.dTag("audioxx", "WAV saved: ${file.path}, samples=${audio.size}")
|
||||
val file = File(
|
||||
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)!!.getAbsolutePath(),
|
||||
"xxx.wav"
|
||||
)
|
||||
AudioDebugUtil.saveFloatPcmAsWav(audio, file)
|
||||
LogUtils.dTag("audioxx", "WAV saved: ${file.path}, samples=${audio.size}")
|
||||
// lifecycleScope.launch(Dispatchers.Main) {
|
||||
//
|
||||
// mVerticalAnimator?.show()
|
||||
|
||||
@ -34,8 +34,8 @@ class MainViewModel: BaseViewModel() {
|
||||
}
|
||||
|
||||
|
||||
private val _uploadVoiceLiveData = ApiLiveData<Unit>()
|
||||
val uploadVoiceLiveData: LiveData<ApiResult<Unit>> = _uploadVoiceLiveData
|
||||
private val _uploadVoiceLiveData = ApiLiveData<String>()
|
||||
val uploadVoiceLiveData: LiveData<ApiResult<String>> = _uploadVoiceLiveData
|
||||
fun uploadVoice(audioVoice: String, sessionCode: Int){
|
||||
_uploadVoiceLiveData.request {
|
||||
RxHttp.postJson(ApiService.UPLOAD_RECORD_VOICE_URL)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user