netlib.narod.ru< Назад | Оглавление | Далее >

Как воспроизвести файл WAV

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

Программа DMusic_PlaySound демонстрирует как инициализировать интерфейсы исполнителя и загрузчика, загрузить сегмент и воспроизвести его.

Проект DMusic_PlaySound

Программа содержит несколько исходных файлов: main.cpp, main.h и DXUtil.cpp. Все они являются уникальными для данного проекта, за исключением файла DXUtil.cpp, который является частью набора вспомогательных файлов DirectX SDK.

Проект использует следующие библиотеки: dxguid.lib, winmm.lib и dsound.lib. Вы можете спросить, почему нет библиотеки с именем dmusic.lib. Я не знаю, что вам ответить. Microsoft решила поместить функции DirectMusic в библиотеку DirectSound. Я предполагаю, что это вызвано тем, что они большей частью совместно используют одну и ту же логику.

Что-то я давно не показывал вам новых иллюстраций. Взгляните на рис. 7.1, где показан основной поток выполнения примера программы.


Рис. 7.1. Поток выполнения программы DMusic_PlaySound

Рис. 7.1. Поток выполнения программы DMusic_PlaySound


На рис. 7.1 видно, что WinMain() вызывает функцию bInitializeSoundSystem(). Эта функция инициализации выполняет несколько вызовов функций DirectX для инициализации звуковой системы. Затем программа ожидает события мыши и воспроизводит файл WAV с помощью функции vPlaySound(). Если вы еще этого не сделали, запустите программу и щелкните по ее окну левой кнопкой мыши чтобы воспроизвести файл WAV. Разве вам не понравился этот замечательный тестовый файл WAV? Эй, я знаю, что он не производит особого впечатления. Если вы хотите поэкспериментировать, замените файл testsound.wav одним из ваших собственных звуковых файлов. Программа должна работать с любым WAV-файлом.

Заголовочный файл Main.h

Откройте заголовочный файл main.h и следуйте за мной. Заголовочный файл в этом примере очень простой и короткий. Вот он целиком:

#ifndef MAIN_H
#define MAIN_H
#define STRICT 
#include <windows.h>
#include <stdio.h>
#include <D3DX9.h>
#include <dmusici.h>
#include <dsound.h>
#include <dshow.h>
#include <dxutil.h>

