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

Работа с DirectMusic

В то время, как DirectSound обрабатывает цифровые звуки, DirectMusic обрабатывает воспроизведение музыки из файлов MIDI (файлы с расширением .MID), родных файлов DirectMusic (файлы .SGT) и цифровых записей песен, хранящихся в волновом формате (файлы .WAV). Какой из этих форматов лучше для вас — вот вопрос, требующий ответа.

У каждого есть преимущества и недостатки. Настоящая магия DirectMusic проявляется, когда используют его родной формат. Песня в родном формате DirectMusic может состоять из небольших музыкальных партитур, которые могут случайно воспроизводиться одна за другой в различных аккордах. Случайный выбор партитур и аакордов означает, что музыка никогда не будет той же самой — она все время будет меняться. Добавьте изменение темпа, и вы получите крутую музыкальную систему.

ПРИМЕЧАНИЕ
Другая возможность — использование мотивов (motif), то есть звуков, накладывающихся на музыкальные сегменты при их воспроизведении. Они служат оттенками, смешивающимися с песней. Например, если игрок достиг цели, чтобы показать это можно воспроизвести короткий звук рожка.

Преимущество использования файлов MIDI — их широкая поддержка. В Интернете вы найдете тысячи песен в этом формате, и сотни программных пакетов для создания MIDI-музыки. Замечательно, что вы можете использовать цифровые записи звуков в качестве инструментов.

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

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

ПРИМЕЧАНИЕ
Путь, которым музыкальные данные поступают в синтезатор (DirectSound) называются аудио-путем (audio path) и вы можете перехватить музыку и воспроизвести точно также, как обычный звуковой буфер DirectSound. Только вообразите - захват звуковой дорожки и создание на ее основе трехмерного звукового буфера! Это все возможно, и это делает DirectMusic таким привлекательным.

Использование MIDI или родного формата DirectMusic имеет одно общее преимущество (возможность менять темп воспроизведения), добавляющее одну замечательную возможность — ускорять и замедлять музыку, согласно происходящим на экране действиям. Когда на экране разворачиваются активные действия, увеличьте темп музыки, а затем уменьшите его, когда обстановка станет более спокойной.

ПРИМЕЧАНИЕ
Проблема с родным форматом состоит в том, что вам потребуется время на привыкание к написанию партитур и аккордов. Нет никакой возможности показать вам основы в ограниченном объеме книги, так что я отсылаю вас к DirectMusic Producer — музыкальному редактору от Microsoft. Вы найдете его на прилагаемом к книге CD-ROM или можете загрузить с сайта http://www.microsoft.com.

Цифровые записи музыки создают бесподобную систему... в каком-то роде. Хотя качество превосходно, вы не можете менять песню в соответствии с действиями. Песня звучит точно так, как записана, ничего больше но и ничего меньше.

Начинаем работу с DirectMusic

Теперь, когда вы заинтересовались, пойдем дальше. Первый этап использования DirectMusic — это создание главного объекта, называемого исполнителем (performance), который представляет музыкальную систему. Затем, создается объект, называемый загрузчик (loader), который загружает все основные музыкальные файлы. Взаимодействие между этими двумя объектами показано на рис. 4.12.


Рис. 4.12. Объект загрузчика предоставляет данные, необходимые объекту исполнителя для воспроизведения музыки

Рис. 4.12. Объект загрузчика предоставляет данные, необходимые объекту исполнителя для воспроизведения музыки


Затем вы загружаете реальные музыкальные сегменты в объекты сегментов (segment). Для создания длинных или более динамичных песен вы можете загрузить несколько сегментов и воспроизводить их один за другим. В этой главе я буду иметь дело только с одним сегментом (который представляет всю песню целиком).

В DirectMusic нет функции помогающей вам создать или инициализировать основной интерфейс DirectMusic, поэтому вам надо самостоятельно инициализировать COM-систему. Для инициализации COM вызовите следующую функцию:

CoInitialize(NULL);

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

CoUninitialize();

Он уменьшает счетчик ссылок использующих COM и, когда значение станет равным 0, COM-система будет выгружена из памяти. Это повышает эффективность использования памяти и все COM-объекты следуют этой процедуре. Чтобы создать объект вы используете функцию CoCreateInstance. В следующих двух разделах я покажу вам, как создать объекты исполнителя и загрузчика соответственно.

Создание объекта исполнителя

Объект исполнителя — это большая шишка и основной объект, с которым вы будете работать. У вас может быть несколько объектов исполнителей, но я рекомендую использовать только один. Для создания объекта исполнителя сперва объявите экземпляр объекта IDirectMusicPerformance8, а затем вызовите функцию CoCreateInstance, как показано ниже:

// Глобальный объект исполнителя
IDirectMusicPerformance8 *g_pDMPerformance;

CoCreateInstance(CLSID_DirectMusicPerformance, NULL,
        CLSCTX_INPROC, IID_IDirectMusicPerformance8,
        (void**)&g_pDMPerformance);

 

ПРИМЕЧАНИЕ
Функция CoCreateInstance возвращает значение S_OK, если вызов завершен успешно. Любое другое значение свидетельствует об ошибке.

Объект исполнителя необходимо инициализировать. При этом создаются объекты DirectMusic и DirectSound, они создают звуковые буферы и устанавливают возможности вопроизведения. Также устанавливается аудио-путь по умолчанию, по которому воспроизводится музыка. Стандартные параметры предполагают использование 128 каналов (инструментов) и включение стереофонии и эффекта эха (отражения звука от объектов). Вот функция, вызов который делает все это:

HRESULT IDirectMusicPerformance8::InitAudio(
     IDirectMusic **ppDirectMusic,   // NULL
     IDirectSound **ppDirectSound,   // NULL
     HWND         hWnd,              // Дескриптор родительского окна
     DWORD        dwDefaultPathType, // Тип аудио-пути по умолчанию
                                     // используйте
                                     // DMUS_APATH_SHARED_STEREOPLUSREVERB
     DWORD        dwPChannelCount,   // Количество каналов - используйте 128
     DWORD        dwFlags,           // DMUS_AUDIOF_ALL (разрешить все
                                     // музыкальные возможности)
     DMUS_AUDIOPARAMS *pParams);     // NULL (структура параметров)

Здесь много информации, но почти все сказано в комментариях. Вам не нужны указатели на внутренние объекты DirectMusic и DirectSound, поэтому пропустим их. Вы должны дать функции дескриптор родительского окна — это необходимо. Для других параметров можно оставить значения, приведенные в комментариях прототипа функции InitAudio.

Вот пример вызова функции:

// g_pDMPerformance = ранее созданный объект исполнителя
if(FAILED(g_pDMPerformance->InitAudio(NULL, NULL, hWnd,
               DMUS_APATH_SHARED_STEREOPLUSREVERB, 128,
               DMUS_AUDIOF_ALL, NULL))) {
    // Произошла ошибка
}

Создание объекта загрузчика

Следующий этап использования DirectMusic — создание объекта загрузчика. Объект в основном является системой кэширования, ускоряющей загрузку данных и выполняющей загрузку необходимых песням файлов (таких, как цифровые записи, используемые для инструментов).

Загрузчик представляет объект IDirectMusicLoader8. Вы создаете его с помощью следующего кода:

IDirectMusicLoader8 *g_pDMLoader; // Глобальный объект загрузчика

CoCreateInstance(CLSID_DirectMusicLoader, NULL,
                 CLSCTX_INPROC, IID_IDirectMusicLoader8,
                 (void**)&g_pDMLoader);

 

ВНИМАНИЕ!
Убедитесь, что в вашем приложении вы создаете только один объект IDirectMusicLoader8. Это помогает кэшированию и управлению часто используемыми данными и ресурсами, необходимыми при работе с DirectMusic.

Следующий этап использования загрузчика — указание ему, в каком каталоге следует искать файлы. На этот каталог ссылаются как на каталог поиска по умолчанию (default search directory). Обычно при загрузке единственного музыкального файла, такого как MIDI-файл, установка каталога по умолчанию не требуется, поскольку вы просто сообщаете загрузчику полный путь. Но для файлов родного формата DirectMusic объект загрузчика должен знать, где искать вспомогательные файлы.

Установка каталога поиска по умолчанию — это работа функции IDirectMusicLoader8::SetSearchDirectory:

HRESULT IDirectMusicLoader8::SetSearchDirectory(
     REFGUID rguidClass, // Класс (GUID_DirectMusicallTypes)
     WCHAR   *pwszPath,  // Путь к каталогу (широкие символы)
     BOOL    fClear);    // FALSE - очистить кэш

Показанному вызову реально нужен только один параметр — устанавливаемый путь к каталогу для поиска. Будьте внимательны — это должна быть строка широких символов, так что преобразуйте ее или используйте тип данных WCHAR.

ПРИМЕЧАНИЕ
Чтобы объявить строку широких символов используйте следующий код:
WCHAR *Text = L"Testing";
Чтобы преобразовать строку обычных символов в строку широких символов используйте функцию mbstowcs, как показано ниже:
char Text[] = "Roleplaying is fun!"; // Буфер исходного текста
WCHAR WText[256];                    // Буфер для преобразованного текста
// Преобразуем 256 символов из источника в приемник
mbstowcs(WText, Text, 256);

Для простоты я обычно в качестве каталога поиска задаю текущий каталог приложения. Таким образом вы можете сослаться на файлы песен из ваших собственных подкаталогов (например, .\Songs\ — обратите внимание на точку, означающую текущий каталог).

Вот пример установки текущего каталога в качестве каталога поиска по умолчанию:

