Использование FMOD в звуковых играх

Дата публикации:2005
Twitter Facebook Vkontakte

Достоинства и недостатки FMOD

Речь пойдёт о программной библиотеке для работы со звуком, известной под названием FMOD. Разработчиком этого программного продукта является компания Firelight Technologies Pty, Ltd. На момент написания статьи последним релизом FMOD была версия 3.74. С текущим состоянием дел и с новыми версиями, если они появились, вы можете ознакомиться на сайте www.FMOD.org. На том же сайте вы можете получить версии FMOD для различных операционных систем. Прямые ссылки на дистрибутив FMOD расположены здесь.

Библиотека функций FMOD представляет собой реализацию API верхнего уровня, который включает широкий набор функций для работы со звуковыми файлами различных форматов, обработки звуковых данных и воспроизведения звука через аудиосистему компьютера или игровых приставок. Для того чтобы рассказать обо всех возможностях этого API потребуется не один десяток web-страниц. Кроме того, основной нашей тематикой названа разработка звуковых игр, для которых главный интерес представляют функции для работы с объёмным (3D) звуком. Знакомством с этими функциями мы и ограничимся. Следует иметь в виду, что данная публикация не является полным руководством по FMOD, поэтому, разбирая приводимые ниже примеры, не следует пренебрегать официальной документацией. Все примеры в статье составлены на языке программирования C (Си).

Для создания источников 3D звука и расчета трёхмерной звуковой картины FMOD (версия для Windows) использует в качестве базы любую из следующих звуковых систем: WMM (Windows Multimedia), DirectSound3D (звуковая подсистема Microsoft DirectX) и A3D. FMOD обеспечивает такое взаимодействие с этими звуковыми системами, что переход от одной из них к другой требует минимальных изменений в коде программы (а часто вообще не требует никаких изменений). При работе с базовой звуковой системой DirectSound3D функции FMOD заметно облегчают управление объёмными источниками звука, упрощают их инициализацию, воспроизведение и позиционирование. Простота программирования FMOD (в сравнении с программированием базовых звуковых систем) является важным достоинством этого API (О программировании непосредственно DirectSound3D можно прочитать в статье Программирование объёмного звука в DirectSound3D).

Другим достоинством FMOD является лёгкость установки - достаточно разместить в системной папке или в папке, из которой запускается приложение, dll библиотеку, чтобы функции FMOD стали доступны. В версии 3.74, кроме обычной для Windows 32-разрядной dll, поставляется 64-разрядная библиотека FMOD.

FMOD содержит подробную документацию и примеры (вместе с заголовочными и другими служебными файлами) для VisualC, BorlandC, Watcom C, Borland Delphi и VisualBasic. Для некоммерческих приложений библиотека FMOD предоставляется бесплатно.

К недостаткам FMOD следует отнести отсутствие таких возможностей (которые реализованы в DirectSound3D), как создание направленных источников звука и создание зависимых источников звука (по крайней мере, в наборе функций FMOD не представлены функции, устанавливающие данные параметры пространственной обработки звука).

Модель пространства в FMOD

Модель пространства (система координат), используемая для размещения источников 3D звука, аналогична модели пространства в DirectSound3D. Здесь используется левосторонняя декартова система координат, состоящая из трёх ортогональных координатных осей. Ось X (икс) направлена вправо; ось Y (игрек) направлена вверх; ось Z (зэт) направлена вперед (то есть в монитор, если сидеть лицом к нему). Расстояние измеряется в метрах, но можно установить другую единицу измерения длин, задав соотношение между метром и новой единицей измерения. Любая точка в пространстве задается своими координатами, которые записываются в последовательности X, Y, Z. Координаты могут принимать как положительные, так и отрицательные значения. Координатную триаду XYZ, характеризующую положение точки в пространстве, можно рассматривать как вектор, начало которого находится в начале отсчета, то есть в точке с координатами (0, 0, 0), и конечной точкой с координатами (X, Y, Z). Кроме векторов положения, в FMOD используются векторы скорости, необходимые для вычисления доплеровского смещения в спектре звука движущихся источников. Вектор скорости задается тремя координатами своей конечной точки (начальной точкой вектора скорости считается точка (0, 0, 0)). Следует заметить, что FMOD (как и DirectSound3D) не занимается расчетом координат движущегося объекта. Эту задачу должен решать программист, передавая функциям FMOD данные, необходимые для моделирования исключительно звуковой картины в конкретной точке пространства и в конкретный момент. То есть по сути FMOD (как и DirectSound3D) рассчитывает статическую звуковую картину, которую программист может сделать динамичной для слушателя (игрока), часто меняя положение источников звука.

