How to realize a multi channel audio pre-mixer in .net
Asked Answered
M

1

4

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.)

Maneating answered 26/10, 2009 at 14:2 Comment(2)
Maybe one idea that just arrived my brain: It is totally okay to require the available sound files to be imported and transcoded initially into a kind of a sound bank. The vorbis format supports up to 255 channels and there is a (possibly abandoned) lib available: vorbisdotnet.sourceforge.netManeating
A little bit of bumping: I found the NAudio library on Codeplex. Nice stuff...Maneating
M
2

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);
    }
}
Maneating answered 31/10, 2009 at 17:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.