public double TimeAt(double beat) { //check if last entry is valid for efficiency if (_timeCache != null && _timeCache.EndBeat >= beat && _timeCache.StartBeat <= beat) { return(_timeCache.TimeAt(beat));//reuse cache } //(trivial) edge case if (BpmSections.Count == 1) { return(BpmSections[0].TimeAt(beat)); } //binary search int min = 0; int max = BpmSections.Count - 1; while (true)//will break when found { int midpt = (min + max) / 2; if (max - min == 1)//right next to each other { if (BpmSections[min].BeatInBounds(beat)) { _timeCache = BpmSections[min]; return(BpmSections[min].TimeAt(beat)); } else { _timeCache = BpmSections[max]; return(BpmSections[max].TimeAt(beat)); } } BpmSection mid = BpmSections[midpt]; if (mid.EndBeat > beat) { max = midpt; } else if (mid.StartBeat < beat) { min = midpt; } else { _timeCache = mid; return(mid.TimeAt(beat)); } } }
public static BpmGraph ParseBpm(IList <BeatEvent> changes, IList <BeatEvent> stops) { var beatGraph = new List <BpmSection>(changes.Count); double curBps; double curBeat; double nextBeat = changes[0].beat; double nextBps = 60.0 / changes[0].change;//actually inverse BPS double time = 0.0; foreach (var change in changes.Skip(1)) { curBeat = nextBeat; curBps = nextBps; nextBeat = change.beat; nextBps = 60.0 / change.change; double endTime = time + (nextBeat - curBeat) * curBps; beatGraph.Add(new BpmSection(time, endTime, curBeat, nextBeat)); time = endTime; } curBeat = nextBeat; curBps = nextBps; nextBeat = Math.Pow(10, 5);//last segment: make it practically infinitely long double lastEndTime = time + (nextBeat - curBeat) * curBps; beatGraph.Add(new BpmSection(time, lastEndTime, curBeat, nextBeat));//insert last segment var finalGraph = new List <BpmSection>(changes.Count + stops.Count); int beatPos = 1;//array index (first one already used) BpmSection currentSegment = beatGraph[0]; double timeOffset = 0.0; foreach (var stop in stops) { double beat = stop.beat; double delay = stop.change; //insert all segments we've passed while (beat > currentSegment.EndBeat) { finalGraph.Add(currentSegment); currentSegment = beatGraph[beatPos++]; currentSegment.StartTime += timeOffset; currentSegment.EndTime += timeOffset; } //currentSegment now intersects the delay: //cut it in half, insert first half and stop, set currentSegment to second half. double midTime = currentSegment.TimeAt(beat); BpmSection firstHalf = new BpmSection(currentSegment.StartTime, midTime, currentSegment.StartBeat, beat); BpmSection stopSection = new BpmSection(midTime, midTime + delay, beat, beat); BpmSection secondHalf = new BpmSection(midTime + delay, currentSegment.EndTime + delay, beat, currentSegment.EndBeat); finalGraph.Add(firstHalf); finalGraph.Add(stopSection); currentSegment = secondHalf; timeOffset += delay; } finalGraph.Add(currentSegment);//add the last piece //add any remaining segments while (beatPos != changes.Count) { BpmSection seg = beatGraph[beatPos++]; seg.StartTime += timeOffset; seg.EndTime += timeOffset; finalGraph.Add(seg); } return(new BpmGraph(finalGraph)); }