- PVSM.RU - https://www.pvsm.ru -

Захвати и визуализируй! Или гистограмма с микрофона средствами Web Audio API

Захвати и визуализируй! Или гистограмма с микрофона средствами Web Audio API - 1

Я очень люблю «живые» графики. Смертельная скука — смотреть на статичные картинки с цифрами. Мне хочется, чтобы график завораживал, чтобы заставлял человека, который смотрит на него, взаимодействовать и открывать для себя новые грани всех данных на нем. Поэтому любой пример, что попадает мне в руки, и любая библиотека визуализации, которой не повезло оказаться на моей машине, проходит испытание “оживлением”. Вот и в очередной раз, раздумывая, как же еще я могу раскорячить визуализационные виджеты из DevExtreme [1] библиотеки, я задумалась об отображении звука. «Интересно и живо» — подумала я в тот день, запаслась чаем с печеньками и засела за эту задачу. Что у меня в итоге вышло — узнаете под катом.

Первое, что мне предстояло выбрать — это какой же звук визуализировать. Просто мелодия показалась слишком банальной, к тому же, я нашла замечательную статью [2] на Хабре, полностью описывающую весь процесс от создания звука до визуализации на Canvas. Но, почитав комментарии к этой статье, я заметила некий интерес к захвату аудио с микрофона. Потыкав гугл на предмет наличия подобных статей, я выяснила, что их на эту тему немного. Появилась цель — появился и план действий:

Захвати и визуализируй! Или гистограмма с микрофона средствами Web Audio API - 2

Захват аудио с микрофона. Один из самых простых способов получить данные с микрофона в браузере — использовать getUserMedia [3] метод, что находится в объекте общедоступных браузерных методов — в navigator. Принимает он три аргумента:

  1. Настройки захвата аудио и видео:
    { audio: true/false, video: true/false }
    

  2. Функцию, выполняющуюся в случае успеха
  3. Функцию, выполняющуюся в случае ошибки

То есть, в общем случае вызов этого метода выглядит вот так:

navigator.getUserMedia(
    { audio: true, video: true },
    function (stream) {
        //stream processing
    },
    function (error) {
        //error processing
    }
);

Но! Нужно помнить, что метод в разных браузерах различен, поэтому будет не лишним перед вызовом getUserMedia провернуть следующую штуку, и обеспечить поддержку метода во всех распространенных браузерах:

navigator.getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);

Анализ захваченного аудио. В случае успеха выполнения метода getUserMedia, мы получаем объект типа MediaStream, который уже можно использовать для создания источника аудио с помощью Web Audio API. Но чтобы воспользоваться этим способом, для начала нужно создать AudioContext [4] — наш проводник в мир Web Audio API. Создание очень простое:

var ctx = new AudioContext();

Опять же, нужно не забывать про кроссбраузерность:

var AudioContext = window.AudioContext || window.webkitAudioContext;

Теперь, с помощью аудио контекста ctx, есть возможность создать все необходимые элементы для анализа нашего звука. Что же это за элементы?

Захвати и визуализируй! Или гистограмма с микрофона средствами Web Audio API - 3

Элемент Destination [5] нам доступен по-умолчанию, уже после создания аудио контекста. По сути, это то, куда будет уходить наш звук, а использование конструкции ctx.destination дает нам доступ к стандартному выводу звука для системы, например, к колонкам или наушникам. Остальные же элементы из этой схемы требуется создать и настроить.

  • Source [6] — это источник звука, наше пойманное аудио с микрофона. Необходимо лишь преобразовать поток MediaStream в элемент, с которым мы сможем работать, и для этого подойдет метод аудио контекста createMediaStreamSource:
    var source = ctx.createMediaStreamSource(stream);
    

  • Analyser [7] — это узел для анализа аудио, многие параметры звука можно вытянуть именно с его помощью. Создается он незатейливым методом аудио контекста createAnalyser:
    var analyser = ctx.createAnalyser();
    

  • ScriptProcessor [8] — это модуль обработки аудио. Он необходим для отслеживания момента изменения звука с помощью события onaudioprocess. Создать его можно, используя метод аудио контекста createScriptProcessor с параметрами размера буфера, количеством входов и выходов:
    var processor = ctx.createScriptProcessor(2048, 1, 1);
    

Все созданные элементы необходимо соединить между собой, как показано на схеме. Web Audio API предоставляет пару методов connect/disconnect для связывания и развязывания узлов:

source.connect(analyser);
source.connect(processor);
analyser.connect(ctx.destination);
processor.connect(ctx.destination);

При запуске всего этого кода (вот пример [9]) и при малой толике удачи можно услышать из колонок/наушников звук с микрофона. Слушать свой голос весьма раздражает, поэтому с чистой совестью можно не создавать связи анализатора с аудио выходом, на конечный результат это никак не повлияет. Конечная схема работы будет выглядеть так:

