Beispiel #1
0
        // 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());
        }
Beispiel #2
0
        // 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());
        }
Beispiel #3
0
        // 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);
            }
        }
Beispiel #4
0
        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
        }
Beispiel #5
0
        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;
        }
Beispiel #6
0
        // 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();
                }
            }
        }