FMOD — проприетарная программная аудио библиотека, которая позволяет проигрывать музыкальные файлы различных форматов. Эта библиотека написана на С++ и используется во многих известных играх, например Call of Duty 4 Diablo 3 Far Cry, Bioshock и др…
У меня возникла необходимость в ее использовании в Android и после долгих поисков как это сделать я решил написать статью.
Для начала нужно скачать саму библиотеку FMOD Ex под андроид http://www.fmod.org/fmod-downloads.html. Там содержится документация и все необходимые файлы.
Далее нужно настроить android ndk. Он нужен для того, чтобы можно было запускать код C/C++ в андроид проекте http://developer.android.com/tools/sdk/ndk/index.html.
У меня Mac OS поэтому качал версию под Mac OS X 32-bit.
После того когда ndk скачано нужно настроить Eclipse для поддержки Android ndk и заставить проект работать с C/C++ кодом.
Шаг 1: Настраиваем среду
Итак, создаем новый проект (playSound). Я выбирал под андроид 2.3, после чего нужно указать где находится ndk (Eclipse -> Preferences -> Android -> NDK) и в NDK Location указать путь к скачанному ndk.

Когда путь к ndk указан, нужно добавить в проект поддержку ndk. Для этого щелкаем правой клавишей на проекте -> Android Tools -> Add Native Support…

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

Если же нажать на Finish то в проекте появляется папка jni (Java Native Interface) в которой появилось 2 файла (PlaySound.cpp и Android.mk).
В Android.mk содержатся настройки для сборки библиотеки. Там мы должны указать все файлы, которые будут принимать участие в создании библиотеки. Сейчас он выглядит так:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := PlaySound
LOCAL_SRC_FILES := PlaySound.cpp
include $(BUILD_SHARED_LIBRARY)
LOCAL_MODULE название библиотеки которая сформируется
LOCAL_SRC_FILES название файлов, которые принимают участие в формировании библиотеки.
Если сейчас сделать билд проекта (command + B), то в папке libs появилась папка armabi и в ней находится библиотека libPlaySound.so. На данный момент она не содержит никаких функций или методов, но если их описать и делать их вызов с java кода, то вызываться они будут как раз из этой библиотеки.
Эта библиотека сформировалась из файла (PlaySound.cpp). Для того чтобы использовать функции из fmod, нам нужно подключить готовую библиотеку libfmod.so которая скачана с сайта fmod. Если ее скопировать в папку armabi то она там не появится, так как эта папка создается на этапе компиляции и библиотеки туда добавляются динамически.
Для включения звукового выхода через режим звуковой дорожки необходимо включить и инициализировать Java FMOD аудио драйвер в приложении (не обязательно, если используется OpenSL выход). Для этого необходимо будет добавить fmodex.jar в проект Java, и импортировать org.fmod.FMODAudioDevice пакет. Аудио Драйвер Java FMOD также требует, чтобы приложение загружало fmodex библиотеку.
FMODAudioDevice класс имеет две функции которые необходимо вызвать для проигрывания аудио. Они могут быть вызваны в любое время, но в документации рекомендуется делать вызовы start() и stop() в переопределеннsые методы onStart() и onStop ().
fmodex.jar находится в архиве который скачан с сайта fmod. fmodapi44418android -> api -> lib -> fmodex.jar
Для того чтобы добавить jar файл в проект нужно скопировать его в папку libs. В eclipse достаточно перетащить ее туда с помощью мышки и подтвердить копирование.


Теперь нужно добавить эту библиотеку в Java Build Path. Для этого нужно нажать правой клавишей по проекту и зайти в свойства проекта.

Далее переход в вкладку Java Build Path, а там в вкладку Libraries и нажимаем Add jars… Там нужно указать где находится fmodex.jar в проекте и жмем ОК.

и дале переход в вкладку Order and Export и поставить галочку на только что добавленой библиотеке.

Нажимаем Ок и можем приступать к инициализации аудио драйвера.
Переходим в MainActivity.java и добавляем два Override методы.
package com.example.playsound;
import org.fmod.FMODAudioDevice;
import android.os.Bundle;
import android.app.Activity;
public class MainActivity extends Activity {
static {
System.loadLibrary(«fmodex»);
}
private FMODAudioDevice mFMODAudioDevice = new FMODAudioDevice();
Override
public void onStart()
{
super.onStart();
mFMODAudioDevice.start();
}
Override
public void onStop()
{
mFMODAudioDevice.stop();
super.onStop();
}
Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
FMODAudioDevice использует функции, которые находятся в сишной библиотеке libfmodex.so. Если сейчас запустить проект, то будет ошибка, потому что компилятор не сможет найти этой библиотеки.
static {
System.loadLibrary(«fmodex»);
}
В этом месте указано что библиотека fmodex будет загружаться в проект. Остается ее разместить в папке libs -> armabi. Для этого я из папки fmodapi44418android копирую папку api в рабочую папку, где лежит мой проект

И в Android.mk указываю путь к библиотекам.

LOCAL_PATH := $(call my-dir)
#
# FMOD Ex Shared Library
#
include $(CLEAR_VARS)
LOCAL_MODULE := fmodex
LOCAL_SRC_FILES := ../../api/lib/$(TARGET_ARCH_ABI)/libfmodex.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../api/inc
include $(PREBUILT_SHARED_LIBRARY)
LOCAL_SRC_FILES – путь к библиотекам
LOCAL_EXPORT_C_INCLUDES – путь к заголовочным файлам
Также обязательно нужно указать, что библиотека fmodex будет подключаться и использоваться в созданной библиотеке PlaySound. so include $(CLEAR_VARS)
LOCAL_MODULE := PlaySound
LOCAL_SRC_FILES := PlaySound.cpp
LOCAL_LDLIBS := -llog
LOCAL_SHARED_LIBRARIES := fmodex
include $(BUILD_SHARED_LIBRARY)
LOCAL_LDLIBS := -llog для того чтобы можно было пользоваться LogCat в сишных модулях.
Полный код файла выглядит так:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := PlaySound
LOCAL_SRC_FILES := PlaySound.cpp
LOCAL_LDLIBS := -llog
LOCAL_SHARED_LIBRARIES := fmodex
include $(BUILD_SHARED_LIBRARY)
#
# FMOD Ex Shared Library
#
include $(CLEAR_VARS)
LOCAL_MODULE := fmodex
LOCAL_SRC_FILES := ../../api/lib/$(TARGET_ARCH_ABI)/libfmodex.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../api/inc
include $(PREBUILT_SHARED_LIBRARY)
Когда пути добавлены, нужно сделать build проекта (command + B) и в папке armabi появляется необходимая библиотека

Теперь можем запускать проект и убедиться что программа успешно выполнилась и библиотеки все проинициализированы.
Шаг 2. Проигрываем музыку
Поскольку libfmodex сишная библиотека, то и пользоваться ею нужно с сишного кода, а вызывать его в java коде. Для этого в java надо написать какую функцию реализовать в сишном коде.
Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public native void play(String path);
Теперь нужно реализовать функцию play в PlaySound.cpp. Можно писать самому, а можно воспользоваться утилитой javah.
Для этого нужно открыть терминал, перейти в директорию с проектом и вызвать javah. У меня это выглядело так:

com.example.playsound — название пакета, а MainActivity — класс где находится описание функции которую нужно сгенерировать.
После обновления проекта (F5) видно что появился новый файл com_example_playsound_MainActivity.h

Он содержит прототип функции play, который выглядит так:
JNIEXPORT void JNICALL Java_com_example_playsound_MainActivity_play
(JNIEnv *, jobject, jstring);
Переходим в PlaySound.cpp и добавляем необходимые нам библиотеки для работы с fmod.
#include "../../api/inc/fmod_errors.h"
#include "../../api/inc/fmod.h"
Ну и конечно все остальные при необходимости.
Также я еще подключил
#include <android/log.h>
#include <stdlib.h>
#include <string.h>
Для того чтобы можно было выводить сообщения в LogCat, работать со строками и др.
И конечно файл с прототипом функции play.
#include «com_example_playsound_MainActivity.h»
Далее реализуем функцию:
#include <jni.h>
#include <android/log.h>
#include <stdlib.h>
#include <string.h>
#include «com_example_playsound_MainActivity.h»
#include "../../api/inc/fmod_errors.h"
#include "../../api/inc/fmod.h"
JNIEXPORT void JNICALL Java_com_example_playsound_MainActivity_play
(JNIEnv *env, jobject thiz, jstring mediaPath) {
// mediaPath буде містити шллях до файлу, який буде програватись (в java ми його передаємо void play(String path))
// Для того чтобы перевести со стринга java в массив символов пишем следующее
const char *_mediaPath = env->GetStringUTFChars(mediaPath, NULL);
// Создаем необходимые переменные
FMOD_SYSTEM *gSystem;
FMOD_SOUND *sound;
FMOD_CHANNEL *channel;
FMOD_RESULT result = FMOD_OK;
// Инициализируем среду fmod.
FMOD_System_Create(&gSystem);
result = FMOD_System_Init(gSystem, 32, FMOD_INIT_NORMAL, 0);
if (result != FMOD_OK) // если возникла ошибка, то выводим ее в LogCat
__android_log_print(ANDROID_LOG_ERROR, «fmod», «FMOD error! (%d) %s\n%s:%d», result, FMOD_ErrorString(result), __FILE__, __LINE__);
// Создаем песню и проигрываем
FMOD_System_CreateSound(gSystem, _mediaPath, FMOD_DEFAULT, 0, &sound);
FMOD_System_PlaySound(gSystem, FMOD_CHANNEL_FREE, sound, false, &channel);
}
Теперь осталось добавить в MainActivity загрузку библиотеки PlaySound
static {
System.loadLibrary(«fmodex»);
System.loadLibrary(«PlaySound»);
}
записать на устройство свою любимую песенку, получить путь к ней и вызвать play.
Песню я записал в корень карты памяти.
Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
File extStore = Environment.getExternalStorageDirectory();
play(extStore.getAbsoluteFile() + "/Capital-Cities-Safe-And-Sound(muzofon.com).mp3");
}
Проблема только в том, что нужно долго ждать чтобы песня проигралась (на моем дешевом китайском где-то около минуты ).
У меня возникла необходимость в ее использовании в Android и после долгих поисков как это сделать я решил написать статью.
Для начала нужно скачать саму библиотеку FMOD Ex под андроид http://www.fmod.org/fmod-downloads.html. Там содержится документация и все необходимые файлы.
Далее нужно настроить android ndk. Он нужен для того, чтобы можно было запускать код C/C++ в андроид проекте http://developer.android.com/tools/sdk/ndk/index.html.
У меня Mac OS поэтому качал версию под Mac OS X 32-bit.
После того когда ndk скачано нужно настроить Eclipse для поддержки Android ndk и заставить проект работать с C/C++ кодом.
Шаг 1: Настраиваем среду
Итак, создаем новый проект (playSound). Я выбирал под андроид 2.3, после чего нужно указать где находится ndk (Eclipse -> Preferences -> Android -> NDK) и в NDK Location указать путь к скачанному ndk.

Когда путь к ndk указан, нужно добавить в проект поддержку ndk. Для этого щелкаем правой клавишей на проекте -> Android Tools -> Add Native Support…

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

Если же нажать на Finish то в проекте появляется папка jni (Java Native Interface) в которой появилось 2 файла (PlaySound.cpp и Android.mk).
В Android.mk содержатся настройки для сборки библиотеки. Там мы должны указать все файлы, которые будут принимать участие в создании библиотеки. Сейчас он выглядит так:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := PlaySound
LOCAL_SRC_FILES := PlaySound.cpp
include $(BUILD_SHARED_LIBRARY)
LOCAL_MODULE название библиотеки которая сформируется
LOCAL_SRC_FILES название файлов, которые принимают участие в формировании библиотеки.
Если сейчас сделать билд проекта (command + B), то в папке libs появилась папка armabi и в ней находится библиотека libPlaySound.so. На данный момент она не содержит никаких функций или методов, но если их описать и делать их вызов с java кода, то вызываться они будут как раз из этой библиотеки.
Эта библиотека сформировалась из файла (PlaySound.cpp). Для того чтобы использовать функции из fmod, нам нужно подключить готовую библиотеку libfmod.so которая скачана с сайта fmod. Если ее скопировать в папку armabi то она там не появится, так как эта папка создается на этапе компиляции и библиотеки туда добавляются динамически.
Для включения звукового выхода через режим звуковой дорожки необходимо включить и инициализировать Java FMOD аудио драйвер в приложении (не обязательно, если используется OpenSL выход). Для этого необходимо будет добавить fmodex.jar в проект Java, и импортировать org.fmod.FMODAudioDevice пакет. Аудио Драйвер Java FMOD также требует, чтобы приложение загружало fmodex библиотеку.
FMODAudioDevice класс имеет две функции которые необходимо вызвать для проигрывания аудио. Они могут быть вызваны в любое время, но в документации рекомендуется делать вызовы start() и stop() в переопределеннsые методы onStart() и onStop ().
fmodex.jar находится в архиве который скачан с сайта fmod. fmodapi44418android -> api -> lib -> fmodex.jar
Для того чтобы добавить jar файл в проект нужно скопировать его в папку libs. В eclipse достаточно перетащить ее туда с помощью мышки и подтвердить копирование.


