如何使用 NAudio 的 MixingSampleProvider 将数千个 WAV 文件混合到一个文件中?

问题描述 投票:0回答:2

我正在寻找一种使用 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

c# wav naudio mixing
2个回答
0
投票

我已经能够通过将歌曲分成较小的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;
    }
}

0
投票

我需要解决类似的问题,我需要为计算机生成的视频生成音轨,并在发生特定事件的每个时间戳中添加声音效果。当我尝试添加太多音效时,我也遇到了上限,这是我的解决方案。

首先,我计算了每个效果的绝对时间戳。然后,我将它们分成少于 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;
    });
}
© www.soinside.com 2019 - 2024. All rights reserved.