如何在.net中实现多通道音频预混音器

发布于 2024-08-09 03:13:38 字数 423 浏览 3 评论 0原文

我想使用 C# 实现一个可以同时播放多个音频流的应用程序。花生 - 现在有趣的部分:假设每个流都是单通道(单声道),我想分别调整每个流的每个扬声器(5.1 甚至 7.1)的音量。我可以使用 Windows 混音器来执行此操作,但问题是,只有一个混音器,我想为每个流单独调整它。有什么想法如何实施吗?

我的第一个猜测是将流复用八次 (7.1),为每个“通道”应用音量级别,然后将其发送到 Windows 混音器,例如,所有通道的音量级别都为 80%。您知道有哪些库可能支持此类用例吗?

AFAIK bass 和 fmod 不能做到这一点,但如果我错了,请纠正我。作为替代方案,我正在考虑破解 XNA:使用一个向量来描述与侦听器相关的流的位置,并使用它来应用音量补偿......只是漫谈。

(请不要向我指出一些关于此的 C++/WinAPI 想法,这个项目现在不值得学习另一种语言。)

I'd like to use C# to implement an application that can play multiple audio streams at the same time. Peanuts - now the interesting part: assuming every stream is single channel (mono) I want to adjust the volume for every speaker (5.1 or even 7.1) for every stream separately. I can use the windows mixer to do this, but the problem is, that there is only one mixer and I want to adjust this for every stream separately. Any ideas how to implement this?

My first guess was to multiplex the stream eight times (7.1), apply the volume level for every "channel" and then send it to the windows mixer, which is leveled for all channels at 80% for example. Do you know any libraries that might support such use case?

AFAIK bass and fmod cannot do this, but correct me if I'm wrong. As alternative I was thinking of hacking the XNA for this: using a vector that describes the position of the stream related to the listener and using this to apply the volume compensation... just ramblings.

(and please don't point me to some C++/WinAPI ideas on this, this project is not worth to learn another language now.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

永言不败 2024-08-16 03:13:38

终于明白了:bass.dll 允许使用方法 BassMix.BASS_Mixer_ChannelSetMatrix(int streamHandle, float[,]volumeMatrix) 分别应用矩阵作为每个扬声器的音量设置。您可以在此处查看示例,他们使用此来将立体声流向上混音到四个扬声器。下面是我为解决问题而创建的完整课程。

public class SeparateVolumeLevelPlayer : IDisposable
{
    private readonly int outputMixerStream;
    private readonly int inputStream;
    private readonly int numberOfSpeakers;

    public SeparateVolumeLevelPlayer(string fileName, int numberOfSpeakers)
    {
        this.numberOfSpeakers = numberOfSpeakers;
        outputMixerStream = BassMix.BASS_Mixer_StreamCreate(44100, numberOfSpeakers, BASSFlag.BASS_MIXER_MATRIX);
        ThrowOnError();

        // create a stream from the media file
        inputStream = Bass.BASS_StreamCreateFile(fileName, 0L, 0L, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_MIXER_MATRIX | BASSFlag.BASS_SAMPLE_MONO);
        ThrowOnError();

        // add the stream to the mixer
        BassMix.BASS_Mixer_StreamAddChannel(outputMixerStream, inputStream, BASSFlag.BASS_MIXER_MATRIX);
        ThrowOnError();
    }

    public void Play()
    {
        // start playback of the mixed streams
        Bass.BASS_ChannelPlay(outputMixerStream, false);
        ThrowOnError();
    }

    public void SetVolume(float[] volumeValues)
    {
        if (volumeValues == null) 
        {
            throw new ArgumentNullException("volumeValues");
        }

        if (volumeValues.Length != numberOfSpeakers)
        {
            string message =
                string.Format("You must pass a volume level for every speaker. You provided {0} values for {1} speakers",
                                            volumeValues.Length, numberOfSpeakers);
            throw  new ArgumentException(message);
        }

        var volumeMatrix = new float[numberOfSpeakers, 1];

        for (int i = 0; i < numberOfSpeakers; i++)
        {
            volumeMatrix[i, 0] = volumeValues[i];
        }

        // adjust the volume using the matrix
        BassMix.BASS_Mixer_ChannelSetMatrix(inputStream, volumeMatrix);
        ThrowOnError();

    }

    private static void ThrowOnError()
    {
        BASSError err = Bass.BASS_ErrorGetCode();
        if (err != BASSError.BASS_OK)
        {
            throw new ApplicationException(string.Format("bass.dll reported {0}.", err));
        }
    }

    public void Dispose()
    {
        Bass.BASS_StreamFree(inputStream);
        Bass.BASS_StreamFree(outputMixerStream);
    }
}

Finally got it: bass.dll allows to apply a matrix as volume settings for every speaker separately using the method BassMix.BASS_Mixer_ChannelSetMatrix(int streamHandle, float[,] volumeMatrix). You can see a sample here, they're using this to upmix a stereo stream to four speakers. Below the full class I created to solve my problem.

public class SeparateVolumeLevelPlayer : IDisposable
{
    private readonly int outputMixerStream;
    private readonly int inputStream;
    private readonly int numberOfSpeakers;

    public SeparateVolumeLevelPlayer(string fileName, int numberOfSpeakers)
    {
        this.numberOfSpeakers = numberOfSpeakers;
        outputMixerStream = BassMix.BASS_Mixer_StreamCreate(44100, numberOfSpeakers, BASSFlag.BASS_MIXER_MATRIX);
        ThrowOnError();

        // create a stream from the media file
        inputStream = Bass.BASS_StreamCreateFile(fileName, 0L, 0L, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_MIXER_MATRIX | BASSFlag.BASS_SAMPLE_MONO);
        ThrowOnError();

        // add the stream to the mixer
        BassMix.BASS_Mixer_StreamAddChannel(outputMixerStream, inputStream, BASSFlag.BASS_MIXER_MATRIX);
        ThrowOnError();
    }

    public void Play()
    {
        // start playback of the mixed streams
        Bass.BASS_ChannelPlay(outputMixerStream, false);
        ThrowOnError();
    }

    public void SetVolume(float[] volumeValues)
    {
        if (volumeValues == null) 
        {
            throw new ArgumentNullException("volumeValues");
        }

        if (volumeValues.Length != numberOfSpeakers)
        {
            string message =
                string.Format("You must pass a volume level for every speaker. You provided {0} values for {1} speakers",
                                            volumeValues.Length, numberOfSpeakers);
            throw  new ArgumentException(message);
        }

        var volumeMatrix = new float[numberOfSpeakers, 1];

        for (int i = 0; i < numberOfSpeakers; i++)
        {
            volumeMatrix[i, 0] = volumeValues[i];
        }

        // adjust the volume using the matrix
        BassMix.BASS_Mixer_ChannelSetMatrix(inputStream, volumeMatrix);
        ThrowOnError();

    }

    private static void ThrowOnError()
    {
        BASSError err = Bass.BASS_ErrorGetCode();
        if (err != BASSError.BASS_OK)
        {
            throw new ApplicationException(string.Format("bass.dll reported {0}.", err));
        }
    }

    public void Dispose()
    {
        Bass.BASS_StreamFree(inputStream);
        Bass.BASS_StreamFree(outputMixerStream);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文