渐弱的声音输入/输出
本文关键字:输出 输入 声音 | 更新日期: 2023-09-27 18:25:52
我有一个背景音在无休止的循环中播放。我希望它在用户按下按钮时淡出。
我尝试了以下方法:
- 使用WaveStream启动DirectSoundOut
- 定时器改变WaveChannel32的音量
问题:
- 在播放声音时更改音量会产生噪音
有人知道更好的解决方案吗?
要执行平滑的淡入淡出,需要在样本级别执行。然后将每个样本乘以一个逐渐增加或减少的数字。您正在使用WaveChannel32,因此您的音频已经转换为32位浮点。然后,我将创建另一个IWaveProvider实现器,负责执行淡入淡出。通常情况下,它会以不变的方式通过采样,但在Read方法中,如果您处于淡入或淡出状态,它会将每个采样相乘(如果是立体声,则为成对)。
NAudio 1.5中的ISampleProvider接口旨在使这类事情变得更容易,因为它允许您将样本处理为32位浮点,而不是实现需要从字节[]转换为浮点[]的IWaveProvider。这是我刚刚制作的一个淡入淡出的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; }
}
}
或者你可以这样做:
while (waveOut.volume > 0.1)
{
waveOut.volume -= 0.1;
System.Threading.Thread.Sleep(10);
}
^淡出的一个例子。我在自己的程序中使用它,效果很好。