// g_pDMLoader = ранее инициализированный объект загрузчика
CHAR strPath[MAX_PATH];   // Текущий путь
WCHAR wstrPath[MAX_PATH]; // Буфер широких символов

GetCurrentDirectory(MAX_PATH, strPath);
mbstowcs(wstrPath, strPath, MAX_PATH);

if(FAILED(g_pDMLoader->SetSearchDirectory(
            GUID_DirectMusicAllTypes, wstrPath, FALSE))) {
    // Произошла ошибка
}

Работа с музыкальными сегментами

Теперь система инициализирована и загрузчик готов — настало время начать загрузку песни и включить ее воспроизведение. Это работа объекта IDirectMusicSegment8. Объект загрузчика DirectMusic (как показано на рис. 4.13) загружает музыку и данные инструментов и создает объект IDirectMusicSegment8 за вас. Учтите, что процесс загрузки состоит из двух этапов — сперва вы загружаете музыкальный сегмент, содержащий ноты для воспроизведения.


Рис. 4.13. Объект загрузчика отвечает за получение данных, таких как музыкальные партитуры и данные инструментов, необходимые для создания музыкального объекта

Рис. 4.13. Объект загрузчика отвечает за получение данных, таких как музыкальные партитуры и данные инструментов, необходимые для создания музыкального объекта


Загрузка музыкальных сегментов

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

typedef struct {
     DWORD        dwSize;      // Размер структуры
     DWORD        dwValidData; // Флаги, определяющие
                               // действительные поля
     GUID         guidObject;  // Уникальный GUID объекта
     GUID         guidClass;   // CLSID_DirectMusicSegment
     FILETIME     ftDate;      // Дата последнего
                               // редактирования объекта
     DMUS_VERSION vVersion;    // Структура, содержащая
                               // информацию о версии
     WCHAR wszName[DMUS_MAX_NAME];         // Имя объекта
     WCHAR wszCategory[DMUS_MAX_CATEGORY]; // Категория объекта
     WCHAR wszFileName[DMUS_MAX_FILENAME]; // Имя файла для загрузки
     LONGLONG     llMemLength; // Размер данных в памяти
     LPBYTE       pbMemData;   // Указатель на данные в памяти
     IStream      *pStream;    // Интерфейс потока для загрузки
} DMUS_OBJECTDESC;

К счастью, большинство полей в DMUS_OBJECTDESC можно игнорировать. Первое, заслуживающее внимания поле — это dwValidData. Оно хранит комбинацию флагов, сообщающих загрузчику, какие поля в структуре используются. Например, если вы хотите использовать wszFilename и guidClass, установите соответствующие флаги. Список флагов приведен в таблице 4.4.


Таблица 4.4. Флаги dwValidData



Флаг Описание

DMUS_OBJ_CATEGORY Действительно значение wszCategory.
DMUS_OBJ_CLASS Действительно значение guidClass.
DMUS_OBJ_DATE Действительно значение ftDate.
DMUS_OBJ_FILENAME Действительно значение wszFileName.
DMUS_OBJ_FULLPATH wszFileName содержит полный путь к объекту.
DMUS_OBJ_LOADED Объект уже загружен.
DMUS_OBJ_MEMORY Объект в памяти. Действительны значения llMemLength и pbMemData.
DMUS_OBJ_NAME Действительно значение wszName.
DMUS_OBJ_OBJECT Действительно значение guidObject.
DMUS_OBJ_STREAM Действительно значение pStream.
DMUS_OBJ_URL wszFileName представляет адрес URL.
DMUS_OBJ_VERSION Действительно значение vVersion.


Структура DMUS_OBJECTDESC передается функции IDirectMusicLoader8::GetObject, обеспечивающей загрузку и размещение в объекте сегмента всех связанных файлов данных. Вот прототип функции:

HRESULT IDirectMusicLoader8::GetObject(
     LPDMUS_OBJECTDESC pDesc, // Указатель на структуру DMUS_OJBECTDESC
     REFIID riid,             // IID_IDirectMusicSegment8
     LPVOID FAR * ppv);       // Указатель на новый загруженный объект

Вы говорите функции GetObject использовать структуру, которую вы инициализировали и загрузить данные в объект IDirectMusicSegment8. Конечно, вам сперва нужно объявить объект сегмента, чтобы иметь дело с функцией, которая обрабатывает загрузку фрагмента музыки и возвращает объект сегмента (или NULL, если произошла ошибка).

// g_pDMLoader = ранее инициализированный объект загрузчика
//               с установленным каталогом поиска
IDirectMusicSegment8 *LoadSong(char *Filename)
{
    DMUS_OBJECTDESC dmod;
    IDirectMusicSegment8 *pDMSegment;

    ZeroMemory(&dmod, sizeof(DMUS_OBJECTDESC));

    dmod.dwSize      = sizeof(DMUS_OBJECTDESC);
    dmod.guidClass   = CLSID_DirectMusicSegment;
    dmod.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME |
                       DMUS_OBJ_FULLPATH;
    mbstowcs(dmod.wszFileName, Filename, MAX_PATH);

    if(FAILED(g_pDMLoader->GetObject(&dmod,
           IID_IDirectMusicSegment8, (LPVOID)&pDMSegment)))
        return NULL;

    // Загрузка завершена
    return p_DMSegment;
}

Загрузка инструментов

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

Инструменты называются модификаторами (patches), а набор модификаторов называется DLS-данными инструментов (DLS instrument data — от Downloadable Sounds), которые включаются в коллекции инструментов (instrument collections). Модификаторы нумеруются последовательностью из трех значений: старшего значащего байта (most significant byte, MSB), младшего значащего байта (least significant byte, LSB) и номера модификатора (patch number).

Основные модификаторы MIDI стандартизованы, так что модификатор с номером 1 (пианино) всегда будет относиться к пианино. Если вы хотите использовать новый модификатор пианино, достаточно просто загрузить его из DLS-коллекции. DirectMusic поставляется с коллекцией инструментов, соответствующей спецификации General MIDI, называемой набор GM/GS (GM/GS set) созданной Roland.

ПРИМЕЧАНИЕ
Чтобы создать собственный DLS установите DirectMusic Producer, находящийся на CD-ROM. В меню выберите File, New и создайте DLS-коллекцию (DLS Collection). Добавьте в список волновые файлы, а затем добавьте инструменты, убедившись, что им назначены соответствующие волновые данные.

Если вы делаете новые инструменты для замены модификаторов General MIDI, убедитесь, что у них MSB и LSB равны 0; в ином случае bcgjkmpeqnt различные значения для каждого инструмента, чтобы гарантировать отсутствие вторжений в пространство того или другого инструмента. Если вам потребуется дополнительная помощь, обратитесь к файлу справки DirectMusic Producer.

Если вы хотите использовать только пару новых инструментов, любым способом сохраните их в DLS. Загружая новый DLS, вы перезаписываете ранее загруженные в память инструменты (как показано на рис. 4.14). После того, как DLS-коллекция готова к использованию сообщите DirectMusic, что эту коллекцию надо использовать для ваших музыкальных сегментов.


Рис. 4.14. Загружая DLS-инструменты вы либо перезаписываете данные существующего инструмента, либо вставляете данные нового инструмента туда, где никаких инструментов нет

Рис. 4.14. Загружая DLS-инструменты вы либо перезаписываете данные существующего инструмента, либо вставляете данные нового инструмента туда, где никаких инструментов нет


Для загрузки DLS-коллекции вам необходимо получить объект IDirectMusicCollection8 через объект загрузчика. Вы снова используете функцию IDirectMusicLoader8::GetObject, но на этот раз указываете объект коллекции и имя файла. Вот функция, которая загружает для вас DLS-коллекцию и возвращает указатель на объект загруженной коллекции для дальнейшей работы:

IDirectMusicCollection8 *LoadDLSCollection(char *Filename)
{
    DMUS_OBJECTDESC dmod;
    IDirectMusicCollection8 *pDMCollection;

    ZeroMemory(&dmod, sizeof(DMUS_OBJECTDESC));
    dmod.dwSize      = sizeof(DMUS_OBJECTDESC);
    dmod.guidClass   = CLSID_DirectMusicCollection;
    dmod.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME |
                       DMUS_OBJ_FULLPATH;
    mbstowcs(dmod.wszFileName, Filename, MAX_PATH);
    if(FAILED(g_pDMLoader->GetObject(&dmod,
            IID_IDirectMusicCollection8, (void**)pDMCollection)))
        return NULL;

    // Возврашаем указатель на объект коллекции
    return IDirectMusicCollection8;
}

Теперь коллекция загружена, но вам надо еще назначить ее сегменту. Это делается путем установки параметра сегмента трека с помощью функции IDirectMusicSegment8::SetParam:

HRESULT IDirectMusicSegment8::SetParam(
     REFGUID rguidType, // GUID устанавливаемого параметра
     DWORD dwGroupBits, // На какой трек оказывает эффект (0xFFFFFFFF)
     DWORD dwIndex,     // 0
     MUSIC_TIME mtTime, // Когда применять изменения - используйте 0
     void* pParam);     // Новый параметр или NULL если не требуется

Сейчас вы хотите установить параметр, задающий тип DLS-коллекции, которому соответствует значение GUID GUID_ConnectToDLSCollection. Вы хотите, чтобы параметр влиял на каждый трек и изменения вступили в силу немедленно. Для этого используйте следующий фрагмент кода (который загружает DLS и устанавливает ее для ранее загруженного сегмента):

IDirectMusicCollection8 *pDMCollection;

if((pDMCollection = LoadDLSCollection("MyDLS.dls")) != NULL)
    pDMSegment->SetParam(GUID_ConnectToDLSCollection,
                         0xFFFFFFFF, 0, 0, (void*)pDMCollection);

Иногда вам надо будет использовать предлагаемую по умолчанию коллекцию, что достигается вызовом GetObject со значением GUID GUID_DefaultGMCollection в поле объекта класса:

IDirectMusicCollection8 *GetDefaultCollection()
{
    DMUS_OBJECTDESC mod;
    IDirectMusicCollection8 *pDMCollection;

    ZeroMemory(&dmod, sizeof(DMUS_OBJECTDESC));
    dmod.dwSize      = sizeof(DMUS_OBJECTDESC);
    dmod.guidObject  = GUID_DefaultDMCollection;
    dmod.dwValidData = DMUS_OBJ_OBJECT;

    if(FAILED(g_pDMLoader->GetObject(&dmod,
                      IID_IDirectMusicCollection8,
                      (void**)pDMCollection)))
        return NULL;

    return pDMCollection;
}

Вызов показанной выше функции GetDefaultCollection создаст объект коллекции инструментов, содержащий DLS-данные инструментов по умолчанию, которые вы можете использовать.

Настройки для MIDI

Вся песня в памяти (с инструментами) и почти готова к использованию. Осталось лишь пара проблем, которыми следует заняться. Во-первых, поскольку система должна подготовить себя в соответствии со спецификацией General MIDI, вам необходимо сообщить системе является ли загружаемый файл MIDI-файлом.

Чтобы сообщить DirectMusic, что сегмент является MIDI-файлом вы снова устанавливаете параметры сегмента трека функцией IDirectMusicSegment8::SetParam. На этот раз вы используете значение GUID GUID_StandardMidiFile:

pDMSegment->SetParam(GUID_StandardMidiFile,
                     0xFFFFFFFF, 0, 0, NULL);

Вы можете поместить показанный ниже вызов функции SetParam в функцию LoadSongFile (после того, как песня полностью загружена):

    if(FAILED(g_pDMLoader->GetObject(&dmod,
           IID_IDirectMusicSegment8, (LPVOID)&pDMSegment)))
        return NULL;

    // Загрузка завершена
    // Устанавливаем признак MIDI-файла
    if(strstr(Filename, ".mid") != NULL)
        pDMSegment->SetParam(GUID_StandardMidiFile,
                             0xFFFFFFFF, 0, 0, NULL);

    return p_DMSegment;
}

Установка инструментов

Следующий этап подготовки сегмента к воспроизведению — установка данных инструментов путем их загрузки в объект исполнителя. Это выполняется с помощью вызова IDirectMusicSegment8::Download:

HRESULT IDirectMusicSegment8::Download(IUnknown *pAudioPath);

 

ВНИМАНИЕ!
Выполняйте этот вызов только для MIDI-файлов, поскольку он меняет способ восприятия музыкальной информации. Если вы поэкспериментируете с ним, то увидите, что отдельные данные трека изменяются или теряются.

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

if(FAILED(g_pDMSegment->Download(g_pDMPerformance))) {
    // Произошла ошибка
}

 

СОВЕТ
Чтобы изменить инструменты (например, назначить DLS сегменту) вы сперва должны выгрузить данные инструментов. После выгрузки инструментов вы можете загрузить новые данные инструментов и продолжить воспроизведение песни.

Когда вы завершите работу с музыкальным сегментом, необходимо выполнить вызов IDirectMusicSegment8::Unload, освобождающий данные инструментов. Делайте это после того, как остановили воспроизведение сегмента и завершили работать с ним или когда вы переключаете коллекцию инструментов. Вызов идентичен IDirectMusicSegment8::Download, так что я пропущу прототип и сразу покажу реальный код:

if(FAILED(g_pDMSegment->Unload(g_pDMPerformance))) {
    // Произошла ошибка
}

Использование циклов и повторов

Последний шаг перед воспроизведением — установка точек повтора и количества повторений цикла. Например, если у вас есть ритм и вы хотите повторить его небольшую часть несколько раз, то устанавливаете начальную и конечную точки цикла (как показано на рис. 4.15), а затем задаете количество повторений цикла.


Рис. 4.15. Установите начальную и конечную точки цикла внутри песни, чтобы воспользоваться циклами и возможностями повторов DirectMusic

Рис. 4.15. Установите начальную и конечную точки цикла внутри песни, чтобы воспользоваться циклами и возможностями повторов DirectMusic


Установка точек цикла — это назначение функции IDirectMusicSegment8::SetLoopPoints:

HRESULT IDirectMusicSegment8::SetLoopPoints(
     MUSIC_TIME mtStart,
     MUSIC_TIME mtEnd);

 

ВНИМАНИЕ!
Обратите внимание на использование MUSIC_TIME — единиц измерения времени, применяемых в DirectMusic. Это измерение времени основано на темпе песни, а не на таймере, так что иногда с ним сложно работать. Выбор времени это еще одна проблема и лучше обратиться к документации DirectX SDK. Для текущих целей, когда вы хотите, чтобы все изменения давали эффект немедленно, безопасно для времени задавать значение 0.

