接入讯飞SDK及参数设置
简要描述
主要协助云上越秀App接入讯飞SDK,并且设置讯飞相应参数的功能代码示例;
主要内容
版本号:1.1140
包大小:2.7M(jar包+so文件大小)
引入方式:jar包+so库,本地引入
代码设置
1、10s内无声音自动中断
核心代码段为 SpeechConstant.VAD_BOS 和 SpeechConstant.VAD_EOS
//kotlin实现SpeechRecognizer.createRecognizer(mContext, mTtsInitListener).apply {// 清空参数setParameter(SpeechConstant.PARAMS, null)// 设置听写引擎setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD)// 设置返回结果格式setParameter(SpeechConstant.RESULT_TYPE, "json")// * 在线听写支持多种小语种设置。支持语言类型如下:// * <item>zh_cn</item> 中文 默认// * <item>en_us</item> 英文// * <item>ja_jp</item> 日语// * <item>ru-ru</item> 俄语// * <item>es_es</item> 西班牙语// * <item>fr_fr</item> 法语// * <item>ko_kr</item> 韩语setParameter(SpeechConstant.LANGUAGE, "zh_cn")// 设置语言区域setParameter(SpeechConstant.ACCENT, "mandarin")// 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理setParameter(SpeechConstant.VAD_BOS, "10000")// 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音setParameter(SpeechConstant.VAD_EOS, "10000")// 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点setParameter(SpeechConstant.ASR_PTT, "1")// 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限setParameter(SpeechConstant.AUDIO_FORMAT, "pcm")}
2、监听讯飞回调
实现RecognizerListener接口(kotlin)
fun startListening(filePath: (String) -> Unit,ready: () -> Unit,endSpeak: () -> Unit,error: (SpeechError?) -> Unit,result: (String?) -> Unit,finish: () -> Unit): Int {val value = object : RecognizerListener {override fun onVolumeChanged(volume: Int, data: ByteArray?) {// SparringLog.d(TAG,"当前正在说话,音量大小:$volume")// SparringLog.d(TAG,"返回音频数据:" + data?.size)}override fun onResult(results: RecognizerResult?, isLast: Boolean) {val resultString = results?.resultStringSparringLog.d(TAG, resultString.toString())result(resultString)if (isLast) {finish()}}override fun onBeginOfSpeech() {// 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入SparringLog.d(TAG, "开始说话")ready()}override fun onEvent(eventType: Int, arg1: Int, arg2: Int, obj: Bundle?) {// 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因// 若使用本地能力,会话id为null// if (SpeechEvent.EVENT_SESSION_ID == eventType) {// String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);// Log.d(TAG, "session id =" + sid);// }}override fun onEndOfSpeech() {// 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入SparringLog.d(TAG, "结束说话")endSpeak()}override fun onError(error: SpeechError?) {// TODO: 处理此权限的错误 立即停止回调的也是10118 没有权限也是10118 怎么区分开?// 错误码:10118(您没有说话),可能是录音机权限被禁,需要提示用户打开应用的录音权限。SparringLog.e(TAG, error?.toString())error(error)finish()}}return mIat.run {val tempPath =com.yxt.sparring.utils.FileUtil.sInstance.PATH_APP_MEDIA +File.separator + UUID.randomUUID().toString() + ".pcm"filePath(tempPath)setParameter(SpeechConstant.ASR_AUDIO_PATH, tempPath)startListening(value)}}
以下是我们封装的讯飞工具类,供参考
XunFeiUtil
import android.content.Contextimport android.os.Bundleimport android.os.Environmentimport android.os.MemoryFileimport com.iflytek.cloud.*import com.iflytek.cloud.msc.util.FileUtilimport com.yxt.sparring.utils.common.log.SparringLogimport java.io.Fileimport java.io.IOExceptionimport java.util.*class XunFeiUtil private constructor() {companion object {private var TAG = XunFeiUtil::class.java.simpleName@JvmStaticval instance: XunFeiUtil by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {XunFeiUtil()}}private lateinit var mContext: Contextfun init(context: Context) {mContext = contextSpeechUtility.createUtility(context, SpeechConstant.APPID + "=5ab0cdad")}fun startListening(filePath: (String) -> Unit,ready: () -> Unit,endSpeak: () -> Unit,error: (SpeechError?) -> Unit,result: (String?) -> Unit,finish: () -> Unit): Int {val value = object : RecognizerListener {override fun onVolumeChanged(volume: Int, data: ByteArray?) {// SparringLog.d(TAG,"当前正在说话,音量大小:$volume")// SparringLog.d(TAG,"返回音频数据:" + data?.size)}override fun onResult(results: RecognizerResult?, isLast: Boolean) {val resultString = results?.resultStringSparringLog.d(TAG, resultString.toString())result(resultString)if (isLast) {finish()}}override fun onBeginOfSpeech() {// 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入SparringLog.d(TAG, "开始说话")ready()}override fun onEvent(eventType: Int, arg1: Int, arg2: Int, obj: Bundle?) {// 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因// 若使用本地能力,会话id为null// if (SpeechEvent.EVENT_SESSION_ID == eventType) {// String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);// Log.d(TAG, "session id =" + sid);// }}override fun onEndOfSpeech() {// 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入SparringLog.d(TAG, "结束说话")endSpeak()}override fun onError(error: SpeechError?) {// TODO: 处理此权限的错误 立即停止回调的也是10118 没有权限也是10118 怎么区分开?// 错误码:10118(您没有说话),可能是录音机权限被禁,需要提示用户打开应用的录音权限。SparringLog.e(TAG, error?.toString())error(error)finish()}}return mIat.run {val tempPath =com.yxt.sparring.utils.FileUtil.sInstance.PATH_APP_MEDIA +File.separator + UUID.randomUUID().toString() + ".pcm"filePath(tempPath)setParameter(SpeechConstant.ASR_AUDIO_PATH, tempPath)startListening(value)}}// 语音合成fun startSpeaking(name: String?, content: String, needDelay: (Boolean) -> Unit) {mTts.apply {// 后台api没有返回speaker的时候是否返回性别问题;旁白声音问题;// 已确认2020.5.9(开会小组)-----后端不返回speaker时,App端不需要转语音,跟旁白一样处理if (name.isNullOrBlank()) {needDelay(true)} else {setParameter(SpeechConstant.VOICE_NAME, name)startSpeaking(content, object : SynthesizerListener {val container = Vector<ByteArray?>()override fun onBufferProgress(percent: Int, beginPos: Int, endPos: Int, info: String?) {// 合成进度SparringLog.d(TAG, "缓冲进度为$percent")}override fun onSpeakBegin() {SparringLog.d(TAG, "开始播放")}override fun onSpeakProgress(percent: Int, beginPos: Int, endPos: Int) {SparringLog.d(TAG, "播放进度为$percent")}override fun onEvent(eventType: Int, arg1: Int, arg12: Int, obj: Bundle?) {// 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因// 若使用本地能力,会话id为nullif (SpeechEvent.EVENT_SESSION_ID == eventType) {val sid: String? = obj?.getString(SpeechEvent.KEY_EVENT_SESSION_ID)SparringLog.d(TAG, "session id =$sid")}//当设置SpeechConstant.TTS_DATA_NOTIFY为1时,抛出buf数据if (SpeechEvent.EVENT_TTS_BUFFER == eventType) {val buf: ByteArray? = obj?.getByteArray(SpeechEvent.KEY_EVENT_TTS_BUFFER)SparringLog.d(TAG, "buf is =" + buf?.size)container.add(buf)}}override fun onSpeakPaused() {SparringLog.d(TAG, "暂停播放")}override fun onSpeakResumed() {SparringLog.d(TAG, "继续播放")}override fun onCompleted(error: SpeechError?) {if (error == null) {SparringLog.d(TAG, "播放完成," + container.size)needDelay.invoke(false)try {for (i in container.indices) {writeToFile(container[i])}} catch (e: IOException) {}FileUtil.saveFile(memFile,mTotalSize,Environment.getExternalStorageDirectory().toString() + "/real.pcm")} else {needDelay.invoke(true)SparringLog.e(TAG, error.toString())}}})}}}fun stopListening() {if (mIat.isListening) {mIat.stopListening()}}fun stopSpeaking() {if (mTts.isSpeaking) {mTts.stopSpeaking()}}fun pauseSpeaking() {mTts.pauseSpeaking()}fun resumeSpeaking() {mTts.resumeSpeaking()}@Volatileprivate var mTotalSize: Long = 0private val memFile: MemoryFile by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {val mFilepath = Environment.getExternalStorageDirectory().toString() + "/real.pcm"MemoryFile(mFilepath, 1920000)}@Throws(IOException::class)private fun writeToFile(data: ByteArray?) {if (data == null || data.isEmpty()) returntry {memFile.allowPurging(false)memFile.writeBytes(data, 0, mTotalSize.toInt(), data.size)mTotalSize += data.size.toLong()} finally {}}private val mTts: SpeechSynthesizer by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {SpeechSynthesizer.createSynthesizer(mContext, mTtsInitListener).apply {// 清空参数setParameter(SpeechConstant.PARAMS, null)// 根据合成引擎设置相应参数 目前只使用了cloudsetParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD)//支持实时音频返回,仅在synthesizeToUri条件下支持setParameter(SpeechConstant.TTS_DATA_NOTIFY, "0")// setParameter(SpeechConstant.TTS_BUFFER_TIME,"1");// 设置在线合成发音人// setParameter(SpeechConstant.VOICE_NAME, SPUtil.getString(SpeechConstant.VOICE_NAME, "xiaofeng"))//设置合成语速setParameter(SpeechConstant.SPEED, "50")//设置合成音调setParameter(SpeechConstant.PITCH, "50")//设置合成音量setParameter(SpeechConstant.VOLUME, "50")//设置播放器音频流类型 参见demo 通话/系统/铃声/*音乐*/闹铃/通知setParameter(SpeechConstant.STREAM_TYPE, "3")// 设置播放合成音频打断音乐播放,默认为true// setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "true")// 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限setParameter(SpeechConstant.AUDIO_FORMAT, "pcm")setParameter(SpeechConstant.TTS_AUDIO_PATH,Environment.getExternalStorageDirectory().toString() + "/msc/tts.pcm")}}private val mTtsInitListener: (Int) -> Unit = { code ->if (code != ErrorCode.SUCCESS) {SparringLog.e(TAG,"初始化失败,错误码:$code,请点击网址https://www.xfyun.cn/document/error-code查询解决方案")}}// 在线听写private val mIat: SpeechRecognizer by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {SpeechRecognizer.createRecognizer(mContext, mTtsInitListener).apply {// 清空参数setParameter(SpeechConstant.PARAMS, null)// 设置听写引擎setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD)// 设置返回结果格式setParameter(SpeechConstant.RESULT_TYPE, "json")// * 在线听写支持多种小语种设置。支持语言类型如下:// * <item>zh_cn</item> 中文 默认// * <item>en_us</item> 英文// * <item>ja_jp</item> 日语// * <item>ru-ru</item> 俄语// * <item>es_es</item> 西班牙语// * <item>fr_fr</item> 法语// * <item>ko_kr</item> 韩语setParameter(SpeechConstant.LANGUAGE, "zh_cn")// 设置语言区域setParameter(SpeechConstant.ACCENT, "mandarin")// 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理setParameter(SpeechConstant.VAD_BOS, "10000")// 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音setParameter(SpeechConstant.VAD_EOS, "10000")// 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点setParameter(SpeechConstant.ASR_PTT, "1")// 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限setParameter(SpeechConstant.AUDIO_FORMAT, "pcm")// setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory().toString() + "/pcm/2.pcm")}}fun stop() {stopListening()// 退出时释放连接mIat.cancel()mIat.destroy()stopSpeaking()mTts.destroy()}}
IatBean
data class IatBean(val bg: Int,val ed: Int,val ls: Boolean,val sn: Int,val ws: List<W>) {val plainText :String?get() {val buffer = StringBuffer()for (w in ws) {// 转写结果词,默认使用第一个结果// 如果需要多候选结果,解析数组其他字段// for(int j = 0; j < items.length(); j++)// {// JSONObject obj = items.getJSONObject(j);// ret.append(obj.getString("w"));// }buffer.append(w.cw[0].w)}return buffer.toString()}}data class W(val bg: Int,val cw: List<Cw>)data class Cw(val sc: Double,val w: String)