|
|
@ -34,9 +34,6 @@ class AzureAsrHelper(private val context: Context) { |
|
|
private val tag = "AzureAsrHelper" |
|
|
private val tag = "AzureAsrHelper" |
|
|
|
|
|
|
|
|
// 核心组件 |
|
|
// 核心组件 |
|
|
private var echoCanceler: AcousticEchoCanceler? = null |
|
|
|
|
|
private var noiseSuppressor: NoiseSuppressor? = null |
|
|
|
|
|
private var automaticGainControl: AutomaticGainControl? = null |
|
|
|
|
|
private var speechConfig: SpeechConfig? = null |
|
|
private var speechConfig: SpeechConfig? = null |
|
|
private var recognizer: SpeechRecognizer? = null |
|
|
private var recognizer: SpeechRecognizer? = null |
|
|
private var audioConfig: AudioConfig? = null |
|
|
private var audioConfig: AudioConfig? = null |
|
|
@ -55,7 +52,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
var audioSourceType = AudioSourceType.MICROPHONE |
|
|
var audioSourceType = AudioSourceType.MICROPHONE |
|
|
|
|
|
|
|
|
// 音频处理 |
|
|
// 音频处理 |
|
|
// private var microphoneStream: MicrophoneStream? = null |
|
|
private var audioStream: AudioStream? = null |
|
|
private var externalAudioStream: ExternalAudioPullStream? = null |
|
|
private var externalAudioStream: ExternalAudioPullStream? = null |
|
|
|
|
|
|
|
|
// 音频模式管理 |
|
|
// 音频模式管理 |
|
|
@ -64,11 +61,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
|
|
|
|
|
// 录音文件处理 |
|
|
// 录音文件处理 |
|
|
var recordfile: RecordFile? = null |
|
|
var recordfile: RecordFile? = null |
|
|
|
|
|
var isPaused = true |
|
|
private var isPaused = false |
|
|
|
|
|
private var audioRecord: AudioRecord? = null |
|
|
|
|
|
private var captureThread: Thread? = null |
|
|
|
|
|
private var audioStream: PushAudioInputStream? = null |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 音频来源类型 |
|
|
* 音频来源类型 |
|
|
@ -129,9 +122,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
|
|
|
|
|
// 释放之前的资源 |
|
|
// 释放之前的资源 |
|
|
dispose() |
|
|
dispose() |
|
|
// 1. 创建可控制的音频流 |
|
|
|
|
|
val format = getOptimalAudioFormat() |
|
|
|
|
|
audioStream = AudioInputStream.createPushStream(format) |
|
|
|
|
|
// 初始化音频管理器 |
|
|
// 初始化音频管理器 |
|
|
audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager |
|
|
audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager |
|
|
originalAudioMode = audioManager?.mode ?: AudioManager.MODE_NORMAL |
|
|
originalAudioMode = audioManager?.mode ?: AudioManager.MODE_NORMAL |
|
|
@ -197,6 +188,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
*/ |
|
|
*/ |
|
|
private fun setupRecognizer(): Boolean { |
|
|
private fun setupRecognizer(): Boolean { |
|
|
try { |
|
|
try { |
|
|
|
|
|
|
|
|
// 清理旧的识别器 |
|
|
// 清理旧的识别器 |
|
|
recognizer?.close() |
|
|
recognizer?.close() |
|
|
recognizer = null |
|
|
recognizer = null |
|
|
@ -211,7 +203,11 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
|
|
|
|
|
AudioSourceType.EXTERNAL -> { |
|
|
AudioSourceType.EXTERNAL -> { |
|
|
// 改用拉流方式处理外部音频 |
|
|
// 改用拉流方式处理外部音频 |
|
|
|
|
|
Log.d(tag, "改用拉流方式处理外部音频") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setupExternalAudioStream() |
|
|
setupExternalAudioStream() |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -237,13 +233,25 @@ class AzureAsrHelper(private val context: Context) { |
|
|
*/ |
|
|
*/ |
|
|
private fun setupMicrophoneStream() { |
|
|
private fun setupMicrophoneStream() { |
|
|
try { |
|
|
try { |
|
|
|
|
|
Log.d(tag, "设置麦克风流 - 使用拉流方式: }") |
|
|
|
|
|
restoreOriginalAudioState() |
|
|
|
|
|
if (audioStream == null) { |
|
|
|
|
|
// 创建外部音频拉流对象 |
|
|
|
|
|
audioStream = AudioStream() |
|
|
|
|
|
audioStream!!.initAudioRecord() |
|
|
|
|
|
} |
|
|
|
|
|
// else |
|
|
|
|
|
// { |
|
|
|
|
|
// audioStream.stopCaptureThread() |
|
|
|
|
|
// audioStream.startCaptureThread() |
|
|
|
|
|
// } |
|
|
// 创建麦克风流 |
|
|
// 创建麦克风流 |
|
|
// microphoneStream = MicrophoneStream() |
|
|
// microphoneStream = MicrophoneStream() |
|
|
|
|
|
|
|
|
// 创建音频配置 - 正确使用fromStreamInput方法,只传入回调 |
|
|
// 创建音频配置 - 正确使用fromStreamInput方法,只传入回调 |
|
|
// audioConfig = AudioConfig.fromStreamInput(microphoneStream) |
|
|
// audioConfig = AudioConfig.fromStreamInput(microphoneStream) |
|
|
audioConfig = AudioConfig.fromStreamInput(audioStream) |
|
|
audioConfig = AudioConfig.fromStreamInput(audioStream!!.pushAudioStream) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
Log.e(tag, "设置麦克风流失败: ${e.message}") |
|
|
Log.e(tag, "设置麦克风流失败: ${e.message}") |
|
|
@ -278,6 +286,8 @@ class AzureAsrHelper(private val context: Context) { |
|
|
*/ |
|
|
*/ |
|
|
private fun setupExternalAudioStream() { |
|
|
private fun setupExternalAudioStream() { |
|
|
try { |
|
|
try { |
|
|
|
|
|
Log.d(tag, "设置外部音频流 - 使用拉流方式: }") |
|
|
|
|
|
//disableBluetoothAudio() |
|
|
// 创建外部音频拉流对象 |
|
|
// 创建外部音频拉流对象 |
|
|
externalAudioStream = ExternalAudioPullStream() |
|
|
externalAudioStream = ExternalAudioPullStream() |
|
|
|
|
|
|
|
|
@ -299,7 +309,6 @@ class AzureAsrHelper(private val context: Context) { |
|
|
if (audioSourceType != AudioSourceType.EXTERNAL) { |
|
|
if (audioSourceType != AudioSourceType.EXTERNAL) { |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 使用拉流模式,将数据推入队列 |
|
|
// 使用拉流模式,将数据推入队列 |
|
|
externalAudioStream?.pushAudio(data) |
|
|
externalAudioStream?.pushAudio(data) |
|
|
|
|
|
|
|
|
@ -379,8 +388,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
// 5. 初始化麦克风 |
|
|
|
|
|
initAudioRecord() |
|
|
|
|
|
// 设置各种事件监听 |
|
|
// 设置各种事件监听 |
|
|
setupEventListeners(callback) |
|
|
setupEventListeners(callback) |
|
|
|
|
|
|
|
|
@ -389,9 +397,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
|
|
|
|
|
// 开始连续识别 |
|
|
// 开始连续识别 |
|
|
recognizer?.startContinuousRecognitionAsync() |
|
|
recognizer?.startContinuousRecognitionAsync() |
|
|
// 开始录音 |
|
|
audioStream?.startCaptureThread() |
|
|
audioRecord?.startRecording() |
|
|
|
|
|
startCaptureThread() // 再启动数据读取线程 |
|
|
|
|
|
isContinuousRecognitionActive = true |
|
|
isContinuousRecognitionActive = true |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -442,6 +448,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
recognizer?.sessionStarted?.addEventListener( |
|
|
recognizer?.sessionStarted?.addEventListener( |
|
|
EventHandler<SessionEventArgs> { _, _ -> |
|
|
EventHandler<SessionEventArgs> { _, _ -> |
|
|
// 直接在当前线程调用回调 |
|
|
// 直接在当前线程调用回调 |
|
|
|
|
|
Log.d(tag, "会话开始事件") |
|
|
callback.onSessionStarted() |
|
|
callback.onSessionStarted() |
|
|
} |
|
|
} |
|
|
) |
|
|
) |
|
|
@ -450,6 +457,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
recognizer?.sessionStopped?.addEventListener( |
|
|
recognizer?.sessionStopped?.addEventListener( |
|
|
EventHandler<SessionEventArgs> { _, _ -> |
|
|
EventHandler<SessionEventArgs> { _, _ -> |
|
|
// 直接在当前线程调用回调 |
|
|
// 直接在当前线程调用回调 |
|
|
|
|
|
Log.d(tag, "会话结束事件") |
|
|
callback.onSessionStopped() |
|
|
callback.onSessionStopped() |
|
|
isContinuousRecognitionActive = false |
|
|
isContinuousRecognitionActive = false |
|
|
stopAudioProcessing() |
|
|
stopAudioProcessing() |
|
|
@ -497,8 +505,6 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
|
|
|
|
|
// 停止音频处理 |
|
|
// 停止音频处理 |
|
|
stopAudioProcessing() |
|
|
stopAudioProcessing() |
|
|
// 停止录音 |
|
|
|
|
|
stopMicrophoneCapture() |
|
|
|
|
|
// 会话结束事件会设置isContinuousRecognitionActive = false |
|
|
// 会话结束事件会设置isContinuousRecognitionActive = false |
|
|
return true |
|
|
return true |
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
@ -508,8 +514,6 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
|
|
|
|
|
// 停止音频处理 |
|
|
// 停止音频处理 |
|
|
stopAudioProcessing() |
|
|
stopAudioProcessing() |
|
|
// 停止录音 |
|
|
|
|
|
stopMicrophoneCapture() |
|
|
|
|
|
// 尝试强制关闭识别器 |
|
|
// 尝试强制关闭识别器 |
|
|
try { |
|
|
try { |
|
|
recognizer?.close() |
|
|
recognizer?.close() |
|
|
@ -538,7 +542,6 @@ class AzureAsrHelper(private val context: Context) { |
|
|
recognizer?.stopContinuousRecognitionAsync() |
|
|
recognizer?.stopContinuousRecognitionAsync() |
|
|
isContinuousRecognitionActive = false |
|
|
isContinuousRecognitionActive = false |
|
|
} |
|
|
} |
|
|
releaseAudioResources() |
|
|
|
|
|
|
|
|
|
|
|
// 停止音频处理 |
|
|
// 停止音频处理 |
|
|
stopAudioProcessing() |
|
|
stopAudioProcessing() |
|
|
@ -552,9 +555,6 @@ class AzureAsrHelper(private val context: Context) { |
|
|
speechConfig?.close() |
|
|
speechConfig?.close() |
|
|
speechConfig = null |
|
|
speechConfig = null |
|
|
|
|
|
|
|
|
// 关闭音频流(根据实际实现可能需要) |
|
|
|
|
|
audioStream?.close() |
|
|
|
|
|
audioStream = null |
|
|
|
|
|
|
|
|
|
|
|
// 释放音频配置 |
|
|
// 释放音频配置 |
|
|
audioConfig?.close() |
|
|
audioConfig?.close() |
|
|
@ -601,16 +601,19 @@ class AzureAsrHelper(private val context: Context) { |
|
|
* 停止音频处理 |
|
|
* 停止音频处理 |
|
|
*/ |
|
|
*/ |
|
|
private fun stopAudioProcessing() { |
|
|
private fun stopAudioProcessing() { |
|
|
// microphoneStream?.let { |
|
|
audioStream?.let { |
|
|
// try { |
|
|
try { |
|
|
// it.close() |
|
|
it.stopMicrophoneCapture() |
|
|
// microphoneStream = null |
|
|
it.releaseAudioResources() |
|
|
// } catch (e: Exception) { |
|
|
|
|
|
// Log.e(tag, "关闭麦克风流失败: ${e.message}") |
|
|
|
|
|
// e.printStackTrace() |
|
|
|
|
|
// } |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it.pushAudioStream?.close() |
|
|
|
|
|
|
|
|
|
|
|
audioStream = null |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e(tag, "关闭麦克风流失败: ${e.message}") |
|
|
|
|
|
e.printStackTrace() |
|
|
|
|
|
} |
|
|
|
|
|
} // 关闭音频流(根据实际实现可能需要) |
|
|
externalAudioStream?.let { |
|
|
externalAudioStream?.let { |
|
|
try { |
|
|
try { |
|
|
it.close() |
|
|
it.close() |
|
|
@ -622,70 +625,22 @@ class AzureAsrHelper(private val context: Context) { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 开启录音 |
|
|
|
|
|
*/ |
|
|
|
|
|
fun enableRecord(filePath: String) { |
|
|
|
|
|
Log.i(tag, "开启录音:") |
|
|
|
|
|
|
|
|
|
|
|
recordfile!!.closeFile(true) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
recordfile!!.creatingFiles(filePath) |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 移动文件到新路径 |
|
|
|
|
|
*/ |
|
|
|
|
|
fun moveFile(sourcePath: String, destPath: String): Boolean { |
|
|
|
|
|
Log.i(tag, "移动文件到新路径:") |
|
|
|
|
|
|
|
|
|
|
|
recordfile!!.moveFile(sourcePath, destPath) |
|
|
|
|
|
return true |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 重命名指定路径的音频文件 |
|
|
|
|
|
*/ |
|
|
|
|
|
fun renameFile(filePath: String, newName: String): Boolean { |
|
|
|
|
|
Log.i(tag, "重命名指定路径的音频文件:") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
recordfile!!.renameFile(filePath, newName) |
|
|
|
|
|
return true |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 停止录音 |
|
|
|
|
|
*/ |
|
|
|
|
|
fun pauseRecord() { |
|
|
|
|
|
Log.i(tag, "停止连续录音:") |
|
|
|
|
|
|
|
|
|
|
|
recordfile!!.isPause = true |
|
|
/* |
|
|
} |
|
|
暂停音频输入 |
|
|
|
|
|
*/ |
|
|
|
|
|
fun audioPause() { |
|
|
/** |
|
|
|
|
|
* 关闭录音 |
|
|
|
|
|
*/ |
|
|
|
|
|
fun stopRecord(isSave: Boolean) { |
|
|
|
|
|
Log.i(tag, "关闭录音:") |
|
|
|
|
|
|
|
|
|
|
|
recordfile!!.isPause = false |
|
|
|
|
|
recordfile!!.closeFile(isSave) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun stopAudioPause() { |
|
|
|
|
|
Log.d(tag, "关闭麦克风stopAudioPause: ") |
|
|
Log.d(tag, "关闭麦克风stopAudioPause: ") |
|
|
// 清理旧的识别器 |
|
|
// 清理旧的识别器 |
|
|
isPaused = true |
|
|
isPaused = true |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fun setAudiocontinue() { |
|
|
/* |
|
|
|
|
|
继续音频输入 |
|
|
|
|
|
*/ |
|
|
|
|
|
fun audioContinue() { |
|
|
Log.d(tag, "继续麦克风stopAudioPause: ") |
|
|
Log.d(tag, "继续麦克风stopAudioPause: ") |
|
|
|
|
|
|
|
|
// 开始连续识别 |
|
|
// 开始连续识别 |
|
|
@ -727,403 +682,289 @@ class AzureAsrHelper(private val context: Context) { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//录音音频文件 |
|
|
/** |
|
|
/** |
|
|
* 开始麦克风捕获 |
|
|
* 开启录音 |
|
|
*/ |
|
|
*/ |
|
|
private fun initAudioRecord() { |
|
|
fun enableRecord(filePath: String) { |
|
|
// 获取已确定的采样率 |
|
|
Log.i(tag, "开启录音:") |
|
|
val format = getOptimalAudioFormat() |
|
|
|
|
|
val supportedSampleRates = intArrayOf(16000, 8000, 11025, 22050, 44100) |
|
|
|
|
|
val channelConfig = AudioFormat.CHANNEL_IN_MONO // 单声道输入 |
|
|
|
|
|
val audioFormat = AudioFormat.ENCODING_PCM_16BIT // 16位PCM格式 |
|
|
|
|
|
// 查找设备支持的最佳采样率 |
|
|
|
|
|
val sampleRate = supportedSampleRates.firstOrNull { rate -> |
|
|
|
|
|
val bufferSize = AudioRecord.getMinBufferSize( |
|
|
|
|
|
rate, |
|
|
|
|
|
channelConfig, |
|
|
|
|
|
audioFormat |
|
|
|
|
|
) |
|
|
|
|
|
bufferSize > 0 // 返回正值表示支持 |
|
|
|
|
|
} ?: 16000 // 默认回退值 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算最小缓冲区大小(避免出现音频欠载) |
|
|
|
|
|
val minBufferSize = AudioRecord.getMinBufferSize( |
|
|
|
|
|
sampleRate, |
|
|
|
|
|
channelConfig, |
|
|
|
|
|
audioFormat |
|
|
|
|
|
) |
|
|
|
|
|
// 创建录音对象 |
|
|
|
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { |
|
|
|
|
|
Log.d("AzureASR", "android.os.Build.VERSION.SDK_INT") |
|
|
|
|
|
val format = AudioFormat.Builder() |
|
|
|
|
|
.setSampleRate(sampleRate) |
|
|
|
|
|
.setEncoding(audioFormat) |
|
|
|
|
|
.setChannelMask(channelConfig) |
|
|
|
|
|
.build() |
|
|
|
|
|
|
|
|
|
|
|
audioRecord = AudioRecord.Builder() |
|
|
|
|
|
.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION) |
|
|
|
|
|
.setAudioFormat(format) |
|
|
|
|
|
.setBufferSizeInBytes(minBufferSize * 2) |
|
|
|
|
|
.build() |
|
|
|
|
|
} else { |
|
|
|
|
|
// 创建 AudioRecord 对象 |
|
|
|
|
|
audioRecord = AudioRecord( |
|
|
|
|
|
MediaRecorder.AudioSource.VOICE_RECOGNITION, // 语音识别专用音源(自带降噪/回声消除) |
|
|
|
|
|
sampleRate, |
|
|
|
|
|
channelConfig, |
|
|
|
|
|
audioFormat, |
|
|
|
|
|
minBufferSize * 2 // 使用两倍缓冲区防止溢出 |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
recordfile!!.closeFile(true) |
|
|
|
|
|
|
|
|
// 检查初始化状态 |
|
|
|
|
|
if (audioRecord?.state != AudioRecord.STATE_INITIALIZED) { |
|
|
|
|
|
throw IllegalStateException("AudioRecord初始化失败") |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
recordfile!!.creatingFiles(filePath) |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 3. 单独封装线程启动逻辑 |
|
|
/** |
|
|
private fun startCaptureThread() { |
|
|
* 移动文件到新路径 |
|
|
captureThread = Thread { |
|
|
*/ |
|
|
// 根据实际采样率计算缓冲区大小 |
|
|
fun moveFile(sourcePath: String, destPath: String): Boolean { |
|
|
val supportedSampleRates = intArrayOf(16000, 8000, 11025, 22050, 44100) |
|
|
Log.i(tag, "移动文件到新路径:") |
|
|
var bufferSize = 0 |
|
|
|
|
|
// 查找设备支持的最佳采样率 |
|
|
|
|
|
val sampleRate = supportedSampleRates.firstOrNull { rate -> |
|
|
|
|
|
bufferSize = AudioRecord.getMinBufferSize( |
|
|
|
|
|
rate, |
|
|
|
|
|
AudioFormat.CHANNEL_IN_MONO, |
|
|
|
|
|
AudioFormat.ENCODING_PCM_16BIT |
|
|
|
|
|
) |
|
|
|
|
|
bufferSize > 0 // 返回正值表示支持 |
|
|
|
|
|
} ?: 16000 // 默认回退值 |
|
|
|
|
|
|
|
|
|
|
|
val buffer = ByteArray(bufferSize) |
|
|
recordfile!!.moveFile(sourcePath, destPath) |
|
|
|
|
|
return true |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
while (!Thread.interrupted() && audioRecord?.recordingState == AudioRecord.RECORDSTATE_RECORDING) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 重命名指定路径的音频文件 |
|
|
|
|
|
*/ |
|
|
|
|
|
fun renameFile(filePath: String, newName: String): Boolean { |
|
|
|
|
|
Log.i(tag, "重命名指定路径的音频文件:") |
|
|
|
|
|
|
|
|
if (!isPaused) { |
|
|
|
|
|
// 读取音频 |
|
|
|
|
|
val bytesRead = audioRecord?.read(buffer, 0, buffer.size) ?: 0 |
|
|
|
|
|
if (bytesRead > 0) { |
|
|
|
|
|
// 写入Azure流 |
|
|
|
|
|
audioStream?.write(buffer) |
|
|
|
|
|
|
|
|
|
|
|
// 保存录音 |
|
|
recordfile!!.renameFile(filePath, newName) |
|
|
recordfile?.saveAudioDataToWav(buffer) |
|
|
return true |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
}.apply { start() } |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 停止麦克风捕获并释放所有相关资源 |
|
|
* 停止录音 |
|
|
*/ |
|
|
*/ |
|
|
private fun stopMicrophoneCapture() { |
|
|
fun pauseRecord() { |
|
|
try { |
|
|
Log.i(tag, "停止连续录音:") |
|
|
// 中断并等待捕获线程结束 |
|
|
|
|
|
captureThread?.interrupt() |
|
|
|
|
|
captureThread?.join(300) // 最多等待300ms |
|
|
|
|
|
|
|
|
|
|
|
// 停止录音并释放AudioRecord资源 |
|
|
recordfile!!.isPause = true |
|
|
audioRecord?.stop() // 必须先停止再释放 |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (e: Exception) { |
|
|
/** |
|
|
Log.e("AudioConfig", "Error releasing resources: ${e.message}") |
|
|
* 关闭录音 |
|
|
} finally { |
|
|
*/ |
|
|
// 确保恢复原始音频状态 |
|
|
fun stopRecord(isSave: Boolean) { |
|
|
restoreOriginalAudioState() |
|
|
Log.i(tag, "关闭录音:") |
|
|
} |
|
|
|
|
|
|
|
|
recordfile!!.isPause = false |
|
|
|
|
|
recordfile!!.closeFile(isSave) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 应用音频效果 - 总是启用所有音频增强功能 |
|
|
* 麦克风流 - 拉流模式 |
|
|
|
|
|
* 实现PullAudioInputStreamCallback,为Azure SDK提供音频数据 |
|
|
*/ |
|
|
*/ |
|
|
private fun applyAudioEffects() { |
|
|
inner class AudioStream { |
|
|
val sessionId = audioRecord?.audioSessionId ?: return |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
private var echoCanceler: AcousticEchoCanceler? = null |
|
|
// 设置音频模式为通信模式 |
|
|
private var noiseSuppressor: NoiseSuppressor? = null |
|
|
audioManager?.mode = AudioManager.MODE_IN_COMMUNICATION |
|
|
private var automaticGainControl: AutomaticGainControl? = null |
|
|
|
|
|
|
|
|
// 启用声学回音消除 |
|
|
|
|
|
if (AcousticEchoCanceler.isAvailable()) { |
|
|
|
|
|
echoCanceler = AcousticEchoCanceler.create(sessionId) |
|
|
|
|
|
echoCanceler?.enabled = true |
|
|
|
|
|
Log.i(tag, "声学回音消除已启用") |
|
|
|
|
|
} else { |
|
|
|
|
|
Log.w(tag, "设备不支持声学回音消除") |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 启用噪声抑制 |
|
|
var audioRecord: AudioRecord? = null |
|
|
if (NoiseSuppressor.isAvailable()) { |
|
|
private var captureThread: Thread? = null |
|
|
noiseSuppressor = NoiseSuppressor.create(sessionId) |
|
|
var pushAudioStream: PushAudioInputStream? = null |
|
|
noiseSuppressor?.enabled = true |
|
|
|
|
|
Log.i(tag, "噪声抑制已启用") |
|
|
|
|
|
|
|
|
// 音频配置 |
|
|
|
|
|
|
|
|
|
|
|
private val channelConfig = AudioFormat.CHANNEL_IN_MONO |
|
|
|
|
|
private val audioFormat = AudioFormat.ENCODING_PCM_16BIT |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 开始麦克风捕获 |
|
|
|
|
|
*/ |
|
|
|
|
|
fun initAudioRecord() { |
|
|
|
|
|
|
|
|
|
|
|
val format = getOptimalAudioFormat() |
|
|
|
|
|
pushAudioStream = AudioInputStream.createPushStream(format) |
|
|
|
|
|
val supportedSampleRates = intArrayOf(16000, 8000, 11025, 22050, 44100) |
|
|
|
|
|
// 查找设备支持的最佳采样率 |
|
|
|
|
|
val sampleRate = supportedSampleRates.firstOrNull { rate -> |
|
|
|
|
|
val bufferSize = AudioRecord.getMinBufferSize( |
|
|
|
|
|
rate, |
|
|
|
|
|
channelConfig, |
|
|
|
|
|
audioFormat |
|
|
|
|
|
) |
|
|
|
|
|
bufferSize > 0 // 返回正值表示支持 |
|
|
|
|
|
} ?: 16000 // 默认回退值 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算最小缓冲区大小(避免出现音频欠载) |
|
|
|
|
|
val minBufferSize = AudioRecord.getMinBufferSize( |
|
|
|
|
|
sampleRate, |
|
|
|
|
|
channelConfig, |
|
|
|
|
|
audioFormat |
|
|
|
|
|
) |
|
|
|
|
|
// 创建录音对象 |
|
|
|
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { |
|
|
|
|
|
Log.d("AzureASR", "android.os.Build.VERSION.SDK_INT") |
|
|
|
|
|
val format = AudioFormat.Builder() |
|
|
|
|
|
.setSampleRate(sampleRate) |
|
|
|
|
|
.setEncoding(audioFormat) |
|
|
|
|
|
.setChannelMask(channelConfig) |
|
|
|
|
|
.build() |
|
|
|
|
|
|
|
|
|
|
|
audioRecord = AudioRecord.Builder() |
|
|
|
|
|
.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION) |
|
|
|
|
|
.setAudioFormat(format) |
|
|
|
|
|
.setBufferSizeInBytes(minBufferSize * 2) |
|
|
|
|
|
.build() |
|
|
} else { |
|
|
} else { |
|
|
Log.w(tag, "设备不支持噪声抑制") |
|
|
// 创建 AudioRecord 对象 |
|
|
|
|
|
audioRecord = AudioRecord( |
|
|
|
|
|
MediaRecorder.AudioSource.VOICE_RECOGNITION, // 语音识别专用音源(自带降噪/回声消除) |
|
|
|
|
|
sampleRate, |
|
|
|
|
|
channelConfig, |
|
|
|
|
|
audioFormat, |
|
|
|
|
|
minBufferSize * 2 // 使用两倍缓冲区防止溢出 |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 启用自动增益控制 |
|
|
|
|
|
if (AutomaticGainControl.isAvailable()) { |
|
|
// 检查初始化状态 |
|
|
automaticGainControl = AutomaticGainControl.create(sessionId) |
|
|
if (audioRecord?.state != AudioRecord.STATE_INITIALIZED) { |
|
|
automaticGainControl?.enabled = true |
|
|
throw IllegalStateException("AudioRecord初始化失败") |
|
|
Log.i(tag, "自动增益控制已启用") |
|
|
|
|
|
} else { |
|
|
|
|
|
Log.w(tag, "设备不支持自动增益控制") |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Log.i(tag, "回音消除和音频增强功能已全部启用") |
|
|
startCaptureThread() // 再启动数据读取线程 |
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e(tag, "设置音频效果失败: ${e.message}") |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
// 3. 单独封装线程启动逻辑 |
|
|
* 释放音频资源 |
|
|
fun startCaptureThread() { |
|
|
*/ |
|
|
try { |
|
|
private fun releaseAudioResources() { |
|
|
// 确保之前的线程已停止 |
|
|
try { |
|
|
stopCaptureThread() |
|
|
// 停止录音 |
|
|
|
|
|
if (audioRecord?.recordingState == AudioRecord.RECORDSTATE_RECORDING) { |
|
|
// 开始录音 |
|
|
audioRecord?.stop() |
|
|
audioRecord?.startRecording() |
|
|
|
|
|
|
|
|
|
|
|
captureThread = Thread({ |
|
|
|
|
|
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO) |
|
|
|
|
|
|
|
|
|
|
|
val supportedSampleRates = intArrayOf(16000, 8000, 11025, 22050, 44100) |
|
|
|
|
|
var bufferSize = 0 |
|
|
|
|
|
val sampleRate = supportedSampleRates.firstOrNull { rate -> |
|
|
|
|
|
bufferSize = AudioRecord.getMinBufferSize( |
|
|
|
|
|
rate, |
|
|
|
|
|
AudioFormat.CHANNEL_IN_MONO, |
|
|
|
|
|
AudioFormat.ENCODING_PCM_16BIT |
|
|
|
|
|
) |
|
|
|
|
|
bufferSize > 0 |
|
|
|
|
|
} ?: 16000 |
|
|
|
|
|
|
|
|
|
|
|
val buffer = ByteArray(bufferSize) |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
while (!Thread.currentThread().isInterrupted && |
|
|
|
|
|
audioRecord?.recordingState == AudioRecord.RECORDSTATE_RECORDING) { |
|
|
|
|
|
|
|
|
|
|
|
if (!isPaused) { |
|
|
|
|
|
val bytesRead = audioRecord?.read(buffer, 0, buffer.size) ?: 0 |
|
|
|
|
|
if (bytesRead > 0) { |
|
|
|
|
|
// 安全地写入Azure流 |
|
|
|
|
|
try { |
|
|
|
|
|
pushAudioStream?.write(buffer) |
|
|
|
|
|
recordfile?.saveAudioDataToWav(buffer) |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.w(tag, "写入音频流失败: ${e.message}") |
|
|
|
|
|
} |
|
|
|
|
|
} else if (bytesRead < 0) { |
|
|
|
|
|
Log.w(tag, "AudioRecord读取错误: $bytesRead") |
|
|
|
|
|
break |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
Thread.sleep(10) // 暂停时短暂休眠 |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e: InterruptedException) { |
|
|
|
|
|
Log.d(tag, "音频捕获线程被中断") |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e(tag, "音频捕获异常: ${e.message}", e) |
|
|
|
|
|
} |
|
|
|
|
|
}, "AudioCaptureThread") |
|
|
|
|
|
|
|
|
|
|
|
captureThread?.start() |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e(tag, "启动音频捕获线程失败: ${e.message}", e) |
|
|
|
|
|
throw e |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 释放回音消除器 |
|
|
private fun stopCaptureThread() { |
|
|
echoCanceler?.let { |
|
|
captureThread?.let { thread -> |
|
|
it.enabled = false |
|
|
if (thread.isAlive) { |
|
|
it.release() |
|
|
thread.interrupt() |
|
|
echoCanceler = null |
|
|
try { |
|
|
|
|
|
thread.join(500) // 等待最多500ms |
|
|
|
|
|
} catch (e: InterruptedException) { |
|
|
|
|
|
Log.w(tag, "等待音频线程结束被中断") |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
captureThread = null |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 停止麦克风捕获并释放所有相关资源 |
|
|
|
|
|
*/ |
|
|
|
|
|
fun stopMicrophoneCapture() { |
|
|
|
|
|
try { |
|
|
|
|
|
if (captureThread != null) { |
|
|
|
|
|
// 中断并等待捕获线程结束 |
|
|
|
|
|
captureThread?.interrupt() |
|
|
|
|
|
captureThread?.join(300) // 最多等待300ms |
|
|
|
|
|
|
|
|
|
|
|
// 停止录音并释放AudioRecord资源 |
|
|
|
|
|
audioRecord?.stop() // 必须先停止再释放 |
|
|
|
|
|
isPaused= true |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 释放噪声抑制器 |
|
|
|
|
|
noiseSuppressor?.let { |
|
|
|
|
|
it.enabled = false |
|
|
|
|
|
it.release() |
|
|
|
|
|
noiseSuppressor = null |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 释放自动增益控制 |
|
|
} catch (e: Exception) { |
|
|
automaticGainControl?.let { |
|
|
Log.e("AudioConfig", "Error releasing resources: ${e.message}") |
|
|
it.enabled = false |
|
|
} finally { |
|
|
it.release() |
|
|
// 确保恢复原始音频状态 |
|
|
automaticGainControl = null |
|
|
// restoreOriginalAudioState() |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 恢复音频模式 |
|
|
|
|
|
this@AzureAsrHelper.audioManager?.mode = this@AzureAsrHelper.originalAudioMode |
|
|
/** |
|
|
|
|
|
* 释放音频资源 |
|
|
|
|
|
*/ |
|
|
|
|
|
fun releaseAudioResources() { |
|
|
|
|
|
try { |
|
|
|
|
|
isPaused= true |
|
|
|
|
|
// 停止录音 |
|
|
|
|
|
if (audioRecord?.recordingState == AudioRecord.RECORDSTATE_RECORDING) { |
|
|
|
|
|
audioRecord?.stop() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 释放录音实例 |
|
|
// // 释放回音消除器 |
|
|
audioRecord?.release() |
|
|
// echoCanceler?.let { |
|
|
audioRecord = null |
|
|
// it.enabled = false |
|
|
} catch (e: Exception) { |
|
|
// it.release() |
|
|
Log.e(tag, "释放音频资源失败: ${e.message}") |
|
|
// echoCanceler = null |
|
|
e.printStackTrace() |
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// // 释放噪声抑制器 |
|
|
|
|
|
// noiseSuppressor?.let { |
|
|
|
|
|
// it.enabled = false |
|
|
|
|
|
// it.release() |
|
|
|
|
|
// noiseSuppressor = null |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// // 释放自动增益控制 |
|
|
|
|
|
// automaticGainControl?.let { |
|
|
|
|
|
// it.enabled = false |
|
|
|
|
|
// it.release() |
|
|
|
|
|
// automaticGainControl = null |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// 恢复音频模式 |
|
|
|
|
|
this@AzureAsrHelper.audioManager?.mode = this@AzureAsrHelper.originalAudioMode |
|
|
|
|
|
|
|
|
|
|
|
// 释放录音实例 |
|
|
|
|
|
audioRecord?.release() |
|
|
|
|
|
audioRecord = null |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e(tag, "释放音频资源失败: ${e.message}") |
|
|
|
|
|
e.printStackTrace() |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// /** |
|
|
} |
|
|
// * 麦克风流 - 拉流模式 |
|
|
|
|
|
// * 实现PullAudioInputStreamCallback,为Azure SDK提供音频数据 |
|
|
|
|
|
// */ |
|
|
|
|
|
// private inner class MicrophoneStream : PullAudioInputStreamCallback() { |
|
|
|
|
|
// private var audioRecord: AudioRecord? = null |
|
|
|
|
|
// private var echoCanceler: AcousticEchoCanceler? = null |
|
|
|
|
|
// private var noiseSuppressor: NoiseSuppressor? = null |
|
|
|
|
|
// private var automaticGainControl: AutomaticGainControl? = null |
|
|
|
|
|
|
|
|
|
|
|
// // 音频配置 |
|
|
|
|
|
// private val sampleRate = 16000 |
|
|
|
|
|
// private val channelConfig = AudioFormat.CHANNEL_IN_MONO |
|
|
|
|
|
// private val audioFormat = AudioFormat.ENCODING_PCM_16BIT |
|
|
|
|
|
|
|
|
|
|
|
// // 优化:减少缓冲区大小以降低延迟 |
|
|
|
|
|
// private val bufferSize = AudioRecord.getMinBufferSize( |
|
|
|
|
|
// sampleRate, channelConfig, audioFormat |
|
|
|
|
|
// ).let { if (it < 0) 2048 else it } |
|
|
|
|
|
|
|
|
|
|
|
// init { |
|
|
|
|
|
// initMicrophone() |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// /** |
|
|
|
|
|
// * 初始化麦克风 |
|
|
|
|
|
// */ |
|
|
|
|
|
// private fun initMicrophone() { |
|
|
|
|
|
// try { |
|
|
|
|
|
// // 创建录音对象 |
|
|
|
|
|
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { |
|
|
|
|
|
// val format = AudioFormat.Builder() |
|
|
|
|
|
// .setSampleRate(sampleRate) |
|
|
|
|
|
// .setEncoding(audioFormat) |
|
|
|
|
|
// .setChannelMask(channelConfig) |
|
|
|
|
|
// .build() |
|
|
|
|
|
|
|
|
|
|
|
// audioRecord = AudioRecord.Builder() |
|
|
|
|
|
// .setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION) |
|
|
|
|
|
// .setAudioFormat(format) |
|
|
|
|
|
// .setBufferSizeInBytes(bufferSize) |
|
|
|
|
|
// .build() |
|
|
|
|
|
// } else { |
|
|
|
|
|
// audioRecord = AudioRecord( |
|
|
|
|
|
// MediaRecorder.AudioSource.VOICE_COMMUNICATION, |
|
|
|
|
|
// sampleRate, |
|
|
|
|
|
// channelConfig, |
|
|
|
|
|
// audioFormat, |
|
|
|
|
|
// bufferSize |
|
|
|
|
|
// ) |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// // 检查初始化状态 |
|
|
|
|
|
// if (audioRecord?.state != AudioRecord.STATE_INITIALIZED) { |
|
|
|
|
|
// throw IllegalStateException("AudioRecord初始化失败") |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// // 应用音频效果 |
|
|
|
|
|
// applyAudioEffects() |
|
|
|
|
|
|
|
|
|
|
|
// // 开始录音 |
|
|
|
|
|
// audioRecord?.startRecording() |
|
|
|
|
|
|
|
|
|
|
|
// // 检查录音状态 |
|
|
|
|
|
// if (audioRecord?.recordingState != AudioRecord.RECORDSTATE_RECORDING) { |
|
|
|
|
|
// throw IllegalStateException("录音启动失败") |
|
|
|
|
|
// } |
|
|
|
|
|
// } catch (e: Exception) { |
|
|
|
|
|
// e.printStackTrace() |
|
|
|
|
|
// } |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// /** |
|
|
|
|
|
// * 应用音频效果 - 总是启用所有音频增强功能 |
|
|
|
|
|
// */ |
|
|
|
|
|
// private fun applyAudioEffects() { |
|
|
|
|
|
// val sessionId = audioRecord?.audioSessionId ?: return |
|
|
|
|
|
|
|
|
|
|
|
// try { |
|
|
|
|
|
// // 设置音频模式为通信模式 |
|
|
|
|
|
// this@AzureAsrHelper.audioManager?.mode = AudioManager.MODE_IN_COMMUNICATION |
|
|
|
|
|
|
|
|
|
|
|
// // 启用声学回音消除 |
|
|
|
|
|
// if (AcousticEchoCanceler.isAvailable()) { |
|
|
|
|
|
// echoCanceler = AcousticEchoCanceler.create(sessionId) |
|
|
|
|
|
// echoCanceler?.enabled = true |
|
|
|
|
|
// Log.i(tag, "声学回音消除已启用") |
|
|
|
|
|
// } else { |
|
|
|
|
|
// Log.w(tag, "设备不支持声学回音消除") |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// // 启用噪声抑制 |
|
|
|
|
|
// if (NoiseSuppressor.isAvailable()) { |
|
|
|
|
|
// noiseSuppressor = NoiseSuppressor.create(sessionId) |
|
|
|
|
|
// noiseSuppressor?.enabled = true |
|
|
|
|
|
// Log.i(tag, "噪声抑制已启用") |
|
|
|
|
|
// } else { |
|
|
|
|
|
// Log.w(tag, "设备不支持噪声抑制") |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// // 启用自动增益控制 |
|
|
|
|
|
// if (AutomaticGainControl.isAvailable()) { |
|
|
|
|
|
// automaticGainControl = AutomaticGainControl.create(sessionId) |
|
|
|
|
|
// automaticGainControl?.enabled = true |
|
|
|
|
|
// Log.i(tag, "自动增益控制已启用") |
|
|
|
|
|
// } else { |
|
|
|
|
|
// Log.w(tag, "设备不支持自动增益控制") |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// Log.i(tag, "回音消除和音频增强功能已全部启用") |
|
|
|
|
|
// } catch (e: Exception) { |
|
|
|
|
|
// Log.e(tag, "设置音频效果失败: ${e.message}") |
|
|
|
|
|
// } |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// /** |
|
|
|
|
|
// * 获取音频格式 |
|
|
|
|
|
// */ |
|
|
|
|
|
// fun getFormat(): AudioStreamFormat = |
|
|
|
|
|
// AudioStreamFormat.getWaveFormatPCM(16000.toLong(), 16.toShort(), 1.toShort()) |
|
|
|
|
|
|
|
|
|
|
|
// /** |
|
|
|
|
|
// * Azure SDK 调用此方法获取音频数据 |
|
|
|
|
|
// * 这是拉流模式的核心方法,由SDK调用以获取音频数据 |
|
|
|
|
|
// */ |
|
|
|
|
|
// /** |
|
|
|
|
|
// * 优化:简化音频数据读取(移除文件保存) |
|
|
|
|
|
// */ |
|
|
|
|
|
// override fun read(buffer: ByteArray): Int { |
|
|
|
|
|
// try { |
|
|
|
|
|
// val result = audioRecord?.read(buffer, 0, buffer.size) ?: -1 |
|
|
|
|
|
// if (result < 0) { |
|
|
|
|
|
// Log.e(tag, "读取音频数据失败: $result") |
|
|
|
|
|
// return 0 |
|
|
|
|
|
// } |
|
|
|
|
|
// recordfile?.saveAudioDataToWav(buffer) |
|
|
|
|
|
// return result |
|
|
|
|
|
// } catch (e: Exception) { |
|
|
|
|
|
// Log.e(tag, "读取音频异常: ${e.message}") |
|
|
|
|
|
// return 0 |
|
|
|
|
|
// } |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// /** |
|
|
|
|
|
// * 优化:简化资源关闭 |
|
|
|
|
|
// */ |
|
|
|
|
|
// override fun close() { |
|
|
|
|
|
// releaseAudioResources() |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// /** |
|
|
|
|
|
// * 释放音频资源 |
|
|
|
|
|
// */ |
|
|
|
|
|
// private fun releaseAudioResources() { |
|
|
|
|
|
// try { |
|
|
|
|
|
// // 停止录音 |
|
|
|
|
|
// if (audioRecord?.recordingState == AudioRecord.RECORDSTATE_RECORDING) { |
|
|
|
|
|
// audioRecord?.stop() |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// // 释放回音消除器 |
|
|
|
|
|
// echoCanceler?.let { |
|
|
|
|
|
// it.enabled = false |
|
|
|
|
|
// it.release() |
|
|
|
|
|
// echoCanceler = null |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// // 释放噪声抑制器 |
|
|
|
|
|
// noiseSuppressor?.let { |
|
|
|
|
|
// it.enabled = false |
|
|
|
|
|
// it.release() |
|
|
|
|
|
// noiseSuppressor = null |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// // 释放自动增益控制 |
|
|
|
|
|
// automaticGainControl?.let { |
|
|
|
|
|
// it.enabled = false |
|
|
|
|
|
// it.release() |
|
|
|
|
|
// automaticGainControl = null |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// // 恢复音频模式 |
|
|
|
|
|
// this@AzureAsrHelper.audioManager?.mode = this@AzureAsrHelper.originalAudioMode |
|
|
|
|
|
|
|
|
|
|
|
// // 释放录音实例 |
|
|
|
|
|
// audioRecord?.release() |
|
|
|
|
|
// audioRecord = null |
|
|
|
|
|
// } catch (e: Exception) { |
|
|
|
|
|
// Log.e(tag, "释放音频资源失败: ${e.message}") |
|
|
|
|
|
// e.printStackTrace() |
|
|
|
|
|
// } |
|
|
|
|
|
// } |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 外部音频拉流 |
|
|
* 外部音频拉流 |
|
|
@ -1247,4 +1088,4 @@ class AzureAsrHelper(private val context: Context) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
} |