Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion EarTrumpet/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ private void CompleteStartup()
Settings.AbsoluteVolumeUpHotkeyTyped += AbsoluteVolumeIncrement;
Settings.AbsoluteVolumeDownHotkeyTyped += AbsoluteVolumeDecrement;
Settings.RegisterHotkeys();
Settings.UseLogarithmicVolumeChanged += (_, __) => UpdateTrayTooltip();

_trayIcon.PrimaryInvoke += (_, type) => _flyoutViewModel.OpenFlyout(type);
_trayIcon.SecondaryInvoke += (_, args) => _trayIcon.ShowContextMenu(GetTrayContextMenuItems(), args.Point);
Expand Down Expand Up @@ -187,7 +188,8 @@ private void TrayIconScrolled(object _, int wheelDelta)
{
if (Settings.UseScrollWheelInTray && (!Settings.UseGlobalMouseWheelHook || _flyoutViewModel.State == FlyoutViewState.Hidden))
{
CollectionViewModel.Default?.IncrementVolume(Math.Sign(wheelDelta) * 2);
CollectionViewModel.Default?.IncrementVolume(
Math.Sign(wheelDelta) * (Settings.UseLogarithmicVolume ? 0.2f : 2.0f));
Comment thread
riverar marked this conversation as resolved.
}
}

Expand Down
17 changes: 16 additions & 1 deletion EarTrumpet/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace EarTrumpet;
public class AppSettings
{
public event EventHandler<bool> UseLegacyIconChanged;
public event EventHandler<EventArgs> UseLogarithmicVolumeChanged;
public event Action FlyoutHotkeyTyped;
public event Action MixerHotkeyTyped;
public event Action SettingsHotkeyTyped;
Expand Down Expand Up @@ -160,7 +161,21 @@ public bool IsTelemetryEnabled
public bool UseLogarithmicVolume
{
get => _settings.Get("UseLogarithmicVolume", false);
set => _settings.Set("UseLogarithmicVolume", value);
set
{
_settings.Set("UseLogarithmicVolume", value);
UseLogarithmicVolumeChanged?.Invoke(this, new EventArgs());
}
}

public float LogarithmicVolumeMinDb
{
get => _settings.Get("LogarithmicVolumeMinDb", -40f);
set
{
_settings.Set("LogarithmicVolumeMinDb", value);
UseLogarithmicVolumeChanged?.Invoke(this, new EventArgs());
}
}

public WINDOWPLACEMENT? FullMixerWindowPlacement
Expand Down
12 changes: 1 addition & 11 deletions EarTrumpet/DataModel/Audio/Mocks/AudioDevice.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using EarTrumpet.DataModel.WindowsAudio;
using EarTrumpet.DataModel.WindowsAudio.Internal;
using EarTrumpet.Extensions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
Expand Down Expand Up @@ -42,18 +41,9 @@ public bool IsMuted
private float _volume = 1;
public float Volume
{
get
{
return App.Settings.UseLogarithmicVolume ? _volume.ToDisplayVolume() : _volume;
}

get => _volume;
set
{
if (App.Settings.UseLogarithmicVolume)
{
value = value.ToLogVolume();
}

if (_volume != value)
{
_volume = value;
Expand Down
8 changes: 2 additions & 6 deletions EarTrumpet/DataModel/Audio/Mocks/AudioDeviceSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,12 @@ public bool IsMuted
private float _volume = 1;
public float Volume
{
get
{
return App.Settings.UseLogarithmicVolume ? _volume.ToDisplayVolume() : _volume;
}

get => _volume;
set
{
if (App.Settings.UseLogarithmicVolume)
{
value = value.ToLogVolume();
value = value.LinearToLogNormalized();
}

if (_volume != value)
Expand Down
73 changes: 55 additions & 18 deletions EarTrumpet/DataModel/WindowsAudio/Internal/AudioDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public class AudioDevice : BindableBase, IAudioEndpointVolumeCallback, IAudioDev
private readonly WeakReference<IAudioDeviceManager> _deviceManager;
private readonly string _id;
private readonly AudioDeviceChannelCollection _channels;
private readonly float _deviceVolumeMinDb;
private readonly float _deviceVolumeMaxDb;
private IMMDevice _device;
private string _displayName;
private string _iconPath;
Expand All @@ -51,8 +53,16 @@ public AudioDevice(IAudioDeviceManager deviceManager, IMMDevice device, Dispatch
{
_deviceVolume = device.Activate<IAudioEndpointVolume>();
_deviceVolume.RegisterControlChangeNotify(this);
_deviceVolume.GetMasterVolumeLevelScalar(out _volume);
if (App.Settings.UseLogarithmicVolume)
{
_deviceVolume.GetMasterVolumeLevel(out _volume);
}
else
{
_deviceVolume.GetMasterVolumeLevelScalar(out _volume);
}
_deviceVolume.GetMute(out var isMuted);
_deviceVolume.GetVolumeRange(out _deviceVolumeMinDb, out _deviceVolumeMaxDb, out _);
_isMuted = isMuted;
_isRegistered = true;
_meter = device.Activate<IAudioMeterInformation>();
Expand All @@ -67,6 +77,19 @@ public AudioDevice(IAudioDeviceManager deviceManager, IMMDevice device, Dispatch
}

ReadProperties();

App.Settings.UseLogarithmicVolumeChanged += (sender, args) =>
{
if (App.Settings.UseLogarithmicVolume)
{
_deviceVolume.GetMasterVolumeLevel(out _volume);
}
else
{
_deviceVolume.GetMasterVolumeLevelScalar(out _volume);
}
RaisePropertyChanged(nameof(Volume));
};
}

~AudioDevice()
Expand All @@ -87,6 +110,10 @@ public AudioDevice(IAudioDeviceManager deviceManager, IMMDevice device, Dispatch
public unsafe void OnNotify(AUDIO_VOLUME_NOTIFICATION_DATA* pNotify)
{
_volume = (*pNotify).fMasterVolume;
if (App.Settings.UseLogarithmicVolume)
{
_deviceVolume.GetMasterVolumeLevel(out _volume);
}
_isMuted = (*pNotify).bMuted != 0;

_channels.OnNotify((nint)pNotify, *pNotify);
Expand All @@ -100,29 +127,31 @@ public unsafe void OnNotify(AUDIO_VOLUME_NOTIFICATION_DATA* pNotify)

public float Volume
{
get => App.Settings.UseLogarithmicVolume ? _volume.ToDisplayVolume() : _volume;
get => _volume;
set
{
value = value.Bound(0, 1f);

if (App.Settings.UseLogarithmicVolume)
try
{
value = value.ToLogVolume();
}

if (_volume != value)
{
try
if (App.Settings.UseLogarithmicVolume)
{
value = value.Bound(App.Settings.LogarithmicVolumeMinDb, 0);
value = value.Bound(_deviceVolumeMinDb, _deviceVolumeMaxDb);
_volume = value;
_deviceVolume.SetMasterVolumeLevelScalar(value, Guid.Empty);
_deviceVolume.SetMasterVolumeLevel(value, Guid.Empty);
IsMuted = value <= App.Settings.LogarithmicVolumeMinDb;
}
catch (Exception ex) when (ex.Is(HRESULT.AUDCLNT_E_DEVICE_INVALIDATED))
else
{
// Expected in some cases.
value = value.Bound(0, 1f);
_volume = value;
_deviceVolume.SetMasterVolumeLevelScalar(value, Guid.Empty);
IsMuted = _volume.ToVolumeInt() == 0;
}

IsMuted = App.Settings.UseLogarithmicVolume ? _volume <= (1 / 100f).ToLogVolume() : _volume.ToVolumeInt() == 0;
}
catch (Exception ex) when (ex.Is(HRESULT.AUDCLNT_E_DEVICE_INVALIDATED))
{
// Expected in some cases.
// Known: when the master volume in dB is beyond device capabilities
}
}
}
Expand Down Expand Up @@ -176,8 +205,16 @@ public IAudioDeviceManager Parent
public void UpdatePeakValue()
{
var newValues = Helpers.ReadPeakValues(_meter);
PeakValue1 = newValues[0];
PeakValue2 = newValues[1];
if (App.Settings.UseLogarithmicVolume)
{
PeakValue1 = newValues[0].LinearToLogNormalized();
PeakValue2 = newValues[1].LinearToLogNormalized();
}
else
{
PeakValue1 = newValues[0];
PeakValue2 = newValues[1];
}

foreach(var session in _sessions.Sessions.ToArray())
{
Expand Down
21 changes: 19 additions & 2 deletions EarTrumpet/DataModel/WindowsAudio/Internal/AudioDeviceChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@ public AudioDeviceChannel(IAudioEndpointVolume deviceVolume, uint index)
{
_index = index;
_deviceVolume = deviceVolume;
_deviceVolume.GetChannelVolumeLevelScalar(index, out _level);
if (App.Settings.UseLogarithmicVolume)
{
_deviceVolume.GetChannelVolumeLevel(index, out _level);
}
else
{
_deviceVolume.GetChannelVolumeLevelScalar(index, out _level);
}
}

public float Level
Expand All @@ -25,7 +32,17 @@ public float Level
if (_level != value)
{
var context = Guid.Empty;
unsafe { _deviceVolume.SetChannelVolumeLevelScalar(_index, value, &context); }
unsafe
{
if (App.Settings.UseLogarithmicVolume)
{
_deviceVolume.SetChannelVolumeLevel(_index, value, &context);
}
else
{
_deviceVolume.SetChannelVolumeLevelScalar(_index, value, &context);
}
}

_level = value;
RaisePropertyChanged(nameof(Level));
Expand Down
56 changes: 39 additions & 17 deletions EarTrumpet/DataModel/WindowsAudio/Internal/AudioDeviceSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using Windows.Win32;
using Windows.Win32.Media.Audio;
Expand All @@ -33,29 +34,32 @@ public IAudioDevice Parent

public float Volume
{
get => App.Settings.UseLogarithmicVolume ? _volume.ToDisplayVolume() : _volume;
get => App.Settings.UseLogarithmicVolume
? _volume.LinearToLog()
: _volume;
set
{
value = value.Bound(0, 1f);

if (App.Settings.UseLogarithmicVolume)
{
value = value.ToLogVolume();
}

if (_volume != value)
try
{
try
if (App.Settings.UseLogarithmicVolume)
{
value = value.Bound(App.Settings.LogarithmicVolumeMinDb, 0);
// We must convert manually here because sessions use linear volume.
_simpleVolume.SetMasterVolume(value.LogToLinear(), Guid.Empty);
_volume = value;
_simpleVolume.SetMasterVolume(value, Guid.Empty);
IsMuted = value <= App.Settings.LogarithmicVolumeMinDb;
}
catch (Exception ex) when (ex.Is(HRESULT.AUDCLNT_E_DEVICE_INVALIDATED))
else
{
// Expected in some cases.
value = value.Bound(0, 1f);
_simpleVolume.SetMasterVolume(value, Guid.Empty);
_volume = value;
IsMuted = _volume.ToVolumeInt() == 0;
}

IsMuted = App.Settings.UseLogarithmicVolume ? _volume <= (1 / 100f).ToLogVolume() : _volume.ToVolumeInt() == 0;
}
catch (Exception ex) when (ex.Is(HRESULT.AUDCLNT_E_DEVICE_INVALIDATED))
{
// Expected in some cases.
}
}
}
Expand Down Expand Up @@ -189,6 +193,16 @@ public AudioDeviceSession(IAudioDevice parent, IAudioSessionControl session, Dis
SyncPersistedEndpoint(parent);
}
}

WeakEventManager<AppSettings, EventArgs>.AddHandler(
App.Settings,
nameof(AppSettings.UseLogarithmicVolumeChanged),
UseLogarithmicVolumeChangedHandler);
}

private void UseLogarithmicVolumeChangedHandler(object sender, EventArgs e)
{
RaisePropertyChanged(nameof(Volume));
}

~AudioDeviceSession()
Expand Down Expand Up @@ -242,8 +256,16 @@ public void MoveToDevice(string id, bool hide)
public void UpdatePeakValueBackground()
{
var newValues = Helpers.ReadPeakValues(_meter);
PeakValue1 = newValues[0];
PeakValue2 = newValues[1];
if (App.Settings.UseLogarithmicVolume)
{
PeakValue1 = newValues[0].LinearToLogNormalized();
PeakValue2 = newValues[1].LinearToLogNormalized();
}
else
{
PeakValue1 = newValues[0];
PeakValue2 = newValues[1];
}
}

private void ChooseDisplayName(string displayNameFromSession)
Expand Down
18 changes: 8 additions & 10 deletions EarTrumpet/Extensions/FloatExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ namespace EarTrumpet.Extensions;

public static class FloatExtensions
{
private const float CURVE_FACTOR = 5.757f;

public static int ToVolumeInt(this float val)
{
return Convert.ToInt32(Math.Round(val * 100, MidpointRounding.AwayFromZero));
Expand All @@ -16,13 +14,13 @@ public static float Bound(this float val, float min, float max)
return Math.Max(min, Math.Min(max, val));
}

public static float ToLogVolume(this float val)
{
return ((float)(Math.Exp(CURVE_FACTOR * val) / Math.Exp(CURVE_FACTOR))).Bound(0, 1f);
}
public static float LinearToLog(this float val) => (float)(20 * Math.Log10(val));
Comment thread
Dwscdv3 marked this conversation as resolved.

public static float ToDisplayVolume(this float val)
{
return ((float)(Math.Log(val * Math.Exp(CURVE_FACTOR)) / CURVE_FACTOR)).Bound(0, 1f);
}
public static float LinearToLogNormalized(this float val) =>
val == 0
? 0
: ((float)(20 * Math.Log10(val) / -App.Settings.LogarithmicVolumeMinDb + 1))
.Bound(0, 1f);

public static float LogToLinear(this float val) => (float)Math.Pow(10, val / 20);
}
Loading
Loading