Understanding the effect of reverbocclusion

Hello! I’m trying to set up a scene in a cave where there is a boulder separating a sound source and the player.

I have a reverb setup with preset “CAVE” following the player position and I’ve used geo and 3dOcclusion to make geo around the boulder with directocclusion 1.0f and reverbocclusion 0.0f. The reverb is working great in the non-occluded case, but is not doing what I want in the occluded case.

When the boulder is between the player and the sound source I expected to hear the direct path sound being dampened, while the reverb effects still having the same volume as in the non-occluded case; i.e you would still clearly hear the echo of the sound. But this is not what I’m hearing, almost 100% of the audible sound is gone. Is this expected? Can I do something to make the reverb audible in this case?

I’m using channel groups if that matters, and the channel group has wetness 1.0f.

Hi,

I’ve been able to reproduce the issue, but unfortunately haven’t been able to find a workaround. Nonetheless, thanks for the report - it does appear to be a bug, so I’ve passed it along to the development team for further investigation.

Thank you Louis,

That’s good news! Meanwhile I’ve been trying to narrow the problem down, and what I notice is that I can only get reverb to work on the ChannelGroup, and not on the Channels themselves. The documentation (https://www.fmod.com/docs/2.02/api/core-api-channelcontrol.html#channelcontrol_setreverbproperties) hints at the fact that the ChannelGroup reverb is applied after mixing, would that perhaps mean after volume adjustment from occlusion?

Perhaps my real problem is that the channel reverb isn’t working? I’ve been trying with both standard reverb and 3d reverb, but for some reason I can’t get it to apply to the channel :thonk:. Any ideas what could be wrong? And what did your repro find? Do you have some workaround? Thanks

After looking at this some more I found some more clues. I did have channel reverb, but the effect of the reverb it is much less if I set it on the channel than if I set it on the channel group. I could hear the effect if I listened carefully. I also verified that the behavior is identical in the geo case and when you set 3d occlusion manually. So what I think I’ve narrowed it down to is:

  1. channelGroup->setReverbProperties(0, 1.0f) has a much stronger effect than channel->setReverbProperties(0, 1.0f) for some reason.
  2. When you call channel->set3DOcclusion(directocclusion, reverbocclusion) or channelGroup->set3DOcclusion(directocclusion, reverbocclusion), it is as if the directocclusion volume adjustment is applied after reverbocclusion, as I mentioned above.

You are correct. Geometry occlusion is handled in series - the reverb send comes after the fader (or filters, if you’re using FMOD_INIT_CHANNEL_LOWPASS or FMOD_INIT_CHANNEL_DISTANCEFILTER) that is used to occlude the direct signal. If your sound source is already directly occluded, then the reverb send will recieve no signal. There is no simple workaround for this - while it is possible to move the reverb send to before the fader to avoid the direct occlusion issue, your reverb signal will no longer be distance-attenuated, and therefore audio from all sources connected to the reverb will be audible when the reverb is unoccluded, no matter where the listener is.

This is difficult to diagnose without more information. If possible, could you provide a screenshot of your DSP chains in FMOD Core API Profiler tool (found in the /bin directory of the API distribution) by connecting the Profiler to your scene?

Hi Louis,

I see. Would you consider this as being by design? I’m thinking of workarounds right now. Could one perhaps do something like playing each sound twice; one channel with the direct sound and one channel with only the reverb, and attenuate these two channels separately? Or is there some other workaround?

Thanks for the profiler tip! I think that will help my general understanding of the overall setup too. I will look into it and try go get back to you.

It is by design, yes. I believe the occlusion DSP chain used to run parallel signal chains to support separate occlusion of reverb, but was changed to run in series some time ago in the transition to FMOD Studio.

This is possible, but it’ll require you to manually remove the DSP units that are being used to occlude the signal on the dedicated reverb channel (i.e. the channel fader or filters) and reconnect the Reverb DSP as a normal connection instead of a send.

From my testing everything seems to work as you want it to, but depending on how you’re handling your reverb, and whether you have multiple sounds running into that reverb, and you may run into more problems that require further manual changes to DSP connections.

Your DSP chain structure with this workaround, for a “single” audio source, might look something like this:

This example is using FMOD_INIT_CHANNEL_DISTANCEFILTER, and the resulting LP and HP filters have been removed from the topmost channel in the graph, which is the dedicated reverb channel.

Hi Louis,

I see, thank you. Is this something you would consider for a feature request? I found one other possible mention of the problem on the forum, not sure if that matters or not. But I report my interest if it’s possible to request :smiley:

That sounds very promising. Thank you very much for putting in the time to suggest this and testing it out! I take it you can use the core APIs to construct such a chain, i.e basically following the structure of the white paper? And then I can use the profiler to observe the result? I will give it a shot. From the image it looks like you can have multiple WaveTable Units in the same graph, does that mean that I don’t necessarily need to play the sound twice, but can send it down two parallel paths in the same graph from one “play” call?

Thank you again for your time.

It is something I’d consider as a feature request, and I’ve noted it alongside the bug itself. The post you’ve linked doesn’t appear to be related though.

Yes, you can use the Core API to construct/edit the DSP chain, and use the Core API profiler to observe the result - this is essentially what I’ve been doing myself. The white paper goes over most of the things you need to understand in order to do this.

You can indeed do that. The only thing worth noting is that the Reverb DSP unit that is created by System::setReverbProperties isn’t a part of any channels, so you’ll need to grab it by checking the connections to the master channel fader.

After the channel is generated using FMOD_INIT_CHANNEL_DISTANCEFILTER, your code to set up what you’ve suggested might look something like this:

// get master channel group
FMOD::ChannelGroup* mcg;
result = fmodSystem->getMasterChannelGroup(&mcg);
ERRCHECK(result);

// get fader attached to MCG
FMOD::DSP* faderDSP;
result = mcg->getDSP(0, &faderDSP);
ERRCHECK(result);

// get floating reverb DSP from connection to master fader. You may need
// to enumerate the connections if you have many channels connected to the mcg,
// though the index of the reverb DSP was always 0 in my limited tests
FMOD::DSP* reverbDSP;
FMOD::DSPConnection* connection;
result = faderDSP->getInput(0, &reverbDSP, &connection);
ERRCHECK(result);

// get channel fader at head of channel and disconnect it from reverb DSP
FMOD::DSP* channel0Fader;
FMOD::DSPConnection* channel0connection;
reverbDSP->getInput(0, &channel0Fader, &channel0connection);
reverbDSP->disconnectFrom(channel0Fader);

// traverse down DSP chain from channel fader to the wavetable unit
FMOD::DSP* nextDSP;
FMOD::DSPConnection* nextDSPCon;
nextDSP = channel0Fader;
// FMOD_INIT_CHANNEL_DISTANCEFILTER = channel fader + 2 filters before wavetable
int numDSPToTraverse = 3;
for (int i = 0; i < numDSPToTraverse; i++) {
    nextDSP->getInput(0, &nextDSP, &nextDSPCon);
}

// add standard connection from reverb to wavetable
reverbDSP->addInput(nextDSP, &nextDSPCon, FMOD_DSPCONNECTION_TYPE_STANDARD);

Which produces a signal chain that looks like this:

No problem!

Hi Louis,

I managed to follow your code to create a separate path for the reverb, with a fader that’s controlled by the reverbocclusion that I can read out with channel->get3DOcclusion. So great! The only downside is that it seems like I have to make my own reverb gain calculations based on the occlusion and distance to the main listener, so it seems to be a bit brittle/non-generic, but it’s going to work great for what I’m making right now. Thanks again.

1 Like