company_banner
7 августа 2011 в 18:33

Визуализация аудио в HTML5

Наше практическое погружение описывает необычный сценарий — мы будем говорить не о том, что может HTML5, а о тех возможностях, которые на сегодня он еще не предоставляет и как эту неувязку можно обходить на практике.



HTML5 сегодня — это сериал, концовку которого не знают даже сценаристы, история, в которой есть как практически готовые главы и главы в черновых набросках, так и просто заметки для будущих сюжетов.

Визуализация аудио, точнее принципиальный низкоуровневый доступ к аудио-информации, находится где-то между черновыми набросками и заметками на будущее.


Что может и что не может <audio>

<audio>-элемент HTML5, как вы, наверняка, уже догадались, сам по себе никакого низкоуровневого API не предоставляет. Он позволяет только управлять воспроизведением аудио-потока: запускать, ставить на паузу, останавливать проигрывание, узнавать текущую позицию и общую длительность композиции.

На самом деле, надо честно сказать, что это не единственные проблемы и сложности — если вы пробовали сделать сколь-нибудь сложное приложение с использованием нескольких аудиопотоков и необходимостью их синхронизации, почти наверняка, вы столкнетесь со сложностями в реализации.

Причем это будет зависеть не только от возможностей, предоставляемых спецификацией, но и (в большей степени) от реализации в конкретном браузере — не случайно Rovio и Google, делая Angry Birds на HTML5, оптимизированную для Chrome, отказались от идеи использовать для звуков audio-элементы HTML5. Вместо этого «Angry Birds на HTML5» использует Flash. (См. также обсуждение в блоге разработчиков.)

Для более глубокого погружения в тему <audio>-элемента также рекомендую статью Unlocking the power of HTML5 <audio>, описывающую основные приемы работы аудио в HTML5. 

Стандарты на извлечение звука



В настоящее время работа над созданием низкоуровневого API для доступа к аудиопотоку уже активно ведется в рамках Audio-группы W3C.

Разрабатываемое API будет предоставлять не только возможность получить низкоуровневый доступ к аудио-потоку, но и синтезировать аудио на лету.
The audio API will provide methods to read audio samples, write audio data, create sounds, and perform client-side audio processing and synthesis with minimal latency. It will also add programmatic access to the PCM audio stream for low-level manipulation directly in script.

На сегодня Mozilla и Google уже успели предоставить собственные версии API для доступа к аудио-информации.

Audio Data API от Mozilla предоставляет простой доступ к аудио-потоку на чтение и запись, задача реализации алгоритмов обработки аудио в реальном времени должна при этом решаться на стороне скрипта (на JavaScript). Спецификация для Webkit — Web Audio API от Google  — предоставляет высокоуровневый API, при котором основные задачи обработки могут выполняться нативно браузером.

Рабочая группа W3C работает над выработкой общего подхода, в котором будет предоставлен двуслойный API для обеспечения более широких возможностей.

К слову, область деятельности группы помимо клиентского API для работы с Audio включает также задачи доступа к аудио-устройствам, включая микрофоны и другие источники звука, и работы с колонками, в том числе в многоканальном режиме.

Следить за новостями группы можно в твиттере @w3caudio.

Но это все лирика, давайте к практике!

Практический подход: что работает сегодня?

Практический подход, работающий сегодня — это предобработка.

Да-да! Вот так банально. Предобработка аудио-информации с последующей генерацией визуализации, синхронизированной с воспроизводимым аудио-потоком.

На самом деле, если речь идет о смысловом извлечении информации (например, текста песни), то предобработка — единственный практичный выход, причем как правило, эта обработка делается ручками.

В целом, если аудио-файл и механизм визуализации известны заранее, предобработка — это не просто хороший способ, но и единственный разумный, экономящий вычислительные ресурсы и, следовательно, снижающий нагрузку на клиентские машины.

Давайте смотреть, как это работает. 

Пример из жизни:  Chell in the Rain



Chell in the Rain — это красивая аудио-текстовая визуализация песни Exile Vilify. Синхронно с  аудио-потоком на экране возникают слова из текста песни.

Что внутри
  • jQuery + Sizzle.js (для селекторов)
  • jPlayer (для проигрывания Audio и Video)
  • собственный код, который нам, собственно и интересен ;)

Как все работает  
Пропуская инициализацию аудио и обработчики событий для управления воспроизведением.

Вся песня предварительно побита на фрагменты, соответствующие началу той или иной фразы или этапу анимации. Начало каждого фрагмента хранится в массиве:
var timings = newArray();
timings[0] = 11.5;
timings[1] = 17;
timings[2] = 24;
timings[3] = 29;
timings[4] = 35.5;
...

Отдельно хранится массив фраз из текста песни:
var lyrics = newArray();
lyrics[0] = 'Exile';
lyrics[1] = 'It takes your mind... again';
lyrics[2] = "You've got sucker's luck";
lyrics[3] ='Have you given up?';
...

С привязкой к таймингу и на основании текущего момента в аудио-композиции срабатывает триггер на переход к новой фразе:
if(event.jPlayer.status.currentTime >= timings[currentTrigger] && nolyrics != true) {
   fireTrigger(currentTrigger);
   currentTrigger++;
}

Далее в нужный момент срабатывает тот или иной триггер, запускающий средствами jQuery соответствующую анимацию:
function fireTrigger(trigger) {
   switch (trigger) {
      case 0:
         $('#lyrics1 p').addClass('vilify').html(lyrics[0]).fadeIn(1500);
         break;
      case 1:
         $('#lyrics2 p').html(lyrics[1]).fadeIn(1000).delay(5000).fadeOut(1000);
         $('#lyrics1 p').delay(6000).fadeOut(1000);
         break;
      case 2:
         $('#lyrics1 p').fadeIn(1000);
         break;
      case 3:
         $('#lyrics2 p').fadeIn(1000).delay(4000).fadeOut(1000);
         $('#lyrics1 p').delay(5000).fadeOut(1000);
         break;
      case 4:
         $('#lyrics1 p').removeClass('vilify').html(lyrics[2]).fadeIn(1000);
         break;
      case 5:
         $('#lyrics2 p').html(lyrics[3]).fadeIn(1000).delay(3000).fadeOut(1000);
         $('#lyrics1 p').delay(4000).fadeOut(1000);
         break;
      ...

Довольно просто и эффектно, согласитесь! Самое главное во всей этой истории — это легкость совмещения аудио-потока и возможностей HTML, CSS и JavaScript.

Пример из жизни: Music Can Be Fun



Music Can Be Fun — мини-игра на стыке искусства и музыки. Предлагаю сначала немного поиграться, чтобы было понятно, о чем пойдет речь ;)

Пример посложнее — и здесь уже активно используются возможности Canvas, но так как нас интересует только музыкальная составляющая, то все не так страшно!

Как и в предыдущем случае, здесь по ходу композиции воспроизводится текст песни, для чего в дебрях JS-кода зашита соответствующая привязка ко времени:
var _lyrics = [
   ["00:17.94", "00:22.39", "When I have once or twice"],
   ["00:23.93", "00:30.52", "Thought I lived my life .. for"],
   ["00:40.74", "00:47.38", "Oh oh I'll wake up in a thousand years"],
   ["00:48.40", "00:52.06", "With every ghost I'm looking through"],
   ["00:53.33", "00:57.80", "I was a cold, cold boy"],
   ["00:59.52", "01:03.00", "Hey! Oh when I lie with you"],
   ...

Помимо текста, если вы поигрались в игрушку, вы не могли не заметить спец-эффектов, также привязанных к музыкальной композиции и соответствующим переходам. Привязка делается абсолютно аналогично:
var _effects = [
   ["00:06.00", 1],
   ["00:30.50", 1],
   ["00:42.50", 1],
   ["00:54.50", 2],
   ["00:57.00", 1],
   ...

(На самом деле, ко времени привязаны даже частоты появления синих и красных шариков ;)

При обновлении момента проигрывания (событие onTimeUpdate) происходит применение тех или иных визуализаций:
var _onTimeUpdate = function() {
   var t = MusicManager.currentTime = _song.currentTime;
   ...

   for (var i = _lyricsId; i < _lyrics.length; i++) {
      if (MusicManager.currentTime < _lyrics[i][0]) break;
      if (MusicManager.currentTime < _lyrics[i][1]) {
         SubtitleManager.changeSubtitle(_lyrics[i][2]);
      } else {
         SubtitleManager.changeSubtitle("");
      _lyricsId++;
      }
   }

   for (var i = _effectsId; i < _effects.length; i++) {
      if (MusicManager.currentTime < _effects[i][0]) break;
         MusicManager.isEffect1Used = false;
         MusicManager.isEffect2Used = !_effects[i][1] == 2;
         _effectsId++;
      }
   }
   ...

}

По-прежнему просто и эффективно. Один и тот же прием легко применим не только к текстовой информации, но и к различным визуальным эффектам.

Остается понять, можно ли второе как-то автоматизировать, чтобы уж совсем все не делать ручками. Очевидно, можно — и Grant Skinner в своем блоге подсказывает, как это сделать ;)

Пример из жизни: извлечение данных



В своем блоге в посте Music Visualizer in HTML5 / JS with Source Code Грант делится своим опытом в визуализации аудио с помощью HTML5.

Столкнувшись с тем, что HTML5 Audio не предоставляет API для экстракции низкоуровневых данных о проигрываемой композиции, Грант написал небольшое AIR-приложение (архив также содержит примеры), позволяющее вытащить из mp3-файла информацию об уровнях звука в текстовом виде или в виде изображения.

В увеличенном масштабе информация о музыкальной композиции выглядит так:


Имея данные в таком виде, их можно легко извлечь, например, средствами Canvas. В текстовом виде все еще проще (пример не привожу, т.к. данные в текстовом файле упакованы).

Чтобы работать с такими предобработанным данными, Грант написал специальную библиотеку на JavaScript (VolumeData.js в архиве).

Работа с библиотекой осуществляется довольно просто. Начинается все с загрузки информации о композиции:
loadMusic("music.jpg");

где, внутри функции loadMusic, как вы уже догадались, загружается обычная картинка:
function loadMusic(dataImageURL) {
    image = new Image();
    image.src = dataImageURL;
    playing = false;
    Ticker.addListener(window);
}

После загрузки всех необходимых компонент, из изображения извлекаются данные о звуке:
volumeData = newVolumeData(image);

Далее в нужный момент времени из этих данных можно получить как усредненную информацию, так и информацию об уровне звука в левом и правых каналах:
var t = audio.currentTime;
var vol = volumeData.getVolume(t);
var avgVol = volumeData.getAverageVolume(t-0.1,t);
var volDelta = volumeData.getVolume(t-0.05);
volDelta.left = vol.left-volDelta.left;
volDelta.right = vol.right-volDelta.right;

Визуальные эффекты привязываются к этим данным. Для визуализации используется библиотека EaselJS.
Посмотреть, как это работает на практике, можно в примерах Star Field и Atomic.

Заключение

Подводя итог, остается только сказать, что смотря на все это, меня не покидает ощущение, что с HTML5 индустрия движется в правильном направлении. Да, еще не все возможно, и далеко не все вещи делаются так же легко (и вообще возможны), как их сегодня можно делать во Flash или Silverlight. Но многое уже на горизонте!
Автор: @kichik
Microsoft
рейтинг 625,02
Microsoft — мировой лидер в области ПО и ИТ-услуг

Комментарии (29)

  • +10
    Ура, интересный материал по HTML5! А то уже надоело читать одно и то же про сокращенную запись doctype
  • +1
    Лично я столкнулся с вот какой проблемой :
    Запускаешь трек1, синхронно с ним трек2, переключаешь вкладку в браузере, наблюдаешь хаос. А если их ещё больше, то ещё больший хаос. В добавок ко всему этому переключение громкости происходит в каждом браузере по-своему, но чаще всего от момента включения/выключения звука до того как это произойдёт проходит ощутимый промежуток времени. Да и музыку которая не успела загрузиться по каким-то причинам контролировать сложновато
    • 0
      О чем и речь — сегодня сколь-нибудь сложные сценарии вызывают множество мелких проблем и нюансов :)
  • 0
    easeljs.com не открывается… Безжалостный хабраэффект?!
  • 0
    Интересно, можно ли это все как-то увязать с Google Music?
  • 0
    сдается мне что достаточно вбить в аудио мета-события, чтобы иметь точные тайминги аудио-событий потока. как насчет того, чтобы читать через html5 мета-события?
    • 0
      А что такое мета-события?
      • 0
        в midi было такое понятие «meta events».
        при чём здесь оно — хз.
        • 0
          тоже хз, но, как вариант — брать тайминг из соответствующих lyrics, а на его основе делать эффектные акценты :)
  • 0
    А что у них там с вводом-выводом в реальном режиме времени (или близко к тому — с задержками до 10мс)?

    Т.е., чтобы, например, можно было снять с аудиоинтерфейса звук с микрофона (караоке, например, с автотюном), обработать его в online-плагине, и выпустить в колонки, с задержкой 0-8мс? С этим есть какие-то продвижения?
    • 0
      У них — это у кого? :) Если в W3C, то они пока только над новым стандартом думают.
      • 0
        Промахнулся, извините. Ответил вам комментарием ниже.
    • –1
      Врят-ли с обычной звуковой картой удастся добиться таких таймингов.
      • 0
        Интересуют обладатели нормальных аудиоинтерфейсов. Например, есть у меня M-Audio Fast Track USB для демок — на винде удается без особых проблем получить 4мс через ASIO-дрова.

        А насчет «у них — это у кого? :)» — меня больше интересует не черновик стандарта, а реальная возможность воплотить это в современных браузерах, в течение пары ближайших лет. Хотя бы Gecko (FF) и WebKit (Chrome). Существует ли она сейчас? Например, чтобы из браузера задействовать ASIO в Win и CoreAudio в MacOS, получить интерфейс звуковухи (входы/выходы), читать оттуда поток, обрабатывать, и выбрасывать обратно, роутинг на плечах приложения.

        Есть несколько классных идей, но для их реализации как раз не хватает этой возможности.

        Подскажите, какие могут быть варианты, кроме написания отдельного плагина под NSAPI, чтобы код загружался из онлайна (будь он JS или еще что-нибудь), исполнялся без лишних установок чего-нибудь и позволял производить обработку сигнала с аудиоинтерфейса с малыми задержками?

        У Google присматривался Native Client, я писал туда запрос на фичу, но они его по непонятным причинам отклонили, поэтому ищу еще варианты.
        • 0
          Честно говоря, чтобы что-то такое работало кроссбраузерно не вижу других адекватных вариантов кроме того, что будет соответствующий стандарт или набор стандартов, предоставляющие необходимые API, + нативная реализация в браузерах большинства сложных операций, учитывающая возможности железа для оптимизации обработки данных.

          Собственно, в задачи Audio WG входит создать что-то похожее на то, о чем вы говорите:
          •Access to audio devices, such as for microphones or other audio inputs, and multi-channel speakers or other audio outputs.
          •APIs and advanced functionality regarding audio cache management and audio capability information
  • +2
    www.ro.me/
    Мне интересно как тут синхронизировали.
    Вообще безумно красиво. Хочется сразу сайты в WebGL делать.
  • 0
    У меня сразу же возникла ассоциация с субтитрами :) Было бы не плохо если нечто подобное добавили прямо в audio\video элементы, например как-то так <audio src="..." subtitles="lyrics.sub"> вместо того чтобы прописывать подобные данные в JS-коде
    • 0
      На самом деле, в HTML5 есть возможность прикрепить к мультимедиа файл с субтитрами через элемент Track, но это не везде работает и дальше встает вопрос с форматом файла, который пока не решен.
  • 0
    > jQuery + Sizzle.js (для селекторов)

    А что есть в Sizzle, чего нельзя сделать с помощью jQuery?
    • 0
      Если выражаться точнее, то:

      Sizzle.js is a JavaScript library that implements a «CSS selector engine designed to be easily dropped in to a host library.» jQuery uses it internally for its CSS selection needs. If you wanted a CSS engine and had no need for all the other JavaScript benefits of jQuery, you could use Sizzle.js separately.
      • 0
        На это и намекаю, что «jQuery + Sizzle.js» вместе использовать смысла нет. Либо то, либо другое.
        • 0
          Да, это один файл :) Просто даже внутри написано, что это jQuery + Sizzle.
          • 0
            Ммм, теперь понял, спасибо :)
  • 0
    а можно всё это в обратном направлении проделать? т.е. не текст переключать в зависимости от положения аудио, а аудио переключать в зависимости от чего-то…

    можно ли как-то программно давать указание проигрывать определенный отрезок аудио? во флеше я догадываюсь как это сделать, как-то почти приступил к реализации. а средствами jquery — не представляю даже :(
    • 0
      Не знаю, причем тут jQuery, но в HTML5 Audio (Video) легко можно менять текущую позицию проигрывания.
  • 0
    а останавливать её тоже можно? допустим, задать, проиграть от секунды 310 до 345.
    и как это будет происходить? файл сначала весь загружается, потом перематывается куда нужно, или грузится тот интервал, что нам нужен?

    может я упустил где-то, — если не трудно, дайте пожалуйста ссылку на документацию исчерпывающую и доступную по HTML5 Audio (Video)
  • 0
    большое спасибо
  • 0
    Если все таки использовать Flash как источник низкоуровневых аудиоданных, то нельзя обойти вниманием soundManager2. В нем уже реализовано получение EQ, например.

    Собственно, само получение данных не менее красиво:
    soundObject = soundManager.createSound({
      ... разные параметры
      useFastPolling: true, // включение обновления 10 мс, вместо обычных 50 мс
      useEQData: true,	// включаем EQ
      whileplaying: function() {
        some_func_for_update_anything(); // вызов функции обновления визуализации (таблицами, дивами, канвасом - как уже нравится.
      }
    });
    
    function some_func_for_update_anything() {
      //например, используем библиотеку WaveForm (http://waveformjs.org/) чтобы нарисовать частотный спектр в стиле SoundCloud
      waveform.update({data: soundObject.eqData.right}); // для упрощенного примера взят поток правого динамика. если сделать console.log(soundObject.eqData.right), то отобразится массив из 256 значений от -1 до 1.
    }
    


    Если есть желание нарисовать фон проигрывания «в будущее», то без предобработки не обойтись. Но никто не мешает для заранее фиксированного аудиофайла получить картинку с помощью, например php-waveform-png, отобразить фоном, а реалтайм отображать soundManager'ом.

    Пример плеера с частотным спектром, увы, располагается в ЛС.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка