// 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()); }
// This gets called when an active sound has been added or removed (and every five seconds) public static void OnUpdateSoundSourceInfos(SoundSourceInfo[] soundSourceInfos) { if (Program.LicenseExpired) return; Dictionary<string, SoundPlayerInfo> prevSessionInstanceToPlayerInfoDict = SessionInstanceToSoundPlayerInfoDict; SessionInstanceToSoundPlayerInfoDict = new Dictionary<string, SoundPlayerInfo>(); Dictionary<long, List<string>> idToSessionInstanceIdsDict = new Dictionary<long, List<string>>(); Dictionary<long, long> idToPidDict = new Dictionary<long, long>(); Dictionary<long, float> fgMusicVol = new Dictionary<long, float>(); Dictionary<long, bool> fgMusicMuted = new Dictionary<long, bool>(); Dictionary<long, bool> fgMusicIsActive = new Dictionary<long, bool>(); Dictionary<long, bool> fgMusicIgnore = new Dictionary<long, bool>(); //List<SoundPlayerInfo> fgMusics = new List<SoundPlayerInfo>(); bool bgMusicHeard = false; bool nonBgMusicHeard = false; DateTime now = DateTime.Now; bool isEffectiveSound = false; bool bgIsEmmittingSound = false; SoundServer.SoundSourceInfos = soundSourceInfos; // Determine if background music and any non-background music sound is active DateTime newestActive = DateTime.MinValue; for (int i = 0; i < SoundServer.SoundSourceInfos.Length; i++) { try { SoundSourceInfo info = SoundServer.SoundSourceInfos[i]; if (info.IsEmittingSound() && !info.Muted) { bool ignore = false; IgnoreProcNameForAutomuteDict.TryGetValue(info.ProcessName, out ignore); if (!ignore) { isEffectiveSound = true; } } if (SoundSourceInfoIsBgMusic(info, ActiveBgMusic.ShortProcessName)) // background sound { List<string> temp; if (!idToSessionInstanceIdsDict.TryGetValue(SmartVolManagerPackage.BgMusicManager.ActiveBgMusic.Id, out temp)) idToSessionInstanceIdsDict[SmartVolManagerPackage.BgMusicManager.ActiveBgMusic.Id] = new List<string>(); idToSessionInstanceIdsDict[SmartVolManagerPackage.BgMusicManager.ActiveBgMusic.Id].Add(info.SessionInstanceIdentifier); if (bgIsEmmittingSound == true) // For media player and Zune, there are multiple session instances for the bgmusic; if one is emitting then bgmusic is emitting and we'll use that session instance properties continue; BgMusicVolume = info.MixerVolume; BgMusicMuted = info.Muted; BgMusicVolInit = true; bgMusicHeard = !info.EffectiveVolumeIsZero(); bgIsEmmittingSound = info.IsEmittingSound(); if (IsPausing || !bgIsEmmittingSound) { if (MusicState != BgMusicState.Pause) UpdateBgMusicState(BgMusicState.Pause); // Assume it is paused if we haven't heard background music in awhile } else { if (MusicState != BgMusicState.Stop) { if (info.MixerVolumeIsZeroOrMuted() || ((FadingThreadCount > 0) && (IsMuting))) { if (MusicState != BgMusicState.Mute) UpdateBgMusicState(BgMusicState.Mute); } else { if (MusicState != BgMusicState.Play) UpdateBgMusicState(BgMusicState.Play); UserWantsBgMusic = true; } } } } else // Foreground sounds { //string effectiveProcessPath = (info.ProcessFullPath != "") ? info.ProcessFullPath : info.ProcessName; if ((info.IsEmittingSound() == true) && ((info.ProcessFullPath != "") || (info.IsSystemIsSystemSoundsSession))) { SoundPlayerInfo fgMusic; if (!prevSessionInstanceToPlayerInfoDict.TryGetValue(info.SessionInstanceIdentifier, out fgMusic)) // TODO: just changed to use sessioninstance { fgMusic = new SoundPlayerInfo(); fgMusic.Id = MuteFmConfig.NextId; MuteFmConfig.NextId++; } List<string> temp; if (!idToSessionInstanceIdsDict.TryGetValue(fgMusic.Id, out temp)) idToSessionInstanceIdsDict[fgMusic.Id] = new List<string>(); idToSessionInstanceIdsDict[fgMusic.Id].Add(info.SessionInstanceIdentifier); fgMusic.Name = (info.IsSystemIsSystemSoundsSession) ? "System Sounds" : info.WindowTitle; fgMusic.UrlOrCommandLine = info.ProcessFullPath; fgMusic.ShortProcessName = info.ProcessName; fgMusic.IsWeb = false; MuteFmConfigUtil.InitDefaultsProcess(fgMusic, info.ProcessName); MuteFmConfigUtil.GenerateIconImage(fgMusic, false); fgMusicVol[fgMusic.Id] = info.MixerVolume; fgMusicMuted[fgMusic.Id] = info.Muted; fgMusicIsActive[fgMusic.Id] = info.IsEmittingSound(); bool ignore = false; IgnoreProcNameForAutomuteDict.TryGetValue(info.ProcessName, out ignore); fgMusicIgnore[fgMusic.Id] = ignore; SessionInstanceToSoundPlayerInfoDict[info.SessionInstanceIdentifier] = fgMusic; } if ((!DisableAutomuteTemporarily) && (info.IsContinuouslyActiveForAwhile() == true)) { bool shouldIgnore = false; IgnoreProcNameForAutomuteDict.TryGetValue(info.ProcessName.ToLower(), out shouldIgnore); if (!shouldIgnore) nonBgMusicHeard = true; } } } catch (Exception ex) { MuteFm.SmartVolManagerPackage.SoundEventLogger.LogException(ex); } } if (isEffectiveSound) EffectiveSilenceDateTime = DateTime.MaxValue; else EffectiveSilenceDateTime = (EffectiveSilenceDateTime < now) ? EffectiveSilenceDateTime : now; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Update Automute ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// try { if ((UserWantsBgMusic) && (SmartVolManagerPackage.BgMusicManager.MuteFmConfig.GeneralSettings.AutoMuteEnabled == true) && (MasterMuted == false)) { if ((nonBgMusicHeard == true) && (bgMusicHeard == true)) { AutoMuted = true; UiPackage.UiCommands.SetNotification("Music fading out...", true); UiPackage.UiCommands.TrackEvent("automute"); PerformOperation(Operation.SmartMute); } else if ((MusicState != BgMusicState.Play) && (nonBgMusicHeard == false)) { if (AutoMuted == true) { AutoMuted = false; PerformOperation(Operation.Restore); UiPackage.UiCommands.SetNotification("Music fading in...", true); } } } if (AutoMuted && UserWantsBgMusic && (SmartVolManagerPackage.BgMusicManager.MuteFmConfig.GeneralSettings.AutoMuteEnabled == false)) { AutoMuted = false; PerformOperation(Operation.Restore); UiPackage.UiCommands.SetNotification("Music fading in...", true); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Update background-related state ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// BgMusicHeard = bgMusicHeard; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Update foreground-related state ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// if (ForegroundSoundPlaying != nonBgMusicHeard) { ForegroundSoundPlaying = nonBgMusicHeard; UiPackage.UiCommands.UpdateUiForState(); } SoundPlayerInfo[] fgMusics = new SoundPlayerInfo[SessionInstanceToSoundPlayerInfoDict.Count]; SessionInstanceToSoundPlayerInfoDict.Values.CopyTo(fgMusics, 0); List<SoundPlayerInfo> fgMusicList = new List<SoundPlayerInfo>(fgMusics); fgMusicList.Sort(delegate(SoundPlayerInfo c1, SoundPlayerInfo c2) { if ((c1.ShortProcessName == "") && (c2.ShortProcessName == ""))// System always goes first return 0; if (c1.ShortProcessName == "") // System always goes first return -1; if (c2.ShortProcessName == "") return 1; bool c1Active = false; bool c2Active = false; fgMusicIsActive.TryGetValue(c1.Id, out c1Active); fgMusicIsActive.TryGetValue(c2.Id, out c2Active); if (c1Active && !c2Active) return -1; if (!c1Active && c2Active) return 1; return -1 * c1.GetName().CompareTo(c2.GetName()); }); FgMusics = fgMusicList.ToArray(); IdToPidDict = idToPidDict; FgMusicVol = fgMusicVol; FgMusicMuted = fgMusicMuted; FgMusicIsActive = fgMusicIsActive; FgMusicIgnore = fgMusicIgnore; FgInfo[] fgInfos = CollectFgInfo(); // Only update UI with fginfo if it is different if (FgInfosAreDifferent(_oldFgInfos, fgInfos)) { UpdateFgMusicState(); _oldFgInfos = fgInfos; } // Remove anything older than an hour List<string> keys = RecentMusics.Keys.Take(RecentMusics.Keys.Count).ToList(); for (int i = 0; i < keys.Count(); i++) { if (RecentMusics[keys[i]].Value.AddSeconds(60 * 60) < DateTime.Now) RecentMusics.Remove(keys[i]); } for (int i = 0; i < fgMusics.Length; i++) { KeyValuePair<SoundPlayerInfo, DateTime> kvp; if (!RecentMusics.TryGetValue(fgMusics[i].UrlOrCommandLine, out kvp)) { kvp = new KeyValuePair<SoundPlayerInfo, DateTime>(fgMusics[i], DateTime.Now); RecentMusics[fgMusics[i].ShortProcessName] = kvp; } else { kvp = new KeyValuePair<SoundPlayerInfo, DateTime>(kvp.Key, DateTime.Now); } } IdToSessionInstanceIdsDict = idToSessionInstanceIdsDict; } catch (Exception ex) { MuteFm.SmartVolManagerPackage.SoundEventLogger.LogException(ex); } }
public static void OnManualVolumeChange(SoundSourceInfo info) { if (Program.LicenseExpired) return; if (SoundSourceInfoIsBgMusic(info, ActiveBgMusic.ShortProcessName) && (FadingThreadCount == 0) && (IsMuting == false) && (IsUnmuting == false)) { BgMusicVolume = info.MixerVolume; BgMusicMuted = info.Muted; BgMusicVolInit = true; if ((BgMusicMuted == true) && (IsMuting == false) && (IsUnmuting == false)) UserWantsBgMusic = false; UiPackage.UiCommands.UpdateUiForState(); //TODO: doesn't update state correctly /* if (BgMusicMuted == true) UpdateBgMusicState(BgMusicState.Mute); UpdateBgMusicState(MusicState);*/ } // TODO: doesn't update fgvol/muted correctly }
private static bool SoundSourceInfoIsBgMusic(SoundSourceInfo info, string bgMusicProcessName) { bool isBgMusic = (BgMusicPids.Contains(info.Pid) || ((ActiveBgMusic.IsWeb == true) && Constants.ProcessIsMuteFm(info.ProcessName))); if ((bgMusicProcessName.ToLower() == "") && (info.Pid == 0)) isBgMusic = false; else if (!isBgMusic) { if (info.Pid == 0) return false; try { //System.Diagnostics.Process p = System.Diagnostics.Process.GetProcessById(info.Pid); //if ((p != null) && (p.Modules != null) && (p.Modules.Count > 0) && (p.Modules[0].FileName == bgMusicProcessName)) if (info.ProcessName.ToLower() == bgMusicProcessName.ToLower()) isBgMusic = true; } catch (Exception ex) { string msg = ex.Message; MuteFm.SmartVolManagerPackage.SoundEventLogger.LogMsg("Error getting process info for pid " + info.Pid); } } if (isBgMusic) { if (!BgMusicPids.Contains(info.Pid)) { List<int> bgMusicPidList = new List<int>(); bgMusicPidList.Add(info.Pid); BgMusicPids = bgMusicPidList.ToArray(); } } return isBgMusic; }
// 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(); } } }