private void addForwardsTransformations(pSprite sprite, double startTime, int index, double extraDistance, float offsetY = 0, double extraTime = 0)
        {
            //Same thing as the method above, but forwards
            //This loop has two stages, first going until the final time, then going until the final position
            //To switch between the two stages, Infinity is used to disable the other final state
            double startPos  = ManiaStage.Skin.HitPosition;
            double finalPos  = Double.PositiveInfinity;
            double finalTime = startTime + extraTime;
            byte   tag       = TransformationTag.BetweenStartEndTime;

            for (int i = index; i + 1 < speedChanges.Count; i++)
            {
REDO:
                //Move forwards either to the final time, or to when this control point ends
                double endTime = Math.Min(speedChanges[i + 1].Offset, finalTime);
                double endPos = startPos + SpeedMania.DistanceAt(speedChanges[i].BeatLength, endTime - startTime);

                if (startTime == endTime)
                {
                    goto END;
                }

                if (endPos > finalPos)
                {
                    endTime = startTime + SpeedMania.TimeAt(speedChanges[i].BeatLength, finalPos - startPos);
                    endPos  = finalPos;
                }

                sprite.Transformations.Add(new Transformation(TransformationType.MovementY,
                                                              ScaleFlipPosition((float)startPos + offsetY),
                                                              ScaleFlipPosition((float)endPos + offsetY),
                                                              (int)startTime, (int)endTime)
                {
                    TagNumeric = tag
                });
                if (endPos == finalPos)
                {
                    break;
                }

                startTime = endTime;
                startPos  = endPos;

END:
                //Reached the final time, so go to the final position
                if (endTime == finalTime)
                {
                    finalPos  = endPos - ManiaStage.Skin.HitPosition + WindowManager.DEFAULT_HEIGHT + Math.Max(extraDistance, 0);
                    finalTime = Double.PositiveInfinity;
                    tag       = TransformationTag.AfterEndTime;

                    //We need to redo this iteration because we may not be at the end of the control point
                    goto REDO;
                }
            }
        }
        private void addBackwardsTransformations(pSprite sprite, double endTime, int index, double extraDistance, float offsetY = 0)
        {
            //Move backwards through speedChanges to find the exact time the note should appear
            double endPos   = ManiaStage.Skin.HitPosition;
            double finalPos = 0 + Math.Min(extraDistance, 0);

            for (int i = index; i >= 0; i--)
            {
                //Move backwards to the start of this control point
                double startTime = speedChanges[i].Offset;
                double startPos  = endPos - SpeedMania.DistanceAt(speedChanges[i].BeatLength, endTime - startTime);

                if (startTime == endTime)
                {
                    continue;
                }

                if (startPos < finalPos)
                {
                    startTime = endTime - SpeedMania.TimeAt(speedChanges[i].BeatLength, endPos - finalPos);
                    startPos  = finalPos;
                }

                sprite.Transformations.Add(new Transformation(TransformationType.MovementY,
                                                              ScaleFlipPosition((float)(startPos + offsetY)),
                                                              ScaleFlipPosition((float)(endPos + offsetY)),
                                                              (int)startTime, (int)endTime)
                {
                    TagNumeric = TransformationTag.BeforeStartTime
                });
                if (startPos == finalPos)
                {
                    break;
                }

                endTime = startTime;
                endPos  = startPos;
            }
        }