使用 NAudio 在指定位置应用线性淡入淡出

Applying a linear fade at a specify position using NAudio

我正在我编写的 C# 程序中使用 NAudio

我想在我正在处理的一段音频中的特定位置应用线性淡入淡出。

在 NAudio 示例项目中有一个名为 FadeInOutSampleProvider.cs (Cached example) 的文件,其中包含 BeginFadeIn(double fadeDurationInMilliseconds)BeginFadeOut(double fadeDurationInMilliseconds) 方法。

我修改了这些方法以 BeginFadeOut(double fadeDurationInMilliseconds, double beginFadeAtMilliseconds)BeginFadeOut(double fadeDurationInMilliseconds, double beginFadeAtMilliseconds)

但是,我在实施间隔逻辑以使这些更改生效时遇到了困难。

我的第一个想法是在Read()方法中引入代码。调用时,它将请求的字节数除以采样率,这将给出请求的音频秒数。

然后我可以跟踪这一点,当读取到正确数量的自动数据时,允许应用淡入淡出。

但是我没有在我的计算中得到我期望看到的数字。我相信有更好的方法来解决这个问题。

非常感谢任何帮助。

听起来您的工作是正确的。正如您所说,可以通过将请求的样本数除以采样率来计算请求的音频量。但您还必须考虑渠道。在立体声文件中,每秒采样数是采样率的两倍。

我在 GitHub gist here 中放置了一个非常基本的延迟淡出代码示例。可以进行一些改进,例如允许淡出从调用 Read 返回的音频中途开始,但希望这可以让您大致了解如何通过一些小的实现对 FadeInOutSampleProvider 的修改。

主要变化是 BeginFadeOut 的一个额外参数,它设置了两个新变量(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;
   }
}

然后在 Read 方法中我们可以跟踪我们进入延迟的程度,如果是这样,我们可以开始淡出

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