How to Schedule a Fade Out using NAudio
I added a FadeInOutSampleProvider
to NAudio a few years back as an example of how you could implement basic fade in and out functionality. This class does have a limitation though – it assumes you want to dynamically trigger the fade out while the audio is playing.
But what if you want to schedule a fade out to start at a specific time? Well prompted by this question on StackOverflow, I made some minor modifications to FadeInOutSampleProvider
to show how this could be done. This is a quick and dirty fix, so it won’t be going into the library, but I’ve made the code available as a gist so you can easily try it for yourself.
For an explanation of how it works, read on…
What we do is we calculate the number of samples that we need to wait before beginning the fade-out. So if I want to wait 5 seconds, then I must wait for 5 multiplied by the sample rate (and multiply again by 2 if it is stereo). Now all we need to do is count how many samples we’ve returned in the Read
method, and when it goes over the threshold, begin the fadeout.
This is a fairly simplistic approach as it means that the fade out could begin slightly late, since we don’t let it begin mid-way through a call to Read
. But that’s not a huge change to make if you need it.
The main modifications to FadeInOutSampleProvider
are an extra parameter to BeginFadeOut
, that sets two new variables (fadeOutDelaySamples
, fadeOutDelayPosition
). We also no longer set the fadeState
immediately:
public void BeginFadeOut(double fadeAfterMilliseconds, double fadeDurationInMilliseconds)
{
lock (lockObject)
{
fadeSamplePosition = 0;
fadeSampleCount = (int)((fadeDurationInMilliseconds * source.WaveFormat.SampleRate) / 1000);
fadeOutDelaySamples = (int)((fadeAfterMilliseconds * source.WaveFormat.SampleRate) / 1000);
fadeOutDelayPosition = 0;
//fadeState = FadeState.FadingOut;
}
}
Now in the Read
method we simply need to keep track of how far into the delay we are, and if we have gone over the threshold, we can start the fade-out:
public int Read(float[] buffer, int offset, int count)
{
int sourceSamplesRead = source.Read(buffer, offset, count);
lock (lockObject)
{
if (fadeOutDelaySamples > 0)
{
fadeOutDelayPosition += sourceSamplesRead / WaveFormat.Channels;
if (fadeOutDelayPosition >= fadeOutDelaySamples)
{
fadeOutDelaySamples = 0;
fadeState = FadeState.FadingOut;
}
}
if (fadeState == FadeState.FadingIn)
{
FadeIn(buffer, offset, sourceSamplesRead);
}
else if (fadeState == FadeState.FadingOut)
{
FadeOut(buffer, offset, sourceSamplesRead);
}
else if (fadeState == FadeState.Silence)
{
ClearBuffer(buffer, offset, count);
}
}
return sourceSamplesRead;
}
Hopefully this points you in the right direction if you need to implement a scheduled fade-out for your own application.
Comments
Hi Mark,
David AThanks for your work on this library. I use it all the time. I have a simple question I think but I don't see much chatter about it and the one or two solutions seem to be quite a workaround. I need to simply take a wave file on disk, add a 1 second fade in and out, and save it back to disk. I don't need/want to play it live. I have done a lot of searching around but cannot seem to get it to work. Thank you!