Updating this thread with the source code with the patch above applied
/*==============================================================================
3D Example
Copyright (c), Firelight Technologies Pty, Ltd 2004-2018.
This example shows how to basic 3D positioning of sounds.
==============================================================================*/
#include "fmod.hpp"
#include "common.h"
#include <initguid.h>
#include <mmdeviceapi.h>
const int INTERFACE_UPDATETIME = 50; // 50ms update for interface
const float DISTANCEFACTOR = 1.0f; // Units per meter. I.e feet would = 3.28. centimeters would = 100.
class ENotificationClient : public IMMNotificationClient
{
public:
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId);
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
{
UNREFERENCED_PARAMETER(pwstrDeviceId);
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
{
UNREFERENCED_PARAMETER(pwstrDeviceId);
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState)
{
UNREFERENCED_PARAMETER(pwstrDeviceId);
UNREFERENCED_PARAMETER(dwNewState);
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key)
{
UNREFERENCED_PARAMETER(pwstrDeviceId);
UNREFERENCED_PARAMETER(key);
return S_OK;
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface)
{
if (IID_IUnknown == riid)
{
*ppvInterface = (IUnknown*)this;
}
else if (__uuidof(IMMNotificationClient) == riid)
{
*ppvInterface = (IMMNotificationClient*)this;
}
else
{
*ppvInterface = nullptr;
return E_NOINTERFACE;
}
return S_OK;
}
ULONG STDMETHODCALLTYPE AddRef()
{
return 1;
}
ULONG STDMETHODCALLTYPE Release()
{
return 0;
}
};
#define EXIT_ON_ERROR(hres) \
if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk) \
if ((punk) != NULL) \
{ (punk)->Release(); (punk) = NULL; }
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
ENotificationClient NotificationClient;
CRITICAL_SECTION CriticalDevice;
bool DeviceAudioDefaultChanged = false;
FMOD_GUID DeviceAudioDefaultGUID = {};
HRESULT ENotificationClient::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
{
UNREFERENCED_PARAMETER(pwstrDeviceId);
if (flow != EDataFlow::eRender ||
role != ERole::eConsole)
{
return S_OK;
}
HRESULT hr = S_OK;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pEndpoint = NULL;
IPropertyStore *pProps = NULL;
LPWSTR pwszID = NULL;
hr = CoCreateInstance(
CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&pEnumerator);
EXIT_ON_ERROR(hr);
hr = pEnumerator->GetDefaultAudioEndpoint(flow, role, &pEndpoint);
EXIT_ON_ERROR(hr);
// Get the endpoint ID string.
hr = pEndpoint->GetId(&pwszID);
EXIT_ON_ERROR(hr);
hr = pEndpoint->OpenPropertyStore(
STGM_READ, &pProps);
EXIT_ON_ERROR(hr);
PROPVARIANT varGUID;
// Initialize container for property value.
PropVariantInit(&varGUID);
hr = pProps->GetValue(
PKEY_AudioEndpoint_GUID, &varGUID);
EXIT_ON_ERROR(hr);
CLSID deviceGuid;
CLSIDFromString(varGUID.pwszVal, &deviceGuid);
FMOD_GUID fmodDeviceID;
fmodDeviceID.Data1 = deviceGuid.Data1;
fmodDeviceID.Data2 = deviceGuid.Data2;
fmodDeviceID.Data3 = deviceGuid.Data3;
memcpy(fmodDeviceID.Data4, deviceGuid.Data4, sizeof(fmodDeviceID.Data4));
EnterCriticalSection(&CriticalDevice);
DeviceAudioDefaultChanged = true;
memcpy(&DeviceAudioDefaultGUID, &fmodDeviceID, sizeof(DeviceAudioDefaultGUID));
LeaveCriticalSection(&CriticalDevice);
CoTaskMemFree(pwszID);
pwszID = NULL;
PropVariantClear(&varGUID);
SAFE_RELEASE(pProps);
SAFE_RELEASE(pEndpoint);
SAFE_RELEASE(pEnumerator);
return S_OK;
Exit:
CoTaskMemFree(pwszID);
SAFE_RELEASE(pEnumerator);
SAFE_RELEASE(pEndpoint);
SAFE_RELEASE(pProps);
return S_OK;
}
int FMOD_Main()
{
FMOD::System *system;
FMOD::Sound *sound1, *sound2, *sound3;
FMOD::Channel *channel1 = 0, *channel2 = 0, *channel3 = 0;
FMOD_RESULT result;
bool listenerflag = true;
FMOD_VECTOR listenerpos = { 0.0f, 0.0f, -1.0f * DISTANCEFACTOR };
unsigned int version;
void *extradriverdata = 0;
InitializeCriticalSection(&CriticalDevice);
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
{
return 0;
}
IMMDeviceEnumerator *Enumerator = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (void**)&Enumerator);
if (SUCCEEDED(hr))
{
Enumerator->RegisterEndpointNotificationCallback(&NotificationClient);
}
Common_Init(&extradriverdata);
/*
Create a System object and initialize.
*/
result = FMOD::System_Create(&system);
ERRCHECK(result);
result = system->getVersion(&version);
ERRCHECK(result);
if (version < FMOD_VERSION)
{
Common_Fatal("FMOD lib version %08x doesn't match header version %08x", version, FMOD_VERSION);
}
result = system->init(100, FMOD_INIT_NORMAL, extradriverdata);
ERRCHECK(result);
/*
Set the distance units. (meters/feet etc).
*/
result = system->set3DSettings(1.0, DISTANCEFACTOR, 1.0f);
ERRCHECK(result);
/*
Load some sounds
*/
result = system->createSound(Common_MediaPath("drumloop.wav"), FMOD_3D, 0, &sound1);
ERRCHECK(result);
result = sound1->set3DMinMaxDistance(0.5f * DISTANCEFACTOR, 5000.0f * DISTANCEFACTOR);
ERRCHECK(result);
result = sound1->setMode(FMOD_LOOP_NORMAL);
ERRCHECK(result);
result = system->createSound(Common_MediaPath("jaguar.wav"), FMOD_3D, 0, &sound2);
ERRCHECK(result);
result = sound2->set3DMinMaxDistance(0.5f * DISTANCEFACTOR, 5000.0f * DISTANCEFACTOR);
ERRCHECK(result);
result = sound2->setMode(FMOD_LOOP_NORMAL);
ERRCHECK(result);
result = system->createSound(Common_MediaPath("swish.wav"), FMOD_2D, 0, &sound3);
ERRCHECK(result);
/*
Play sounds at certain positions
*/
{
FMOD_VECTOR pos = { -10.0f * DISTANCEFACTOR, 0.0f, 0.0f };
FMOD_VECTOR vel = { 0.0f, 0.0f, 0.0f };
result = system->playSound(sound1, 0, true, &channel1);
ERRCHECK(result);
result = channel1->set3DAttributes(&pos, &vel);
ERRCHECK(result);
result = channel1->setPaused(false);
ERRCHECK(result);
}
{
FMOD_VECTOR pos = { 15.0f * DISTANCEFACTOR, 0.0f, 0.0f };
FMOD_VECTOR vel = { 0.0f, 0.0f, 0.0f };
result = system->playSound(sound2, 0, true, &channel2);
ERRCHECK(result);
result = channel2->set3DAttributes(&pos, &vel);
ERRCHECK(result);
result = channel2->setPaused(false);
ERRCHECK(result);
}
/*
Main loop
*/
do
{
EnterCriticalSection(&CriticalDevice);
if (DeviceAudioDefaultChanged == true)
{
FMOD_OUTPUTTYPE output;
result = system->getOutput(&output);
ERRCHECK(result);
result = system->setOutput(FMOD_OUTPUTTYPE_NOSOUND);
ERRCHECK(result);
result = system->setOutput(output);
ERRCHECK(result);
int numDrivers = 0;
if (system->getNumDrivers(&numDrivers) == FMOD_OK)
{
for (int n = 0; n < numDrivers; ++n)
{
char name[256] = {};
FMOD_GUID guid = {};
int systemrate = 0;
FMOD_SPEAKERMODE speakermode = FMOD_SPEAKERMODE_DEFAULT;
int speakermodechannels = 0;
if (system->getDriverInfo(n, name, sizeof(name), &guid, &systemrate, &speakermode, &speakermodechannels) == FMOD_OK)
{
if (memcmp(&DeviceAudioDefaultGUID, &guid, sizeof(FMOD_GUID)) == 0)
{
system->setDriver(n);
}
}
}
}
DeviceAudioDefaultChanged = false;
}
LeaveCriticalSection(&CriticalDevice);
Common_Update();
if (Common_BtnPress(BTN_ACTION1))
{
bool paused;
channel1->getPaused(&paused);
channel1->setPaused(!paused);
}
if (Common_BtnPress(BTN_ACTION2))
{
bool paused;
channel2->getPaused(&paused);
channel2->setPaused(!paused);
}
if (Common_BtnPress(BTN_ACTION3))
{
result = system->playSound(sound3, 0, false, &channel3);
ERRCHECK(result);
}
if (Common_BtnPress(BTN_MORE))
{
listenerflag = !listenerflag;
}
if (!listenerflag)
{
if (Common_BtnDown(BTN_LEFT))
{
listenerpos.x -= 1.0f * DISTANCEFACTOR;
if (listenerpos.x < -24 * DISTANCEFACTOR)
{
listenerpos.x = -24 * DISTANCEFACTOR;
}
}
if (Common_BtnDown(BTN_RIGHT))
{
listenerpos.x += 1.0f * DISTANCEFACTOR;
if (listenerpos.x > 23 * DISTANCEFACTOR)
{
listenerpos.x = 23 * DISTANCEFACTOR;
}
}
}
// ==========================================================================================
// UPDATE THE LISTENER
// ==========================================================================================
{
static float t = 0;
static FMOD_VECTOR lastpos = { 0.0f, 0.0f, 0.0f };
FMOD_VECTOR forward = { 0.0f, 0.0f, 1.0f };
FMOD_VECTOR up = { 0.0f, 1.0f, 0.0f };
FMOD_VECTOR vel;
if (listenerflag)
{
listenerpos.x = (float)sin(t * 0.05f) * 24.0f * DISTANCEFACTOR; // left right pingpong
}
// ********* NOTE ******* READ NEXT COMMENT!!!!!
// vel = how far we moved last FRAME (m/f), then time compensate it to SECONDS (m/s).
vel.x = (listenerpos.x - lastpos.x) * (1000 / INTERFACE_UPDATETIME);
vel.y = (listenerpos.y - lastpos.y) * (1000 / INTERFACE_UPDATETIME);
vel.z = (listenerpos.z - lastpos.z) * (1000 / INTERFACE_UPDATETIME);
// store pos for next time
lastpos = listenerpos;
result = system->set3DListenerAttributes(0, &listenerpos, &vel, &forward, &up);
ERRCHECK(result);
t += (30 * (1.0f / (float)INTERFACE_UPDATETIME)); // t is just a time value .. it increments in 30m/s steps in this example
}
result = system->update();
ERRCHECK(result);
// Create small visual display.
char s[80] = "|.............<1>......................<2>.......|";
s[(int)(listenerpos.x / DISTANCEFACTOR) + 25] = 'L';
Common_Draw("==================================================");
Common_Draw("3D Example.");
Common_Draw("Copyright (c) Firelight Technologies 2004-2018.");
Common_Draw("==================================================");
Common_Draw("");
Common_Draw("Press %s to toggle sound 1 (16bit Mono 3D)", Common_BtnStr(BTN_ACTION1));
Common_Draw("Press %s to toggle sound 2 (8bit Mono 3D)", Common_BtnStr(BTN_ACTION2));
Common_Draw("Press %s to play a sound (16bit Stereo 2D)", Common_BtnStr(BTN_ACTION3));
Common_Draw("Press %s or %s to move listener in still mode", Common_BtnStr(BTN_LEFT), Common_BtnStr(BTN_RIGHT));
Common_Draw("Press %s to toggle listener auto movement", Common_BtnStr(BTN_MORE));
Common_Draw("Press %s to quit", Common_BtnStr(BTN_QUIT));
Common_Draw("");
Common_Draw(s);
Common_Sleep(INTERFACE_UPDATETIME - 1);
} while (!Common_BtnPress(BTN_QUIT));
/*
Shut down
*/
result = sound1->release();
ERRCHECK(result);
result = sound2->release();
ERRCHECK(result);
result = sound3->release();
ERRCHECK(result);
result = system->close();
ERRCHECK(result);
result = system->release();
ERRCHECK(result);
Common_Close();
if (Enumerator)
{
Enumerator->UnregisterEndpointNotificationCallback(&NotificationClient);
Enumerator->Release();
Enumerator = nullptr;
}
CoUninitialize();
return 0;
}