netlib.narod.ru | < Назад | Оглавление | Далее > |
Хотите ли вы, чтобы в вашей игре было реализовано воспроизведение файлов MP3? Если да, этот раздел для вас. К сожалению, воспроизведение файлов MP3 требует полностью нового набора интерфейсов и функций. Разве не удивляет такой быстрый переход от радости к сожалениям? Такова жизнь, мой друг.
На тот случай, если последние несколько лет вы провели на необитаемом острове, сообщу, что MP3 — это формат для хранения звуковых данных с высокой степенью сжатия. Большинство людей использует его для песен, но он также подходит и для речи и звуковых эффектов. Единственный недостаток формата MP3 — большая, по сравнению с другими форматами, нагрузка на процессор при воспроизведении. Учитывая производительность современных процессоров, эта особенность не представляет большой проблемы, но ее все же следует иметь в виду.
В сопроводительные файлы к этой книге включен проект с названием DShow_PlayMP3. На рис. 7.4 показано окно, выводимое этой программой.
Рис. 7.4. Окно программы DShow_PlayMP3
Согласен, в изображенном на рис. 7.4 окне нет ничего особенного. Но для этого есть причина: программа создана для воспроизведения файлов MP3, а не для показа вращающихся трехмерных кубов!
Теперь загрузите проект DShow_PlayMP3, чтобы иметь возможность идти дальше. Я рекомендую вам скомпилировать и запустить программу, чтобы услышать результат ее работы. Если вы ничего не услышали, проверьте строку, в которой указано имя файла c:\dxsdk\samples\media\track.mp3. Если в указанном каталоге у вас нет MP3-файла, скорректируйте путь, чтобы он указывал на любой существующий в вашей системе файл MP3. Несколько пригодных для воспроизведения файлов входят в DirectX SDK.
Первая вещь на которой следует остановиться — имя проекта. В отличие от первых двух проектов из этой главы, имя данного проекта начинается с префикса DShow. Я сделал это потому, что данная программа использует DirectShow а не DirectMusic. DirectShow представляет собой отдельный интерфейс DirectX предназначенный для работы с потоковой аудиовизуальной информацией в Windows. Он может применяться для воспроизведения различных форматов, в том числе AVI, MPEG, MP3 и даже WAV. Как видите, вы можете воспроизводить не только звук, но и видео, а также комбинировать оба этих способа. Это действительно замечательная возможность, открывающая дорогу к воспроизведению видеофрагментов в начале вашей игры и между уровнями.
Программа содержит несколько файлов с исходным кодом: main.cpp, main.h и DXUtil.cpp. Все исходные файлы, за исключением DXUtil.cpp, являются уникальными для данного проекта. Кроме того, в проекте используются следующие библиотеки: dxguid.lib, winmm.lib и Strmiids.lib. Файл Strmiids.lib необходим для работы с DirectShow.
Заголовочный файл main.h содержит обычный набор объявлений глобальных переменных и директив включения файлов, необходимых для примера. Вот как выглядит код секции с директивами включения файлов:
#include <windows.h> #include <stdio.h> #include <D3DX9.h> #include <dxutil.h> #include <dshow.h>
Новым в этом блоке является включение файла dshow.h. Он необходим для вызова интерфейсов и функций DirectShow. Если вы планируете использовать функциональность DirectShow, убедитесь, что этот файл есть в списке включаемых.
В следующем блоке кода находятся прототипы функций. Вот как он выглядит:
LRESULT WINAPI fnMessageProcessor(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); void vCleanup(void); bool bPlayTitleMusic(void); void vStopTitleMusic(void); void vCheckMusicStatus(void);
Функция fnMessageProcessor() — это обычный обработчик сообщений Windows. Здесь нет ничего нового — все та же старая чепуха.
Функция vCleanup() вызывается перед выходом из программы. Она выполняет освобождение интерфейсов.
Функция bPlayTitleMusic() вызывается один раз в начале программы. Она инициализирует DirectShow, загружает файл MP3 и начинает его воспроизведение.
Функция vStopTitleMusic() останавливает воспроизведение музыки перед завершением работы программы.
Функция vCheckMusicStatus() проверяет не завершилось ли воспроизведенеи музыкального сегмента. Если да, воспроизведение музыки повторяется с начала.
Далее в заголовочном файле расположены глобальные переменные. Я создаю несколько указателей на интерфейсы и одну логическую переменную, как показано ниже:
bool g_bBackgroundMusicActive = 0; IGraphBuilder *g_pGraph; IMediaControl *g_pMediaControl; IMediaEvent *g_pEvent; IMediaSeeking *g_pSeeking;
Переменная g_bBackgroundMusicActive используется для отслеживания состояния музыки. Если музыка воспроизводится, ее значение равно 1. Если нет, значение равно 0.
Переменная g_pGraph является указателем на интерфейс IGraphBuilder. Эй, эй, что это за граф?
DirectShow построен на базе программных фильтров. Фильтры в DirectShow выполняют операции над потоками данных. Фильтры выполняют множество функций, и в число основных входят:
Например, граф фильтров может читать файл MP3 и вормировать звук для его вывода аудиооборудованием. Эти действия показаны на рис. 7.5.
Рис. 7.5. Граф фильтров MP3
Как видно на рис. 7.5, граф фильтров читает данные из файла MP3, декодирует их, а затем отправляет аудиоаппаратуре для воспроизведения. Рабочей лошадкой индустрии фильтров в DirectShow является интерфейс IGraphBuilder. В таблице 7.6 перечислены входящие в этот интерфейс функции.
Таблица 7.6. Методы интерфейса IGraphBuilder | |
Метод | Описание |
Abort | Сообщает графу о необходимости прекратить текущую операцию. |
AddSourceFilter | Добавляет фильтр источника. |
Connect | Соединяет два контакта. |
Render | Добавляет фильтр к выходному контакту. |
RenderFile | Загружает файл для воспроизведения. Я использую этот метод в своем примере для загрузки MP3-файла. |
SetLogFile | Устанавливает обработчик для файла журналирования выходной информации. |
ShouldOperationContinue | Сообщает должна ли продолжаться операция. Это очень странная функция, которую вам никогда не придется вызывать. |
В следующей строке кода заголовочного файла я создаю указатель на интерфейс IMediaControl с именем g_pMediaControl. Интерфейс управления аудиовизуальным потоком предназначен для контроля проходящих через граф фильтров данных. Этот интерфейс позволяет запустить, закончить и даже временно приостановить прохождение данных через граф. Вы можете представлять его как пульт дистанционного упроавления видеомагнитофона.
В рассматриваемом примере программы я использую интерфейс управления аудиовизуальным потоком для запуска, прекращения и перезапуска музыки. Функции интерфейса перечислены в таблице 7.7.
Таблица 7.7. Методы интерфейса IMediaControl | |
Метод | Описание |
GetState | Возвращает состояние графа. |
Pause | Приостанавливает воспроизводимый в данный момент аудиовизуальный поток. |
Run | Запускает аудиовизуальный поток. Это аналог кнопки Play на пульте дистанционного управления видеомагнитофона. |
Stop | Завершает воспроизведение аудиовизуального потока. |
StopWhenReady | Более мягкая остановка. |
Следующим в заголовочном файле расположен указатель на интерфейс IMediaEvent. Этот тип интерфейса используется для коммуникации с графом фильтров. Он будет информировать вас о текущем состоянии воспроизводимого аудиовизуального потока. В рассматриваемом примере программы я использую данный интерфейс, чтобы получить сообщение о завершении воспроизведения музыки. Методы интерфейса перечислены в таблице 7.8.
Таблица 7.8. Методы интерфейса IMediaEvent | |
Метод | Описание |
CancelDefaultHandling | Отменяет установленную по умолчанию обработку события фильтром. |
FreeEventParams | Освобождает связанные с параметром ресурсы. |
GetEvent | Возвращает следующее событие из очереди. |
GetEventHandle | Возвращает дескриптор следующего сообщения в очереди. |
RestoreDefaultHandling | Восстанавливает обработчик по умолчанию. |
WaitForCompletion | Ожидает пока граф фильтров не завершит воспроизведение аудиовизуального потока. Я использую эту функцию в примере программы чтобы проверить завершено ли воспроизведение музыки. |
Следующий интерфейс называется IMediaSeeking. Как следует из его названия, он предназначен для установки позиции в аудиовизуальном потоке. Кроме того, он позволяет задать темп воспроизведения аудиовизуального потока. В рассматриваемом примере программы я использую этот интерфейс чтобы осуществить перемотку аудиовизуального потока к его началу, когда воспроизведение музыки заканчивается. Так же я использую его для задания темпа воспроизведения. Методы данного интерфейса перечислены в таблице 7.9.
Таблица 7.8. Методы интерфейса IMediaSeeking | |
Метод | Описание |
CheckCapabilities | Проверяет, обладает ли поток указанными возможностями. |
ConvertTimeFormat | Преобразует из одного формата в другой. |
GetAvailable | Возвращает доступный диапазон значений времени для позиционирования. |
GetCapabilities | Возвращает возможности аудиовизуального потока. |
GetCurrentPosition | Возвращает текущую позицию в потоке. |
GetDuration | Возвращает длину потока. |
GetPositions | Возвращает текущую и конечную позиции. |
GetPreroll | Возвращает размер аудиовизуального потока, расположенного перед начальной позицией. |
GetRate | Возвращает темп воспроизведения. |
GetStopPosition | Возвращает конечную позицию. Она сообщает вам, когда воспроизведение потока будет завершено. |
GetTimeFormat | Возвращает используемый в данный момент формат времени. |
IsFormatSupported | Проверяет поддерживается ли указанный формат времени. |
IsUsingTimeFormat | Проверяет используется ли в данный момент указанный формат времени. |
QueryPreferredFormat | Возвращает предпочтительный формат времени. |
SetPositions | Устанавливает текущую и завершающую позиции. |
SetRate | Устанавливает темп воспроизведения. |
SetTimeFormat | Устанавливает формат времени. |
В описываемой программе я использую функции SetRate() и SetPositions().
Основной код программы располагается в файле main.cpp. Загрузите его сейчас и следуйте дальше. Найдите в коде функцию WinMain() и обратите внимание на следующий фрагмент:
// Воспроизведение музыки bRet = bPlayTitleMusic(); if(bRet == 0) { MessageBox(hWnd, "Initialization Failure", "Failed to initialize DirectShow", MB_ICONEXCLAMATION | MB_OK); // Сбой в программе, выход exit(1); }
В этом блоке кода вызывается функция bPlayTitleMusic(), являющаяся локальной для моей программы. Она отвечает за инициализацию DirectShow и воспроизведение файла MP3 из каталога с звуковыми файлами DirectX SDK. Давайте перейдем к этой функции.
Эта функция в рассматриваемой программе выполняет большую часть работы. Она инициализирует COM, создает интерфейсы, загружает музыку, устанавливает темп воспроизведения и начинает проигрывание музыки. Вот как выглядит ее код:
bool bPlayTitleMusic(void) { HRESULT hr; // Инициализация COM CoInitialize(NULL); // Создание графа CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&g_pGraph); // Запрос интерфейсов объекта g_pGraph->QueryInterface( IID_IMediaControl, (void **)&g_pMediaControl); g_pGraph->QueryInterface( IID_IMediaEvent, (void **)&g_pEvent); g_pGraph->QueryInterface( IID_IMediaSeeking, (void **)&g_pSeeking); // Загрузка песни (вставьте имя своего файла) hr = g_pGraph->RenderFile( L"c:\\dxsdk\\samples\\media\\track3.mp3", NULL); if(hr != S_OK) { return(0); } // Установка темпа воспроизведения g_pSeeking->SetRate(1); // Воспроизведение музыки g_pMediaControl->Run(); // Установка флага воспроизведения g_bBackgroundMusicActive = 1; return(1); }
Первая вещь, которую делает функция, — инициализация COM. Это необходимый этап, поскольку DirectShow использует COM-интерфейсы.
Следующий шаг к небесам DirectShow — создание объекта графа. Я выполняю это с помощью вызова функции CoCreateInstance(). Интерфейс IGraphBuilder использует CLSID CLSID_FilterGraph и идентификатор интерфейса IID_IGraphBuilder. Указатель на интерфейс сохраняется в глобальной переменной с именем g_pGraph.
Теперь, когда у вас есть интерфейс построителя графов, вы можете создать другие интерфейсы, отправив запрос построителю графов. Это делается с помощью функции IGraphBuilder::QueryInterface(). Мы создаем три интерфейса: IID_IMediaControl, IID_IMediaEvent и IID_IMediaSeeking. После создания указатель на каждый из интерфейсов сохраняется в глобальных переменных, о которых я упоминал ранее.
К данному моменту вы инициализировали COM, создали объект графа и необходимые для программы вспомогательные интерфейсы. Это основные действия инициализации, необходимые для воспроизведения файлов MP3. Осталось только загрузить файл с музыкой, установить темп воспроизведения и начать воспроизведение. Перед тем, как идти дальше, взгляните на рис. 7.6.
Рис. 7.6. Этапы инициализации DirectShow
На рис. 7.6 показана взаимосвязь между интерфейсом управления аудиовизуальным потоком, интерфейсом событий аудиовизуального потока, интерфейсом позиционирования аудиовизуального потока и интерфейсом графа фильтров. Также видна связь между функциями воспроизведения и интерфейсом управления аудиовизуальным потоком и между функцией установки темпа и интерфейсом позиционирования аудиовизуального потока. Позднее вы увидите как в эту большую картину вписывается интерфейс событий аудиовизуального потока.
Функция IGraphBuilder::RenderFile() выполняет всю работу, необходимую для загрузки файла. Она получает два параметра: первый содержит строку с именем файла, а второй не используется. Как видно из кода в рассматриваемой программе я загружаю файл c:\dxsdk\samples\media\track3.mp3. Если у вас DirectX SDK установлен в другом каталоге, убедитесь что путь соответствующим образом скорректирован. Вы можете скорректировать имя файла, чтобы воспроизводить любую из имеющихся на вашем компьютере песен в формате MP3. Лично я указал песню Amish Paradise, которую исполняет Weird Al Yankovic. Поскольку у меня нет прав на распространение этой песни, пришлось указать файл, который, скорее всего, будет на диске вашего компьютера!
Если загрузка файла прошла успешно, функция возвращает значение S_OK. Если же при загрузке произошел сбой, вам может понадобиться консультация с документацией DirectX SDK для получения информации о возвращаемом коде ошибки.
Давайте двигаться дальше. Следующий фрагмент кода задает темп песни. Темп музыки определяет насколько быстро (или медленно) она воспроизводится. Данный параметр может использоваться для того, чтобы голос актера звучал похоже на белку или на Дарта Вейдера. Все это делает функция IMediaSeeking::SetRate(). Она получает единственный параметр — темп воспроизведения. Если вы хотите, чтобы для воспроизведения песни использовался ее темп по умолчанию, укажите здесь значение 1. Чтобы услышать, как артист поет с удвоенной скоростью, укажите значение 2. Кроме того, вы можете использовать различные промежуточные значения. Например, я люблю воспроизводить музыку группы Metallica с темпом 1.25, чтобы получить чистый скоростной металл. Вы почти можете видеть, как руки Ларса дымятся от этого!
После всей выполненной работы воспроизведение MP3 осуществляется исключительно просто. Достаточно лишь вызвать функцию IMediaControl::Run(). Если функция возвращает S_OK, вы знаете, что воспроизведение успешно началось.
Теперь, когда все необходимые части проинициализированы и запущены, код присваивает флагу g_bBackgroundMusicActive значение 1. Благодаря этому цикл сообщений функции WinMain() узнает, что надо проверять состояние музыки. Вернитесь назад к функции WinMain() и взгляните на следующий фрагмент кода:
if(g_bBackgroundMusicActive) { vCheckMusicStatus(); }
Эй, я не говорил, что будет много кода! Так или иначе, главный цикл проверяет состояние музыки вызывая мою функцию vCheckMusicStatus().
Функция vCheckMusicStatus() проверяет завершено ли фоновое воспроизведение музыки. Если да, музыка перематывается к началу и воспроизведение запускается по новой. Вот как выглядит код функции:
void vCheckMusicStatus(void) { long evCode; // Проверка кода события g_pEvent->WaitForCompletion(0, &evCode); // Если музыка закончилась, запустить ее заново if(evCode == EC_COMPLETE) { // Устанавливаем начальную позицию в 0 LONGLONG lStartPos = 0; // Останавливаем музыку g_pMediaControl->Stop(); // Устанавливаем позиции g_pSeeking->SetPositions( &lStartPos, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning); // Запускаем музыку g_pMediaControl->Run(); } }
Пришло время использовать старый верный интерфейс IMediaEvent. Этот интерфейс позволяет увидеть состояние музыки. Я вызываю функцию IMediaEvent::WaitForCompletion() чтобы получить самый верхний код события.
Первый параметр указывает как долго я хочу ожидать возвращения кода. Присвоив этому параметру значение 0, я сообщаю системе, что возврат должен быть осуществлен немедленно, без ожидания. Второй параметр содержит адрес переменной в которой будет сохранен возвращаемый код события.
Если код события равен EC_COMPLETE, я знаю, что воспроизведение песни закончилось. Это явная подсказка, что пора перемотать песню к началу и снова запустить ее воспроизведение. Если возвращен код EC_ERRORABORT или EC_USERABORT, я знаю, что что-то пошло наперекосяк и воспроизведение музыки прекращено.
Следующий фрагмент кода подразумевая, что песня подошла к концу, останавливает музыку, перематывает ее к началу и заново начинает воспроизведение. Остановка музыки осуществляется вызовом функции IMediaControl::Stop(). Параметров у функции нет.
Я знаю, это звучит странно, но перед тем как выполнять перемотку вы должны остановить музыку, как будто вы работаете с древним кассетным магнитофоном.
Теперь, когда музыка остановлена, вам необходимо перемотать ее к началу. Конечно же никакой реальной перемотки не выполняется; вы просто снова устанавливаете указатель позиции на начало песни. Это выполняет функция IMediaSeeking::SetPositions(). Вот как выглядит ее прототип:
HRESULT SetPositions( LONGLONG *pCurrent, DWORD dwCurrentFlags, LONGLONG *pStop, DWORD dwStopFlags );
Первый параметр является адресом переменной, содержащей устанавливаемую позицию песни. Поскольку я хочу перемотать песню к началу, значение этой переменной равно 0.
Следующий параметр, dwCurrentFlags, является комбинацией битовых флагов, относящихся к устанавливаемой позиции. Существует два типа флагов: флаги позиционирования и модификаторы. Здесь я использую флаг AM_SEEKING_AbsolutePositioning, чтобы сообщить системе, что позиция 0 является абсолютной, а не относительной. Названия трех других флагов описывают их назначение: AM_SEEKING_NoPositioning, AM_SEEKING_RelativePositioning и AM_SEEKING_IncrementalPositioning.
Следующий параметр, pStop, является адресом переменной, содержащей позицию остановки музыки. Передавая в этом параметре значение NULL я сообщаю системе, что следует использовать время завершения по умолчанию, которое хранится в файле с песней.
Последний параметр, dwStopFlags, содержит комбинацию битовых флагов, относящихся к позиции остановки. Поскольку я не задаю позицию остановки, здесь я использую флаг AM_SEEKING_NoPositioning. Он сообщает системе, что я не устанавливаю позицию остановки и нечего об этом беспокоиться.
Теперь, после того, как песня остановлена и перемотана к началу, я снова запускаю ее воспроизведение с помощью испытанной функции запуска, которой я уже пользовался ранее при инициализации.
Взгляните на рис. 7.7, чтобы увидеть все, что выполняет наша программа.
Рис. 7.7. Поток исполнения программы воспроизведения MP3
На рисунке видно как система инициализирует DirectShow, загружает песню и начинает ее воспроизведение, проверяет не закончилась ли песня, перематывает ее и запускает воспроизведение снова. На этом мы завершаем обсуждение воспроизведения файлов MP3.
netlib.narod.ru | < Назад | Оглавление | Далее > |