[8.0.0]
[fea] [唤醒 线程调整]
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.mogo.mgintelligent.speech" >
|
||||
|
||||
<uses-sdk tools:overrideLibrary="com.bun.miitmdid" />
|
||||
|
||||
</manifest>
|
||||
@@ -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<AiResponse>, 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<AiResponse>, 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"
|
||||
}
|
||||
}
|
||||
@@ -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<AiResponse>, 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<AiResponse>, 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"
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user