В тех функциях FMOD, для которых в качестве параметра должен передаваться вектор, используется указатель на структуру, состоящую из трёх вещественных (float) чисел, или на массив, состоящий из трёх вещественных (float) чисел. То есть две следующие конструкции будут одинаково восприняты функциями FMOD:

/* масив */
float pos[3] = {10.0f, 2.0f, 4.2f};
/* структура */
struct VECTOR
{
float X;
float Y;
float Z;
};
VECTOR pos = {10.0f, 2.0f, 4.2f};

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

Объекты звуковой картины в FMOD

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

Источники звука в FMOD могут быть только точечными и ненаправленными, что означает отсутствие у источника собственных размеров в и всенаправленность распространения звука от этого источника. Положение слушателя, напротив, является ориентированным. Для этого задаютс направление двух ортогональных векторов единичного размера. Первый вектор ориентирует направление лица, то есть указывает, куда смотрит слушатель. Второй вектор всегда направлен из макушки вверх, то есть указывает направление вертикальной оси слушателя. Модуль каждого из этих векторов равен единице измерения длины, поэтому они носят названия единичных (Модуль вектора вычисляется как корень квадратный из суммы квадратов длин его проекций на оси координат).

Функции для работы с 3D звуком

для простоты изложения ниже приводятся не только функции FMOD, отвечающие непосредственно за создание объёмного звука, но и все те функции, которыми необходимо воспользоваться программисту, чтобы работать с библиотекой FMOD. Причем в списке функции расположены в той ориентировочной последовательности, в которой их следует вызывать в программе, взаимодействующей с FMOD. В документации, которая входит в комплект FMOD все приводимые ниже функции помещены в раздел "FSOUND API Reference".

  • FSOUND_GetVersion() - возвращает версию библиотеки FMOD, установленной на компьютере. Возвращаемое значение следует сравнить с константой FMOD_VERSION, которая хранит номер версии FMOD, для которой была скомпилирована программа.
  • FSOUND_SetOutput () / FSOUND_GetOutput () - выбрать/получить базовую звуковую систему (Windows Multimedia, DirectSound, A3D и т.п.). Выбор базовой системы должен производится до вызова функции FSOUND_Init().
  • FSOUND_SetDriver () / FSOUND_GetDriver () - выбрать/получить номер устройства вывода (звуковой карты). Выбор устройства должен производится до вызова функции FSOUND_Init().
  • FSOUND_SetMixer () /FSOUND_GetMixer () - выбрать/получить тип цифрового микшера. Выбор микшера должен производится до вызова функции FSOUND_Init(). Определение типа микшера не является обязательным, так как FMOD самостоятельно определит лучший из имеющихся вариантов.
  • FSOUND_Init() - инициализирует звуковую систему FMOD.
  • FSOUND_Sample_Load () - загружает в память и декодирует звуковой файл (поддерживаются .wav, .mp2, .mp3, .ogg, .raw и др.).
  • FSOUND_3D_SetDistanceFactor () - позволяет установить единицы измерения длин, отличные от метров.
  • FSOUND_3D_SetDopplerFactor () - позволяет установить доплеровское смещение. Базовое значение (1.0) соответствует скорости звука 340 м/с.
  • FSOUND_3D_SetRolloffFactor () - позволяет установить уровень потерь энергии звуковой волны (затухания). Базовое значение (1.0) соответствует нормальным условиям.
  • FSOUND_PlaySoundEx () - проигрывает звуковой файл, загруженный в память, через звуковой канал.
  • FSOUND_3D_SetAttributes () / FSOUND_3D_GetAttributes () - установить/получить вектор положения и вектор скорости источника звука.
  • FSOUND_3D_SetMinMaxDistance () / FSOUND_3D_GetMinMaxDistance () установить / получить минимальное и максимальное расстояние слышимости источника звука. Минимальным называется такое расстояние от источника звука до слушателя, при уменьшении которого громкость звука больше не возрастает, а остается на том значении, которого она достигла на минимальном расстоянии. Устанавливая разные минимальные расстояния, например, для самолета и шмеля, можно сделать их одинаково заметными на слух, несмотря на то, что гул мотора будет восприниматься как более мощный звук. Максимальным называется такое расстояние от источника звука до слушателя, начиная с которого громкость звука больше не уменьшается, а остается на уровне, который она достигла на максимальном расстоянии. Это означает, что как бы далеко не находился источник звука, он будет слышен.
  • FSOUND_SetPaused () - приостанавливает / возобновляет воспроизведение звука в канале.
  • FSOUND_3D_Listener_SetAttributes () / FSOUND_3D_Listener_GetAttributes () - установить/ получить вектор положения, вектор скорости и векторы ориентации слушателя.
  • FSOUND_Update () -обновить состояние звукового микшера, то есть обновить состояние звуковой панорамы, после чего все изменения в положении слушателя или источников звука вступают в силу.
  • FSOUND_Sample_Free () - освобождает память от звуковых данных, загруженных функцией FSOUND_Sample_Load().
  • FSOUND_Close () - выгружает звуковую систему FMOD.

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

