HTML5 <audio>
öğesinden önce web'in sessizliğini kırmak için Flash veya başka bir eklenti gerekiyordu. Web'de ses için artık eklenti gerekmese de ses etiketi, gelişmiş oyunların ve etkileşimli uygulamaların uygulanmasıyla ilgili önemli sınırlamalar getirir.
Web Audio API, web uygulamalarında sesi 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 özelliklerin yanı sıra modern masaüstü ses üretimi uygulamalarında bulunan bazı karıştırma, işleme ve filtreleme görevlerini kapsamaktır. Şimdi, bu güçlü API'yi kullanmaya nasıl başlayacağınıza bakalım.
AudioContext'i kullanmaya başlama
AudioContext, tüm sesleri yönetmek ve çalmak içindir. Web Audio API'sını kullanarak ses üretmek 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şleme modülü görevi gören çok sayıda ara AudioNodes üzerinden geçebilir. Bu yönlendirme, Web Sesi spesifikasyonunda daha ayrıntılı olarak açıklanmıştır.
Tek bir AudioContext
örneği, birden çok ses girişini ve karmaşık ses grafiklerini destekleyebilir. Bu nedenle, oluşturduğumuz her ses uygulaması için bunlardan yalnızca bir tanesine 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ı daha eski tarayıcılarda, webkitAudioContext
gibi webkit
önekini kullanın.
AudioNodes oluşturma ve ses dosyası verilerinin kodunu çözme gibi birçok ilgi çekici Web Audio API işlevi, AudioContext
yöntemidir.
Sesler yükleniyor
Web Audio API, kısa ve orta uzunlukta sesler için bir AudioBuffer kullanır. Temel yaklaşım, ses dosyalarını getirmek için XMLHttpRequest işlevini kullanmaktır.
API, ses dosyası verilerinin WAV, MP3, AAC, OGG ve diğerleri gibi birden fazla biçimde yüklenmesini destekler. Farklı ses biçimleri için tarayıcı desteği değişiklik gösterir.
Aşağıdaki snippet'te bir ses örneğinin yüklenmesi 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 verilerdir (metin değil). Bu nedenle, isteğin responseType
değerini 'arraybuffer'
olarak ayarladık. ArrayBuffers
hakkında daha fazla bilgi için XHR2 ile ilgili bu makaleyi inceleyin.
Kodu çözülen ses dosyası verileri alındıktan sonra, daha sonra kod çözme işlemi için bir yerde saklanabilir veya AudioContext decodeAudioData()
yöntemi kullanılarak kodu hemen çözülebilir. Bu yöntem, request.response
içinde depolanan ses dosyası verilerinin ArrayBuffer
kadarını alır ve eşzamansız olarak bu verilerin kodunu çözer (ana JavaScript yürütme iş parçacığını engellemez).
decodeAudioData()
işlemi bittiğinde, kodu çözülmüş PCM ses verilerini AudioBuffer
olarak sağlayan bir geri çağırma işlevi çağırır.
Ses çalınıyor
Bir veya daha fazla AudioBuffers
yüklendikten sonra ses çalmaya hazır hale geliriz. AudioBuffer
adlı cihaza köpek havlaması sesi
yüklediğimizi ve yüklemenin tamamlandığını varsayalım. Ardından bu tamponu aşağıdaki
kodla 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, birisi fare ile bir tuşa bastığında veya bir öğeyi her tıkladığında çağrılabilir.
noteOn(time)
işlevi, oyunlar ve zaman açısından kritik diğer uygulamalar için hassas ses oynatma planlamalarını kolaylaştırır. Ancak bu planlamanın düzgün çalışması için ses arabelleklerinizin önceden yüklendiğinden emin olun.
Web Audio API'sını Soyutlama
Elbette, bu sesi yüklemek için kodu sabit bir şekilde yüklemeyen, daha genel bir yükleme sistemi oluşturmak daha iyi olur. Bir ses uygulamasının veya oyunun kullanacağı çok sayıda kısa ve orta uzunlukta sesle başa çıkmak için birçok yaklaşım vardır. İşte BufferLoader kullanmanın bir yolu (web standardının parçası değildir).
Aşağıda, BufferLoader
sınıfını nasıl kullanabileceğinize dair bir örnek verilmiştir.
İki AudioBuffers
oluşturalım ve yüklenir yüklenmez
bunları da 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);
}
Zamana ayak uydurma: Sesleri ritimli çalma
Web Audio API, geliştiricilerin çalmayı hassas bir şekilde programlamasına olanak tanır. Bunu göstermek için basit bir ritim parçası oluşturalım. Muhtemelen en yaygın olarak bilinen drumkit kalıbı şöyledir:
Sekizde bir notada bir hihat, 4/4'lük sürede ise her üç ayda bir sırayla vuruş ve trampet çalınır.
kick
, snare
ve hihat
arabelleklerini yüklediğimizi varsayarsak bunu yapmak için gereken 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, notalarda gördüğümüz sınırsız döngü yerine
yalnızca bir tekrarı yaparız. playSound
işlevi, aşağıdaki gibi belirli bir zamanda bir tampon oynatan yöntemdir:
function playSound(buffer, time) {
var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.noteOn(time);
}
Sesin ses düzeyini değiştirme
Bir ses üzerinde yapmak isteyebileceğiniz en temel işlemlerden biri ses düzeyini değiştirmektir. Web Audio API'sını kullanarak, sesi değiştirmek için kaynağımızı bir AudioGainNode aracılığıyla hedefine yönlendirebiliriz:
Bu bağlantı kurulumu şu ş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 ayarlandıktan sonra gainNode.gain.value
değerini aşağıdaki gibi değiştirerek ses düzeyini programlı bir şekilde değiştirebilirsiniz:
// Reduce the volume.
gainNode.gain.value = 0.5;
İki sesin kesişmesi
Şimdi, birden fazla ses çaldığımız ancak bunların arasında geçiş yapmak istediğimiz biraz daha karmaşık bir senaryomuz olduğunu varsayalım. Bu durum, iki pikapa sahip olduğumuz ve bir ses kaynağından diğerine yatay kaydırma yapmak istediğimiz DJ benzeri uygulamalarda yaygın bir durumdur.
Bunun için aşağıdaki ses grafiği kullanılabilir:
Bunu ayarlamak için iki AudioGainNodes oluşturmamız ve aşağıdaki gibi bir işlev kullanarak her bir kaynağı düğümler üzerinden bağlamanız yeterlidir:
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
Naif doğrusal çapraz geçiş yaklaşımı, örnekler arasında gezinirken hacmin düştüğünü gösterir.
Bu sorunu çözmek için karşılık gelen kazanç eğrilerinin doğrusal olmadığı ve daha yüksek bir genlikle kesiştiği eşit bir güç eğrisi kullanırız. Bu, ses bölgeleri arasındaki ses düşüşlerini en aza indirerek ses seviyesi biraz farklı olabilecek bölgeler arasında daha eşit geçiş sağlar.
Oynatma listesi çapraz geçişi
Diğer bir yaygın çapraz geçiş uygulaması, müzik çalar uygulaması içindir.
Bir şarkı değiştiğinde, sarsıcı bir geçişin önüne geçmek için mevcut parçanın yerini yavaşça azaltmak ve yeni olanı yavaşça soluklamak istiyoruz. Bunu yapmak için, geleceğe
geçiş planlayın. Bu planlamada setTimeout
kullanılabilir ancak bu kesin değildir. Web Audio API ile AudioGainNode
'nin kazanç değeri gibi parametrelerin gelecekteki değerlerini planlamak için AudioParam arayüzünü kullanabiliriz.
Böylece, oynatma listesi verildiğinde, o anda çalan parçada düşüş planlayarak parçalar arasında geçiş yapabiliriz. Aynı zamanda mevcut parçanın çalınması tamamlanmadan biraz önce bir sonraki parça için artış planlayabiliriz:
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 sunar.
Geçiş zamanlama işlevi yerleşik doğrusal ve üstel olanlardan (yukarıda gösterildiği gibi) seçilebilir ancak setValueCurveAtTime
işlevini kullanarak bir değer dizisi aracılığıyla kendi değer eğrinizi de belirtebilirsiniz.
Seslere basit bir filtre efekti uygulama
Web Audio API, sesi bir ses düğümünden diğerine aktarmanıza olanak tanıyarak ses formlarınıza karmaşık efektler eklemek için karmaşık olabilecek bir işlemci zinciri oluşturmanızı sağlar.
Bunu yapmanın bir yolu, ses kaynağınızla hedef arasına BiquadFilterNode yerleştirmektir. Bu tür ses düğümü, grafik ekolayzerler ve hatta daha karmaşık efektler oluşturmak için kullanılabilecek düşük düzeyli çeşitli filtreler yapabilir. Bu filtreler, çoğunlukla sesin frekans spektrumunun hangi kısımlarının vurgulanacağını ve hangilerinin azaltılacağını seçmeyle ilgilidir.
Desteklenen filtre türleri şunlardır:
- Düşük geçiş filtresi
- Yüksek geçiş filtresi
- Bant kartı filtresi
- Alt raf filtresi
- Üst sınıf raf filtresi
- Odak boyama filtresi
- Çentik filtresi
- Tüm kartlar filtresi
Tüm filtreler de bir miktar kazanç, filtrenin uygulanma sıklığı ve bir kalite faktörü belirtecek parametreler içerir. Alçak geçiş filtresi düşük frekans aralığını korur ancak yüksek frekansları siler. Kırılma noktası, frekans değeriyle belirlenir. Q faktörü de birimsizdir ve grafiğin şeklini belirler. Kazanç, bu düşük geçiren filtreyi değil, yalnızca düşük raf ve tepe noktası filtreleri gibi belirli filtreleri etkiler.
Bir ses örneğinden yalnızca bazları çıkarmak için basit bir düşük geçiş filtresi 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 işitmesi aynı ilkeye göre çalıştığından (yani, A4 440 Hz ve A5 880 Hz'dir) frekans kontrollerinin logaritmik ölçekte çalışması için ince ayar yapılması gerekir. Daha fazla bilgi için yukarıdaki kaynak kodu bağlantısında FilterSample.changeFrequency
işlevine bakın.
Son olarak, örnek kodun, Audio Bağlam grafiğini dinamik olarak değiştirerek filtreyi bağlayıp bağlantısını kesebilmenizi sağladığını unutmayın. node.disconnect(outputNumber)
yöntemini çağırarak AudioNodes'un grafikle bağlantısını kesebiliriz.
Örneğin, grafiği bir filtreden doğrudan bağlantıya yeniden 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 örneklerini yükleme ve çalma da dahil olmak üzere API'nin temel özelliklerini ele aldık. Kazanç düğümleri ve filtreler içeren sesli grafikler oluşturduk ve bazı yaygın ses efektlerini etkinleştirmek için planlanmış sesler ve ses parametresi ayarlamaları yaptık. Artık web'den güzel ses uygulamaları oluşturmaya hazırsınız!
İlham arıyorsanız birçok geliştirici Web Audio API'sını kullanarak zaten harika işler ortaya koymuştur. En beğendiklerim şunlardan bazıları:
- AudioJedit, SoundCloud kalıcı bağlantılarını kullanan bir tarayıcı içi ses birleştirme aracıdır.
- ToneCraft, seslerin 3D bloklar üst üste yığılarak oluşturulduğu bir ses sıralayıcıdır.
- Plink: Web Audio ve WebSockets kullanan, ortak çalışmaya dayalı bir müzik yapma oyunu.