Добавление звука в пример Marble Maze
В данном документе описаны ключевые принципы, которые следует учитывать при работе со звуком, и показано, как эти принципы применяются в игре Marble Maze. Marble Maze использует Microsoft Media Foundation для загрузки звуковых ресурсов из файлов и XAudio2 для микширования и воспроизведения звука, а также для добавления эффектов к звукозаписям.
Marble Maze воспроизводит музыку в фоновом режиме, а также использует звуки во время игрового процесса для указания на события в игре, например столкновение шарика со стеной. Важной частью реализации является то, что Marble Maze использует эффект реверберации (эхо) для имитации звука шарика при отскоке. Реализация эффекта реверберации позволяет эху доходить до ушей пользователя быстрее и более громко в небольших пространствах. Если пространство велико, эхо будет тише и дойдет до слушателя не так скоро.
Пример кода, соответствующего этому документу, см. в примере игры DirectX Marble Maze.
Некоторые из ключевых аспектов работы со звуком в игре, рассматриваемые в данном документе.
Рассмотрите возможность использования Media Foundation для декодирования звуковых ресурсов и XAudio2 для воспроизведения звука. Однако если у вас уже есть готовый механизм загрузки звуковых ресурсов, который работает в приложении универсальной платформы Windows (UWP), то можно использовать его.
Граф звука содержит один исходный тембр для каждого активного звука, ноль или более тембров субмикширования и один тембр мастеринга. Исходные тембры могут передаваться в тембры субмикширования и (или) тембр мастеринга. Тембры субмикширования передаются в другие тембры субмикширования и (или) тембр мастеринга.
Если файлы фоновой музыки велики, следует подумать о потоковой передаче музыки в более мелкие буферы, чтобы расходовать меньше памяти.
Если это имеет смысл, приостанавливайте воспроизведение звука, когда приложение утрачивает фокус, становится невидимым или приостанавливается. Возобновляйте воспроизведение, когда приложение вновь получает фокус, становится видимым или возобновляется.
Установите категории звука для отражения роли каждого из звуков. Например, для звуковых эффектов обычно используется AudioCategory_GameMedia для звукового звука игры и AudioCategory_GameEffects .
Реакция на смену устройств, в том числе наушников, должна состоять в освобождении и воссоздании всех звуковых ресурсов и интерфейсов.
Подумайте, следует ли сжимать звуковые файлы, если требуется минимизация занимаемого пространства диска и расходов на потоковую передачу. В ином случае звук можно передавать в несжатом виде, чтобы он загружался быстрее.
Знакомство с XAudio2 и Microsoft Media Foundation
XAudio2 — это низкоуровневая звуковая библиотека Windows, которая, в частности, поддерживает звук для игр. Она предоставляет обработку цифровых сигналов (DSP) и обработчик звукового графа для игр. XAudio2 расширяет возможности своих предшественников, DirectSound и XAudio, поддерживая такие направления развития вычислительной техники, как архитектуры с плавающей запятой SIMD и звук высокого разрешения. Она также поддерживает более комплексные требования к обработке звука, предъявляемые современными играми.
В документе Ключевые концепции XAudio2 объясняются ключевые концепции использования XAudio2. В двух словах эти концепции заключаются в следующем.
Интерфейс IXAudio2 является ядром обработчика XAudio2. Marble Maze использует этот интерфейс для создания тембров и получения уведомлений, когда устройство вывода меняется или в нем происходит сбой.
Тембр обрабатывает, корректирует и воспроизводит звуковые данные.
Исходный тембр является набором звуковых каналов (моно, 5.1 и т. д.) и представляет собой единый поток звуковых данных. В XAudio2 исходный тембр также является местом, где начинается обработка звуковых данных. Обычно звуковые данные загружаются из внешнего источника, такого как файл или сеть, и отправляются исходному тембру. Marble Maze использует Media Foundation для загрузки звуковых данных из файлов. Media Foundation будет представлен ниже в данном документе.
Тембр субмикширования обрабатывает звуковые данные. Эта обработка может включать в себя изменение звукового потока или объединение нескольких потоков в один. Marble Maze использует субмикширования для создания эффекта реверберации.
Голосовая речь объединяет данные из исходного и субмикс голоса и отправляет их на звуковое оборудование.
Звуковая диаграмма содержит один исходный голос для каждого активного звука, ноль или более субмикс голосов и только один основной голос.
Обратный вызов информирует клиентский код о том, что событие произошло в голоса или в объекте Engine. Применение обратных вызовов позволяет повторно использовать память, когда XAudio2 завершает работу с буфером, реагировать на изменения используемого звукового устройства (например, на подключение или отключение наушников) и прочее. В разделе Обработка смены наушников и устройств далее в этом документе объясняется, как Marble Maze использует этот механизм для обработки смены устройств.
Marble Maze использует для обработки звука два обработчика (то есть два объекта IXAudio2). Один из них обрабатывает фоновую музыку, а второй — звуки, связанные с игровым процессом.
Marble Maze также необходимо создать по одному тембру мастеринга для каждого обработчика. Вспомним, что обработчик мастеринга объединяет звуковые потоки в один поток и отправляет этот поток звуковому оборудованию. Поток фоновой музыки, исходный тембр, выдает данные в тембр мастеринга и в два тембра субмикширования. Тембры сумбикширования создают эффект реверберации.
Media Foundation — это библиотека мультимедиа, поддерживающая многочисленные форматы звука и видео. XAudio2 и Media Foundation дополняют друг друга. Marble Maze использует Media Foundation для загрузки звуковых ресурсов из файлов, а XAudio2 — для воспроизведения звука. Использовать Media Foundation для загрузки звуковых ресурсов не обязательно. Если у вас уже есть готовый механизм загрузки звуковых ресурсов, который работает в приложениях универсальной платформы Windows (UWP), можно использовать его. В разделе Звук, видео и камера рассматривается несколько способов реализации звука в приложении UWP.
Подробнее об XAudio2 см. в руководстве по программированию. Дополнительные сведения о Media Foundation см. в разделе Microsoft Media Foundation.
Инициализация звуковых ресурсов
Marble Maze использует файлы Windows Media Audio (WMA-файлы) для фоновой музыки и WAV-файлы для звуков, связанных с игровым процессом. Media Foundation поддерживает эти форматы. Хотя формат файлов WAV непосредственно поддерживается XAudio2, игре необходим анализ формата файла вручную для заполнения соответствующих структур данных XAudio2. Marble Maze использует Media Foundation для упрощения работы с WAV-файлами. Полный список форматов мультимедиа, поддерживаемых Media Foundation, см. в разделе Поддерживаемые форматы мультимедиа в Media Foundation. Marble Maze не использует различные звуковые форматы на этапе разработки и во время выполнения и не включает поддержку сжатия XAudio2 ADPCM. Дополнительные сведения о сжатии ADPCM в XAudio2 см. в разделе Обзор ADPCM.
Метод Audio::CreateResources, вызываемый из MarbleMazeMain::LoadDeferredResources, загружает звуковые потоки из файлов, инициализирует объекты обработчика XAudio2 и создает тембры: исходный, субмикширования и мастеринга.
Создание обработчиков XAudio2Вспомним, что Marble Maze создает один объект IXAudio2 для представления каждого используемого обработчика звука. Для создания обработчика звука вызовите метод XAudio2Create. В следующем примере показано, как Marble Maze создает обработчик звука для фоновой музыки.
Marble Maze также выполняет аналогичное действие для создания обработчика звуков игрового процесса.
Работа с интерфейсом IXAudio2 в приложении UWP имеет два отличия от классических приложений. Во-первых, нет необходимости вызывать метод CoInitializeEx перед вызовом XAudio2Create. Во-вторых, интерфейс IXAudio2 больше не поддерживает перечисление устройств. Дополнительные сведения о перечислении звуковых устройств см. в разделе Перечисление устройств.
Создание тембров мастерингаВ следующем примере показано, как метод Audio::CreateResources создает тембр мастеринга для фоновой музыки с помощью метода IXAudio2::CreateMasteringVoice. В этом примере m_musicMasteringVoice является объектом IXAudio2MasteringVoice . Мы указываем два входных канала; это позволяет упростить логику для эффекта реверберации.
В качестве входной частоты дискретизации мы указываем 48 000. Мы выбрали такую частоту дискретизации, так как она обеспечивает баланс между качеством звука и нагрузкой на ЦП. Большая частота дискретизации увеличила бы объем работы ЦП без значительного повышения качества.
И наконец, мы указываем AudioCategory_GameMedia в качестве категории звукового потока, чтобы во время игры пользователи могли слушать музыку из другого приложения. при воспроизведении музыкального приложения Windows отменяет любые голоса, созданные с помощью параметра AudioCategory_GameMedia . Пользователь по-прежнему слышит звуки игрового процесса, так как они создаются с помощью параметра AudioCategory_GameEffects . Дополнительные сведения о категориях аудио см. в разделе AUDIO_STREAM_CATEGORY.
Метод Audio:: CreateResources выполняет аналогичное действие для создания основного голоса для игрового процессаных звуков, за исключением того, что он указывает AudioCategory_GameEffects для параметра стреамкатегори , который является значением по умолчанию.
Создание эффекта реверберацииДля каждого тембра можно использовать XAudio2, чтобы создавать последовательности эффектов, обрабатывающих звук. Такая последовательность называется цепочкой эффектов. Цепочки эффектов используются, если нужно применить к тембру один или несколько эффектов. Цепочки эффектов могут быть разрушительны. Это значит, что каждый эффект в цепочке может перезаписывать звуковой буфер. Это свойство важно, поскольку XAudio2 не гарантирует, что буферы вывода инициализируются тишиной. Объекты эффектов представлены в XAudio2 межплатформенными объектами обработки звука (XAPO). Дополнительные сведения о XAPO см. в Обзоре XAPO.
При создании цепочки эффектов выполните следующие действия.
Создайте объект эффекта.
Заполните структуру XAUDIO2_EFFECT_DESCRIPTOR данными о действиях.
Заполнение структуры XAUDIO2_EFFECT_CHAIN данными.
Примените цепочку эффектов к тембру.
Заполните структуру параметров эффекта и примените ее к эффекту.
Отключайте или включайте эффект по мере необходимости.
Класс Audio определяет метод CreateReverb, который создает цепочку эффектов, реализующую реверберацию. Этот метод вызывает метод XAudio2CreateReverb для создания объекта ComPtr IUnknown — soundEffectXAPO, который играет роль тембра субмикширования для эффекта реверберации.
Структура XAUDIO2_EFFECT_DESCRIPTOR содержит сведения о ксапо для использования в цепочке эффектов, например целевое количество выходных каналов. Метод Audio:: креатереверб создает объект XAUDIO2_EFFECT_DESCRIPTOR , саундеффектдескриптор, для которого задано состояние disabled, использует два выходных канала и ссылается на саундеффектксапо для перенастраиваемого действия. soundEffectdescriptor запускается в отключенном состоянии, поскольку игра должна задать параметры, прежде чем эффект начнет изменять игровые звуки. Marble Maze использует два канала вывода для упрощения логики эффекта реверберации.
Если в цепочке несколько эффектов, для каждого эффекта необходим объект. Структура XAUDIO2_EFFECT_CHAIN содержит массив объектов XAUDIO2_EFFECT_DESCRIPTOR , которые участвуют в результате. В следующем примере показано, как метод Audio::CreateReverb определяет один эффект для реализации реверберации.
Метод Audio::CreateReverb вызывает метод IXAudio2::CreateSubmixVoice для создания тембра субмикширования для этого эффекта. Он указывает объект XAUDIO2_EFFECT_CHAIN , саундеффектчаин, для параметра пеффектчаин , чтобы связать цепочку эффектов со голоса. Marble Maze также задает два канала вывода и частоту дискретизации 48 килогерц.
Если нужно присоединить существующую цепочку эффектов к существующему тембру субмикширования или заменить текущую цепочку эффектов, используйте метод IXAudio2Voice::SetEffectChain.
Метод Audio::CreateReverb вызывает метод IXAudio2Voice::SetEffectParameters, чтобы задать дополнительные параметры, связанные с этим эффектом. Этот метод принимает структуру параметров, характерную для данного эффекта. Объект XAUDIO2FX_REVERB_PARAMETERS , m_reverbParametersSmall, который содержит параметры эффектов для REVERB, инициализируется в методе Audio:: Initialize , так как каждый перекомандный результат использует одни и те же параметры. В следующем примере показано, как метод Audio::Initialize инициализирует параметры для реверберации в ближней зоне.
В этом примере используются значения по умолчанию для большинства параметров реверберации, но для параметра DisableLateField значение TRUE, указывая, что это реверберация в ближней зоне, для EarlyDiffusion — значение 4, чтобы имитировать плоские поверхности поблизости, и для LateDiffusion — значение 15, чтобы имитировать рассеивающие удаленные поверхности. Плоские поверхности, расположенные поблизости, создают громкое эхо, достигающее слушателя быстрее. Рассеивающие удаленные поверхности создают тихое эхо, достигающее слушателя дольше. Со значениями реверберации можно экспериментировать, чтобы получить в игре нужный эффект, либо можно применить метод ReverbConvertI3DL2ToNative, чтобы использовать стандартные для отрасли параметры I3DL2 (рекомендации по обработке трехмерного звука в интерактивных приложениях, уровень 2.0).
В следующем примере показано, как метод Audio::CreateReverb задает параметры реверберации. newSubmix — это объект IXAudio2SubmixVoice**. Параметры — это объект XAUDIO2FX_REVERB_PARAMETERS*.
Метод Audio::CreateReverb завершает работу, включая эффект с помощью IXAudio2Voice::EnableEffect, если установлен флаг enableEffect. Он также задает громкость с помощью IXAudio2Voice::SetVolume и матрицу вывода с помощью IXAudio2Voice::SetOutputMatrix. Эта часть задает полную громкость (1.0), а затем определяет матрицу громкости как тишину для левого и правого входов и левого и правого динамиков вывода. Это необходимо, так как другой код затем обеспечивает перекрестное затухание двух эффектов реверберации (имитируя переход от близости к стене к пребыванию в большой комнате) или отключает обе реверберации, если это необходимо. Когда после этого путь реверберации включается, игра задает матрицу , чтобы направить левый выход реверберации на левый вход тембра мастеринга, а правый выход реверберации — на правый вход тембра мастеринга.
Marble Maze вызывает метод Audio::CreateReverb четыре раза: два раза для фоновой музыки и два раза для звуков игрового процесса. В следующем примере показано, как Marble Maze вызывает метод CreateReverb для фоновой музыки.
Список возможных источников эффектов для использования с XAudio2 см. на странице Звуковые эффекты XAudio2.
Загрузка звуковых данных из файлаMarble Maze определяет класс MediaStreamer, который использует Media Foundation для загрузки звуковых ресурсов из файлов. Marble Maze использует по одному объекту MediaStreamer для загрузки каждого из звуковых файлов.
Marble Maze вызывает метод MediaStreamer::Initialize для инициализации каждого из звуковых потоков. Ниже показано, как метод Audio::CreateResources вызывает метод MediaStreamer::Initialize, чтобы инициализировать звуковой поток для фоновой музыки.
Метод MediaStreamer::Initialize начинает с вызова метода MFStartup для инициализации Media Foundation. MF_VERSION — это макрос, определенный в mfapi.h, который указывается в качестве используемой версии Media Foundation.
Метод MediaStreamer::Initialize затем вызывает метод MFCreateSourceReaderFromURL, чтобы создать объект IMFSourceReader. Объект IMFSourceReader — m_reader — считывает данные мультимедиа из файла, заданного параметром url.
Метод MediaStreamer::Initialize затем создает объект IMFMediaType с помощью функции MFCreateMediaType, чтобы описать формат звукового потока. У звукового формата имеется два типа: основной тип и подтип. Основной тип определяет общий формат мультимедиа, то есть видео, звук, сценарий и так далее. Подтип определяет формат, например PCM, ADPCM или WMA.
Метод медиастреамер:: Initialize использует метод Имфаттрибутес:: сетгуид , чтобы указать основной тип (MF_MT_MAJOR_TYPE) как аудио (MFMediaType_Audio) и дополнительный тип (MF_MT_SUBTYPE) в качестве несжатого звука PCM (MFAudioFormat_PCM). MF_MT_MAJOR_TYPE и MF_MT_SUBTYPE — это атрибуты Media Foundation. MFMediaType_Audio и MFAudioFormat_PCM — это идентификаторы GUID типа и подтипа; подробнее см. в статье Звуковые типы мультимедиа. Метод IMFSourceReader::SetCurrentMediaType связывает тип мультимедиа со средством чтения потока.
Метод MediaStreamer::Initialize затем получает полный формат вывода мультимедиа из Media Foundation с помощью IMFSourceReader::GetCurrentMediaType и вызывает метод MFCreateWaveFormatExFromMFMediaType, чтобы преобразовать звуковой тип мультимедиа Media Foundation в структуру WAVEFORMATEX. Структура WAVEFORMATEX определяет формат данных, содержащих форму звуковой волны. Marble Maze использует эту структуру для создания исходных тембров и применения фильтра низких частот к звуку катящегося шарика.
Метод MFCreateWaveFormatExFromMFMediaType использует CoTaskMemAlloc для выделения памяти для объекта WAVEFORMATEX. Поэтому нужно обязательно вызвать функцию CoTaskMemFree по завершении использования объекта.
Метод медиастреамер:: Initialize завершается вычислением длины потока, m_maxStreamLengthInBytesв байтах. Для этого он вызывает метод IMFSourceReader::GetPresentationAttribute, чтобы получить продолжительность звукового потока в единицах, равных 100 наносекундам, преобразовывает ее в секунды и умножает на среднюю скорость передачи данных в байтах в секунду. Затем Marble Maze использует это значение, чтобы выделить память под буфер, содержащий все звуки игрового процесса.
Создание исходных тембровMarble Mazе создает исходные тембры XAudio2 для воспроизведения всех звуков и мелодий игры в соответствующих исходных тембрах. Класс Audio определяет объект IXAudio2SourceVoice для фоновой музыки и массив объектов SoundEffectData, содержащих звуки игрового процесса. Структура SoundEffectData содержит объект IXAudio2SourceVoice для эффекта, а также определяет другие связанные с эффектом данные, такие как буфер звука. Audio. h определяет перечисление саундевент . Marble Maze использует это перечисление для определения каждого из звуков игрового процесса. Класс Audio также использует это перечисление, чтобы индексировать массив объектов SoundEffectData.
В следующей таблице показаны связи между каждым из этих значений и файлом, содержащим соответствующие звуковые данные, а также краткие описания каждого звука. Звуковые файлы находятся в папке \медиа\аудио
Значение SoundEvent Имя файла Описание RollingEvent MarbleRoll.wav Воспроизводится, когда шарик катится. FallingEvent MarbleFall.wav Воспроизводится, когда шарик выпадает из лабиринта. CollisionEvent MarbleHit.wav Воспроизводится, когда шарик сталкивается со стенками лабиринта. CheckpointEvent Checkpoint.wav Воспроизводится, когда шарик проходит контрольную точку. MenuChangeEvent MenuChange.wav Воспроизводится, когда пользователь изменяет текущий пункт меню. MenuSelectedEvent MenuSelect.wav Воспроизводится, когда пользователь выбирает пункт меню.
В следующем примере показано, как метод Audio::CreateResources создает исходный тембр для фоновой музыки. Структура XAUDIO2_SEND_DESCRIPTOR определяет целевой конечный компьютер на основе другого голоса и указывает, следует ли использовать фильтр. Marble Maze вызывает метод Audio::SetSoundEffectFilter, чтобы использовать фильтры для изменения звука катящегося шарика. Структура XAUDIO2_VOICE_SENDS определяет набор голосов для получения данных из одного выходного голоса. Marble Maze отправляет данные из исходного тембра на тембр мастеринга (для "сухой", или оставшейся неизменной, части воспроизводимого звука) и на два тембра субмикширования, которые реализуют "влажную", или реверберирующую, часть воспроизводимого звука.
Метод IXAudio2::CreateSourceVoice создает и настраивает исходный тембр. Он принимает структуру WAVEFORMATEX, которая определяет формат буферов звука, отправляемых тембру. Как упоминалось ранее, Marble Maze использует формат PCM.
Воспроизведение фоновой музыки
Исходный тембр создается в остановленном состоянии. Marble Maze запускает фоновую музыку в игровом цикле. Первый вызов метода MarbleMazeMain::Update вызывает Audio::Start, чтобы запустить фоновую музыку.
Метод Audio::Start вызывает метод IXAudio2SourceVoice::Start, чтобы запустить обработку исходного тембра для фоновой музыки.
Исходный тембр передает звуковые данные на следующий этап графа звука. В случае Marble Maze следующий этап включает два тембра субмикширования, которые применяют к звуку два эффекта реверберации. Один из них применяет реверберацию с задержкой в ближней зоне. Второй применяет реверберацию с задержкой в дальней зоне.
Степень влияния каждого из них на окончательное микширование определяется размером и формой помещения. Реверберация в ближней зоне имеет большее влияние, если шарик находится рядом со стеной или в небольшом помещении, а реверберация в поздней, или дальней, зоне оказывает большее влияние, если шарик находится на открытом пространстве. Этот подход дает более реалистичный эффект эха при движении шарика через лабиринт. Дополнительные сведения о том, как Marble Maze реализует этот эффект, см. в описании методов Audio::SetRoomSize и Physics::CalculateCurrentRoomSize в исходном коде Marble Maze.
Если разница в размерах помещений в игре относительно невелика, можно использовать более простую модель реверберации. Например, можно использовать одну настройку реверберации для всех помещений или заранее определить настройку реверберации для каждого помещения.
Метод Audio::CreateResources использует Media Foundation для загрузки фоновой музыки. Но на этом этапе у исходного тембра нет звуковых данных для обработки. Кроме того, так как фоновая музыка повторяется циклично, исходный тембр необходимо регулярно обновлять данными, чтобы воспроизведение музыки продолжалось.
Для поддержки наполнения исходного тембра данными игровой цикл обновляет буферы звука каждый кадр. Метод MarbleMazeMain::Render вызывает метод Audio::Render для обработки звукового буфера фоновой музыки. Класс Audio определяет массив из трех звуковых буферов, m_audioBuffers. Каждый буфер содержит 64 КБ (65536 байт) данных. Цикл считывает данные из объекта Media Foundation и записывает их в исходный тембр до тех пор, пока в исходном тембре не будет три поставленных в очередь буфера.
Marble Maze использует для музыкальных данных буфер размером 64 КБ, но вам может понадобиться буфер большего или меньшего размера. Этот объем зависит от требований создаваемой игры.
Цикл также обрабатывает ситуацию, когда объект Media Foundation достигает конца потока. В этом случае он вызывает метод IMFSourceReader::SetCurrentPosition, чтобы сбросить позицию источника звука.
Чтобы реализовать циклическое воспроизведение для одного буфера (или для всего звука, полностью загруженного в память), можно задать поле XAUDIO2_BUFFER:: лупкаунт, чтобы XAUDIO2_LOOP_INFINITE при инициализации звука. Marble Maze использует эту методику для воспроизведения звука катящегося шарика.
Однако в случае фоновой музыки Marble Maze управляет буферами напрямую, чтобы лучше контролировать объем используемой памяти. Если файлы с музыкой большие, можно выполнять потоковую передачу данных музыки в меньшие по размеру буферы. Это поможет сбалансировать объем памяти с той частотой, с которой игра может обрабатывать и передавать поток звуковых данных.
Если в игре используется низкая или переменная частота кадров, обработка звука в основном потоке может привести к неожиданным остановкам звука или щелчкам, так как обработчику звука будет недостаточно звуковых данных в буфере для работы. В играх, для которых эта проблема актуальна, можно вынести обработку в отдельный поток, не занятый отрисовкой. Этот подход особенно полезен на компьютерах с несколькими процессорами, потому что игра может использовать простаивающие процессоры.
Реагирование на события в игре
Класс Audio предоставляет методы, такие как PlaySoundEffect, IsSoundEffectStarted, StopSoundEffect, SetSoundEffectVolume, SetSoundEffectPitch и SetSoundEffectFilter, позволяющие игре контролировать воспроизведение и остановку звуков, а также свойства звуков (громкость и высоту). Например, если шарик выпадает из лабиринта, метод MarbleMazeMain::Update вызывает метод Audio::PlaySoundEffect, чтобы воспроизвести звук FallingEvent.
Метод Audio::PlaySoundEffect вызывает метод IXAudio2SourceVoice::Start, чтобы начать воспроизведение звука. Если метод IXAudio2SourceVoice::Start уже вызывался, он не запускается повторно. Затем метод Audio::PlaySoundEffect выполняет специальную логику для определенных звуков.
Для всех звуков, кроме звука катящегося шарика, метод Audio::PlaySoundEffect вызывает метод IXAudio2SourceVoice::GetState, чтобы определить количество буферов, воспроизводимых исходным тембром. Он вызывает метод IXAudio2SourceVoice::SubmitSourceBuffer, чтобы добавить звуковые данные для этого звука во входную очередь тембра, если нет активных буферов. Метод Audio::PlaySoundEffect также включает двукратное последовательное воспроизведение звука столкновения. Например, это происходит, если шарик сталкивается с углом лабиринта.
Как уже было сказано выше, класс Audio использует флаг XAUDIO2_LOOP_INFINITE при инициализации звука для события пошаговой передачи. Звук начинает циклически воспроизводиться после первого вызова Audio::PlaySoundEffect для этого события. Для упрощения логики воспроизведения звука катящегося шарика Marble Maze не останавливает звук, а отключает его. Если шарик меняет скорость, Marble Maze изменяет высоту и громкость звука для большей реалистичности. Ниже показано, как метод MarbleMazeMain::Update обновляет высоту и громкость звучания шарика при изменении скорости и как он отключает звук при остановке шарика, задавая значение громкости, равное нулю.
Реагирование на события приостановки и возобновления
В статье Структура приложения Marble Maze описано, как игра Marble Maze поддерживает приостановку и возобновление. Если игра приостановлена, она приостанавливает звук. При возобновлении игры звук возобновляется с момента остановки. Такая схема позволяет следовать рекомендации: не использовать ресурсы там, где они заведомо не требуются.
Метод Audio::SuspendAudio вызывается при приостановке игры. Этот метод вызывает метод IXAudio2::StopEngine, чтобы полностью прекратить звук. Хотя IXAudio2::StopEngine немедленно останавливает весь вывод звука, он сохраняет граф звука и параметры эффектов (например, эффект реверберации, применяемый при отскоке шарика).
Метод Audio::ResumeAudio вызывается, когда игра возобновляется. Этот метод вызывает метод IXAudio2::StartEngine, чтобы возобновить воспроизведение звука. Так как вызов метода IXAudio2::StopEngine сохраняет граф звука и параметры эффектов, вывод звука возобновляется с момента остановки.
Обработка смены устройств и наушников
Marble Maze использует обратные вызовы обработчика, чтобы обрабатывать сбои XAudio2, например при смене звукового устройства. Вероятная причина смены устройства — пользователь игры подключил или отключил наушники. Рекомендуется реализовать обратный вызов обработчика, который обрабатывает смену устройств. В противном случае при включении или отключении наушников игра не будет воспроизводить звук, пока не будет перезапущена.
В Audio.h определен класс AudioEngineCallbacks. Этот класс реализует интерфейс IXAudio2EngineCallback.
Интерфейс IXAudio2EngineCallback позволяет коду получать уведомления при возникновении событий обработки звука и критических ошибках обработчика. Для регистрации обратных вызовов Marble Maze вызывает метод IXAudio2::RegisterForCallbacks в Audio::CreateResources — после создания объекта IXAudio2 для обработчика музыки.
Marble Maze не требует уведомления при начале или окончании обработки звука. Поэтому в игре реализованы методы IXAudio2EngineCallback::OnProcessingPassStart и IXAudio2EngineCallback::OnProcessingPassEnd, которые не делают ничего. Для метода IXAudio2EngineCallback:: Онкритикалеррор мрамор лабиринт вызывает метод сетенгиникспериенцедкритикалеррор , который устанавливает флаг m_engineExperiencedCriticalError .
При возникновении критической ошибки обработка звука останавливается и все последующие вызовы к XAudio2 заканчиваются сбоями. Для восстановления после этой ситуации необходимо высвободить текущий экземпляр XAudio2 и создать новый. Метод Audio:: Render , который вызывается из каждого кадра игры, сначала проверяет флаг m_engineExperiencedCriticalError . Если этот флаг установлен, он снимает его, высвобождает текущий экземпляр XAudio2, инициализирует ресурсы и затем запускает фоновую музыку.
Лабиринт мрамора также использует флаг m_engineExperiencedCriticalError , чтобы защититься от вызова XAudio2, когда звуковое устройство недоступно. Например, метод MarbleMazeMain::Update не обрабатывает звук для событий катящегося шарика или столкновения, если установлен этот флаг. Приложение пытается восстановить каждый кадр обработчика звука, если это необходимо; Однако флаг m_engineExperiencedCriticalError может быть задан всегда, если на компьютере нет звукового устройства или если наушники не подключены и нет другого доступного звукового устройства.
Как правило, не следует выполнять блокирующие операции в теле обратного вызова обработчика. Это может привести к проблемам с производительностью. Marble Maze задает флаг в обратном вызове OnCriticalError и затем обрабатывает ошибку во время обычной стадии обработки звука. Дополнительные сведения об обратных вызовах XAudio2 см. в разделе Обратные вызовы XAudio2.
Заключение
На этом рассмотрение игры-примера Marble Maze можно считать завершенным. Хотя эта игра относительно проста, она содержит многие из важных компонентов, присутствующих в любой игре UWP на базе DirectX, и служит хорошим примером, которому вы можете следовать при создании своей собственной игры.
Теперь, когда мы полностью рассмотрели игру, попробуйте поэкспериментировать с ее исходным кодом и посмотреть, что произойдет. Или переходите к статье Создание простой игры UWP с использованием DirectX, где рассматривается другой пример игры UWP на базе DirectX.
Готовы углубиться в работу с DirectX? Ознакомьтесь с нашими руководствами по программированию в DirectX.
Если же вас интересует разработка игр для UWP в целом, обратитесь к документации в разделе Программирование игр.