Захвати и визуализируй! Или гистограмма с микрофона средствами Web Audio API - 4

Последние штрихи — разбор звука по кирпичикам. Для анализатора есть возможность задать размерность преобразования Фурье — опцию fftSize. Зачем она сдалась? А затем, что в итоге получится количество элементов данных равное fftSize/2, то есть количество точек для графика:

analyser.fftSize = 128;

Осталось лишь получать данные о частотах звука от анализатора, сохраняя их в специально созданную для этого переменную data. Изменение звука отслеживается благодаря уже знакомому нам событию onaudioprocess:

var data = new Uint8Array(analyser.frequencyBinCount);
processor.onaudioprocess = function (){
    analyser.getByteFrequencyData(data);
    console.log(data);
}

Та-дам! И вот они, заветные циферки с микрофона в консоли (пример [10]):

Захвати и визуализируй! Или гистограмма с микрофона средствами Web Audio API - 5

Визуализация полученных данных. Непосредственно “оживляющая” часть, превращение циферок в график, для которого понадобится библиотека DevExtreme [11]. Первым делом, конечно же, требуется создать график:

<div id="chart" style="width: 800px; height: 250px"></div>

var chart = chart = $("#chart").dxChart({ 
    //chart options
}).dxChart("instance");

Такой созданный график будет пустым, потому что для него нужны данные — опция dataSource, и серия, которую нужно отобразить — опция series. При этом данные — динамические, и их нужно менять при срабатывания события onaudioprocess:

var dataSource = $.map(data, function (item, index) {
    return { arg: index, val: item };
});
chart.option("dataSource", dataSource);

Добавив пару настроек для графика, типа цвета и вида серии, можно в итоге получить вот такую замечательную вещь:

Захвати и визуализируй! Или гистограмма с микрофона средствами Web Audio API - 6

А итоговый код будет выглядеть вот так, просто и лаконично:

Итоговый код

navigator.getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);

navigator.getUserMedia(
    { audio: true, video: false },
    function (stream) {
        var AudioContext = window.AudioContext || window.webkitAudioContext,
            ctx = new AudioContext(),
            source = ctx.createMediaStreamSource(stream),
            analyser = ctx.createAnalyser(),
            processor = ctx.createScriptProcessor(2048, 1, 1),
            data,
            chart,
            dataSource;
        
        source.connect(analyser);
        source.connect(processor);
        //analyser.connect(ctx.destination);
        processor.connect(ctx.destination);
        
        chart = $("#chart").dxChart({
            dataSource: [],
 		      legend: {
                visible: false
            },
            argumentAxis: {
                label: {
                    visible: false
                }
            },
            valueAxis: {
                grid: {
                    visible: false
                },
                label: {
                    visible: false
                }
            },
            series: {
                hoverMode: "none",
                type: "bar",
                color: "#1E90FF"
            }
        }).dxChart("instance");
        data = new Uint8Array(analyser.frequencyBinCount);
        processor.onaudioprocess = function (){
             analyser.getByteFrequencyData(data);
             dataSource = $.map(data, function (item, index) {
                return { arg: index, val: item };
             });
             chart.option("dataSource", dataSource);
        }
    },
    function (error) {
        //error processing
    }
);

Вот здесь можно найти готовый пример [12]. При желании можно собрать пример локально, или даже заменить визуализацию на любую другую понравившуюся. Главное — это насколько быстро и просто с помощью Web Audio API и библиотеки DevExtreme собирается подобный пример.

Как всегда, жду ваши замечания и предложения в комментариях к статье. Всем хорошего дня!

Автор: DevExpress

Источник [13]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/114437

Ссылки в тексте:

[1] DevExtreme: http://js.devexpress.com/WebDevelopment/Charts/

[2] замечательную статью: http://habrahabr.ru/post/210422/

[3] getUserMedia: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getUserMedia

[4] AudioContext: https://developer.mozilla.org/en-US/docs/Web/API/AudioContext

[5] Destination: https://developer.mozilla.org/en-US/docs/Web/API/AudioDestinationNode

[6] Source: https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamAudioSourceNode

[7] Analyser: https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode

[8] ScriptProcessor: https://developer.mozilla.org/ru/docs/Web/API/ScriptProcessorNode

[9] вот пример: https://jsfiddle.net/jdxnmgkq/5/

[10] пример: https://jsfiddle.net/vsj05jr2/1/

[11] DevExtreme: http://js.devexpress.com/

[12] готовый пример: https://jsfiddle.net/tatyanaRyzh/1ba1c0ta/1/

[13] Источник: https://habrahabr.ru/post/278823/