+ MeterSection* cp = new MeterSection (*m);
+ copy.push_back (cp);
+ }
+ }
+
+ return ret;
+}
+
+bool
+TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
+{
+ Metrics copy;
+ TempoSection* tempo_copy = 0;
+
+ {
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
+ if (!tempo_copy) {
+ return false;
+ }
+ }
+
+ const double beat = bbt_to_beats_locked (copy, bbt);
+ const bool ret = solve_map (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
+
+ Metrics::const_iterator d = copy.begin();
+ while (d != copy.end()) {
+ delete (*d);
+ ++d;
+ }
+
+ return ret;
+}
+
+/**
+* This is for a gui that needs to know the frame of a tempo section if it were to be moved to some bbt time,
+* taking any possible reordering as a consequence of this into account.
+* @param section - the section to be altered
+* @param bpm - the new Tempo
+* @param bbt - the bbt where the altered tempo will fall
+* @return returns - the position in frames where the new tempo section will lie.
+*/
+framepos_t
+TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ Metrics future_map;
+ framepos_t ret = 0;
+ TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
+ if (!tempo_copy) {
+ return 0;
+ }
+ const double beat = bbt_to_beats_locked (future_map, bbt);
+
+ if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
+ ret = tempo_copy->frame();
+ } else {
+ ret = section->frame();
+ }
+
+ Metrics::const_iterator d = future_map.begin();
+ while (d != future_map.end()) {
+ delete (*d);
+ ++d;
+ }
+ return ret;
+}
+
+double
+TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ Metrics future_map;
+ double ret = 0.0;
+ TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
+
+ if (solve_map (future_map, tempo_copy, frame)) {
+ ret = tempo_copy->pulse();
+ } else {
+ ret = section->pulse();
+ }
+
+ Metrics::const_iterator d = future_map.begin();
+ while (d != future_map.end()) {
+ delete (*d);
+ ++d;
+ }
+ return ret;
+}
+
+void
+TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
+{
+ Metrics future_map;
+ {
+ Glib::Threads::RWLock::WriterLock lm (lock);
+ TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
+ if (solve_map (future_map, tempo_copy, frame)) {
+ solve_map (_metrics, ts, frame);
+ recompute_meters (_metrics);
+ }
+ }
+
+ Metrics::const_iterator d = future_map.begin();
+ while (d != future_map.end()) {
+ delete (*d);
+ ++d;
+ }
+
+ MetricPositionChanged (); // Emit Signal
+}
+
+void
+TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
+{
+ Metrics future_map;
+ {
+ Glib::Threads::RWLock::WriterLock lm (lock);
+ TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
+ if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
+ solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
+ recompute_meters (_metrics);
+ }
+ }
+
+ Metrics::const_iterator d = future_map.begin();
+ while (d != future_map.end()) {
+ delete (*d);
+ ++d;
+ }
+
+ MetricPositionChanged (); // Emit Signal
+}
+
+void
+TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
+{
+ Metrics future_map;
+ {
+ Glib::Threads::RWLock::WriterLock lm (lock);
+ MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
+ if (solve_map (future_map, copy, frame)) {
+ solve_map (_metrics, ms, frame);
+ recompute_tempos (_metrics);
+ }
+ }
+
+ Metrics::const_iterator d = future_map.begin();
+ while (d != future_map.end()) {
+ delete (*d);
+ ++d;
+ }
+
+ MetricPositionChanged (); // Emit Signal
+}
+
+void
+TempoMap::gui_move_meter (MeterSection* ms, const Timecode::BBT_Time& bbt)
+{
+ Metrics future_map;
+ {
+ Glib::Threads::RWLock::WriterLock lm (lock);
+ MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
+ if (solve_map (future_map, copy, bbt)) {
+ solve_map (_metrics, ms, bbt);
+ recompute_tempos (_metrics);
+ }
+ }
+
+ Metrics::const_iterator d = future_map.begin();
+ while (d != future_map.end()) {
+ delete (*d);
+ ++d;
+ }
+
+ MetricPositionChanged (); // Emit Signal
+}
+
+bool
+TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
+{
+ Metrics future_map;
+ bool can_solve = false;
+ {
+ Glib::Threads::RWLock::WriterLock lm (lock);
+ TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
+ tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
+ recompute_tempos (future_map);
+
+ if (check_solved (future_map, true)) {
+ ts->set_beats_per_minute (bpm.beats_per_minute());
+ recompute_map (_metrics);
+ can_solve = true;
+ }
+ }
+
+ Metrics::const_iterator d = future_map.begin();
+ while (d != future_map.end()) {
+ delete (*d);
+ ++d;
+ }
+ if (can_solve) {
+ MetricPositionChanged (); // Emit Signal
+ }
+ return can_solve;
+}
+
+void
+TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
+{
+ Metrics future_map;
+ TempoSection* ts = 0;
+
+ if (frame <= first_meter().frame()) {
+ return;
+ }
+
+ if (ms->position_lock_style() == AudioTime) {
+ /* disabled for now due to faked tempo locked to meter pulse */
+ return;
+ }
+
+ {
+ Glib::Threads::RWLock::WriterLock lm (lock);
+ ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
+ if (!ts) {
+ return;
+ }
+ TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
+ TempoSection* prev_to_prev_t = 0;
+ const frameoffset_t fr_off = frame - ms->frame();
+ double new_bpm = 0.0;
+
+ if (prev_t && prev_t->pulse() > 0.0) {
+ prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
+ }
+
+ /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
+ constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
+ */
+ double contribution = 0.0;
+ frameoffset_t frame_contribution = 0;
+ frameoffset_t prev_t_frame_contribution = fr_off;
+
+ if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+ /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
+ contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
+ frame_contribution = contribution * (double) fr_off;
+ prev_t_frame_contribution = fr_off - frame_contribution;
+ }
+
+ if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
+
+ if (prev_t->position_lock_style() == MusicTime) {
+ if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+ new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
+ / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
+
+ } else {
+ /* prev to prev is irrelevant */
+ const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
+ const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
+
+ if (frame_pulse != prev_t->pulse()) {
+ new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
+ } else {
+ new_bpm = prev_t->beats_per_minute();
+ }
+ }
+ } else {
+ /* AudioTime */
+ if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+ new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
+ / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));