قبل ظهور عنصر <audio> في HTML5، كان من الضروري استخدام Flash أو مكوّن إضافي آخر
لإصدار الأصوات على الويب. على الرغم من أنّ الصوت على الويب لم يعُد يتطلّب مكوّنًا إضافيًا، تفرض علامة الصوت قيودًا كبيرة على تنفيذ الألعاب المتطوّرة والتطبيقات التفاعلية.
Web Audio API هي واجهة برمجة تطبيقات JavaScript عالية المستوى لمعالجة المحتوى الصوتي وتوليفه في تطبيقات الويب. الهدف من واجهة برمجة التطبيقات هذه هو تضمين الإمكانات المتوفّرة في محرّكات الصوت الحديثة للألعاب وبعض مهام المزج والمعالجة والفلترة المتوفّرة في تطبيقات إنتاج الصوت الحديثة على أجهزة الكمبيوتر المكتبي. في ما يلي مقدمة بسيطة حول كيفية استخدام واجهة برمجة التطبيقات الفعّالة هذه.
بدء استخدام AudioContext
يُستخدَم AudioContext لإدارة جميع الأصوات وتشغيلها. لإنتاج
صوت باستخدام Web Audio API، أنشئ مصدرًا واحدًا أو أكثر للصوت
واربطها بوجهة الصوت التي يوفّرها مثيل AudioContext. ولا يلزم أن يكون هذا الاتصال مباشرًا، ويمكن أن يمر عبر أي عدد من AudioNodes الوسيطة التي تعمل كوحدات معالجة للإشارة الصوتية. يمكنك الاطّلاع على تفاصيل أكثر حول التوجيه في مواصفات Web Audio.
يمكن لمثيل واحد من AudioContext أن يدعم مدخلات صوتية متعددة
ورسومات صوتية معقدة، لذلك سنحتاج فقط إلى واحد من هذه لكل
تطبيق صوتي نقوم بإنشائه.
ينشئ المقتطف التالي 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');
}
}
بالنسبة إلى المتصفّحات القديمة المستندة إلى WebKit، استخدِم البادئة webkit، كما هو الحال مع
webkitAudioContext.
تتوفّر العديد من وظائف Web Audio API المثيرة للاهتمام، مثل إنشاء AudioNodes وفك ترميز بيانات الملفات الصوتية، ضمن AudioContext.
جارٍ تحميل الأصوات
تستخدم واجهة برمجة التطبيقات Web Audio واجهة AudioBuffer للأصوات القصيرة إلى المتوسطة الطول. النهج الأساسي هو استخدام XMLHttpRequest لجلب الملفات الصوتية.
تتيح واجهة برمجة التطبيقات تحميل بيانات الملفات الصوتية بتنسيقات متعددة، مثل WAV وMP3 والترميز المتقدّم للصوت وOGG وغيرها. يختلف توافق المتصفّحات مع تنسيقات الصوت المختلفة.
يوضّح المقتطف التالي كيفية تحميل عيّنة صوتية:
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();
}
بيانات الملف الصوتي ثنائية (وليست نصية)، لذا نضبط responseType
الطلب على 'arraybuffer'. لمزيد من المعلومات حول
ArrayBuffers، يُرجى الاطّلاع على هذه المقالة حول XHR2.
Once the (undecoded) audio file data has been received, it can be kept
around for later decoding, or it can be decoded right away using the
AudioContext decodeAudioData() method. تأخذ هذه الطريقة
ArrayBuffer من بيانات الملف الصوتي المخزّنة في request.response
وتفكّ ترميزها بشكل غير متزامن (بدون حظر سلسلة تنفيذ JavaScript
الرئيسية).
عند انتهاء decodeAudioData()، يتم استدعاء دالة رد اتصال توفّر بيانات صوت PCM التي تم فك ترميزها كـ AudioBuffer.
تشغيل الأصوات
بعد تحميل ملف واحد أو أكثر من ملفات AudioBuffers، نصبح جاهزين لتشغيل
الأصوات. لنفترض أنّنا حمّلنا للتوّ AudioBuffer بصوت نباح كلب وأنّ عملية التحميل قد انتهت. بعد ذلك، يمكننا تشغيل هذا المخزن المؤقت
باستخدام التعليمة البرمجية التالية.
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
}
يمكن استدعاء دالة playSound() هذه في كل مرة يضغط فيها شخص ما على مفتاح أو
ينقر على شيء ما بالماوس.
تسهّل الدالة noteOn(time) جدولة تشغيل الصوت بدقة للألعاب والتطبيقات الأخرى التي تتطلب وقتًا دقيقًا. ومع ذلك، لكي تعمل هذه الجدولة بشكل صحيح، تأكَّد من تحميل مخازن الصوت المؤقتة مسبقًا.
استخلاص واجهة برمجة التطبيقات Web Audio API
بالطبع، سيكون من الأفضل إنشاء نظام تحميل أكثر عمومية لا يتم ترميزه بشكل ثابت لتحميل هذا الصوت المحدد. هناك العديد من الطرق للتعامل مع الأصوات القصيرة إلى المتوسطة الطول التي قد يستخدمها تطبيق صوتي أو لعبة، وإليك طريقة واحدة باستخدام BufferLoader (ليست جزءًا من معيار الويب).
في ما يلي مثال على كيفية استخدام فئة BufferLoader.
لننشئ عنصرَي AudioBuffers، وبمجرد تحميلهما،
لنقم بتشغيلهما في الوقت نفسه.
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);
}
التعامل مع الوقت: تشغيل الأصوات بإيقاع
تتيح واجهة Web Audio API للمطوّرين جدولة التشغيل بدقة. لتوضيح ذلك، لنقم بإعداد مسار إيقاع بسيط. ربما يكون نمط مجموعة الطبول الأكثر شهرة هو التالي:
يتم فيه عزف الصنج المزدوج كل ثُمن نغمة، ويتم عزف الطبلة الكبيرة والطبلة الجانبية بالتناوب كل ربع نغمة، في إيقاع 4/4.
لنفترض أنّنا حمّلنا المخازن المؤقتة kick وsnare وhihat،
فإنّ الرمز البرمجي لتنفيذ ذلك بسيط:
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);
}
}
هنا، نقوم بتكرار واحد فقط بدلاً من الحلقة غير المحدودة التي نراها في
النوتة الموسيقية. الدالة playSound هي طريقة لتشغيل
مخزن مؤقت في وقت محدّد، على النحو التالي:
function playSound(buffer, time) {
var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.noteOn(time);
}
تغيير مستوى صوت معيّن
من العمليات الأساسية التي قد ترغب في إجرائها على الصوت تغيير مستوى الصوت. باستخدام Web Audio API، يمكننا توجيه المصدر إلى وجهته من خلال AudioGainNode من أجل معالجة مستوى الصوت:

