X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Ftempo.cc;h=3865fe3e197ba05575a8885c8a26c5ac12333a86;hb=f3ada6f89347d1230cb2ce3a10582ab769c321af;hp=e74dc660205d60e532a94ba6af36a96de7f400e8;hpb=2e8e5f14fde562f19b2bc12b177d89e9a3acdeb4;p=ardour.git diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index e74dc66020..3865fe3e19 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -45,7 +45,7 @@ using Timecode::BBT_Time; /* _default tempo is 4/4 qtr=120 */ Meter TempoMap::_default_meter (4.0, 4.0); -Tempo TempoMap::_default_tempo (120.0, 4.0); +Tempo TempoMap::_default_tempo (120.0, 4.0, 120.0); framepos_t MetricSection::frame_at_minute (const double& time) const @@ -71,7 +71,7 @@ Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const The return value IS NOT interpretable in terms of "beats". */ - return (60.0 * sr) / (tempo.note_types_per_minute() * (_note_type/tempo.note_type())); + return (60.0 * sr) / (tempo.note_types_per_minute() * (_note_type / tempo.note_type())); } double @@ -90,6 +90,7 @@ TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate) , _c (0.0) , _active (true) , _locked_to_meter (false) + , _legacy_end (false) { XMLProperty const * prop; LocaleGuard lg; @@ -143,7 +144,28 @@ TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate) 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) { + 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; + _legacy_end = true; + } + } else { + _legacy_end = true; + } + + if ((prop = node.property ("tempo-type")) != 0) { + TempoSection::Type old_type; + + old_type = Type (string_2_enum (prop->value(), old_type)); + if (old_type == TempoSection::Constant) { + _end_note_types_per_minute = _note_types_per_minute; + } } if ((prop = node.property ("movable")) == 0) { @@ -160,12 +182,6 @@ TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate) set_active (string_is_affirmative (prop->value())); } - if ((prop = node.property ("tempo-type")) == 0) { - _type = Constant; - } else { - _type = Type (string_2_enum (prop->value(), _type)); - } - if ((prop = node.property ("lock-style")) == 0) { if (!initial()) { set_position_lock_style (MusicTime); @@ -185,6 +201,11 @@ TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate) } 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 */ + if (initial()) { + set_locked_to_meter (true); + } } XMLNode& @@ -202,34 +223,29 @@ 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), "%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 ("tempo-type", enum_2_string (_type)); root->add_property ("lock-style", enum_2_string (position_lock_style())); root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no"); return *root; } -void -TempoSection::set_type (Type type) -{ - _type = type; -} - /** returns the Tempo at the session-relative minute. */ Tempo TempoSection::tempo_at_minute (const double& m) const { - const bool constant = _type == Constant || _c == 0.0 || (initial() && m < minute()); + const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute()); if (constant) { return Tempo (note_types_per_minute(), note_type()); } - return Tempo (_tempo_at_time (m - minute()), _note_type); + return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute); } /** returns the session relative minute where the supplied tempo in note types per minute occurs. @@ -250,7 +266,7 @@ TempoSection::tempo_at_minute (const double& m) const double TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const { - const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse()); + const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse()); if (constant) { return ((p - pulse()) / pulses_per_minute()) + minute(); } @@ -263,13 +279,13 @@ TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const Tempo TempoSection::tempo_at_pulse (const double& p) const { - const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse()); + const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse()); if (constant) { return Tempo (note_types_per_minute(), note_type()); } - return Tempo (_tempo_at_pulse (p - pulse()), _note_type); + return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute); } /** returns the whole-note pulse where a tempo in note types per minute occurs. @@ -285,7 +301,7 @@ TempoSection::tempo_at_pulse (const double& p) const double TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const { - const bool constant = _type == Constant || _c == 0.0 || (initial() && m < minute()); + const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute()); if (constant) { return ((m - minute()) * pulses_per_minute()) + pulse(); } @@ -298,7 +314,7 @@ TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const double TempoSection::pulse_at_minute (const double& m) const { - const bool constant = _type == Constant || _c == 0.0 || (initial() && m < minute()); + const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute()); if (constant) { return ((m - minute()) * pulses_per_minute()) + pulse(); } @@ -311,7 +327,7 @@ TempoSection::pulse_at_minute (const double& m) const double TempoSection::minute_at_pulse (const double& p) const { - const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse()); + const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse()); if (constant) { return ((p - pulse()) / pulses_per_minute()) + minute(); } @@ -328,7 +344,7 @@ TempoSection::minute_at_pulse (const double& p) const double TempoSection::pulse_at_frame (const framepos_t& f) const { - const bool constant = _type == Constant || _c == 0.0 || (initial() && f < frame()); + const bool constant = type() == Constant || _c == 0.0 || (initial() && f < frame()); if (constant) { return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse(); } @@ -339,7 +355,7 @@ TempoSection::pulse_at_frame (const framepos_t& f) const framepos_t TempoSection::frame_at_pulse (const double& p) const { - const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse()); + const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse()); if (constant) { return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute()); } @@ -430,7 +446,7 @@ https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Te double TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const { - if (note_types_per_minute() == end_npm || _type == Constant) { + if (note_types_per_minute() == end_npm || type() == Constant) { return 0.0; } @@ -446,7 +462,7 @@ TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) c double TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const { - if (note_types_per_minute() == end_npm || _type == Constant) { + if (note_types_per_minute() == end_npm || type() == Constant) { return 0.0; } @@ -743,7 +759,7 @@ TempoMap::TempoMap (framecnt_t fr) _frame_rate = fr; BBT_Time start (1, 1, 0); - TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo.note_types_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime, fr); + TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, AudioTime, fr); MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr); t->set_initial (true); @@ -971,10 +987,6 @@ TempoMap::do_insert (MetricSection* section) *(dynamic_cast(*i)) = *(dynamic_cast(insert_tempo)); (*i)->set_position_lock_style (AudioTime); - TempoSection* t; - if ((t = dynamic_cast(*i)) != 0) { - t->set_type (insert_tempo->type()); - } need_add = false; } else { delete (*i); @@ -1062,7 +1074,7 @@ TempoMap::do_insert (MetricSection* section) } /* user supplies the exact pulse if pls == MusicTime */ TempoSection* -TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls) +TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls) { if (tempo.note_types_per_minute() <= 0.0) { warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg; @@ -1070,11 +1082,29 @@ TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& } TempoSection* ts = 0; + TempoSection* prev_tempo = 0; { Glib::Threads::RWLock::WriterLock lm (lock); - ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true); - } + ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true); + for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) { + if ((*i)->is_tempo()) { + TempoSection* const this_t = static_cast (*i); + + bool const ipm = ts->position_lock_style() == MusicTime; + bool const lm = ts->locked_to_meter(); + if ((ipm && this_t->pulse() == ts->pulse()) || (!ipm && this_t->frame() == ts->frame()) + || (lm && this_t->pulse() == ts->pulse())) { + if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) { + prev_tempo->set_end_note_types_per_minute (ts->note_types_per_minute()); + } + break; + } + prev_tempo = this_t; + } + } + recompute_map (_metrics); + } PropertyChanged (PropertyChange ()); @@ -1082,7 +1112,7 @@ TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& } void -TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls) +TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls) { if (tempo.note_types_per_minute() <= 0.0) { warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg; @@ -1090,13 +1120,13 @@ TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pul } const bool locked_to_meter = ts.locked_to_meter(); + TempoSection* new_ts = 0; { Glib::Threads::RWLock::WriterLock lm (lock); TempoSection& first (first_tempo()); if (!ts.initial()) { if (locked_to_meter) { - ts.set_type (type); { /* cannot move a meter-locked tempo section */ *static_cast(&ts) = tempo; @@ -1104,10 +1134,30 @@ TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pul } } else { remove_tempo_locked (ts); - add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter); + new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter); + + if (new_ts && new_ts->type() == TempoSection::Constant) { + new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute()); + } else { + for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) { + + if ((*i)->is_tempo()) { + TempoSection* const this_t = static_cast (*i); + + bool const ipm = new_ts->position_lock_style() == MusicTime; + bool const lm = new_ts->locked_to_meter(); + if ((ipm && this_t->pulse() > new_ts->pulse()) || (!ipm && this_t->frame() > new_ts->frame()) + || (lm && this_t->pulse() > new_ts->pulse())) { + new_ts->set_end_note_types_per_minute (tempo.end_note_types_per_minute()); + + break; + } + } + } + } } + } else { - first.set_type (type); first.set_pulse (0.0); first.set_minute (minute_at_frame (frame)); first.set_position_lock_style (AudioTime); @@ -1115,9 +1165,9 @@ TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pul { /* cannot move the first tempo section */ *static_cast(&first) = tempo; - recompute_map (_metrics); } } + recompute_map (_metrics); } PropertyChanged (PropertyChange ()); @@ -1125,27 +1175,22 @@ TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pul TempoSection* TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute - , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter) + , PositionLockStyle pls, bool recompute, bool locked_to_meter) { - TempoSection* t = new TempoSection (pulse, minute, tempo.note_types_per_minute(), tempo.note_type(), type, pls, _frame_rate); + TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate); t->set_locked_to_meter (locked_to_meter); - bool solved = false; do_insert (t); if (recompute) { if (pls == AudioTime) { - solved = solve_map_minute (_metrics, t, t->minute()); + solve_map_minute (_metrics, t, t->minute()); } else { - solved = solve_map_pulse (_metrics, t, t->pulse()); + solve_map_pulse (_metrics, t, t->pulse()); } recompute_meters (_metrics); } - if (!solved && recompute) { - recompute_map (_metrics); - } - return t; } @@ -1210,7 +1255,7 @@ TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& whe if (pls == AudioTime) { /* add meter-locked tempo */ - mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), TempoSection::Ramp, AudioTime, true, true); + mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), AudioTime, true, true); if (!mlt) { return 0; @@ -1256,9 +1301,9 @@ TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& whe } void -TempoMap::change_initial_tempo (double note_types_per_minute, double note_type) +TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute) { - Tempo newtempo (note_types_per_minute, note_type); + Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute); TempoSection* t; for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) { @@ -1278,9 +1323,9 @@ TempoMap::change_initial_tempo (double note_types_per_minute, double note_type) } void -TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type) +TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm) { - Tempo newtempo (note_types_per_minute, note_type); + Tempo newtempo (note_types_per_minute, note_type, end_ntpm); TempoSection* prev; TempoSection* first; @@ -1428,14 +1473,14 @@ TempoMap::recompute_tempi (Metrics& metrics) } if (prev_t) { if (t->position_lock_style() == AudioTime) { - prev_t->set_c (prev_t->compute_c_minute (t->note_types_per_minute(), t->minute())); + prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute())); if (!t->locked_to_meter()) { - t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())); + t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())); } } else { - prev_t->set_c (prev_t->compute_c_pulse (t->note_types_per_minute(), t->pulse())); - t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse())); + prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse())); + t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse())); } } @@ -1740,7 +1785,7 @@ TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) } } - return Tempo (prev_t->note_types_per_minute(), prev_t->note_type()); + return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute()); } /** returns the frame at which the supplied tempo occurs, or @@ -1773,17 +1818,20 @@ TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) co continue; } - const double t_bpm = t->note_types_per_minute(); - if (t_bpm == tempo_bpm) { + + if (t->note_types_per_minute() == tempo_bpm) { return t->minute(); } if (prev_t) { const double prev_t_bpm = prev_t->note_types_per_minute(); + const double prev_t_end_bpm = prev_t->end_note_types_per_minute(); + if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm) + || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm) + || (prev_t_end_bpm == tempo_bpm)) { - if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) { - return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse()); + return prev_t->minute_at_ntpm (tempo_bpm, t->pulse()); } } prev_t = t; @@ -1814,7 +1862,7 @@ TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) co } } - return Tempo (prev_t->note_types_per_minute(), prev_t->note_type()); + return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute()); } double @@ -2565,7 +2613,7 @@ TempoMap::check_solved (const Metrics& metrics) const } /* precision check ensures tempo and frames align.*/ - if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) { + if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) { if (!t->locked_to_meter()) { return false; } @@ -2676,20 +2724,20 @@ TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const dou if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) { section_prev = prev_t; - section_prev->set_c (section_prev->compute_c_minute (section->note_types_per_minute(), minute)); + section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute)); if (!section->locked_to_meter()) { - section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute)); + section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute)); } prev_t = section; } if (t->position_lock_style() == MusicTime) { - prev_t->set_c (prev_t->compute_c_pulse (t->note_types_per_minute(), t->pulse())); - t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse())); + prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse())); + t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse())); } else { - prev_t->set_c (prev_t->compute_c_minute (t->note_types_per_minute(), t->minute())); + prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute())); if (!t->locked_to_meter()) { - t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())); + t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())); } } } @@ -2746,12 +2794,12 @@ TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const doub } if (t->position_lock_style() == MusicTime) { - prev_t->set_c (prev_t->compute_c_pulse (t->note_types_per_minute(), t->pulse())); - t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse())); + prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse())); + t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse())); } else { - prev_t->set_c (prev_t->compute_c_minute (t->note_types_per_minute(), t->minute())); + prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute())); if (!t->locked_to_meter()) { - t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())); + t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())); } } } @@ -2760,8 +2808,8 @@ TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const doub } if (section_prev) { - section_prev->set_c (section_prev->compute_c_pulse (section->note_types_per_minute(), pulse)); - section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse)); + section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse)); + section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse)); } #if (0) @@ -3333,18 +3381,35 @@ TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame) } bool -TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm) +TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm, bool change_end) { 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_note_types_per_minute (bpm.note_types_per_minute()); + + if (change_end && 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()); + } + recompute_tempi (future_map); if (check_solved (future_map)) { - ts->set_note_types_per_minute (bpm.note_types_per_minute()); + if (change_end && 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_note_types_per_minute (bpm.note_types_per_minute()); + } + recompute_map (_metrics); can_solve = true; } @@ -3358,11 +3423,12 @@ TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm) if (can_solve) { MetricPositionChanged (PropertyChange ()); // Emit Signal } + return can_solve; } void -TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame) +TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame) { /* Ts (future prev_t) Tnext @@ -3382,105 +3448,224 @@ TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t& frame, const fr } TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts); - TempoSection* prev_to_prev_t = 0; - const frameoffset_t fr_off = end_frame - frame; - assert (prev_t); + 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->note_types_per_minute() * ((frame - prev_t->frame()) + / (double) (end_frame - prev_t->frame())); + } else { + new_bpm = prev_t->note_types_per_minute(); + } + + std::cout << "new bpm : " << new_bpm << std::endl; + 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. + */ + + if (new_bpm < 0.5) { + goto out; + } + + if (prev_t && prev_t->type() == TempoSection::Ramp) { + prev_t->set_note_types_per_minute (new_bpm); + } else { + prev_t->set_end_note_types_per_minute (new_bpm); + prev_t->set_note_types_per_minute (new_bpm); + } + + recompute_tempi (future_map); + recompute_meters (future_map); - if (prev_t->pulse() > 0.0) { - prev_to_prev_t = const_cast(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1))); + if (check_solved (future_map)) { + if (prev_t && prev_t->type() == TempoSection::Ramp) { + ts->set_note_types_per_minute (new_bpm); + } else { + ts->set_end_note_types_per_minute (new_bpm); + ts->set_note_types_per_minute (new_bpm); + } + recompute_tempi (_metrics); + recompute_meters (_metrics); } + } + + MetricPositionChanged (PropertyChange ()); // Emit Signal +out: + Metrics::const_iterator d = future_map.begin(); + while (d != future_map.end()) { + delete (*d); + ++d; + } + +} +void +TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame) +{ + /* + Ts (future prev_t) Tnext + | | + | [drag^] | + |----------|---------- + e_f qn_beats(frame) + */ + + Metrics future_map; + + { + Glib::Threads::RWLock::WriterLock lm (lock); + + if (!ts) { + return; + } + + TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts); + +/* TempoSection* next_t = 0; - for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) { - TempoSection* t = 0; - if ((*i)->is_tempo()) { - t = static_cast (*i); - if (t->frame() > ts->frame()) { - next_t = t; - break; - } + 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 */ - const framepos_t min_dframe = 2; + framepos_t const min_dframe = 2; + double new_bpm; - /* 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 (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())); + } else { + new_bpm = prev_t->end_note_types_per_minute(); + } - 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()); + new_bpm = min (new_bpm, (double) 1000.0); + + if (new_bpm < 0.5) { + goto out; } - const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off); + prev_t->set_end_note_types_per_minute (new_bpm); - const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame)); - const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame)); + recompute_tempi (future_map); + recompute_meters (future_map); - double new_bpm; + if (check_solved (future_map)) { + ts->set_end_note_types_per_minute (new_bpm); - if (prev_t->type() == TempoSection::Constant || prev_t->c() == 0.0) { + recompute_tempi (_metrics); + recompute_meters (_metrics); + } + } - if (prev_t->position_lock_style() == MusicTime) { - if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) { - if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) { + MetricPositionChanged (PropertyChange ()); // Emit Signal - 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 { - /* prev to prev is irrelevant */ +out: + Metrics::const_iterator d = future_map.begin(); + while (d != future_map.end()) { + delete (*d); + ++d; + } - if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) { - new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse())); - } else { - new_bpm = prev_t->note_types_per_minute(); - } - } - } else { - /* AudioTime */ - if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) { - if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) { +} +bool +TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame) +{ + TempoSection* next_t = 0; + TempoSection* next_to_next_t = 0; + Metrics future_map; + bool can_solve = false; - new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame()) - / (double) ((end_frame) - prev_to_prev_t->frame())); - } else { - new_bpm = prev_t->note_types_per_minute(); - } - } else { - /* prev_to_prev_t is irrelevant */ + /* minimum allowed measurement distance in frames */ + framepos_t const min_dframe = 2; - 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())); - } else { - new_bpm = prev_t->note_types_per_minute(); - } - } + { + Glib::Threads::RWLock::WriterLock lm (lock); + if (!ts) { + return false; + } + + TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts); + TempoSection* prev_to_prev_t = 0; + const frameoffset_t fr_off = end_frame - frame; + + if (!tempo_copy) { + return false; + } + + if (tempo_copy->pulse() > 0.0) { + prev_to_prev_t = const_cast(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1))); + } + + for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) { + if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) { + next_t = static_cast (*i); + break; } - } else { + } - double frame_ratio = 1.0; - double pulse_ratio = 1.0; - const double pulse_pos = frame; + if (!next_t) { + return false; + } - if (prev_to_prev_t) { - if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) { - frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame())); - } - if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) { - pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse())); - } - } else { - if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) { - frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame())); - } - pulse_ratio = (start_pulse / end_pulse); + for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) { + if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) { + next_to_next_t = static_cast (*i); + break; } - new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio); + } + + if (!next_to_next_t) { + std::cout << "no next to next t" << std::endl; + return false; + } + + double prev_contribution = 0.0; + + if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) { + prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame()); + } + + 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(); + 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) { + new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame()) + / (double) (end_frame - tempo_copy->frame())); + } else { + new_bpm = tempo_copy->note_types_per_minute(); } /* don't clamp and proceed here. @@ -3488,17 +3673,95 @@ TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t& frame, const fr which is an entirely different thing to just being too low. */ if (new_bpm < 0.5) { - return; + return false; } + new_bpm = min (new_bpm, (double) 1000.0); - prev_t->set_note_types_per_minute (new_bpm); + + tempo_copy->set_note_types_per_minute (new_bpm); + if (tempo_copy->type() == TempoSection::Constant) { + tempo_copy->set_end_note_types_per_minute (new_bpm); + } recompute_tempi (future_map); - recompute_meters (future_map); if (check_solved (future_map)) { + + if (!next_t) { + return false; + } ts->set_note_types_per_minute (new_bpm); - recompute_tempi (_metrics); - recompute_meters (_metrics); + 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) { + + 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)); + + } else { + new_next_bpm = next_t->note_types_per_minute(); + } + + next_t->set_note_types_per_minute (new_next_bpm); + recompute_tempi (future_map); + + if (check_solved (future_map)) { + for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) { + if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) { + next_t = static_cast (*i); + break; + } + } + + if (!next_t) { + return false; + } + next_t->set_note_types_per_minute (new_next_bpm); + recompute_map (_metrics); + can_solve = true; + } + } else { + double next_frame_ratio = 1.0; + 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); + + 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); + } + + 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; + + next_t->set_note_types_per_minute (new_next_bpm); + tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm); + recompute_tempi (future_map); + + if (check_solved (future_map)) { + for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) { + if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) { + next_t = static_cast (*i); + break; + } + } + + if (!next_t) { + return false; + } + 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; + } } } @@ -3507,10 +3770,12 @@ TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t& frame, const fr delete (*d); ++d; } + if (can_solve) { + MetricPositionChanged (PropertyChange ()); // Emit Signal + } - MetricPositionChanged (PropertyChange ()); // Emit Signal + return can_solve; } - /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to * the supplied frame, possibly returning a negative value. * @@ -3586,7 +3851,7 @@ TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& fr qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num); } else if (sub_num == 1) { /* the gui requested exact musical (BBT) beat */ - qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5)) * 4.0); + qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0; } else if (sub_num == -1) { /* snap to bar */ Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0); @@ -4020,6 +4285,42 @@ TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& be return *prev_t; } +TempoSection* +TempoMap::next_tempo_section (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) { + + if ((*i)->is_tempo()) { + TempoSection* t = static_cast (*i); + + if (!t->active()) { + continue; + } + + if (prev && prev == ts) { + + return t; + } + + prev = t; + } + } + + if (prev == 0) { + fatal << endmsg; + abort(); /*NOTREACHED*/ + } + + return 0; +} /* don't use this to calculate length (the tempo is only correct for this frame). do that stuff based on the beat_at_frame and frame_at_beat api */ @@ -4130,6 +4431,7 @@ TempoMap::fix_legacy_session () { MeterSection* prev_m = 0; TempoSection* prev_t = 0; + bool have_initial_t = false; for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) { MeterSection* m; @@ -4168,10 +4470,21 @@ TempoMap::fix_legacy_session () t->set_minute (0.0); t->set_position_lock_style (AudioTime); prev_t = t; + have_initial_t = true; continue; } if (prev_t) { + /* some 4.x sessions have no initial (non-movable) tempo. */ + if (!have_initial_t) { + prev_t->set_pulse (0.0); + prev_t->set_minute (0.0); + prev_t->set_position_lock_style (AudioTime); + prev_t->set_initial (true); + prev_t->set_locked_to_meter (true); + have_initial_t = true; + } + const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0)) + (t->legacy_bbt().beats - 1) + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat); @@ -4186,6 +4499,30 @@ TempoMap::fix_legacy_session () } } } +void +TempoMap::fix_legacy_end_session () +{ + TempoSection* prev_t = 0; + + for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) { + TempoSection* t; + + if ((t = dynamic_cast(*i)) != 0) { + + if (!t->active()) { + continue; + } + + if (prev_t) { + if (prev_t->type() != TempoSection::Constant) { + prev_t->set_end_note_types_per_minute (t->note_types_per_minute()); + } + } + + prev_t = t; + } + } +} XMLNode& TempoMap::get_state () @@ -4262,6 +4599,12 @@ TempoMap::set_state (const XMLNode& node, int /*version*/) fix_legacy_session(); break; } + + if (t->legacy_end()) { + fix_legacy_end_session(); + break; + } + break; } } @@ -4320,19 +4663,20 @@ TempoMap::dump (std::ostream& o) const for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) { if ((t = dynamic_cast(*i)) != 0) { - o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type() + o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type() << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse() << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl; if (prev_t) { - o << " current : " << t->note_types_per_minute() + o << " current start : " << t->note_types_per_minute() + << " current end : " << t->end_note_types_per_minute() << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl; o << " previous : " << prev_t->note_types_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl; o << " calculated : " << prev_t->tempo_at_pulse (t->pulse()) - << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()) - << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse())) - << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl; + << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()) + << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse())) + << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl; } prev_t = t; } else if ((m = dynamic_cast(*i)) != 0) {