Обычно вы хотите, чтобы песня была воспроизведена вся целиком до завершения, так что нет причин заморачиваться с функцией SetLoopPoints. Если вы все же решитесь на это, помните, что измерение времени основано на темпе (за дополнительными сведениями по этой теме обратитесь к документации DX SDK).

После установки точек цикла (даже если у вас их нет), вы готовы указать сколько раз будет повторена песня. Если вы хотите, чтобы песня была воспроизведена один раз и остановлена, число повторов должно быть равно нулю. Если вы хотите чтобы песня была воспроизведена дважды, повторите ее один раз.

Вы устанавливаете количество повторов функцией IDirectMusicSegment8::SetRepeats, у которой всего один параметр — количество повторений цикла песни (или макрос DMUS_SEG_REPEAT_INFINITE, который вызывает бесконечное воспроизведение песни):

pDMSegment->SetRepeats(0); // Воспроизводим песню один раз (нет циклов)

Воспроизведение и остановка сегмента

Теперь, наконец-то пришло время воспроизвести вашу песню. Да, дорога была долгой и трудной, но мы достигли ее конца. Объект исполнителя воспроизводит сегмент с помощью функции IDirectMusicPerformance8::PlaySegmentEx:

HRESULT IDirectMusicPerformance8::PlaySegmentEx(
     IUnknown *pSource,     // Воспроизводимый сегмент
     WCHAR *pwzSegmentName, // NULL - не используется
     IUknown *pTransition,  // Сегмент перехода - используйте NULL
     DWORD dwFlags,         // Флаги изменения поведения
     __int64 i64Starttime,  // Когда начинать воспроизведение
                            // 0 - немедленно
     IDirectMusicSegmentState **ppSegmentState, // Указатель на объект
                                                // получающий объект
                                                // состояния сегмента
     IUnknown *pFrom,       // NULL
     IUnknown *pAudioPath); // Используемый аудио-путь или
                            // NULL для аудио-пути по умолчанию

Ничего себе! Как много всего; к счастью вам не надо использовать все эти параметры. Вы видите, что указатель на сегмент является первым аргументом, но что такое состояние сегмента (segment state)? Это объект который отслеживает статус сегмента. Он вам не нужен, так что укажите NULL.

Флаги поведения позволяют изменить время начала сегмента, начинается ли он с такта, выравнивается по предшествующему темпу, или как вы указали. Поскольку сейчас нас интересует только воспроизведение песни, можно пропустить флаги и сообщить функции PlaySegmentEx о немедленном начале воспроизведения.

Быстро начать воспроизведение сегмента можно с помощью следующего фрагмента кода:

if(FAILED(g_pDMPerformance->PlaySegmentEx(g_pDMSegment,
                     NULL, NULL, 0, 0, NULL, NULL, NULL))) {
    // Произошла ошибка
}

Чтобы остановить воспроизведение сегмента используйте вызов IDirectMusicPerformance::Stop:

HRESULT IDirectMusicPerformance8::Stop(
     IDirectMusicSegment *pSegment, // Останавливаемый сегмент
     IDirectMusicSegmentState *pSegmentState, // Состояние для остановки
     MUSIC_TIME mtTime,             // Время остановки (0 - немедленно)
     DWORD dwFlags);                // Поведение времени остановки

Снова вызов получает в аргументе сегмент, а также время, когда вы хотите выполнить остановку. Вся перечисленная информация не требуется; достаточно предоставить указатель на объект сегмента и время остановки воспроизведения, как показано ниже:

if(FAILED(g_pDMPerformance->Stop(g_pDMSegment, NULL, 0, 0))) {
    // Произошла ошибка
}

Выгрузка данных сегмента

После того, как вы остановили сегмент и завершили работу с ним, необходимо выгрузить данные инструментов:

pDMSegment->Unload(g_pDMPerformance);

Вам также нужно обратиться к загрузчику для освобождения кэшированных данных с помощью вызова IDirectMusicLoader8::ReleaseObjectByUnknown:

HRESULT IDirectMusicLoader8::ReleaseObjectByUnknown(
     IUnknown *pObject);

Метод ReleaseObjectByUnknown получает один параметр — указатель на объект выгружаемого сегмента. Когда выгрузка произведена вы можете освободить COM-объект сегмента. Вот два вызова, которые делают это:

g_pDMLoader->ReleaseObjectByUnknown(pDMSegment);
pDMSegment->Release();

Кроме того, если вы загружали коллекцию инструментов, пришло время выгрузить ее из объекта загрузчика, точно также, как вы это делали, когда освобождали музыкальный сегмент. Чтобы сделать очистку кэша более простой, предусмотрен единственный вызов, который можно использовать для принудительной очистки всего кэша. Вам не нужно выполнять этот вызов перед освобождением загрузчика, поскольку в этом случае все выполняется автоматически.

