|
|
|
@ -80,6 +80,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
/** 使用外部提供的音频数据 */ |
|
|
|
EXTERNAL |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 获取设备支持的最佳音频格式 |
|
|
|
* 优先选择16000Hz,若不支持则降级到8000Hz |
|
|
|
@ -103,6 +104,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
// 创建对应的音频格式 |
|
|
|
return AudioStreamFormat.getWaveFormatPCM(sampleRate.toLong(), 16, 1) |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 初始化Azure语音服务 |
|
|
|
* |
|
|
|
@ -391,8 +393,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
|
audioRecord?.startRecording() |
|
|
|
startCaptureThread() // 再启动数据读取线程 |
|
|
|
isContinuousRecognitionActive = true |
|
|
|
Log.d(tag, "识别器创建成功: ${recognizer != null}") |
|
|
|
Log.d(tag, "开始识别任务: recognizeOnce 或 startContinuousRecognition 被调用") |
|
|
|
|
|
|
|
|
|
|
|
return true |
|
|
|
} catch (e: Exception) { |
|
|
|
@ -729,8 +730,8 @@ Log.d(tag, "开始识别任务: recognizeOnce 或 startContinuousRecognition 被 |
|
|
|
* 开始麦克风捕获 |
|
|
|
*/ |
|
|
|
private fun initAudioRecord() { |
|
|
|
// 获取已确定的采样率 |
|
|
|
val format = getOptimalAudioFormat() |
|
|
|
// 获取已确定的采样率 |
|
|
|
val format = getOptimalAudioFormat() |
|
|
|
val supportedSampleRates = intArrayOf(16000, 8000, 11025, 22050, 44100) |
|
|
|
|
|
|
|
// 查找设备支持的最佳采样率 |
|
|
|
@ -751,58 +752,75 @@ Log.d(tag, "开始识别任务: recognizeOnce 或 startContinuousRecognition 被 |
|
|
|
channelConfig, |
|
|
|
audioFormat |
|
|
|
) |
|
|
|
|
|
|
|
// 创建 AudioRecord 对象 |
|
|
|
audioRecord = AudioRecord( |
|
|
|
MediaRecorder.AudioSource.VOICE_RECOGNITION, // 语音识别专用音源(自带降噪/回声消除) |
|
|
|
sampleRate, |
|
|
|
channelConfig, |
|
|
|
audioFormat, |
|
|
|
minBufferSize * 2 // 使用两倍缓冲区防止溢出 |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
// 应用音频效果 |
|
|
|
applyAudioEffects() |
|
|
|
} |
|
|
|
|
|
|
|
// 3. 单独封装线程启动逻辑 |
|
|
|
private fun startCaptureThread() { |
|
|
|
captureThread = Thread { |
|
|
|
// 根据实际采样率计算缓冲区大小 |
|
|
|
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 |
|
|
|
// 创建录音对象 |
|
|
|
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 // 使用两倍缓冲区防止溢出 |
|
|
|
) |
|
|
|
bufferSize > 0 // 返回正值表示支持 |
|
|
|
} ?: 16000 // 默认回退值 |
|
|
|
// (sampleRate * 0.032).toInt() * 2 // 32ms数据量 |
|
|
|
|
|
|
|
val buffer = ByteArray(bufferSize) |
|
|
|
|
|
|
|
while (!Thread.interrupted() && audioRecord?.recordingState == AudioRecord.RECORDSTATE_RECORDING) { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 读取音频 |
|
|
|
val bytesRead = audioRecord?.read(buffer, 0, buffer.size) ?: 0 |
|
|
|
Log.i("AzureASR", "AudioRecord state: ${audioRecord?.state}, recordingState: ${audioRecord?.recordingState}") |
|
|
|
Log.i("AzureASR", "BytesRead = $bytesRead, buffer.isSilent = ${buffer.all { it == 0.toByte() }}") |
|
|
|
// 检查初始化状态 |
|
|
|
if (audioRecord?.state != AudioRecord.STATE_INITIALIZED) { |
|
|
|
throw IllegalStateException("AudioRecord初始化失败") |
|
|
|
} |
|
|
|
|
|
|
|
if (!isPaused && bytesRead > 0) { |
|
|
|
|
|
|
|
// 写入Azure流 |
|
|
|
audioStream?.write(buffer) |
|
|
|
} |
|
|
|
|
|
|
|
// 保存录音 |
|
|
|
recordfile?.saveAudioDataToWav(buffer) |
|
|
|
// 3. 单独封装线程启动逻辑 |
|
|
|
private fun startCaptureThread() { |
|
|
|
captureThread = Thread { |
|
|
|
// 根据实际采样率计算缓冲区大小 |
|
|
|
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) |
|
|
|
|
|
|
|
while (!Thread.interrupted() && audioRecord?.recordingState == AudioRecord.RECORDSTATE_RECORDING) { |
|
|
|
|
|
|
|
// 读取音频 |
|
|
|
val bytesRead = audioRecord?.read(buffer, 0, buffer.size) ?: 0 |
|
|
|
|
|
|
|
if (!isPaused && bytesRead > 0) { |
|
|
|
|
|
|
|
// 写入Azure流 |
|
|
|
audioStream?.write(buffer) |
|
|
|
|
|
|
|
// 保存录音 |
|
|
|
recordfile?.saveAudioDataToWav(buffer) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}.apply { start() } |
|
|
|
} |
|
|
|
}.apply { start() } |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 停止麦克风捕获并释放所有相关资源 |
|
|
|
|