Тег аудио хорошо выполняет свою задачу — статическое представление аудиоконтента на веб-странице. Проблема в том, что для современных веб-приложений этого мало! Операции с DOM-объектом для полноценного интерактивного манипулирования звуковым содержимым просто недостаточно гибки. У браузера нет прямого доступа к аудиоданным и, как следствие, нет никакой, даже теоретической возможности работать со звуком из веб-сценариев. Невозможно наложить какие-либо эффекты, синтезировать звук, создать сэмплы или, например, сколь-нибудь сложное приложение с использованием нескольких аудиопотоков и синхронизации между ними. Вы считаете, что это непомерные требования для звука в www? Тогда оставайтесь в XX веке, а мы пойдем дальше.
Еще одна претензия (прямо, впрочем, следствие основной проблемы) заключалась в невозможности корректной работы с режимом воспроизведения в режиме реального времени. Человеческое ухо — инструмент, очень чувствительный, и работа веб-сценария с объектом HTMLAudioElement просто не может обеспечить должной синхронизации для не улавливаемых им задержек в воспроизведении.
Для решения подобных проблем и задач была предложена более развитая технология — WebAudioAPI. Это мощный механизм, обеспечивающий загрузку, воспроизведение и даже синтез аудиоконтента из любых JavaScript-сценариев.
Правда, этот механизм еще довольно далек от окончательной версии. Mozilla и Google уже успели предоставить собственные версии API для доступа к аудиоконтенту. Рабочая группа W3C работает над выработкой общего подхода, правда, пока в спецификации преобладают webkit-решения, ими мы сейчас и займемся. Его отличие — высокоуровневый API, при котором основные задачи обработки аудио выполняются браузером.
Давайте сразу попробуем возможности AudioAPI на практике.
Сначала создадим экземпляр объекта AudioContext, а заодно и проверим поддержку AudioAPI браузером:
Var context; |
|
Window. addEventListener(‘load’, function { try { Context = new webkitAudioContext(); } catch(e) { Alert(‘Web Audio API is not supported in this browser’); |
|
} }, false); |
|
Если все прошло хорошо — продолжим. Первое, что необходимо для прослушивания музыки, — загрузить файл с аудиоконтентом. Для этого используем уже освоенный нами FileAPI: |
|
<input type="file" id="upFile" accept="audio/*"> <script> Var fileInput = document. querySelector(‘upFile’); fileInput. addEventListener(‘change’, function(e) { var reader = new SileReader(); reader. onload = function(e) { playSound(this. result); |
|
Reader. readAsArrayBuffer(this. files[0]); }, false); |
|
Обратите внимание, в функцию для проигрывания playSound() передается объект типа arraybuffer. Теперь функция: |
Контента сама эта |
Var audioBuffer; var source;
Function playSound(arrayBuffer) {
Context. decodeAudioData(arrayBuffer, function(buffer) {
AudioBuffer = buffer;
Source = context. createBufferSource();
Source. buffer = audioBuffer;
Source. loop = false;
Source. connect(context. destination);
Source. noteOn(0):
}, function(e) {
Console. log(‘Error decoding file’, e); });
}
Уже сейчас, при выборе с помощью элемента <input type="file"> аудиофайла на вашем компьютере, спустя некоторое время (на загрузку) зазвучит музыка. Можно сказать, что мы сами написали пользовательский аудиопроигрыватель. Позже мы сделаем его чуть функциональнее, а пока давайте разберемся в уже написанном коде.
AudioContext является представлением для одного или нескольких источников медиаконтента (файлов или потоков). Метод decodeAudioData принимает данные в формате arrayBuffer и в соответствии с названием декодирует их. Этот метод работает асинхронно. По истечении данного события буфер с данными передается функции обратного вызова. Далее в глобальную переменную source передается буфер источника медиаданных, и с этим объектом уже будут проводиться действия, связанные с воспроизведением контента. И первое действие — метод loop, запрещает циклическое воспроизведение. После этого источник соединяется с предназначением контекста и запускается на воспроизведение командой т oteOn(0). Аргументом здесь служит задержка воспроизведения, выраженная в секундах (0 означает немедленное воспроизведение).
Для более внятного управления воспроизведением напишем еще пару функций:
Function stop() { if (source) {
Source. noteOff(0);
}
}
Метод noteOff(0) останавливает воспроизведение через заданное число секунд.
Function play() { if (source) {
Source = context. createBufferSource(); source. buffer = audioBuffer; source. loop = false; source. connect(context. destination): source. noteOn(0);
}
<div style="border:1px solid green;width:400px;"> <input type="file" accept="audio/*" />
<button onclick="playSound()" >»</button> <button onclick="stopSound()"> 226;</button> </div>
Результат — на рис. 90.
Рис. 90. Простой интерфейс к WebAudio API
Тут явно не хватает еще одной кнопки — паузы. Добавим ее, попутно изучая API:
Var timeState
Function pause() { if (source) {
Alert(source. playbackState) if(source. playbackState == 2){
TimeState = source. context. currentTime; source. noteOff(0)
}
If(source. playbackState == 3){ play()
}
}
В переменной timeState теперь хранится текущая временная отметка воспроизведения. Сейчас немного изменим play():
Function play() {
Source. connect(context. destination); source. noteGrainOn(timeState, timeState,1000): }
}
Тут мы используем метод noteGrainOn(), который проигрывает срез контента. В метод stop() сейчас следует добавить обнуление timeState.
На первый взгляд, по сравнению с использованием тега <audio>, организация воспроизведения аудио на порядок усложнилась. Это на самом деле так, ведь работа теперь проходит на гораздо более низком уровне. Зато теперь мы имеем прямой доступ к контенту, и это дает нам ранее не доступные возможности. Например, сделать простой микс двух загруженных композиций:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="chrome=1" /> <title>Web Audio API</title>
<script>
Var context = new window. webkitAudioContext();
Var audioBuffers = new Array();
Window. onload = function(){
Var filelnput = document. getElementById(‘loader’); filelnput. addEventListener(‘change’, function(e) { var reader = new FileReader(); reader. onload = function(e) { initSound(this. result):
};
Reader. readAsArrayBuffer(this. files[0]);
}, false);
}
Function playSound() {
Var sourcel = context. createBufferSource(); var source2 = context. createBufferSource();
Source1.buffer = audioBuffers[0]; source2.buffer = audioBuffers[1]; source1.connect(context. destination); source2.connect(context. destination); source1.noteOn(0): source2.noteOn(0):
}
Function initSound(arrayBuffer) {
Context. decodeAudioData(arrayBuffer, function(buffer) { audioBuffers[audioBuffers. length] = buffer; alert("ready");
}, function(e) {
Alert(‘Error decoding file’, e);
});
}
</script>
</head>
<body>
<input type="file" id = "loader" accept="audio/*" />
<button onclick="playSound()" >»</button>
</body>
</html>
При таком уровне доступа, в принципе, можно теперь творить со звуком что угодно. Одно замечание — не пытайтесь в процессе проведения опытов заставить петь дуэтом Розенбаума и Яна Гиллана, как это сделал я. Очень трудно такое пережить.
Добавить комментарий