【问题标题】:AudioRecord & AudioTrack do not seem to work with 32-bit encodingAudioRecord 和 AudioTrack 似乎不适用于 32 位编码
【发布时间】:2020-02-15 20:50:05
【问题描述】:

我目前正在做一个项目,我尝试使用 websocket 将音频从一个设备实时传输到另一个设备。为此,我正在尝试实现一个适用于浏览器、android 和 ios 的“跨平台”实现。我的目标是录制和播放各种格式的 PCM 音频。浏览器(chrome 和 firefox)生成的 PCM 具有 32 位编码,我尝试在 android 手机上播放。仅供参考here是项目。

在 android 上,我正在使用 AudioRecord 录制并将原始 pcm 通过 websocket 流式传输到另一台设备。同样,我使用AudioTrack 播放它。如果我使用 16 位编码,采样率为 44100Hz 和 2 个通道,一切正常。但是,它似乎不适用于 32 位编码。来自浏览器(32 位)的录音没有播放,即使我确实交错了频道等。同样,当我尝试使用 32 位编码在 android 上录制时,它不会产生任何声音,因为它没有播放任何东西在浏览器上。

我尝试在 android 上播放 32 位编码的 wav 文件,它工作正常。但是,我不知道系统是否在后台进行下采样。

我的目标是尽可能避免下采样/上采样,因为我想实现低延迟。

我在网上找不到任何解决方案,这是一个常见问题吗?我在这里遗漏了什么吗?

使用 32 位编码,write 方法的结果返回 AudioTrack.ERROR_INVALID_OPERATION

int 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!");
}

录制音频的实现:

public class AudioStream {
    private AudioStreamMetadata metadata = AudioStreamMetadata.getDefault();
    ...

    public void start() {
        ...
        new Thread(() -> {
            socket.send("started");
            socket.send(metadata.toString());

            while (!hasStopped) {
                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));
            }
        }).start();
    }

    private void initRecorder() {
        int min = AudioRecord.getMinBufferSize(metadata.getSampleRate(), metadata.getChannels(true), metadata.getEncoding());
        recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, metadata.getSampleRate(),
                metadata.getChannels(true), metadata.getEncoding(), min);
    }
}

AudioStreamMetadata 类:

public class AudioStreamMetadata {

    public static final int DEFAULT_SAMPLE_RATE = 44100;
    public static final int DEFAULT_CHANNELS = 2;
    public static final int DEFAULT_ENCODING = 32;
    public static final int DEFAULT_BUFFER_SIZE = 6144*4;
    ...

    public AudioStreamMetadata(int sampleRate, int bufferSize, int channels, int encoding) {
        this.sampleRate = sampleRate;
        this.bufferSize = bufferSize;
        this.channels = channels;
        this.encoding = encoding;
        this.bytesPerSample = encoding / 8;
        this.bufferSizeInBytes = bufferSize * bytesPerSample;
    }

    //getters

    public int getChannels(boolean in) {
        if(channels == 1){
            return in? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_OUT_MONO;
        }else if(channels == 2){
            return in? AudioFormat.CHANNEL_IN_STEREO : AudioFormat.CHANNEL_OUT_STEREO;
        }else{
            return 0;
        }
    }

    public int getEncoding() {
        if(encoding == 8){
            return AudioFormat.ENCODING_PCM_8BIT;
        }else if(encoding == 16){
            return AudioFormat.ENCODING_PCM_16BIT;
        }else if(encoding == 32){
            return AudioFormat.ENCODING_PCM_FLOAT;
        }else{
            return 0;
        }
    }

    public static AudioStreamMetadata getDefault(){
        return new AudioStreamMetadata(DEFAULT_SAMPLE_RATE, DEFAULT_BUFFER_SIZE, DEFAULT_CHANNELS, DEFAULT_ENCODING);
    }
}

【问题讨论】:

  • 您使用哪个 API 级别?
  • 最低api等级为23,安卓手机均满足32位编码的最低api等级
  • 你能把你的录音机代码贴在这里吗?
  • @Squti 我添加了代码
  • 感谢这有效! @MerveSahin

标签: android encoding 32-bit pcm


【解决方案1】:

在某些情况下,我们应该在播放 32 位浮点 PCM 时先更改字节顺序。

FloatBuffer fb = buffer.order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer();

【讨论】:

    【解决方案2】:

    我假设AudioTrack 能够处理write() 中的不同数据类型,因为我使用正确的配置对其进行了初始化。但是,初始化为 8 位编码的 AudioTrack 仅接受 byte,作为 16 位编码 byteshort,但 AudioTrack 作为 32 位编码仅接受 float . 我从套接字接收数据为byte[],我需要将其转换为float[]

    @Override
        public void onMessage(WebSocket webSocket, ByteString bytes) {
            super.onMessage(webSocket, bytes);
    
            byte[] buffer = bytes.toByteArray();
            FloatBuffer fb = ByteBuffer.wrap(buffer).asFloatBuffer();
            float[] out = new float[fb.capacity()];
            fb.get(out);
    
            int result = audioTrack.write(out, 0, out.length, AudioTrack.WRITE_BLOCKING);
    
        }
    

    【讨论】:

      【解决方案3】:

      要设置缓冲区大小,请使用以下代码:

      bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRate, channels, encoding);
      

      【讨论】:

        猜你喜欢
        • 2011-07-19
        • 1970-01-01
        • 2012-04-21
        • 2022-06-13
        • 1970-01-01
        • 2019-05-11
        • 1970-01-01
        • 2018-05-12
        • 1970-01-01
        相关资源
        最近更新 更多