/// <summary> /// Finds the most common BPM in a Qua object. /// </summary> /// <returns></returns> public float GetCommonBpm() { if (TimingPoints.Count == 0) { return(0); } // This fallback isn't really justified, but it's only used for tests. if (HitObjects.Count == 0) { return(TimingPoints[0].Bpm); } var lastObject = HitObjects.OrderByDescending(x => x.IsLongNote ? x.EndTime : x.StartTime).First(); double lastTime = lastObject.IsLongNote ? lastObject.EndTime : lastObject.StartTime; var durations = new Dictionary <float, int>(); for (var i = TimingPoints.Count - 1; i >= 0; i--) { var point = TimingPoints[i]; // Make sure that timing points past the last object don't break anything. if (point.StartTime > lastTime) { continue; } var duration = (int)(lastTime - (i == 0 ? 0 : point.StartTime)); lastTime = point.StartTime; if (durations.ContainsKey(point.Bpm)) { durations[point.Bpm] += duration; } else { durations[point.Bpm] = duration; } } if (durations.Count == 0) { return(TimingPoints[0].Bpm); // osu! hangs on loading the map in this case; we return a sensible result. } return(durations.OrderByDescending(x => x.Value).First().Key); }