Web Audio API'sını kullanmaya başlama

Boris Smus
Boris Smus

HTML5 <audio> öğesinden önce, web'in sessizliğini bozmak için Flash veya başka bir eklenti gerekiyordu. Web'de ses için artık eklenti gerekmese de ses etiketi, karmaşık oyunlar ve etkileşimli uygulamalar uygulama konusunda önemli sınırlamalar getirir.

Web Audio API, web uygulamalarında sesleri işlemek ve sentezlemek için kullanılan üst düzey bir JavaScript API'sidir. Bu API'nin amacı, modern oyun ses motorlarında bulunan özellikleri ve modern masaüstü ses prodüksiyon uygulamalarında bulunan bazı karıştırma, işleme ve filtreleme görevlerini içermektir. Aşağıda, bu güçlü API'nin kullanımına dair basit bir giriş verilmiştir.

AudioContext, tüm sesleri yönetmek ve çalmak için kullanılır. Web Audio API'yi kullanarak ses oluşturmak için bir veya daha fazla ses kaynağı oluşturun ve bunları AudioContext örneği tarafından sağlanan ses hedefine bağlayın. Bu bağlantının doğrudan olması gerekmez ve ses sinyali için işlem modülü görevi gören herhangi bir sayıda ara AudioNodes üzerinden geçebilir. Bu yönlendirme, Web Audio spesifikasyonunda daha ayrıntılı olarak açıklanmaktadır.

Tek bir AudioContext örneği, birden fazla ses girişini ve karmaşık ses grafiklerini destekleyebilir. Bu nedenle, oluşturduğumuz her ses uygulaması için bunlardan yalnızca birine ihtiyacımız olacaktır.

Aşağıdaki snippet, bir AudioContext oluşturur:

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');
   
}
}

WebKit tabanlı eski tarayıcılarda webkitAudioContext ile olduğu gibi webkit ön ekini kullanın.

AudioNode oluşturma ve ses dosyası verilerinin kodunu çözme gibi ilgi çekici Web Audio API işlevlerinin çoğu AudioContext yöntemlerindendir.

Sesler yükleniyor

Web Audio API, kısa ve orta uzunluktaki sesler için AudioBuffer kullanır. Temel yaklaşım, ses dosyalarını almak için XMLHttpRequest kullanmaktır.

API, ses dosyası verilerinin WAV, MP3, AAC, OGG ve diğer gibi birden çok biçimde yüklenmesini destekler. Farklı ses biçimleri için tarayıcı desteği değişir.

Aşağıdaki snippet'te ses örneği yükleme gösterilmektedir:

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();
}

Ses dosyası verileri ikili olduğundan (metin değil) isteğin responseType değerini 'arraybuffer' olarak belirleriz. ArrayBuffers hakkında daha fazla bilgi için bu XHR2 makalesine göz atın.

(Kodlanmamış) ses dosyası verileri alındıktan sonra daha sonra kod çözme işlemi için saklanabilir veya AudioContext decodeAudioData() yöntemi kullanılarak hemen kod çözme işlemi yapılabilir. Bu yöntem, request.response içinde depolanan ses dosyası verilerinin ArrayBuffer'ünü alır ve bu verileri eşzamansız olarak kod çözer (ana JavaScript yürütme iş parçacısını engellemez).

decodeAudioData() tamamlandığında, kod çözülmüş PCM ses verilerini AudioBuffer olarak sağlayan bir geri çağırma işlevi çağırır.

Ses çal

Basit bir ses grafiği
Basit bir ses grafiği

Bir veya daha fazla AudioBuffers yüklendikten sonra ses çalmaya hazır oluruz. Bir köpeğin havlama sesini içeren bir AudioBuffer yüklediğimizi ve yüklemenin tamamlandığını varsayalım. Ardından, aşağıdaki kodla bu arabelleği oynatabiliriz.

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
}

Bu playSound() işlevi, kullanıcı bir tuşa her bastığı veya fareyle bir öğeyi her tıkladığı zaman çağrılabilir.

noteOn(time) işlevi, oyunlar ve zaman açısından kritik diğer uygulamalar için ses oynatmayı hassas bir şekilde planlamayı kolaylaştırır. Ancak bu planlamanın düzgün çalışması için ses arabelleklerinizin önceden yüklenmiş olduğundan emin olun.

Web Audio API'yi soyutlama