يمكن إعداد هذا الربط على النحو التالي:
// 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);
بعد إعداد الرسم البياني، يمكنك تغيير مستوى الصوت آليًا من خلال معالجة gainNode.gain.value على النحو التالي:
// Reduce the volume.
gainNode.gain.value = 0.5;
التلاشي المتقاطع بين صوتَين
لنفترض الآن أنّ لدينا سيناريو أكثر تعقيدًا، حيث يتم تشغيل أصوات متعددة ولكن نريد التلاشي المتقاطع بينها. هذه حالة شائعة في تطبيق يشبه DJ، حيث لدينا قرصان دواران ونريد أن نكون قادرين على التحريك من مصدر صوت إلى آخر.
ويمكن إجراء ذلك باستخدام الرسم البياني الصوتي التالي:
لإعداد ذلك، ننشئ ببساطة عقدتَي AudioGainNodes، ونربط كل مصدر من خلال العقد، باستخدام دالة مثل هذه:
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
};
}
تلاشٍ متداخل متساوي القوة
يُظهر نهج التلاشي الخطي البسيط انخفاضًا في مستوى الصوت أثناء التنقل بين العينات.
لمعالجة هذه المشكلة، نستخدم منحنى طاقة متساوية، حيث تكون منحنيات الكسب المقابلة غير خطية، وتتقاطع عند سعة أعلى. يؤدي ذلك إلى تقليل انخفاض مستوى الصوت بين المقاطع الصوتية، ما يؤدي إلى تلاشي متداخل أكثر توازنًا بين المقاطع التي قد تختلف قليلاً في المستوى.
التداخل التدريجي لقوائم التشغيل
هناك تطبيق شائع آخر لـ crossfader وهو تطبيق مشغل الموسيقى.
عندما تتغير أغنية، نريد أن يتلاشى المسار الحالي، ويتلاشى المسار
الجديد، لتجنب الانتقال المزعج. لإجراء ذلك، يمكنك جدولة
تلاشٍ متداخل في المستقبل. على الرغم من أنّه يمكننا استخدام setTimeout لإجراء هذه الجدولة، إلا أنّ ذلك ليس دقيقًا. باستخدام Web Audio API، يمكننا
استخدام واجهة AudioParam لجدولة القيم المستقبلية
لمَعلمات مثل قيمة الكسب في AudioGainNode.
وبالتالي، عند توفّر قائمة تشغيل، يمكننا الانتقال بين المقاطع الصوتية من خلال جدولة انخفاض في مستوى الصوت في المقطع الصوتي الذي يتم تشغيله حاليًا، وزيادة في مستوى الصوت في المقطع الصوتي التالي، قبل انتهاء تشغيل المقطع الصوتي الحالي بقليل:
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 مجموعة ملائمة من طرق RampToValue لتغيير قيمة مَعلمة تدريجيًا، مثل linearRampToValueAtTime وexponentialRampToValueAtTime.
While the transition timing function can be picked from built-in linear
and exponential ones (as above), you can also specify your own value
curve via an array of values using the setValueCurveAtTime function.
تطبيق تأثير فلتر بسيط على صوت

