Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
NAudio basic example of how to begin a fade out after a certain number of milliseconds have elapsed
// Define other methods and classes here
/// <summary>
/// Sample Provider to allow fading in and out
/// </summary>
public class DelayFadeOutSampleProvider : ISampleProvider
{
enum FadeState
{
Silence,
FadingIn,
FullVolume,
FadingOut,
}
private readonly object lockObject = new object();
private readonly ISampleProvider source;
private int fadeSamplePosition;
private int fadeSampleCount;
private int fadeOutDelaySamples;
private int fadeOutDelayPosition;
private FadeState fadeState;
/// <summary>
/// Creates a new FadeInOutSampleProvider
/// </summary>
/// <param name="source">The source stream with the audio to be faded in or out</param>
/// <param name="initiallySilent">If true, we start faded out</param>
public DelayFadeOutSampleProvider(ISampleProvider source, bool initiallySilent = false)
{
this.source = source;
this.fadeState = initiallySilent ? FadeState.Silence : FadeState.FullVolume;
}
/// <summary>
/// Requests that a fade-in begins (will start on the next call to Read)
/// </summary>
/// <param name="fadeDurationInMilliseconds">Duration of fade in milliseconds</param>
public void BeginFadeIn(double fadeDurationInMilliseconds)
{
lock (lockObject)
{
fadeSamplePosition = 0;
fadeSampleCount = (int)((fadeDurationInMilliseconds * source.WaveFormat.SampleRate) / 1000);
fadeState = FadeState.FadingIn;
}
}
/// <summary>
/// Requests that a fade-out begins (will start on the next call to Read)
/// </summary>
/// <param name="fadeDurationInMilliseconds">Duration of fade in milliseconds</param>
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;
}
}
/// <summary>
/// Reads samples from this sample provider
/// </summary>
/// <param name="buffer">Buffer to read into</param>
/// <param name="offset">Offset within buffer to write to</param>
/// <param name="count">Number of samples desired</param>
/// <returns>Number of samples read</returns>
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;
}
private static void ClearBuffer(float[] buffer, int offset, int count)
{
for (int n = 0; n < count; n++)
{
buffer[n + offset] = 0;
}
}
private void FadeOut(float[] buffer, int offset, int sourceSamplesRead)
{
int sample = 0;
while (sample < sourceSamplesRead)
{
float multiplier = 1.0f - (fadeSamplePosition / (float)fadeSampleCount);
for (int ch = 0; ch < source.WaveFormat.Channels; ch++)
{
buffer[offset + sample++] *= multiplier;
}
fadeSamplePosition++;
if (fadeSamplePosition > fadeSampleCount)
{
fadeState = FadeState.Silence;
// clear out the end
ClearBuffer(buffer, sample + offset, sourceSamplesRead - sample);
break;
}
}
}
private void FadeIn(float[] buffer, int offset, int sourceSamplesRead)
{
int sample = 0;
while (sample < sourceSamplesRead)
{
float multiplier = (fadeSamplePosition / (float)fadeSampleCount);
for (int ch = 0; ch < source.WaveFormat.Channels; ch++)
{
buffer[offset + sample++] *= multiplier;
}
fadeSamplePosition++;
if (fadeSamplePosition > fadeSampleCount)
{
fadeState = FadeState.FullVolume;
// no need to multiply any more
break;
}
}
}
/// <summary>
/// WaveFormat of this SampleProvider
/// </summary>
public WaveFormat WaveFormat
{
get { return source.WaveFormat; }
}
}
void Main()
{
using(var reader = new AudioFileReader(@"D:\Audio\Music\Example.mp3"))
{
var fadeOut = new DelayFadeOutSampleProvider(reader);
fadeOut.BeginFadeOut(10000, 2000);
using(var player = new WaveOutEvent())
{
player.Init(fadeOut);
player.Play();
while(player.PlaybackState == PlaybackState.Playing)
{
Thread.Sleep(500);
}
}
}
}
@Ahmed-Abdelhameed

This comment has been minimized.

Copy link

Ahmed-Abdelhameed commented Sep 1, 2017

I just fixed a bug in the Read method which caused the fade out to be applied in the wrong position. Please update the gist. Here's my fork for reference: https://gist.github.com/Ahmed-Abdelhameed/b867a8cfe739cd7a128b73a33d317402. You can also refer to this SO question: https://stackoverflow.com/a/45997576/4934172.

@fosterz

This comment has been minimized.

Copy link

fosterz commented Jun 20, 2018

I'm facing one weird issue with this quick solution. It works fine until I move trackbar of tha song. For e.g if I move the trackbar then fadeout never happens.

@SR-TheCodingGenius

This comment has been minimized.

Copy link

SR-TheCodingGenius commented Nov 30, 2018

I just fixed a bug in the Read method which caused the fade out to be applied in the wrong position. Please update the gist. Here's my fork for reference: https://gist.github.com/Ahmed-Abdelhameed/b867a8cfe739cd7a128b73a33d317402. You can also refer to this SO question: https://stackoverflow.com/a/45997576/4934172.

I tried with your fix , but there are clicks when the audio file is cross-faded with itself , but no clicks were found with the original code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.