diff --git a/src/Collections/Artemis.Plugins.Audio/DataModelExpansion/DataModels/PlaybackVolumeDataModel.cs b/src/Collections/Artemis.Plugins.Audio/DataModelExpansion/DataModels/PlaybackVolumeDataModel.cs index 21fa96aa..e1a8ce7c 100644 --- a/src/Collections/Artemis.Plugins.Audio/DataModelExpansion/DataModels/PlaybackVolumeDataModel.cs +++ b/src/Collections/Artemis.Plugins.Audio/DataModelExpansion/DataModels/PlaybackVolumeDataModel.cs @@ -2,12 +2,13 @@ using Artemis.Core; using Artemis.Core.Modules; using NAudio.CoreAudioApi; +using NAudio.CoreAudioApi.Interfaces; namespace Artemis.Plugins.Audio.DataModelExpansion.DataModels { public class PlaybackVolumeDataModel : DataModel { - [DataModelProperty(Description ="Name of the current playback device.")] + [DataModelProperty(Description = "Name of the current playback device.")] public string DefaultDeviceName { get; set; } [DataModelProperty(Description = "Channel count of the the current playback device.")] public int ChannelCount { get; set; } @@ -32,6 +33,7 @@ public class PlaybackVolumeDataModel : DataModel [DataModelProperty(Description = "Event triggered when current playback device master volume is changed.")] public DataModelEvent VolumeChanged { get; set; } = new DataModelEvent(); public ChannelsDataModel Channels { get; set; } = new ChannelsDataModel(); + public SessionsDataModel Sessions { get; set; } = new SessionsDataModel(); public void Reset() { @@ -49,6 +51,17 @@ public void Reset() } public class ChannelsDataModel : DataModel { } + + public class SessionsDataModel : DataModel { } + + public class SessionDataModel : DataModel + { + public string Name { get; set; } + public AudioSessionState? State { get; set; } + public float PeakVolume { get; set; } + public float PeakVolumeNormalized { get; set; } + } + public class ChannelDataModel : DataModel { public int ChannelIndex { get; set; } diff --git a/src/Collections/Artemis.Plugins.Audio/DataModelExpansion/PlaybackVolumeModule.cs b/src/Collections/Artemis.Plugins.Audio/DataModelExpansion/PlaybackVolumeModule.cs index f195b89d..f161b22a 100644 --- a/src/Collections/Artemis.Plugins.Audio/DataModelExpansion/PlaybackVolumeModule.cs +++ b/src/Collections/Artemis.Plugins.Audio/DataModelExpansion/PlaybackVolumeModule.cs @@ -89,10 +89,11 @@ private void UpdatePeakVolume(double deltaTime) // Update Main volume Peak lock (_audioEventLock) // To avoid query an Device/EndPoint that is not the current device anymore or has more or less channels { + // Absolute master peak volume - float peakVolumeNormalized = _playbackDevice?.AudioMeterInformation.MasterPeakValue ?? 0f; + float peakVolumeNormalized = (float) _playbackDevice?.AudioMeterInformation.MasterPeakValue; - // Don't update datamodel if not neeeded + // Don't update datamodel if not needed if (Math.Abs(_lastMasterPeakVolumeNormalized - peakVolumeNormalized) < 0.00001f) return; @@ -119,6 +120,58 @@ private void UpdatePeakVolume(double deltaTime) channelDataModel.Value.PeakVolumeNormalized = channelsVolumeNormalized[i]; channelDataModel.Value.PeakVolume = channelsVolumeNormalized[i] * 100f; } + + // Populate Sessions DataModel. Note that if the node don't exists, conditions using these values will become invalid + SessionCollection audioSessions = _playbackDevice?.AudioSessionManager.Sessions; + for (int i = 0; i < audioSessions.Count; i++) + { + AudioSessionControl session = audioSessions[i]; + + // Don't waste resources parsing system sessions data + if (session.IsSystemSoundsSession) + continue; + + // Span Code thanks to Darth Affe https://github.com/DarthAffe + ReadOnlySpan data = session.GetSessionInstanceIdentifier.AsSpan(); + int pidStart = data.LastIndexOf("%") + 2; + int nameStart = data.LastIndexOf("\\") + 1; + + ReadOnlySpan nameSlice = data.Slice(nameStart); + int nameEnd = nameSlice.LastIndexOf("."); + + // To avoid crashed with exe name that contains '.' character + string name = nameSlice.Slice(0, nameEnd).ToString().Replace('.', ' '); + + if (!uint.TryParse(data.Slice(pidStart), out uint _)) + continue; + + // Ignore sessions without name. It may be a valid session but without a name it is useless for conditions system + if (string.IsNullOrEmpty(name)) + continue; + + // Don't remove unused sessions as these becomes just inactive for a while. It is faster to keep them than scan and remove them. + if (DataModel.Sessions.TryGetDynamicChild(name, out DynamicChild sessionDataModel)) + { + sessionDataModel.Value.Name = name; + sessionDataModel.Value.State = session.State; + sessionDataModel.Value.PeakVolume = session.AudioMeterInformation.MasterPeakValue * 100; + sessionDataModel.Value.PeakVolumeNormalized = session.AudioMeterInformation.MasterPeakValue; + } + else + { + DataModel.Sessions.AddDynamicChild( + name, + new SessionDataModel() + { + Name = name, + State = session.State, + PeakVolume = session.AudioMeterInformation.MasterPeakValue * 100, + PeakVolumeNormalized = session.AudioMeterInformation.MasterPeakValue + }, + name + ); + } + } } }