如何突破NAudio MixingSampleProvider的1024源限制混合数千WAV文件
解决NAudio MixingSampleProvider 1024输入限制的方案
针对MixingSampleProvider最多支持1024个输入源的问题,你的分段混合思路是可行的,但还有更高效的替代方案,以下是具体解决办法:
1. 分层混合(推荐)
核心思路是创建多层MixingSampleProvider:用多个子混合器分别处理不超过1024个的输入源,再将这些子混合器作为输入加到顶层混合器中。这样顶层混合器的输入数仅为子混合器的数量,完全不会触及1024的限制,且全程在内存中处理,无需导出中间片段再合并。
修改后的代码示例:
public class DrumSampler { private readonly MixingSampleProvider _rootMixer; private readonly List<MixingSampleProvider> _subMixers = new(); private readonly Dictionary<string, SampleSource> _cachedSamples = new(); // 给每个子混合器留余量,避免触发1024上限 private const int MaxInputsPerSubMixer = 1000; public DrumSampler() { var waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(44100, 2); _rootMixer = 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, _rootMixer.WaveFormat)); } // 获取当前可用的子混合器,满了就新建并添加到顶层 private MixingSampleProvider GetCurrentSubMixer() { if (_subMixers.Count == 0 || _subMixers.Last().MixerInputs.Count >= MaxInputsPerSubMixer) { var subMixer = new MixingSampleProvider(_rootMixer.WaveFormat); _subMixers.Add(subMixer); _rootMixer.AddMixerInput(subMixer); } return _subMixers.Last(); } public void ExportSong() { AddDrums(); WaveFileWriter.CreateWaveFile16("song.wav", _rootMixer); } private void AddDrums() { // 模拟基于MIDI生成的鼓采样事件 for (int i = 0; i < 3000; i++) { var sample = _cachedSamples["kick"]; var delayed = new DelayedSampleProvider(sample, TimeSpan.FromSeconds(i * 0.5)); // 将采样添加到当前子混合器而非顶层 GetCurrentSubMixer().AddMixerInput(delayed); } } }
2. 预合并同时间/同类型采样
如果你的MIDI数据中有大量同一时间触发的相同鼓采样(比如叠加重音kick),可以先将这些采样的波形叠加,生成单个SampleProvider后再加入混合器,大幅减少输入源数量。
示例代码(针对同时间重复采样):
private void AddDrums() { // 先统计每个时间点的鼓采样触发次数 var drumEventCounts = new Dictionary<TimeSpan, int>(); for (int i = 0; i < 3000; i++) { var triggerTime = TimeSpan.FromSeconds(i * 0.5); drumEventCounts.TryGetValue(triggerTime, out var count); drumEventCounts[triggerTime] = count + 1; } // 为每个时间点创建叠加后的采样 foreach (var (time, count) in drumEventCounts) { var sample = _cachedSamples["kick"]; // 用VolumeSampleProvider实现波形叠加效果(叠加N次等价于音量乘以N) var amplifiedSample = new VolumeSampleProvider(sample) { Volume = count }; var delayed = new DelayedSampleProvider(amplifiedSample, time); GetCurrentSubMixer().AddMixerInput(delayed); } }
3. 自定义动态扩容的混合器
如果需要完全摆脱1024的限制,可以参考NAudio官方MixingSampleProvider的源码,将内部固定大小的输入数组改为List<ISampleProvider>,实现动态扩容的混合逻辑。需要注意保留原有的“自动移除已完成输入源”的逻辑,避免内存泄漏。
方案对比
- 分层混合:改动最小,逻辑简洁,性能与原MixingSampleProvider一致,适合绝大多数场景。
- 预合并:进一步减少输入数量,适合有大量重复采样事件的场景。
- 自定义混合器:完全灵活,但需要维护自己的实现,适合特殊需求场景。
你的分段混合思路虽然可行,但需要额外处理片段导出与合并,流程更繁琐,不如分层混合高效。
内容的提问来源于stack exchange,提问作者Michal Diviš
相关产品推荐
相关产品推荐

