В настоящее время я работаю над проектом, в котором я пытаюсь транслировать аудио с одного устройства на другое в режиме реального времени с помощью веб-сокетов. Для этого я пытаюсь реализовать кроссплатформенную реализацию, которая работает с браузерами, 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);
}
}