تتيح لك واجهة برمجة التطبيقات Web Audio API نقل الصوت من عقدة صوتية إلى أخرى، مما يؤدي إلى إنشاء سلسلة معقدة من المعالجات لإضافة تأثيرات معقدة إلى أشكال الصوت.
تتمثل إحدى طرق القيام بذلك في وضع BiquadFilterNodes بين مصدر الصوت والوجهة. يمكن لهذا النوع من عُقد الصوت تنفيذ مجموعة متنوعة من الفلاتر ذات الترتيب المنخفض التي يمكن استخدامها لإنشاء معادِلات رسومية وتأثيرات أكثر تعقيدًا، ويتعلق معظمها باختيار أجزاء من الطيف الترددي للصوت التي يجب التركيز عليها والتي يجب إخضاعها.
تشمل أنواع الفلاتر المتوافقة ما يلي:
- فلتر الترددات المنخفضة
- فلتر التمرير العالي
- فلتر تمرير النطاق
- فلتر الرف المنخفض
- فلتر الترددات العالية
- فلتر الذروة
- فلتر النطاق الضيق
- فلتر تمرير كامل
وتتضمّن جميع الفلاتر مَعلمات لتحديد مقدار معيّن من الكسب، والتردد الذي يتم تطبيق الفلتر عليه، وعامل الجودة. يحتفظ الفلتر المنخفض التردد بنطاق التردد المنخفض، ولكنه يتجاهل الترددات العالية. يتم تحديد نقطة الانقطاع من خلال قيمة التردد، وعامل الجودة ليس له وحدة، ويحدد شكل الرسم البياني. يؤثر الكسب فقط على فلاتر معينة، مثل فلاتر الرف المنخفض والذروة، وليس على مرشح التمرير المنخفض هذا.
لنقم بإعداد مرشح تمرير منخفض بسيط لاستخراج القواعد فقط من عينة صوتية:
// 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);
بشكل عام، يجب تعديل عناصر التحكّم في التردد لتعمل على مقياس لوغاريتمي لأنّ السمع البشري نفسه يعمل على المبدأ نفسه (أي أنّ A4 هو 440 هرتز وA5 هو 880 هرتز). لمزيد من التفاصيل، يُرجى الاطّلاع على دالة FilterSample.changeFrequency في رابط الرمز المصدر أعلاه.
أخيرًا، يُرجى العِلم أنّ الرمز النموذجي يتيح لك ربط الفلتر وإلغاء ربطه،
ما يؤدي إلى تغيير رسم AudioContext البياني بشكل ديناميكي. يمكننا إلغاء ربط
AudioNodes من الرسم البياني من خلال استدعاء node.disconnect(outputNumber).
على سبيل المثال، لإعادة توجيه الرسم البياني من المرور عبر فلتر إلى
اتصال مباشر، يمكننا إجراء ما يلي:
// Disconnect the source and filter.
source.disconnect(0);
filter.disconnect(0);
// Connect the source directly.
source.connect(context.destination);
محتوى إضافي للاستماع إليه
لقد تناولنا أساسيات واجهة برمجة التطبيقات، بما في ذلك تحميل عيّنات الصوت وتشغيلها. لقد أنشأنا رسومات بيانية صوتية باستخدام عُقد وفلاتر الكسب، وجدولنا الأصوات وتعديلات معلَمات الصوت لتفعيل بعض المؤثرات الصوتية الشائعة. في هذه المرحلة، أنت مستعد لإنشاء بعض تطبيقات الويب الصوتية الرائعة!
إذا كنت تبحث عن أفكار ملهمة، فقد أنشأ العديد من المطوّرين أعمالاً رائعة باستخدام Web Audio API. تشمل بعض الميزات المفضّلة لديّ ما يلي:
- AudioJedit، وهي أداة لدمج الصوت في المتصفّح تستخدم الروابط الثابتة في SoundCloud
- ToneCraft، وهو منظّم تسلسل صوتي يتم فيه إنشاء الأصوات من خلال تجميع كتل ثلاثية الأبعاد
- Plink، وهي لعبة تعاونية لإنشاء الموسيقى باستخدام Web Audio وWeb Sockets