NAudio:正确使用 MixingSampleProvider 和 VolumeSampleProvider

Posted

技术标签:

【中文标题】NAudio:正确使用 MixingSampleProvider 和 VolumeSampleProvider【英文标题】:NAudio: Using MixingSampleProvider correctly with VolumeSampleProvider 【发布时间】:2016-06-11 16:54:19 【问题描述】:

我一直在使用 NAudio “使用 NAudio 播放并忘记音频”教程(感谢 Mark 提供了这个很棒的实用程序!)如下所示: http://mark-dot-net.blogspot.nl/2014/02/fire-and-forget-audio-playback-with.html

我设法将 VolumeSampleProvider 添加到它,使用 MixingSampleProvider 作为输入。但是,当我现在连续播放两个声音时,第一个声音总是会获得第二个声音的音量,即使第一个声音已经在播放。

所以我的问题是:如何为每个声音添加具有单独音量的声音?

这是我用的:

        mixer = new MixingSampleProvider(waveformat);
        mixer.ReadFully = true;
        volumeProvider = new VolumeSampleProvider(mixer);
        panProvider = new PanningSampleProvider(volumeProvider);
        outputDevice.Init(panProvider);
        outputDevice.Play();

【问题讨论】:

【参考方案1】:

我意识到(感谢 itsmatt)实现这项工作的唯一方法是让混音器保持独立,并在将每个 CachedSound 的声像和音量添加到混音器之前单独调整它。因此,我需要重写 CachedSoundSampleProvider,使用平移和音量作为额外的输入参数。

这是新的构造函数:

    public CachedSoundSampleProvider(CachedSound cachedSound, float volume = 1, float pan = 0)
    
        this.cachedSound = cachedSound;
        LeftVolume = volume * (0.5f - pan / 2);
        RightVolume = volume * (0.5f + pan / 2);
    

这是新的 Read() 函数:

    public int Read(float[] buffer, int offset, int count)
    
        long availableSamples = cachedSound.AudioData.Length - position;
        long samplesToCopy = Math.Min(availableSamples, count);

        int destOffset = offset;
        for (int sourceSample = 0; sourceSample < samplesToCopy; sourceSample += 2)
        
            float outL = cachedSound.AudioData[position + sourceSample + 0];
            float outR = cachedSound.AudioData[position + sourceSample + 1];

            buffer[destOffset + 0] = outL * LeftVolume;
            buffer[destOffset + 1] = outR * RightVolume;
            destOffset += 2;
        

        position += samplesToCopy;
        return (int)samplesToCopy;
    

【讨论】:

【参考方案2】:

我不能 100% 确定你在问什么,我不知道你是否已经解决了这个问题,但这是我对此的看法。

ISampleProvider 对象通过Read() 方法向其源ISampleProvider 进行“推卸责任”游戏。最终,有人会实际读取音频字节。各个 ISampleProvider 类对字节执行任何操作。

MixingSampleProvider,例如,需要 N 个音频源……它们混合在一起。当调用Read() 时,它会迭代音频源并从每个音频源中读取count 字节。

将它传递给VolumeSampleProvider 将所有字节(来自各种来源)作为一个组处理......它说:

buffer[offset+n] *= volume;

这将全面调整字节...所以缓冲区中的每个字节都会通过 volume 乘数进行调整;

PanningSampleProvider 只是为立体声音频提供一个乘法器并相应地调整字节,与VolumeSampleProvider 做同样的事情。

如果您想单独处理音频源音量,则需要处理 MixingSampleProvider 的上游。本质上,您传递给 MixingSampleProvider 的东西需要能够独立调整它们的音量。

如果您将一堆SampleChannel 对象传递给您的MixingSampleProvider...,您可以完成独立的音量调节。 Samplechannel 类包含一个 VolumeSampleProvider 对象并提供一个 Volume 属性,允许用户设置该 VolumeSampleProvider 对象的音量。

SampleChannel 还包含一个MeteringSampleProvider,它提供给定时间段内最大样本值的报告。它会引发一个事件,为您提供这些值的数组,每个通道一个。

【讨论】:

亲爱的马特,感谢您的回答。我将不得不仔细研究 SampleChannel 类,看看我是否可以让它工作。自从我上次编写此代码以来已经有一年多了,所以我想我不会很快知道。问候。

以上是关于NAudio:正确使用 MixingSampleProvider 和 VolumeSampleProvider的主要内容,如果未能解决你的问题,请参考以下文章

使用 NAudio 从麦克风录制声音。为啥不能正确地从列表中记录整个缓冲区?

Naudio:如何在不停止所有其他声音的情况下正确删除 AsioOut 播放的几种声音中的一种?

使用 NAudio 1.7+ 播放音频文件

无法使用 NAudio 获取音频的波形图像

NAudio MeteringSampleProvider 没有触发事件?

NAudio - wav 的流式字节 [] 使其播放缓慢