Как реализовать предварительный микшер многоканального звука в .NET

Я хотел бы использовать C # для реализации приложения, которое может одновременно воспроизводить несколько аудиопотоков. Арахис - теперь самое интересное: предполагая, что каждый поток является одноканальным (моно), я хочу отрегулировать громкость для каждого динамика (5.1 или даже 7.1) для каждого потока отдельно. Я могу использовать микшер Windows для этого, но проблема в том, что есть только один микшер, и я хочу настроить его для каждого потока отдельно. Есть идеи, как это реализовать?

Мое первое предположение заключалось в том, чтобы мультиплексировать поток восемь раз (7.1), применить уровень громкости для каждого «канала», а затем отправить его в микшер Windows, который, например, выровнен для всех каналов на уровне 80%. Знаете ли вы какие-либо библиотеки, которые могут поддерживать такой вариант использования?

AFAIK bass и fmod не могут этого сделать, но поправьте меня, если я ошибаюсь. В качестве альтернативы я думал о том, чтобы взломать XNA для этого: использовать вектор, который описывает положение потока, относящегося к слушателю, и использовать его для применения компенсации громкости ... просто бессвязный треп.

(и, пожалуйста, не указывайте мне на какие-то идеи C ++ / WinAPI по этому поводу, этот проект сейчас не стоит изучать другой язык.)


person Marc Wittke    schedule 26.10.2009    source источник
comment
Возможно, одна идея пришла мне в голову: совершенно нормально требовать, чтобы доступные звуковые файлы были изначально импортированы и перекодированы в своего рода звуковой банк. Формат vorbis поддерживает до 255 каналов, и имеется (возможно, заброшенная) библиотека: vorbisdotnet.sourceforge.net   -  person Marc Wittke    schedule 26.10.2009
comment
Немного натыкаясь: я нашел библиотеку NAudio на Codeplex. Хорошая штука ...   -  person Marc Wittke    schedule 27.10.2009


Ответы (1)


В итоге получилось: 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);
    }
}
person Marc Wittke    schedule 31.10.2009