Elbette, bu belirli sesi yüklemek için sabit kodlanmamış daha genel bir yükleme sistemi oluşturmak daha iyi olur. Bir ses uygulamasının veya oyunun kullanacağı kısa ve orta uzunluktaki seslerle ilgili birçok yaklaşım vardır. Burada, BufferLoader (web standardının bir parçası değildir) kullanan bir yöntem gösterilmektedir.

Aşağıda, BufferLoader sınıfını nasıl kullanabileceğinize dair bir örnek verilmiştir. İki AudioBuffers oluşturalım ve yüklendikten sonra aynı anda oynatalım.

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);
}

Zamanla ilgili işlemler: Ritimle ses çalma

Web Audio API, geliştiricilerin oynatmayı hassas bir şekilde planlamasına olanak tanır. Bunu göstermek için basit bir ritim parçası oluşturalım. Muhtemelen en yaygın bilinen davul seti kalıbı aşağıdaki gibidir:

Basit bir rock davul ritmi
Basit bir rock davul ritmi

4/4 zaman ölçüsünde her sekizli nota için bir hi-hat, her çeyrek nota için de davul ve trampet çalınır.

kick, snare ve hihat tamponlarını yüklediğimizi varsayalım. Bunu yapmak için kullanacağımız kod basittir:

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);
   
}
}

Burada, nota kağıdında gördüğümüz sınırsız döngü yerine yalnızca bir tekrar yapıyoruz. playSound işlevi, aşağıdaki gibi belirli bir zamanda bir arabelleği çalan bir yöntemdir:

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

Bir sesin ses düzeyini değiştirme

Bir sesle yapmak isteyebileceğiniz en temel işlemlerden biri sesin seviyesini değiştirmektir. Web Audio API'yi kullanarak ses seviyesini değiştirmek için kaynağımızı bir AudioGainNode aracılığıyla hedefine yönlendirebiliriz:

Kazanç düğümü içeren ses grafiği
Güçlendirme düğümü içeren ses grafiği

Bu bağlantı kurulumu aşağıdaki şekilde yapılabilir:

// 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);

Grafik oluşturulduktan sonra gainNode.gain.value öğesini aşağıdaki gibi değiştirerek sesi programatik olarak değiştirebilirsiniz:

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

İki ses arasında geçiş yapma

Şimdi, birden fazla ses çaldığımız ancak bunlar arasında geçiş yapmak istediğimiz biraz daha karmaşık bir senaryoya sahip olduğumuzu varsayalım. Bu, iki pikapımız olduğu ve bir ses kaynağından diğerine geçiş yapmak istediğimiz DJ benzeri bir uygulamada sık karşılaşılan bir durumdur.

Bu işlem aşağıdaki ses grafiğiyle yapılabilir:

Kazanç düğümleri aracılığıyla bağlı iki kaynağın yer aldığı ses grafiği
Güç düğümleri aracılığıyla bağlı iki kaynağın yer aldığı ses grafiği

Bunu ayarlamak için iki AudioGainNodes oluşturup her kaynağı düğümler aracılığıyla bağlarız. Bunun için aşağıdaki işleve benzer bir işlev kullanırız:

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
   
};
}

Eşit güç geçişi

Basit bir doğrusal geçiş yaklaşımı, parçalar arasında pan yaptığınızda ses seviyesinde bir düşüş gösterir.

Doğrusal bir geçiş efekti
Doğrusal geçiş

Bu sorunu gidermek için, karşılık gelen kazanç eğrilerinin doğrusal olmayan ve daha yüksek bir genlikte kesiştiği eşit güç eğrisi kullanırız. Bu sayede ses bölgeleri arasındaki ses düşüşleri en aza indirilir. Böylece, seviyeleri biraz farklı olabilecek bölgeler arasında daha eşit bir geçiş elde edilir.

Eşit güç geçişi.
Eşit güçte bir geçiş

Oynatma listelerinde geçiş efekti

Geçiş efektleri, müzik çalar uygulamalarında da yaygın olarak kullanılır. Bir şarkı değiştiğinde, rahatsız edici bir geçiş olmaması için mevcut parçanın sesini azaltıp yeni parçanın sesini artırırız. Bunu yapmak için gelecekte bir geçiş planlayın. Bu planlamayı yapmak için setTimeout kullanabiliriz ancak bu kesin değildir. Web Audio API ile, bir AudioGainNode'nin kazanç değeri gibi parametreler için gelecekteki değerleri planlamak üzere AudioParam arayüzünü kullanabiliriz.

