summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/editor.h1
-rw-r--r--gtk2_ardour/editor_drag.cc126
-rw-r--r--gtk2_ardour/editor_drag.h8
-rw-r--r--gtk2_ardour/editor_markers.cc46
-rw-r--r--gtk2_ardour/editor_tempodisplay.cc4
-rw-r--r--libs/ardour/ardour/tempo.h12
-rw-r--r--libs/ardour/tempo.cc193
7 files changed, 277 insertions, 113 deletions
diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h
index ec781e2df6..917dacb53e 100644
--- a/gtk2_ardour/editor.h
+++ b/gtk2_ardour/editor.h
@@ -1688,6 +1688,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
void marker_menu_rename ();
void rename_marker (ArdourMarker *marker);
void toggle_marker_lock_style ();
+ void toggle_tempo_clamped ();
void toggle_tempo_type ();
void continue_previous_tempo ();
void ramp_to_next_tempo ();
diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc
index cd60f721bf..1538457f7e 100644
--- a/gtk2_ardour/editor_drag.cc
+++ b/gtk2_ardour/editor_drag.cc
@@ -3364,7 +3364,7 @@ TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
, _copy (c)
, _grab_bpm (120.0, 4.0)
, _grab_qn (0.0)
- , before_state (0)
+ , _before_state (0)
{
DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
@@ -3423,7 +3423,7 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
_marker->hide();
/* get current state */
- before_state = &map.get_state();
+ _before_state = &map.get_state();
if (!_copy) {
_editor->begin_reversible_command (_("move tempo mark"));
@@ -3451,7 +3451,7 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
if (ArdourKeyboard::indicates_constraint (event->button.state) && ArdourKeyboard::indicates_copy (event->button.state)) {
double new_bpm = max (1.5, _grab_bpm.end_note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
stringstream strs;
- _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()), true);
+ _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), new_bpm));
strs << "end:" << fixed << setprecision(3) << new_bpm;
show_verbose_cursor_text (strs.str());
@@ -3459,7 +3459,7 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
/* use vertical movement to alter tempo .. should be log */
double new_bpm = max (1.5, _grab_bpm.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
stringstream strs;
- _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()), false);
+ _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type(), _real_section->end_note_types_per_minute()));
strs << "start:" << fixed << setprecision(3) << new_bpm;
show_verbose_cursor_text (strs.str());
@@ -3501,7 +3501,7 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
TempoMap& map (_editor->session()->tempo_map());
XMLNode &after = map.get_state();
- _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
+ _editor->session()->add_command (new MementoCommand<TempoMap>(map, _before_state, &after));
_editor->commit_reversible_command ();
// delete the dummy marker we used for visual representation while moving.
@@ -3515,7 +3515,7 @@ TempoMarkerDrag::aborted (bool moved)
_marker->set_position (_marker->tempo().frame());
if (moved) {
TempoMap& map (_editor->session()->tempo_map());
- map.set_state (*before_state, Stateful::current_state_version);
+ map.set_state (*_before_state, Stateful::current_state_version);
// delete the dummy (hidden) marker we used for events while moving.
delete _marker;
}
@@ -3525,7 +3525,7 @@ BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
: Drag (e, i)
, _grab_qn (0.0)
, _tempo (0)
- , before_state (0)
+ , _before_state (0)
{
DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
@@ -3540,6 +3540,14 @@ BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
_editor->tempo_curve_selected (_tempo, true);
ostringstream sstr;
+ if (_tempo->clamped()) {
+ TempoSection* prev = map.previous_tempo_section (_tempo);
+ if (prev) {
+ _editor->tempo_curve_selected (prev, true);
+ sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
+ }
+ }
+
sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
show_verbose_cursor_text (sstr.str());
}
@@ -3548,6 +3556,9 @@ void
BBTRulerDrag::setup_pointer_frame_offset ()
{
TempoMap& map (_editor->session()->tempo_map());
+ /* get current state */
+ _before_state = &map.get_state();
+
const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
const uint32_t divisions = _editor->get_grid_beat_divisions (0);
double beat = 0.0;
@@ -3574,8 +3585,6 @@ BBTRulerDrag::motion (GdkEvent* event, bool first_move)
TempoMap& map (_editor->session()->tempo_map());
if (first_move) {
- /* get current state */
- before_state = &map.get_state();
_editor->begin_reversible_command (_("stretch tempo"));
}
@@ -3591,7 +3600,15 @@ BBTRulerDrag::motion (GdkEvent* event, bool first_move)
/* adjust previous tempo to match pointer frame */
_editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
}
+
ostringstream sstr;
+ if (_tempo->clamped()) {
+ TempoSection* prev = map.previous_tempo_section (_tempo);
+ if (prev) {
+ _editor->tempo_curve_selected (prev, true);
+ sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
+ }
+ }
sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
show_verbose_cursor_text (sstr.str());
}
@@ -3606,16 +3623,23 @@ BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
TempoMap& map (_editor->session()->tempo_map());
XMLNode &after = map.get_state();
- _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
+ _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
_editor->commit_reversible_command ();
_editor->tempo_curve_selected (_tempo, false);
+
+ if (_tempo->clamped()) {
+ TempoSection* prev_tempo = map.previous_tempo_section (_tempo);
+ if (prev_tempo) {
+ _editor->tempo_curve_selected (prev_tempo, false);
+ }
+ }
}
void
BBTRulerDrag::aborted (bool moved)
{
if (moved) {
- _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
+ _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
}
}
@@ -3626,7 +3650,7 @@ TempoTwistDrag::TempoTwistDrag (Editor* e, ArdourCanvas::Item* i)
, _tempo (0)
, _next_tempo (0)
, _drag_valid (true)
- , before_state (0)
+ , _before_state (0)
{
DEBUG_TRACE (DEBUG::Drags, "New TempoTwistDrag\n");
@@ -3637,6 +3661,8 @@ TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
{
Drag::start_grab (event, cursor);
TempoMap& map (_editor->session()->tempo_map());
+ /* get current state */
+ _before_state = &map.get_state();
_tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
_next_tempo = map.next_tempo_section (_tempo);
@@ -3697,8 +3723,6 @@ TempoTwistDrag::motion (GdkEvent* event, bool first_move)
TempoMap& map (_editor->session()->tempo_map());
if (first_move) {
- /* get current state */
- before_state = &map.get_state();
_editor->begin_reversible_command (_("twist tempo"));
}
@@ -3734,7 +3758,7 @@ TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred)
_editor->tempo_curve_selected (_next_tempo, false);
XMLNode &after = map.get_state();
- _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
+ _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
_editor->commit_reversible_command ();
}
@@ -3742,7 +3766,7 @@ void
TempoTwistDrag::aborted (bool moved)
{
if (moved) {
- _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
+ _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
}
}
@@ -3750,22 +3774,37 @@ TempoEndDrag::TempoEndDrag (Editor* e, ArdourCanvas::Item* i)
: Drag (e, i)
, _grab_qn (0.0)
, _tempo (0)
- , before_state (0)
+ , _before_state (0)
{
DEBUG_TRACE (DEBUG::Drags, "New TempoEndDrag\n");
-
+ TempoMarker* marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
+ _tempo = &marker->tempo();
+ _grab_qn = _tempo->pulse() * 4.0;
}
void
TempoEndDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
{
Drag::start_grab (event, cursor);
- TempoMap& map (_editor->session()->tempo_map());
- _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
- _editor->tempo_curve_selected (&map.tempo_section_at_frame (_tempo->frame() - 1), true);
+ TempoMap& tmap (_editor->session()->tempo_map());
+
+ /* get current state */
+ _before_state = &tmap.get_state();
+
ostringstream sstr;
- sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
+
+ TempoSection* prev = 0;
+ if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
+ _editor->tempo_curve_selected (tmap.previous_tempo_section (_tempo), true);
+ sstr << "end: " << fixed << setprecision(3) << tmap.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
+ }
+
+ if (_tempo->clamped()) {
+ _editor->tempo_curve_selected (_tempo, true);
+ sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
+ }
+
show_verbose_cursor_text (sstr.str());
}
@@ -3773,21 +3812,6 @@ void
TempoEndDrag::setup_pointer_frame_offset ()
{
TempoMap& map (_editor->session()->tempo_map());
- const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
- const uint32_t divisions = _editor->get_grid_beat_divisions (0);
- double beat = 0.0;
-
- if (divisions > 0) {
- beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
- } else {
- /* while it makes some sense for the user to determine the division to 'grab',
- grabbing a bar often leads to confusing results wrt the actual tempo section being altered
- and the result over steep tempo curves. Use sixteenths.
- */
- beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
- }
-
- _grab_qn = map.quarter_note_at_beat (beat);
_pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
@@ -3799,8 +3823,6 @@ TempoEndDrag::motion (GdkEvent* event, bool first_move)
TempoMap& map (_editor->session()->tempo_map());
if (first_move) {
- /* get current state */
- before_state = &map.get_state();
_editor->begin_reversible_command (_("stretch end tempo"));
}
@@ -3810,7 +3832,12 @@ TempoEndDrag::motion (GdkEvent* event, bool first_move)
map.gui_stretch_tempo_end (&map.tempo_section_at_frame (_tempo->frame() - 1), map.frame_at_quarter_note (_grab_qn), pf);
ostringstream sstr;
- sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute();
+ sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
+
+ if (_tempo->clamped()) {
+ sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
+ }
+
show_verbose_cursor_text (sstr.str());
}
@@ -3821,19 +3848,28 @@ TempoEndDrag::finished (GdkEvent* event, bool movement_occurred)
return;
}
- TempoMap& map (_editor->session()->tempo_map());
+ TempoMap& tmap (_editor->session()->tempo_map());
- XMLNode &after = map.get_state();
- _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
+ XMLNode &after = tmap.get_state();
+ _editor->session()->add_command(new MementoCommand<TempoMap>(tmap, _before_state, &after));
_editor->commit_reversible_command ();
- _editor->tempo_curve_selected (&map.tempo_section_at_frame (_tempo->frame() - 1), false);
+
+ TempoSection* prev = 0;
+ if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
+ _editor->tempo_curve_selected (prev, false);
+ }
+
+ if (_tempo->clamped()) {
+ _editor->tempo_curve_selected (_tempo, false);
+
+ }
}
void
TempoEndDrag::aborted (bool moved)
{
if (moved) {
- _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
+ _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
}
}
diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h
index d41ca2708f..84356f4636 100644
--- a/gtk2_ardour/editor_drag.h
+++ b/gtk2_ardour/editor_drag.h
@@ -794,7 +794,7 @@ private:
bool _movable;
ARDOUR::Tempo _grab_bpm;
double _grab_qn;
- XMLNode* before_state;
+ XMLNode* _before_state;
};
/** BBT Ruler drag */
@@ -821,7 +821,7 @@ public:
private:
double _grab_qn;
ARDOUR::TempoSection* _tempo;
- XMLNode* before_state;
+ XMLNode* _before_state;
};
/** tempo curve twist drag */
@@ -851,7 +851,7 @@ private:
ARDOUR::TempoSection* _tempo;
ARDOUR::TempoSection* _next_tempo;
bool _drag_valid;
- XMLNode* before_state;
+ XMLNode* _before_state;
};
@@ -879,7 +879,7 @@ public:
private:
double _grab_qn;
ARDOUR::TempoSection* _tempo;
- XMLNode* before_state;
+ XMLNode* _before_state;
};
/** Drag of the playhead cursor */
diff --git a/gtk2_ardour/editor_markers.cc b/gtk2_ardour/editor_markers.cc
index 66911fc34c..40942e1569 100644
--- a/gtk2_ardour/editor_markers.cc
+++ b/gtk2_ardour/editor_markers.cc
@@ -991,8 +991,17 @@ Editor::build_tempo_marker_menu (TempoMarker* loc, bool can_remove)
MenuList& items = tempo_marker_menu->items();
tempo_marker_menu->set_name ("ArdourContextMenu");
- if (loc->tempo().type() == TempoSection::Ramp) {
- items.push_back (MenuElem (_("Set Constant"), sigc::mem_fun(*this, &Editor::toggle_tempo_type)));
+ if (!loc->tempo().initial()) {
+ if (loc->tempo().clamped()) {
+ items.push_back (MenuElem (_("Unlock Continue"), sigc::mem_fun(*this, &Editor::toggle_tempo_clamped)));
+ } else {
+ items.push_back (MenuElem (_("Lock Continue"), sigc::mem_fun(*this, &Editor::toggle_tempo_clamped)));
+ }
+
+ TempoSection* prev_ts = _session->tempo_map().previous_tempo_section (&loc->tempo());
+ if (prev_ts && prev_ts->end_note_types_per_minute() != loc->tempo().note_types_per_minute()) {
+ items.push_back (MenuElem (_("Continue"), sigc::mem_fun(*this, &Editor::continue_previous_tempo)));
+ }
}
TempoSection* next_ts = _session->tempo_map().next_tempo_section (&loc->tempo());
@@ -1000,9 +1009,8 @@ Editor::build_tempo_marker_menu (TempoMarker* loc, bool can_remove)
items.push_back (MenuElem (_("Ramp to Next"), sigc::mem_fun(*this, &Editor::ramp_to_next_tempo)));
}
- TempoSection* prev_ts = _session->tempo_map().previous_tempo_section (&loc->tempo());
- if (prev_ts && prev_ts->end_note_types_per_minute() != loc->tempo().note_types_per_minute()) {
- items.push_back (MenuElem (_("Continue"), sigc::mem_fun(*this, &Editor::continue_previous_tempo)));
+ if (loc->tempo().type() == TempoSection::Ramp) {
+ items.push_back (MenuElem (_("Set Constant"), sigc::mem_fun(*this, &Editor::toggle_tempo_type)));
}
if (loc->tempo().position_lock_style() == AudioTime && can_remove) {
@@ -1464,6 +1472,34 @@ Editor::toggle_tempo_type ()
commit_reversible_command ();
}
}
+/* clamped locks the previous section end tempo to the start tempo */
+void
+Editor::toggle_tempo_clamped ()
+{
+ TempoMarker* tm;
+ MeterMarker* mm;
+ dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
+
+ if (tm) {
+ begin_reversible_command (_("Clamp Tempo"));
+ XMLNode &before = _session->tempo_map().get_state();
+
+ TempoSection* tsp = &tm->tempo();
+ TempoSection* prev = _session->tempo_map().previous_tempo_section (tsp);
+
+ if (prev) {
+ /* set to the end tempo of the previous section */
+ Tempo new_tempo (prev->end_note_types_per_minute(), prev->note_type(), tsp->end_note_types_per_minute());
+ _session->tempo_map().gui_change_tempo (tsp, new_tempo);
+ }
+
+ tsp->set_clamped (!tsp->clamped());
+
+ XMLNode &after = _session->tempo_map().get_state();
+ _session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
+ commit_reversible_command ();
+ }
+}
void
Editor::continue_previous_tempo ()
diff --git a/gtk2_ardour/editor_tempodisplay.cc b/gtk2_ardour/editor_tempodisplay.cc
index 752373db6e..7f34e19398 100644
--- a/gtk2_ardour/editor_tempodisplay.cc
+++ b/gtk2_ardour/editor_tempodisplay.cc
@@ -316,6 +316,10 @@ Editor::redisplay_tempo (bool immediate_redraw)
void
Editor::tempo_curve_selected (TempoSection* ts, bool yn)
{
+ if (ts == 0) {
+ return;
+ }
+
for (Curves::iterator x = tempo_curves.begin(); x != tempo_curves.end(); ++x) {
if (&(*x)->tempo() == ts) {
if (yn) {
diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h
index b1c2dc6428..dcf68d7f16 100644
--- a/libs/ardour/ardour/tempo.h
+++ b/libs/ardour/ardour/tempo.h
@@ -201,7 +201,7 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
};
TempoSection (const double& pulse, const double& minute, Tempo tempo, PositionLockStyle pls, framecnt_t sr)
- : MetricSection (pulse, minute, pls, true, sr), Tempo (tempo), _c (0.0), _active (true), _locked_to_meter (false) {}
+ : MetricSection (pulse, minute, pls, true, sr), Tempo (tempo), _c (0.0), _active (true), _locked_to_meter (false), _clamped (false) {}
TempoSection (const XMLNode&, const framecnt_t sample_rate);
@@ -220,6 +220,9 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
bool locked_to_meter () const { return _locked_to_meter; }
void set_locked_to_meter (bool yn) { _locked_to_meter = yn; }
+ bool clamped () const { return _clamped; }
+ void set_clamped (bool yn) { _clamped = yn; }
+
Tempo tempo_at_minute (const double& minute) const;
double minute_at_ntpm (const double& ntpm, const double& pulse) const;
@@ -264,9 +267,11 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
this enables us to keep the tempo change at the same relative
position within the bar if/when the meter changes.
*/
+
double _c;
bool _active;
bool _locked_to_meter;
+ bool _clamped;
Timecode::BBT_Time _legacy_bbt;
bool _legacy_end;
};
@@ -501,7 +506,7 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible
void gui_set_tempo_position (TempoSection*, const framepos_t& frame, const int& sub_num);
void gui_set_meter_position (MeterSection*, const framepos_t& frame);
- bool gui_change_tempo (TempoSection*, const Tempo& bpm, bool change_end);
+ bool gui_change_tempo (TempoSection*, const Tempo& bpm);
void gui_stretch_tempo (TempoSection* tempo, const framepos_t frame, const framepos_t end_frame);
void gui_stretch_tempo_end (TempoSection* tempo, const framepos_t frame, const framepos_t end_frame);
bool gui_twist_tempi (TempoSection* first, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame);
@@ -515,6 +520,9 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible
private:
+ TempoSection* previous_tempo_section_locked (const Metrics& metrics, TempoSection*) const;
+ TempoSection* next_tempo_section_locked (const Metrics& metrics, TempoSection*) const;
+
double beat_at_minute_locked (const Metrics& metrics, const double& minute) const;
double minute_at_beat_locked (const Metrics& metrics, const double& beat) const;
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);