public void Update()
        {
            AllowSpinnerOptimisation = false;

            List <HitObject> activeObjects = ActiveStreamObjects;

            if (activeObjects == null)
            {
                return;
            }

            int lowestActiveObject = -1;

            ProcessTo = activeObjects.Count - 1;
            //initialise to the last object. if we don't find an earlier one below, this will be used.

            ActiveObject = null;
            NextObject   = null;

            for (int i = ProcessFrom; i < activeObjects.Count; i++)
            {
                HitObject h = activeObjects[i];

                int hitObjectNow = h.ClockingNow;

                if (h.IsVisible || !h.IsHit)
                {
                    h.Update();

                    if (h.StartTime <= hitObjectNow && h.EndTime > hitObjectNow)
                    {
                        ActiveObject = h;
                    }
                    else if (h.StartTime > hitObjectNow)
                    {
                        if (NextObject == null && !h.IsHit)
                        {
                            NextObject = h;
                        }
                    }

                    if (!AllowSpinnerOptimisation)
                    {
                        AllowSpinnerOptimisation |= h is Spinner && h.Sprites[0].Alpha == 1;
                    }

                    if (Player.Autoplay && !h.IsHit && hitObjectNow >= h.StartTime)
                    {
                        TriggerScoreChange(h.Hit(), h);
                    }
                    if (Clock.AudioTimeSource.IsElapsing)
                    {
                        TriggerScoreChange(h.CheckScoring(), h);
                    }

                    if (lowestActiveObject < 0)
                    {
                        lowestActiveObject = i;
                    }
                }
                else
                {
                    if (h is Slider s && s.EndTime < hitObjectNow)
                    {
                        s.DisposePathTexture();
                    }
                }

                if (h.StartTime > hitObjectNow + 4000 && !h.IsVisible)
                {
                    ProcessTo = i;
                    break; //stop processing after a decent amount of leeway...
                }
            }

            if (lowestActiveObject >= 0)
            {
                ProcessFrom = lowestActiveObject;
            }

            if (nextStreamChange > 0 && nextStreamChange <= Clock.AudioTime)
            {
                if (OnStreamChanged != null)
                {
                    OnStreamChanged(ActiveStream);
                }

                nextStreamChange = 0;
            }

            streamSpriteManagers[(int)(ActiveStream)].Update();

            spriteManager.Update();
        }