Алгоритм использования FMOD в игровых программах

В центре нашего внимания будут игры, использующие объёмный (3D) звук. Как правило, в играх существует основной цикл игры, внутри которого происходит перемещение игровых объектов в пространстве. Такое перемещение может быть вызвано действиями игрока (например, в автосимуляторе это может быть поворот рулевого колеса вправо или влево) или изменением игровой ситуации (например, в "экшене" может приблизится монстр). Рассчитав новые координаты объектов, следует изменить и положение источников звука так, чтобы они соответствовали новому положению объектов в игровом пространстве.

Игровому циклу, как правило, предшествует та часть программы, в которой происходит инициализация переменных, загрузка необходимых библиотек и ресурсов. При использовании FMOD, в этой части должны располагаться функции: FSOUND_SetOutput, FSOUND_SetDriver и FSOUND_SetMixer. В качестве параметра для автоматического определения базовой звуковой системы в функцию FSOUND_SetOutput можно передать -1 (0FFFFFFFFh).

После успешной инициализации FMOD необходимо в соответствии со сценарием игры разместить в памяти звуковые данные. Функция FSOUND_Sample_Load, отвечающая за этот процесс, поддерживает несколько звуковых форматов и загрузка, скажем, .mp3 файла с точки зрения программиста ничем не отличается от загрузки файла в формате wav. Здесь полностью проявляются достоинства библиотеки FMOD, позволяющей упростить утомительную процедуру создания звуковых буферов и загрузки в них данных, характерную для DirectSound. Результатом работы FSOUND_Sample_Load будет указатель на образец звука, размещенный в памяти. Если обратиться к терминологии DirectSound, то это будет аналог вторичного звукового буфера. Указанный образец звука может быть использован для создания нескольких источников 3D звука, при этом нет необходимости создавать дополнительные копии этого образца.

Теперь, когда необходимые данные загружены, можно приступать к формированию трёхмерной звуковой картины. Необходимо помнить, что изменение вектора положения и вектора скорости, а также многих иных характеристик источника звука возможно только непосредственно во время воспроизведения звука функциями FSOUND_PlaySound и FSOUND_PlaySoundEx, поэтому, прежде чем поместить источник звука в какую-либо точку пространства, вы должны начать воспроизведение этого звука. Однако при вызове указанных функций звук будет воспроизводиться в той точке, где находится слушатель (Listener), что может нарушить сцену игры (например, монстр, который должен приближаться из самого дальнего закоулка, вдруг зарычит под ухом у игрока). Чтобы избежать подобного казуса, в функции FSOUND_PlaySoundEx() предусмотрен специальный параметр, который сразу же приостанавливает воспроизведение звука. Молчащий источник можно, без риска нарушить сцену игры, поместить в нужное место и возобновить воспроизведение звука. Для того чтобы изменения в звуковой картине вступили в силу, необходимо вызвать функцию FSOUND_Update.

Следует сказать, что в понятиях FMOD воспроизведение звука происходит через канал (channel), поэтому все, что программист собирается сделать с источником 3D звука, необходимо выполнять, пользуясь функциями, которые работают с каналом. Первым параметром в таких функциях выступает номер канала.

Синтаксис вызова функции FSOUND_PlaySoundEx следующий:

int F_API FSOUND_PlaySoundEx(
int channel,
FSOUND_SAMPLE *sptr,
FSOUND_DSPUNIT *dspunit,
signed char startpaused
);

В качестве параметров эта функция получает номер канала (channel), через который будет воспроизводится звук,; указатель (дескриптор sptr) на образец звука, размещенный в памяти; указатель (дескриптор dspunit) на блок каналов, к которому должен присоединиться вновь создаваемый канал (этот параметр может иметь значение NULL); флаг приостановки воспроизведения (paused), который должен быть TRUE, чтобы воспроизведение звука сразу же было приостановлено.

Если необходимо создать новый канал, то в параметре channel передается константа FSOUND_FREE. Если необходимо, чтобы звук воспроизводился во всех существующих каналах, то в channel передается константа FSOUND_ALL.

Функция возвращает номер (дескриптор) канала, через который воспроизводится звук. Если возникла ошибка, то функция возвращает -1 (0FFFFFFFFH). Номер (дескриптор) канала необходим для работы с функциями канального уровня, например, FSOUND_3D_SetAttributes.

Таким образом, создание и позиционирование источника звука программно может быть реализовано так:

FSOUND_SAMPLE *samp1 = NULL;
int channel1 = -1;
/* проверяем версию FMOD */
if (FSOUND_GetVersion() < FMOD_VERSION)
{
/* обработка ошибки */
return 1;
}
/* выбираем базовую звуковую систему DirectSound */
FSOUND_SetOutput(FSOUND_OUTPUT_DSOUND);
/* выбираем звуковую карту по умолчанию */
FSOUND_SetDriver(0);
/* позволяем FMOD выбрать микшер */
FSOUND_SetMixer(FSOUND_MIXER_AUTODETECT);
/* инициализируем FMOD */
/* частота, к которой будут приводится все звуки в микшере, - 22050 Гц */
/* максимальное число программных каналов - 32 */
if (!FSOUND_Init(22050, 32, 0))
{
/* обработка ошибки */
return 1;
}
/* загружаем в память звуковой фрагмент из файла myfile.wav */
/* константа FSOUND_HW3D указывает, что необходимо включить аппаратную поддержку 3D звука */
samp1 = FSOUND_Sample_Load(FSOUND_FREE, "myfile.wav", FSOUND_HW3D, 0, 0);
if (!samp1)
{
/* обработка ошибки */
return 1; 
}
/* устанавливаем циклическое воспроизведение, т.е. */
/* источник слышен всегда */
FSOUND_Sample_SetMode(samp1, FSOUND_LOOP_NORMAL);
/* указываем позицию и скорость источника */
float pos[3] = { -10.0f, -20.0f, 0.5f };
float vel[3] = { 0,0,0 };
/* создаем новый канал для воспроизведения звука */
/* сразу же приостанавливаем воспроизведение */
channel1 = FSOUND_PlaySoundEx(FSOUND_FREE, samp1, NULL, TRUE);
if (channel1 == -1)
{
/* обработка ошибки */
return 1; 
}
/* помещаем источник звука в нужную точку */
FSOUND_3D_SetAttributes(channel1, pos, vel);
/* здесь может происходить изменение позиции слушателя */
. . .
/* обновляем звуковую картину */
FSOUND_Update();
/* возобновляем воспроизведение */
FSOUND_SetPaused(channel1, FALSE);
. . .

Следует помнить, что функции FSOUND_PlaySound и FSOUND_PlaySoundEx помещают источник звука в текущую позицию слушателя, поэтому , если в параметре channel этих функций указан номер (дескриптор) существующего канала, то, несмотря на то, что для этого канала функцией FSOUND_3D_SetAttributes была установлена позиция, отличная от позиции слушателя, все равно источник звука переместится в позицию слушателя. Чтобы избежать искажения звуковой картины, необходимо перед вызовом FSOUND_PlaySoundEx сохранить позицию и скорость источника звука в специальных переменных, получив эти значения функцией FSOUND_3D_GetAttributes. После чего вызвать FSOUND_PlaySoundEx с приостановкой воспроизведения; затем переместить источник в нужную точку, воспользовавшись сохраненными значениями, а уж потом продолжить воспроизведение. Указанная последовательность действий, кроме всего прочего, напрямую относится к однократно проигрываемым звукам (то есть к таким источникам, которые не издают звук непрерывно, а звучат лишь при наступлении определенного события в игре).

Для изменения позиции, скорости и ориентации слушателя в пространстве следует использовать функцию FSOUND_3D_Listener_SetAttributes. Вот ее синтаксис:

