You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何突破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š

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 12:40:33