Как связать несколько эффектов NAudio ISampleProvider
У меня есть некоторые эффекты DSP, закодированные в модели ISampleProvider. Чтобы применить один эффект, я делаю это, и он отлично работает.
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 ответа
Эффекты могут быть объединены в цепочку, передавая один в качестве "источника" для следующего. Поэтому, если вы хотите, чтобы ваш звук проходил сначала через реверберацию, а затем искажение, вы можете сделать что-то вроде этого, передав исходный звук в эффект реверберации, вывод реверберации в эффект искажения и затем отправив искажение в waveOut устройство.
var reverb = new ReverbSampleProvider(sampProvider);
var distortion = new DistortionSampleProvider(reverb);
waveOutDevice.Init(distortion);
(nb NAudio не поставляется со встроенными эффектами реверберации / искажения - вы должны сделать это сами или получить их из других источников)
Ответ Марка верен, но такой подход является болезненным, если вы копируете и вставляете вещи в разные порядки, потому что вам нужно изменить переменные, через которые вы проходите.
Например, если вы начинаете с:
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);
Это позволяет вам быстро переупорядочивать эффекты в цепочке, так как каждый эффект берет на себя голову цепочки эффектов в качестве входных данных. Если вы не добавляете какие-либо эффекты, это просто действует как проход. Вы можете легко расширить этот класс, чтобы иметь больше методов для управления содержащимися эффектами, если хотите, но в его нынешнем виде он работает довольно чисто.