使用 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;
}
我正在我编写的 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;
}