我正在寻找一种使用 NAudio 的
MixingSampleProvider
将数千个 WAV 文件混合到一个文件中的方法。我正在构建一个鼓采样器,我希望能够创建整首歌曲(基于 MIDI 信息),然后可以将其导出到单个 WAV 文件中。
我遇到的问题是
MixingSampleProvider
的 限制为 1024 个源,如果达到该限制,则会抛出异常并显示 Too many mixer inputs
。我确信这个限制的存在是有原因的,尽管如此,我还是想知道如何实现我的目标。
我已经搜索了 NAudio 演示和 Mark Heath 的博客,但我还没有找到我真正需要的东西。
我想我可以将歌曲分成更小的片段(在 1024 个采样器输入下)然后合并单独的部分。这是可行的方法,还是有更好的方法?感谢您的任何建议。
这是我的代码的一部分:
public class DrumSampler
{
private readonly MixingSampleProvider _mixer;
private readonly Dictionary<string, SampleSource> _cachedSamples = new();
public DrumSampler()
{
var waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(44100, 2);
_mixer = new MixingSampleProvider(waveFormat);
LoadSamples();
}
private void LoadSamples()
{
LoadSample("kick", @"C:\Samples\kick.wav");
LoadSample("snare", @"C:\Samples\snare.wav");
LoadSample("crash", @"C:\Samples\crash.wav");
}
private void LoadSample(string key, string filePath)
{
_cachedSamples.Add(key, SampleSource.CreateFromWaveFile(filePath, _mixer.WaveFormat));
}
public void ExportSong()
{
AddDrums();
WaveFileWriter.CreateWaveFile16("song.wav", _mixer);
}
private void AddDrums()
{
//simulate adding drum samples based on MIDI information
for (int i = 0; i < 1000; i++)
{
var sample = _cachedSamples["kick"];
var delayed = new DelayedSampleProvider(sample, TimeSpan.FromSeconds(123));
_mixer.AddMixerInput(delayed);
}
}
}
SampleSource
实现取自NAudio的DrumMachineDemo。
DelayedSampleProvider
实现的灵感来自NAudio的OffsetSampleProvider。
我已经能够通过将歌曲分成较小的chungs(在1024个混音器输入下)来解决这个问题,每个chungs都有自己的
MixingSampleProvider
,然后使用ConcatenatingSampleProvider
连接所有chungs。
它有效,但是,我想知道是否有更优雅的解决方案......
这是我的代码:
public class DrumSampler
{
private readonly WaveFormat _waveFormat;
private readonly Dictionary<string, SampleSource> _cachedSamples = new();
public DrumSampler()
{
_waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(44100, 2);
LoadSamples();
}
private void LoadSamples()
{
LoadSample("kick", @"C:\Samples\kick.wav");
LoadSample("snare", @"C:\Samples\snare.wav");
LoadSample("crash", @"C:\Samples\crash.wav");
}
private void LoadSample(string key, string filePath)
{
_cachedSamples.Add(key, SampleSource.CreateFromWaveFile(filePath, _mixer.WaveFormat));
}
public void ExportSong()
{
var result = GenerateDrumAudio();
WaveFileWriter.CreateWaveFile16("song.wav", result);
}
private ISampleProvider GenerateDrumAudio()
{
//simulate reading drum hits from MIDI
var drumHits = Enumerable.Range(0, 10_000).ToList();
//split drum hits into chunks of 1024 (max input limit of the mixing sample provider)
var drumHitChunks = drumHits.Chunk(1024);
//store all chunk mixers in a list
var mixers = new List<MixingSampleProvider>();
foreach (var drumHitChunk in drumHitChunks)
{
//for each chunk: create a mixer, add drum hits to it and add it to the mixers list
var mixer = new MixingSampleProvider(_waveFormat);
foreach (var drumHit in drumHitChunk)
{
var sample = _cachedSamples["kick"];
var delayed = new DelayedSampleProvider(sample, TimeSpan.FromSeconds(123));
mixer.AddMixerInput(delayed);
}
mixers.Add(mixer);
}
//concatenate all chunk mixers into a single ISampleProvider
var mixersConcatenated = new ConcatenatingSampleProvider(mixers);
return mixersConcatenated;
}
}
我需要解决类似的问题,我需要为计算机生成的视频生成音轨,并在发生特定事件的每个时间戳中添加声音效果。当我尝试添加太多音效时,我也遇到了上限,这是我的解决方案。
首先,我计算了每个效果的绝对时间戳。然后,我将它们分成少于 1024 项的组,并使用
MixingSampleProvider
将它们组合起来,并使用另一个 MixingSampleProvider
将这些块组合成单个提供程序。请注意,如果我有超过 1023^2 个项目,则此过程需要递归进行。
public static ISampleProvider CreateAudioFile(List<int> timestamps)
{
var format = new AudioFileReader(File).WaveFormat;
return timestamps.Chunk(1023).Select(chunk =>
{
return chunk.Aggregate(new MixingSampleProvider(format), (mixer, ts) =>
{
mixer.AddMixerInput(new OffsetSampleProvider(new AudioFileReader(File))
{
DelayBy = TimeSpan.FromMilliseconds(ts),
});
return mixer;
}) as ISampleProvider;
}).Aggregate(new MixingSampleProvider(format), (mixer, chunk) =>
{
mixer.AddMixerInput(chunk);
return mixer;
});
}