AudioRecord и AudioTrack не работают с 32-битным кодированием - PullRequest
0 голосов
/ 18 октября 2019

В настоящее время я работаю над проектом, в котором я пытаюсь транслировать аудио с одного устройства на другое в режиме реального времени с помощью веб-сокетов. Для этого я пытаюсь реализовать кроссплатформенную реализацию, которая работает с браузерами, Android и IOS. Моя цель - записывать и воспроизводить звук PCM в различных форматах. PCM, созданный браузером (chrome & firefox), имеет 32-битную кодировку, которую я пытаюсь воспроизвести на телефоне Android. Просто для справки здесь - это проект.

На андроиде я записываю с AudioRecord и передаю сырой PCM через веб-сокет на другое устройство. И точно так же я играю, используя AudioTrack . Все работает нормально, если я использую 16-битное кодирование, с частотой дискретизации 44100 Гц и 2 каналами. Однако, похоже, он не работает с 32-битной кодировкой. Запись из браузера (32-битная) не воспроизводится, хотя я чередовал каналы и т. Д. И аналогично, когда я пытаюсь записать на Android с использованием 32-битной кодировки, он не производит никакого звука, так как ничего не воспроизводитв браузере.

Я пытался воспроизвести файл WAV на Android с 32-разрядной кодировкой, и он работает нормально. Однако я не знаю, выполняет ли система понижающую дискретизацию в фоновом режиме.

Моя цель состоит в том, чтобы избежать максимально возможной понижающей / повышающей дискретизации, так как я хочу добиться низкой задержки.

Я не смог найти какое-либо решение в сети, это распространенная проблема, я что-то здесь упускаю?

При 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);
    }
}

Ответы [ 2 ]

0 голосов
/ 19 октября 2019

Я предположил, что AudioTrack сможет обрабатывать различные типы данных в write(), так как я инициализирую его с правильными конфигурациями. Однако AudioTrack, инициализированный как 8-битное кодирование, принимает только byte, как 16-битное кодирование и byte и short, но 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);

    }
0 голосов
/ 19 октября 2019

Чтобы установить размер буфера, используйте следующий код:

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