|
|
@ -18,6 +18,7 @@ import com.deep_voice.speech.tts.TtsEventListener |
|
|
import com.deep_voice.speech.tts.TtsEventType |
|
|
import com.deep_voice.speech.tts.TtsEventType |
|
|
import com.yunqiinnovation.ble_service.BleService |
|
|
import com.yunqiinnovation.ble_service.BleService |
|
|
import com.deep_voice.speech.tts.AudioOutputDevice |
|
|
import com.deep_voice.speech.tts.AudioOutputDevice |
|
|
|
|
|
import com.deep_voice.speech.tts.AudioDataListener |
|
|
import kotlinx.coroutines.* |
|
|
import kotlinx.coroutines.* |
|
|
import com.yunqiinnovation.azure_speech.tools.RecordFile |
|
|
import com.yunqiinnovation.azure_speech.tools.RecordFile |
|
|
/** AzureSpeechPlugin */ |
|
|
/** 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>) { |
|
|
private fun sendAudioDataEvent(event: Map<String, Any>) { |
|
|
if (audioDataEventSink == null) { |
|
|
if (audioDataEventSink == null) { |
|
|
@ -937,7 +1002,15 @@ class AzureSpeechPlugin : BleService.Callback, FlutterPlugin { |
|
|
} |
|
|
} |
|
|
override fun onSynthesisAudioGenerated(text: String, audioData: ByteArray) { |
|
|
override fun onSynthesisAudioGenerated(text: String, audioData: ByteArray) { |
|
|
FileLogger.d(tag, "语音合成音频生成,文本=${text},音频数据大小=${audioData.size}") |
|
|
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) { |
|
|
override fun onSynthesisFailed(text: String, error: String) { |
|
|
// sendAstEvent( |
|
|
// sendAstEvent( |
|
|
|