Преобразование аудио Float32 в Int16 в javascript

Я пытаюсь создать фильтр шумоподавления в WebRtc, используя мою библиотеку C, скомпилированную в wasm и вызываемую из Javascript.

Я могу захватить звук PCM с помощью WebAudioApi и обработать кадр с помощью wasm.

Моя библиотека принимает ввод только в int16.

Вот мой код: я пробовал 2 метода

Способ 1:

navigator.mediaDevices.getUserMedia(constraints).then(function success(stream) {
var audiocontext;
var audiosource;
var audiopreprocessnode;

 audiocontext = new (window.AudioContext || window.webkitAudioContext)();
 audiosource = audiocontext.createMediaStreamSource(stream);
 audioPreprocessNode = audioCtx.createScriptProcessor(2048,1,1);

 audiosource.connect(audioPreprocessNode);
 audioPreprocessNode.connect(audioCtx.destination);
 
 audioPreprocessNode.onaudioprocess = function(e) {
 
 var input = new Int16Array(e.inputbuffer.getChannelData(0));
 
 console.log(input.length); // prints 4096

 var denoised_array = Module["_denoise"](input);

 var output = new Float32Array(denoised_array);
 
 console.log(output.length);  // prints 2048
 
 e.outputBuffer.getChannelData(0).set(output); 
 
 
 }
   
}

Преимущества метода в том, что он сохраняет количество байтов, поэтому потери данных не будет.

Но когда я конвертирую его обратно в Float32Array, значения с плавающей запятой выходят за пределы предельного значения аудиобуфера [-1,1]. Таким образом, никакие аудиоданные не передаются.

Способ 2:


function floatTo16Bit(inputArray){
    var output = new Int16Array(2048);
    for (var i = 0; i < inputArray.length; i++){
        var s = Math.max(-1, Math.min(1, inputArray[i]));
        output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
    }
    return output;
}
function int16ToFloat32(inputArray) {
    var output = new Float32Array(2048);
    for (var i = 0; i < 2048; i++) {
        var int = inputArray[i];
        var float = (int >= 0x8000) ? -(0x10000 - int) / 0x8000 : int / 0x7FFF;
        output[i] = float;
    }
    return output;
}

navigator.mediaDevices.getUserMedia(constraints).then(function success(stream) {
var audiocontext;
var audiosource;
var audiopreprocessnode;

 audiocontext = new (window.AudioContext || window.webkitAudioContext)();
 audiosource = audiocontext.createMediaStreamSource(stream);
 audioPreprocessNode = audioCtx.createScriptProcessor(2048,1,1);

 audiosource.connect(audioPreprocessNode);
 audioPreprocessNode.connect(audioCtx.destination);
 
 audioPreprocessNode.onaudioprocess = function(e) {
 
 var input = floatTo16Bit(e.inputbuffer.getChannelData(0));
 
 console.log(input.length); // prints 2048

 var denoised_array = Module["_denoise"](input);

 var output = int16ToFloat32(denoised_array);
 
 console.log(output.length);  // prints 2048
 
 e.outputBuffer.getChannelData(0).set(output); 
 
 
 }
   
}

Преимущество этого метода в том, что он преобразует обратно значения в диапазоне [-1,1].

Но качество звука (искажения) сильно страдает из-за потери байтов.

Есть ли способ сохранить байты и эффективно преобразовать float32 - int16 и обратно.

Anyhelp было бы здорово.


person Andrew    schedule 13.07.2020    source источник


Ответы (1)


Не уверен, что вы подразумеваете под сохранением байтов, но преобразование из float32 в int16 обязательно приведет к потере информации. Но их не должно быть так много, чтобы возникали огромные искажения. В конце концов, компакт-диски всего 16 бит.

Для простоты я бы преобразовал float в int16, зажав float до [-1, 1], как вы сделали, а затем просто умножив на 32768 и взяв целую часть. Это дает 16-битное целое число со знаком.

Чтобы пойти другим путем, просто разделите значение int16 на 32768. (Опять же, это предполагает, что у вас есть подписанные 16-битные числа.)

person Raymond Toy    schedule 13.07.2020
comment
var float = new Float32Array(2048); var int = new Int16Array(float.buffer); var float = new Float32Array(int.buffer); Строка 1: создает массив float32 длиной 2048 (8192 байта) Строка 2: создает массив int16 из буфера с плавающей запятой длиной 4096 (8192 байта) Строка 3: создает массив с плавающей точкой из буфера int длиной 2048 (8192 байта) делая таким образом, он сохранит точное количество байтов, а float32-int16-float32 не потеряет никаких данных, но после преобразования моей библиотеки, обработанной int16 в float, значение float выходит за пределы IEEE float limit [-1,1]. - person Andrew; 14.07.2020
comment
Да, это сохраняет значения с плавающей запятой. Но int на самом деле не содержит аудиоданных. Это байты с плавающей запятой, а не тот звук, который вам нужен. Посмотрите, что происходит, когда float[0] = 1.0. int[0] и int[1] не похожи на 1.0. int[0] будет младшими 2 байтами 1.0, а int[1] — двумя старшими байтами. Это не аудиоданные PCM в 16-битном формате. - person Raymond Toy; 14.07.2020
comment
Спасибо за ваше объяснение. Можно ли как-то удалить искажения, вызванные преобразованием float в int? - person Andrew; 15.07.2020
comment
Вы имеете в виду потерю точности из-за того, как я это делаю? Это неизбежно. Вы можете использовать 32-битные целые числа, но вы все равно потеряете некоторую точность, потому что могут быть числа меньше 2^(-32). Если вы получаете выборки PCM с микрофона, таких чисел, вероятно, не будет, поскольку вход, вероятно, является ЦАП с 24 битами. Это хорошо вписывается в 32-битные целые числа и числа с плавающей запятой одинарной точности. - person Raymond Toy; 15.07.2020