You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AudioRecord与AudioTrack 32位编码无法正常工作问题求助

解决跨平台WebSocket 32位PCM音频流互播问题

我来帮你拆解一下问题的核心,并给出针对性的解决方案——你的问题主要出在Android AudioTrack的API使用错误和跨平台格式兼容性细节上,咱们一步步来解决:

核心问题分析

  1. AudioTrack.write()返回ERROR_INVALID_OPERATION的直接原因:
    当你使用AudioFormat.ENCODING_PCM_FLOAT(32位浮点PCM)时,调用write(byte[])方法是完全错误的!这个重载方法仅适用于8位/16位整数PCM编码,浮点PCM必须使用write(float[])的重载版本,否则系统会直接返回无效操作错误。

  2. 跨平台互播的格式一致性问题:
    浏览器和Android的32位PCM都是浮点格式,但要确保两端的字节序一致(你已经用了小端序,这点没问题),以及通道交错逻辑统一(双声道必须是左-右-左-右的顺序)。另外,Android 5.0(API 21)才开始支持PCM_FLOAT,低版本设备需要降级处理。


具体解决方案

1. 修复Android端AudioTrack的写入逻辑

把原来直接写入字节数组的代码,改成根据编码格式选择对应的写入方法:

// 假设buffer是从WebSocket接收的字节数组
int result;
if (metadata.getEncoding() == AudioFormat.ENCODING_PCM_FLOAT) {
    // 32位浮点PCM:将字节数组转换为float数组
    float[] floatBuffer = new float[buffer.length / 4]; // 每个float占4字节
    ByteBuffer.wrap(buffer)
              .order(ByteOrder.LITTLE_ENDIAN)
              .asFloatBuffer()
              .get(floatBuffer);
    result = audioTrack.write(floatBuffer, 0, floatBuffer.length, AudioTrack.WRITE_BLOCKING);
} else {
    // 8/16位整数PCM:直接写入字节数组
    result = audioTrack.write(buffer, 0, buffer.length, AudioTrack.WRITE_BLOCKING);
}

// 错误判断逻辑保持不变
if(result == AudioTrack.ERROR_BAD_VALUE){
 System.out.println("ERROR: bad value");
}else if (result == AudioTrack.ERROR_DEAD_OBJECT){
 System.out.println("ERROR: dead object");
}else if (result == AudioTrack.ERROR_INVALID_OPERATION){
 System.out.println("ERROR: invalid operation");
}else if (result == AudioTrack.ERROR){
 System.out.println("ERROR: ??");
}else{
 System.out.println("Successfully written to buffer!");
}

2. 添加API版本兼容性检查

在初始化AudioTrack前,先判断设备版本,避免在Android 5.0以下设备使用PCM_FLOAT:

private void initAudioTrack() {
    // 检查版本,不支持PCM_FLOAT则降级到16位
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && metadata.getEncoding() == AudioFormat.ENCODING_PCM_FLOAT) {
        Log.w("AudioStream", "32位浮点PCM不支持当前设备,自动降级到16位PCM");
        metadata = new AudioStreamMetadata(
            metadata.getSampleRate(),
            metadata.getBufferSize(),
            metadata.getChannels(),
            16 // 降级到16位
        );
    }

    int channelConfig = metadata.getChannels(false); // false表示输出通道
    int audioFormat = metadata.getEncoding();
    int sampleRate = metadata.getSampleRate();
    int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
    
    audioTrack = new AudioTrack(
        AudioManager.STREAM_MUSIC,
        sampleRate,
        channelConfig,
        audioFormat,
        minBufferSize,
        AudioTrack.MODE_STREAM
    );
    audioTrack.play();
}

3. 确保浏览器端的格式匹配

浏览器发送32位浮点PCM时,要保证是小端字节序的交错双声道数据:

// 从Web Audio API的AudioBuffer获取双声道数据并交错
function interleaveChannels(leftChannel, rightChannel) {
    const interleaved = new Float32Array(leftChannel.length * 2);
    for (let i = 0; i < leftChannel.length; i++) {
        interleaved[i * 2] = leftChannel[i];
        interleaved[i * 2 + 1] = rightChannel[i];
    }
    return interleaved;
}

// 发送到WebSocket
const audioBuffer = await audioContext.decodeAudioData(blob);
const left = audioBuffer.getChannelData(0);
const right = audioBuffer.getChannelData(1);
const interleavedData = interleaveChannels(left, right);
socket.send(new Uint8Array(interleavedData.buffer));

4. 验证Android录制端的逻辑

你当前的录制代码是正确的,读取float数组后转换成小端字节序发送,保持这个逻辑即可:

float[] data = new float[metadata.getBufferSize()];
recorder.read(data, 0, data.length, AudioRecord.READ_BLOCKING);
byte[] output = new byte[data.length * metadata.getBytesPerSample()];
ByteBuffer.wrap(output).order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer().put(data);
socket.send(ByteString.of(output));

补充说明

关于你提到的Android能播放32位WAV文件但不确定是否降采样:系统的MediaPlayer会自动处理格式适配(比如降采样到设备支持的格式),但AudioTrack是直接播放原始PCM数据,必须严格匹配参数,这就是为什么WAV能播但WebSocket流不行的原因——MediaPlayer帮你做了格式转换,而AudioTrack没有。

按照上面的修改,你应该能在Android 5.0+设备上实现32位PCM的低延迟互播,同时兼容低版本设备降级到16位。

内容的提问来源于stack exchange,提问作者Merve Sahin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:31:49