|
|
@ -1360,7 +1360,7 @@ startBytesStatistics() |
|
|
if (data != null) { |
|
|
if (data != null) { |
|
|
// 编码完成的数据处理: |
|
|
// 编码完成的数据处理: |
|
|
// 1. 保存到WAV文件 |
|
|
// 1. 保存到WAV文件 |
|
|
recordfile1?.saveAudioDataToWav(data) |
|
|
|
|
|
// 2. 将编码后的数据加入发送队列进行分块发送 |
|
|
// 2. 将编码后的数据加入发送队列进行分块发送 |
|
|
addAudioDataToSendQueue(data) |
|
|
addAudioDataToSendQueue(data) |
|
|
} else { |
|
|
} else { |
|
|
@ -1493,13 +1493,16 @@ startBytesStatistics() |
|
|
audioSendHandler.post { |
|
|
audioSendHandler.post { |
|
|
try { |
|
|
try { |
|
|
callWriteChar?.value = chunk |
|
|
callWriteChar?.value = chunk |
|
|
|
|
|
recordfile1?.saveAudioDataAsUtf8Hex(chunk, true) |
|
|
val isSuccess = bluetoothGatt?.writeCharacteristic(callWriteChar) |
|
|
val isSuccess = bluetoothGatt?.writeCharacteristic(callWriteChar) |
|
|
|
|
|
|
|
|
if (isSuccess == true) { |
|
|
if (isSuccess == true) { |
|
|
|
|
|
recordfile1?.saveAudioDataToWav("发送成功,大小: ${chunk.size} 字节") |
|
|
// 累加发送成功的字节数 |
|
|
// 累加发送成功的字节数 |
|
|
bytesSentInCurrentSecond += chunk.size |
|
|
bytesSentInCurrentSecond += chunk.size |
|
|
Log.d(TAG, "成功发送音频数据块,大小: ${chunk.size} 字节") |
|
|
Log.d(TAG, "成功发送音频数据块,大小: ${chunk.size} 字节") |
|
|
} else { |
|
|
} else { |
|
|
|
|
|
recordfile1?.saveAudioDataToWav("发送失败,大小: ${chunk.size} 字节") |
|
|
Log.e(TAG, "发送音频数据块失败,大小: ${chunk.size} 字节") |
|
|
Log.e(TAG, "发送音频数据块失败,大小: ${chunk.size} 字节") |
|
|
// 详细的失败原因分析 |
|
|
// 详细的失败原因分析 |
|
|
val failureReason = StringBuilder("发送音频数据块失败,大小: ${chunk.size} 字节") |
|
|
val failureReason = StringBuilder("发送音频数据块失败,大小: ${chunk.size} 字节") |
|
|
@ -1636,7 +1639,6 @@ startBytesStatistics() |
|
|
*/ |
|
|
*/ |
|
|
fun closeCodec(): Boolean { |
|
|
fun closeCodec(): Boolean { |
|
|
Log.i(TAG, "关闭编解码...") |
|
|
Log.i(TAG, "关闭编解码...") |
|
|
|
|
|
|
|
|
return sendCommand( |
|
|
return sendCommand( |
|
|
BleConst.CMD_CONTROL_CODEC.toByte(), |
|
|
BleConst.CMD_CONTROL_CODEC.toByte(), |
|
|
byteArrayOf( |
|
|
byteArrayOf( |
|
|
@ -1646,12 +1648,7 @@ startBytesStatistics() |
|
|
) // 或其他默认声道 |
|
|
) // 或其他默认声道 |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
fun closeCodec1(): Boolean { |
|
|
|
|
|
recordfile1!!.closeFile() |
|
|
|
|
|
|
|
|
|
|
|
recordfile!!.closeFile() |
|
|
|
|
|
return true |
|
|
|
|
|
} |
|
|
|
|
|
/** |
|
|
/** |
|
|
* 控制编解码 - 打开解码 |
|
|
* 控制编解码 - 打开解码 |
|
|
*/ |
|
|
*/ |
|
|
@ -1682,6 +1679,7 @@ startBytesStatistics() |
|
|
*/ |
|
|
*/ |
|
|
fun openA2DPDecoder(): Boolean { |
|
|
fun openA2DPDecoder(): Boolean { |
|
|
Log.i(TAG, "打开编码 0xA2") |
|
|
Log.i(TAG, "打开编码 0xA2") |
|
|
|
|
|
|
|
|
startOpusEncodeStream() |
|
|
startOpusEncodeStream() |
|
|
//双声道 80字节 |
|
|
//双声道 80字节 |
|
|
startOpusStreamDecoding(false, 2, 16000, 80) |
|
|
startOpusStreamDecoding(false, 2, 16000, 80) |
|
|
@ -1724,10 +1722,7 @@ startBytesStatistics() |
|
|
*/ |
|
|
*/ |
|
|
fun openEncoder(): Boolean { |
|
|
fun openEncoder(): Boolean { |
|
|
Log.i(TAG, "打开编码0xB1") |
|
|
Log.i(TAG, "打开编码0xB1") |
|
|
recordfile1!!.closeFile() |
|
|
|
|
|
recordfile1!!.creatingFiles() |
|
|
|
|
|
recordfile!!.closeFile() |
|
|
|
|
|
recordfile!!.creatingFiles() |
|
|
|
|
|
startOpusStreamDecoding(false, 1, 16000, 40) |
|
|
startOpusStreamDecoding(false, 1, 16000, 40) |
|
|
// // 目前仅发送命令通知设备开始编码。 |
|
|
// // 目前仅发送命令通知设备开始编码。 |
|
|
// return sendCommand( |
|
|
// return sendCommand( |
|
|
@ -2096,7 +2091,7 @@ class RecordingFile(private val context: Context) { |
|
|
val timestamp = dateFormat.format(Date()) |
|
|
val timestamp = dateFormat.format(Date()) |
|
|
|
|
|
|
|
|
val filePath = |
|
|
val filePath = |
|
|
File(context.getExternalFilesDir(null), "${fileName}_${timestamp}.opus").absolutePath |
|
|
File(context.getExternalFilesDir(null), "${fileName}_${timestamp}.txt").absolutePath |
|
|
currentAudioFile = File(filePath) |
|
|
currentAudioFile = File(filePath) |
|
|
currentAudioFile?.createNewFile() |
|
|
currentAudioFile?.createNewFile() |
|
|
// 追加音频数据到文件 |
|
|
// 追加音频数据到文件 |
|
|
@ -2124,11 +2119,12 @@ class RecordingFile(private val context: Context) { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 保存音频数据到 opus 文件(异步) |
|
|
* 保存音频数据到 opus 文件(异步) |
|
|
|
|
|
* @param buffer 音频数据字节数组 |
|
|
*/ |
|
|
*/ |
|
|
internal fun saveAudioDataToWav(buffer: ByteArray) { |
|
|
internal fun saveAudioDataToWav(buffer: ByteArray) { |
|
|
if (fos == null || currentAudioFile == null) return |
|
|
if (fos == null || currentAudioFile == null) return |
|
|
// 放入队列,由写线程写入 |
|
|
// 放入队列,由写线程写入 |
|
|
// 1. 将字节数据转换为十六进制字符串 |
|
|
// // 1. 将字节数据转换为十六进制字符串 |
|
|
// val hexData = buildString { |
|
|
// val hexData = buildString { |
|
|
// buffer.forEachIndexed { index, byte -> |
|
|
// buffer.forEachIndexed { index, byte -> |
|
|
// append("%02X".format(byte)) |
|
|
// append("%02X".format(byte)) |
|
|
@ -2142,6 +2138,52 @@ class RecordingFile(private val context: Context) { |
|
|
totalBytesWritten += buffer.size |
|
|
totalBytesWritten += buffer.size |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 保存日志信息到文件(重载方法) |
|
|
|
|
|
* 使用UTF-8编码处理字符串 |
|
|
|
|
|
* @param logMessage 日志消息字符串 |
|
|
|
|
|
*/ |
|
|
|
|
|
internal fun saveAudioDataToWav(logMessage: String) { |
|
|
|
|
|
if (fos == null || currentAudioFile == null) return |
|
|
|
|
|
try { |
|
|
|
|
|
// 使用UTF-8编码将字符串转换为字节数组,并添加换行符 |
|
|
|
|
|
val logBytes = (logMessage + "\n").toByteArray(Charsets.UTF_8) |
|
|
|
|
|
writeQueue.offer(logBytes) |
|
|
|
|
|
totalBytesWritten += logBytes.size |
|
|
|
|
|
Log.d("RecordingFile", "保存日志信息: ${logMessage.length} 字符, ${logBytes.size} 字节 (UTF-8)") |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e("RecordingFile", "UTF-8编码转换失败: ${e.message}") |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 将音频数据转换为UTF-8编码的十六进制字符串格式并保存 |
|
|
|
|
|
* @param buffer 音频数据字节数组 |
|
|
|
|
|
* @param addTimestamp 是否添加时间戳,默认为true |
|
|
|
|
|
*/ |
|
|
|
|
|
internal fun saveAudioDataAsUtf8Hex(buffer: ByteArray, addTimestamp: Boolean = true) { |
|
|
|
|
|
if (fos == null || currentAudioFile == null) return |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
val timestamp = if (addTimestamp) { |
|
|
|
|
|
"[${SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault()).format(Date())}] " |
|
|
|
|
|
} else "" |
|
|
|
|
|
|
|
|
|
|
|
// 将字节数组转换为十六进制字符串 |
|
|
|
|
|
val hexString = buffer.joinToString(" ") { "%02X".format(it) } |
|
|
|
|
|
val dataLine = "${timestamp}音频数据 (${buffer.size} 字节): $hexString\n" |
|
|
|
|
|
|
|
|
|
|
|
// 使用UTF-8编码转换为字节数组 |
|
|
|
|
|
val utf8Bytes = dataLine.toByteArray(Charsets.UTF_8) |
|
|
|
|
|
writeQueue.offer(utf8Bytes) |
|
|
|
|
|
totalBytesWritten += utf8Bytes.size |
|
|
|
|
|
|
|
|
|
|
|
Log.d("RecordingFile", "保存UTF-8格式音频数据: ${buffer.size} 字节 -> ${utf8Bytes.size} UTF-8字节") |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e("RecordingFile", "UTF-8格式保存失败: ${e.message}") |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal fun closeFile() { |
|
|
internal fun closeFile() { |
|
|
try { |
|
|
try { |
|
|
|