// Update time-based state variables from prevInfo in smart way public void UpdateState(SoundSourceInfo prevInfo) { if (prevInfo == null) return; if (prevInfo._resetActive) { EffectiveStartDateTime = DateTime.MaxValue; EffectiveSilentDateTime = DateTime.MinValue; EffectiveStartDateTimeBackup = DateTime.MaxValue; EffectiveSilentDateTimeBackup = DateTime.MinValue; EmittedSilentDateTime = DateTime.MinValue; ContinuousEffectivePlayingStartTime = DateTime.MaxValue; EffectivePrevSoundDuration = TimeSpan.MaxValue; WasMaybeEffectivelyPlaying = false; WasEmmittingSound = false; WasActiveForAwhile = false; _resetActive = false; return; } else { this.EffectiveStartDateTimeBackup = prevInfo.EffectiveStartDateTimeBackup; this.EffectiveSilentDateTimeBackup = prevInfo.EffectiveSilentDateTimeBackup; } if (!this.EffectiveVolumeIsZero()) // If something is coming out of the speakers right now { if (this.EffectiveSilentDateTime != DateTime.MaxValue) // there is not silence { this.EffectiveSilentDateTimeBackup = this.EffectiveSilentDateTime; this.EffectiveStartDateTimeBackup = this.EffectiveStartDateTime; } this.EffectiveSilentDateTime = DateTime.MaxValue; // there is not silence this.EffectiveStartDateTime = new DateTime(Math.Min(this.EffectiveStartDateTime.Ticks, prevInfo.EffectiveStartDateTime.Ticks)); // sound started either now or earlier } else { this.EffectiveSilentDateTime = prevInfo.EffectiveVolumeIsZero() ? prevInfo.EffectiveSilentDateTime : DateTime.Now; // there is silence (or there was earlier) this.EffectiveStartDateTime = this.IsMaybeEffectivelyPlaying() ? new DateTime(Math.Min(this.EffectiveStartDateTime.Ticks, prevInfo.EffectiveStartDateTime.Ticks)) : DateTime.MaxValue; if (!this.IsMaybeEffectivelyPlaying() && prevInfo.WasMaybeEffectivelyPlaying) { this.EffectivePrevSoundDuration = DateTime.Now.Subtract(prevInfo.EffectiveStartDateTime); // if sound just stopped, record how long it was. If short enough, then pretend it never happened. // TODO: only do this if sound duration was short enough this.EffectiveSilentDateTime = this.EffectiveSilentDateTimeBackup; this.EffectiveStartDateTime = this.EffectiveStartDateTimeBackup; } } if (!this.EmittedVolumeIsZero()) { this.EmittedSilentDateTime = DateTime.MaxValue; } else { this.EmittedSilentDateTime = prevInfo.EffectiveVolumeIsZero() ? prevInfo.EffectiveSilentDateTime : DateTime.Now; } if (this.IsMaybeEffectivelyPlaying() != prevInfo.WasMaybeEffectivelyPlaying) { if (prevInfo.WasMaybeEffectivelyPlaying == false) this.ContinuousEffectivePlayingStartTime = DateTime.Now; else this.ContinuousEffectivePlayingStartTime = DateTime.MaxValue; } else { this.ContinuousEffectivePlayingStartTime = prevInfo.ContinuousEffectivePlayingStartTime; } if (this.Muted == true) { if (this.EffectiveSilentDateTime == DateTime.MaxValue) this.EffectiveSilentDateTime = DateTime.Now; } this.WasMaybeEffectivelyPlaying = this.IsMaybeEffectivelyPlaying(); this.WasActiveForAwhile = this.IsContinuouslyActiveForAwhile(); this.WasEmmittingSound = this.IsEmittingSound(); //System.Diagnostics.Debug.WriteLine(this.ToString()); }
// Update time-based state variables from prevInfo in smart way public void UpdateState(SoundSourceInfo prevInfo) { if (prevInfo == null) { return; } if (prevInfo._resetActive) { EffectiveStartDateTime = DateTime.MaxValue; EffectiveSilentDateTime = DateTime.MinValue; EffectiveStartDateTimeBackup = DateTime.MaxValue; EffectiveSilentDateTimeBackup = DateTime.MinValue; EmittedSilentDateTime = DateTime.MinValue; ContinuousEffectivePlayingStartTime = DateTime.MaxValue; EffectivePrevSoundDuration = TimeSpan.MaxValue; WasMaybeEffectivelyPlaying = false; WasEmmittingSound = false; WasActiveForAwhile = false; _resetActive = false; return; } else { this.EffectiveStartDateTimeBackup = prevInfo.EffectiveStartDateTimeBackup; this.EffectiveSilentDateTimeBackup = prevInfo.EffectiveSilentDateTimeBackup; } if (!this.EffectiveVolumeIsZero()) // If something is coming out of the speakers right now { if (this.EffectiveSilentDateTime != DateTime.MaxValue) // there is not silence { this.EffectiveSilentDateTimeBackup = this.EffectiveSilentDateTime; this.EffectiveStartDateTimeBackup = this.EffectiveStartDateTime; } this.EffectiveSilentDateTime = DateTime.MaxValue; // there is not silence this.EffectiveStartDateTime = new DateTime(Math.Min(this.EffectiveStartDateTime.Ticks, prevInfo.EffectiveStartDateTime.Ticks)); // sound started either now or earlier } else { this.EffectiveSilentDateTime = prevInfo.EffectiveVolumeIsZero() ? prevInfo.EffectiveSilentDateTime : DateTime.Now; // there is silence (or there was earlier) this.EffectiveStartDateTime = this.IsMaybeEffectivelyPlaying() ? new DateTime(Math.Min(this.EffectiveStartDateTime.Ticks, prevInfo.EffectiveStartDateTime.Ticks)) : DateTime.MaxValue; if (!this.IsMaybeEffectivelyPlaying() && prevInfo.WasMaybeEffectivelyPlaying) { this.EffectivePrevSoundDuration = DateTime.Now.Subtract(prevInfo.EffectiveStartDateTime); // if sound just stopped, record how long it was. If short enough, then pretend it never happened. // TODO: only do this if sound duration was short enough this.EffectiveSilentDateTime = this.EffectiveSilentDateTimeBackup; this.EffectiveStartDateTime = this.EffectiveStartDateTimeBackup; } } if (!this.EmittedVolumeIsZero()) { this.EmittedSilentDateTime = DateTime.MaxValue; } else { this.EmittedSilentDateTime = prevInfo.EffectiveVolumeIsZero() ? prevInfo.EffectiveSilentDateTime : DateTime.Now; } if (this.IsMaybeEffectivelyPlaying() != prevInfo.WasMaybeEffectivelyPlaying) { if (prevInfo.WasMaybeEffectivelyPlaying == false) { this.ContinuousEffectivePlayingStartTime = DateTime.Now; } else { this.ContinuousEffectivePlayingStartTime = DateTime.MaxValue; } } else { this.ContinuousEffectivePlayingStartTime = prevInfo.ContinuousEffectivePlayingStartTime; } if (this.Muted == true) { if (this.EffectiveSilentDateTime == DateTime.MaxValue) { this.EffectiveSilentDateTime = DateTime.Now; } } this.WasMaybeEffectivelyPlaying = this.IsMaybeEffectivelyPlaying(); this.WasActiveForAwhile = this.IsContinuouslyActiveForAwhile(); this.WasEmmittingSound = this.IsEmittingSound(); //System.Diagnostics.Debug.WriteLine(this.ToString()); }
// The main loop for sound monitoring thread. Runs forever. public static void Init() { #if WINDOWS WinCoreAudioApiSoundServer.WinCoreAudioApiSoundServer.InitMasterVolumeListener(_onMasterVolumeChange); #endif SoundSourceInfo[] oldSoundSourceInfos = new SoundSourceInfo[] { }; Dictionary <string, SoundSourceInfo> oldSoundSourceDict = new Dictionary <string, SoundSourceInfo>(); DateTime lastUpdateDateTime = DateTime.MinValue; DateTime procDictResetDateTime = DateTime.Now.AddMinutes(5); // When to regenerate the cache for process/window info // Continually get information about process sound volumes. Forward on // to callback (i.e. smart volume manager every five seconds or whenever something significant changes (including durations with/without sound)) long i = 0; while (true) { #if WINDOWS WinCoreAudioApiSoundServer.SoundInfo[] soundInfos = WinCoreAudioApiSoundServer.WinCoreAudioApiSoundServer.GetCurrentSoundInfo(); #else SoundInfo[] soundInfos = new List <SoundInfo>().ToArray(); #endif bool changeMade = false; List <SoundSourceInfo> soundSourceInfoList = new List <SoundSourceInfo>(); for (int latestSoundInfoIndex = 0; latestSoundInfoIndex < soundInfos.Length; latestSoundInfoIndex++) { SoundSourceInfo prevInfo = null; #if WINDOWS SoundSourceInfo latestInfo = new SoundSourceInfo(soundInfos[latestSoundInfoIndex]); #else SoundSourceInfo latestInfo = new SoundSourceInfo(); //TODO #endif if (oldSoundSourceDict.TryGetValue(latestInfo.SessionInstanceIdentifier, out prevInfo)) { latestInfo.UpdateState(prevInfo); if ( (latestInfo.WindowTitle != prevInfo.WindowTitle) || (latestInfo.DisplayName != prevInfo.DisplayName) || //(latestInfo.SessionIdentifier != prevInfo.SessionIdentifier) || (latestInfo.EffectiveVolumeIsZero() != prevInfo.EffectiveVolumeIsZero()) || (latestInfo.IconPath != prevInfo.IconPath) || (latestInfo.IsMaybeEffectivelyPlaying() != prevInfo.WasMaybeEffectivelyPlaying) || (latestInfo.IsEmittingSound() != prevInfo.WasEmmittingSound) || (latestInfo.IsContinuouslyActiveForAwhile() != prevInfo.WasActiveForAwhile) || (latestInfo.MixerVolume != prevInfo.MixerVolume) || (latestInfo.Muted != prevInfo.Muted) ) { changeMade = true; } // Alert if user changed volume, muted state if ((latestInfo.MixerVolume != prevInfo.MixerVolume) || (latestInfo.Muted != prevInfo.Muted)) { if (OnManualVolumeChange != null) { OnManualVolumeChange(latestInfo); } } soundSourceInfoList.Add(latestInfo); } else { changeMade = true; // is new sound source latestInfo.EffectiveSilentDateTime = (latestInfo.EffectiveVolumeIsZero() || latestInfo.Muted) ? DateTime.MinValue : DateTime.MaxValue; latestInfo.EffectiveStartDateTime = DateTime.Now; soundSourceInfoList.Add(latestInfo); } } if (soundSourceInfoList.Count != oldSoundSourceInfos.Length) { changeMade = true; } SoundSourceInfos = soundSourceInfoList.ToArray(); oldSoundSourceInfos = SoundSourceInfos; // Initialize dictionary oldSoundSourceDict = new Dictionary <string, SoundSourceInfo>(); for (int dictUpdateIndex = 0; dictUpdateIndex < oldSoundSourceInfos.Length; dictUpdateIndex++) { oldSoundSourceDict[oldSoundSourceInfos[dictUpdateIndex].SessionInstanceIdentifier] = oldSoundSourceInfos[dictUpdateIndex]; } TimeSpan timeSpan = DateTime.Now.Subtract(lastUpdateDateTime); if ((changeMade || (timeSpan.TotalSeconds >= 5)) && (OnChange != null)) { OnChange(SoundSourceInfos); lastUpdateDateTime = DateTime.Now; } System.Threading.Thread.Sleep((int)(SoundPollIntervalInS * 1000)); if (DateTime.Now > procDictResetDateTime) { WinCoreAudioApiSoundServer.WinCoreAudioApiSoundServer.ProcNameDict = new Dictionary <int, string>(); WinCoreAudioApiSoundServer.WinCoreAudioApiSoundServer.ProcWindowTitleDict = new Dictionary <int, string>(); procDictResetDateTime = DateTime.Now.AddMinutes(5); } i++; i = i % 10; if (i == 0) { GC.Collect(); } } }