Русский
Русский
English
Статистика
Реклама

Sound

Из песочницы Как не надо разрабатывать звуковые движки

31.10.2020 18:19:25 | Автор: admin


Программируя звук в приложениях и в играх, мне часто приходилось переписывать всю кодовую базу звуковых модулей, так как многие из них обладали либо слишком запутанной архитектурой, либо наоборот ничего не умели кроме простого проигрывания звуков.


Со звуковыми движками хорошо подходит аналогия с рендером изображения в играх: Если у тебя слишком простой pipeline с большим кол-вом абстракций, то ты вряд ли сможешь адекватно программировать что-то сложнее чем куб с шестеренками. С другой стороны, если у тебя весь код состоит из прямых OpenGL или D3D вызовов, то ты не сможешь без боли масштабировать свой спагетти-код.


Насколько уместно сравнение с графическим рендером?


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


Помимо чтения файла из SSD диска, декодирования Opus файла и записывания его даты в микшерный буфер, библиотека создает имитацию объемного звука, обрабатывает сигнал с помощью DSP модулей (компрессор, эквалайзер), а также ресемплирует сигнал. Конфиг машины, на которой проводился тест: Inte Core i9 9900 4.5GHz, 32GB RAM, SSD 480GB SATA. Ресемплинг принимал на вход сигнал с частотой дискретизации 48000Гц и выдавал с 44100Гц.


Вот тот самый код код рендера аудио
    FRESPONZE_BEGIN_TEST    if (!pFirstListener) return false;    // Обновляем размеры буферов если параметры аудио-девайса изменились    if (RingBuffer.GetLeftBuffers()) return false;    RingBuffer.SetBuffersCount(RING_BUFFERS_COUNT);    RingBuffer.Resize(Frames * Channels);    OutputBuffer.Resize(Frames * Channels);    tempBuffer.Resize(Channels, Frames);    mixBuffer.Resize(Channels, Frames);    for (size_t i = 0; i < RING_BUFFERS_COUNT; i++) {        tempBuffer.Clear();        mixBuffer.Clear();        pListNode = pFirstListener;        while (pListNode) {            /* Обновляем значения эмиттеров и обрабатываем сигнал */            EmittersNode* pEmittersNode = nullptr;            if (!pListNode->pListener) break;            pListNode->pListener->GetFirstEmitter(&pEmittersNode);            while (pEmittersNode) {                tempBuffer.Clear();                pEmittersNode->pEmitter->Process(tempBuffer.GetBuffers(), Frames);                // Микшируем сигнал                for (size_t o = 0; o < Channels; o++) {                    MixerAddToBuffer(mixBuffer.GetBufferData((fr_i32)o), tempBuffer.GetBufferData((fr_i32)o), Frames);                }                pEmittersNode = pEmittersNode->pNext;            }            pListNode = pListNode->pNext;        }        /* Обновляем кольцевые буфера для вывода отрендеренного звука  */        PlanarToLinear(mixBuffer.GetBuffers(), OutputBuffer.Data(), Frames * Channels, Channels);        RingBuffer.PushBuffer(OutputBuffer.Data(), Frames * Channels);        RingBuffer.NextBuffer();    }    FRESPONZE_END_TEST("Audio render")

[00:00:59:703]: 'Audio render' operation passed: 551 microseconds[00:00:59:797]: 'Audio render' operation passed: 512 microseconds[00:00:59:906]: 'Audio render' operation passed: 541 microseconds[00:01:00:000]: 'Audio render' operation passed: 583 microseconds

Если добавить уже несколько элементов, то время рендера будет сопоставимо с графическими рендером движком, с единственным отличием в том, что это все происходит в одном потоке. Если же это все попробовать распараллелить на систему задач, сделать более оптимальные алгоритмы микширования, то время рендера с большим количеством уникальных звуков может уменьшиться в несколько раз.


Каким стоит делать звуковой рендер для игр?


Чтобы ответь на этот вопрос, нужно уточнить ваши первоначальные данные. Если вы инди-разработчик, и вы не обладая знаниями в звуке решили разрабатывать игру на C++, то вам подойдут простые библиотеки вроде SoLoud или OpenAL. Они сочетают в себе удобство более продвинутых систем и относительно неплохим функционалом, но при этом обладают важнейшим недостатком плохая переносимость. Так как у всех этих библиотек API элементарный и монолитный, то сложно представить себе портирования с OpenAL на тот же Wwise.


