|
|
|
@ -1,23 +1,17 @@ |
|
|
|
package com.yunqiinnovation.azure_speech |
|
|
|
|
|
|
|
import android.content.Context |
|
|
|
import android.content.pm.PackageManager |
|
|
|
import android.media.AudioAttributes |
|
|
|
|
|
|
|
import android.media.AudioFormat |
|
|
|
import android.media.AudioRecord |
|
|
|
import android.media.MediaRecorder |
|
|
|
import android.media.audiofx.AcousticEchoCanceler |
|
|
|
import android.media.audiofx.NoiseSuppressor |
|
|
|
import android.media.audiofx.AutomaticGainControl |
|
|
|
import android.os.Process |
|
|
|
import android.util.Log |
|
|
|
import com.microsoft.cognitiveservices.speech.* |
|
|
|
import com.microsoft.cognitiveservices.speech.audio.* |
|
|
|
import com.microsoft.cognitiveservices.speech.util.EventHandler |
|
|
|
import kotlinx.coroutines.* |
|
|
|
import java.util.concurrent.ExecutionException |
|
|
|
import java.util.concurrent.atomic.AtomicBoolean |
|
|
|
import kotlin.coroutines.CoroutineContext |
|
|
|
|
|
|
|
import android.media.AudioManager |
|
|
|
import com.microsoft.cognitiveservices.speech.audio.PullAudioInputStreamCallback |
|
|
|
import com.microsoft.cognitiveservices.speech.audio.AudioStreamFormat |
|
|
|
@ -25,6 +19,9 @@ import java.util.concurrent.BlockingQueue |
|
|
|
import java.util.concurrent.LinkedBlockingQueue |
|
|
|
import java.util.concurrent.TimeUnit |
|
|
|
import com.yunqiinnovation.azure_speech.tools.RecordFile |
|
|
|
import com.microsoft.cognitiveservices.speech.audio.AudioConfig |
|
|
|
import com.microsoft.cognitiveservices.speech.audio.AudioInputStream |
|
|
|
import com.microsoft.cognitiveservices.speech.audio.PushAudioInputStream |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -37,6 +34,9 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
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 recognizer: SpeechRecognizer? = null |
|
|
|
private var audioConfig: AudioConfig? = null |
|
|
|
@ -55,7 +55,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
var audioSourceType = AudioSourceType.MICROPHONE |
|
|
|
|
|
|
|
// 音频处理 |
|
|
|
private var microphoneStream: MicrophoneStream? = null |
|
|
|
// private var microphoneStream: MicrophoneStream? = null |
|
|
|
private var externalAudioStream: ExternalAudioPullStream? = null |
|
|
|
|
|
|
|
// 音频模式管理 |
|
|
|
@ -65,6 +65,10 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
// 录音文件处理 |
|
|
|
var recordfile: RecordFile? = null |
|
|
|
|
|
|
|
private var isPaused = false |
|
|
|
private var audioRecord: AudioRecord? = null |
|
|
|
private var captureThread: Thread? = null |
|
|
|
private var audioStream: PushAudioInputStream? = null |
|
|
|
|
|
|
|
/** |
|
|
|
* 音频来源类型 |
|
|
|
@ -101,11 +105,16 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
|
|
|
|
// 释放之前的资源 |
|
|
|
dispose() |
|
|
|
|
|
|
|
// 1. 创建可控制的音频流 |
|
|
|
val format = AudioStreamFormat.getWaveFormatPCM(16000, 16, 1) |
|
|
|
audioStream = AudioInputStream.createPushStream(format) |
|
|
|
// 初始化音频管理器 |
|
|
|
audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager |
|
|
|
originalAudioMode = audioManager?.mode ?: AudioManager.MODE_NORMAL |
|
|
|
|
|
|
|
// 5. 初始化麦克风数据 |
|
|
|
initAudioRecord() |
|
|
|
|
|
|
|
// 保存配置 |
|
|
|
this.subscriptionKey = subscriptionKey |
|
|
|
this.region = region |
|
|
|
@ -206,14 +215,17 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
*/ |
|
|
|
private fun setupMicrophoneStream() { |
|
|
|
try { |
|
|
|
|
|
|
|
// 创建麦克风流 |
|
|
|
microphoneStream = MicrophoneStream() |
|
|
|
// microphoneStream = MicrophoneStream() |
|
|
|
|
|
|
|
// 创建音频配置 - 正确使用fromStreamInput方法,只传入回调 |
|
|
|
audioConfig = AudioConfig.fromStreamInput(microphoneStream) |
|
|
|
// audioConfig = AudioConfig.fromStreamInput(microphoneStream) |
|
|
|
audioConfig = AudioConfig.fromStreamInput(audioStream) |
|
|
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(tag, "设置麦克风流失败: ${e.message}") |
|
|
|
microphoneStream = null |
|
|
|
//microphoneStream = null |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -349,10 +361,13 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
setupEventListeners(callback) |
|
|
|
|
|
|
|
// 启动音频处理 |
|
|
|
startAudioProcessing() |
|
|
|
//startAudioProcessing() |
|
|
|
|
|
|
|
// 开始连续识别 |
|
|
|
recognizer?.startContinuousRecognitionAsync() |
|
|
|
// 开始录音 |
|
|
|
audioRecord?.startRecording() |
|
|
|
startCaptureThread() // 再启动数据读取线程 |
|
|
|
isContinuousRecognitionActive = true |
|
|
|
|
|
|
|
return true |
|
|
|
@ -458,7 +473,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
// 停止音频处理 |
|
|
|
stopAudioProcessing() |
|
|
|
// 停止录音 |
|
|
|
|
|
|
|
stopMicrophoneCapture() |
|
|
|
// 会话结束事件会设置isContinuousRecognitionActive = false |
|
|
|
return true |
|
|
|
} catch (e: Exception) { |
|
|
|
@ -497,6 +512,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
recognizer?.stopContinuousRecognitionAsync() |
|
|
|
isContinuousRecognitionActive = false |
|
|
|
} |
|
|
|
releaseAudioResources() |
|
|
|
|
|
|
|
// 停止音频处理 |
|
|
|
stopAudioProcessing() |
|
|
|
@ -510,13 +526,17 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
speechConfig?.close() |
|
|
|
speechConfig = null |
|
|
|
|
|
|
|
// 关闭音频流(根据实际实现可能需要) |
|
|
|
audioStream?.close() |
|
|
|
audioStream = null |
|
|
|
|
|
|
|
// 释放音频配置 |
|
|
|
audioConfig?.close() |
|
|
|
audioConfig = null |
|
|
|
|
|
|
|
// 确保状态被重置 |
|
|
|
isContinuousRecognitionActive = false |
|
|
|
microphoneStream = null |
|
|
|
//microphoneStream = null |
|
|
|
externalAudioStream = null |
|
|
|
audioConfig = null |
|
|
|
recognizer = null |
|
|
|
@ -525,7 +545,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
// 确保状态被重置 |
|
|
|
|
|
|
|
isContinuousRecognitionActive = false |
|
|
|
microphoneStream = null |
|
|
|
//microphoneStream = null |
|
|
|
externalAudioStream = null |
|
|
|
audioConfig = null |
|
|
|
recognizer = null |
|
|
|
@ -555,15 +575,15 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
* 停止音频处理 |
|
|
|
*/ |
|
|
|
private fun stopAudioProcessing() { |
|
|
|
microphoneStream?.let { |
|
|
|
try { |
|
|
|
it.close() |
|
|
|
microphoneStream = null |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(tag, "关闭麦克风流失败: ${e.message}") |
|
|
|
e.printStackTrace() |
|
|
|
} |
|
|
|
} |
|
|
|
// microphoneStream?.let { |
|
|
|
// try { |
|
|
|
// it.close() |
|
|
|
// microphoneStream = null |
|
|
|
// } catch (e: Exception) { |
|
|
|
// Log.e(tag, "关闭麦克风流失败: ${e.message}") |
|
|
|
// e.printStackTrace() |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
externalAudioStream?.let { |
|
|
|
try { |
|
|
|
@ -631,200 +651,418 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
recordfile!!.closeFile(isSave) |
|
|
|
} |
|
|
|
|
|
|
|
fun stopAudioPause() { |
|
|
|
Log.d(tag, "关闭麦克风stopAudioPause: ") |
|
|
|
// 清理旧的识别器 |
|
|
|
isPaused = true |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
fun setAudiocontinue() { |
|
|
|
Log.d(tag, "继续麦克风stopAudioPause: ") |
|
|
|
|
|
|
|
// 开始连续识别 |
|
|
|
isPaused = false |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 麦克风流 - 拉流模式 |
|
|
|
* 实现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() |
|
|
|
fun disableBluetoothAudio() { |
|
|
|
try { |
|
|
|
// 关闭蓝牙SCO(Synchronous Connection Oriented)音频路由 |
|
|
|
audioManager?.isBluetoothScoOn = false |
|
|
|
// 停止蓝牙SCO连接 |
|
|
|
audioManager?.stopBluetoothSco() |
|
|
|
// 切换回正常音频模式(非通话模式) |
|
|
|
audioManager?.mode = AudioManager.MODE_IN_COMMUNICATION |
|
|
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e("AudioConfig", "Failed to disable Bluetooth: ${e.message}") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 初始化麦克风 |
|
|
|
*/ |
|
|
|
private fun initMicrophone() { |
|
|
|
try { |
|
|
|
// 创建录音对象 |
|
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.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 |
|
|
|
) |
|
|
|
} |
|
|
|
/** |
|
|
|
* 恢复原始音频设备状态(通常是重新启用蓝牙) |
|
|
|
*/ |
|
|
|
fun restoreOriginalAudioState() { |
|
|
|
try { |
|
|
|
// 恢复应用启动时的原始音频模式 |
|
|
|
audioManager?.mode = AudioManager.MODE_NORMAL |
|
|
|
|
|
|
|
// 检查初始化状态 |
|
|
|
if (audioRecord?.state != AudioRecord.STATE_INITIALIZED) { |
|
|
|
throw IllegalStateException("AudioRecord初始化失败") |
|
|
|
} |
|
|
|
// 重新启用蓝牙SCO |
|
|
|
audioManager?.isBluetoothScoOn = true |
|
|
|
// 启动蓝牙SCO连接(通常在需要蓝牙通话时使用) |
|
|
|
audioManager?.startBluetoothSco() |
|
|
|
|
|
|
|
// 应用音频效果 |
|
|
|
applyAudioEffects() |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e("AudioConfig", "Failed to restore audio state: ${e.message}") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 开始录音 |
|
|
|
audioRecord?.startRecording() |
|
|
|
/** |
|
|
|
* 开始麦克风捕获 |
|
|
|
*/ |
|
|
|
private fun initAudioRecord() { |
|
|
|
// 设置音频参数 |
|
|
|
val sampleRate = 16000 // 16kHz采样率(语音识别常用) |
|
|
|
val channelConfig = AudioFormat.CHANNEL_IN_MONO // 单声道输入 |
|
|
|
val audioFormat = AudioFormat.ENCODING_PCM_16BIT // 16位PCM格式 |
|
|
|
|
|
|
|
// 计算最小缓冲区大小(避免出现音频欠载) |
|
|
|
val minBufferSize = AudioRecord.getMinBufferSize( |
|
|
|
sampleRate, |
|
|
|
channelConfig, |
|
|
|
audioFormat |
|
|
|
) |
|
|
|
|
|
|
|
// 检查录音状态 |
|
|
|
if (audioRecord?.recordingState != AudioRecord.RECORDSTATE_RECORDING) { |
|
|
|
throw IllegalStateException("录音启动失败") |
|
|
|
} |
|
|
|
} catch (e: Exception) { |
|
|
|
e.printStackTrace() |
|
|
|
} |
|
|
|
} |
|
|
|
// 创建 AudioRecord 对象 |
|
|
|
audioRecord = AudioRecord( |
|
|
|
MediaRecorder.AudioSource.VOICE_RECOGNITION, // 语音识别专用音源(自带降噪/回声消除) |
|
|
|
sampleRate, |
|
|
|
channelConfig, |
|
|
|
audioFormat, |
|
|
|
minBufferSize * 2 // 使用两倍缓冲区防止溢出 |
|
|
|
) |
|
|
|
|
|
|
|
/** |
|
|
|
* 应用音频效果 - 总是启用所有音频增强功能 |
|
|
|
*/ |
|
|
|
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, "设备不支持声学回音消除") |
|
|
|
} |
|
|
|
// 应用音频效果 |
|
|
|
applyAudioEffects() |
|
|
|
} |
|
|
|
|
|
|
|
// 启用噪声抑制 |
|
|
|
if (NoiseSuppressor.isAvailable()) { |
|
|
|
noiseSuppressor = NoiseSuppressor.create(sessionId) |
|
|
|
noiseSuppressor?.enabled = true |
|
|
|
Log.i(tag, "噪声抑制已启用") |
|
|
|
} else { |
|
|
|
Log.w(tag, "设备不支持噪声抑制") |
|
|
|
// 3. 单独封装线程启动逻辑 |
|
|
|
private fun startCaptureThread() { |
|
|
|
captureThread = Thread { |
|
|
|
// 循环条件确保录音已开始 |
|
|
|
|
|
|
|
val buffer = ByteArray(1024) // 4KB数据缓冲区 |
|
|
|
while (!Thread.interrupted() && audioRecord?.recordingState == AudioRecord.RECORDSTATE_RECORDING) { |
|
|
|
// 从麦克风读取数据 |
|
|
|
val bytesRead = audioRecord?.read(buffer, 0, buffer.size) ?: 0 |
|
|
|
|
|
|
|
// 仅在未暂停时处理数据 |
|
|
|
if (!isPaused && bytesRead > 0) { |
|
|
|
// 写入音频流(可能是网络传输或本地处理) |
|
|
|
audioStream?.write(buffer) |
|
|
|
// 保存到WAV文件(如果启用了录制功能) |
|
|
|
recordfile?.saveAudioDataToWav(buffer) |
|
|
|
} |
|
|
|
|
|
|
|
// 启用自动增益控制 |
|
|
|
if (AutomaticGainControl.isAvailable()) { |
|
|
|
automaticGainControl = AutomaticGainControl.create(sessionId) |
|
|
|
automaticGainControl?.enabled = true |
|
|
|
Log.i(tag, "自动增益控制已启用") |
|
|
|
} else { |
|
|
|
Log.w(tag, "设备不支持自动增益控制") |
|
|
|
// 暂停时短暂休眠以减少CPU占用 |
|
|
|
if (isPaused) { |
|
|
|
try { |
|
|
|
Thread.sleep(50) |
|
|
|
} catch (e: InterruptedException) { |
|
|
|
break // 线程被中断时退出循环 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Log.i(tag, "回音消除和音频增强功能已全部启用") |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(tag, "设置音频效果失败: ${e.message}") |
|
|
|
} |
|
|
|
|
|
|
|
}.apply { start() } |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 停止麦克风捕获并释放所有相关资源 |
|
|
|
*/ |
|
|
|
private fun stopMicrophoneCapture() { |
|
|
|
try { |
|
|
|
// 中断并等待捕获线程结束 |
|
|
|
captureThread?.interrupt() |
|
|
|
captureThread?.join(300) // 最多等待300ms |
|
|
|
|
|
|
|
// 停止录音并释放AudioRecord资源 |
|
|
|
audioRecord?.stop() // 必须先停止再释放 |
|
|
|
|
|
|
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e("AudioConfig", "Error releasing resources: ${e.message}") |
|
|
|
} finally { |
|
|
|
// 确保恢复原始音频状态 |
|
|
|
restoreOriginalAudioState() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 获取音频格式 |
|
|
|
*/ |
|
|
|
fun getFormat(): AudioStreamFormat = |
|
|
|
AudioStreamFormat.getWaveFormatPCM(16000.toLong(), 16.toShort(), 1.toShort()) |
|
|
|
/** |
|
|
|
* 应用音频效果 - 总是启用所有音频增强功能 |
|
|
|
*/ |
|
|
|
private fun applyAudioEffects() { |
|
|
|
val sessionId = audioRecord?.audioSessionId ?: return |
|
|
|
|
|
|
|
/** |
|
|
|
* 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 |
|
|
|
try { |
|
|
|
// 设置音频模式为通信模式 |
|
|
|
audioManager?.mode = AudioManager.MODE_IN_COMMUNICATION |
|
|
|
|
|
|
|
// 启用声学回音消除 |
|
|
|
if (AcousticEchoCanceler.isAvailable()) { |
|
|
|
echoCanceler = AcousticEchoCanceler.create(sessionId) |
|
|
|
echoCanceler?.enabled = true |
|
|
|
Log.i(tag, "声学回音消除已启用") |
|
|
|
} else { |
|
|
|
Log.w(tag, "设备不支持声学回音消除") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 优化:简化资源关闭 |
|
|
|
*/ |
|
|
|
override fun close() { |
|
|
|
releaseAudioResources() |
|
|
|
} |
|
|
|
// 启用噪声抑制 |
|
|
|
if (NoiseSuppressor.isAvailable()) { |
|
|
|
noiseSuppressor = NoiseSuppressor.create(sessionId) |
|
|
|
noiseSuppressor?.enabled = true |
|
|
|
Log.i(tag, "噪声抑制已启用") |
|
|
|
} else { |
|
|
|
Log.w(tag, "设备不支持噪声抑制") |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 释放音频资源 |
|
|
|
*/ |
|
|
|
private fun releaseAudioResources() { |
|
|
|
try { |
|
|
|
// 停止录音 |
|
|
|
if (audioRecord?.recordingState == AudioRecord.RECORDSTATE_RECORDING) { |
|
|
|
audioRecord?.stop() |
|
|
|
} |
|
|
|
// 启用自动增益控制 |
|
|
|
if (AutomaticGainControl.isAvailable()) { |
|
|
|
automaticGainControl = AutomaticGainControl.create(sessionId) |
|
|
|
automaticGainControl?.enabled = true |
|
|
|
Log.i(tag, "自动增益控制已启用") |
|
|
|
} else { |
|
|
|
Log.w(tag, "设备不支持自动增益控制") |
|
|
|
} |
|
|
|
|
|
|
|
// 释放回音消除器 |
|
|
|
echoCanceler?.let { |
|
|
|
it.enabled = false |
|
|
|
it.release() |
|
|
|
echoCanceler = null |
|
|
|
} |
|
|
|
Log.i(tag, "回音消除和音频增强功能已全部启用") |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(tag, "设置音频效果失败: ${e.message}") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 释放噪声抑制器 |
|
|
|
noiseSuppressor?.let { |
|
|
|
it.enabled = false |
|
|
|
it.release() |
|
|
|
noiseSuppressor = null |
|
|
|
} |
|
|
|
/** |
|
|
|
* 释放音频资源 |
|
|
|
*/ |
|
|
|
private fun releaseAudioResources() { |
|
|
|
try { |
|
|
|
// 停止录音 |
|
|
|
if (audioRecord?.recordingState == AudioRecord.RECORDSTATE_RECORDING) { |
|
|
|
audioRecord?.stop() |
|
|
|
} |
|
|
|
|
|
|
|
// 释放自动增益控制 |
|
|
|
automaticGainControl?.let { |
|
|
|
it.enabled = false |
|
|
|
it.release() |
|
|
|
automaticGainControl = null |
|
|
|
} |
|
|
|
// 释放回音消除器 |
|
|
|
echoCanceler?.let { |
|
|
|
it.enabled = false |
|
|
|
it.release() |
|
|
|
echoCanceler = null |
|
|
|
} |
|
|
|
|
|
|
|
// 恢复音频模式 |
|
|
|
this@AzureAsrHelper.audioManager?.mode = this@AzureAsrHelper.originalAudioMode |
|
|
|
// 释放噪声抑制器 |
|
|
|
noiseSuppressor?.let { |
|
|
|
it.enabled = false |
|
|
|
it.release() |
|
|
|
noiseSuppressor = null |
|
|
|
} |
|
|
|
|
|
|
|
// 释放录音实例 |
|
|
|
audioRecord?.release() |
|
|
|
audioRecord = null |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(tag, "释放音频资源失败: ${e.message}") |
|
|
|
e.printStackTrace() |
|
|
|
// 释放自动增益控制 |
|
|
|
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() |
|
|
|
// } |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
/** |
|
|
|
* 外部音频拉流 |
|
|
|
* 实现PullAudioInputStreamCallback,将外部推送的音频数据转换为SDK可拉取的形式 |
|
|
|
|