
Элемент Audio имеет очень красивый интерфейс, но нам его надо расширить, потому напишем обертку:
var LibCanvasAudio = function (file) {
this.audio = new Audio;
this.audio.src = file;
};
LibCanvasAudio.prototype = {};
.ogg vs .mp3
Для начала — муть с поддерживаемыми кодеками. Большинство браузеров поддерживает ogg vorbis(для Оперы в Линуксе не забудьте установить gstreamer base и good плагины), но, например, Apple решил выебнуться. Давайте для вменяемых браузеров будем отдавать в .ogg, всем остальным — в .mp3. Все звездочки в имени файла будем заменять на ogg или mp3 соответственно:
var LibCanvasAudio = function (file) {
this.audio = new Audio;
this.src(file);
};
LibCanvasAudio.prototype = {
src : function (file) {
var codec = this.getSupport();
if (!codec) throw 'AudioNotSupported';
this.audio.src = file.replace(/\*/g, this.getSupport());
this.audio.load();
return this;
},
getSupport : function () {
return !this.audio.canPlayType ? false :
this.audio.canPlayType('audio/ogg;') ? 'ogg' :
this.audio.canPlayType('audio/mpeg;') ? 'mp3' : false;
}
}
Схема Гатлинга
Хорошо. Мы подошли к главной теме. Допустим, мы разрабатываем экшн. У нас огромное количество взрывов, выстрелов, етс. Допустим, один взрыв с эхо длится 5 секунд, а интервал между взрывами может быть 0.5 секунд. Если просто запускать файл сначала, то предыдущий взрыв резко оборвется. Мы могли бы клонировать элемент Audio каждый раз перед запуском, но так мы будем плодить кучу DOM-элементов. Прибилизительно через 5 минут игры все браузеры сходят с ума. Потому предлагаю воспользоваться схемой Гатлинга. Заносим определенное количество элементов в массив и вызываем их по-очереди. Пока сыграет последний элемент первый успеет закончиться. Главное — выставить достаточно количество «стволов» для каждого из звуков.
LibCanvasAudio.prototype = {
// ...
cloneAudio : function () {
audioClone = this.audio.cloneNode(true);
audioClone.load();
return audioClone;
},
gatling : function (count) {
this.barrels = [];
this.gatIndex = 0;
while (count--) {
this.barrels.push(this.cloneAudio());
}
return this;
},
getNext : function () {
var elem = this.barrels[this.gatIndex];
++this.gatIndex >= this.barrels.length && (this.gatIndex = 0);
return elem;
},
playNext : function () {
var elem = this.getNext();
elem.pause();
elem.currentTime = 0;
elem.play();
return this;
}
};
Интерфейс у нас получается приблизительно такой:
var shotSound = new LibCanvasAudio('explosion.*').gatling(6);
window.addEventListener('keydown', function (e) {
(e.keyCode == keys.SPACE) && shotSound.playNext();
}, false);
Облом в Опере
В Опере нас ждёт облом. Детально изучив этот вопрос нашел баг, который я зарепортил с кодом DSK-309302. Клонированный элемент Аудио в Опере не работает:
// var audioOrig = document.createElement('audio'); // аналогично с:
var audioOrig = new Audio();
audioOrig.src = 'shot.ogg';
audioOrig.controls = 'controls';
var audioClone = audioOrig.cloneNode(true);
function appendToBody (node) {
document.getElementsByTagName('body')[0].appendChild(node);
}
audioOrig.play(); // работает
audioClone.play(); // не работает в Опере
appendToBody(audioOrig); // работает
appendToBody(audioClone); // не работает в Опере
Напишем небольшой фикс для Оперы:
LibCanvasAudio.prototype = {
// ..
cloneAudio : function () {
if (window.opera) { // Reported Opera bug DSK-309302
var audioClone = new Audio;
audioClone.src = this.audio.src;
} else {
audioClone = this.audio.cloneNode(true);
}
audioClone.load();
return audioClone;
},
// ..
};
Баг в Фоксе
Другой баг мы можем наблюдать в firefox 3.5 (в 3.6 и 4 уже нету) — при повторном воспроизведении аудиотрека первая секунда± — дублируется. Судя по всему, не только у меня: «the second time you hit the button it plays the “badumm” twice in Firefox». (видео с записью бага). Добавим небольшой фикс — будем отматывать аудио не в начало, а на 25 миллисекунду (минимальное значение установленно экспериментально и равно приблизительно 0.021-0.022 секунды). При желании можно добавить проверку версии и всех кроме 3.5 фокса возвращать в начало (но разница между 25 миллисекундой и нулевой не ощущается, в крайнем случае, зная про этот нюанс можно всё аудио отодвинуть в любимом аудиоредакторе на 25 миллисекунд влево):
LibCanvasAudio.prototype = {
// ..
playNext : function () {
var elem = this.getNext();
elem.pause();
elem.currentTime = 0.025;
elem.play();
return this;
}
};
Бонус
В ie9 preview 4 не работает
new Audio()
, но это очень просто решить, заменив его на document.createElement('audio');
Что получилось в итоге (тыкать в пробел)
Исходник, получившийся в результате: pastebin.com/xG4mhX3w