FMOD_INIT_CHANNEL_LOWPASS not working?

Hi,

I am using FMOD 1.08.15 and I am experimenting with the FMOD_INIT_CHANNEL_LOWPASS flag to get 3D occlusion and the “fake HRTF style” effect described in the documentation. However, no matter what values I pass to set3DOcclusion and the value of the HRTF settings in setAdvancedSettings, I can’t hear any differences. Is the feature actually working?

Hi Samuele,
It definitely works. I just made this modification to the 3d example in the fmod examples.

See if it works for you

/*==============================================================================
3D Example
Copyright (c), Firelight Technologies Pty, Ltd 2004-2017.

This example shows how to basic 3D positioning of sounds.
==============================================================================*/
#include "fmod.hpp"
#include "common.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.

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;

    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 | FMOD_INIT_PROFILE_METER_ALL | FMOD_INIT_CHANNEL_LOWPASS, 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->set3DOcclusion(0.90f, 0.90f);
        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->set3DOcclusion(0.90f, 0.90f);
        ERRCHECK(result);
        result = channel2->setPaused(false);
        ERRCHECK(result);
    }

    /*
        Main loop
    */
    do
    {
        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-2017.");
        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();

    return 0;
}

Notice how I added FMOD_INIT_PROFILE_METER_ALL

This is good for checking if the lowpass is added into the signal. Connect FMOD Profiler.exe to it and see the graph in realtime.

I am using FMODStudio, so instead of getting a channel from playSound, I use getChannelGroup on the EventInstance. Calling set3DOcclusion either to the channel group or on the channels of said group (or subgroups) has no effect whatsoever. Could it be that this use case is not supported?

About FMOD_INIT_PROFILE_METER_ALL, how is that supposed to work? I added it but I don’t see any additional data or graph in the profiler that can confirm or deny that the lowpass is present.

Found out something. I was using the FMODStudio profiler, but I get that you were talking about the FMOD profiler instead. Within the profiler I see the “FMOD Lowpass Simple” blocks introduced by FMOD_INIT_CHANNEL_LOWPASS (they are not present if I remove the flag). However all those blocks are gray (bypass is true) and that would explain why I don’t hear their effect. The question is: why are they bypassed?

Hi Samuele,
The FMOD_INIT_CHANNEL_LOWPASS is meant for the low level API only. FMOD Studio uses channels, but only as the things you see in a sound/instrument panel. The effect deck is based on a channelgroup, and is a higher level concept. Distance filtering is done through the 3d panner (depends if it is a filtering panner like the oculus vr plugin or google vr plugin), or you could add your own lowpass simple and automate it with a parameter if you like.

1 Like