Теперь нужно добавить эту библиотеку в Java Build Path. Для этого нужно нажать правой клавишей по проекту и зайти в свойства проекта.

Далее переход в вкладку Java Build Path, а там в вкладку Libraries и нажимаем Add jars… Там нужно указать где находится fmodex.jar в проекте и жмем ОК.

и дале переход в вкладку Order and Export и поставить галочку на только что добавленой библиотеке.

Нажимаем Ок и можем приступать к инициализации аудио драйвера.
Переходим в MainActivity.java и добавляем два Override методы.
package com.example.playsound;
import org.fmod.FMODAudioDevice;
import android.os.Bundle;
import android.app.Activity;
public class MainActivity extends Activity {
static {
System.loadLibrary(«fmodex»);
}
private FMODAudioDevice mFMODAudioDevice = new FMODAudioDevice();
Override
public void onStart()
{
super.onStart();
mFMODAudioDevice.start();
}
Override
public void onStop()
{
mFMODAudioDevice.stop();
super.onStop();
}
Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
FMODAudioDevice использует функции, которые находятся в сишной библиотеке libfmodex.so. Если сейчас запустить проект, то будет ошибка, потому что компилятор не сможет найти этой библиотеки.
static {
System.loadLibrary(«fmodex»);
}
В этом месте указано что библиотека fmodex будет загружаться в проект. Остается ее разместить в папке libs -> armabi. Для этого я из папки fmodapi44418android копирую папку api в рабочую папку, где лежит мой проект

И в Android.mk указываю путь к библиотекам.

LOCAL_PATH := $(call my-dir)
#
# FMOD Ex Shared Library
#
include $(CLEAR_VARS)
LOCAL_MODULE := fmodex
LOCAL_SRC_FILES := ../../api/lib/$(TARGET_ARCH_ABI)/libfmodex.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../api/inc
include $(PREBUILT_SHARED_LIBRARY)
LOCAL_SRC_FILES – путь к библиотекам
LOCAL_EXPORT_C_INCLUDES – путь к заголовочным файлам
Также обязательно нужно указать, что библиотека fmodex будет подключаться и использоваться в созданной библиотеке PlaySound. so include $(CLEAR_VARS)
LOCAL_MODULE := PlaySound
LOCAL_SRC_FILES := PlaySound.cpp
LOCAL_LDLIBS := -llog
LOCAL_SHARED_LIBRARIES := fmodex
include $(BUILD_SHARED_LIBRARY)
LOCAL_LDLIBS := -llog для того чтобы можно было пользоваться LogCat в сишных модулях.
Полный код файла выглядит так:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := PlaySound
LOCAL_SRC_FILES := PlaySound.cpp
LOCAL_LDLIBS := -llog
LOCAL_SHARED_LIBRARIES := fmodex
include $(BUILD_SHARED_LIBRARY)
#
# FMOD Ex Shared Library
#
include $(CLEAR_VARS)
LOCAL_MODULE := fmodex
LOCAL_SRC_FILES := ../../api/lib/$(TARGET_ARCH_ABI)/libfmodex.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../api/inc
include $(PREBUILT_SHARED_LIBRARY)
Когда пути добавлены, нужно сделать build проекта (command + B) и в папке armabi появляется необходимая библиотека

Теперь можем запускать проект и убедиться что программа успешно выполнилась и библиотеки все проинициализированы.
Шаг 2. Проигрываем музыку
Поскольку libfmodex сишная библиотека, то и пользоваться ею нужно с сишного кода, а вызывать его в java коде. Для этого в java надо написать какую функцию реализовать в сишном коде.
Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public native void play(String path);
Теперь нужно реализовать функцию play в PlaySound.cpp. Можно писать самому, а можно воспользоваться утилитой javah.
Для этого нужно открыть терминал, перейти в директорию с проектом и вызвать javah. У меня это выглядело так:

com.example.playsound — название пакета, а MainActivity — класс где находится описание функции которую нужно сгенерировать.
После обновления проекта (F5) видно что появился новый файл com_example_playsound_MainActivity.h

Он содержит прототип функции play, который выглядит так:
JNIEXPORT void JNICALL Java_com_example_playsound_MainActivity_play
(JNIEnv *, jobject, jstring);
Переходим в PlaySound.cpp и добавляем необходимые нам библиотеки для работы с fmod.
#include "../../api/inc/fmod_errors.h"
#include "../../api/inc/fmod.h"
Ну и конечно все остальные при необходимости.
Также я еще подключил
#include <android/log.h>
#include <stdlib.h>
#include <string.h>
Для того чтобы можно было выводить сообщения в LogCat, работать со строками и др.
И конечно файл с прототипом функции play.
#include «com_example_playsound_MainActivity.h»
Далее реализуем функцию:
#include <jni.h>
#include <android/log.h>
#include <stdlib.h>
#include <string.h>
#include «com_example_playsound_MainActivity.h»
#include "../../api/inc/fmod_errors.h"
#include "../../api/inc/fmod.h"
JNIEXPORT void JNICALL Java_com_example_playsound_MainActivity_play
(JNIEnv *env, jobject thiz, jstring mediaPath) {
// mediaPath буде містити шллях до файлу, який буде програватись (в java ми його передаємо void play(String path))
// Для того чтобы перевести со стринга java в массив символов пишем следующее
const char *_mediaPath = env->GetStringUTFChars(mediaPath, NULL);
// Создаем необходимые переменные
FMOD_SYSTEM *gSystem;
FMOD_SOUND *sound;
FMOD_CHANNEL *channel;
FMOD_RESULT result = FMOD_OK;
// Инициализируем среду fmod.
FMOD_System_Create(&gSystem);
result = FMOD_System_Init(gSystem, 32, FMOD_INIT_NORMAL, 0);
if (result != FMOD_OK) // если возникла ошибка, то выводим ее в LogCat
__android_log_print(ANDROID_LOG_ERROR, «fmod», «FMOD error! (%d) %s\n%s:%d», result, FMOD_ErrorString(result), __FILE__, __LINE__);
// Создаем песню и проигрываем
FMOD_System_CreateSound(gSystem, _mediaPath, FMOD_DEFAULT, 0, &sound);
FMOD_System_PlaySound(gSystem, FMOD_CHANNEL_FREE, sound, false, &channel);
}
Теперь осталось добавить в MainActivity загрузку библиотеки PlaySound
static {
System.loadLibrary(«fmodex»);
System.loadLibrary(«PlaySound»);
}
записать на устройство свою любимую песенку, получить путь к ней и вызвать play.
Песню я записал в корень карты памяти.
Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
File extStore = Environment.getExternalStorageDirectory();
play(extStore.getAbsoluteFile() + "/Capital-Cities-Safe-And-Sound(muzofon.com).mp3");
}
Проблема только в том, что нужно долго ждать чтобы песня проигралась (на моем дешевом китайском где-то около минуты ).