Как воспроизвести записанные сэмплы FFT? - PullRequest
0 голосов
/ 20 октября 2018

Я записываю FFT (" быстрое преобразование Фурье ") сэмплов из аудио, как это:

 const no_samples = 100;

 const samples = [];
 for (let i = 0, l = no_samples; i < l; ++i) {
     samples.push({ sample: new Uint8Array(this.sampleSize), time:-1 });
 }
 var lambdaToRemove = null;

 let samplesDone = 0;
 let start = -1;
 let second = 0;

 this.scriptProcessor.addEventListener("audioprocess", lambdaToRemove =
 /**
  * @param {AudioProcessingEvent} event
  * */
     (event) => {
         // firefox provides data even before user allows acces to microphone
         if (!this.mediaAllocated) {
             console.log("Sample before media was allocated!");
             return;
         }

         // AudioAnalyser - https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode
         this.analyser.getByteFrequencyData(samples[samplesDone].sample);
         ++samplesDone;

         if (samplesDone >= no_samples) {
             this.scriptProcessor.removeEventListener("audioprocess", lambdaToRemove);
             resolve(samples);
         }
 });

Соответствующая строка:

this.analyser.getByteFrequencyData(samples[samplesDone].sample);

Этот вызовзаполняет Uint8Array данными FFT.Длина предварительно установлена ​​и фиксирована.

Итак, теперь у меня есть массив с Uint8Array s, представляющий записанный звук.Как передать данные обратно в аудиопоток для воспроизведения в браузере?

Я хотел бы записать звук в формате FFT, изменить его, а затем воспроизвести.

Я пробовал это:

/** @type {{time:number, sample:Uint8Array}[]} **/
const samples = this.getSamples();
this.oscilator = this.audioContext.createOscillator();
this.oscilator.connect(this.audioContext.destination);

let ctime = 0;
const ssize = samples[0].sample.length;
const imaginary = new Float32Array(ssize);
const half255 = 255 / 2;
this.oscilator.start();

for (let i = 0, l = samples.length; i < l; ++i) {
    const sample = samples[i];
    await sleep(sample.time - ctime);
    ctime = sample.time;
    const real = new Float32Array(ssize);
    // convert 0->255 to -1.0->1.0
    for (let is = 0, ls = real.length; is < ls; ++is) {
        real[is] = (sample.sample[is] / half255) / half255;
    }

    const wave = this.audioContext.createPeriodicWave(real, imaginary);
    this.oscilator.setPeriodicWave(wave);

}
this.isPlaying = false;
this.oscilator.stop();

Но это просто звучит как зуммер.Должен быть способ перехода от одной волны к другой.

1 Ответ

0 голосов
/ 20 октября 2018

Вы должны будете повторно синтезировать звук, используя синусоидальные волны, поскольку это в основном то, что представляет собой разложение БПФ.

В частности, вы бы реализовали своего рода вокодер .

Вы можете изобразить массив значений БПФ в конкретный момент времени в виде амплитуд синусоидальных осцилляторов в этот момент времени, при этом fft[0] - это генератор с частотой 0 Гц, а fft[n - 1] - это генератор, настроенный наполовина вашей исходной частоты дискретизации (теорема Найквиста).

Качество ресинтеза, вероятно, не будет превосходным;чем больше массив БПФ, тем лучше разрешение по частоте (вероятно, за счет временного разрешения).

...