Change wave file left and right volumes separately in C#

1.8k Views Asked by At

I am working with Wave file (Stereo file) processing, where i want to set my system volume to maximum and alter the sounds of wave file using the scroll bar. i.e. System volume should be 100%, and i want to play my wave file in the range of 60dB to 100dB, and always wave file left channel and right channel volumes are not going to same. (Left channel may play 60dB and right channel may play at 70dB).

To achieve this i am using CSCore audio library as my project is developed on C#.

// Creating the Wave source from the source file
IWaveSource waveSource = CodecFactory.Instance.GetCodec("C:\\SampleWave.wav");

WasAPIOut mWasAPIOutObj = new WasapiOut();

AudioEndOutputVolume aeovObj = AudioEndpointVolume.FromDevice(_soundOut.Device);

// Getting the maximum and minimum volume range of Wave source.
aeovObj.GetVolumeRange(out vMinDB, out vMaxDB, out vIncrDB);

// Setting the System Master Volume to maximum (100%)
aepv.SetMasterVolumeLevel(vMaxDB, Guid.Empty);

// I want to play the wave file in a loop, so pushing the wave file to loop stream
LoopStream stream = new LoopStream(waveSource);

mWasAPIOutObj.Initialize(stream);

Before i play the wave file i want to set my wave file left channel to 60 dB and right channel to 70dB and start playing.

mWasAPIOutObj.Play();

mWasAPIOutObj.Volume property is taking the single value ranging from 0.0 to 1.0 only. Does not talking about any channel here.

Do i need to set the volume at WaveSource level?

I have been through NAudio audio library as well, but could not get the solution around this.

Stacktrace for ArgumentOutOfRange Exception:

   at CSCore.SampleSourceBase.Read(Single[] buffer, Int32 offset, Int32 count)
   at SampleTool.VPWaveSource.Read(Single[] buffer, Int32 offset, Int32 count) in c:\****\*****\Projects\Sample Tool\Try\SampleTool\SampleTool\SoundManager.cs:line 308
   at CSCore.Streams.SampleConverter.SampleToIeeeFloat32.Read(Byte[] buffer, Int32 offset, Int32 count)
   at CSCore.WaveAggregatorBase.Read(Byte[] buffer, Int32 offset, Int32 count)
   at CSCore.Streams.LoopStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at CSCore.SoundOut.WasapiOut.FeedBuffer(AudioRenderClient renderClient, Byte[] buffer, Int32 numFramesCount, Int32 frameSize)
   at CSCore.SoundOut.WasapiOut.PlaybackProc(Object playbackStartedEventWaithandle)

Can any one help me in solving this problem.

Thanks in advance.

2

There are 2 best solutions below

3
On BEST ANSWER

You could simply create a new source:

class ChannelVolumeControlSource : SampleSourceBase
{
    public ChannelVolumeControlSource(ISampleSource source) : base(source)
    {
        if(source.WaveFormat.Channels != 2) 
            throw new ArgumentException("Source must have two channels.", "source");
    }

    //todo: add some validation -> make sure only values between 0 and 1 are allowed.
    public float VolumeLeft { get; set; }
    public float VolumeRight { get; set; }

    public override int Read(float[] buffer, int offset, int count)
    {
        int read = base.Read(buffer, offset, count);
        for (int i = 0; i < read; i+=2)
        {
            buffer[i] *= VolumeLeft;
            buffer[i + 1] *= VolumeRight;
        }

        return read;
    }
}

Usage:

    var channelVolumeSourceControl = new ChannelVolumeControlSource(stream.ToSampleSource());
    mWasAPIOutObj.Initialize(channelVolumeSourceControl.ToWaveSource());

    //control the volume during playback by setting the volume properties
    channelVolumeSourceControl.VolumeLeft = 0.5f; //left channel 50%
    channelVolumeSourceControl.VolumeRight = 1.0f; //right channel 100%
0
On

I'm not sure this code compiles, but I guess at your intent. The SetChannelVolumeLevelNative API lets you set dB settings per channel rather than SetMasterVolume.