Tutorials

Создание музыкального визуализатора

Узнайте больше, создав копию полного проекта.

В этом уроке вы научитесь создавать приложение музыкального визуализатора на WebGL с использованием PlayCanvas. Мы собираемся взять аудиопоток, извлечь данные о частоте и затем отобразить эти данные на холсте PlayCanvas.

Наш музыкальный визуализатор состоит из двух скриптов. Анализатор воспроизводит аудио и извлекает данные с помощью анализатора узлов. Что является частью Web Audio API, встроенного в современные браузеры. Визуализатор берет данные из анализатора и отображает их на экране в виде стильного графика.

Анализатор

var Analyser = pc.createScript('analyser');

Analyser.attributes.add('fftsize', {
    type: 'number'
});

// инициализация кода, вызываемая один раз для каждой сущности
Analyser.prototype.initialize = function() {
    var context = this.app.systems.sound.context;

    // создание узла анализатора и настройка
    this.analyser = context.createAnalyser();
    this.analyser.smoothingTimeConstant = 0.6;
    this.analyser.fftSize = this.fftsize;

    this.freqData = new Float32Array(this.fftsize/2);
    this.timeData = new Float32Array(this.fftsize/2);

    var slot = this.entity.sound.slot("track");
    slot.setExternalNodes(this.analyser);
};

// код обновления, вызываемый каждый кадр
Analyser.prototype.update = function(dt) {
    this.analyser.getFloatFrequencyData(this.freqData);
    this.analyser.getFloatTimeDomainData(this.timeData);
};

Давайте более внимательно рассмотрим код здесь.

Сначала мы получаем context. Это экземпляр приложения AudioContext. Мы используем его для создания нового AnalyserNode, который является частью Web Audio API, стандарта для всех современных браузеров. AnalyserNode позволяет нам получать доступ к необработанным данным аудио каждый кадр в виде массива значений. У него есть несколько свойств: smoothingTimeConstant определяет, сглаживается ли выборка данных со временем. 0 означает отсутствие сглаживания, 1 означает сверхсглаживание. И fftSize определяет, сколько образцов будет генерировать узел анализатора. Он должен быть степенью двойки, чем выше это значение, тем более детальным и тем более дорогостоящим для вашего процессора оно будет.

Вы можете получить доступ к данным из AnalyserNode в виде целых чисел, в Uint8Array или в виде чисел с плавающей запятой, в Float32Array. В этом демо мы используем числа с плавающей запятой, поэтому мы выделяем два Float32Arrays, каждый из которых должен быть вдвое меньше, чем fftSize.

Последняя часть настройки заключается в использовании setExternalNodes из API PlayCanvas SoundSlot для вставки этого нового узла в цепочку узлов в Sound Component.

Затем, в нашем цикле обновления, мы используем методы AnalyserNode getFloatFrequencyData и getFloatTimeDomainData для заполнения наших массивов данными. Мы будем использовать эти данные в нашем скрипте визуализатора.

Визуализатор

var Visualizer = pc.createScript('visualizer');

Visualizer.attributes.add('analyser', {
    type: 'entity'
});

Visualizer.attributes.add('freqcolor', {
    type: 'rgba'
});

Visualizer.attributes.add('timecolor', {
    type: 'rgba'
});

Visualizer.attributes.add('heightScale', {
    type: 'number',
    default: 1
});

// инициализация кода, вызываемого один раз для каждой сущности
Visualizer.prototype.initialize = function() {
    this.lines = [];
    var count = this.analyser.script.analyser.fftsize;
    for (var i = 0; i < count; i++) {
        this.lines.push(new pc.Vec3());
    }

    this.yScale = 1;
    this.xScale = 0.10 * 2048 / count;

    this.minDb = this.analyser.script.analyser.analyser.minDecibels;
    this.maxDb = this.analyser.script.analyser.analyser.maxDecibels;
    this.freqScale = 1 / (this.maxDb - this.minDb);
    this.freqOffset = this.minDb;
};

// код обновления, вызываемый каждый кадр
Visualizer.prototype.update = function(dt) {
    this.freqScale = 1 / (this.maxDb - this.minDb);
    this.freqOffset = this.minDb;

    this.renderData(this.analyser.script.analyser.freqData, this.freqcolor, this.freqScale, this.freqOffset);
    this.renderData(this.analyser.script.analyser.timeData, this.timecolor, 0.5, 0);
};


Visualizer.prototype.renderData = function (data, color, scale, offset) {
    var line = 0;
    for (var i = 0; i < data.length; i++) {
        if (line < this.lines.length) {
            var h1 = scale * (data[i] - offset);
            var h2 = scale * (data[i+1] - offset);

            this.lines[line].set(i * this.xScale, this.heightScale*h1, 0);
            this.lines[line+1].set((i+1) * this.xScale, this.heightScale*h2, 0);

            line += 2;
        }
    }
    this.app.renderLines(this.lines, color);
};

Скрипт визуализатора берет все данные из анализатора и отображает их в виде линейного графика с использованием API this.app.renderLines.

В нашей конфигурации мы выделяем множество векторов для использования в линиях. Нам нужно 2 для каждой точки данных (для начала и конца линий). Затем мы настраиваем некоторые масштабные коэффициенты, чтобы убедиться, что все линии отображаются на экране. AnalyserNode содержит минимальное и максимальное значение децибел, которые могут содержать данные. Я обнаружил, что это не совсем точно (я определенно получил значения за пределами этого диапазона), но это служит хорошей основой для нормализации данных.

Функция renderData проходит через все данные и устанавливает один из наших предварительно выделенных векторов, чтобы начать с текущей точки и закончить на следующей точке.

В нашем цикле обновления мы отображаем графики для данных частоты и данных временной области.

Еще идеи?

Это всего лишь небольшой пример того, как вы можете визуализировать свою музыку. Почему бы не попробовать масштабировать 3D-бары, настраивать цвета и яркость в соответствии с музыкой? Подключите визуализатор к SoundCloud и позвольте пользователям выбирать треки? Возможностей множество.

This site is translated by the community. If you want to get involved visit this page