Memulai Web Audio API

Sebelum elemen <audio> HTML5, Flash atau plugin lain diperlukan untuk menghentikan keheningan web. Meskipun audio di web tidak lagi memerlukan plugin, tag audio memberikan batasan yang signifikan untuk menerapkan game canggih dan aplikasi interaktif.

Web Audio API adalah JavaScript API tingkat tinggi untuk memproses dan menyintesis audio di aplikasi web. Tujuan API ini adalah untuk menyertakan kemampuan yang ditemukan di game engine audio modern dan beberapa tugas pencampuran, pemrosesan, dan pemfilteran yang ditemukan di aplikasi produksi audio desktop modern. Berikut adalah pengantar sederhana untuk menggunakan API yang canggih ini.

AudioContext digunakan untuk mengelola dan memutar semua suara. Untuk menghasilkan suara menggunakan Web Audio API, buat satu atau beberapa sumber suara dan hubungkan ke tujuan suara yang disediakan oleh instance AudioContext. Koneksi ini tidak perlu langsung, dan dapat melalui berbagai AudioNodes perantara yang berfungsi sebagai modul pemrosesan untuk sinyal audio. Pemilihan rute ini dijelaskan secara lebih mendetail di spesifikasi Audio Web.

Satu instance AudioContext dapat mendukung beberapa input suara dan grafik audio yang kompleks, sehingga kita hanya memerlukan salah satu dari ini untuk setiap aplikasi audio yang kita buat.

Cuplikan berikut akan membuat AudioContext:

var context;
window.addEventListener('load', init, false);
function init() {
    try {
    context = new AudioContext();
    }
    catch(e) {
    alert('Web Audio API is not supported in this browser');
    }
}

Untuk browser berbasis WebKit lama, gunakan awalan webkit, seperti webkitAudioContext.

Banyak fungsi Web Audio API yang menarik seperti membuat AudioNodes dan mendekode data file audio adalah metode AudioContext.

Memuat suara

Web Audio API menggunakan AudioBuffer untuk suara dengan durasi pendek hingga sedang. Pendekatan dasarnya adalah menggunakan XMLHttpRequest untuk mengambil file suara.

API ini mendukung pemuatan data file audio dalam beberapa format, seperti WAV, MP3, AAC, OGG, dan lainnya. Dukungan browser untuk berbagai format audio bervariasi.

Cuplikan berikut menunjukkan pemuatan contoh suara:

var dogBarkingBuffer = null;
var context = new AudioContext();

function loadDogSound(url) {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';

    // Decode asynchronously
    request.onload = function() {
    context.decodeAudioData(request.response, function(buffer) {
        dogBarkingBuffer = buffer;
    }, onError);
    }
    request.send();
}

Data file audio bersifat biner (bukan teks), jadi kita menetapkan responseType permintaan ke 'arraybuffer'. Untuk informasi selengkapnya tentang ArrayBuffers, lihat artikel tentang XHR2 ini.

Setelah data file audio (yang tidak didekode) diterima, data tersebut dapat disimpan untuk dekode nanti, atau dapat langsung didekode menggunakan metode decodeAudioData() AudioContext. Metode ini mengambil ArrayBuffer data file audio yang disimpan di request.response dan mendekodenya secara asinkron (tidak memblokir thread eksekusi JavaScript utama).

Setelah selesai, decodeAudioData() akan memanggil fungsi callback yang menyediakan data audio PCM yang didekode sebagai AudioBuffer.

Memutar suara

Grafik audio sederhana
Grafik audio sederhana

Setelah satu atau beberapa AudioBuffers dimuat, kita siap memutar suara. Anggaplah kita baru saja memuat AudioBuffer dengan suara gonggongan dan pemuatan telah selesai. Kemudian, kita dapat memutar buffer ini dengan kode berikut.

var context = new AudioContext();

function playSound(buffer) {
    var source = context.createBufferSource(); // creates a sound source
    source.buffer = buffer;                    // tell the source which sound to play
    source.connect(context.destination);       // connect the source to the context's destination (the speakers)
    source.noteOn(0);                          // play the source now
}

Fungsi playSound() ini dapat dipanggil setiap kali seseorang menekan tombol atau mengklik sesuatu dengan mouse.

Fungsi noteOn(time) memudahkan penjadwalan pemutaran suara yang akurat untuk game dan aplikasi penting lainnya. Namun, agar penjadwalan ini berfungsi dengan benar, pastikan buffering suara Anda dimuat sebelumnya.

Mengabstraksi Web Audio API

Tentu saja, akan lebih baik untuk membuat sistem pemuatan yang lebih umum yang tidak di-hardcode untuk memuat suara tertentu ini. Ada banyak pendekatan untuk menangani banyak suara berdurasi pendek hingga sedang yang akan digunakan aplikasi atau game audio. Berikut adalah salah satu cara menggunakan BufferLoader (bukan bagian dari standar web).

Berikut adalah contoh cara menggunakan class BufferLoader. Mari kita buat dua AudioBuffers; dan, segera setelah dimuat, mari kita putar ulang secara bersamaan.

window.onload = init;
var context;
var bufferLoader;

function init() {
    context = new AudioContext();

    bufferLoader = new BufferLoader(
    context,
    [
        '../sounds/hyper-reality/br-jam-loop.wav',
        '../sounds/hyper-reality/laughter.wav',
    ],
    finishedLoading
    );

    bufferLoader.load();
}

function finishedLoading(bufferList) {
    // Create two sources and play them both together.
    var source1 = context.createBufferSource();
    var source2 = context.createBufferSource();
    source1.buffer = bufferList[0];
    source2.buffer = bufferList[1];

    source1.connect(context.destination);
    source2.connect(context.destination);
    source1.noteOn(0);
    source2.noteOn(0);
}

Menangani waktu: memutar suara dengan ritme

Web Audio API memungkinkan developer menjadwalkan pemutaran dengan tepat. Untuk mendemonstrasikannya, mari kita siapkan trek ritme sederhana. Mungkin pola drumkit yang paling banyak dikenal adalah sebagai berikut:

Pola drum rock sederhana
Pola drum rock sederhana

dengan hihat dimainkan setiap not delapan, dan kick dan snare dimainkan secara bergantian setiap kuartal, dalam waktu 4/4.

Misalkan kita telah memuat buffer kick, snare, dan hihat, kode untuk melakukannya sangat sederhana:

for (var bar = 0; bar < 2; bar++) {
    var time = startTime + bar * 8 * eighthNoteTime;
    // Play the bass (kick) drum on beats 1, 5
    playSound(kick, time);
    playSound(kick, time + 4 * eighthNoteTime);

    // Play the snare drum on beats 3, 7
    playSound(snare, time + 2 * eighthNoteTime);
    playSound(snare, time + 6 * eighthNoteTime);

    // Play the hi-hat every eighth note.
    for (var i = 0; i < 8; ++i) {
    playSound(hihat, time + i * eighthNoteTime);
    }
}

Di sini, kita hanya membuat satu pengulangan, bukan loop tanpa batas yang kita lihat di partitur. Fungsi playSound adalah metode yang memutar buffer pada waktu yang ditentukan, sebagai berikut:

function playSound(buffer, time) {
    var source = context.createBufferSource();
    source.buffer = buffer;
    source.connect(context.destination);
    source.noteOn(time);
}

Mengubah volume suara

Salah satu operasi paling dasar yang mungkin ingin Anda lakukan terhadap suara adalah mengubah volumenya. Dengan menggunakan Web Audio API, kita dapat merutekan sumber ke tujuannya melalui AudioGainNode untuk memanipulasi volume:

Grafik audio dengan node gain
Grafik audio dengan node gain

Penyiapan koneksi ini dapat dilakukan sebagai berikut:

// Create a gain node.
var gainNode = context.createGainNode();
// Connect the source to the gain node.
source.connect(gainNode);
// Connect the gain node to the destination.
gainNode.connect(context.destination);

