Questions about fade in and fade out?


#1

I have some questions about fading in and fading out a playing sound. Here is my situation, there are two areas in my application and each have a sound. When the player enter an area, sound belongs to the will play with 5 seconds fade-in, and when the player leave it, the sound will add 5 seconds fade-out then stop. The FMOD_Channel_AddFadePoint works fine when player leave an area and re-enter the area after 5 seconds or enter the area again after leave the area 5 seconds. Following is my code:

void Sound::fadein(float fade_time)
{
	if (!isPlaying())
		return;

	int rate = 0;
	FMOD_System_GetSoftwareFormat(SoundUtil::FMODSystem, &rate, 0, 0);
	unsigned long long dspclock = 0u;
	FMOD_Channel_GetDSPClock(d_p->channel, nullptr, &dspclock);
	FMOD_Channel_AddFadePoint(d_p->channel, dspclock, 0.f);
	FMOD_Channel_AddFadePoint(d_p->channel, dspclock + unsigned long long(rate * fade_time), 1.f);
}

void Sound::fadeout(float fade_time)
{
	if (!isPlaying())
		return;

	int rate = 0;
	FMOD_System_GetSoftwareFormat(SoundUtil::FMODSystem, &rate, 0, 0);
	unsigned long long dspclock = 0u;
	FMOD_Channel_GetDSPClock(d_p->channel, nullptr, &dspclock);
	FMOD_Channel_AddFadePoint(d_p->channel, dspclock, 1.f));
	FMOD_Channel_AddFadePoint(d_p->channel, dspclock + unsigned long long(rate * fade_time), 0.f);
	FMOD_Channel_SetDelay(d_p->channel, 0, dspclock + unsigned long long(rate * fade_time), true);
}

However, sometimes the player will re-enter area before fade-out tick finished, for example leave area at 10 o’clock and re-enter it at 10 o’click and 3 second, I find the fade-out will still tick. I check the document and find another api: FMOD_Channel_SetFadePointRamp, it says “This is a helper function that automatically ramps from the current fade volume to the newly provided volume at a specified time. It will clear any fade points set after this time.”, I think it will meet my requirement, but it doesn’t work fine.
So how can I do at this situation? How can I get actual volume(not the volume set by FMOD_Channel_SetVolume) in fade process and stop it then begin a new fade process?


#2

Hi Russell,
your code looks good.

On the fade in, you’ll need to remove the future fade point that it is fading out to ((https://www.fmod.com/resources/documentation-api?page=content/generated/FMOD_Channel_RemoveFadePoints.html#/) , and cancel the setdelay stop command as well. I’ll see if I can come up with some sample code.


#3

Here is my version of your fade in
Note I changed some variables to suit my testbed, you can change gSystem back to SoundUtil::FMODSystem and gChannel[0] back to d_p->channel

The key is if a fade out is already happening

  1. you need to stop that end delay ‘stopchannels’ command from stopping the sound. This is done by clearing the delays with setDelay(0,0,false)
  2. you need to remove the future fade point that is making it fade to 0.
  3. because it needs to fade ‘from’ your current position, this is where it gets a bit more complicated. You have to insert a new point at the current time, so you have to interpolate where the volume should be at this point
  4. set a new 1.0 fade point into the future, but not 5 seconds into the future, just the amount of time it took to fade to the current volume.

Note I lockDSP’ed and unlockDSP’ed the whole code block, so that the mixer wont run while we’re getting values and adding new points. You don’t want to have the mixer butt in during this setup. This makes it more ‘atomic’

Side note, addFadePointRamp is not for slow fades, the docs say it is a fixed 64 sample ramp, used primarily for declicking stop/start/pause state changes.

void fadein(float fade_time)
{
	int rate = 0;
    unsigned int numpoints = 0;
	FMOD_System_GetSoftwareFormat((FMOD_SYSTEM *)gSystem, &rate, 0, 0);
	unsigned long long dspclock = 0;
    float vol = 0;
    FMOD_RESULT result;

    FMOD_System_LockDSP((FMOD_SYSTEM *)gSystem);
    {
	    FMOD_Channel_GetDSPClock((FMOD_CHANNEL *)gChannel[0], nullptr, &dspclock);

        result = FMOD_Channel_GetFadePoints((FMOD_CHANNEL *)gChannel[0], &numpoints, 0, 0);
        // If a fade out is already happening, reset fade points to interrupt the fade out 
        if (numpoints)
        {
            float volumes[2];
            unsigned long long points[2];

            result = FMOD_Channel_GetFadePoints((FMOD_CHANNEL *)gChannel[0], &numpoints, points, volumes);

            // Make sure we're 'in the fade' and make sure it is a fade out, not a fade in.  
            if (dspclock >= points[0] && dspclock < points[1] && volumes[0] > volumes[1])
            {
                float currentvol;
                // First clear end delay
                FMOD_Channel_SetDelay((FMOD_CHANNEL *)gChannel[0], 0, 0, false);    // First clear end delay

                // Next clear future fade points
                FMOD_Channel_RemoveFadePoints((FMOD_CHANNEL *)gChannel[0], dspclock, dspclock + unsigned long long(rate * fade_time));

                // Now set a new 'current' source volume point where we are. Interpolate
                currentvol = volumes[0] + ((volumes[1] - volumes[0]) / (points[1] - points[0]) * (dspclock - points[0]));
                FMOD_Channel_AddFadePoint((FMOD_CHANNEL *)gChannel[0], dspclock, currentvol);

                // Then set a target volume fade point in the future with 1 instead of 0.  Length of time is the time it took to fade out this far. (current time minus the first fade point time)
                FMOD_Channel_AddFadePoint((FMOD_CHANNEL *)gChannel[0], dspclock + (dspclock - points[0]), 1.f);
            }
        }
        else
        {
    	    FMOD_Channel_AddFadePoint((FMOD_CHANNEL *)gChannel[0], dspclock, 0.f);
	        FMOD_Channel_AddFadePoint((FMOD_CHANNEL *)gChannel[0], dspclock + unsigned long long(rate * fade_time), 1.f);
        }
    }
    FMOD_System_UnlockDSP((FMOD_SYSTEM *)gSystem);
}

#4

It Works! Thanks very much!