+ _extend_map (const_cast<TempoSection*> ((*i).tempo),
+ const_cast<MeterSection*> ((*i).meter),
+ next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
+}
+
+void
+TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
+ Metrics::iterator next_metric,
+ BBT_Time current, framepos_t current_frame, framepos_t end)
+{
+ /* CALLER MUST HOLD WRITE LOCK */
+
+ TempoSection* ts;
+ MeterSection* ms;
+ double beat_frames;
+ double current_frame_exact;
+ framepos_t bar_start_frame;
+
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
+
+ if (current.beats == 1) {
+ bar_start_frame = current_frame;
+ } else {
+ bar_start_frame = 0;
+ }
+
+ beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
+ current_frame_exact = current_frame;
+
+ while (current_frame < end) {
+
+ current.beats++;
+ current_frame_exact += beat_frames;
+ current_frame = llrint(current_frame_exact);
+
+ if (current.beats > meter->divisions_per_bar()) {
+ current.bars++;
+ current.beats = 1;
+ }
+
+ if (next_metric != metrics.end()) {
+
+ /* no operator >= so invert operator < */
+
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
+
+ if (!(current < (*next_metric)->start())) {
+
+ set_metrics:
+ if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
+
+ tempo = ts;
+
+ /* new tempo section: if its on a beat,
+ * we don't have to do anything other
+ * than recompute various distances,
+ * done further below as we transition
+ * the next metric section.
+ *
+ * if its not on the beat, we have to
+ * compute the duration of the beat it
+ * is within, which will be different
+ * from the preceding following ones
+ * since it takes part of its duration
+ * from the preceding tempo and part
+ * from this new tempo.
+ */
+
+ if (tempo->start().ticks != 0) {
+
+ double next_beat_frames = tempo->frames_per_beat (_frame_rate);
+
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
+ tempo->start(), current_frame, tempo->bar_offset()));
+
+ /* back up to previous beat */
+ current_frame_exact -= beat_frames;
+ current_frame = llrint(current_frame_exact);
+
+ /* set tempo section location
+ * based on offset from last
+ * bar start
+ */
+ tempo->set_frame (bar_start_frame +
+ llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames)));
+
+ /* advance to the location of
+ * the new (adjusted) beat. do
+ * this by figuring out the
+ * offset within the beat that
+ * would have been there
+ * without the tempo
+ * change. then stretch the
+ * beat accordingly.
+ */
+
+ double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
+
+ current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
+ current_frame = llrint(current_frame_exact);
+
+ /* next metric doesn't have to
+ * match this precisely to
+ * merit a reloop ...
+ */
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
+
+ } else {
+
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
+ tempo->start(), current_frame));
+ tempo->set_frame (current_frame);
+ }
+
+ } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
+
+ meter = ms;
+
+ /* new meter section: always defines the
+ * start of a bar.
+ */
+
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
+ meter->start(), current, current_frame));
+
+ assert (current.beats == 1);
+
+ meter->set_frame (current_frame);
+ }
+
+ beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
+
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
+ beat_frames, meter->divisions_per_bar(), *((Meter*)meter), *((Tempo*)tempo)));
+
+ ++next_metric;
+
+ if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
+ /* same position so go back and set this one up before advancing
+ */
+ goto set_metrics;
+ }
+
+ }
+ }
+
+ if (current.beats == 1) {
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
+ _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, 1));
+ bar_start_frame = current_frame;
+ } else {
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
+ _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, current.beats));
+ }
+
+ if (next_metric == metrics.end()) {
+ /* no more metrics - we've timestamped them all, stop here */
+ if (end == max_framepos) {
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("stop extending map now that we've reach the end @ %1|%2 = %3\n",
+ current.bars, current.beats, current_frame));
+ break;
+ }
+ }
+ }