175 lines
6.5 KiB
C++
175 lines
6.5 KiB
C++
/*==============================================================================
|
|
Music Callback Example
|
|
Copyright (c), Firelight Technologies Pty, Ltd 2012-2025.
|
|
|
|
This example demonstrates beat and named marker callbacks when playing music.
|
|
|
|
### See Also ###
|
|
* Studio::EventInstance::setCallback
|
|
* FMOD_STUDIO_EVENT_CALLBACK_TIMELINE_MARKER
|
|
* FMOD_STUDIO_EVENT_CALLBACK_TIMELINE_BEAT
|
|
|
|
For information on using FMOD example code in your own programs, visit
|
|
https://www.fmod.com/legal
|
|
==============================================================================*/
|
|
#include "fmod_studio.hpp"
|
|
#include "fmod.hpp"
|
|
#include "common.h"
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
static const int MAX_ENTRIES = 6;
|
|
|
|
struct CallbackInfo
|
|
{
|
|
Common_Mutex mMutex;
|
|
std::vector<std::string> mEntries;
|
|
};
|
|
|
|
FMOD_RESULT F_CALL markerCallback(FMOD_STUDIO_EVENT_CALLBACK_TYPE type, FMOD_STUDIO_EVENTINSTANCE* event, void *parameters);
|
|
|
|
int FMOD_Main()
|
|
{
|
|
void *extraDriverData = NULL;
|
|
Common_Init(&extraDriverData);
|
|
|
|
FMOD::Studio::System* system = NULL;
|
|
ERRCHECK( FMOD::Studio::System::create(&system) );
|
|
|
|
// The example Studio project is authored for 5.1 sound, so set up the system output mode to match
|
|
FMOD::System* coreSystem = NULL;
|
|
ERRCHECK( system->getCoreSystem(&coreSystem) );
|
|
ERRCHECK( coreSystem->setSoftwareFormat(0, FMOD_SPEAKERMODE_5POINT1, 0) );
|
|
|
|
ERRCHECK( system->initialize(1024, FMOD_STUDIO_INIT_NORMAL, FMOD_INIT_NORMAL, extraDriverData) );
|
|
|
|
FMOD::Studio::Bank* masterBank = NULL;
|
|
ERRCHECK( system->loadBankFile(Common_MediaPath("Master.bank"), FMOD_STUDIO_LOAD_BANK_NORMAL, &masterBank) );
|
|
|
|
FMOD::Studio::Bank* stringsBank = NULL;
|
|
ERRCHECK( system->loadBankFile(Common_MediaPath("Master.strings.bank"), FMOD_STUDIO_LOAD_BANK_NORMAL, &stringsBank) );
|
|
|
|
FMOD::Studio::Bank* musicBank = NULL;
|
|
FMOD_RESULT result = system->loadBankFile(Common_MediaPath("Music.bank"), FMOD_STUDIO_LOAD_BANK_NORMAL, &musicBank);
|
|
if (result != FMOD_OK)
|
|
{
|
|
// Music bank is not exported by default, you will have to export from the tool first
|
|
Common_Fatal("Please export music.bank from the Studio tool to run this example");
|
|
}
|
|
|
|
FMOD::Studio::EventDescription* eventDescription = NULL;
|
|
ERRCHECK( system->getEvent("event:/Music/Level 01", &eventDescription) );
|
|
|
|
FMOD::Studio::EventInstance* eventInstance = NULL;
|
|
ERRCHECK( eventDescription->createInstance(&eventInstance) );
|
|
|
|
CallbackInfo info;
|
|
Common_Mutex_Create(&info.mMutex);
|
|
|
|
ERRCHECK( eventInstance->setUserData(&info) );
|
|
ERRCHECK( eventInstance->setCallback(markerCallback,
|
|
FMOD_STUDIO_EVENT_CALLBACK_TIMELINE_MARKER | FMOD_STUDIO_EVENT_CALLBACK_TIMELINE_BEAT |
|
|
FMOD_STUDIO_EVENT_CALLBACK_SOUND_PLAYED | FMOD_STUDIO_EVENT_CALLBACK_SOUND_STOPPED) );
|
|
ERRCHECK( eventInstance->start() );
|
|
|
|
FMOD_STUDIO_PARAMETER_DESCRIPTION parameterDescription;
|
|
ERRCHECK( eventDescription->getParameterDescriptionByName("Progression", ¶meterDescription) );
|
|
|
|
FMOD_STUDIO_PARAMETER_ID progressionID = parameterDescription.id;
|
|
|
|
float progression = 0.0f;
|
|
ERRCHECK(eventInstance->setParameterByID(progressionID, progression));
|
|
|
|
do
|
|
{
|
|
Common_Update();
|
|
|
|
if (Common_BtnPress(BTN_MORE))
|
|
{
|
|
progression = (progression == 0.0f ? 1.0f : 0.0f);
|
|
ERRCHECK(eventInstance->setParameterByID(progressionID, progression));
|
|
}
|
|
|
|
ERRCHECK( system->update() );
|
|
|
|
int position;
|
|
ERRCHECK( eventInstance->getTimelinePosition(&position) );
|
|
|
|
Common_Draw("==================================================");
|
|
Common_Draw("Music Callback Example.");
|
|
Common_Draw("Copyright (c) Firelight Technologies 2012-2025.");
|
|
Common_Draw("==================================================");
|
|
Common_Draw("");
|
|
Common_Draw("Timeline = %d", position);
|
|
Common_Draw("");
|
|
// Obtain lock and look at our strings
|
|
Common_Mutex_Enter(&info.mMutex);
|
|
for (size_t i=0; i<info.mEntries.size(); ++i)
|
|
{
|
|
Common_Draw(" %s\n", info.mEntries[i].c_str());
|
|
}
|
|
Common_Mutex_Leave(&info.mMutex);
|
|
Common_Draw("");
|
|
Common_Draw("Press %s to toggle progression (currently %g)", Common_BtnStr(BTN_MORE), progression);
|
|
Common_Draw("Press %s to quit", Common_BtnStr(BTN_QUIT));
|
|
|
|
Common_Sleep(50);
|
|
} while (!Common_BtnPress(BTN_QUIT));
|
|
|
|
ERRCHECK( system->release() );
|
|
|
|
Common_Mutex_Destroy(&info.mMutex);
|
|
Common_Close();
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Obtain a lock and add a string entry to our list
|
|
void markerAddString(CallbackInfo* info, const char* format, ...)
|
|
{
|
|
char buf[256];
|
|
va_list args;
|
|
va_start(args, format);
|
|
Common_vsnprintf(buf, 256, format, args);
|
|
va_end(args);
|
|
buf[255] = '\0';
|
|
Common_Mutex_Enter(&info->mMutex);
|
|
if (info->mEntries.size() >= MAX_ENTRIES)
|
|
{
|
|
info->mEntries.erase(info->mEntries.begin());
|
|
}
|
|
info->mEntries.push_back(std::string(buf));
|
|
Common_Mutex_Leave(&info->mMutex);
|
|
}
|
|
|
|
// Callback from Studio - Remember these callbacks will occur in the Studio update thread, NOT the game thread.
|
|
FMOD_RESULT F_CALL markerCallback(FMOD_STUDIO_EVENT_CALLBACK_TYPE type, FMOD_STUDIO_EVENTINSTANCE* event, void *parameters)
|
|
{
|
|
CallbackInfo* callbackInfo;
|
|
ERRCHECK(((FMOD::Studio::EventInstance*)event)->getUserData((void**)&callbackInfo));
|
|
|
|
if (type == FMOD_STUDIO_EVENT_CALLBACK_TIMELINE_MARKER)
|
|
{
|
|
FMOD_STUDIO_TIMELINE_MARKER_PROPERTIES* props = (FMOD_STUDIO_TIMELINE_MARKER_PROPERTIES*)parameters;
|
|
markerAddString(callbackInfo, "Named marker '%s'", props->name);
|
|
}
|
|
else if (type == FMOD_STUDIO_EVENT_CALLBACK_TIMELINE_BEAT)
|
|
{
|
|
FMOD_STUDIO_TIMELINE_BEAT_PROPERTIES* props = (FMOD_STUDIO_TIMELINE_BEAT_PROPERTIES*)parameters;
|
|
markerAddString(callbackInfo, "beat %d, bar %d (tempo %.1f %d:%d)", props->beat, props->bar, props->tempo, props->timesignatureupper, props->timesignaturelower);
|
|
}
|
|
if (type == FMOD_STUDIO_EVENT_CALLBACK_SOUND_PLAYED || type == FMOD_STUDIO_EVENT_CALLBACK_SOUND_STOPPED)
|
|
{
|
|
FMOD::Sound* sound = (FMOD::Sound*)parameters;
|
|
char name[256];
|
|
ERRCHECK(sound->getName(name, 256));
|
|
unsigned int len;
|
|
ERRCHECK(sound->getLength(&len, FMOD_TIMEUNIT_MS));
|
|
|
|
markerAddString(callbackInfo, "Sound '%s' (length %.3f) %s",
|
|
name, (float)len/1000.0f,
|
|
type == FMOD_STUDIO_EVENT_CALLBACK_SOUND_PLAYED ? "Started" : "Stopped");
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|