淡入/淡出声音

Posted

技术标签:

【中文标题】淡入/淡出声音【英文标题】:Fading sound in/out 【发布时间】:2012-02-27 16:01:50 【问题描述】:

我有一个无限循环播放的背景声音。我希望它在用户按下按钮时淡出。

我尝试了以下方法:

使用 WaveStream 启动 DirectSoundOut 定时器改变 WaveChannel32 的音量。

问题:

在播放声音时改变音量会产生噪音。

有人知道更好的解决方案吗?

【问题讨论】:

【参考方案1】:

要执行平滑的淡入或淡出,您需要在样本级别执行此操作。然后,您将每个样本乘以逐渐增加或减少的数字。您使用的是 WaveChannel32,因此您的音频已经转换为 32 位浮点数。然后,我将创建另一个 IWaveProvider 实现者,负责淡入和淡出。通常它会不加改变地通过样本,但在 Read 方法中,如果您处于淡入或淡出状态,它会将每个样本相乘(如果是立体声,则将成对)。

NAudio 1.5 中的 ISampleProvider 接口旨在使这种类型的事情变得更容易,因为它允许您将样本处理为 32 位浮点数,而不是实现需要您从 byte[] 转换为 float[ 的 IWaveProvider ]。这是我刚刚制作的用于淡入和淡出的 SampleProvider,我将在下一个 NAudio 中包含它,并希望尽快发布有关它的博客。只需调用 BeginFadeInBeginFadeOut 并使用适当的淡入淡出持续时间。

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; 
    

【讨论】:

【参考方案2】:

或者你可以这样做:

while (waveOut.volume > 0.1)

  waveOut.volume -= 0.1;
  System.Threading.Thread.Sleep(10);

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

【讨论】:

以上是关于淡入/淡出声音的主要内容,如果未能解决你的问题,请参考以下文章

Audiokit 交叉淡入淡出声音循环

初学者 python 编程帮助交叉淡入淡出声音

使用javascript淡入/淡出播放/暂停音乐

使用 Actionscript 3.0 淡入声音

iOS 如何在 Sprite Kit 游戏中创建音频淡入/淡出效果?

开始播放时声音随机中断