private void AudioEndpointVolume_OnVolumeNotification(NAudio.CoreAudioApi.AudioVolumeNotificationData data) { _Master.Volume = data.MasterVolume; int ChannelCount = data.ChannelVolume.Count(); for (int iChannel = 0; iChannel < ChannelCount; ++iChannel) { NAudio.CoreAudioApi.AudioEndpointVolumeChannel Channel = Device.AudioEndpointVolume.Channels[iChannel]; _Channels[iChannel].Volume = Math.Round(Channel.VolumeLevelScalar * 100.0); } OnPropertyChanged("Master"); OnPropertyChanged("Channels"); }
static int correcting; // = 0; async void VolumeChangedHandler(NAudio.CoreAudioApi.AudioVolumeNotificationData data) { var oldVol = Volume; double newVol = data.MasterVolume * 100; if (Math.Abs(newVol - Target) <= SmallVolumeHysterisis) { if (Taskmaster.ShowInaction) { Log.Verbose("<Microphone> Volume change too small ({VolumeChange:N1}%) to act on.", Math.Abs(newVol - Target)); } return; } if (Taskmaster.Trace) { Log.Verbose("<Microphone> Volume changed from {OldVolume:N1}% to {NewVolume:N1}%", oldVol, newVol); } // This is a light HYSTERISIS limiter in case someone is sliding a volume bar around, // we act on it only once every [AdjustDelay] ms. // HOPEFULLY there are no edge cases with this triggering just before last adjustment // and the notification for the last adjustment coming slightly before. Seems super unlikely tho. // TODO: Delay this even more if volume is changed ~2 seconds before we try to do so. if (Math.Abs(newVol - Target) >= VolumeHysterisis) // Volume != Target for double { if (Taskmaster.Trace) { Log.Verbose("<Microphone> DEBUG: Volume changed = [{OldVolume:N1} -> {NewVolume:N1}], Off.Target: {VolumeOffset:N1}", oldVol, newVol, Math.Abs(newVol - Target)); } if (Atomic.Lock(ref correcting)) { try { await System.Threading.Tasks.Task.Delay(AdjustDelay); // actual hysterisis, this should be cancellable oldVol = Control.Percent; Log.Information("<Microphone> Correcting volume from {OldVolume:N1} to {NewVolume:N1}", oldVol, Target); Volume = Target; Corrections += 1; micstatsdirty = true; VolumeChanged?.Invoke(this, new VolumeChangedEventArgs { Old = oldVol, New = Target, Corrections = Corrections }); } catch { throw; } // required for finally to be guaranteed finally { Atomic.Unlock(ref correcting); } } else { if (Taskmaster.Trace) { Log.Verbose("<Microphone> DEBUG CorrectionAlreadyQueued"); } } } else { if (Taskmaster.Trace) { Log.Verbose("<Microphone> DEBUG NotCorrected"); } } }