В этой статье два ключевых слова:音频可视化
а такжеWeb Audio
. Первое — это практика, второе — стоящая за ней техническая поддержка. Веб-аудио — отличный источник знаний, эта статья будет посвященаКак получить аудиоданныеЭта часть, для получения дополнительной информации о ее API, вы можете просмотретьMDN.
Кроме того, для преобразования аудиоданных в визуальную графику помимо понимания Web Audio также необходимо иметь определенное представление о Canvas (конкретно 2D, то же самое ниже) и даже WebGL (необязательно). Если у читателей нет никаких оснований для того, чтобы узнать о них, они могут начать со следующих ресурсов:
Что такое звуковая визуализация
Получая частоты, формы волны и другие данные от источников звука, преобразовывая их в графики или изображения и отображая их на экране, интерактивная обработка.
В Cloud Music много кейсов, связанных с аудиоанимацией, но некоторые из них слишком сложны или слишком бизнес-ориентированы. Итак, вот два относительно простых, но репрезентативных примера.
Первая — это аудио гистограмма, реализованная в Canvas.
↑Нажмите, чтобы воспроизвести↑
Второй — эффект частиц, реализованный с помощью WebGL.
↑Нажмите, чтобы воспроизвести↑
На практике, помимо преобразования этих основных форм (прямоугольники, круги и т. д.), также возможно комбинировать звук с естественным движением и 3D-графикой.
Нажмите, чтобы увидеть: некоторые визуальные эффекты на Pinterest
Что такое веб-аудио
Веб-аудио — это набор API для обработки и анализа аудио в Интернете. Он может устанавливать различные источники звука (включая
<audio>
узел, ArrayBuffer, пользовательское устройство и т. д.), добавлять звуковые эффекты к аудио, генерировать визуальную графику и т. д.
Далее мы сосредоточимся на роли веб-аудио в визуализации, как показано на рисунке ниже.
Проще говоря, этополучить данные + данные картыдва процесса. Давайте сначала решим проблему «выборки данных», которую можно выполнить в соответствии со следующими 5 шагами.
1. Создайте аудиоконтекст
Перед любыми манипуляциями со звуком необходимо создать AudioContext. Его роль заключается в связывании аудиовхода, декодировании аудио, управлении воспроизведением и паузой аудио и других основных операциях.
Создано следующим образом:
const AudioContext = window.AudioContext || window.webkitAudioContext;
const ctx = new AudioContext();
2. Создайте узел анализатора
AnalyserNode используется для получения данных о звуковой частоте ( FrequencyData ) и данных во временной области ( TimeDomainData ). Это позволяет визуализировать звук.
Он будет только читать аудио, не внося никаких изменений в аудио.
const analyser = ctx.createAnalyser();
analyser.fftSize = 512;
Что касается fftSize , вMDNВышеприведенное введение может быть трудным для понимания, поскольку в нем говорится, что это параметр быстрого преобразования Фурье.
Его можно понять со следующих точек зрения:
1. Какова его ценность?
Требование fftSize — степень числа 2, например 256, 512 и т. д. Чем выше число, тем лучше результат.
Для мобильных веб-страниц битрейт самого аудио в основном составляет 128 Кбит/с, поэтому нет необходимости использовать большой частотный массив для хранения недостаточно точных исходных данных. Кроме того, размер экрана мобильного телефона меньше, чем у настольного компьютера, поэтому конечную графику отображения не нужно собирать для каждой частоты. Он должен только отражать ритм, поэтому 512 — разумное значение.
2. Что он делает?
fftSize определяет длину FrequencyData, которая составляет половину fftSize.
Что касается того, почему это 1/2, вы можете прочитать эту статью, если вам интересно:Почему БПФ «зеркальный»?
3. Установите исходный узел
Теперь нам нужно связать узел аудио с AudioContext в качестве входных данных для всего процесса анализа звука.
В веб-аудио есть три типа источников звука:
-
MediaElementAudioSourceNodeразрешить
<audio>
Узел напрямую используется в качестве входных данных, которые можно передавать в потоковом режиме. - AudioBufferSourceNodeАудиофайл загружается заранее через xhr, а затем декодируется с помощью AudioContext.
-
MediaStreamAudioSourceNodeВ качестве входа можно использовать микрофон пользователя. то есть через
navigator.getUserMedia
После получения аудио- или видеопотока пользователя создайте источник звука.
Среди этих трех аудиоисточников, кроме MediaStreamAudioSourceNode, есть свои незаменимые сценарии использования (например, голос или живое видео). MediaElementAudioSourceNode и AudioBufferSourceNode относительно легко смешивать, поэтому здесь я сосредоточусь на них.
MediaElementAudioSourceNode
MediaElementAudioSourceNode будет<audio>
теги в качестве источников звука. Его вызовы API очень просты.
// 获取<audio>节点
const audio = document.getElementById('audio');
// 通过<audio>节点创建音频源
const source = ctx.createMediaElementSource(audio);
// 将音频源关联到分析器
source.connect(analyser);
// 将分析器关联到输出设备(耳机、扬声器)
analyser.connect(ctx.destination);
AudioBufferSourceNode
В одном случае на стороне Android тест былChrome/69
(Не включено) В следующих версиях, когда используется MediaElementAudioSourceNode, полученные данные FrequencyData представляют собой массив всех нулей.
Поэтому, если вы хотите быть совместимым с такими машинами, вам нужно изменить способ предварительной загрузки, то есть использовать AudioBufferSourceNode, метод загрузки следующий:
// 创建一个xhr
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/audio.mp3', true);
// 设置响应类型为 arraybuffer
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
var source = ctx.createBufferSource();
// 对响应内容进行解码
ctx.decodeAudioData(xhr.response, function(buffer) {
// 将解码后得到的值赋给buffer
source.buffer = buffer;
// 完成。将source绑定到ctx。也可以连接AnalyserNode
source.connect(ctx.destination);
});
};
xhr.send();
Было бы легче понять, если бы AnalyserNode был аналогом промежуточного программного обеспечения?
Сравните с обычным<audio>
Воспроизведение и процесс воспроизведения в Web Audio:
4. Воспроизведение аудио
для<audio>
Узлы, то есть MediaElementAudioSourceNode, относительно знакомы для воспроизведения:
audio.play();
Но в случае AudioBufferSourceNode у него нет метода воспроизведения, а вместо этого:
// 创建AudioBufferSourceNode
const source = ctx.createBufferSource();
// buffer是通过xhr获取的音频文件
source.buffer = buffer;
// 调用start方法进行播放
source.start(0);
5. Получить данные о частоте
На данный момент мы подключили аудиовход к AnalyserNode и начали воспроизводить звук. Для этой части веб-аудио осталась только одна последняя задача: получение данных о частоте.
Что касается частоты, Web Audio предоставляет два связанных API, а именно:
analyser.getByteFrequencyData
analyser.getFloatFrequencyData
Оба возвращают TypedArray, разница только в точности.
getByteFrequencyData возвращает массив Uint8Array от 0 до 255. И getFloatFrequencyData возвращает Float32Array от 0 до 22050.
Напротив, если проект требует большего характеристики, чем точность, рекомендуется использовать getbytefrequency ensio. На следующем рисунке показан конкретный пример:
Что касается длины массива (256), как объяснялось выше, она составляет половину от fftSize.
Теперь давайте посмотрим, как получить массив частот:
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
analyser.getByteFrequencyData(dataArray);
Следует отметить, что getByteFrequencyData присваивает значения существующим элементам массива, а не возвращает новый массив после создания.
Его преимущество в том, что в коде есть только одна ссылка на dataArray, и нет необходимости переоценивать через вызовы функций и передачу параметров.
Две реализации визуализации
Узнав о веб-аудио, я уже могу использовать getByteFrequencyData для получения массива Uint8Array с временным именем dataArray.
В принципе, данные, на которые опирается визуализация, могут быть звуком, изменениями температуры или даже случайными числами. Поэтому в следующем содержании нам нужно заботиться только о том, как сопоставить dataArray с графическими данными, без учета работы Web Audio.
(Чтобы упростить описание Canvas и WebGL, далее Canvas упоминается отдельно.Canvas 2D
. )
1. Холст решение
Щелкните для просмотра: Исходный код первого примера
Сам холст — это воспроизведение последовательности кадров. Он должен сначала очищать Canvas в каждом кадре, а затем перерисовывать его.
Вот выдержка из примера кода:
function renderFrame() {
requestAnimationFrame(renderFrame);
// 更新频率数据
analyser.getByteFrequencyData(dataArray);
// bufferLength表示柱形图中矩形的个数
for (var i = 0, x = 0; i < bufferLength; i++) {
// 根据频率映射一个矩形高度
barHeight = dataArray[i];
// 根据每个矩形高度映射一个背景色
var r = barHeight + 25 * (i / bufferLength);
var g = 250 * (i / bufferLength);
var b = 50;
// 绘制一个矩形,并填充背景色
ctx.fillStyle = "rgb(" + r + "," + g + "," + b + ")";
ctx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
x += barWidth + 1;
}
}
renderFrame();
Для визуализации основная логика такова: как преобразовать частотные данные в графические параметры. В приведенном выше примере мы просто изменили высоту и цвет каждого прямоугольника в гистограмме.
Canvas предоставляет множество API-интерфейсов для рисования, и только с точки зрения 2D он также может создавать множество интересных эффектов. По аналогии с DOM, если только<div>
Комбинация может сделать страницы красочными, поэтому Canvas может.
2. WebGL-решение
Нажмите для просмотра: исходный код второго примера
Холст — это вычисление ЦП.При расчете цикла 10000 раз, и вычисление повторяется каждый кадр, ЦП не может быть загружен. Поэтому мы редко видим использование Canvas 2D для достижения эффектов частиц. Вместо этого используйте WebGL с помощью вычислительной мощности графического процессора.
В WebGL есть относительно новое понятие — шейдеры. Это общий термин для класса алгоритмов рендеринга, которые работают на графическом процессоре. Он написан на GLSL (язык шейдеров OpenGL), который является просто языком, подобным C. Вот простой пример:
void main()
{
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
Для более подробного ознакомления с шейдерами вы можетеПрочтите эту статью.
Собственный API WebGL очень сложен, поэтому мы используемThree.js
В качестве базовой библиотеки она упрощает написание бизнес-логики.
Давайте посмотрим, что делается во всем процессе разработки, как показано ниже:
В этом процессе тип юниформ — это простой Object, и мы передадим аудио-массив как свойство юниформ в шейдер. Что касается того, что делает шейдер, то можно просто понять, что он сопоставляет ряд атрибутов, определенных в юниформах, с вершинами и цветами на экране.
Написание вершинных и фрагментных шейдеров часто не требует участия в разработке внешнего интерфейса и может быть знакомо изучающим игры, которые изучили такие методы, как Unity3D. читатели могут перейти кShaderToyНайдите готовые шейдеры.
Затем введите следующие три класса в Three.js:
1. THREE.Geometry
Можно понимать как форму. Другими словами, этот класс определяет, является ли конечный отображаемый объект сферой, прямоугольным параллелепипедом или другими неправильными формами.
Следовательно, вам нужно передать ему некоторые координаты вершины. Например, если треугольник имеет 3 вершины, то передаются координаты 3 вершин.
Конечно, в Three.js встроено много часто используемых фигур, таких как BoxGeometry, CircleGeometry и так далее.
2. THREE.ShaderMaterial
Можно понять как цвет. Принимая треугольники в качестве примера, треугольник может быть черным, белым, градиентным цветом и т. Д. Эти цвета определяются пошлютериалом.
ShaderMaterial — это тип материала, определяемый вершинными и фрагментными шейдерами.
3. THREE.Mesh
После определения формы и цвета объектов их необходимо сгруппировать, что называется сеткой. Когда у вас есть сетка, вы можете добавить ее на холст. Затем идет обычный процесс requestAnimationFrame.
Точно так же мы извлекли наиболее важный код из примера и пометили его.
I. Создайте Geometry (это класс, унаследованный от THREE.BufferGeometry):
var geometry = ParticleBufferGeometry({
// TODO 一些参数
});
ii. Дайте определение униформе:
var uniforms = {
dataArray: {
value: null,
type: 't' // 对应THREE.DataTexture
},
// TODO 其他属性
};
iii. Создайте ShaderMaterial:
var material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: '', // TODO 传入顶点着色器
fragmentShader: '', // TODO 传入片元着色器
// TODO 其他参数
});
iv. Создайте сетку:
var mesh = new THREE.Mesh(geometry, material);
v. Создайте необходимые объекты рендеринга в Three.js, включая сцену и камеру:
var scene, camera, renderer;
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
camera = new THREE.PerspectiveCamera(45, 1, .1, 1e3);
scene = new THREE.Scene();
vi. Обычная логика рендеринга:
function animate() {
requestAnimationFrame(animate);
// TODO 此处可以触发事件,用于更新频率数据
renderer.render(scene, camera);
}
резюме
В этой статье сначала рассказывается, как получить данные о частоте звука с помощью API-интерфейсов, связанных с веб-аудио.
Затем вводятся две схемы визуализации, Canvas и WebGL, и вводятся некоторые распространенные способы отображения частотных данных в графические данные.
Кроме того, облачный музыкальный клиент уже некоторое время находится в сети с анимацией Jingyun.После прочтения этой статьи кто-нибудь из студентов хочет попробовать реализовать свою собственную аудиоанимацию?
Наконец, два упомянутых выше примеров кодепена прилагаются:
Эта статья была опубликована сКоманда внешнего интерфейса NetEase Cloud Music, Любое несанкционированное воспроизведение статьи запрещено. Мы всегда нанимаем, если вы готовы сменить работу и вам нравится облачная музыка, тоПрисоединяйтесь к нам!