/*============================================================================== Object Panning Example Copyright (c), Firelight Technologies Pty, Ltd 2015-2025. This example demonstrates the FMOD object panner. The usage is completely transparent to the API, the only difference is how the event is authored in the FMOD Studio tool. To hear the difference between object panning and normal panning this example has two events (one configured with the normal panner, and one with the object panner). As they move around the listener you may toggle between panning method and two different sounds. Object panning requires compatible hardware such as a Dolby Atmos amplifier or a Playstation VR headset. For cases when the necessary hardware is not available FMOD will fallback to standard 3D panning. 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 int FMOD_Main() { bool isOnGround = false; bool useListenerAttenuationPosition = false; 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) ); // Attempt to initialize with a compatible object panning output FMOD_RESULT result = coreSystem->setOutput(FMOD_OUTPUTTYPE_AUDIO3D); if (result != FMOD_OK) { result = coreSystem->setOutput(FMOD_OUTPUTTYPE_WINSONIC); if (result == FMOD_OK) { ERRCHECK( coreSystem->setSoftwareFormat(0, FMOD_SPEAKERMODE_7POINT1POINT4, 0) ); } else { result = coreSystem->setOutput(FMOD_OUTPUTTYPE_PHASE); if (result == FMOD_OK) { ERRCHECK( coreSystem->setSoftwareFormat(0, FMOD_SPEAKERMODE_7POINT1POINT4, 0) ); } } } int numDrivers = 0; ERRCHECK( coreSystem->getNumDrivers(&numDrivers) ); if (numDrivers == 0) { ERRCHECK( coreSystem->setDSPBufferSize(512, 4) ); ERRCHECK( coreSystem->setOutput(FMOD_OUTPUTTYPE_AUTODETECT) ); } // Due to a bug in WinSonic on Windows, FMOD initialization may fail on some machines. // If you get the error "FMOD error 51 - Error initializing output device", try using // a different output type such as FMOD_OUTPUTTYPE_AUTODETECT ERRCHECK( system->initialize(1024, FMOD_STUDIO_INIT_NORMAL, FMOD_INIT_NORMAL, extraDriverData) ); // Load everything needed for playback FMOD::Studio::Bank *masterBank = NULL; FMOD::Studio::Bank *musicBank = NULL; FMOD::Studio::Bank *stringsBank = NULL; FMOD::Studio::EventDescription *spatializerDescription = NULL; FMOD::Studio::EventInstance *spatializerInstance = NULL; float spatializer; float radioFrequency; ERRCHECK( system->loadBankFile(Common_MediaPath("Master.bank"), FMOD_STUDIO_LOAD_BANK_NORMAL, &masterBank) ); ERRCHECK( system->loadBankFile(Common_MediaPath("Music.bank"), FMOD_STUDIO_LOAD_BANK_NORMAL, &musicBank) ); ERRCHECK( system->loadBankFile(Common_MediaPath("Master.strings.bank"), FMOD_STUDIO_LOAD_BANK_NORMAL, &stringsBank) ); ERRCHECK( system->getEvent("event:/Music/Radio Station", &spatializerDescription) ); ERRCHECK( spatializerDescription->createInstance(&spatializerInstance) ); ERRCHECK( spatializerInstance->start() ); do { Common_Update(); ERRCHECK(spatializerInstance->getParameterByName("Freq", NULL, &radioFrequency)); ERRCHECK(spatializerInstance->getParameterByName("Spatializer", NULL, &spatializer)); if (Common_BtnPress(BTN_ACTION1)) { if (radioFrequency == 3.00f) { ERRCHECK(spatializerInstance->setParameterByName("Freq", 0.00f)); } else { ERRCHECK(spatializerInstance->setParameterByName("Freq", (radioFrequency + 1.50f))); } } if (Common_BtnPress(BTN_ACTION2)) { if (spatializer == 1.00) { ERRCHECK(spatializerInstance->setParameterByName("Spatializer", 0.00f)); } else { ERRCHECK(spatializerInstance->setParameterByName("Spatializer", 1.00f)); } } if (Common_BtnPress(BTN_ACTION3)) { isOnGround = !isOnGround; } if (Common_BtnPress(BTN_ACTION4)) { useListenerAttenuationPosition = !useListenerAttenuationPosition; } FMOD_3D_ATTRIBUTES vec = { }; vec.forward.z = 1.0f; vec.up.y = 1.0f; static float t = 0; vec.position.x = sinf(t) * 3.0f; /* Rotate sound in a circle */ vec.position.z = cosf(t) * 3.0f; /* Rotate sound in a circle */ t += 0.03f; if (isOnGround) { vec.position.y = 0; /* At ground level */ } else { vec.position.y = 5.0f; /* Up high */ } ERRCHECK( spatializerInstance->set3DAttributes(&vec) ); FMOD_3D_ATTRIBUTES listener_vec = { }; listener_vec.forward.z = 1.0f; listener_vec.up.y = 1.0f; FMOD_VECTOR listener_attenuationPos = vec.position; listener_attenuationPos.z -= -10.0f; ERRCHECK( system->setListenerAttributes(0, &listener_vec, useListenerAttenuationPosition ? &listener_attenuationPos : nullptr) ); ERRCHECK( system->update() ); const char *radioString = (radioFrequency == 0.00f) ? "Rock" : (radioFrequency == 1.50f) ? "Lo-fi" : "Hip hop"; const char *spatialString = (spatializer == 0.00f) ? "Standard 3D Spatializer" : "Object Spatializer"; Common_Draw("=================================================="); Common_Draw("Object Panning Example."); Common_Draw("Copyright (c) Firelight Technologies 2015-2025."); Common_Draw("=================================================="); Common_Draw(""); Common_Draw("Playing %s with the %s.", radioString, spatialString); Common_Draw("Radio is %s.", isOnGround ? "on the ground" : "up in the air"); Common_Draw(""); Common_Draw("Press %s to switch stations.", Common_BtnStr(BTN_ACTION1)); Common_Draw("Press %s to switch spatializer.", Common_BtnStr(BTN_ACTION2)); Common_Draw("Press %s to elevate the event instance.", Common_BtnStr(BTN_ACTION3)); Common_Draw("Press %s to %s use of attenuation position.", Common_BtnStr(BTN_ACTION4), useListenerAttenuationPosition ? "disable" : "enable"); Common_Draw(""); Common_Draw("Press %s to quit", Common_BtnStr(BTN_QUIT)); Common_Sleep(50); } while (!Common_BtnPress(BTN_QUIT)); ERRCHECK( stringsBank->unload() ); ERRCHECK( musicBank->unload() ); ERRCHECK( system->release() ); Common_Close(); return 0; }