Web Audio і об’ємний звуковий ландшафт: вступ


Дізнайтесь більше про нові кар'єрні можливості в EchoUA. Цікаві проекти, ринкова оплата, гарний колектив. Надсилайте резюме та приєднуйтеся до нас.

Розповідає Si Robertson


У цьому керівництві ми розберемося з основним елементами Web Audio, які використовують для створення об’ємних звукових ландшафтів в інтерактивних додатках з ефектом присутності, наприклад, 3D іграх.

Web Audio API і термінологія можуть збентежити, тому метою цього керівництва є полегшити розуміння принципів роботи з Web Audio.

Демонстрація

Ця демонстрація містить три джерела звуку, які обертаються навколо слухача, його напрям вказаний стрілкою. Уявіть, що він – ігровий персонаж, обертальні джерела звуку можуть бути союзниками або ворогами.

Вихідний код демо і використовувані ресурси наведені наприкінці статті.

AudioContext

Інтерфейс AudioContext – це основа Web Audio, він надає необхідні для створення різних елементів Web Audio функції та спосіб передачі звуку “залізу” і облаштування виведення звуку.

var audioContext = null if (window.AudioContext !== undefined) { audioContext = new AudioContext ()}

Важливо, щоб інтерфейс AudioContext був доступним, оскільки Web Audio є порівняно новою технологією, і вона може не підтримуватися деякими браузерами.

Крім надання необхідних функцій, цей інтерфейс має дві важливі властивості: destination і listener, обидві read-only. Властивість destination можна представити як засіб зв’язку з аудіо-апаратурою. Властивість listener є тим об’єктом, який слухає весь звук у грі, наприклад, персонаж або камера.

Буфери

Інтерфейси AudioBuffer і AudioBufferSourceNode дозволяють програвати звуки. Об’єкти AudioBuffer містять “сирі” звукові файли, які всіляко обробляються по дорозі до чиїхсь колонок або навушників. Об’єкти AudioBufferSourceNode використовуються для запуску і зупинки відтворення звуків, що містяться в об’єктах AudioBuffer.

Стандартним способом завантаження аудіо в об’єкт AudioBufferє використання об’єкта XMLHttpRequest зі встановленою в arraybuffer властивістю responseType. Після завантаження файлу буфер вирушає до об’єкта AudioContext для декодування, і, у разі успіху, ми отримуємо об’єкт AudioBuffer.

var loader = new XMLHttpRequest () loader.open ("GET", "massive - explosion.ogg") loader.responseType = " arraybuffer" loader.onload = whenLoadedloader.send () function whenLoaded (event) { var data = loader.response if (data === null) { // There was a problem loading the file. return } // Decode the data. audioContext.decodeAudioData (data, whenDecoded)} function whenDecoded (audioBuffer) { // "audioBuffer" is an AudioBuffer object.
}

Функція decodeAudioData () має третій параметр, який приймає другий callback, що викликається у випадку неможливості декодування завантаженого аудіофайлу.

decodeAudioData (data, whenDecoded, whenFailed)

Не всі браузери підтримують ті самі формати аудіо (подивіться таблицю), тому Ви можете використати другий callback для переходу до іншого формату. Наприклад, Internet Explorer не підтримує OGG Vorbis, але підтримує MP3. Єдиною проблемою MP3 є унеможливлення безшовно зациклювати аудіо, але така можливість представлена в OGG Vorbis.

Коли нам доступний об’єкт AudioBuffer, то ми можемо відтворити його, використовуючи об’єкт AudioBufferSourceNode.

var source = audioContext.createBufferSource () // Attach an AudioBuffer object.source.buffer = audioBuffer // Connect the " source" object to the " destination" object.source.connect (audioContext.destination)  // Optionally, tell " source" to loop the audio continuously.source.loop = false // Start the audio.source.start ()

Важливо пам’ятати, що об’єкти AudioBufferSourceNode є одноразовими, тобто Ви можете викликати функцію start () тільки один раз. Вам потрібно буде створити об’єкт AudioBufferSourceNode і зв’язати його з об’єктом destination, впровадженим в AudioContext, коли ми хочемо відтворити з нього аудіофайл.

Можна полегшити собі життя, написавши невелику функцію, яка створює, поєднує і запускає за нас об’єкт AudioBufferSourceNode.

function play (audioBuffer, audioContext) { var source = audioContext.createSourceBuffer () source.buffer = audioBuffer source.connect (audioContext.destination) source.start ()} play (audioBuffer01, audioContext) play (audioBuffer02, audioContext) play (audioBuffer03, audioContext)

Коли об’єкт AudioBufferSourceCode закінчує відтворення, і якщо його ніщо не використовує, Web Audio автоматично його відключить. Це дуже зручно, якщо Вам треба запускати короткі звукові ефекти, які не потребують великої уваги  і т. ін. 

Якщо Ви хочете зациклити аудіо, використовуючи властивість AudioBufferSourceNode loop, то Вам знадобиться десь зберегти посилання на об’єкт AudioBufferSourceNode, щоб можна було викликати функцію stop () для зупинки аудіо.

source.stop ()

До цього моменту ми використовуємо буфери для відтворення аудіо, але це відбувається без панорамування і орієнтації в просторі. Ось де в гру вступають об’єкти PannerNode.

Панери

Об’єкти PannerNode дозволяють задати позицію звуку в 3D просторі, використовуючи прямокутну систему координат. Саме тут твориться велика частина 3D магії.

Об’єкт PannerNode має декілька властивостей, що дозволяють тонко налаштувати поведінку аудіо, але зараз нам цікаві лише два: maxDistance і panningModel. Властивість maxDistance – це та відстань до слухача, на якій гучність звуку дорівнюватиме нулю. Це довільне значення, що має значення 10000 за умовчанням. panningModel повідомляє Web Audio, як обробляти аудіо, що проходить через об’єкт PannerNode. Для об’ємних звукових ландшафтів Вам необхідно буде встановити значення HRTF (head – related transfer function).

Для установки положення AudioBufferSourceNode ми використовуємо функцію setPosition (), впроваджену в об’єкт PannerNode.

var panner = audioContext.createPanner () panner.panningModel = " HRTF" // Set the 3d position (x, y, z).panner.setPosition (1, 2, 3)  // Connect the " source" object to the " panner" object.source.connect (panner)  // Connect the " panner" object to the " destination" object.panner.connect (audioContext.destination)  // Start the audio.source.start ()

Для розуміння оновимо маленьку функцію, створену раніше:

function play (audioBuffer, x, y, z, audioContext) { var source = audioContext.createSourceBuffer () source.buffer = audioBuffer var panner = audioContext.createPanner()
panner.panningModel = " HRTF" panner.setPosition (x, y, z) source.connect (panner) panner.connect (audioContext.destination) source.start ()} play (audioBuffer01, 1, 2, 3, audioContext) play (audioBuffer02, 4, 5, 6, audioContext) play (audioBuffer03, 7, 8, 9, audioContext)

Зараз ми відтворюємо звуки, позиціонуючи їх в просторі, але залишився ще один важливий елемент: аудіо-слухач.

Аудіослухач

Кожному об’єкту AudioContext відповідає об’єкт listener, який містить положення і орієнтацію в просторі чогось, що слухає звук. Зазвичай цим чимось є віртуальна камера, закріплена на голові персонажа, бампер машини, хвіст літака або щось інше – головне, щоб це мало сенс з точки зору користувача.

Об’єкт listener має функції setPosition () і setOrientation (). Функція setPosition ()поміщає слухача десь у просторі, а функція setOrientation () його обертає.

Функція setPosition () працює так само, як і аналогічна функція PannerNode, і приймає три координати.

audioContext.listener.setPosition (x, y, z)

Функція setOrientation () трохи складніша, вона приймає два одиничних вектори. Перший вектор відбиває обертання слухача, а другий – вертикальний напрям відносно слухача.

audioContext.listener.setOrientation (x1, y1, z1, x2, y2, z2)

Якщо Вам треба обертати слухача лише по одній осі, обчислення прості. Наприклад, якщо Ви використовуєте ту саму систему координат, що і WebGL, у ній позитивний напрям x вказує на правий край екрана, позитивний напрям y – на верхній, а z – з екрана, Ви можете обертати слухача навколо осі y, використовуючи один виклик функції cos () і один виклик функції sin ().

// The listener's position (could be anything).var x = 50var y = 100var z = 0 audioContext.listener.setPosition (x, y, z)  // Calculate the rotation vector.// rad = rotation, in radiansvar rad = 0.10var v1 = Math.cos (rad)  // xvar v2 = 0 // yvar v3 = Math.sin (rad)  // z // The " up" vectorvar v4 = 0 // xvar v5 = 1 // yvar v6 = 0 // z
audioContext.listener.setOrientation (v1, v2, v3, v4, v5, v6)

Демонстрація до цього керівництва виконує подібну дію і обертає об’єкти PannerNode навколо однієї осі.

Висновок

У цьому керівництві ми розглянули основні елементи Web Audio, використовувані з метою створення об’ємних звукових ландшафтів для інтерактивних додатків з ефектом занурення, таких як 3D ігри. Сподіваюся, ця стаття була корисною і Ви отримали достатньо інформації, щоб зрозуміти принципи спільної роботи аудіобуферів, панерів і слухачів.

Ресурси

Наступний крок: реалізація

У наступній частині керівництва ми використаємо все написане вище, додамо ще трохи і створимо простий API. У ній ми зосередимося на 3D іграх, але API буде узагальненим, щоб його можна було використати також в інших інтерактивних додатках з ефектом занурення.

Переклад статті “Web Audio and 3d Soundscapes: Introduction”

Київ, Харків, Одеса, Дніпро, Запоріжжя, Кривий Ріг, Вінниця, Херсон, Черкаси, Житомир, Хмельницький, Чернівці, Рівне, Івано-Франківськ, Кременчук, Тернопіль, Луцьк, Ужгород, Кам'янець-Подільський, Стрий - за статистикою саме з цих міст програмісти найбільше переїжджають працювати до Львова. А Ви розглядаєте relocate?


Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *