NAudio:正确使用 MixingSampleProvider 和 VolumeSampleProvider
Posted
技术标签:
【中文标题】NAudio:正确使用 MixingSampleProvider 和 VolumeSampleProvider【英文标题】:NAudio: Using MixingSampleProvider correctly with VolumeSampleProvider 【发布时间】:2016-06-11 16:54:19 【问题描述】:我一直在使用 NAudio “使用 NAudio 播放并忘记音频”教程(感谢 Mark 提供了这个很棒的实用程序!)如下所示: http://mark-dot-net.blogspot.nl/2014/02/fire-and-forget-audio-playback-with.html
我设法将 VolumeSampleProvider 添加到它,使用 MixingSampleProvider 作为输入。但是,当我现在连续播放两个声音时,第一个声音总是会获得第二个声音的音量,即使第一个声音已经在播放。
所以我的问题是:如何为每个声音添加具有单独音量的声音?
这是我用的:
mixer = new MixingSampleProvider(waveformat);
mixer.ReadFully = true;
volumeProvider = new VolumeSampleProvider(mixer);
panProvider = new PanningSampleProvider(volumeProvider);
outputDevice.Init(panProvider);
outputDevice.Play();
【问题讨论】:
【参考方案1】:我意识到(感谢 itsmatt)实现这项工作的唯一方法是让混音器保持独立,并在将每个 CachedSound 的声像和音量添加到混音器之前单独调整它。因此,我需要重写 CachedSoundSampleProvider,使用平移和音量作为额外的输入参数。
这是新的构造函数:
public CachedSoundSampleProvider(CachedSound cachedSound, float volume = 1, float pan = 0)
this.cachedSound = cachedSound;
LeftVolume = volume * (0.5f - pan / 2);
RightVolume = volume * (0.5f + pan / 2);
这是新的 Read() 函数:
public int Read(float[] buffer, int offset, int count)
long availableSamples = cachedSound.AudioData.Length - position;
long samplesToCopy = Math.Min(availableSamples, count);
int destOffset = offset;
for (int sourceSample = 0; sourceSample < samplesToCopy; sourceSample += 2)
float outL = cachedSound.AudioData[position + sourceSample + 0];
float outR = cachedSound.AudioData[position + sourceSample + 1];
buffer[destOffset + 0] = outL * LeftVolume;
buffer[destOffset + 1] = outR * RightVolume;
destOffset += 2;
position += samplesToCopy;
return (int)samplesToCopy;
【讨论】:
【参考方案2】:我不能 100% 确定你在问什么,我不知道你是否已经解决了这个问题,但这是我对此的看法。
ISampleProvider
对象通过Read()
方法向其源ISampleProvider
进行“推卸责任”游戏。最终,有人会实际读取音频字节。各个 ISampleProvider
类对字节执行任何操作。
MixingSampleProvider
,例如,需要 N 个音频源……它们混合在一起。当调用Read()
时,它会迭代音频源并从每个音频源中读取count
字节。
将它传递给VolumeSampleProvider
将所有字节(来自各种来源)作为一个组处理......它说:
buffer[offset+n] *= volume;
这将全面调整字节...所以缓冲区中的每个字节都会通过 volume
乘数进行调整;
PanningSampleProvider
只是为立体声音频提供一个乘法器并相应地调整字节,与VolumeSampleProvider
做同样的事情。
如果您想单独处理音频源音量,则需要处理 MixingSampleProvider
的上游。本质上,您传递给 MixingSampleProvider 的东西需要能够独立调整它们的音量。
如果您将一堆SampleChannel
对象传递给您的MixingSampleProvider
...,您可以完成独立的音量调节。 Samplechannel
类包含一个 VolumeSampleProvider
对象并提供一个 Volume
属性,允许用户设置该 VolumeSampleProvider
对象的音量。
SampleChannel
还包含一个MeteringSampleProvider
,它提供给定时间段内最大样本值的报告。它会引发一个事件,为您提供这些值的数组,每个通道一个。
【讨论】:
亲爱的马特,感谢您的回答。我将不得不仔细研究 SampleChannel 类,看看我是否可以让它工作。自从我上次编写此代码以来已经有一年多了,所以我想我不会很快知道。问候。以上是关于NAudio:正确使用 MixingSampleProvider 和 VolumeSampleProvider的主要内容,如果未能解决你的问题,请参考以下文章
使用 NAudio 从麦克风录制声音。为啥不能正确地从列表中记录整个缓冲区?
Naudio:如何在不停止所有其他声音的情况下正确删除 AsioOut 播放的几种声音中的一种?