Bu nedenle, bir oynatma listesi verildiğinde, oynatılan parçanın bitmesinden kısa bir süre önce mevcut parçada kazanç azaltma ve sonraki parçada kazanç artırma planlayarak parçalar arasında geçiş yapabiliriz:

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, linearRampToValueAtTime ve exponentialRampToValueAtTime gibi bir parametrenin değerini kademeli olarak değiştirmek için kullanışlı bir RampToValue yöntemleri grubu sağlar.

Geçiş zamanlama işlevi, yerleşik doğrusal ve üstel işlevler arasından (yukarıda gösterildiği gibi) seçilebilir. Bununla birlikte, setValueCurveAtTime işlevini kullanarak bir değer dizisi aracılığıyla kendi değer eğrinizi de belirtebilirsiniz.

Bir sese basit bir filtre efekti uygulama

BiquadFilterNode içeren bir ses grafiği
BiquadFilterNode içeren bir ses grafiği

Web Audio API, sesi bir ses düğümünden diğerine aktarmanıza olanak tanır. Böylece, ses formlarınıza karmaşık efektler eklemek için karmaşık olabilecek bir işlemci zinciri oluşturabilirsiniz.

Bunu yapmanın bir yolu, ses kaynağınız ile hedefiniz arasına BiquadFilterNode'lar yerleştirmektir. Bu tür ses düğümleri, grafik eşitleyiciler ve hatta daha karmaşık efektler oluşturmak için kullanılabilecek çeşitli düşük dereceli filtreler yapabilir. Bu filtreler genellikle bir sesin frekans spektrumunun hangi kısımlarının vurgulanacağını ve hangilerinin azaltılacağını seçmekle ilgilidir.

Desteklenen filtre türleri şunlardır:

  • Düşük geçiren filtre
  • Yüksek geçiren filtre
  • Bant geçiren filtre
  • Düşük raf filtresi
  • Yüksek raf filtresi
  • Tepe noktası filtresi
  • Çentik filtresi
  • Tüm kart filtresi

Tüm filtreler, belirli bir miktarda kazanç, filtrenin uygulanacağı sıklık ve kalite faktörü belirtmek için parametreler içerir. Düşük geçiren filtre, düşük frekans aralığını korur ancak yüksek frekansları atar. Kesme noktası, sıklık değerine göre belirlenir ve Q faktörü, birimi olmayan ve grafiğin şeklini belirleyen bir değerdir. Kazanç, bu düşük geçiren filtreyi değil, yalnızca düşük raf ve tepe filtresi gibi belirli filtreleri etkiler.

Bir ses örneğinden yalnızca basları ayıklamak için basit bir düşük geçiren filtre oluşturalım:

// 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);

Genel olarak, insan kulağı da aynı prensiple çalıştığından (yani A4 440 Hz, A5 ise 880 Hz) frekans kontrollerinin logaritmik ölçekte çalışacak şekilde ayarlanması gerekir. Daha fazla bilgi için yukarıdaki kaynak kod bağlantısındaki FilterSample.changeFrequency işlevine bakın.

Son olarak, örnek kodun filtreyi bağlamanıza ve filtrenin bağlantısını kesmenize olanak tanıdığını, böylece AudioContext grafiğini dinamik olarak değiştirdiğini unutmayın. node.disconnect(outputNumber) işlevini çağırarak AudioNodes'ın grafikle olan bağlantısını kesebiliriz. Örneğin, grafiği bir filtreden doğrudan bağlantıya yönlendirmek için aşağıdakileri yapabiliriz:

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

Daha fazla dinleme

Ses kesitlerini yükleme ve çalma da dahil olmak üzere API'nin temel özelliklerini ele aldık. Bazı yaygın ses efektlerini etkinleştirmek için kazanç düğümleri ve filtreler içeren ses grafikleri, planlanmış sesler ve ses parametresi ayarlamaları oluşturduk. Bu noktada, harika web ses uygulamaları oluşturmaya hazırsınız.

İlham almak istiyorsanız Web Audio API'yi kullanan birçok geliştirici harika çalışmalar oluşturmuştur. En sevdiğim içeriklerden bazıları şunlardır:

  • SoundCloud kalıcı bağlantılarını kullanan tarayıcı içi ses birleştirme aracı AudioJedit.
  • 3D blokları üst üste yığarak seslerin oluşturulduğu bir ses sıralayıcı olan ToneCraft.
  • Web Audio ve Web Sockets'i kullanan ortak bir müzik yapma oyunu olan Plink.