X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Ftempo.cc;h=84fcaa118328332d3e40d34146229b81d0873f8f;hb=499e7907337da36d6d0440c22a2f63e3ada7c439;hp=a45f6fb95e4a12ec5197005c7a56714cbeb45f1f;hpb=b3c68030f40bcfb5d3ad1fb537d0de58a32f0c9c;p=ardour.git diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index a45f6fb95e..84fcaa1183 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -32,6 +32,7 @@ #include "ardour/debug.h" #include "ardour/lmath.h" #include "ardour/tempo.h" +#include "ardour/types_convert.h" #include "pbd/i18n.h" #include @@ -59,6 +60,34 @@ MetricSection::minute_at_frame (const framepos_t& frame) const return (frame / (double) _sample_rate) / 60.0; } +/***********************************************************************/ + +bool +ARDOUR::bbt_time_to_string (const BBT_Time& bbt, std::string& str) +{ + char buf[256]; + int retval = snprintf (buf, sizeof(buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, bbt.bars, bbt.beats, + bbt.ticks); + + if (retval <= 0 || retval >= (int)sizeof(buf)) { + return false; + } + + str = buf; + return true; +} + +bool +ARDOUR::string_to_bbt_time (const std::string& str, BBT_Time& bbt) +{ + if (sscanf (str.c_str (), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &bbt.bars, &bbt.beats, + &bbt.ticks) == 3) { + return true; + } + return false; +} + + /***********************************************************************/ double @@ -82,6 +111,44 @@ Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const /***********************************************************************/ +void +MetricSection::add_state_to_node(XMLNode& node) const +{ + node.set_property ("pulse", _pulse); + node.set_property ("frame", frame()); + node.set_property ("movable", !_initial); + node.set_property ("lock-style", _position_lock_style); +} + +int +MetricSection::set_state (const XMLNode& node, int /*version*/) +{ + node.get_property ("pulse", _pulse); + + framepos_t frame; + if (node.get_property ("frame", frame)) { + set_minute (minute_at_frame (frame)); + } + + bool tmp; + if (!node.get_property ("movable", tmp)) { + error << _("TempoSection XML node has no \"movable\" property") << endmsg; + throw failed_constructor(); + } + _initial = !tmp; + + if (!node.get_property ("lock-style", _position_lock_style)) { + if (!initial()) { + _position_lock_style = MusicTime; + } else { + _position_lock_style = AudioTime; + } + } + return 0; +} + +/***********************************************************************/ + const string TempoSection::xml_state_node_name = "Tempo"; TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate) @@ -90,66 +157,50 @@ 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; LocaleGuard lg; - BBT_Time bbt; - double pulse; - uint32_t frame; _legacy_bbt = BBT_Time (0, 0, 0); - if ((prop = node.property ("start")) != 0) { - if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, - &bbt.bars, - &bbt.beats, - &bbt.ticks) == 3) { + BBT_Time bbt; + std::string start_bbt; + if (node.get_property ("start", start_bbt)) { + if (string_to_bbt_time (start_bbt, bbt)) { /* legacy session - start used to be in bbt*/ _legacy_bbt = bbt; - pulse = -1.0; + set_pulse(-1.0); info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg; } } - if ((prop = node.property ("pulse")) != 0) { - if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) { - error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg; - } - } + // Don't worry about return value, exception will be thrown on error + MetricSection::set_state (node, Stateful::loading_state_version); - set_pulse (pulse); - - if ((prop = node.property ("frame")) != 0) { - if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) { - error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg; + if (node.get_property ("beats-per-minute", _note_types_per_minute)) { + if (_note_types_per_minute < 0.0) { + error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg; throw failed_constructor(); - } else { - set_minute (minute_at_frame (frame)); } } - /* XX replace old beats-per-minute name with note-types-per-minute */ - if ((prop = node.property ("beats-per-minute")) != 0) { - if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) { - error << _("TempoSection XML node has an illegal \"beats-per-minute\" value") << endmsg; + if (node.get_property ("note-type", _note_type)) { + if (_note_type < 1.0) { + error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg; throw failed_constructor(); } - } - - if ((prop = node.property ("note-type")) == 0) { + } else { /* older session, make note type be quarter by default */ _note_type = 4.0; - } else { - if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) { - error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg; - throw failed_constructor(); - } } - /* 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) { + if (!node.get_property ("clamped", _clamped)) { + _clamped = false; + } + + if (node.get_property ("end-beats-per-minute", _end_note_types_per_minute)) { + if (_end_note_types_per_minute < 0.0) { info << _("TempoSection XML node has an illegal \"in-beats-per-minute\" value") << endmsg; //throw failed_constructor(); _end_note_types_per_minute = _note_types_per_minute; @@ -159,47 +210,24 @@ TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate) _legacy_end = true; } - if ((prop = node.property ("tempo-type")) != 0) { - TempoSection::Type old_type; - - old_type = Type (string_2_enum (prop->value(), old_type)); + TempoSection::Type old_type; + if (!node.get_property ("tempo-type", old_type)) { if (old_type == TempoSection::Constant) { _end_note_types_per_minute = _note_types_per_minute; } } - if ((prop = node.property ("movable")) == 0) { - error << _("TempoSection XML node has no \"movable\" property") << endmsg; - throw failed_constructor(); - } - - set_initial (!string_is_affirmative (prop->value())); - - if ((prop = node.property ("active")) == 0) { + if (!node.get_property ("active", _active)) { warning << _("TempoSection XML node has no \"active\" property") << endmsg; - set_active(true); - } else { - set_active (string_is_affirmative (prop->value())); - } - - if ((prop = node.property ("lock-style")) == 0) { - if (!initial()) { - set_position_lock_style (MusicTime); - } else { - set_position_lock_style (AudioTime); - } - } else { - set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style()))); + _active = true; } - if ((prop = node.property ("locked-to-meter")) == 0) { + if (!node.get_property ("locked-to-meter", _locked_to_meter)) { if (initial()) { set_locked_to_meter (true); } else { set_locked_to_meter (false); } - } else { - set_locked_to_meter (string_is_affirmative (prop->value())); } /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */ @@ -212,25 +240,16 @@ XMLNode& TempoSection::get_state() const { XMLNode *root = new XMLNode (xml_state_node_name); - char buf[256]; LocaleGuard lg; - snprintf (buf, sizeof (buf), "%lf", pulse()); - root->add_property ("pulse", buf); - snprintf (buf, sizeof (buf), "%li", frame()); - root->add_property ("frame", buf); - snprintf (buf, sizeof (buf), "%lf", _note_types_per_minute); - root->add_property ("beats-per-minute", buf); - snprintf (buf, sizeof (buf), "%lf", _note_type); - root->add_property ("note-type", 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"); - root->add_property ("movable", buf); - snprintf (buf, sizeof (buf), "%s", active()?"yes":"no"); - root->add_property ("active", buf); - root->add_property ("lock-style", enum_2_string (position_lock_style())); - root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no"); + MetricSection::add_state_to_node (*root); + + root->set_property ("beats-per-minute", _note_types_per_minute); + root->set_property ("note-type", _note_type); + root->set_property ("clamped", _clamped); + root->set_property ("end-beats-per-minute", _end_note_types_per_minute); + root->set_property ("active", _active); + root->set_property ("locked-to-meter", _locked_to_meter); return *root; } @@ -532,130 +551,76 @@ const string MeterSection::xml_state_node_name = "Meter"; MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate) : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter()) { - XMLProperty const * prop; LocaleGuard lg; - BBT_Time bbt; - double pulse = 0.0; - double beat = 0.0; - framepos_t frame = 0; pair start; - if ((prop = node.property ("start")) != 0) { - if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, - &bbt.bars, - &bbt.beats, - &bbt.ticks) < 3) { - error << _("MeterSection XML node has an illegal \"start\" value") << endmsg; - } else { + BBT_Time bbt; + std::string bbt_str; + if (node.get_property ("start", bbt_str)) { + if (string_to_bbt_time (bbt_str, bbt)) { /* legacy session - start used to be in bbt*/ info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg; - pulse = -1.0; - } - } - - if ((prop = node.property ("pulse")) != 0) { - if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) { - error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg; + set_pulse(-1.0); + } else { + error << _("MeterSection XML node has an illegal \"start\" value") << endmsg; } } - set_pulse (pulse); - if ((prop = node.property ("beat")) != 0) { - if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) { - error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg; - } - } + MetricSection::set_state (node, Stateful::loading_state_version); - start.first = beat; + start.first = 0.0; + node.get_property ("beat", start.first); - if ((prop = node.property ("bbt")) == 0) { + if (node.get_property ("bbt", bbt_str)) { + if (!string_to_bbt_time (bbt_str, start.second)) { + error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg; + throw failed_constructor(); + } + } else { warning << _("MeterSection XML node has no \"bbt\" property") << endmsg; - } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, - &bbt.bars, - &bbt.beats, - &bbt.ticks) < 3) { - error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg; - throw failed_constructor(); } - start.second = bbt; set_beat (start); - if ((prop = node.property ("frame")) != 0) { - if (sscanf (prop->value().c_str(), "%li", &frame) != 1) { - error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg; - throw failed_constructor(); - } else { - set_minute (minute_at_frame (frame)); - } - } - /* beats-per-bar is old; divisions-per-bar is new */ - if ((prop = node.property ("divisions-per-bar")) == 0) { - if ((prop = node.property ("beats-per-bar")) == 0) { + if (!node.get_property ("divisions-per-bar", _divisions_per_bar)) { + if (!node.get_property ("beats-per-bar", _divisions_per_bar)) { error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg; throw failed_constructor(); } } - if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) { + + if (_divisions_per_bar < 0.0) { error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg; throw failed_constructor(); } - if ((prop = node.property ("note-type")) == 0) { + if (!node.get_property ("note-type", _note_type)) { error << _("MeterSection XML node has no \"note-type\" property") << endmsg; throw failed_constructor(); } - if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) { - error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg; - throw failed_constructor(); - } - if ((prop = node.property ("movable")) == 0) { - error << _("MeterSection XML node has no \"movable\" property") << endmsg; + if (_note_type < 0.0) { + error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg; throw failed_constructor(); } - - set_initial (!string_is_affirmative (prop->value())); - - if ((prop = node.property ("lock-style")) == 0) { - warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg; - if (!initial()) { - set_position_lock_style (MusicTime); - } else { - set_position_lock_style (AudioTime); - } - } else { - set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style()))); - } } XMLNode& MeterSection::get_state() const { XMLNode *root = new XMLNode (xml_state_node_name); - char buf[256]; LocaleGuard lg; - snprintf (buf, sizeof (buf), "%lf", pulse()); - root->add_property ("pulse", buf); - snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, - bbt().bars, - bbt().beats, - bbt().ticks); - root->add_property ("bbt", buf); - snprintf (buf, sizeof (buf), "%lf", beat()); - root->add_property ("beat", buf); - snprintf (buf, sizeof (buf), "%lf", _note_type); - root->add_property ("note-type", buf); - snprintf (buf, sizeof (buf), "%li", frame()); - root->add_property ("frame", buf); - root->add_property ("lock-style", enum_2_string (position_lock_style())); - snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar); - root->add_property ("divisions-per-bar", buf); - snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no"); - root->add_property ("movable", buf); + MetricSection::add_state_to_node (*root); + + std::string bbt_str; + bbt_time_to_string (_bbt, bbt_str); + root->set_property ("bbt", bbt_str); + root->set_property ("beat", beat()); + root->set_property ("note-type", _note_type); + root->set_property ("divisions-per-bar", _divisions_per_bar); return *root; } @@ -774,27 +739,12 @@ TempoMap::TempoMap (framecnt_t fr) } -TempoMap::TempoMap (TempoMap const & other) -{ - _frame_rate = other._frame_rate; - for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) { - TempoSection* ts = dynamic_cast (*m); - MeterSection* ms = dynamic_cast (*m); - - if (ts) { - TempoSection* new_section = new TempoSection (*ts); - _metrics.push_back (new_section); - } else { - MeterSection* new_section = new MeterSection (*ms); - _metrics.push_back (new_section); - } - } -} - TempoMap& TempoMap::operator= (TempoMap const & other) { if (&other != this) { + Glib::Threads::RWLock::ReaderLock lr (other.lock); + Glib::Threads::RWLock::WriterLock lm (lock); _frame_rate = other._frame_rate; Metrics::const_iterator d = _metrics.begin(); @@ -805,8 +755,8 @@ TempoMap::operator= (TempoMap const & other) _metrics.clear(); for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) { - TempoSection* ts = dynamic_cast (*m); - MeterSection* ms = dynamic_cast (*m); + TempoSection const * const ts = dynamic_cast (*m); + MeterSection const * const ms = dynamic_cast (*m); if (ts) { TempoSection* new_section = new TempoSection (*ts); @@ -1119,7 +1069,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 +1086,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 +1114,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(&first) = tempo; @@ -1596,6 +1549,10 @@ TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const Glib::Threads::RWLock::ReaderLock lm (lock); TempoMetric m (first_meter(), first_tempo()); + if (last) { + *last = ++_metrics.begin(); + } + /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing at something, because we insert the default tempo and meter during TempoMap construction. @@ -3123,15 +3080,13 @@ TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Ti * to section's equivalent in copy. */ TempoSection* -TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section) +TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section) const { TempoSection* ret = 0; for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { - TempoSection* t; - MeterSection* m; if ((*i)->is_tempo()) { - t = static_cast (*i); + TempoSection const * const t = dynamic_cast (*i); if (t == section) { ret = new TempoSection (*t); copy.push_back (ret); @@ -3140,9 +3095,8 @@ TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSe TempoSection* cp = new TempoSection (*t); copy.push_back (cp); - } - if (!(*i)->is_tempo()) { - m = static_cast (*i); + } else { + MeterSection const * const m = dynamic_cast (*i); MeterSection* cp = new MeterSection (*m); copy.push_back (cp); } @@ -3152,21 +3106,17 @@ TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSe } MeterSection* -TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section) +TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section) const { MeterSection* ret = 0; for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { - TempoSection* t; - MeterSection* m; if ((*i)->is_tempo()) { - t = static_cast (*i); + TempoSection const * const t = dynamic_cast (*i); TempoSection* cp = new TempoSection (*t); copy.push_back (cp); - } - - if (!(*i)->is_tempo()) { - m = static_cast (*i); + } else { + MeterSection const * const m = dynamic_cast (*i); if (m == section) { ret = new MeterSection (*m); copy.push_back (ret); @@ -3381,7 +3331,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 +3339,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 +3419,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 +3465,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 +3482,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 +3500,8 @@ out: delete (*d); ++d; } + MetricPositionChanged (PropertyChange ()); // Emit Signal + } void @@ -3532,31 +3526,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 (*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 +3549,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 +3582,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 +3633,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,17 +3645,15 @@ 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; double new_next_bpm; double new_copy_end_bpm; - if (frame > prev_to_prev_t->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > prev_to_prev_t->frame() + min_dframe) { + if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) { new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame()) / (double) (end_frame - tempo_copy->frame())); } else { @@ -3682,6 +3674,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,19 +3682,22 @@ 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; } if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) { - if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) { + 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 +3726,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 +3755,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 +3774,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; } @@ -4285,14 +4288,68 @@ TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& be return *prev_t; } +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; + } + + TempoSection* prev = 0; + + for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { + + if ((*i)->is_tempo()) { + TempoSection* t = static_cast (*i); + + if (!t->active()) { + continue; + } + + if (prev && t == ts) { + + return prev; + } + + prev = t; + } + } + + if (prev == 0) { + fatal << endmsg; + abort(); /*NOTREACHED*/ + } + + return 0; +} + 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; + } + 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 (*i); @@ -4510,7 +4567,7 @@ TempoMap::fix_legacy_end_session () } if (prev_t) { - if (prev_t->type() == TempoSection::Ramp) { + if (prev_t->type() != TempoSection::Constant) { prev_t->set_end_note_types_per_minute (t->note_types_per_minute()); } }