音频淡入淡出

6
我有一个背景声音在无限循环播放。我希望当用户按下按钮时,它能渐隐。
我尝试了以下方法:
  • 使用WaveStream初始化DirectSoundOut
  • 一个计时器改变WaveChannel32的音量。
问题在于:
  • 当声音正在播放时改变音量会产生噪音。
是否有更好的解决方案呢?
2个回答

7
要实现平滑的淡入或淡出效果,您需要在样本级别进行操作。然后,您需要逐渐增加或减少每个样本的数字。由于您正在使用WaveChannel32,因此您的音频已经转换为32位浮点数。然后我会创建另一个实现了IWaveProvider接口的对象,它负责执行淡入和淡出的操作。通常它会无损地传递样本,但是在Read方法中,如果处于淡入或淡出状态,则会对每个样本(如果是立体声则对每对样本)进行相应的乘法运算。
在NAudio 1.5中,ISampleProvider接口的设计旨在使这种类型的操作更加容易,因为它允许您将样本视为32位浮点数进行处理,而不需要实现IWaveProvider,从而需要您将byte[]转换为float[]。以下是我刚刚创建的用于淡入淡出的SampleProvider,它将包含在下一个NAudio中,并希望能尽快在博客中分享。只需使用适当的淡入或淡出持续时间调用BeginFadeIn或BeginFadeOut即可。
public class FadeInOutSampleProvider : ISampleProvider
{
    enum FadeState
    {
        Silence,
        FadingIn,
        FullVolume,
        FadingOut,
    }

    private readonly object lockObject = new object();
    private readonly ISampleProvider source;
    private int fadeSamplePosition;
    private int fadeSampleCount;
    private FadeState fadeState;

    public FadeInOutSampleProvider(ISampleProvider source)
    {
        this.source = source;
        this.fadeState = FadeState.FullVolume;
    }

    public void BeginFadeIn(double fadeDurationInMilliseconds)
    {
        lock (lockObject)
        { 
            fadeSamplePosition = 0;
            fadeSampleCount = (int)((fadeDurationInMilliseconds * source.WaveFormat.SampleRate) / 1000);
            fadeState = FadeState.FadingIn;
        }
    }

    public void BeginFadeOut(double fadeDurationInMilliseconds)
    {
        lock (lockObject)
        {
            fadeSamplePosition = 0;
            fadeSampleCount = (int)((fadeDurationInMilliseconds * source.WaveFormat.SampleRate) / 1000);
            fadeState = FadeState.FadingOut;
        }
    }

    public int Read(float[] buffer, int offset, int count)
    {
        int sourceSamplesRead = source.Read(buffer, offset, count);
        lock (lockObject)
        {
            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;
            }
        }
    }

    public WaveFormat WaveFormat
    {
        get { return source.WaveFormat; }
    }
}

0

或者你可以这样做:

while (waveOut.volume > 0.1)
{
  waveOut.volume -= 0.1;
  System.Threading.Thread.Sleep(10);
}

^一个淡出效果的例子。我在自己的程序中使用它,效果很好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接