// Прототипы функций
LRESULT WINAPI fnMessageProcessor(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void vCleanup(void);
bool bInitializeSoundSystem(HWND hWnd);
void vPlaySound(void);

// Глобальные звуковые данные
IDirectMusicLoader8       *g_pLoader;
IDirectMusicPerformance8  *g_pPerformance;
IDirectMusicSegment8      *g_pSound;
#endif

Список включаемых файлов не должен преподнести вам много сюрпризов. В основном это обычные включаемые файлы для Windows-программы. Единственный файл, добавляемый специально для DirectMusic — это dmusici.h.

В разделе прототипов функций появились две новые функции: bInitializeSoundSystem() и vPlaySound(). Назначение обоих очевидно из их названий. (Разве вы не любите самодокументируемый код?)

Следующий блок кода заголовочного файла содержит глобальные переменные, которые я использую в программе. Если вы прочитали предыдущий раздел этой главы, они должны выглядеть для вас очень знакомо. Интерфейс g_pLoader используется для загрузки звукового файла, интерфейс g_pPerformance предназначен для воспроизведения звука, а интерфейс g_pSound содержит загружаемые из звукового файла данные.

ПРИМЕЧАНИЕ
Я вовсе не поощряю вас использовать глобальные переменные в коде. Я применяю их в примерах только для того, чтобы сделать код как можно проще.

Файл программы Main.cpp

Главная часть кода расположена в файле программы main.cpp. Пришло время открыть его. Как обычно, нашего внимания требует функция WinMain(). Она содержит стандартный код инициализации приложения Windows и несколько новых вызовов функций. Вот фрагмент кода, который должен заинтересовать вас:

// Инициализация Direct Sound
bRet = bInitializeSoundSystem(hWnd);
if(bRet == 0) {
   MessageBox(hWnd, "Initialization Failure",
             "Failed to initialize Direct Sound",
             MB_ICONEXCLAMATION | MB_OK);
   // Сбой в программе, выход
   exit(1);
}

Я вызываю функцию bInitializeSoundSystem() сразу после того, как создано окно программы. Функция получает один параметр — дескриптор окна. Если функция возвращает 0, значит ее выполнение закончилось неудачно. В этом случае я вывожу на экран окно с сообщением об ошибке и завершаю работу программы после того, как пользователь щелкнет по кнопке OK. В противном случае все работает как предполагалось и выполнение кода продолжается.

Функция bInitializeSoundSystem()

Ох парни, сейчас начнется настоящее веселье. Данная функция содержит весь код, необходимый для инициализации DirectMusic. Кроме того, она выполняет загрузку звукового файла, воспроизводимого программой.

Как инициализировать COM

Первая вещь, которую должен сделать код, — инициализация COM. DirectX использует COM-интерфейсы, так что это неизбежное зло (или добро). Если вы не знакомы с COM, я рекомендую вам пойти в любимый книжный магазин и посмотреть несколько книг, посвященных этой технологии. Вызов для инициализации выглядит следующим образом:

CoInitialize(NULL);

Исключительно просто, правда? К счастью, это все, что требуется сделать для инициализации COM. После этого вы можете создавать интерфейсы DirectX.

Создание интерфейса загрузчика

Следующий фрагмент кода создает интерфейс загрузчика. Как вы, возможно, помните, интерфейс загрузчика отвечает за загрузку звуковых данных, таких как файлы WAV и файлы MIDI. Вот как выглядит соответствующий код:

if(FAILED(hResult = CoCreateInstance(CLSID_DirectMusicLoader, NULL,
   CLSCTX_INPROC, IID_IDirectMusicLoader8,
   (void**) &g_pLoader))) {
   return(0);
}

Для получения интерфейса загрузчика я вызываю функцию CoCreateInstance(). Интерфейс IDirectMusicLoader8 использует CLSID CLSID_DirectMusicLoader и идентификатор интерфейса IID_IDirectMusicLoader8. Указатель на интерфейс сохраняется в глобальной переменной g_pLoader.

Я знаю, что если вы незнакомы с COM, все это может казаться вам немного чуждым, но поводов для беспокойства нет. Вам достаточно просто скопировать этот код в свою собственную программу. Потребность модифицировать приведенный здесь код инициализации DirectMusic возникает крайне редко.

Создание интерфейса исполнителя

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

if(FAILED(hResult = CoCreateInstance(CLSID_DirectMusicPerformance, NULL,
   CLSCTX_INPROC, IID_IDirectMusicPerformance8,
   (void**) &g_pPerformance))) {
   return(0);
}

И снова для создания интерфейса я применяю функцию CoCreateInstance(). Интерфейс IDirectPerformance8 использует CLSID CLSID_DirectMusicPerformance и идентификатор интерфейса IID_DirectMusicPerformance8. Указатель на интерфейс я сохраняю в глобальной переменной g_pPerformance.

Инициализация аудиосистемы

В отличие от загрузчика, исполнитель требует, чтобы после успешного создания интерфейса была выполнена его инициализация. Эта задача осуществляется функцией IDirectMusicPerformance8::InitAudio(), которая инициализирует исполнителя и устанавливает для него аудио-путь. Вот как выглядит прототип функции:

HRESULT InitAudio(
   IDirectMusic** ppDirectMusic,
   IDirectSound** ppDirectSound,
   HWND hWnd,
   DWORD dwDefaultPathType,
   DWORD dwPChannelCount,
   DWORD dwFlags,
   DMUS_AUDIOPARAMS *pParams
);

Первый параметр, ppDirectMusic, позволяет функции возвратить созданный интерфейс DirectMusic. Если значение этого параметра равно NULL, то интерфейс DirectMusic создается и используется внутри объекта исполнителя. Я предпочитаю использовать NULL, поскольку это упрощает код.

Второй параметр предназначен для тех же целей, что и первый, за исключением того, что от хранит или возвращает интерфейс DirectSound. Для этого параметра я также предпочитаю использовать значение NULL.

Третий параметр, hWnd, предназначен для передачи дескриптора того окна для которого создается интерфейс DirectSound. Если значение параметра равно NULL, используется фоновое окно. Я предпочитаю использовать здесь NULL.

Четвертый параметр, dwDefaultPathType, задает тип аудио-пути по умолчанию. Возможные значения перечислены в таблице 7.4.

Таблица 7.4. Типы аудио-пути

Значение Описание
DMUS_APATH_DYNAMIC_3D Трехмерный звук
DMUS_APATH_DYNAMIC_MONO Монофонический звук
DMUS_APATH_DYNAMIC_STEREO Стереофонический звук
DMUS_APATH_SHARED_STEREOPLUSREVERB Стереофонический звук с эхом

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

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

Шестой параметр, dwFlags, позволяет вам задать набор функциональных возможностей, которые вы хотите видеть в объекте исполнителя. Доступные флаги и их назначение описаны в таблице 7.5.

Таблица 7.5. Флаги функциональных возможностей исполнителя

Значение Описание
DMUS_AUDIOF_3D Трехмерные буферы
DMUS_AUDIOF_ALL Все возможности
DMUS_AUDIOF_BUFFERS Множественные буферы
DMUS_AUDIOF_DMOS Дополнительные DMO (DirectX Media Object)
DMUS_AUDIOF_EAX Эффекты EAX
DMUS_AUDIOF_ENVIRON Моделирование среды
DMUS_AUDIOF_STREAMING Изменяющиеся формы волн

В рассматриваемом примере я использую флаг DMUS_AUDIOF_ALL. Он указывает объекту исполнителя использовать любые доступные возможности. Это удобный вариант, позволяющий пользователю отключить отдельные возможности, которые могут привести к снижению быстродействия.

Седьмой параметр, pParams, позволяет задать желаемые аудио параметры в виде структуры данных DMUS_AUDIOPARAMS. Я обычно использую значения параметров по умолчанию и указываю здесь NULL.

Функция инициализации аудиосистемы выполнила свою работу. Последний шаг, необходимый для инициализации интерфейса исполнителя — вызов функции IDirectMusicPerformance8::GetDefaultAudioPath(), возвращающей аудио-путь по умолчанию, созданный функцией инициализации аудиосистемы. Аудио-путь требуется для установки уровня громкости. Если вы не желаете связываться с регулировкой громкости, можно пропустить этот этап.

Вот как выглядит код, описываемый в приведенном выше тексте:

// Инициализация аудиосистемы
if(FAILED(hResult = g_pPerformance->InitAudio(
   NULL,
   NULL,
   hWnd,
   DMUS_APATH_DYNAMIC_STEREO,
   4,
   DMUS_AUDIOF_ALL,
   NULL
   ))) {
   return(0);
}
// Получение пути по умолчанию
if(FAILED(g_pPerformance->GetDefaultAudioPath(&dmAudioPath)))
   return(0);
Как регулировать громкость

Как я намекал раньше, интерфейс IDirectMusicAudioPath8 позволяет вам регулировать уровень громкости. Для этого предназначена функция SetVolume(), прототип которой выглядит следующим образом:

HRESULT SetVolume(
   long lVolume,
   DWORD dwDuration
);

Первый параметр, lVolume, устанавливает желаемый уровень громкости в сотнях децибел. Допустимы значения от –9600 до 0. Значение 0 соответствует максимальной громкости.

Второй параметр, dwDuration, задает период времени за который осуществляется изменение громкости. Если его значение равно 0, система изменит громкость как только это будет возможно.

Вот как выглядит используемый в примере код:

// Установка громкости
if(FAILED(dmAudioPath->SetVolume(0,0)))
   return(0);
Загрузка звукового файла

Вы помните интерфейс загрузчика, который создали минуту назад? Пора снова воспользоваться им для загрузки тестового файла WAV, включенного в сопроводительные файлы. Чтобы сделать это, обратимся к функции LoadObjectFromFile(). Перед тем, как я покажу вам ее прототип, взгляните на код из программы:

// Загрузка звукового файла
if (FAILED(g_pLoader->LoadObjectFromFile(
   CLSID_DirectMusicSegment,
   IID_IDirectMusicSegment8,
   L"testsound.wav",
   (LPVOID*) &g_pSound
   )))
{
   return(0);
}

// Загрузка данных
if (FAILED (g_pSound->Download(g_pPerformance))) {
   return(0);
}

В коде я использую функцию загрузки файла, чтобы загрузить WAV-файл с жесткого диска. Затем я загружаю сегмент в объект исполнителя для воспроизведения. Вот что видно с высоты птичьего полета.

Прототип функции LoadObjectFromFile() выглядит следующим образом:

HRESULT LoadObjectFromFile(
   REFGUID rguidClassID,
   REFIID iidInterfaceID,
   WCHAR *pwzFilePath,
   void ** ppObject
);

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

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

Третий параметр, pwzFilePath, является именем загружаемого файла. В рассматриваемом примере я использую файл testsound.wav. Вы можете заметить букву L перед строкой с именем файла. Я поместил ее потому, что в данном параметре должно передаваться имя в кодировке Unicode (где для представления одного символа используются два байта).

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

Как загрузить сегмент

Теперь вы должны загрузить сегмент в объект исполнителя. Эту задачу выполняет функция IDirectMusicSegment8::Download(), прототип которой выглядит так:

HRESULT Download(
   IUnknown* pAudioPath
);

Как приятно и просто. Всего один параметр, который является указателем на интерфейс, в который загружается сегмент. Я в данном параметре передаю указатель на объект исполнителя g_pPerformance.

Взгляните на рис. 7.2, чтобы увидеть описанные к данному моменту этапы.


Рис. 7.2. Этапы инициализации DirectMusic

Рис. 7.2. Этапы инициализации DirectMusic


На рис. 7.2 видно, как вы инициализировали COM, создали загрузчик, создали исполнителя, инициализировали аудиосистему, получили аудио-путь по умолчанию, установили уровень громкости, загрузили файл и, наконец, загрузили сегмент WAV. Этот набор шагов будет повторяться каждый раз, когда вы будете использовать в своих программах DirectMusic, так что запомните его (или по крайней мере, заложите эту страницу).

Функция vPlaySound()

Пример программы обрабатывает события левой кнопки мыши и вызывает при каждом событии функцию vPlaySound(). Обработкой событий занимается функция fnMessageProcessor(), но представляющий интерес код выполняется в функции воспроизведения звука. Давайте взглянем на код этой функции:

void vPlaySound(void)
{
   // Воспроизведение звукового сегмента
   g_pPerformance->PlaySegmentEx(
      g_pSound,
      NULL,
      NULL,
      DMUS_SEGF_DEFAULT | DMUS_SEGF_SECONDARY,
      0,
      NULL,
      NULL,
      NULL
   );
}

Функция vPlaySound() очень проста. Фактически она состоит из единственного вызова функции IDirectMusicPerformance8::PlaySegmentEx().

Воспроизведение звука

Функция PlaySegmentEx() начинает воспроизведение загруженного сегмента. У нее есть несколько параметров, но как видите, в коде моего примера значения многих из них равны NULL. Вот как выглядит прототип функции:

HRESULT PlaySegmentEx(
   IUnknown* pSource,
   WCHAR *pwzSegmentName,
   IUnknown* pTransition,
   DWORD dwFlags,
   __int64 i64StartTime,
   IDirectMusicSegmentState** ppSegmentState,
   IUnknown* pFrom,
   IUnknown* pAudioPath
);

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

Второй параметр, pwzSegmentName, в DirectX 9.0 не используется.

Третий параметр, pTransition, позволяет задать модуляцию для сегмента. Я передаю в этом параметре NULL.

Четвертый параметр, dwFlags, позволяет вам указать набор флагов, определяющих различные параметры воспроизведения. В рассматриваемом примере для этого параметра я использую флаги DMUS_SEGF_DEFAULT и DMUS_SEGF_SECONDARY. Эти флаги указывают, что сегмент воспроизводится в его границах по умолчанию и что сегмент воспроизводится как вторичный звук. Доступно еще много других флагов, и я рекомендую вам посмотреть их описание в документации DirectX SDK.

Пятый параметр, i64StartTime, задает начальное время для сегмента. Я передаю в этом параметре NULL чтобы воспроизведение звука началось немедленно.

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

Седьмой параметр, pFrom, позволяет указать интерфейс для остановки воспроизведения когда стартует новый сегмент. Здесь я также передаю NULL.

Восьмой параметр, pAudioPath, сообщает системе какой аудио-путь используется для воспроизведения сегмента. Я присваиваю этому параметру значение NULL чтобы для воспроизведения использовался аудио-путь по умолчанию.

Теперь запустите программу и щелкните несколько раз по ее окну левой кнопкой мыши. Важная особенность программы заключается в том, что она может одновременно воспроизводить несколько звуков. Это может не произвести на вас большого впечатления, если вы никогда раньше не занимались воспроизведением звука, но поверьте мне — это очень круто. Замечательно то, что DirectMusic за вас выполняет всю необходимую буферизацию.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz