FFT dengan offlineAudioContext

Saya mencoba melakukan analisis frekuensi file audio, tetapi saya ingin itu terjadi tanpa memutarnya. Saya menemukan ada offlineAudioContext, yang mungkin itulah yang saya butuhkan di sini.

Kode lengkap di jsfiddle ini

Web Audio API adalah wilayah yang belum saya jelajahi dan saya tidak sepenuhnya yakin dengan apa yang saya lakukan, banyak tutorial berfokus pada audio waktu nyata dan itulah yang ingin saya cegah.

Di dalam context.oncomplete, saya berhasil mendapatkan beberapa data audiobuffer yang tampak akurat. Saat mendapatkan data dari fft, sepertinya saya mendapatkan kumpulan data yang sangat kecil, yang menurut saya hanya data dari sampel terakhir. Saya lebih suka memiliki data ini untuk setiap x ms file audio yang saya muat. Saya ingin mendapatkan ide tentang cara mendapatkan format data ini?

Pada dasarnya yang saya harapkan adalah sesuatu seperti:

[
  // array with frequency data for every (nth) frequency band for 1st sample,
  // array with frequency data for every (nth) frequency band for 2nd sample,
  // array with frequency data for every (nth) frequency band for 3rd sample,
  …
]

person Marco    schedule 10.09.2013    source sumber
comment
Silakan periksa jawaban saya dan tandai sebagai diterima: stackoverflow.com/a/57744228/236062   -  person Zibri    schedule 01.09.2019
comment
@Zibri Sepertinya ini solusi yang tepat, tetapi proyek ini ditinggalkan segera setelah pertanyaan diposting, jadi saya tidak punya sarana atau energi untuk memverifikasi, maaf.   -  person Marco    schedule 02.09.2019


Jawaban (2)


Saat Anda menyetel fftSize di AnalyserNode, Anda akan mendapatkan (fftSize / 2) jumlah bin. Oleh karena itu, Anda melihat data yang jauh lebih sedikit dari yang diharapkan.

Pada dasarnya apa yang terjadi adalah getByteFrequencyData hanya melihat 128 sampel pertama dari buffer yang Anda berikan, dan sisanya diabaikan begitu saja.

Sebaliknya, Anda mungkin ingin mencoba menggunakan ScriptProcessorNode dengan bufferSize sama dengan fftSize Anda. Kemudian, di event handler ScriptProcessorNode's onaudioprocess, Anda dapat mengambil buffer dan mendapatkan FFT-nya. Sesuatu seperti ini:

var processor = context.createScriptProcessor(fftSize, 1, 1);
source.connect(processor);

processor.onaudioprocess = function( e ) {
  var input = e.inputBuffer.getChannelData(0),
      data = new Uint8Array(fftSamples);
  fft.getByteFrequencyData(data);
  // do whatever you want with `data`
}
person Kevin Ennis    schedule 10.09.2013
comment
Saya tidak bisa mengaktifkan proses onaudio, tetapi dari apa yang saya tahu itu karena audioContext tidak diinisialisasi dengan benar. Saat saya menggunakan new webkitAudioContext() acara menyala seperti yang diharapkan, tetapi tidak berhasil dengan webkitOfflineAudioContext. Tampaknya seseorang memerlukan beberapa parameter, tetapi bahkan dengan 1 saluran, panjang super tinggi dan samplerate 44100 sepertinya tidak menghasilkan apa-apa. Ada ide? - person Marco; 10.09.2013
comment
Bisakah Anda memposting jsFiddle tentang apa yang Anda dapatkan sejauh ini? - person Kevin Ennis; 10.09.2013
comment
Oh man. Anda sangat dekat! Cukup tambahkan context.startRendering() di akhir fungsi processBuffer dan pengendali onaudioprocess Anda akan mulai bekerja. - person Kevin Ennis; 11.09.2013
comment
Ah ya. Itu lebih terlihat seperti itu. Masih belum berfungsi seperti yang diharapkan. Tapi saya akan mendalaminya lagi. Anggap saja yang ini terjawab :) - person Marco; 11.09.2013
comment
Apakah Anda tahu untuk mengatur parameter panjang? Saya tidak yakin nilai apa yang mereka harapkan dan bagaimana mendapatkannya ketika saya memuat mp3 atau semacamnya. Ada ide? - person Marco; 22.09.2013
comment
Pada dasarnya hanya processBuffer.length -- atau panjang AudioBuffer berapa pun yang ingin Anda render. Triknya di sini adalah Anda mungkin memerlukan AudioContext dan OfflineAudioContext. Gunakan AudioContext untuk decodeAudioData sehingga Anda bisa mendapatkan panjang buffer, dan kemudian Anda dapat membuat konteks offline untuk membuat BufferSourceNode dan ScriptProcessorNode dan melakukan rendering. - person Kevin Ennis; 23.09.2013
comment
@Marco, apakah Anda membuatnya berhasil? Saya mencoba melakukan hal yang sama dan menghabiskan begitu banyak waktu untuk mencari solusi tetapi tetap tidak berhasil.. - person okram; 16.02.2019
comment
@okram Saya rasa tidak, tapi saya sudah lama meninggalkan proyek ini;) - person Marco; 18.02.2019

Dengan asumsi bahwa buffer adalah array float32 yang berisi suara satu saluran,
berikut adalah kode bagus untuk analisis offline yang saya tulis.

Fungsi yang diteruskan ke callback akan dieksekusi pada setiap langkah analisis.
Fungsi yang diteruskan ke onend akan dipanggil pada akhir proses.

function callback(data) {
    callback.count = callback.count || 0;
    console.log(callback.count++, data);
}
fftAnalyser = function(buff, sampleRate, fftSize, callback, onend) {

    // by Zibri (2019)
    var frequencies = [1000,1200,1400,1600]; //for example
    var frequencyBinValue = (f)=>{
        const hzPerBin = (ctx.sampleRate) / (2 * analyser.frequencyBinCount);
        const index = parseInt((f + hzPerBin / 2) / hzPerBin);
        return data[index]+1;
    }
    ctx = new OfflineAudioContext(1,buff.length,sampleRate);
    ctx.sampleRate = sampleRate;
    ctx.destination.channelCount = 1;

    processor = ctx.createScriptProcessor(1024, 1, 1);
    processor.connect(ctx.destination);

    analyser = ctx.createAnalyser();
    analyser.fftSize = fftSize;
    analyser.smoothingTimeConstant = 0.0;

    //analyser.minDecibels = -60;
    //analyser.maxDecibels = -20;

    source = ctx.createBufferSource();
    source.connect(analyser);

    processor.onaudioprocess = function(e) {
        data = new Uint8Array(analyser.frequencyBinCount);
        analyser.getByteFrequencyData(data);
        state = frequencies.map(frequencyBinValue.bind(this))
        if ("function" == typeof callback)
            onend();
        callback(state);
    }

    analyser.connect(processor);

    var sbuffer = ctx.createBuffer(1, buff.length, ctx.sampleRate);
    sbuffer.getChannelData(0).set(buff);
    source.buffer = sbuffer;

    source.onended = function(e) {
        console.log("Analysys ended.");
        if ("function" == typeof onend)
            onend();
    }

    console.log("Start Analysis.");

    source.start(0);
    ctx.startRendering();
}
person Zibri    schedule 01.09.2019
comment
Pertanyaannya mungkin di luar topik tetapi... Apakah mungkin untuk mendapatkan data frekuensi/frekuensiBinCount untuk waktu tertentu? Saya ingin menggambar bentuk gelombang untuk data frekuensi pada waktu tertentu ketika pengguna mencari file audio. Tanpa audio diputar. - person frizurd; 03.05.2020
comment
@frizurd tentu.. cukup atur waktu sebelum source.start - person Zibri; 12.07.2020