如何将PCM字节数组转换为小端序单声道格式适配Vosk语音识别API
代码问题修正
原有实现的错误点
- 单声道转换逻辑错误:你的输入是
PCM_SIGNED 48000.0 Hz, 16 bit, stereo, 4 bytes/frame, big-endian格式,每帧4个字节对应左右两个声道各2字节的采样,原有代码将所有2字节块都读取为独立采样,最终输出仍是双声道采样序列,没有合并为单声道。 - 端序转换逻辑冗余:不需要通过ByteBuffer写入再读取的方式转换端序,可在采样处理环节一次性完成,性能更高。
修正后的实现代码
转换逻辑为:拆分左右声道采样 -> 合并为单声道(取左右声道平均值)-> 转小端序输出
byte[] audioData = userAudio.getAudioData(1); // 立体声每帧4字节,单声道采样总数为总字节数 / 4 int monoSampleCount = audioData.length / 4; short[] convertedAudio = new short[monoSampleCount]; for (int i = 0; i < audioData.length; i += 4) { // 读取大端序左声道采样 short left = (short) ((audioData[i] & 0xFF) << 8 | (audioData[i+1] & 0xFF)); // 读取大端序右声道采样 short right = (short) ((audioData[i+2] & 0xFF) << 8 | (audioData[i+3] & 0xFF)); // 合并为单声道:先转int计算避免溢出,取平均值 short monoSample = (short) ((left + right) / 2); // 转换为小端序 convertedAudio[i/4] = Short.reverseBytes(monoSample); } queue.add(convertedAudio);
可选优化:如果确认有效音频仅在左/右其中一个声道,可以直接取对应声道的采样,无需计算平均值,减少计算开销。
可选:直接输出小端单声道字节数组
如果Vosk接口要求输入字节数组格式的PCM数据,可以直接生成目标字节数组,省略中间short数组转换步骤,性能更高:
byte[] audioData = userAudio.getAudioData(1); int monoSampleCount = audioData.length / 4; byte[] monoLittleEndianPcm = new byte[monoSampleCount * 2]; int writeIndex = 0; for (int i = 0; i < audioData.length; i += 4) { short left = (short) ((audioData[i] & 0xFF) << 8 | (audioData[i+1] & 0xFF)); short right = (short) ((audioData[i+2] & 0xFF) << 8 | (audioData[i+3] & 0xFF)); short monoSample = (short) ((left + right) / 2); // 直接写入小端字节:先写低8位,再写高8位 monoLittleEndianPcm[writeIndex++] = (byte) (monoSample & 0xFF); monoLittleEndianPcm[writeIndex++] = (byte) ((monoSample >> 8) & 0xFF); } queue.add(monoLittleEndianPcm);
内容的提问来源于stack exchange,提问作者moeux
相关产品推荐
相关产品推荐