void F_API FSOUND_3D_Listener_SetAttributes(
const F_FLOAT_API *pos,
const F_FLOAT_API *vel,
F_FLOAT_API fx,
F_FLOAT_API fy,
F_FLOAT_API fz,
F_FLOAT_API tx,
F_FLOAT_API ty,
F_FLOAT_API tz
);

В качестве параметров эта функция получает указатель на триаду координат позиции (pos), указатель на вектор скорости (vel), X, Y и Z составляющие единичного вектора, определяющего фронтальную ориентацию головы слушателя (fx, fy, fz); X, Y и Z составляющие единичного вектора, определяющего вертикальную ориентацию головы слушателя (tx, ty, tz). Несмотря на тип void, функция, согласно официальной документации, возвращает TRUE, если ее выполнение было успешным, и FALSE в случае ошибки.

В отличие от DirectSound3D, в FMOD может быть не один, а несколько слушателей. Это сделано для игровых приставок, где одно игровое устройство могут использовать одновременно несколько игроков. Выбор текущего слушателя производится при помощи функции FSOUND_3D_Listener_SetCurrent.

Когда игра завершена и в заключительном сегменте кода программы происходит освобождение ресурсов, занятых программой, необходимо использовать функции FSOUND_Sample_Free, которой в качестве параметра передается указатель (дескриптор) фрагмента звуковых данных в памяти, и FSOUND_Close, которая вызывается без параметров и выгружает звуковую систему FMOD.

FSOUND_Sample_Free(samp1); 
FSOUND_Close();

Обработка ошибок

Большинство функций FMOD, если иное не оговорено в документации, возвращают TRUE в случае успешного завершения. Если же в работе функции возникла ошибка, то возвращается FALSE. Для того чтобы получить дополнительную информацию об ошибке, следует воспользоваться функциями FSOUND_GetError и FMOD_ErrorString. Последняя функция является макросом, поэтому необходимо включить заголовочный файл FMOD_errors.h.

Функция FSOUND_GetError не требует параметров и возвращает код ошибки, возникшей при выполнении функции FMOD. Макрос FMOD_ErrorString получает этот код в качестве параметра и возвращает текстовое описание ошибки. Вот пример вызова этих функций:

/* вывод сообщения об ошибке в консольном режиме */
printf("Error: %s\n", FMOD_ErrorString(FSOUND_GetError()));

Управление ресурсами и звуковые платы нижней ценовой категории

Сегодня существуют различные по производительности и возможностям звуковые карты. Одни из них способны поддерживать до 100 аппаратных каналов для воспроизведения 3D звука, другие - не более четырех. Было бы недальновидно при разработке игр ориентироваться на "слабые" звуковые карты. Однако ошибка, которая может возникнуть при нехватке аппаратных каналов в игре с большим количеством источников звука, может сделать игру совершенно неработоспособной на указанной группе звуковых карт. В качестве выхода из данной ситуации FMOD предлагает использовать функцию FSOUND_SetMinHardwareChannels.

Эта функция вызывается один раз перед вызовом FSOUND_Init и устанавливает минимальнное количество аппаратных каналов, которое должно поддерживаться звуковой картой. После этого либо все из указанного при вызове FSOUND_SetMinHardwareChannels числа каналов будут воспроизводится с использованием аппаратных каналов, либо аппаратная поддержка использоваться не будет и микширование каналов будет происходить программным путем. Иными словами, например, если у вас 16 звуков и карта имеет такое же или большее число аппаратных каналов, то воспроизведение будет идти через эти каналы. Если окажется, что у карты всего 4 канала, то смешивание всех звуков будет выполняться программно.

Поддержка приоритетов

В FMOD реализована система приоритетов. Если множество источников звука должны воспроизводиться на ограниченном числе каналов, то некоторые из них могут быть более важными, чем другие. Если, например, главным действующим лицом в игре выступает стрелок, то воспроизведение звука выстрела было бы, вероятно, самым важным. Так что этому источнику звука (выстрел) следовало бы установить самый высокий приоритет посредством FSOUND_SetPriority. Менее важные звуки не будут воспроизводиться, если уже заняты все доступные каналы.

Заключение

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



Распространение материалов сайта означает, что распространитель принял условия лицензионного соглашения.
Идея и реализация: © Владимир Довыденков и Анатолий Камынин,  2004-2017