Browse Source

去处咔滋声

newdev_shunjiawei
fdp 12 months ago
parent
commit
309d861422
  1. 75
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt
  2. 74
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift

75
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt

@ -18,6 +18,7 @@ import com.deep_voice.speech.tts.TtsEventListener
import com.deep_voice.speech.tts.TtsEventType
import com.yunqiinnovation.ble_service.BleService
import com.deep_voice.speech.tts.AudioOutputDevice
import com.deep_voice.speech.tts.AudioDataListener
import kotlinx.coroutines.*
import com.yunqiinnovation.azure_speech.tools.RecordFile
/** AzureSpeechPlugin */
@ -105,6 +106,70 @@ class AzureSpeechPlugin : BleService.Callback, FlutterPlugin {
}
}
}
/**
* 从WAV格式音频数据中提取纯PCM数据
*
* @param wavData WAV格式的音频数据
* @return 纯PCM音频数据
*/
private fun extractPcmFromWav(wavData: ByteArray): ByteArray {
try {
// WAV文件头最小长度为44字节
if (wavData.size < 44) {
FileLogger.e(tag, "WAV数据长度不足,无法解析文件头")
return ByteArray(0)
}
// 检查RIFF标识符
val riffHeader = String(wavData, 0, 4, Charsets.US_ASCII)
if (riffHeader != "RIFF") {
FileLogger.e(tag, "不是有效的RIFF WAV文件,标识符: $riffHeader")
return ByteArray(0)
}
// 检查WAVE标识符
val waveHeader = String(wavData, 8, 4, Charsets.US_ASCII)
if (waveHeader != "WAVE") {
FileLogger.e(tag, "不是有效的WAVE文件,标识符: $waveHeader")
return ByteArray(0)
}
// 查找data chunk
var offset = 12 // 跳过RIFF头部
while (offset < wavData.size - 8) {
val chunkId = String(wavData, offset, 4, Charsets.US_ASCII)
val chunkSize = (wavData[offset + 4].toInt() and 0xFF) or
((wavData[offset + 5].toInt() and 0xFF) shl 8) or
((wavData[offset + 6].toInt() and 0xFF) shl 16) or
((wavData[offset + 7].toInt() and 0xFF) shl 24)
if (chunkId == "data") {
// 找到data chunk,提取PCM数据
val dataOffset = offset + 8
val dataSize = minOf(chunkSize, wavData.size - dataOffset)
if (dataSize > 0) {
val pcmData = ByteArray(dataSize)
System.arraycopy(wavData, dataOffset, pcmData, 0, dataSize)
FileLogger.d(tag, "成功提取PCM数据: ${pcmData.size}字节")
return pcmData
}
break
}
// 移动到下一个chunk
offset += 8 + chunkSize
}
FileLogger.e(tag, "未找到data chunk")
return ByteArray(0)
} catch (e: Exception) {
FileLogger.e(tag, "解析WAV数据异常: ${e.message}")
return ByteArray(0)
}
}
// 音频数据 事件发送方法
private fun sendAudioDataEvent(event: Map<String, Any>) {
if (audioDataEventSink == null) {
@ -937,7 +1002,15 @@ class AzureSpeechPlugin : BleService.Callback, FlutterPlugin {
}
override fun onSynthesisAudioGenerated(text: String, audioData: ByteArray) {
FileLogger.d(tag, "语音合成音频生成,文本=${text},音频数据大小=${audioData.size}")
BleService.writeExternalAudioData(audioData)
// Azure TTS返回的是RIFF WAV格式,需要去除文件头
val pcmData = extractPcmFromWav(audioData)
if (pcmData.isNotEmpty()) {
FileLogger.d(tag, "提取PCM数据,大小=${pcmData.size}字节")
BleService.writeExternalRightAudioData(pcmData)
}
else {
FileLogger.e(tag, "提取PCM数据失败,音频数据可能不是WAV格式")
}
}
override fun onSynthesisFailed(text: String, error: String) {
// sendAstEvent(

74
local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift

@ -264,6 +264,65 @@ private func sendAstEvent(_ event: [String: Any]) {
sendAudioDataEvent(eventMap)
}
/**
* 从WAV格式音频数据中提取纯PCM数据
* @param wavData WAV格式的音频数据
* @return 纯PCM音频数据,如果提取失败则返回空Data
*/
private func extractPcmFromWav(_ wavData: Data) -> Data {
// WAV文件头最小长度为44字节
guard wavData.count >= 44 else {
os_log("WAV数据长度不足,无法解析文件头", log: log, type: .error)
return Data()
}
// 检查RIFF标识符
let riffHeader = String(data: wavData.subdata(in: 0..<4), encoding: .ascii) ?? ""
guard riffHeader == "RIFF" else {
os_log("不是有效的RIFF WAV文件,标识符: %@", log: log, type: .error, riffHeader)
return Data()
}
// 检查WAVE标识符
let waveHeader = String(data: wavData.subdata(in: 8..<12), encoding: .ascii) ?? ""
guard waveHeader == "WAVE" else {
os_log("不是有效的WAVE文件,标识符: %@", log: log, type: .error, waveHeader)
return Data()
}
// 查找data chunk
var offset = 12 // 跳过RIFF头部
while offset < wavData.count - 8 {
let chunkIdData = wavData.subdata(in: offset..<(offset + 4))
let chunkId = String(data: chunkIdData, encoding: .ascii) ?? ""
// 读取chunk大小(小端序)
let chunkSizeData = wavData.subdata(in: (offset + 4)..<(offset + 8))
let chunkSize = chunkSizeData.withUnsafeBytes { bytes in
return bytes.load(as: UInt32.self).littleEndian
}
if chunkId == "data" {
// 找到data chunk,提取PCM数据
let dataOffset = offset + 8
let dataSize = min(Int(chunkSize), wavData.count - dataOffset)
if dataSize > 0 {
let pcmData = wavData.subdata(in: dataOffset..<(dataOffset + dataSize))
os_log("成功提取PCM数据: %d字节", log: log, type: .info, pcmData.count)
return pcmData
}
break
}
// 移动到下一个chunk
offset += 8 + Int(chunkSize)
}
os_log("未找到data chunk", log: log, type: .error)
return Data()
}
// MARK: - ASR 方法处理
private func handleAsrMethodCall(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
@ -950,9 +1009,18 @@ private class AstEventCallback: IntegratedSpeechTranslationService.ServiceEventC
* @param audioData 音频数据
*/
public func onSynthesisAudioGenerated(text: String, audioData: Data) {
print("语音合成音频数据生成回调", text, audioData.count)
// 调用BLE服务写入音频数据
BleService.shared.writeExternalAudioData(data: audioData)
guard let plugin = plugin else { return }
plugin.os_log("语音合成音频生成,文本=%@,音频数据大小=%d", log: plugin.log, type: .info, text, audioData.count)
// Azure TTS返回的是RIFF WAV格式,需要去除文件头提取PCM数据
let pcmData = plugin.extractPcmFromWav(audioData)
if !pcmData.isEmpty {
plugin.os_log("提取PCM数据,大小=%d字节", log: plugin.log, type: .info, pcmData.count)
BleService.shared.writeExternalAudioData(data: pcmData)
} else {
plugin.os_log("提取PCM数据失败,音频数据可能不是WAV格式", log: plugin.log, type: .error)
}
// plugin?.sendAsrEvent([
// "type": "synthesisAudioGenerated",

Loading…
Cancel
Save