Skip to content

Instantly share code, notes, and snippets.

Created May 2, 2014 05:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/4d62c4c4147f30be470d to your computer and use it in GitHub Desktop.
Save anonymous/4d62c4c4147f30be470d to your computer and use it in GitHub Desktop.
/*==============================================================================
Event Parameter Example
Copyright (c), Firelight Technologies Pty, Ltd 2012-2014.
This example demonstrates how to control event playback using game parameters.
==============================================================================*/
#include "fmod_studio.hpp"
#include "fmod.hpp"
#include "common.h"
#include "fmod_errors.h"
////////////////////////////////////////////////////////////////////////////////
// if defined, load from banks from memory
#define BANK_MEMORY
//! by NOT having async loading we get the strings loaded immediately and we will not fail on lookup
//! in the 'create_engine_sound' function
#define BANK_MEMORY_ASYNC_LOADING
////////////////////////////////////////////////////////////////////////////////
#define LOG_OUT(s) OutputDebugStringA((s))
#define VSNPRINTF(s, n, f, v) _vsnprintf_s((s), (n), _TRUNCATE, (f), (v))
#define LOG_TEMP_BUFFER_SIZE (32*1024)
void log_message_format(const char *msg_format, ...)
{
char buffer[LOG_TEMP_BUFFER_SIZE];
va_list args;
va_start(args, msg_format);
VSNPRINTF(buffer, LOG_TEMP_BUFFER_SIZE-1, msg_format, args);
LOG_OUT(buffer);
va_end(args);
}
#define FLOG(s, ...) log_message_format(s "\n", ##__VA_ARGS__)
typedef unsigned __int64 tick_t;
typedef float deltatime_t;
static tick_t _time_freq = 0;
static double _time_oofreq = 0;
static tick_t _time_startup = 0;
tick_t time_current(void)
{
tick_t curclock;
QueryPerformanceCounter( (LARGE_INTEGER*)&curclock );
return curclock;
}
int time_init(void)
{
tick_t unused;
if( !QueryPerformanceFrequency( (LARGE_INTEGER*)&_time_freq ) ||
!QueryPerformanceCounter( (LARGE_INTEGER*)&unused ) )
return -1;
_time_oofreq = 1.0 / (double)_time_freq;
_time_startup = time_current();
return 0;
}
tick_t time_startup(void) { return _time_startup; }
tick_t time_ticks_per_second(void) { return _time_freq; }
tick_t time_seconds_to_ticks(const deltatime_t s) { return time_ticks_per_second() * (tick_t)s; }
tick_t time_diff(const tick_t from, const tick_t to)
{
if(to <= from)
return 0;
return (to - from);
}
tick_t time_elapsed_ticks(const tick_t t)
{
tick_t dt, curclock = t;
QueryPerformanceCounter( (LARGE_INTEGER*)&curclock );
dt = curclock - t;
return dt;
}
deltatime_t time_seconds_elapsed(const tick_t t) { return (deltatime_t)( (double)time_elapsed_ticks(t) * _time_oofreq); }
deltatime_t time_ticks_to_seconds(const tick_t dt) { return (deltatime_t)( (double)dt * _time_oofreq ); }
#define XSTRINGIFY_FMOD(s) STRINGIFY_FMOD( (s) )
#define STRINGIFY_FMOD(s) #s
static const char *instance_type_to_string(FMOD_ERRORCALLBACK_INSTANCETYPE t)
{
#define MAKE_CASE(a) case (a) : return STRINGIFY_FMOD(a);
switch (t)
{
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_NONE)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_SYSTEM)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_CHANNEL)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_CHANNELGROUP)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_CHANNELCONTROL)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_SOUND)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_SOUNDGROUP)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_DSP)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_DSPCONNECTION)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_GEOMETRY)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_REVERB3D)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_SYSTEM)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_EVENTDESCRIPTION)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_EVENTINSTANCE)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_PARAMETERINSTANCE)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_CUEINSTANCE)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_MIXERSTRIP)
MAKE_CASE(FMOD_ERRORCALLBACK_INSTANCETYPE_STUDIO_BANK)
default : return "UNKNOWN INSTANCE TYPE";
}
#undef MAKE_CASE
}
static FMOD_RESULT F_CALLBACK FMOD_SYSTEM_CALLBACK_SELF(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACK_TYPE type, void *commanddata1, void *commanddata2, void *userdata)
{
(void)userdata;
(void)commanddata2;
(void)system;
switch (type)
{
/* Called from System::update when the enumerated list of devices has changed. */
case FMOD_SYSTEM_CALLBACK_DEVICELISTCHANGED :
FLOG("%s", STRINGIFY_FMOD(FMOD_SYSTEM_CALLBACK_DEVICELISTCHANGED));
break;
/* Called from System::update when an output device has been lost due to control panel parameter changes and FMOD cannot automatically recover. */
case FMOD_SYSTEM_CALLBACK_DEVICELOST :
FLOG("%s", STRINGIFY_FMOD( FMOD_SYSTEM_CALLBACK_DEVICELOST ));
break;
/* Called directly when a memory allocation fails somewhere in FMOD. (NOTE - 'system' will be NULL in this callback type.)*/
case FMOD_SYSTEM_CALLBACK_MEMORYALLOCATIONFAILED :
FLOG("%s", STRINGIFY_FMOD( FMOD_SYSTEM_CALLBACK_MEMORYALLOCATIONFAILED ));
break;
/* Called directly when a thread is created. (NOTE - 'system' will be NULL in this callback type.) */
case FMOD_SYSTEM_CALLBACK_THREADCREATED :
FLOG("%s. Thread ID [%d], name [%s]", STRINGIFY_FMOD( FMOD_SYSTEM_CALLBACK_THREADCREATED ), (int)commanddata1, (char*)commanddata2);
break;
/* Called when a bad connection was made with DSP::addInput. Usually called from mixer thread because that is where the connections are made. */
case FMOD_SYSTEM_CALLBACK_BADDSPCONNECTION :
FLOG("%s", STRINGIFY_FMOD( FMOD_SYSTEM_CALLBACK_BADDSPCONNECTION));
break;
/* Called each tick before a mix update happens. */
case FMOD_SYSTEM_CALLBACK_PREMIX :
FLOG("%s", STRINGIFY_FMOD( FMOD_SYSTEM_CALLBACK_PREMIX));
break;
/* Called each tick after a mix update happens. */
case FMOD_SYSTEM_CALLBACK_POSTMIX :
FLOG("%s", STRINGIFY_FMOD( FMOD_SYSTEM_CALLBACK_POSTMIX ));
break;
case FMOD_SYSTEM_CALLBACK_ERROR :
{
FMOD_ERRORCALLBACK_INFO *error_info = (FMOD_ERRORCALLBACK_INFO*)commanddata1;
FMOD_RESULT fmod_error = error_info->result;
FLOG( "FMOD ERROR [%i] - %s. [%s 0x%p] %s(%s)",
fmod_error, FMOD_ErrorString(fmod_error),
instance_type_to_string(error_info->instancetype), error_info->instance,
error_info->functionname, error_info->functionparams);
fmod_error = fmod_error;
}
break;
}
return FMOD_OK;
}
#if defined(BANK_MEMORY)
static void read_file_aligned32(const char *name, void **buff, int *length)
{
FILE *file = NULL;
fopen_s(&file, name, "rb");
fseek(file, 0, SEEK_END);
long len = ftell(file);
fseek(file, 0, SEEK_SET);
void *mem = _aligned_malloc(len, 32);
fread(mem, 1, len, file);
fclose(file);
*buff = mem;
*length = len;
}
#endif
static int num_checks = 0;
static int create_engine_sound(FMOD::Studio::System *system, FMOD::Studio::EventInstance **eventInstance)
{
++num_checks;
FMOD::Studio::ID eventID = {0};
if (FMOD_OK != system->lookupID("event:/Vehicles/Basic Engine", &eventID))
return 0;
FMOD::Studio::EventDescription* eventDescription = NULL;
if (FMOD_OK != system->getEvent(&eventID, FMOD_STUDIO_LOAD_BEGIN_NOW, &eventDescription) )
return 0;
ERRCHECK( eventDescription->createInstance(eventInstance) );
FMOD::Studio::ParameterInstance* surfaceParameter = NULL;
ERRCHECK( (*eventInstance)->getParameter("RPM", &surfaceParameter) );
ERRCHECK( surfaceParameter->setValue(500.0f) );
float surfaceParameterValue = 0;
ERRCHECK( surfaceParameter->getValue(&surfaceParameterValue) );
ERRCHECK( (*eventInstance)->start() );
FLOG("ENGINE SOUND CREATED %i! %p", num_checks, *eventInstance);
return 1;
}
int FMOD_Main()
{
tick_t start_time, start_time_bank_sampledata_load;
deltatime_t delta_time_elapsed;
int sample_data_loaded;
FMOD_STUDIO_LOADING_STATE loading_state;
void *extraDriverData = NULL;
#if defined (BANK_MEMORY)
FLOG("BANKS FROM MEMORY");
FMOD_STUDIO_LOAD_MEMORY_MODE load_bank_memory_mode = FMOD_STUDIO_LOAD_MEMORY_POINT;
#if defined(BANK_MEMORY_ASYNC_LOADING)
FMOD_STUDIO_LOAD_BANK_FLAGS load_bank_flags = FMOD_STUDIO_LOAD_BANK_NONBLOCKING;
#else
FMOD_STUDIO_LOAD_BANK_FLAGS load_bank_flags = FMOD_STUDIO_LOAD_BANK_NORMAL;
#endif
void *mem_master_bank, *mem_master_bank_strings, *mem_vehicles;
int mem_size_master_bank, mem_size_master_bank_strings, mem_size_vehicles;
read_file_aligned32(Common_MediaPath("Master Bank.bank"), &mem_master_bank, &mem_size_master_bank);
read_file_aligned32(Common_MediaPath("Master Bank.strings.bank"), &mem_master_bank_strings, &mem_size_master_bank_strings);
read_file_aligned32(Common_MediaPath("Vehicles.bank"), &mem_vehicles, &mem_size_vehicles);
#else
FLOG("NOT USING BANKS FROM MEMORY");
#endif
Common_Init(&extraDriverData);
if (-1 == time_init())
Common_Fatal("failed to init time");
FMOD::Studio::System* system = NULL;
ERRCHECK( FMOD::Studio::System::create(&system) );
start_time = time_current();
ERRCHECK( system->initialize(32, FMOD_STUDIO_INIT_NORMAL, FMOD_INIT_NORMAL, extraDriverData) );
delta_time_elapsed = time_ticks_to_seconds( time_diff(start_time, time_current()) ), start_time = time_current();
FLOG("System initialize %f s", delta_time_elapsed);
FMOD::System *lls = 0;
ERRCHECK ( system->getLowLevelSystem(&lls) );
FMOD_SYSTEM_CALLBACK_TYPE T =
FMOD_SYSTEM_CALLBACK_DEVICELISTCHANGED | /* Called from System::update when the enumerated list of devices has changed. */
FMOD_SYSTEM_CALLBACK_DEVICELOST | /* Called from System::update when an output device has been lost due to control panel parameter changes and FMOD cannot automatically recover. */
FMOD_SYSTEM_CALLBACK_MEMORYALLOCATIONFAILED | /* Called directly when a memory allocation fails somewhere in FMOD. (NOTE - 'system' will be NULL in this callback type.)*/
FMOD_SYSTEM_CALLBACK_THREADCREATED | /* Called directly when a thread is created. (NOTE - 'system' will be NULL in this callback type.) */
FMOD_SYSTEM_CALLBACK_BADDSPCONNECTION | /* Called when a bad connection was made with DSP::addInput. Usually called from mixer thread because that is where the connections are made. */
//FMOD_SYSTEM_CALLBACK_PREMIX | /* Called each tick before a mix update happens. */
//FMOD_SYSTEM_CALLBACK_POSTMIX | /* Called each tick after a mix update happens. */
FMOD_SYSTEM_CALLBACK_ERROR ;
lls->setCallback(FMOD_SYSTEM_CALLBACK_SELF, T);
FMOD::Studio::Bank* masterBank = NULL;
#if defined (BANK_MEMORY)
ERRCHECK( system->loadBankMemory((const char*)mem_master_bank, mem_size_master_bank, load_bank_memory_mode, load_bank_flags, &masterBank) );
#else
ERRCHECK( system->loadBankFile(Common_MediaPath("Master Bank.bank"), FMOD_STUDIO_LOAD_BANK_NORMAL, &masterBank) );
#endif
delta_time_elapsed = time_ticks_to_seconds( time_diff(start_time, time_current()) ), start_time = time_current();
FLOG("Load bank %s. %f s", "Master Bank.bank", delta_time_elapsed);
FMOD::Studio::Bank* stringsBank = NULL;
#if defined (BANK_MEMORY)
ERRCHECK( system->loadBankMemory((const char*)mem_master_bank_strings, mem_size_master_bank_strings, load_bank_memory_mode, load_bank_flags, &stringsBank) );
#else
ERRCHECK( system->loadBankFile(Common_MediaPath("Master Bank.strings.bank"), FMOD_STUDIO_LOAD_BANK_NORMAL, &stringsBank) );
#endif
delta_time_elapsed = time_ticks_to_seconds( time_diff(start_time, time_current()) ), start_time = time_current();
FLOG("Load bank %s. %f s", "Master Bank.strings.bank", delta_time_elapsed);
FMOD::Studio::Bank* vehiclesBank = NULL;
#if defined (BANK_MEMORY)
ERRCHECK( system->loadBankMemory((const char*)mem_vehicles, mem_size_vehicles, load_bank_memory_mode, load_bank_flags, &vehiclesBank) );
#else
ERRCHECK( system->loadBankFile(Common_MediaPath("Vehicles.bank"), FMOD_STUDIO_LOAD_BANK_NORMAL, &vehiclesBank) );
#endif
delta_time_elapsed = time_ticks_to_seconds( time_diff(start_time, time_current()) ), start_time = time_current();
FLOG("Load bank %s. %f s", "Vehicles.bank", delta_time_elapsed);
sample_data_loaded = 0;
ERRCHECK( vehiclesBank->loadSampleData() );
start_time_bank_sampledata_load = time_current();
ERRCHECK( vehiclesBank->getSampleLoadingState(&loading_state) );
if (loading_state == FMOD_STUDIO_LOADING_STATE_LOADED) {
sample_data_loaded = 1;
delta_time_elapsed = time_ticks_to_seconds( time_diff(start_time_bank_sampledata_load, time_current()) );
FLOG("Load bank sample data(first). %f s", delta_time_elapsed);
}
FMOD::Studio::EventInstance* eventInstance = NULL;
#if 0
FMOD::Studio::ID eventID = {0};
//ERRCHECK( system->lookupID("event:/Character/Footsteps/Footsteps", &eventID) );
ERRCHECK( system->lookupID("event:/Vehicles/Basic Engine", &eventID) );
FMOD::Studio::EventDescription* eventDescription = NULL;
ERRCHECK( system->getEvent(&eventID, FMOD_STUDIO_LOAD_BEGIN_NOW, &eventDescription) );
ERRCHECK( eventDescription->createInstance(&eventInstance) );
FMOD::Studio::ParameterInstance* surfaceParameter = NULL;
//ERRCHECK( eventInstance->getParameter("Surface", &surfaceParameter) );
ERRCHECK( eventInstance->getParameter("RPM", &surfaceParameter) );
// Make the event audible to start with
ERRCHECK( surfaceParameter->setValue(500.0f) );
float surfaceParameterValue = 0;
ERRCHECK( surfaceParameter->getValue(&surfaceParameterValue) );
ERRCHECK( eventInstance->start() );
#endif
FLOG("STARTING MAIN LOOP");
ERRCHECK( system->update() );
unsigned loop_count = 0;
do
{
float surfaceParameterValue = -1.0f;
Common_Update();
if (eventInstance == NULL)
create_engine_sound(system, &eventInstance);
if (eventInstance) {
FMOD::Studio::ParameterInstance* surfaceParameter = NULL;
ERRCHECK( eventInstance->getParameter("RPM", &surfaceParameter) );
ERRCHECK( surfaceParameter->getValue(&surfaceParameterValue) );
}
if (eventInstance && Common_BtnPress(BTN_ACTION1))
{
FMOD::Studio::ParameterInstance* surfaceParameter = NULL;
ERRCHECK( eventInstance->getParameter("RPM", &surfaceParameter) );
ERRCHECK( surfaceParameter->getValue(&surfaceParameterValue) );
surfaceParameterValue -= 100.0f;
ERRCHECK( surfaceParameter->setValue(surfaceParameterValue) );
}
if (eventInstance && Common_BtnPress(BTN_ACTION2))
{
FMOD::Studio::ParameterInstance* surfaceParameter = NULL;
ERRCHECK( eventInstance->getParameter("RPM", &surfaceParameter) );
ERRCHECK( surfaceParameter->getValue(&surfaceParameterValue) );
surfaceParameterValue += 100.0f;
ERRCHECK( surfaceParameter->setValue(surfaceParameterValue) );
}
ERRCHECK( system->update() );
FMOD_STUDIO_BUFFER_USAGE buffer_usage;
ERRCHECK( system->getBufferUsage(&buffer_usage) );
if (buffer_usage.studioCommandQueue.stallCount > 0) {
int break_here = 1;
break_here = 0;
}
if (buffer_usage.studioHandle.stallCount > 0) {
int break_here = 1;
break_here = 0;
}
if (sample_data_loaded == 0) {
ERRCHECK( vehiclesBank->getSampleLoadingState(&loading_state) );
if (loading_state == FMOD_STUDIO_LOADING_STATE_LOADED) {
sample_data_loaded = 1;
delta_time_elapsed = time_ticks_to_seconds( time_diff(start_time_bank_sampledata_load, time_current()) );
FLOG("Load bank sample data @loopcount[%u]. %f s", loop_count, delta_time_elapsed);
}
}
Common_Draw("==================================================");
Common_Draw("Event Parameter Example.");
Common_Draw("Copyright (c) Firelight Technologies 2014-2014.");
Common_Draw("==================================================");
Common_Draw("");
Common_Draw("Surface Parameter = %1.1f", surfaceParameterValue);
Common_Draw("");
Common_Draw("Surface Parameter:");
Common_Draw("Press %s to decrease value", Common_BtnStr(BTN_ACTION1));
Common_Draw("Press %s to increase value", Common_BtnStr(BTN_ACTION2));
Common_Draw("");
Common_Draw("Press %s to quit", Common_BtnStr(BTN_QUIT));
++loop_count;
Common_Sleep(50);
} while (!Common_BtnPress(BTN_QUIT));
ERRCHECK( system->release() );
Common_Close();
#if defined (BANK_MEMORY)
_aligned_free(mem_master_bank);
_aligned_free(mem_master_bank_strings);
_aligned_free(mem_vehicles);
#endif
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment