Использование DirectX Audio 8.
Автор: Mason Smith
Итак, у вас есть прекрасная игра. Она имеет всё, что необходимо: восхитительную графику, сверхсветовую скорость реакции на действия игрока, короче, нормально работает. Отсутствует только одна вещь: ЗВУК. Качество звука может придать игре жизнь или же погубить её. Итак, вы хотите заполучить достаточную систему звука. При помощи чего это сделать? Ответ прост: DirectX Audio!
Этот урок поможет вам научиться пользоваться DirectX Audio. Вы научитесь загружать и проигрывать WAV и MIDI файлы. В этом уроке нет разъяснений по поводу 3D звука. Урок не требует каких-либо знаний по звуку или программированию звука.
Не забудьте прилинковать dsound.lib и dxguid.lib, а так же включить заголовочный файл dmusici.h.
Инициализация DirectX Audio.
Перед тем как мы сможем проиграть звук, необходимо проинициализировать DirectX Audio. Это делается в три этапа:
1. Инициализировать COM.
2. Создать и инициализировать объект исполнения.
3. Создать загрузчик.
1. Инициализация COM.
В отличие от других компонент DirectX, Direct Audio - чистый COM. Это означает, что вам необходимо создать COM объект самостоятельно функцией CoCreateInstance(). Инициализация современной COM системы, однако, очень проста.
CoInitialize(NULL);
Этот простой вызов функции позволяет вам теперь использовать COM, чтобы создать объект DirectX Audio.
2. Создание и инициализация объекта исполнения.
Сначала, давайте поговорим об объекте исполнения (performance object). Объект исполнения управляет всеми данными, идущими от звуковых сегментов - до синтезаторов, воспроизводящие звуки. По существу, это основной интерфейс DirectX Audio. Для того чтобы создать объект исполнения, вам необходимо создать объект интерфейса IDirectMusicPerformance8. Объект создаётся вызовом CoCreateInstance().
IDirectMusicPerformance8* g_performance = NULL; CoCreateInstance(CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC, IID_IDirectMusicPerformance8, ( void**) &g_performance);
Этот код объявляет объект IDirectMusicPerformance8 и создаёт его. Теперь нам его нужно проинициализировать. Для своей инициализации объект интерфейса IDirectMusicPerformance8 использует свой же метод InitAudio(). Давайте взглянем на эту функцию:
HRESULT IDirectMusicPerformance8::InitAudio(IDirectMusic** ppDirectMusic, IDirectSound** ppDirectSound, HWND hWnd, DWORD dwDefaultPathType, DWORD wPChannelCount, DWORD dwFlags, DMUS_AUDIOPARAMS pParams);
Теперь покажем рабочий пример этой функции:
g_performance->InitAudio(NULL, // не требуется никаких интерфейсов NULL, // не требуется никаких интерфейсов NULL, // идентификатор окна не требуется (пустой) // аудио путь по умолчанию DMUS_APATH_SHARED_STEREOPLUSREVERB, 64, // 64 канала выделяется для аудио пути DMUS_AUDIOF_ALL, // поодержка всех возможностей аудио NULL); // параметры аудио по умолчанию
3. Создание загрузчика.
Объект загрузчика загружает всё содержимое аудио, такого как MIDI или WAV, в сегменты, хранящие данные. Загрузчик представлен интерфейсом IDirectMusicLoader8. Создание объекта требует простой вызов, как вы могли догадаться, это CoCreateInstance():
IDirectMusicLoader8* g_loader = NULL; CoCreateInstance(CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC, IID_IDirectMusicLoader8, ( void**) &g_loader);
Воспроизведение аудио.
Итак, мы подготовили DirectX Audio, теперь нам нужно что-нибудь воспроизвести. На очереди - создание сегмента. Посмотрим, что нам нужно сделать:
1. Создать сегмент.
2. Загрузить сегмент.
3. Загрузить трек.
4. Проиграть сегмент.
1. Создание сегмента.
Вы можете удивиться, узнав, что такое сегмент. Сегмент - это объект, включающий в себя звуковые данные, загруженные из звукового файла. Любой звук, воспроизводящийся DirectX Audio, находится в сегменте. Следуя общему принципу, сегментный объект представляется интерфейсом IDirectMusicSegment8 и инициализируется вызовом в стиле COM.
IDirectMusicSegment8* g_segment = NULL; CoCreateInstance(CLSID_DirectMusicSegment, NULL, CLSCTX_INPROC, IID_IDirectMusicSegment8, ( void**) &g_segment);
2. Загрузка сегмента.
Теперь в сегмент нужно загрузить звуковое содержание. Здесь мы используем загрузчик. Сначала, необходимо выставить директорию поиска для загрузчика. Это можно выполнить функцией IDirectMusicLoader8::SetSearchDirectory().
HRESULT IDirectMusicLoader8::SetSearchDirectory(REFGUID rguidClass, WCHAR* pwszPath, BOOL fClear);
Если вы хотите директорию поиска выставить, как директорию, в которой запущена ваша программа, то необходимо сделать, как показано ниже:
char searchPath[MAX_PATH]; WCHAR wSearchPath[MAX_PATH]; GetCurrentDirectory(MAX_PATH, searchPath); MultiByteToWideChar( CP_ACP, 0, searchPath, -1, wSearchPath, MAX_PATH); g_loader->SetSearchDirectory( GUID_DirectMusicAllTypes, wSearchPath, FALSE);
Здесь функция GetCurrentDirectory() - получает директорию, в которой была запущена программа. MultiByteToWideChar() - обычный массив символов конвертируется в массив типа WCHAR. Если вы используете dxutil.h, то вы может воспользоваться функцией DXUtil_ConvertGenericStringToWide(), хотя на самом деле эта функция просто использует MultiByteToWideChar(), только выглядит лучше. Наконец, мы вызываем метод SetSearchDirectory() с предопределённым GUID, который открывает доступ ко всем звуковым типам для чтения. Теперь устанавливаем путь поиска, откуда мы хотим загрузить файл. Это выполняется одним вызовом функции:
HRESULT IDirectMusicLoader::LoadObjectFromFile(REFGUID rguidClassID, REFIID iidInterfaceID, WCHAR *pwzFilePath, void **ppObject);
Ниже показано, как вы можете загрузить файл test.wav.
char filename[MAX_PATH] = "test.wav"; WCHAR wFilename[MAX_PATH]; MultiByteToWideChar(CP_ACP, 0, filename, -1, wFilename, MAX_PATH); g_loader->LoadObjectFromFile( CLSID_DirectMusicSegment, IID_IDirectMusicSegment8, wFilename, ( void**) &g_segment);
3. Загрузка трека.
Чтобы проиграть звук, вы должны загрузить его в "синтезатор". Это выполняет функцией IDirectMusicSegment8::Download()
HRESULT IDirectMusicSegment8::Download(Iunknown *pAudioPath);
Всё что осталось - загрузить трек в объект исполнения, как показано ниже:
g_segment->Download(g_performance);
4. Воспроизведение сегмента.
Наконец, настоящая вещь. Воспроизведение звука, как большинство операций в DirectX Audio, требует только одну функцию. Вы можете использовать PlaySegment() или PlaySegmentEx(). Мы рассмотрим вызов PlaySegmentEx(), поскольку он предлагает больше функциональности.
HRESULT IDirectMusicPerformance8::PlaySegmentEx(IUnknown* pSource, WCHAR* pwzSegmentName, IUnknown* pTransition, DWORD dwFlags, __int64 i64StartTime, IDirectMusicSegmentState** ppSegmentState, IUnknown* pFrom, IUnknown* pAudioPath);
Не смотря на то, что эта функция имеет запутанный вид, она очень проста в использовании. Только пара параметров актуальна для нас.
pSource - звуковые данные, которые будут воспроизводиться. В нашем случае вы будите проигрывать g_segment.
i64StartTime - это точное время, когда звук начнёт воспроизводиться. Обычно, вы просто выставляете этот параметр в ноль, чтобы указать, что звук начнёт воспроизводиться немедленно.
Заметьте, что это функция интерфейса IDirectMusicPerformance8. Это функция не интерфейса IDirectMusicSegment, потому что сегментный интерфейс только управляет звуковыми данными и информацией воспроизведения. IDirectMusicPerformance8 управляет всеми манипуляциями с сегментом, включая воспроизведение и остановку воспроизведения. Теперь, давайте, наконец, проиграем какую-нибудь музыку.
g_performance->PlaySegmentEx(g_segment, NULL, NULL, 0, 0, NULL, NULL, NULL);
Другие функции.
Теперь вы можете загружать и воспроизводить мелодии при помощи DirectX Audio. Вам необходим дополнительный контроль над вашей музыкой. Только одна есть вещь, важная как запуск воспроизведения звука, это остановка воспроизведения. Для этого просто достаточно воспользоваться одной из функций:
HRESULT IDirectMusicPerformance8::Stop(IDirectMusicSegment* pSegment, IDirectMusicSegmentState* pSegmentState, MUSIC_TIME mtTime, DWORD dwFlags); HRESULT IDirectMusicPerformance8::StopEx( IUnknown* pObjectTostop, __int64 i64StopTime, DWORD dwFlags);
Вы можете использовать любую из этих функций для остановки. В обоих случаях два последних параметра имеют одинаковую функциональность.
i64StopTime - говорит, когда сделать остановку. Если вы хотите остановить звук немедленно, выставьте этот параметр в 0.
dwFlags - когда остановка должна произойти в сегменте, тоже можно выставить в 0.
Метод StopEx() имеет дополнительную функциональность, потому что он может остановить сегмент или аудио путь (audiopath). Для остановки одного звука, будем использовать следующее:
g_performance->StopEx(g_segment, 0, 0);
Для того, чтобы выключить все треки, не нужно их выключать по отдельности, достаточно вызвать функцию Stop():
g_performance->Stop(NULL, NULL, 0, 0);
Когда вы выставляете в функции сегмент в ноль, то останавливаются все звуки, которые в этот момент воспроизводятся. Я попытался это проделать с функцией StopEx(), но при этом программа некорректно «свалилась».
Ещё один момент. Что, если мы захотим узнать, проигрываются сейчас звуки или нет? Используйте для этого функцию IsPlaying().
HRESULT IDirectMusicPerformance8::IsPlaying(IDirectMusicSegment* pSegment, IDirectMusicSegmentState* pSegState);
Эта функция возвращает S_OK, если сегмент воспроизводится.
if (g_performance->IsPlaying( g_segment) == S_OK) { // пока играет музыка. }
Ещё одна функция, о которой я бы хотел рассказать, перед тем как мы кончим - SetRepeats().
HRESULT IDirectMusicSegment8::SetRepeats(DWORD dwRepeats);
Эта функция устанавливает число повторений для звука. Если вы воспроизводите музыку на фоне и, например, хотите повторять её постоянно, выставьте значение параметра этой функции равным DMUS_SEG_REPEAT_INFINITE. Мелодия будет проигрываться с повторениями до тех пор, пока вы явно не вызовите останавливающую функцию.
Завершение работы.
Мы хорошо провели время, теперь необходимо корректно завершить работу программы. Первое, что нужно сделать - остановить воспроизведение всех звуков. Это делается функцией Stop(), указанной ниже. Затем, мы вызываем метод IDirectMusicPerformance8::CloseDown(), который не имеет никаких параметров. После мы освобождаем все объекты, применяя к ним вызовы Release(). Наконец, мы используем CoUninitialize() для отключения COM.
g_performance->CloseDown(); g_loader->Release( ); g_performance->Release( ); g_segment->Release( ); CoUninitialize( );
Если вы нашли урок полезным, нашли ошибки или просто есть вопросы - пишите письма автору: . (Понятное дело, на англицком).
Оригинальный урок находится тут.
Урок переведён и выложен с разрешения GameDev.net и автора статьи Mason "Masonium" Smith
Автор вольного перевода: Сергей Ваткин.
Корректировка статьи: VisoR. (5 мая 2003 год).
20 февраля 2002