Skip to content

Instantly share code, notes, and snippets.

@mkoertgen
Last active March 20, 2021 13:24
Show Gist options
  • Save mkoertgen/c4e1f788e793e77059b9 to your computer and use it in GitHub Desktop.
Save mkoertgen/c4e1f788e793e77059b9 to your computer and use it in GitHub Desktop.
Set audio balance using core audio api
public static class BalanceExtensions
{
// left=0, right=1
private const int LeftChan = 0;
private const int RightChan = 1;
/// <summary>
/// Gets the ratio of volume across the left and right speakers in a range between -1 (left) and 1 (right). The default is 0 (center).
/// </summary>
/// <param name="volume">The audio volume endpoint</param>
/// <returns></returns>
public static float GetBalance(this AudioEndpointVolume volume)
{
VerifyChannels(volume);
var masterVol = Math.Max(1e-6f, volume.MasterVolumeLevelScalar);
var leftVol = volume.Channels[LeftChan].VolumeLevelScalar;
var rightVol = volume.Channels[RightChan].VolumeLevelScalar;
var balance = (rightVol - leftVol)/masterVol;
return balance;
}
public static void SetBalance(this AudioEndpointVolume volume, float balance)
{
VerifyChannels(volume);
var safeBalance = Math.Max(-1, Math.Min(1, balance));
var masterVol = volume.MasterVolumeLevelScalar;
var rightVol = 1f + Math.Min(0f, safeBalance);
var leftVol = 1f - Math.Max(0f, safeBalance);
volume.Channels[LeftChan].VolumeLevelScalar = leftVol * masterVol;
volume.Channels[RightChan].VolumeLevelScalar = rightVol * masterVol;
}
public static AudioEndpointVolume GetDefaultVolumeEndpoint()
{
return new MMDeviceEnumerator()
.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia)
.AudioEndpointVolume;
}
private static void VerifyChannels(AudioEndpointVolume volume)
{
if (volume == null) throw new ArgumentNullException(nameof(volume));
if (volume.Channels.Count < 2)
throw new InvalidOperationException("The specified audio endpoint does not expose left/right volume channels");
}
}
@mkoertgen
Copy link
Author

Use with NAudio or roll your own wrapper for the Core Audio API

@mkoertgen
Copy link
Author

Here a simple test panning left-right:

[Test]//, Explicit]
public void BalanceTest()
{
    var volume = _device.AudioEndpointVolume;

    var duration = TimeSpan.FromSeconds(5);
    const int n = 50;

    var dt = (int)(duration.TotalMilliseconds/n);
    for (var i = 1; i<=n; i++)
    {
        var balance = (float) Math.Sin(2*i*Math.PI/n);
        volume.SetBalance(balance);

        Thread.Sleep(dt);

        var eps = 1e-6f / Math.Max(1e-6f, volume.MasterVolumeLevelScalar);
        volume.GetBalance().Should().BeApproximately(balance, eps);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment