summaryrefslogtreecommitdiff
path: root/libs/ardour/tempo.cc
diff options
context:
space:
mode:
authornick_m <mainsbridge@gmail.com>2017-03-05 05:21:56 +1100
committernick_m <mainsbridge@gmail.com>2017-03-05 05:21:56 +1100
commit5c741b7a07d12780ba135bcd8509e7ab420cd7da (patch)
treec42f8a6f6de9133e3906bd83975492ac4674bc2e /libs/ardour/tempo.cc
parent044cfab946649c8006ed1f9f8e770e942a29b51d (diff)
tempo editing updates
- a tempo marker may now be set to always continue (clamped) this means that the end tempo of the previous section will track the start tempo during tempo ops. it mimics the behaviour in 5.8, with the gui indicating the curves to be changed.
Diffstat (limited to 'libs/ardour/tempo.cc')
-rw-r--r--libs/ardour/tempo.cc193
1 files changed, 136 insertions, 57 deletions
diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc
index e11d28bd98..bb61fb7012 100644
--- a/libs/ardour/tempo.cc
+++ b/libs/ardour/tempo.cc
@@ -90,6 +90,7 @@ TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate)
, _c (0.0)
, _active (true)
, _locked_to_meter (false)
+ , _clamped (false)
, _legacy_end (false)
{
XMLProperty const * prop;
@@ -147,6 +148,13 @@ TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate)
}
}
+ if ((prop = node.property ("clamped")) == 0) {
+ warning << _("TempoSection XML node has no \"clamped\" property") << endmsg;
+ set_clamped (false);
+ } else {
+ set_clamped (string_is_affirmative (prop->value()));
+ }
+
/* XX replace old end-beats-per-minute name with note-types-per-minute */
if ((prop = node.property ("end-beats-per-minute")) != 0) {
if (sscanf (prop->value().c_str(), "%lf", &_end_note_types_per_minute) != 1 || _end_note_types_per_minute < 0.0) {
@@ -223,6 +231,8 @@ TempoSection::get_state() const
root->add_property ("beats-per-minute", buf);
snprintf (buf, sizeof (buf), "%lf", _note_type);
root->add_property ("note-type", buf);
+ snprintf (buf, sizeof (buf), "%s", _clamped?"yes":"no");
+ root->add_property ("clamped", buf);
snprintf (buf, sizeof (buf), "%lf", _end_note_types_per_minute);
root->add_property ("end-beats-per-minute", buf);
snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
@@ -1119,7 +1129,8 @@ TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pul
return;
}
- const bool locked_to_meter = ts.locked_to_meter();
+ bool const locked_to_meter = ts.locked_to_meter();
+ bool const ts_clamped = ts.clamped();
TempoSection* new_ts = 0;
{
@@ -1135,6 +1146,7 @@ TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pul
} else {
remove_tempo_locked (ts);
new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter);
+ new_ts->set_clamped (ts_clamped);
if (new_ts && new_ts->type() == TempoSection::Constant) {
new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute());
@@ -1162,6 +1174,7 @@ TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pul
first.set_minute (minute_at_frame (frame));
first.set_position_lock_style (AudioTime);
first.set_locked_to_meter (true);
+ first.set_clamped (ts_clamped);
{
/* cannot move the first tempo section */
*static_cast<Tempo*>(&first) = tempo;
@@ -3381,7 +3394,7 @@ TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
}
bool
-TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm, bool change_end)
+TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
{
Metrics future_map;
bool can_solve = false;
@@ -3389,27 +3402,39 @@ TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm, bool change_end)
Glib::Threads::RWLock::WriterLock lm (lock);
TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
- if (change_end && tempo_copy->type() == TempoSection::Constant) {
+ if (tempo_copy->type() == TempoSection::Constant) {
tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
- } else if (change_end) {
- tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
} else {
tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
+ tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
+ }
+
+ if (ts->clamped()) {
+ TempoSection* prev = 0;
+ if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
+ prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
+ }
}
recompute_tempi (future_map);
if (check_solved (future_map)) {
- if (change_end && ts->type() == TempoSection::Constant) {
+ if (ts->type() == TempoSection::Constant) {
ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
ts->set_note_types_per_minute (bpm.note_types_per_minute());
- } else if (change_end) {
- ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
} else {
+ ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
ts->set_note_types_per_minute (bpm.note_types_per_minute());
}
+ if (ts->clamped()) {
+ TempoSection* prev = 0;
+ if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
+ prev->set_end_note_types_per_minute (ts->note_types_per_minute());
+ }
+ }
+
recompute_map (_metrics);
can_solve = true;
}
@@ -3457,18 +3482,36 @@ TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const fra
framepos_t const min_dframe = 2;
double new_bpm;
+ if (prev_t->clamped()) {
+ TempoSection* next_t = next_tempo_section_locked (future_map, prev_t);
+ TempoSection* prev_to_prev_t = previous_tempo_section_locked (future_map, prev_t);
+ /* 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;
+ if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+ contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
+ }
+ framepos_t const fr_off = (end_frame - frame);
+ const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
- if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
-
- new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
- / (double) (end_frame - prev_t->frame()));
+ if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
+ new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
+ / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
+ } else {
+ new_bpm = prev_t->note_types_per_minute();
+ }
} else {
- new_bpm = prev_t->note_types_per_minute();
- }
+ if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
- std::cout << "new bpm : " << new_bpm << std::endl;
- new_bpm = min (new_bpm, (double) 1000.0);
+ new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
+ / (double) (end_frame - prev_t->frame()));
+ } else {
+ new_bpm = prev_t->note_types_per_minute();
+ }
+ new_bpm = min (new_bpm, (double) 1000.0);
+ }
/* don't clamp and proceed here.
testing has revealed that this can go negative,
which is an entirely different thing to just being too low.
@@ -3485,6 +3528,13 @@ TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const fra
prev_t->set_note_types_per_minute (new_bpm);
}
+ if (prev_t->clamped()) {
+ TempoSection* prev = 0;
+ if ((prev = previous_tempo_section_locked (future_map, prev_t)) != 0) {
+ prev->set_end_note_types_per_minute (prev_t->note_types_per_minute());
+ }
+ }
+
recompute_tempi (future_map);
recompute_meters (future_map);
@@ -3495,12 +3545,17 @@ TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const fra
ts->set_end_note_types_per_minute (new_bpm);
ts->set_note_types_per_minute (new_bpm);
}
+ if (ts->clamped()) {
+ TempoSection* prev = 0;
+ if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
+ prev->set_end_note_types_per_minute (ts->note_types_per_minute());
+ }
+ }
recompute_tempi (_metrics);
recompute_meters (_metrics);
}
}
- MetricPositionChanged (PropertyChange ()); // Emit Signal
out:
Metrics::const_iterator d = future_map.begin();
@@ -3508,6 +3563,8 @@ out:
delete (*d);
++d;
}
+ MetricPositionChanged (PropertyChange ()); // Emit Signal
+
}
void
@@ -3532,31 +3589,17 @@ TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const
TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
-/*
- TempoSection* next_t = 0;
- for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
- if ((*i)->is_tempo() && (*i)->minute() > prev_t->minute()) {
- next_t = static_cast<TempoSection*> (*i);
- break;
- }
- }
-
- if (!next_t) {
- return;
- }
-*/
if (!prev_t) {
return;
}
-
/* minimum allowed measurement distance in frames */
framepos_t const min_dframe = 2;
double new_bpm;
if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
- new_bpm = prev_t->end_note_types_per_minute() * ((frame - prev_t->frame())
- / (double) (end_frame - prev_t->frame()));
+ new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->frame() - frame)
+ / (double) (prev_t->frame() - end_frame));
} else {
new_bpm = prev_t->end_note_types_per_minute();
}
@@ -3569,18 +3612,31 @@ TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const
prev_t->set_end_note_types_per_minute (new_bpm);
+ TempoSection* next = 0;
+ if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
+ if (next->clamped()) {
+ next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
+ }
+ }
+
recompute_tempi (future_map);
recompute_meters (future_map);
if (check_solved (future_map)) {
ts->set_end_note_types_per_minute (new_bpm);
+ TempoSection* true_next = 0;
+ if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
+ if (true_next->clamped()) {
+ true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
+ }
+ }
+
recompute_tempi (_metrics);
recompute_meters (_metrics);
}
}
- MetricPositionChanged (PropertyChange ()); // Emit Signal
out:
Metrics::const_iterator d = future_map.begin();
@@ -3589,7 +3645,9 @@ out:
++d;
}
+ MetricPositionChanged (PropertyChange ()); // Emit Signal
}
+
bool
TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
{
@@ -3638,7 +3696,6 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t
}
if (!next_to_next_t) {
- std::cout << "no next to next t" << std::endl;
return false;
}
@@ -3651,10 +3708,8 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t
const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
- framepos_t old_tc_pos = tempo_copy->frame();
- framepos_t old_next_pos = next_t->frame();
+ framepos_t old_tc_minute = tempo_copy->minute();
double old_next_minute = next_t->minute();
- framepos_t old_next_to_next_pos = next_to_next_t->frame();
double old_next_to_next_minute = next_to_next_t->minute();
double new_bpm;
@@ -3682,6 +3737,7 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t
if (tempo_copy->type() == TempoSection::Constant) {
tempo_copy->set_end_note_types_per_minute (new_bpm);
}
+
recompute_tempi (future_map);
if (check_solved (future_map)) {
@@ -3689,11 +3745,14 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t
if (!next_t) {
return false;
}
+
ts->set_note_types_per_minute (new_bpm);
if (ts->type() == TempoSection::Constant) {
ts->set_end_note_types_per_minute (new_bpm);
}
+
recompute_map (_metrics);
+
can_solve = true;
}
@@ -3701,7 +3760,7 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t
if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
- / (double) ((old_next_to_next_minute) - old_next_minute));
+ / (double) ((old_next_to_next_minute) - old_next_minute));
} else {
new_next_bpm = next_t->note_types_per_minute();
@@ -3730,20 +3789,22 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t
double copy_frame_ratio = 1.0;
if (next_to_next_t) {
- next_frame_ratio = (next_to_next_t->frame() - old_next_pos) / (double) (old_next_to_next_pos - old_next_pos);
+ next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute);
- copy_frame_ratio = ((old_tc_pos - next_t->frame()) / (double) (old_tc_pos - old_next_pos));
-
- } else {
- //next_frame_ratio = (((next_to_next_pos - fr_off) - next_t->frame()) / (double) ((next_to_next_pos) - next_t->frame()));
- //next_pulse_ratio = (start_pulse / end_pulse);
+ copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
}
new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
- new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
+ new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
- next_t->set_note_types_per_minute (new_next_bpm);
tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
+
+ if (next_t->clamped()) {
+ next_t->set_note_types_per_minute (new_copy_end_bpm);
+ } else {
+ next_t->set_note_types_per_minute (new_next_bpm);
+ }
+
recompute_tempi (future_map);
if (check_solved (future_map)) {
@@ -3757,7 +3818,13 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t
if (!next_t) {
return false;
}
- next_t->set_note_types_per_minute (new_next_bpm);
+
+ if (next_t->clamped()) {
+ next_t->set_note_types_per_minute (new_copy_end_bpm);
+ } else {
+ next_t->set_note_types_per_minute (new_next_bpm);
+ }
+
ts->set_end_note_types_per_minute (new_copy_end_bpm);
recompute_map (_metrics);
can_solve = true;
@@ -3770,9 +3837,8 @@ TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t
delete (*d);
++d;
}
- if (can_solve) {
- MetricPositionChanged (PropertyChange ()); // Emit Signal
- }
+
+ MetricPositionChanged (PropertyChange ()); // Emit Signal
return can_solve;
}
@@ -4288,15 +4354,22 @@ TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& be
TempoSection*
TempoMap::previous_tempo_section (TempoSection* ts) const
{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+
+ return previous_tempo_section_locked (_metrics, ts);
+
+}
+
+TempoSection*
+TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
+{
if (!ts) {
return 0;
}
- Glib::Threads::RWLock::ReaderLock lm (lock);
-
TempoSection* prev = 0;
- for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+ for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
if ((*i)->is_tempo()) {
TempoSection* t = static_cast<TempoSection*> (*i);
@@ -4325,15 +4398,21 @@ TempoMap::previous_tempo_section (TempoSection* ts) const
TempoSection*
TempoMap::next_tempo_section (TempoSection* ts) const
{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+
+ return next_tempo_section_locked (_metrics, ts);
+}
+
+TempoSection*
+TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
+{
if (!ts) {
return 0;
}
- Glib::Threads::RWLock::ReaderLock lm (lock);
-
TempoSection* prev = 0;
- for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+ for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
if ((*i)->is_tempo()) {
TempoSection* t = static_cast<TempoSection*> (*i);