public static BpmGraph ParseBars(List <BarSection> barSections) { //In this context, 'time' = bar (beat is still beat) barSections.Sort((a, b) => a.StartBar.CompareTo(b.StartBar)); var barToBeatSections = new List <BpmSection>(); double maxBar = 100_000; var lastBarSection = new BpmSection(0, maxBar, 0, maxBar * 4);//default bar section for default Beats Per Bar of 4 barToBeatSections.Add(lastBarSection); foreach (var bar in barSections) { double startBeat = lastBarSection.BeatAt(bar.StartBar); double endBeat = (maxBar - bar.StartBar) * bar.NewBeatsPerBar + startBeat; var section = new BpmSection(bar.StartBar, maxBar, startBeat, endBeat); //modify the last section to end here lastBarSection.EndBeat = startBeat; lastBarSection.EndTime = bar.StartBar; Assert.AreApproximatelyEqual(bar.NewBeatsPerBar, (float)section.Slope()); barToBeatSections.Add(section); lastBarSection = section; } return(new BpmGraph(barToBeatSections)); }
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)); }