diff --git a/OCH/common/biz/src/main/res/layout/biz_login_business_view.xml b/OCH/common/biz/src/main/res/layout/biz_login_business_view.xml
index 987e471991..5e7f8690bb 100644
--- a/OCH/common/biz/src/main/res/layout/biz_login_business_view.xml
+++ b/OCH/common/biz/src/main/res/layout/biz_login_business_view.xml
@@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="@dimen/dp_200"
- android:layout_height="@dimen/dp_580"
+ android:layout_height="@dimen/dp_680"
android:orientation="vertical"
android:background="@drawable/biz_login_error_info">
diff --git a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/TaxiPassengerBaseFragment.kt b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/TaxiPassengerBaseFragment.kt
index 1545626b05..20898c27ae 100644
--- a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/TaxiPassengerBaseFragment.kt
+++ b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/TaxiPassengerBaseFragment.kt
@@ -13,9 +13,9 @@ import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.function.call.hmi.CallerHmiViewControlListenerManager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_TAXI_P
-import com.mogo.eagle.core.utilcode.util.DeviceUtils
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import com.mogo.map.listener.IMogoMapListener
+import com.mogo.mgintelligent.speech.iflytek.WakeUpManager
import com.mogo.och.common.module.biz.provider.CommonService
import com.mogo.och.common.module.constant.OchCommonConst
import com.mogo.och.common.module.manager.xiaozhi.ZhiStateManager
@@ -135,6 +135,12 @@ class TaxiPassengerBaseFragment :
settingAndMusicListener()
bottom.setOverMapApplyClick(object : BottomBar.ApplyClickLintener{
override fun onApplyClick(selectItem: BottomBar.SelectView) {
+ CallerLogger.d(TAG,"选择的项:${selectItem}")
+ if(selectItem==BottomBar.SelectView.PRECISIONMAP){
+ WakeUpManager.startWakeUp()
+ }else{
+ WakeUpManager.stopWakeup()
+ }
when (selectItem) {
BottomBar.SelectView.PRECISIONMAP -> {
CallerHmiViewControlListenerManager.invokeMainPageViewVisible(View.VISIBLE)
diff --git a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AIViewModel.kt b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AIViewModel.kt
index ee71cbe84c..5644067e07 100644
--- a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AIViewModel.kt
+++ b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AIViewModel.kt
@@ -11,8 +11,8 @@ import com.mogo.mgintelligent.speech.AsrResult
import com.mogo.mgintelligent.speech.AsrState
import com.mogo.mgintelligent.speech.IWakeUpListener
import com.mogo.mgintelligent.speech.MGSpeech
-import com.mogo.mgintelligent.speech.iflytek.WakeManager
import com.mogo.mgintelligent.speech.iflytek.WakeUpListener
+import com.mogo.mgintelligent.speech.iflytek.WakeUpManager
import com.mogo.och.bridge.autopilot.location.OchLocationManager
import com.mogo.och.data.taxi.BaseOrderBean
import com.mogo.och.data.taxi.TaxiOrderStatusEnum
@@ -86,7 +86,7 @@ class AIViewModel : ViewModel(), AIMessageManager.AIMessageListener, IWakeUpList
private val wakeUpListener = object :WakeUpListener{
override fun wakeupSuccess() {
onWakeUp()
- WakeManager.stopWakeup()
+ WakeUpManager.stopWakeup()
}
}
@@ -99,7 +99,7 @@ class AIViewModel : ViewModel(), AIMessageManager.AIMessageListener, IWakeUpList
OrderModel.setOrderStatusCallback(TAG,orderListener)
OchLocationManager.addGCJ02Listener(TAG,1,locationCallback)
AIMessageManager.registerListener(this)
- WakeManager.setWakeUpListener(wakeUpListener)
+ WakeUpManager.setWakeUpListener(wakeUpListener)
mgSpeech.weakUpListener = this
mgSpeech.startWeakUp()
@@ -134,7 +134,7 @@ class AIViewModel : ViewModel(), AIMessageManager.AIMessageListener, IWakeUpList
override fun onCleared() {
AIMessageManager.unregisterListener(this)
mgSpeech.weakUpListener = null
- WakeManager.setWakeUpListener(null)
+ WakeUpManager.setWakeUpListener(null)
mgSpeech.stopWeakUp()
llmResultJob?.cancel()
OrderModel.setOrderStatusCallback(TAG,null)
@@ -240,7 +240,7 @@ class AIViewModel : ViewModel(), AIMessageManager.AIMessageListener, IWakeUpList
}
AsrState.STATE_EXIT -> {
- WakeManager.startWakeUp()
+ WakeUpManager.startWakeUp()
_asrUIStateFlow.value = AsrUIState.Idle
mgSpeech.speak(tipsExit)
mgSpeech.isAssistantShow(false)
diff --git a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AiView.kt b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AiView.kt
index ba271fbc90..12620386eb 100644
--- a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AiView.kt
+++ b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AiView.kt
@@ -15,15 +15,10 @@ import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.findViewTreeViewModelStoreOwner
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.mogo.eagle.core.data.autopilot.pnc.PncActionsHelper
-import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotPlanningActionsListener
-import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener.Companion.STATUS_AUTOPILOT_RUNNING
-import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerPlanningActionsListenerManager
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
-import com.mogo.eagle.core.utilcode.util.UiThreadHandler
-import com.mogo.mgintelligent.speech.iflytek.WakeManager
+import com.mogo.mgintelligent.speech.iflytek.WakeUpManager
import com.mogo.och.common.module.manager.logchainanalytic.OchChainLogManager
import com.mogo.och.common.module.utils.BigFrameAnimatorContainer
import com.mogo.och.common.module.utils.RxUtils
@@ -42,8 +37,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
-import mogo.telematics.pad.MessagePad
-import mogo.telematics.pad.MessagePad.DrivingState.USING_RSI_LINK_VALUE
class AiView @JvmOverloads constructor(
context: Context,
@@ -170,11 +163,6 @@ class AiView @JvmOverloads constructor(
super.onVisibilityAggregated(isVisible)
CallerLogger.d(TAG,"是否展示中:${isVisible}")
try {
- if(isVisible){
- WakeManager.startWakeUp()
- }else{
- WakeManager.stopWakeup()
- }
if(isVisible){
aiAnimator?.start()
RxUtils.createSubscribe(3_000) {
@@ -202,6 +190,7 @@ class AiView @JvmOverloads constructor(
) {
CallerLogger.d(TAG,"${tName()} onTransitionCompleted:${motionLayout?.id}_$currentId")
rvMessagesEmpty.visibility = View.VISIBLE
+ WakeUpManager.startWakeUp()
startContextInfo()
startListInfo()
}
diff --git a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/statusview/StatusBarView.kt b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/statusview/StatusBarView.kt
index d4960b3e42..13f5dbd5a3 100644
--- a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/statusview/StatusBarView.kt
+++ b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/statusview/StatusBarView.kt
@@ -62,12 +62,6 @@ class StatusBarView @JvmOverloads constructor(
LayoutInflater.from(context).inflate(R.layout.taxt_u_p_statusview, this, true)
isClickable = true
isFocusable = true
- textClockDate.onClick {
- WakeManager.startWakeUp()
- }
- bcsv_status.onClick {
- WakeManager.stopWakeup()
- }
}
override fun onAttachedToWindow() {
diff --git a/libraries/mogo-speech/src/main/AndroidManifest.xml b/libraries/mogo-speech/src/main/AndroidManifest.xml
index a253f68ad2..2b0ee014cc 100644
--- a/libraries/mogo-speech/src/main/AndroidManifest.xml
+++ b/libraries/mogo-speech/src/main/AndroidManifest.xml
@@ -1,4 +1,7 @@
+
+
\ No newline at end of file
diff --git a/libraries/mogo-speech/src/main/assets/ivw/version_2 b/libraries/mogo-speech/src/main/assets/ivw/version_1
similarity index 100%
rename from libraries/mogo-speech/src/main/assets/ivw/version_2
rename to libraries/mogo-speech/src/main/assets/ivw/version_1
diff --git a/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/iflytek/WakeManager.kt b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/iflytek/WakeManager.kt
index 8be2cfb2f0..fff50bcec9 100644
--- a/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/iflytek/WakeManager.kt
+++ b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/iflytek/WakeManager.kt
@@ -1,63 +1,23 @@
package com.mogo.mgintelligent.speech.iflytek
-import android.Manifest
-import android.annotation.SuppressLint
import android.content.Context
-import android.content.pm.PackageManager
-import android.media.AudioFormat
-import android.media.AudioRecord
-import android.media.MediaRecorder
import android.util.Log
-import androidx.core.app.ActivityCompat
-import com.iflytek.aikit.core.AiAudio
-import com.iflytek.aikit.core.AiHandle
import com.iflytek.aikit.core.AiHelper
-import com.iflytek.aikit.core.AiListener
-import com.iflytek.aikit.core.AiRequest
-import com.iflytek.aikit.core.AiResponse
-import com.iflytek.aikit.core.AiStatus
import com.iflytek.aikit.core.BaseLibrary
import com.iflytek.aikit.core.CoreListener
import com.iflytek.aikit.core.ErrType
import com.iflytek.aikit.core.LogLvl
import com.mogo.commons.AbsMogoApplication
-import com.mogo.eagle.core.function.call.setting.CallerRequestActivityHandleManager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.util.FileUtils
import com.mogo.eagle.core.utilcode.util.ResourceUtils
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.eagle.core.utilcode.util.Utils
-import java.io.BufferedWriter
import java.io.File
-import java.io.FileOutputStream
-import java.io.IOException
-import java.io.OutputStreamWriter
-import java.util.concurrent.TimeUnit
-import java.util.concurrent.atomic.AtomicBoolean
-import kotlin.math.abs
-import kotlin.math.log10
object WakeManager {
- private val TAG = "WakeManager"
-
- private const val ABILITYID: String = "e867a88f2"
-
- private val weakupKey = "你好小智,小智小智,小智你好"
-
- private val isEnd = AtomicBoolean(true)
-
- //录音是否在进行
- private val isRecording = AtomicBoolean(false)
-
- private var aiHandle: AiHandle? = null
-
- private var audioRecord: AudioRecord? = null
-
- private var _wakeUpListener:WakeUpListener?=null
-
- //录音缓冲区大小
- private const val BUFFER_SIZE = 1280
+ private const val TAG = "WakeManager"
private val coreListener = CoreListener { type, code ->
Log.i(TAG, "core listener code:$code")
@@ -66,10 +26,10 @@ object WakeManager {
ErrType.AUTH -> {
if (code == 0) {
Log.i(TAG, "SDK授权成功")
+ WakeUpManager.startThread()
} else {
Log.i(TAG, "SDK授权失败,授权码为:$code")
}
-
}
ErrType.HTTP -> Log.i(TAG, "SDK状态:HTTP认证结果$code")
@@ -79,258 +39,6 @@ object WakeManager {
}
- fun setWakeUpListener(wakeUpListener:WakeUpListener?){
- this._wakeUpListener = wakeUpListener
- }
-
- fun startWakeUp(){
- ThreadUtils.getIoPool().submit {
- // 检测权限
- val granted = ActivityCompat.checkSelfPermission(AbsMogoApplication.getApp(),
- Manifest.permission.RECORD_AUDIO)
- if(granted != PackageManager. PERMISSION_GRANTED){
- CallerRequestActivityHandleManager.requestPermission(TAG,Manifest.permission.RECORD_AUDIO)
- return@submit
- }
- AiHelper.getInst().registerListener(ABILITYID, edListener)
- CallerLogger.d(TAG,"START")
- val ret = start()
- if (ret == 0) {
- createAudioRecord() //创建录音器
- isRecording.set(true)
- audioRecord?.startRecording() //录音器启动录音
-
- writeRecording(AiStatus.BEGIN)
- }
- }
- }
-
- fun stopWakeup(){
- if(!isEnd.get()) {
- writeRecording(AiStatus.END)
- }
- }
-
- /**
- * 能力监听回调
- */
- private val edListener: AiListener = object : AiListener {
- override fun onResult(handleID: Int, outputData: List, usrContext: Any?) {
- if (null != outputData && outputData.size > 0) {
- for (i in outputData.indices) {
- CallerLogger.d(TAG, "onResult:handleID:" + handleID + ":" + outputData[i].key)
- val key = outputData[i].key //引擎结果的key
- val bytes = outputData[i].value //识别结果
- val result = String(bytes)
- CallerLogger.d(TAG, "key=$key")
- CallerLogger.d(TAG, "value=$result")
- CallerLogger.d(TAG, "status=" + outputData[i].status)
- if ((key == "func_wake_up" || key == "func_pre_wakeup")) {
- CallerLogger.d(TAG,"$key: \n $result")
- }
- _wakeUpListener?.wakeupSuccess()
- }
- }
- }
-
- override fun onEvent(i: Int, i1: Int, list: List, o: Any) {
- CallerLogger.i(TAG, "onEvent:$i,event:$i1")
- }
-
- override fun onError(i: Int, i1: Int, s: String, o: Any) {
- CallerLogger.d(TAG,"错误通知,能力执行终止,Ability $i ERROR::$s,err code:$i1")
- }
- }
-
- private fun writeRecording(aiStatus:AiStatus){
-
- /**
- * 写入数据-录音方式
- */
- val data = ByteArray(BUFFER_SIZE)
- val read = audioRecord!!.read(data, 0, BUFFER_SIZE)
-
- //平方和除以数据总长度,得到音量大小。
- val volume = calculateVolume(data)
-// CallerLogger.d(TAG,"当前分贝:${ abs(volume.toDouble())}")
- if (AudioRecord.ERROR_INVALID_OPERATION != read) {
- //处理录音数据
- write(data, aiStatus) //送尾帧
- }
- if (AiStatus.END == aiStatus) {
- if ( audioRecord != null) {
- audioRecord!!.stop()
- isRecording.set(false)
- end()
- }
- } else {
- if (isRecording.get()) {
- writeRecording(AiStatus.CONTINUE) //调用write方法送音频数据给引擎
- }
- }
- }
-
- /**
- * 结束会话
- */
- private fun end() {
- if (!isEnd.get()) {
- val ret = AiHelper.getInst().end(aiHandle)
- if (ret == 0) {
- isEnd.set(true)
- aiHandle = null
- CallerLogger.d(TAG,"唤醒完成,end: $ret")
- } else {
- isEnd.set(false)
- ThreadUtils.runOnUiThreadDelayed({
- end()
- CallerLogger.d(TAG,"关闭失败2s后重新广播唤醒完成,end: $ret")
- },2_000)
- CallerLogger.d(TAG,"唤醒完成,end: $ret")
- }
- }
- }
-
- /**
- * 写入数据
- */
- private fun write(part: ByteArray, status: AiStatus) {
- if (isEnd.get()) {
- return
- }
- val dataBuilder = AiRequest.builder()
- var ret = 0
-
- /**
- * 送入音频需要标识音频的状态,第一帧为起始帧,status要传AiStatus.BEGIN,最后一帧为结束帧,status要传AiStatus.END,其他为中间帧,status要传AiStatus.CONTINUE
- * 音频要求16bit,16K,单声道的pcm音频。
- * 建议每次发送音频间隔40ms,每次发送音频字节数为一帧音频大小的整数倍。
- */
- val aiAudio = AiAudio.get("wav").data(part).status(status).valid()
- dataBuilder.payload(aiAudio)
-
- ret = AiHelper.getInst().write(dataBuilder.build(), aiHandle)
- if (ret != 0) {
- CallerLogger.d(TAG,"write失败:$ret ")
- }
- }
-
- /**
- * 根据录音数据计算音量
- */
- private fun calculateVolume(buffer: ByteArray): Int {
- var sumVolume = 0.0
- var avgVolume = 0.0
- var volume = 0
- var i = 0
- while (i < buffer.size) {
- val v1 = buffer[i].toInt() and 0xFF
- val v2 = buffer[i + 1].toInt() and 0xFF
- var temp = v1 + (v2 shl 8) // 小端
- if (temp >= 0x8000) {
- temp = 0xffff - temp
- }
- sumVolume += abs(temp.toDouble())
- i += 2
- }
- avgVolume = sumVolume / buffer.size / 2
- volume = (log10(1 + avgVolume) * 10).toInt()
- return volume
- }
-
- /**
- * 创建录音器
- */
- @SuppressLint("MissingPermission")
- private fun createAudioRecord() {
- if (isRecording.get()) {
- return
- }
- if (audioRecord == null) {
- Log.d(TAG, "createAudioRecord")
- audioRecord = AudioRecord(
- MediaRecorder.AudioSource.MIC,
- 16000,
- AudioFormat.CHANNEL_IN_MONO,
- AudioFormat.ENCODING_PCM_16BIT,
- BUFFER_SIZE
- )
- }
- }
-
- /**
- * 开始会话
- */
- private fun start(): Int {
- if (!keyword2File()) {
- CallerLogger.d(TAG,"唤醒词文件写入失败,请检查是否有读写权限。")
- return -1
- }
- val customBuilder = AiRequest.builder()
- customBuilder.customText("key_word", getIvwDir() + "/keyword.txt", 0)
- var ret = AiHelper.getInst().loadData(ABILITYID, customBuilder.build())
- if (ret != 0) {
- CallerLogger.d(TAG,"open ivw loadData 失败:$ret")
- return ret
- }
- CallerLogger.d(TAG,"open ivw loadData success:$ret")
- val indexs = intArrayOf(0)
- ret = AiHelper.getInst().specifyDataSet(ABILITYID, "key_word", indexs) //从缓存中把个性化资源设置到引擎中
- if (ret != 0) {
- CallerLogger.d(TAG,"open ivw specifyDataSet 失败:$ret")
- return ret
- }
- CallerLogger.d(TAG,"open ivw specifyDataSet success:$ret")
-
- val paramBuilder = AiRequest.builder()
- paramBuilder.param("wdec_param_nCmThreshold", "0 0:800")
- paramBuilder.param("gramLoad", true)
- isEnd.set(false)
- aiHandle = AiHelper.getInst().start(ABILITYID, paramBuilder.build(), null)
- if (aiHandle?.code != 0) {
- CallerLogger.d(TAG,"open ivw start失败:${aiHandle?.getCode()}")
- return aiHandle?.code ?:0
- }
- return 0
- }
-
- private fun keyword2File(): Boolean {
- try {
- val keywordFile: File = File(getIvwDir() + "/keyword.txt")
- if (keywordFile.exists()) {
- //强制清空内容
- keywordFile.delete()
- }
- val binFile: File = File(getIvwDir() + "/keyword.bin")
- if (binFile.exists()) {
- binFile.delete()
- }
- var temp: String =weakupKey
- if (temp.isEmpty()) {
- temp = "你好小智"
- }
- val str = temp.replace(",", ",")
- val keywords = str.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
- if (!keywordFile.exists()) {
- keywordFile.createNewFile()
- }
- val writer = OutputStreamWriter(
- FileOutputStream(keywordFile),
- "UTF-8"
- )
- val bufferedWriter = BufferedWriter(writer)
- for (i in keywords.indices) {
- bufferedWriter.write(keywords[i])
- bufferedWriter.write(";")
- bufferedWriter.newLine() //写入换行
- }
- bufferedWriter.close()
- } catch (e: IOException) {
- e.printStackTrace()
- return false
- }
- return true
- }
fun initWakeAi(context: Context) {
@@ -342,6 +50,7 @@ object WakeManager {
file.mkdirs()
}
AiHelper.getInst().setLogInfo(LogLvl.VERBOSE, 1, "${workDir}aeeLog.txt")
+ AiHelper.getInst().setLogMode(2)
//设定初始化参数
val params = BaseLibrary.Params.builder()
@@ -377,7 +86,7 @@ object WakeManager {
val assets = Utils.getApp().assets.list(dir)?.toMutableList()
val tartgetFile = File(targetDir)
if(tartgetFile.isDirectory){
- tartgetFile.listFiles { dir, name ->
+ tartgetFile.listFiles { _, name ->
(assets?.contains(name)?:true).apply {
assets?.remove(name)
}
@@ -393,8 +102,4 @@ object WakeManager {
}
throw IllegalArgumentException("找不到 Application")
}
-
- fun getIvwDir(): String {
- return getWorkDir()+File.separator+"ivw"
- }
}
\ No newline at end of file
diff --git a/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/iflytek/WakeUpManager.kt b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/iflytek/WakeUpManager.kt
new file mode 100644
index 0000000000..f27833332d
--- /dev/null
+++ b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/iflytek/WakeUpManager.kt
@@ -0,0 +1,369 @@
+package com.mogo.mgintelligent.speech.iflytek
+
+import android.Manifest
+import android.annotation.SuppressLint
+import android.content.pm.PackageManager
+import android.media.AudioFormat
+import android.media.AudioRecord
+import android.media.MediaRecorder
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.util.Log
+import androidx.core.app.ActivityCompat
+import com.iflytek.aikit.core.AiAudio
+import com.iflytek.aikit.core.AiHandle
+import com.iflytek.aikit.core.AiHelper
+import com.iflytek.aikit.core.AiListener
+import com.iflytek.aikit.core.AiRequest
+import com.iflytek.aikit.core.AiResponse
+import com.iflytek.aikit.core.AiStatus
+import com.mogo.commons.AbsMogoApplication
+import com.mogo.eagle.core.function.call.setting.CallerRequestActivityHandleManager
+import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
+import com.mogo.eagle.core.utilcode.util.ThreadUtils
+import java.io.BufferedWriter
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.OutputStreamWriter
+import java.util.concurrent.atomic.AtomicBoolean
+import kotlin.math.abs
+import kotlin.math.log10
+
+object WakeUpManager {
+
+ private const val START: Int = 0x0001
+ private const val WRITE_BY_RECORDING: Int = 0x0002
+ private const val END: Int = 0x0004
+
+ //录音缓冲区大小
+ private const val BUFFER_SIZE = 1280
+
+ private const val ABILITYID: String = "e867a88f2"
+
+ private const val weakupKey = "你好小智,小智小智,小智你好"
+
+ private val isEnd = AtomicBoolean(true)
+
+ //录音是否在进行
+ private val isRecording = AtomicBoolean(false)
+
+ private var aiHandle: AiHandle? = null
+
+ private const val TAG = "WakeUpManager"
+
+ private var mHandler: Handler? = null
+
+ private var audioRecord: AudioRecord? = null
+
+ private val mThread = Thread {
+ Looper.prepare()
+ mHandler = object : Handler(Looper.myLooper()!!) {
+ override fun handleMessage(msg: Message) {
+ super.handleMessage(msg)
+ when (msg.what) {
+ START -> {
+ /**
+ * 开始会话
+ */
+ Log.d(TAG, "START")
+ val ret: Int = start()
+ if (ret == 0) {
+ val type = msg.arg1
+ if (type == WRITE_BY_RECORDING) {
+ createAudioRecord() //创建录音器
+ isRecording.set(true)
+ audioRecord?.startRecording() //录音器启动录音
+ }
+ mHandler?.removeCallbacksAndMessages(null) //清空消息队列
+ val writeMsg = Message()
+ writeMsg.what = type
+ writeMsg.obj = AiStatus.BEGIN
+ mHandler?.sendMessage(writeMsg) //调用write方法送音频数据给引擎
+ }
+ }
+
+ WRITE_BY_RECORDING -> {
+ /**
+ * 写入数据-录音方式
+ */
+ val status = msg.obj as AiStatus
+ val data = ByteArray(BUFFER_SIZE)
+ val read: Int = audioRecord?.read(data, 0, BUFFER_SIZE)?:AudioRecord.ERROR_INVALID_OPERATION
+ //平方和除以数据总长度,得到音量大小。
+ val volume: Int = calculateVolume(data)
+ //CallerLogger.d(TAG,"当前分贝:${ abs(volume.toDouble())}")
+ if (AudioRecord.ERROR_INVALID_OPERATION != read) {
+ //处理录音数据
+ write(data, status) //送尾帧
+ }
+ if (AiStatus.END == status) {
+ if (audioRecord != null) {
+ audioRecord?.stop()
+ isRecording.set(false)
+ mHandler?.sendEmptyMessage(END)
+ }
+ } else {
+ if (isRecording.get()) {
+ val writeMsg = Message()
+ writeMsg.what = WRITE_BY_RECORDING
+ writeMsg.obj = AiStatus.CONTINUE
+ mHandler?.sendMessage(writeMsg) //调用write方法送音频数据给引擎
+ }
+ }
+ }
+
+ END -> {
+ /**
+ * 结束会话
+ */
+ Log.d(TAG, "END")
+ end()
+ }
+ }
+ }
+ }
+ Looper.loop()
+ }
+
+ /**
+ * 能力监听回调
+ */
+ private val edListener: AiListener = object : AiListener {
+ override fun onResult(handleID: Int, outputData: List, usrContext: Any?) {
+ if (outputData.isNotEmpty()) {
+ for (i in outputData.indices) {
+ CallerLogger.d(TAG, "onResult:handleID:" + handleID + ":" + outputData[i].key)
+ val key = outputData[i].key //引擎结果的key
+ val bytes = outputData[i].value //识别结果
+ val result = String(bytes)
+ CallerLogger.d(TAG, "key=$key")
+ CallerLogger.d(TAG, "value=$result")
+ CallerLogger.d(TAG, "status=" + outputData[i].status)
+ if ((key == "func_wake_up" || key == "func_pre_wakeup")) {
+ CallerLogger.d(TAG,"$key: \n $result")
+ }
+ _wakeUpListener?.wakeupSuccess()
+ }
+ }
+ }
+
+ override fun onEvent(i: Int, i1: Int, list: List, o: Any) {
+ CallerLogger.i(TAG, "onEvent:$i,event:$i1")
+ }
+
+ override fun onError(i: Int, i1: Int, s: String, o: Any) {
+ CallerLogger.d(TAG,"错误通知,能力执行终止,Ability $i ERROR::$s,err code:$i1")
+ }
+ }
+
+ private var _wakeUpListener:WakeUpListener?=null
+
+ fun setWakeUpListener(wakeUpListener:WakeUpListener?){
+ this._wakeUpListener = wakeUpListener
+ }
+
+ fun startThread(){
+ CallerLogger.d(TAG,"startThread")
+ mThread.start()
+ }
+
+ fun startWakeUp() {
+ if (isRecording.get() || !isEnd.get()) {
+ return
+ }
+ CallerLogger.d(TAG,"startWakeUp")
+ ThreadUtils.getIoPool().submit {
+
+ // 检测权限
+ val granted = ActivityCompat.checkSelfPermission(
+ AbsMogoApplication.getApp(),
+ Manifest.permission.RECORD_AUDIO)
+ if(granted != PackageManager. PERMISSION_GRANTED){
+ CallerRequestActivityHandleManager.requestPermission(TAG, Manifest.permission.RECORD_AUDIO)
+ return@submit
+ }
+ AiHelper.getInst().registerListener(ABILITYID, edListener)
+ val msg = Message()
+ msg.what = START
+ msg.arg1 = WRITE_BY_RECORDING
+ mHandler!!.sendMessage(msg)
+ }
+ }
+
+ fun stopWakeup(){
+ CallerLogger.d(TAG,"stopWakeup")
+ if(!isEnd.get()) {
+ val msg = Message()
+ msg.what = WRITE_BY_RECORDING
+ msg.obj = AiStatus.END
+ mHandler!!.sendMessage(msg)
+ }
+ }
+
+ /**
+ * 写入数据
+ */
+ private fun write(part: ByteArray, status: AiStatus) {
+ if (isEnd.get()) {
+ return
+ }
+ val dataBuilder = AiRequest.builder()
+
+ /**
+ * 送入音频需要标识音频的状态,第一帧为起始帧,status要传AiStatus.BEGIN,最后一帧为结束帧,status要传AiStatus.END,其他为中间帧,status要传AiStatus.CONTINUE
+ * 音频要求16bit,16K,单声道的pcm音频。
+ * 建议每次发送音频间隔40ms,每次发送音频字节数为一帧音频大小的整数倍。
+ */
+ val aiAudio = AiAudio.get("wav").data(part).status(status).valid()
+ dataBuilder.payload(aiAudio)
+
+ val ret: Int = AiHelper.getInst().write(dataBuilder.build(), aiHandle)
+ if (ret != 0) {
+ CallerLogger.d(TAG,"write失败:$ret ")
+ }
+ }
+
+ /**
+ * 创建录音器
+ */
+ @SuppressLint("MissingPermission")
+ private fun createAudioRecord() {
+ if (isRecording.get()) {
+ return
+ }
+ if (audioRecord == null) {
+ Log.d(TAG, "createAudioRecord")
+ audioRecord = AudioRecord(
+ MediaRecorder.AudioSource.MIC,
+ 16000,
+ AudioFormat.CHANNEL_IN_MONO,
+ AudioFormat.ENCODING_PCM_16BIT,
+ BUFFER_SIZE
+ )
+ }
+ }
+
+ /**
+ * 根据录音数据计算音量
+ */
+ private fun calculateVolume(buffer: ByteArray): Int {
+ var sumVolume = 0.0
+ val volume: Int
+ var i = 0
+ while (i < buffer.size) {
+ val v1 = buffer[i].toInt() and 0xFF
+ val v2 = buffer[i + 1].toInt() and 0xFF
+ var temp = v1 + (v2 shl 8) // 小端
+ if (temp >= 0x8000) {
+ temp = 0xffff - temp
+ }
+ sumVolume += abs(temp.toDouble())
+ i += 2
+ }
+ val avgVolume: Double = sumVolume / buffer.size / 2
+ volume = (log10(1 + avgVolume) * 10).toInt()
+ return volume
+ }
+
+ /**
+ * 开始会话
+ */
+ private fun start(): Int {
+ if (!keyword2File()) {
+ CallerLogger.d(TAG,"唤醒词文件写入失败,请检查是否有读写权限。")
+ return -1
+ }
+ val customBuilder = AiRequest.builder()
+ customBuilder.customText("key_word", getIvwDir() + "/keyword.txt", 0)
+ var ret = AiHelper.getInst().loadData(ABILITYID, customBuilder.build())
+ if (ret != 0) {
+ CallerLogger.d(TAG,"open ivw loadData 失败:$ret")
+ return ret
+ }
+ CallerLogger.d(TAG,"open ivw loadData success:$ret")
+ val indexs = intArrayOf(0)
+ ret = AiHelper.getInst().specifyDataSet(ABILITYID, "key_word", indexs) //从缓存中把个性化资源设置到引擎中
+ if (ret != 0) {
+ CallerLogger.d(TAG,"open ivw specifyDataSet 失败:$ret")
+ return ret
+ }
+ CallerLogger.d(TAG,"open ivw specifyDataSet success:$ret")
+
+ val paramBuilder = AiRequest.builder()
+ paramBuilder.param("wdec_param_nCmThreshold", "0 0:800")
+ paramBuilder.param("gramLoad", true)
+ isEnd.set(false)
+ aiHandle = AiHelper.getInst().start(ABILITYID, paramBuilder.build(), null)
+ if (aiHandle?.code != 0) {
+ CallerLogger.d(TAG,"open ivw start失败:${aiHandle?.getCode()}")
+ return aiHandle?.code ?:0
+ }
+ return 0
+ }
+
+ /**
+ * 结束会话
+ */
+ private fun end() {
+ if (!isEnd.get()) {
+ val ret = AiHelper.getInst().end(aiHandle)
+ if (ret == 0) {
+ isEnd.set(true)
+ aiHandle = null
+ CallerLogger.d(TAG,"唤醒完成,end: $ret")
+ } else {
+ isEnd.set(false)
+ ThreadUtils.runOnUiThreadDelayed({
+ end()
+ CallerLogger.d(TAG,"关闭失败2s后重新广播唤醒完成,end: $ret")
+ },2_000)
+ CallerLogger.d(TAG,"唤醒完成,end: $ret")
+ }
+ }
+ }
+
+ private fun keyword2File(): Boolean {
+ try {
+ val keywordFile = File(getIvwDir() + "/keyword.txt")
+ if (keywordFile.exists()) {
+ //强制清空内容
+ keywordFile.delete()
+ }
+ val binFile = File(getIvwDir() + "/keyword.bin")
+ if (binFile.exists()) {
+ binFile.delete()
+ }
+ var temp: String = weakupKey
+ if (temp.isEmpty()) {
+ temp = "你好小智"
+ }
+ val str = temp.replace(",", ",")
+ val keywords = str.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+ if (!keywordFile.exists()) {
+ keywordFile.createNewFile()
+ }
+ val writer = OutputStreamWriter(
+ FileOutputStream(keywordFile),
+ "UTF-8"
+ )
+ val bufferedWriter = BufferedWriter(writer)
+ for (i in keywords.indices) {
+ bufferedWriter.write(keywords[i])
+ bufferedWriter.write(";")
+ bufferedWriter.newLine() //写入换行
+ }
+ bufferedWriter.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ return false
+ }
+ return true
+ }
+
+ private fun getIvwDir(): String {
+ return WakeManager.getWorkDir() +File.separator+"ivw"
+ }
+
+}
\ No newline at end of file