如何将多个NAudio ISampleProvider效果链接在一起

时间:2017-09-26 18:51:33

标签: c# naudio

我在ISampleProvider模型中编码了一些DSP效果。为了应用一种效果,我这样做,它工作正常。

string filename = "C:\myaudio.mp3";
MediaFoundationReader mediaFileReader = new MediaFoundationReader(filename);
ISampleProvider sampProvider = mediaFileReader.ToSampleProvider();
ReverbSampleProvider reverbSamplr = new ReverbSampleProvider(sampProvider);
IWavePlayer waveOutDevice.Init(reverbSamplr);
waveOutDevice.Play();

如何同时将多个效果应用于同一个输入文件? 例如,如果我有一个混响效果和失真效果提供器,我如何将它们链接在一起以同时将它们应用到一个输入文件?

2 个答案:

答案 0 :(得分:1)

效果可以通过传递一个作为下一个的“源”来链接在一起。因此,如果您希望音频首先通过混响,然后失真,您可能会执行类似这样的操作,将原始音频传递到混响效果,将混响输出传递到失真效果,然后将失真发送到waveOut设备

var reverb = new ReverbSampleProvider(sampProvider);
var distortion = new DistortionSampleProvider(reverb);
waveOutDevice.Init(distortion);

(n.b.NAudio没有内置的混响/失真效果 - 您必须自己制作或从其他地方获取它们)

答案 1 :(得分:1)

Mark的答案是正确的,但是如果您以不同的顺序复制和粘贴内容,这种方法会很痛苦,因为您必须更改要传递的变量。

例如,如果您以以下内容开头:

var lpf = new LowPassEffectStream(input);
var reverb = new ReverbEffectStream(lpf);
var stereo = new StereoEffectStream(reverb);
var vol = new VolumeSampleProvider(stereo);
waveOutDevice.Init(vol);

您想交换混响和立体声,快速复制粘贴将输入变量倒退:

var lpf = new LowPassEffectStream(input);
var stereo = new StereoEffectStream(reverb); // <--
var reverb = new ReverbEffectStream(lpf);    // <--
var vol = new VolumeSampleProvider(stereo);
waveOutDevice.Init(vol);

这也使固定参数变得容易,但是却忘记了修改另一个参数,例如修复stereo效果以将lpf作为其输入,但是却忘记修复reverb效果。当效果似乎不起作用时,这通常会导致效果链中跳过效果,导致调试受挫。

为使效果堆叠在一起并对其重新排序时,使事情变得更容易且不易出错,我创建了以下帮助程序类:

class EffectChain : ISampleProvider
{
    public EffectChain(ISampleProvider source)
    {
        this._sourceStream = source;
    }

    private readonly ISampleProvider _sourceStream;
    private readonly List<ISampleProvider> _chain = new List<ISampleProvider>();

    public ISampleProvider Head
    {
        get
        {
            return _chain.LastOrDefault() ?? _sourceStream;
        }
    }

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

    public void AddEffect(ISampleProvider effect)
    {
        _chain.Add(effect);
    }

    public int Read(float[] buffer, int offset, int count)
    {
        return Head.Read(buffer, offset, count);
    }
}

您可以像这样使用它:

var effectChain = new EffectChain(input);

var lpf = new LowPassEffectStream(effectChain.Head);
effectChain.AddEffect(lpf);
var stereo = new StereoEffectStream(effectChain.Head);
effectChain.AddEffect(stereo);
var reverb = new ReverbEffectStream(effectChain.Head);
effectChain.AddEffect(reverb);
var vol = new VolumeSampleProvider(effectChain.Head);
effectChain.AddEffect(vol);

waveOutDevice.Init(effectChain);

这使您可以快速重新排列链中的效果,因为每个效果都以效果链的头部为输入。如果您不添加任何效果,则仅作为传递。如果愿意,您可以轻松地扩展此类,以具有更多方法来管理所包含的效果,但就目前而言,它工作得很干净。