Вот как выглядит очистка кэша:

g_pDMLoader->ClearCache(GUID_DirectMusicAllTypes);

 

ПРИМЕЧАНИЕ
Вы должны выгружать данные кэша только когда уверены, что новый сегмент, который вы будете загружать не нуждается в кэшированной информации. Если вы загружаете отдельные песни, то выгрузка не представляет опасности.

Изменение музыки

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

Установка громкости

Вы можете менять два параметра громкости — общую громкость объекта исполнителя (и музыкальной системы в целом) и индивидуальную громкость воспроизведения отдельного сегмента. Как показано на рис. 4.16, каждый сегмент подвергается изменению громкости, когда передается объекту исполнителя. Объект исполнителя влияет на общую громкость.


Рис. 4.16. Каждый сегмент может менять собственную громкость, а общая громкость влияет на все сегменты

Рис. 4.16. Каждый сегмент может менять собственную громкость, а общая громкость влияет на все сегменты


Громкость исполнителя (основная громкость) представляет собой глобальный параметр и для ее установки используется вызов IDirectMusicPerformance8::SetGlobalParam:

HRESULT IDirectMusicPerformance8::SetGlobalParam(
     REFGUID rguidType, // Устанавливаемый глобальный параметр
     void    *pParam,   // Новое значение параметра
     DWORD   dwSize);   // Размер данных параметра

Параметр rguidType — это GUID глобального параметра, который нужно установить, в данном случае GUID_PerfMasterVolume. Вы можете менять много глобальных параметров, так что за дополнительной информацией обращайтесь к документации DX SDK.

Параметр pParam — это уровень громкости, который вы хотите установить. Значение dwSize — это размер длинного целого, то есть размер переменной, которую вы используете для хранения уровня громкости. DirectMusic использует два макроса с именами DMUS_VOLUME_MIN (–200 децибел) и DMUS_VOLUME_MAX (+20 децибел), представляющих минимальный и максимальный уровень громкости соответственно. Используя значения, расположенные между значениями этих двух макросов, вы можете задать степень затухания в децибелах.

Вы можете упростить задание уровня громкости, создав формулу, которая позволит использовать для задания уровня громкости проценты, а не абсолютные значения. Проценты меняются в диапазоне от 0 до 100, где 0 представляет отсутствие звука, а 100 — максимально усиленный звук. Верно — максимальный уровень громкости усиливает звук (и слегка искажает его!), так что убедитесь, что выставили требуемый уровень.

Вот небольшая функция, которую вы можете использовать для указания основного уровня громкости, задавая значения в процентах от 0 до 100:

BOOL SetMasterVolume(long Level)
{
    long Volume, Range;

    // Вычисляем диапазон уровней громкости
    // и формируем новое значение
    Range = labs(DMUS_VOLUME_MAX - DMUS_VOLUME_MIN); // 220
    Volume = DMUS_VOLUME_MIN + Range / 100 * Level;

    // Устанавливаем новый уровень громкости
    if(FAILED(g_pDMPerformance->SetParam(GUID_PerfMasterVolume,
                                         &Volume, sizeof(long))))
        return FALSE;

    return TRUE;
}

Установка громкости музыкального сегмента выполняется путем перехвата интерфейса аудио-пути и его последующего использования для установки новой громкости. Поскольку вы уже создали аудио-путь по умолчанию, получение указателя заключается в простом обращении к следующей функции:

HRESULT IDirectMusicPerformance8::GetDefaultAudioPath(
     IDirectMusicAudioPath8 **ppAudioPath);

Функция получает единственный параметр — указатель на используемый вами объект IDirectMusicAudioPath8. Когда указатель на аудио-путь получен, можно воспользоваться функцией IDirectMusicAudioPath8::SetVolume:

HRESULT IDirectMusicAudioPath8::SetVolume(
     long  lVolume,     // Устанавливаемый уровень громкости
     DWORD dwDuration); // Время выполнения изменения (миллисекунды)

Уровень громкости варьируется от –600 (тишина) до 0 (полная громкость). Усиления здесь нет. Чтобы излишне не нагружать процессор, время выполнения изменения должно быть равно 0. Присваивание dwDuration значения 0 также сообщает музыкальной системе о том, что громкость надо изменить немедленно.

Почему бы не задавать значение в процентах, вместо того, чтобы задавать громкость числами из диапазона от –600 до 0? Вы можете создать простую функцию, такую как я сейчас покажу вам, для вычисления уровня громкости. Используя этот уровень громкости вы можете получить объект аудио-пути и изменить громкость, как показано ниже:

BOOL SetSegmentVolume(IDirectMusicSegment8 *pDSSegment,
                      long Level)
{
    long Volume;
    IDirectMusicAudioPath8 *pDMAudioPath;

    // Получаем объект аудио-пути для работы с ним
    if(FAILED(g_pDMPerformance->GetDefaultAudioPath(
                                          &pDMAudioPath)))
        return FALSE;

    // Вычисляем значение громкости для использования
    // в объекте аудио-пути
    Volume = -96 * (100 - Level);
    HRESULT Result = pDMAudioPath->SetVolume(Volume, 0);

    // Освобождаем аудио-путь - мы закончили работать с ним
    pDMAudioPath->Release();

    // Возвращаем флаг успеха (TRUE) или неудачи (FALSE)
    // в зависимости от кода HRESULT возвращенного SetVolume
    if(FAILED(Result))
        return FALSE;

    return TRUE;
}

Смена темпа

Вообразите себе возможность небольшого изменения темпа музыки, оживляющего игровой процесс и сообщающего игроку о том, что сейчас происходит. Например, когда игрок приближается к разъяренному монстру, темп ускоряется, и мы знаем, что нас ждут неприятности.

Темп измеряется в ударах в минуту (beats per minute, BPM), и обычное значение равно 120. DirectMusic позволяет менять темп различными способами. Простейший путь — настроить основной темп исполнителя (performance master tempo) путем задания коэффициента масштабирования. Например, коэффициент масштабирования 0.5 вдвое замедлит темп, а коэффициент 2.0 удвоит его.

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

BOOL SetTempo(long Percent)
{
    float Tempo;

    Tempo = (float)Percent / 100.0f;
    if(FAILED(g_pDMPerformance->SetGlobalParam(
                     GUID_PerfMasterTempo,
                     (void*)&Tempo, sizeof(float)))
        return FALSE;
    return TRUE;
}

Единственная загвоздка здесь в том, что проявление эффекта смены темпа может занять пару секунд из-за синхронизации такта. Также помните, что SetTempo влияет на общий темп, и меняться будет темп всех воспроизводимых сегментов. Завершив работу с песней вы должны вернуть нормальное значение темпа (1.0 или 100%).

Захват аудио-канала

Последним в длинном списке музыкальных возможностей идет захват объекта звукового буфера DirectSound, используемого для синтезирования инструментов и музыки. Вы можете сделать это получив объект аудио-пути и потом воспользовавшись им для захвата интерфейса звукового буфера.

На рис. 4.17 показан устанавливаемый по умолчанию поток данных по аудио-пути от объекта исполнителя к синтезатору. Вы перехватываете этот поток и меняете его как хотите.


Рис. 4.17. Перехват аудио-потока позволяет использовать буферы DirectSound (обычные и трехмерные) для создания некоторых ошеломительных эффектов

Рис. 4.17. Перехват аудио-потока позволяет использовать буферы DirectSound (обычные и трехмерные) для создания некоторых ошеломительных эффектов


Все это назначение функции IDirectMusicAudioPath8::GetObjectInPath:

HRESULT IDirectMusicAudioPath8::GetObjectInPath(
     DWORD   dwPChannel,   // DMUS_PCHANNEL_ALL (каналы для поиска)
     DWORD   dwStage,      // DMUS_PATH_BUFFER (этап пути)
     DWORD   dwBuffer,     // 0 (индекс в цепочке буферов)
     REFGUID guidObject,   // GUID_NULL (класс объекта)
     DWORD   dwIndex,      // 0 (индекс объекта в буфере)
     REFGUID iidInterface, // GUID желаемого объекта
     void    **ppObject);  // Указатель на создаваемый объект

Чтобы захватить объект звукового буфера вам достаточно указать его GUID и предоставить инициализируемый указатель. Показанная ниже функция получает заданный по умолчанию путь из объекта исполнителя и предоставляет вам объект IDirectSoundBuffer8, с которым вы можете экспериментировать:

IDirectSoundBuffer8 *GetSoundBuffer()
{
    IDirectMusicAudioPath8 *pDMAudioPath;
    IDirectSoundBuffer     *pDSB;
    IDirectSoundBuffer8    *pDSBuffer;

    // Получаем аудио-путь по умолчанию
    if(FAILED(g_pDMPerformance->GetDefaultAudioPath(
                                           &pDMAudioPath)))
        return NULL;

    // Создаем объект IDirectSoundBuffer
    // и освобождаем объект аудио-пути
    if(FAILED(pDMAudioPath->GetObjectInPath(DMUS_PCHANNEL_ALL,
                                       DMUS_PATH_BUFFER, 0,
                                       GUID_NULL, 0,
                                       IID_IDirectSoundBuffer,
                                       (LPVOID*)&pDSB))) {
        pDMAudioPath->Release();
        return FALSE;
    }
    pDMAudioPath->Release();

    // Запрашиваем новый объект звукового буфера
    // и возвращаем его
    if(FAILED(pDSB->QueryInterface(IID_IDirectSoundBuffer8,
                                   (void**)&pDSBuffer))) {
        pDSB->Release();
        return FALSE;
    }
    pDSB->Release();
    return pDSBuffer;
}

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


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

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