Программирование звука с использованием XAudio2.
Автор: s3dworld
Всем доброго времени суток! Это моя первая статья на GameDev.ru, которая появилась в связи с тем, что в недавнем времени решил я сам перейти на библиотеку XAudio2. Документации по ней в Интернете на русском нет.
Введение
XAudio2
Первые приготовления
Сохраняем аудио-данные
Источник звука
Тестируем
Заключение
Введение
Помимо XAudio2 существует несколько библиотек для вывода звука: DirectSound, OpenAL, FMOD, BASS. Но мне хочется поговорить именно об XAudio2, так как об этом нет русских статей.
Как заявляет сама Microsoft (смотреть тут), она больше не поддерживает DirectSound, и новые проекты, в зависимости от типа, должны использовать новое программное обеспечение. Для игр это XAudio2 и X3DAudio, для всего остального — Windows Core Audio.
Библиотека XAudio2 входят в состав Microsoft DirectX SDK и при её использовании может возникнуть такая же проблема, как и с библиотекой D3DX9 — постоянно нужно пользователю обновлять Microsoft DirectX. Самая последняя версия Microsoft DirectX (июнь 2010) включает следующие библиотеки XAudio2:
- XAudio2_0.dll;
- XAudio2_1.dll;
- XAudio2_2.dll;
- XAudio2_3.dll;
- XAudio2_4.dll;
- XAudio2_5.dll;
- XAudio2_6.dll;
- XAudio2_7.dll;
- XAudioD2_7.dll.
Так что если ваша программа не заработала на другом компьютере, то возможны два варианта:
- Вы что-то сделали не так;
- У пользователя не стоят те же самые библиотеки, которые вы использовали для создания программы.
А теперь перейдём к рассмотрению XAudio2...
XAudio2
Первые приготовления
В XAudio2 имеются два основных интерфейса: IXAudio2 и IXAudio2Voice.
- IXAudio2 - это интерфейс, который работает со всеми аудио-голосами (IXAudio2Voice).
- IXAudio2Voice - это базовый интерфейс для трёх других: IXAudio2SourceVoice, IXAudio2SubmixVoice и IXAudio2MasteringVoice.
Все эти интерфейсы (IXAudio2SourceVoice, IXAudio2SubmixVoice и IXAudio2MasteringVoice) обрабатывают несжатые аудио-данные.
- IXAudio2SourceVoice этот интерфейс, который непосредственно получает несжатый аудио-данные. Далее он передаёт данные в IXAudio2SubmixVoice, если конечно тот существует (что является необязательным) или же в IXAudio2MasteringVoice.
- IXAudio2SubmixVoice служит для всевозможных преобразований аудио-данных. Результат своей работы он передаёт IXAudio2MasteringVoice.
- IXAudio2MasteringVoice тоже может производить некоторые операции над аудио-данными, и именно через этот интерфейс данные попадают в аудио-устройство. Данных интерфейсом может быть создано несколько, но одновременно может работать только один.
Структур для обработки голоса может быть несколько, их разнообразие велико. От простого:
До:
Перейдём к коду...
Создадим в Visual C++ новый пустой проект Win32 приложения. Укажем где у нас находятся заголовочные файлы (include) и библиотеки (lib) от Microsoft DirectX SDK.
Замечание: Для работы с XAudio2 не нужно линковщику указывать статическую библиотеку (lib), достаточно к проекту подключить заголовочный файл XAudio2.h.
Начнём с создания класса для всех операций с аудио-библиотекой, назовём его Audio. Добавим к проекту файл Audio.h и внесём в него:
#pragma once #define AUDIO_OK 0 #define AUDIO_ERROR_CREATE_AUDIO -1 #define AUDIO_ERROR_CREATE_MASTERING_VOICE -2 #include <XAudio2.h> class Audio { private: static Audio* instance; int resultCode; bool isCreate; IXAudio2* audio; IXAudio2MasteringVoice* masteringVoice; Audio(void); ~Audio( void); public: static Audio* GetInstance( void); void Release( void); int GetResult( void) const; bool Create( void); };
Теперь разберём что к чему...
#define AUDIO_OK 0 #define AUDIO_ERROR_CREATE_AUDIO -1 #define AUDIO_ERROR_CREATE_MASTERING_VOICE -2
Нам это будет нужно для контроля выполнения функций. Пока у нас будет всего одна функция, которая возможно выполнится с ошибкой.
int GetResult(void) const;
Возвращает тот самый код выполнения.
static Audio* instance; static Audio* GetInstance(void); void Release( void);
Для того чтобы в памяти находился только один экземпляр класса Audio, мы будем хранить его адрес в статической переменной.
bool Create(void);
Служит для создания двух интерфейсов: IXAudio2 и IXAudio2MasteringVoice.
bool isCreate;
Так мы будем знать создали ли мы главные интерфейсы.
Теперь создадим файл Audio.cpp:
#include "Audio.h" // Пока у нас нет экземпляра в памяти Audio* Audio::instance=0; // Конструктор Audio::Audio(void) { resultCode=AUDIO_OK; isCreate=false; audio=0; masteringVoice=0; } // Деструктор Audio::~Audio( void) { // Сначала освобождаем все голоса (у нас пока только IXAudio2MasteringVoice) if( masteringVoice) masteringVoice->DestroyVoice( ); // Теперь освобождаем главный интерфейс if( audio) audio->Release( ); // Если была инициализирована работа с COM, то деинициализируем её if( isCreate) CoUninitialize( ); } // Получаем указатель на единственный экземпляр класса // Если экземпляр ещё не создан, то создаём его и возвращает на него указатель Audio* Audio::GetInstance( void) { if( !instance) instance=new Audio( ); return instance; } // Удаляем экземпляр void Audio::Release( void) { if( instance) { delete instance; instance=0; } } // Получение кода выполнения int Audio::GetResult( void) const { return resultCode; } // Создание bool Audio::Create( void) { // Пока будем считать что у нас всё хорошо resultCode=AUDIO_OK; // Иницилизируем работы с COM в отдельном потоке CoInitializeEx( 0,COINIT_MULTITHREADED); // Создаём главный интерфейс if( XAudio2Create( &audio,0,XAUDIO2_DEFAULT_PROCESSOR)!=S_OK) { CoUninitialize( ); resultCode=AUDIO_ERROR_CREATE_AUDIO; return false; } // Создаём главный голос if( audio->CreateMasteringVoice( &masteringVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, 0, 0)!=S_OK) { CoUninitialize( ); resultCode=AUDIO_ERROR_CREATE_MASTERING_VOICE; return false; } // Если дошли сюда, то всё в порядке isCreate=true; return true; }
Тут разберём только функцию Create()...
Функция XAudio2Create используется для создания главного интерфейса (IXAudio2). Выглядит следующим образом:
HRESULT XAudio2Create(IXAudio2** ppXAudio2, UINT32 Flags=0, XAUDIO2_PROCESSOR XAudio2Processor=XAUDIO2_DEFAULT_PROCESSOR)
Замечание: При работе с COM не обязательно у функции заполнять все параметры если им присваиваются значения по умолчанию.
- ppXAudio2 указатель на указатель интерфейса IXAudio2.
- Flags используется для установки режима работы. Может быть либо 0, либо XAUDIO2_DEBUG_ENGINE (в этом случае будет запрашивать отладочную информация из библиотеки).
- XAudio2Processor указывает какой процессор использовать для обработки аудио. Тут мы оставили значение по умолчанию, а полный список можно посмотреть в структуре XAUDIO2_WINDOWS_PROCESSOR_SPECIFIER.
Если функция завершается успешно, то обратная величина S_OK.
Далее мы создаём главный интерфейс для передачи аудио-данных на аудио-устройство через функцию CreateMasteringVoice интерфейса IXAudio2. Вот как она выглядит:
HRESULT CreateMasteringVoice(IXAudio2MasteringVoice** ppMasteringVoice, UINT32 InputChannels=XAUDIO2_DEFAULT_CHANNELS, UINT32 InputSampleRate=XAUDIO2_DEFAULT_SAMPLERATE, UINT32 Flags=0, UINT32 DeviceIndex=0, const XAUDIO2_EFFECT_CHAIN* pEffectChain=NULL)
- ppMasteringVoice указатель на указать интерфейса IXAudio2MasteringVoice.
- InputChannels указывает сколько каналов будет обрабатывать IXAudio2MasteringVoice.
- InputSampleRate устанавливает норму отбора. Это значение должно быть кратно XAUDIO2_QUANTUM_DENOMINATOR и лежать в промежутки от XAUDIO2_MIN_SAMPLE_RATE до XAUDIO2_MAX_SAMPLE_RATE (включая сами промежутки). По умолчанию используется значение XAUDIO2_DEFAULT_SAMPLERATE.
- Flags определяет поведение и всегда должен быть равен нулю.
- DeviceIndex указывает на какое аудио-устройство прикрепить IXAudio2MasteringVoice. Указав значение 0 мы прикрепим IXAudio2MasteringVoice к глобальному аудио-устройству.
- pEffectChain указатель на список эффектов. Нам он пока не нужен, поэтому поставим 0.
Если функция завершается успешно, то обратная величина S_OK.
Теперь приступим к созданию аудио-буферов...
23 апреля 2011