Программирование игр, создание игрового движка, OpenGL, DirectX, физика, форум
GameDev.ru / Программирование / Статьи / DirectMusic. Урок 1.

DirectMusic. Урок 1.

В данном уроке нам предстоит научиться самому главному, а именно создать COM интерфейс, отвечающий за работу DirectMusic, проинициализировать его и, собственно говоря, сыграть какую-нибудь мелодию. Несмотря на определенный прогресс в данной области DirectMusic до сих пор позволяет проигрывать только 2 основных типа аудио-файлов: MIDI и WAV.

Автор: Visor

Для успешной работы данного примера нам потребуется включить заголовочный файл dmusici.h, а так же библиотеки dsound.lib и dxguid.lib.

Для начала создадим 3 глобальные переменные (указатели) и установим их в нулевое "значение".

IDirectMusicLoader8*      g_pLoader         = NULL;
IDirectMusicPerformance8* g_pPerformance    = NULL;
IDirectMusicSegment8*     g_pSegment        = NULL;

Можно конечно создавать их непосредственно перед инициализацией, но это только портит общую динамику работы.

После этого в выбранной нами ф-ии делаем следующее:

// Инициализируем (создаем)COM интерфейс, 
// чтобы использовать его впоследствии в DirectMusic (DirectSound). 
    CoInitialize(NULL);

// Создаем (инициализируем) загрузчик
    CoCreateInstance( CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC, 
                      IID_IDirectMusicLoader8, (void**)&g_pLoader );


// Создаем (инициализируем) объект Perfomance
    CoCreateInstance( CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC, 
                      IID_IDirectMusicPerformance8, (void**)&g_pPerformance);

// Инициализируем объект Performance со стандартым аудио-путем. 
// Одновременно инициализируем и DirectMusic и DirectSound
    g_pPerformance->InitAudio( NULL, NULL, NULL, 
                               DMUS_APATH_SHARED_STEREOPLUSREVERB, 64,
                               DMUS_AUDIOF_ALL, NULL );

Остановимся подробнее на методе InitAudio, т.к. это может пригодиться нам в дальнейшем. В общем виде это выглядит так:

HRESULT InitAudio(
  IDirectMusic** ppDirectMusic,
  IDirectSound** ppDirectSound,
  HWND hWnd,
  DWORD dwDefaultPathType,
  DWORD dwPChannelCount,
  DWORD dwFlags,
  DMUS_AUDIOPARAMS *pParams
);
где ppDirectMusic - адрес переменной, которая определяет или получает указатель на объект DirectMusic. Данный объект должен содержать правильный указатель на объект IDirectMusic8. Если данное значение NULL , то DirectMusic создается и используется непосредственно объектом Performance.

ppDirectSound - адрес переменной, которая определяет или получает указатель на объект DirectSound. Если данное значение NULL, то DirectMusic создает private объект DirectSound. Если же значение переменной NULL, то DirectMusic создает объект DirectSound и возвращает его в качестве указателя.

hWnd - Хэндл окна, используемого для создания DirectSound. Если NULL, то будет использован звук на "заднем плане" (то есть не зависит от расположения окон программы).

dwDefaultPathType - переменная определяющая аудио-путь "по-умолчанию" (default). Создать ее можно при помощи IDirectMusicPerformance8:: CreateStandardAudioPath().

dwPChannelCount - переменная определяющая количество performance-каналов, при условии, что DwDefaultPathType имеет ненулевое значение.

dwFlags - флаг, определяющий запрошенные "фишки". Если pParams не NULL, то данное значение игнорируется, а данные pParams надо представить в виде структуры DMUS_AUDIOPARAMS. В противном случае необходимо описать следующие флаги:

DMUS_AUDIOF_3D - 3D буферы
DMUS_AUDIOF_ALL - поддержка все "фишек"
DMUS_AUDIOF_BUFFERS - мульти-буферы
DMUS_AUDIOF_ENVIRON - имитация окружающей среды
DMUS_AUDIOF_EAX - эффект EAX
DMUS_AUDIOF_STREAMING - поддержка "потоковых волн"

pParams - адрес структуры DMUS_AUDIOPARAMS, определяющей параметры синтезатора и получающая информацию о текущих параметрах. Может быть NULL, если устраивают default установки.

Если все в порядке, то метод возвращает S_OK.

Комментарии: данный метод может быть вызван лишь единожды и не может быть использован для возврата интерфейса IDirectMusic8. По окончанию работы программы объект Performance должен быть уничтожен при помощи IDirectMusicPerformance8::CloseDown(). Использование структуры DMUS_AUDIOPARAMS (#include dmusicf.h) позволяет контролировать такие вещи, как:
-  частоту дискретизации выходного аудио-потока
-  количество каналов (64 по умолчанию)
-  выбор синтезатора

Теперь вернемся к нашей программе. Мы проинициализировали объект Performance, но у нас нет пути, откуда брать аудио-файлы. Проинициализируем его:

    CHAR strPath[512];
    //в char загоняем системную директорию (например c:\winnt)
    if( GetWindowsDirectory( strPath, MAX_PATH+1 ) == 0 )
        return 0;

    //добавляем к "системному" пути директорию media
    strcat( strPath, "\\media" );

    // "Сказать" DirectMusic где находится директория по умолчанию
    WCHAR wstrSearchPath[MAX_PATH+1];
    MultiByteToWideChar( CP_ACP, 0, strPath, -1, 
                         wstrSearchPath, MAX_PATH );
    wstrSearchPath[MAX_PATH] = 0;

    g_pLoader->SetSearchDirectory( GUID_DirectMusicAllTypes, 
                                 wstrSearchPath, FALSE );

Мы задали директорию поиска. Теперь из этой директории выбираем необходимый файл:

  
    //имя файла. Не забывайте символ L (так надо).
    WCHAR wstrFileName[MAX_PATH] = L"Вход в Windows.wav";   
    if( FAILED( g_pLoader->LoadObjectFromFile( CLSID_DirectMusicSegment,
                                               IID_IDirectMusicSegment8,
                                               wstrFileName,
                                               (LPVOID*) &g_pSegment ) ) )
    {
        MessageBox( NULL, "Не найден звуковой файл. Приложение завершается", 
                          "DirectMusic Tutorial №1", MB_OK );
        g_pPerformance->CloseDown();
        g_pLoader->Release(); 
        g_pPerformance->Release();
        CoUninitialize();
        return 0;
    }

Если приложение "дожило" до данного момента, значит, инициализация прошла успешно, а файл существует. Значит самое время его проиграть :):

// загружаем полученный сегмент в синтезатор
g_pSegment->Download( g_pPerformance );
// Проигрываем данный сегмент
g_pPerformance->PlaySegmentEx( g_pSegment, NULL, NULL, 0, 
                                   0, NULL, NULL, NULL );

// DirectMusic играет музыку на заднем плане, пока светится MessageBox
MessageBox( NULL, "Остановка и выход", "DirectMusic Tutorial", MB_OK);

// Останавливаем музыку, закрываем Performance
  // если в качестве первого параметра NULL, то останавливаются 
// и освобождаются все сегменты
g_pPerformance->Stop( g_pSegment, NULL, 0, 0 );
g_pPerformance->CloseDown();

// Очищаем память из под созданных интерфейсов
g_pLoader->Release(); 
g_pPerformance->Release();
g_pSegment->Release();

// уничтожаем объект COM
CoUninitialize();

Хочется заметить, что помимо ф-ии Stop(), существует ф-ия StopEx(). Их основное отличие состоит в том, что Stop() приводит к остановке сегмента, а StopEx() - объекта. Поэтому без надобности StopEx() использовать не следует.

В данном приложении так же можно немного разнообразить жизнь. Например иногда интересно знать: играет ли музыка в данный момент. Это можно сделать при помощи метода IDirectMusicPerformance8::IsPlaying():

if (g_performance->IsPlaying(g_segment) == S_OK)
{
  //музыка играет
}

Если необходимо зациклить музыку на n-ое число раз, то нужно вызвать метод IDirectMusicSegment8::SetRepeats. В качестве параметра указать количество повторов. Например:

g_pSegment->SetRepeats(0);//проиграть 1 раз без повтора
g_pSegment->SetRepeats(-1);//проигрывать до бесконечности
g_pSegment->SetRepeats(DMUS_SEG_REPEAT_INFINITE); //аналогично
g_pSegment->SetRepeats(3);//проиграть 3 раза

P.S. Данный метод конечно необходимо вызвать до того, как вы вызовете метод PlaySegmentEx.

Если нам не нужен весь сегмент, но необходима некоторая его часть, то сегмент можно клонировать, т.е. вырезать необходимый блок (по времени) и сопоставить ему все свойства его предка. Этим занимается метод IDirectMusicSegment8::Clone().

HRESULT Clone (
  MUSIC_TIME mtStart, 
  MUSIC_TIME mtEnd, 
  IDirectMusicSegment** ppSegment
);
mtStart, mtEnd - начало и конец сегмента клона.
ppSegment - адрес переменной IdirectMusicSegment.

Например:

IDirectMusicSegment8 *g_pSegment2 = NULL;
g_pSegment->Clone(3000,5000,(IDirectMusicSegment**)&g_pSegment2);

P.S. Следует учесть, что при неправильном выборе параметров времени mtStart и mtEnd выставляются соответственно в начало и конец сегмента. Чтобы получить идентичный дубликат нужно выставить значения в NULL.

Вот, в принципе, и все, что мне хотелось рассказать в данном уроке. Конечно, у IDirectMusicPerformance и IDirectMusicSegment существует еще много методов, но в основном они вспомогательного характера и на практике используются довольно редко. Хотя о некоторых вспомогательных и полезных моментах этого дела будет рассказано в последующих уроках.

5 мая 2003

#DirectMusic, #звук


Обновление: 23 апреля 2011

2001—2018 © GameDev.ru — Разработка игр