В пример могу поставить звук из одного популярного движка. В нем есть как интерфейсы высокого уровня ref_sound и ISoundManager, с помощью которых можно манипулировать звуками на уровне объектов, которые могут проигрываться, останавливаться, обладать дополнительными звуковыми свойствами, а также эмулировать перемещение в виртуальном пространстве. Проще говоря это то, что видит разработчик игровой логики по аналогии с UAudioComponent в Unreal Engine.


void Class::Function(){    // проигрывание звука с позицией игрока и без повтора    snd.play_at_pos(0, Position(), false);    // игровой код    if (IsHappened())    {        // ...    }    // ...}

Помимо высокого уровня, в звуковом модуле есть элементы низкого уровня CSoundRender_Target, отвечающий за вывод конкретного звука через низкоуровневое API OpenAL или DirectSound, и CSoundRender_Cache модуль кэширования декодированных Vorbis звуков. Самое необычным здесь является target он отвечает не за вывод замикшированного сигнала, а за вывод звука отрендеренного с помощью комбинации source + emitter.



Как выглядит сейчас этот звуковой движок


По этой причине, Core часть звукового движка проблематично портировать как на высокоуровневые фреймворки (FMOD либо Wwise), так и на низкоуровневые прослойки над системным API (PortAudio).



А как бы он мог выглядеть, если из него вырезать ненужные компоненты


Основные архитектуры звуковых движков


Как говорилось раннее, движок должен состоять из двух частей низкоуровневой (hardware) и высокоуровневой (mixer). Низкоуровневая часть отвечает либо за вывод звука напрямую в динамик, либо за вывод звука через дополнительную прослойку, облегчающую работу с системным API. Высокоуровневая часть отвечает за микширование и управление звуками.


Приведу пример:


void GameScheduler::Update() {    // ...        // То, за что отвечает высокоуровневая часть.    // Мы хотим проиграть звук, а низкоуровневый модуль пусть    // позаботится об этом сам.    SoundManager::StopSound(id);    // ...}// ...void AudioHardware::Update() {    // ...        // Упрощенная реализация работы низкоуровневой части:     // помимо операций копирования, здесь могут быть системные    // функции вывода звука в системный микшер.    AudioMixer::Render(input, frames);    memcpy(output, input, frames * channels * frame_size);    // ...}

Такая архитектура позволяет очень легко поменять реализацию одних из модулей на что-то более продвинутое с точки зрения технологий. Тот же AudioHardware я могу написать как через прослойку PortAudio, так и напрямую через Windows Audio Session API. Также и с SoundManager он может быть переписан с использованием библиотек FMOD или Wwise. В этом случае работу модуля AudioHardware принимает на себя именно фреймворк, и вам даже не придется думать о реализации вывода звука.


Высокоуровневая часть звукового движка может быть реализована с помощью разных архитектур: routing и emitters-source систем. Первая в основном используется в DAW, и представляет из себя звуковые дорожки, которые связаны между собой с помощью систем маршрутизаций. Это позволяет посылать сигнал из одного канала в другой, делать side-chain из одного канала в другой, а также использовать сразу несколько звуков на одной дорожке. Данный функционал подходит для рабочего софта, но никак не подходит для игровых движков из-за сложности в реализации а также высоких требований к железу.



Как выглядит современная звуковая система


По этой причине мы возьмем вторую архитектуру emitters-source. В ней есть 2 объекта источник звука (source), который ничего кроме чтения, декодирования и ресемплинга звука не делает, и обработчик звука (emitter) на его плечах лежит обработка сигнала с помощью фильтров, позиционирование объекта, пост-обработка. В модифицированной архитектуре emitters-source также предусматривается и возможность использования нескольких обработчиков на одном источнике звука (в системах Wwise и FMOD это называется virtual emitters), что позволяет проигрывать тысячи одинаковых звуковых без сильной нагрузки на железо.


Пример реализации emitters-source системы можно посмотреть на этом репозитории. Здесь я воспользовался библиотекой miniaudio, поэтому проблем с реализацией вывода звука у меня не было.

Подробнее..
Категории: Звук , C++ , Разработка игр , Sound , Wwise , Fmod , Openal

На сайте-музее Winamp выложили 65000 скинов плеера

30.11.2020 20:13:26 | Автор: admin

Разработчик из Facebook Джордан Элдридж (Jordan Eldredge) создал виртуальный музей Winamp в память о любимом плеере. На площадке можно бесконечно скроллить темы и ностальгировать по ушедшей MP3-эпохе. Мы провели здесь несколько часов, потратив это время далеко не впустую.

О Winamp на Хабре знают, наверное, все. Этот медиаплеер называют предшественником эры Spotify и iTunes. Когда-то любимые музыкальные композиции мы скачивали в формате MP3 (или брали у друзей жесткие диски с гигабайтами музыки) и загружали в проигрыватель. Легендарный плеер стал одним из самых популярных, его использовали десятки миллионов меломанов по всему миру.


Winamp проигрыватель компании Nullsoft, проданной AOL в 1999 году и ликвидированной в 2013. Главная особенность плеера возможность стилизовать его под предпочтения пользователя. По сети гуляли тысячи вариантов оформления.

И эти времена вернулись! Американский программист Джордан Элдрейдж вместе с некоммерческой организацией Архив интернета решили оставить онлайн-след о ключевой для многих эпохе становления и развития цифровой музыкальной индустрии. Они создали виртуальный Музей скинов Winamp. Страница музея выглядит как бесконечная лента из скинов плеера. Всего в коллекции 65 тыс. тем (!).

Скриншот витрины музея

Каждый из скинов в музее можно использовать как плеер с уже предустановленными музыкальными произведениями. Туда же при желании можно загрузить собственные композиции. При этом плеер не нужно устанавливать, достаточно просто щелкнуть на понравившийся скин. Любимые темы можно скачать и установить на собственный Winamp.


Джордан говорит, что музей стал данью уважения художникам, которые с помощью скинов создавали знаковые моменты в истории интернет-искусства.

История без точки


Придумали плеер Winamp в 1997 году восемнадцатилетний хакер Джастин Франкель и студент Дмитрий Болдырев. Первая версия приложения 0.20а работала только с одним файлом и не имела списков воспроизведения.


Популярность плеера выросла после выхода версии 1.00 через несколько месяцев после запуска. Эта версия имела спектрограмму, что было нетрадиционно для конца 90-х. За первые 18 месяцев существования медиаплеер скачали 15 млн человек. Версия плеера 2.90 сделала возможным просмотр видеофайлов.

Страница памяти о плеере

Официальная поддержка последней версии Winamp 5.666 (сборка 3516) заканчивалась 20 декабря 2013 года. Тогда Яндекс.Музыка предприняла попытку увековечить память о судьбоносной для электронной музыки эпохе. Была запущена страница под названием Вспоминая Winamp. Полностью кликабельная страница выполнена в виде интерфейса рабочего стола Windows. Справа открыто окно плеера. Слева, в ветви Проводника, можно было выбрать папки с музыкой. Сейчас страница недоступна.

Но на этом история не остановилась. Во-первых, поддержка плеера не закончилась. Во-вторых, в 2014 году бельгийская компания Radionomy Group выкупила плеер. Цена не разглашалась, но предположительно составляла от $5 до 10 млн. СМИ называют компанию Radionomy Group агрегатором интернет-радиостанций. Она собиралась использовать плеер для знакомства аудитории плеера с их собственным сервисом и обеспечения новым контентом.

В 2018 году на сайте Winamp висело такое объявление

Плеер не обновлялся с 2013 года до осени 2018 года. Тогда вышла полностью бесплатная версия 5.8 с небольшими изменениями из нее были убраны все платные функции, появившиеся в 2002 году.

Тогда же Radionomy Group сообщила в СМИ об обещании выпустить через год новую версию популярного плеера. Версию Winamp 6 намеревались сделать мобильной и десктопной, удобной для прослушивания подкастов, плейлистов из облака и стриминговых радиостанций. Разработчики обещали сохранить былое наследие, но обогатить пользовательский опыт прослушивания. По оценке Radionomy Group, ежемесячная аудитория плеера в тот момент уже составляла 100 млн пользователей.

Однако релиз не случился. Для любителей осталась доступна браузерная версия медиаплеера.

Пока мы готовим новые материалы, пишите в комментариях, какие темы Winamp были у вас любимыми?

Подробнее..

Перевод Библиотека Oboe для высокопроизводительного аудио в играх и приложениях

21.04.2021 10:22:09 | Автор: admin

Мы добавили в Android Game SDK библиотеку Oboe C++ для работы со звуком. Она позволяет разрабатывать высокопроизводительные аудиоприложения с низкой задержкой для максимального спектра устройств Android. Эта библиотека также отлично подойдет большинству разработчиков игр. О ней и о том как с ней работать в Android Game SDK мы и хотим рассказать в этой статье.

Один API

Библиотека Oboe использует усовершенствованный интерфейс AAudio с расширенными функциями на устройствах под управлением Android 8.1 (API уровня 27) или более поздней версии, а также обеспечивает обратную совместимость (через OpenSL ES) с Android 4.1 (API уровня 16) или более поздней версии. В дополнение к API платформы библиотека Oboe предлагает разработчикам аудиоприложений ключевые функции для комфортной работы, такие как ресемплинг, преобразование форматов и динамическая корректировка задержек. При необходимости она позволяет преобразовывать аудиоданные, например конвертировать число каналов, чтобы повышать производительность выбранных устройств. Также библиотека предлагает обходные решения для других особенностей работы конкретных устройств, что повышает эффективность кода для обработки звука. В двух словах, теперь библиотека Oboe считается рекомендуемым решением при написании кода для работы со звуком на C/C++ для платформы Android.

Интеграция Oboe

Встроить элементы кода на базе Oboe в проект можно двумя основными способами. Если вы используете плагин Android Gradle 4.1.0 или более поздней версии вместе с CMake, а также используете (или можете подключить) общую библиотеку STL, для включения библиотеки Oboe достаточно добавить ее в список зависимостей Gradle, включить объекты prefab и добавить несколько строк кода в файл CMakeLists.

Интегрировать Oboe также можно с помощью статического связывания при помощи Android Game SDK. Сначаласкачайте библиотекуи зарегистрируйте ее в системе управления версиями. Работать нужно с minSdkVersion 16 или более поздней версии, а также с NDK 18 или более поздней версии. Затем укажите версию игрового SDK для привязки, которая скомпилирована для заданной комбинации ABI, уровня API, NDK и STL, добавив путь к компилятору в следующем формате:

gamesdk/libs/[architecture]_API[apiLevel]_NDK[ndkVersion]_[stlVersion]_ReleaseExample: gamesdk/libs/arm64-v8a_API24_NDK18_cpp_static_Release

Затем добавьте ключ-loboe_staticв команду компоновщика. Поскольку включать общую библиотеку liboboe.so не требуется, статическое связывание позволяет сократить размер кода. Если для комбинации ABI, уровня API, NDK и STL нет предварительно скомпилированной версии SDK под ваши настройки, можно выполнить связывание с общей библиотекой. Дополнительные указания, в том числе о настройке CMake для статических библиотек, см. вдокументации для разработчиков.

Основы Oboe

Для воспроизведения звука нужно создать поток с необходимыми свойствами, такими как обратный вызов, который используется, когда потоку требуются новые данные.

oboe::AudioStreamBuilder builder;builder.setPerformanceMode(oboe::PerformanceMode::LowLatency)  ->setSharingMode(oboe::SharingMode::Exclusive)  ->setDataCallback(myCallback)  ->setFormat(oboe::AudioFormat::Float);

Аудиоданные передаются внутри такого обратного вызова. В случае успешного создания вы получаете поток с запрошенным типом. Если такие типы не указывались, выполните запрос и изучите возвращенный формат.

class MyCallback : public oboe::AudioStreamDataCallback {public:    oboe::DataCallbackResult    onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) {        // We requested AudioFormat::Float        auto *outputData = static_cast<float *>(audioData);        // TODO: populate audioData here        return oboe::DataCallbackResult::Continue;    }};

Подробные сведения об использовании Oboeсм. вдокументации,примерах кодаидокументации по API. Вы также можете ознакомиться спрактической работой, в которой показано создание простой ритмической игры.

Обратная связь

Если вы столкнулись с проблемами, сообщите нам о нихчерез GitHub.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru