Воспроизведение звука с помощью Web Audio API
Вам нужно воспроизводить аудио в браузере с точным контролем — планирование, эффекты, синтез — но элемент <audio> не справляется с этой задачей. Web Audio API решает эту проблему, однако его документация смешивает устаревшие паттерны с актуальными практиками. Эта статья разъясняет современный подход к воспроизведению звука через Web Audio API, охватывая основы AudioContext, узлы источников и модель аудиографа без устаревшего балласта.
Ключевые моменты
- Создавайте один
AudioContextна приложение и всегда обрабатывайте состояние suspended с помощьюresume()после взаимодействия с пользователем - Используйте
AudioBufferSourceNodeдля предварительно загруженных аудиофайлов иOscillatorNodeдля синтезированных тонов — оба являются одноразовыми узлами - Избегайте устаревшего
ScriptProcessorNode; используйтеAudioWorkletдля пользовательской обработки аудио - Рассматривайте маршрутизацию на устройства вывода через
setSinkId()как экспериментальную функцию с неполной поддержкой браузерами
Основы AudioContext
Каждое Web Audio приложение начинается с AudioContext. Этот объект управляет всеми аудиооперациями и предоставляет таймер для планирования. Думайте о нём как о среде выполнения для вашего аудиографа.
const audioContext = new AudioContext()
Критическое ограничение: браузеры приостанавливают новые контексты до тех пор, пока не произойдёт действие пользователя. Всегда проверяйте audioContext.state и вызывайте resume() из обработчика клика или нажатия клавиши:
button.addEventListener('click', async () => {
if (audioContext.state === 'suspended') {
await audioContext.resume()
}
// Теперь безопасно воспроизводить аудио
})
В большинстве приложений достаточно одного AudioContext, и это рекомендуемый подход. Создание нескольких контекстов увеличивает использование ресурсов и усложняет управление синхронизацией и таймингом. (См.: https://developer.mozilla.org/en-US/docs/Web/API/AudioContext)
Модель аудиографа
Web Audio использует направленный граф узлов. Аудио течёт от узлов-источников через узлы обработки к месту назначения. audioContext.destination представляет собой выход по умолчанию — обычно это динамики пользователя.
Узлы соединяются с помощью метода connect():
sourceNode.connect(gainNode)
gainNode.connect(audioContext.destination)
Эта модульная архитектура позволяет вставлять фильтры, регуляторы усиления или анализаторы в любое место цепочки. Отключайте узлы с помощью disconnect() при реконфигурации графа.
Узлы-источники для воспроизведения звука
Два типа источников охватывают большинство сценариев воспроизведения.
AudioBufferSourceNode
Для предварительно загруженных аудиофайлов декодируйте данные в AudioBuffer, затем воспроизведите через AudioBufferSourceNode:
const response = await fetch('sound.mp3')
const arrayBuffer = await response.arrayBuffer()
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer)
const source = audioContext.createBufferSource()
source.buffer = audioBuffer
source.connect(audioContext.destination)
source.start()
Каждый AudioBufferSourceNode воспроизводится один раз. Создавайте новый узел-источник для каждого воспроизведения — они легковесны. Базовый AudioBuffer можно использовать повторно.
Обратите внимание, что хотя decodeAudioData() поддерживает промисы в современных браузерах, старые версии Safari требовали форму с колбэком. В основном это проблема совместимости со старыми версиями, но всё ещё актуальна для долгосрочной поддержки.
OscillatorNode
Для синтезированных тонов используйте OscillatorNode:
const oscillator = audioContext.createOscillator()
oscillator.type = 'sine'
oscillator.frequency.setValueAtTime(440, audioContext.currentTime)
oscillator.connect(audioContext.destination)
oscillator.start()
oscillator.stop(audioContext.currentTime + 1)
Осцилляторы также воспроизводятся один раз. Планируйте время start() и stop() с использованием audioContext.currentTime для точного воспроизведения на уровне сэмплов.
Discover how at OpenReplay.com.
AudioWorklet против ScriptProcessor
Для пользовательской обработки аудио избегайте ScriptProcessorNode. Он устарел, работает в основном потоке и вызывает сбои аудио при нагрузке.
AudioWorklet — это современная замена. Он работает в выделенном потоке рендеринга аудио с детерминированным таймингом:
// processor.js
class MyProcessor extends AudioWorkletProcessor {
process(inputs, outputs, parameters) {
// Обработка аудио здесь
return true
}
}
registerProcessor('my-processor', MyProcessor)
// main.js
await audioContext.audioWorklet.addModule('processor.js')
const workletNode = new AudioWorkletNode(audioContext, 'my-processor')
workletNode.connect(audioContext.destination)
AudioWorklet поддерживается во всех современных браузерах с автообновлением. Основные ограничения, как правило, связаны с окружением (безопасный контекст, бандлинг, настройка cross-origin), а не с отсутствием поддержки API.
Ограничения маршрутизации вывода Web Audio
Возможности маршрутизации вывода API ограничены и зависят от браузера. По умолчанию аудио направляется в audioContext.destination, который соответствует устройству вывода системы по умолчанию.
AudioContext.setSinkId() позволяет выбирать конкретные устройства вывода, но рассматривайте это как экспериментальную и сильно ограниченную функцию. Она требует безопасного контекста (HTTPS), разрешения пользователя и соответствующих заголовков политики разрешений. На практике она недоступна в Safari или iOS и не должна использоваться для кроссплатформенного переключения динамиков.
Для приложений, требующих выбора устройства вывода, явно проверяйте поддержку:
if (typeof audioContext.setSinkId === 'function') {
// Наличие не гарантирует возможность использования; разрешения и политика всё ещё могут блокировать
}
Даже когда метод существует, вызовы могут завершиться неудачей из-за политики или ограничений платформы. Создавайте запасные варианты и чётко сообщайте об этих ограничениях пользователям.
Практические соображения
Политики автовоспроизведения блокируют аудио до взаимодействия с пользователем. Проектируйте пользовательский интерфейс так, чтобы требовался клик перед инициализацией воспроизведения.
Ограничения CORS применяются при загрузке аудиофайлов с других источников. Убедитесь в правильных заголовках или размещайте файлы на том же источнике.
Управление памятью важно для приложений с большим количеством буферов. Удаляйте ссылки на неиспользуемые объекты AudioBuffer и отключайте узлы, с которыми закончили работу.
Мобильные браузеры накладывают дополнительные ограничения — особенно iOS Safari. Тестируйте на реальных устройствах, а не только в симуляторах.
Заключение
Web Audio API предоставляет мощный низкоуровневый контроль над аудио через графовую модель. Начните с одного AudioContext, соблюдайте политики автовоспроизведения и используйте современные паттерны: AudioBufferSourceNode для сэмплов, OscillatorNode для синтеза и AudioWorklet для пользовательской обработки. Рассматривайте маршрутизацию на устройства вывода как сильно ограниченную и зависящую от платформы. Проверяйте наличие функций во всём и создавайте приложения с учётом ограничений, которые фактически накладывают браузеры.
Часто задаваемые вопросы
Браузеры блокируют создание AudioContext до взаимодействия с пользователем. Ваше окружение разработки может иметь более мягкие настройки. Всегда проверяйте audioContext.state и вызывайте resume() внутри обработчика клика или нажатия клавиши перед попыткой воспроизведения. Эта политика автовоспроизведения применяется универсально во всех современных браузерах.
Нет. Экземпляры AudioBufferSourceNode являются одноразовыми по дизайну. После вызова start() узел не может быть перезапущен. Создавайте новый узел-источник для каждого воспроизведения, повторно используя базовый AudioBuffer. Узлы-источники легковесны, поэтому этот паттерн имеет минимальное влияние на производительность.
Используйте AudioContext.setSinkId(), но рассматривайте это как сильно ограниченную функцию. Она требует HTTPS, разрешения пользователя и разрешающих политик, и не поддерживается в Safari или iOS. Всегда проверяйте наличие функции, корректно обрабатывайте сбои и информируйте пользователей, когда выбор устройства недоступен.
ScriptProcessorNode устарел и работает в основном потоке, вызывая сбои аудио при интенсивной обработке. AudioWorklet работает в выделенном потоке рендеринга аудио с детерминированным таймингом, что делает его подходящим для манипуляций с аудио в реальном времени. Всегда используйте AudioWorklet для пользовательской обработки в новых проектах.
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.