WebAudioAPI

Тег аудио хорошо выполняет свою задачу — статическое представление аудиоконтента на веб-странице. Проблема в том, что для современных веб-приложений этого мало! Операции с 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()" >&#187;</button> <button onclick="stopSound()">&#8 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()" >&#187;</button>

</body>

</html>

При таком уровне доступа, в принципе, можно теперь творить со звуком что угодно. Одно замечание — не пытайтесь в процессе проведения опытов заставить петь дуэтом Розенбаума и Яна Гиллана, как это сделал я. Очень трудно такое пережить.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *