1
votes

I am making use of NAudio in a C# program I've written.

I want to apply a linear fade at a certain position within a piece of audio I'm working with.

In the NAudio example project is a file called FadeInOutSampleProvider.cs (Cached example) which has BeginFadeIn(double fadeDurationInMilliseconds) and BeginFadeOut(double fadeDurationInMilliseconds) methods.

I've reworked these methods to BeginFadeOut(double fadeDurationInMilliseconds, double beginFadeAtMilliseconds) and BeginFadeOut(double fadeDurationInMilliseconds, double beginFadeAtMilliseconds)

However I'm having difficulty implementing the interval logic for these changes to work.

My first thought was to introduce code in the Read() method. When called it would divide the number of bytes being requested by the sample rate, which would give the number of seconds of audio requested.

I could then keep track of this and when the correct amount of auto data had been read, allow the fade to be applied.

However I'm not getting the numbers in my calculations I would expect to see. I'm sure there's a better way to approach this.

Any help would be very much appreciated.

1

1 Answers

1
votes

It sounds like you are working along the right lines. As you say the amount of audio being requested can be calculated by dividing number of samples requested by the sample rate. But you must also take into account channels as well. In a stereo file there are twice as many samples per second as the sample rate.

I've put a very basic code sample of a delayed fade out in a GitHub gist here. There are improvements that could be made such as allowing the fade-out to begin part-way through the audio returned from a call to Read but holpefully this gives you a rough idea of how it can be achieved with a few small modifications to FadeInOutSampleProvider.

The main changes are an extra parameter to BeginFadeOut, that sets two new variables (fadeOutDelaySamples, fadeOutDelayPosition):

/// <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;
   }
}

Then in the Read method we can keep track of how far into the delay we are, and if so, 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;
}