Setelah grafik disiapkan, Anda dapat mengubah volume secara terprogram dengan memanipulasi gainNode.gain.value sebagai berikut:

// Reduce the volume.
gainNode.gain.value = 0.5;

Cross-fade antara dua suara

Sekarang, misalkan kita memiliki skenario yang sedikit lebih kompleks, ketika kita memutar beberapa suara, tetapi ingin memudar di antara suara tersebut. Ini adalah kasus umum dalam aplikasi seperti DJ, dengan dua turntable dan ingin dapat menggeser dari satu sumber suara ke sumber suara lainnya.

Hal ini dapat dilakukan dengan grafik audio berikut:

Grafik audio dengan dua sumber yang terhubung melalui node gain
Grafik audio dengan dua sumber yang terhubung melalui node gain

Untuk menyiapkannya, kita cukup membuat dua AudioGainNodes, dan menghubungkan setiap sumber melalui node, menggunakan fungsi seperti ini:

function createSource(buffer) {
    var source = context.createBufferSource();
    // Create a gain node.
    var gainNode = context.createGainNode();
    source.buffer = buffer;
    // Turn on looping.
    source.loop = true;
    // Connect source to gain.
    source.connect(gainNode);
    // Connect gain to destination.
    gainNode.connect(context.destination);

    return {
    source: source,
    gainNode: gainNode
    };
}

Crossfading daya yang sama

Pendekatan crossfade linear naif menunjukkan penurunan volume saat Anda menggeser di antara sampel.

Crossfade linear
Crossfade linear

Untuk mengatasi masalah ini, kami menggunakan kurva daya yang sama, dengan kurva penguatan yang sesuai bersifat non-linear, dan berpotongan pada amplitudo yang lebih tinggi. Hal ini meminimalkan penurunan volume di antara region audio, sehingga menyebabkan crossfade yang lebih merata di antara region yang mungkin sedikit berbeda levelnya.

Crossfade daya yang sama.
Crossfade daya yang sama

Crossfade playlist

Aplikasi crossfader umum lainnya adalah untuk aplikasi pemutar musik. Saat lagu berubah, kita ingin memudarkan trek saat ini, dan memudarkan trek baru, untuk menghindari transisi yang mengganggu. Untuk melakukannya, jadwalkan crossfade ke masa mendatang. Meskipun kita dapat menggunakan setTimeout untuk melakukan penjadwalan ini, tidak tepat. Dengan Web Audio API, kita dapat menggunakan antarmuka AudioParam untuk menjadwalkan nilai mendatang untuk parameter seperti nilai gain AudioGainNode.

Dengan demikian, dengan playlist, kita dapat bertransisi antar-trek dengan menjadwalkan penurunan gain pada trek yang sedang diputar, dan peningkatan gain pada trek berikutnya, keduanya sedikit sebelum trek saat ini selesai diputar:

function playHelper(bufferNow, bufferLater) {
    var playNow = createSource(bufferNow);
    var source = playNow.source;
    var gainNode = playNow.gainNode;
    var duration = bufferNow.duration;
    var currTime = context.currentTime;
    // Fade the playNow track in.
    gainNode.gain.linearRampToValueAtTime(0, currTime);
    gainNode.gain.linearRampToValueAtTime(1, currTime + ctx.FADE_TIME);
    // Play the playNow track.
    source.noteOn(0);
    // At the end of the track, fade it out.
    gainNode.gain.linearRampToValueAtTime(1, currTime + duration-ctx.FADE_TIME);
    gainNode.gain.linearRampToValueAtTime(0, currTime + duration);
    // Schedule a recursive track change with the tracks swapped.
    var recurse = arguments.callee;
    ctx.timer = setTimeout(function() {
    recurse(bufferLater, bufferNow);
    }, (duration - ctx.FADE_TIME) - 1000);
}

Web Audio API menyediakan kumpulan metode RampToValue yang mudah untuk berangsur-angsur mengubah nilai parameter, seperti linearRampToValueAtTime dan exponentialRampToValueAtTime.

Meskipun fungsi pengaturan waktu transisi dapat dipilih dari fungsi linear dan eksponensial bawaan (seperti di atas), Anda juga dapat menentukan kurva nilai Anda sendiri melalui array nilai menggunakan fungsi setValueCurveAtTime.

Menerapkan efek filter sederhana ke suara

Grafik audio dengan BiquadFilterNode
Grafik audio dengan BiquadFilterNode

Web Audio API memungkinkan Anda menyalurkan suara dari satu node audio ke node audio lainnya, sehingga membuat rantai pemroses yang berpotensi kompleks untuk menambahkan efek kompleks ke soundform Anda.

Salah satu cara untuk melakukannya adalah dengan menempatkan BiquadFilterNode di antara sumber dan tujuan suara Anda. Jenis node audio ini dapat melakukan berbagai filter urutan rendah yang dapat digunakan untuk membuat equalizer grafis dan bahkan efek yang lebih kompleks, sebagian besar berkaitan dengan memilih bagian dari spektrum frekuensi suara yang akan ditekankan dan mana yang akan ditundukkan.

Jenis filter yang didukung meliputi:

  • Filter lewat rendah
  • Filter high pass
  • Filter band pass
  • Filter rak rendah
  • Filter rak tinggi
  • Filter puncak
  • Filter lekukan
  • Filter semua kartu

Selain itu, semua filter menyertakan parameter untuk menentukan sejumlah gain, frekuensi penerapan filter, dan faktor kualitas. Filter low-pass mempertahankan rentang frekuensi yang lebih rendah, tetapi menghapus frekuensi tinggi. Titik jeda ditentukan oleh nilai frekuensi, dan faktor Q tidak memiliki satuan, serta menentukan bentuk grafik. Gain hanya memengaruhi filter tertentu, seperti filter low-shelf dan peaking, bukan filter low-pass ini.

Mari kita siapkan filter low-pass sederhana untuk hanya mengekstrak basis dari contoh suara:

// Create the filter
var filter = context.createBiquadFilter();
// Create the audio graph.
source.connect(filter);
filter.connect(context.destination);
// Create and specify parameters for the low-pass filter.
filter.type = 0; // Low-pass filter. See BiquadFilterNode docs
filter.frequency.value = 440; // Set cutoff to 440 HZ
// Playback the sound.
source.noteOn(0);

Secara umum, kontrol frekuensi perlu disesuaikan agar berfungsi dalam skala logaritmik karena pendengaran manusia bekerja dengan prinsip yang sama (yaitu, A4 adalah 440 hz, dan A5 adalah 880 hz). Untuk detail selengkapnya, lihat fungsi FilterSample.changeFrequency di link kode sumber di atas.

Terakhir, perhatikan bahwa kode contoh memungkinkan Anda menghubungkan dan memutuskan filter, sehingga mengubah grafik AudioContext secara dinamis. Kita dapat memutuskan hubungan AudioNodes dari grafik dengan memanggil node.disconnect(outputNumber). Misalnya, untuk merutekan ulang grafik dari melalui filter, ke koneksi langsung, kita dapat melakukan hal berikut:

// Disconnect the source and filter.
source.disconnect(0);
filter.disconnect(0);
// Connect the source directly.
source.connect(context.destination);

Mendengarkan lebih lanjut

Kita telah membahas dasar-dasar API, termasuk memuat dan memutar contoh audio. Kami telah membuat grafik audio dengan node dan filter gain, serta suara terjadwal dan penyesuaian parameter audio untuk mengaktifkan beberapa efek suara umum. Pada tahap ini, Anda siap untuk membuat beberapa aplikasi audio web yang keren.

Jika Anda mencari inspirasi, banyak developer telah membuat karya yang luar biasa menggunakan Web Audio API. Beberapa favorit saya meliputi:

  • AudioJedit, alat penyambungan suara dalam browser yang menggunakan permalink SoundCloud.
  • ToneCraft, sequencer suara tempat suara dibuat dengan menumpuk blok 3D.
  • Plink, game pembuatan musik kolaboratif menggunakan Audio Web dan Web Sockets.