X-Git-Url: https://main.carlh.net/gitweb/?p=ardour.git;a=blobdiff_plain;f=libs%2Fardour%2Ftempo.cc;h=11da2fc81d3892768dbc2efbd2c17bc439228465;hp=5df06f7745295f18621644cfa91cbe409d7260a8;hb=c8c6bca6587450ff64303dbc994a4cd28d6ce7aa;hpb=2d5238d87581bc0ff9dcaaa8aad9e255b5d9c370 diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index 5df06f7745..11da2fc81d 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -33,7 +33,7 @@ #include "ardour/lmath.h" #include "ardour/tempo.h" -#include "i18n.h" +#include "pbd/i18n.h" #include using namespace std; @@ -73,7 +73,7 @@ Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const const string TempoSection::xml_state_node_name = "Tempo"; TempoSection::TempoSection (const XMLNode& node) - : MetricSection (0.0, 0, MusicTime) + : MetricSection (0.0, 0, MusicTime, true) , Tempo (TempoMap::default_tempo()) , _c_func (0.0) , _active (true) @@ -179,13 +179,13 @@ TempoSection::get_state() const char buf[256]; LocaleGuard lg; - snprintf (buf, sizeof (buf), "%f", pulse()); + 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), "%f", _beats_per_minute); + snprintf (buf, sizeof (buf), "%lf", _beats_per_minute); root->add_property ("beats-per-minute", buf); - snprintf (buf, sizeof (buf), "%f", _note_type); + snprintf (buf, sizeof (buf), "%lf", _note_type); root->add_property ("note-type", buf); snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no"); root->add_property ("movable", buf); @@ -453,7 +453,7 @@ TempoSection::time_at_pulse (const double& pulse) const const string MeterSection::xml_state_node_name = "Meter"; MeterSection::MeterSection (const XMLNode& node) - : MetricSection (0.0, 0, MusicTime), Meter (TempoMap::default_meter()) + : MetricSection (0.0, 0, MusicTime, false), Meter (TempoMap::default_meter()) { XMLProperty const * prop; LocaleGuard lg; @@ -569,12 +569,12 @@ MeterSection::get_state() const root->add_property ("bbt", buf); snprintf (buf, sizeof (buf), "%lf", beat()); root->add_property ("beat", buf); - snprintf (buf, sizeof (buf), "%f", _note_type); + 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), "%f", _divisions_per_bar); + snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar); root->add_property ("divisions-per-bar", buf); snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no"); root->add_property ("movable", buf); @@ -772,13 +772,13 @@ TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation) bool TempoMap::remove_meter_locked (const MeterSection& meter) { - Metrics::iterator i; - for (i = _metrics.begin(); i != _metrics.end(); ++i) { - TempoSection* t = 0; - if ((t = dynamic_cast (*i)) != 0) { - if (meter.frame() == (*i)->frame()) { - if (t->locked_to_meter()) { + if (meter.position_lock_style() == AudioTime) { + /* remove meter-locked tempo */ + for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) { + TempoSection* t = 0; + if ((t = dynamic_cast (*i)) != 0) { + if (t->locked_to_meter() && meter.frame() == (*i)->frame()) { delete (*i); _metrics.erase (i); break; @@ -787,7 +787,7 @@ TempoMap::remove_meter_locked (const MeterSection& meter) } } - for (i = _metrics.begin(); i != _metrics.end(); ++i) { + for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) { if (dynamic_cast (*i) != 0) { if (meter.frame() == (*i)->frame()) { if ((*i)->movable()) { @@ -980,18 +980,24 @@ TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame { TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls); t->set_locked_to_meter (locked_to_meter); + bool solved = false; do_insert (t); if (recompute) { if (pls == AudioTime) { - solve_map_frame (_metrics, t, t->frame()); + solved = solve_map_frame (_metrics, t, t->frame()); } else { - solve_map_pulse (_metrics, t, t->pulse()); + solved = solve_map_pulse (_metrics, t, t->pulse()); } recompute_meters (_metrics); } + if (!solved && recompute) { + warning << "Adding tempo may have left the tempo map unsolved." << endmsg; + recompute_map (_metrics); + } + return t; } @@ -1046,29 +1052,49 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T } MeterSection* -TempoMap::add_meter_locked (const Meter& meter, double beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute) +TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute) { const MeterSection& prev_m = meter_section_at_frame_locked (_metrics, frame - 1); const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse(); + TempoSection* mlt = 0; if (pls == AudioTime) { /* add meter-locked tempo */ - add_tempo_locked (tempo_at_frame_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true); + mlt = add_tempo_locked (tempo_at_frame_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true); + + if (!mlt) { + return 0; + } + } MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls); + bool solved = false; do_insert (new_meter); if (recompute) { if (pls == AudioTime) { - solve_map_frame (_metrics, new_meter, frame); + solved = solve_map_frame (_metrics, new_meter, frame); } else { - solve_map_bbt (_metrics, new_meter, where); + solved = solve_map_bbt (_metrics, new_meter, where); + /* required due to resetting the pulse of meter-locked tempi above. + Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead, + but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked). + */ + recompute_map (_metrics); } } + if (!solved && recompute) { + /* if this has failed to solve, there is little we can do other than to ensure that + the new map is recalculated. + */ + warning << "Adding meter may have left the tempo map unsolved." << endmsg; + recompute_map (_metrics); + } + return new_meter; } @@ -1224,14 +1250,15 @@ TempoMap::first_tempo () return *t; } void -TempoMap::recompute_tempos (Metrics& metrics) +TempoMap::recompute_tempi (Metrics& metrics) { TempoSection* prev_t = 0; for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { TempoSection* t; - if ((t = dynamic_cast (*i)) != 0) { + if ((*i)->is_tempo()) { + t = static_cast (*i); if (!t->active()) { continue; } @@ -1273,14 +1300,16 @@ TempoMap::recompute_meters (Metrics& metrics) MeterSection* prev_m = 0; for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) { - if ((meter = dynamic_cast (*mi)) != 0) { + if (!(*mi)->is_tempo()) { + meter = static_cast (*mi); if (meter->position_lock_style() == AudioTime) { double pulse = 0.0; pair b_bbt; TempoSection* meter_locked_tempo = 0; for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) { TempoSection* t; - if ((t = dynamic_cast (*ii)) != 0) { + if ((*ii)->is_tempo()) { + t = static_cast (*ii); if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) { meter_locked_tempo = t; break; @@ -1361,7 +1390,7 @@ TempoMap::recompute_map (Metrics& metrics, framepos_t end) return; } - recompute_tempos (metrics); + recompute_tempi (metrics); recompute_meters (metrics); } @@ -1410,7 +1439,8 @@ TempoMap::metric_at (BBT_Time bbt) const for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) { MeterSection* mw; - if ((mw = dynamic_cast (*i)) != 0) { + if (!(*i)->is_tempo()) { + mw = static_cast (*i); BBT_Time section_start (mw->bbt()); if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) { @@ -1440,13 +1470,12 @@ TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) MeterSection* next_m = 0; for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { - MeterSection* m; - if ((m = dynamic_cast (*i)) != 0) { - if (prev_m && m->frame() > frame) { - next_m = m; + if (!(*i)->is_tempo()) { + if (prev_m && (*i)->frame() > frame) { + next_m = static_cast (*i); break; } - prev_m = m; + prev_m = static_cast (*i); } } if (frame < prev_m->frame()) { @@ -1462,19 +1491,44 @@ TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) return beat; } -framecnt_t +framepos_t TempoMap::frame_at_beat (const double& beat) const { Glib::Threads::RWLock::ReaderLock lm (lock); return frame_at_beat_locked (_metrics, beat); } -/* meter section based */ -framecnt_t +/* meter & tempo section based */ +framepos_t TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const { - const TempoSection* prev_t = &tempo_section_at_beat_locked (metrics, beat); - const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat); + MeterSection* prev_m = 0; + TempoSection* prev_t = 0; + + MeterSection* m; + + for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); + if (prev_m && m->beat() > beat) { + break; + } + prev_m = m; + } + } + + TempoSection* t; + + for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { + if ((*i)->is_tempo()) { + t = static_cast (*i); + if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) { + break; + } + prev_t = t; + } + + } return prev_t->frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate); } @@ -1491,9 +1545,11 @@ TempoMap::tempo_at_frame_locked (const Metrics& metrics, const framepos_t& frame { TempoSection* prev_t = 0; + TempoSection* t; + for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) { - TempoSection* t; - if ((t = dynamic_cast (*i)) != 0) { + if ((*i)->is_tempo()) { + t = static_cast (*i); if (!t->active()) { continue; } @@ -1536,7 +1592,8 @@ TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) con for (i = _metrics.begin(); i != _metrics.end(); ++i) { TempoSection* t; - if ((t = dynamic_cast (*i)) != 0) { + if ((*i)->is_tempo()) { + t = static_cast (*i); if (!t->active()) { continue; @@ -1552,8 +1609,7 @@ TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) con const double prev_t_ppm = prev_t->beats_per_minute() / prev_t->note_type(); if ((t_ppm > tempo_ppm && prev_t_ppm < tempo_ppm) || (t_ppm < tempo_ppm && prev_t_ppm > tempo_ppm)) { - const framepos_t ret_frame = prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate); - return ret_frame; + return prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate); } } prev_t = t; @@ -1606,7 +1662,8 @@ TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) con for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { MeterSection* m; - if ((m = dynamic_cast (*i)) != 0) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); if (prev_m && m->pulse() > pulse) { if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) { break; @@ -1621,7 +1678,7 @@ TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) con } double -TempoMap::pulse_at_frame (const framecnt_t& frame) const +TempoMap::pulse_at_frame (const framepos_t& frame) const { Glib::Threads::RWLock::ReaderLock lm (lock); return pulse_at_frame_locked (_metrics, frame); @@ -1629,14 +1686,15 @@ TempoMap::pulse_at_frame (const framecnt_t& frame) const /* tempo section based */ double -TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const +TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const { /* HOLD (at least) THE READER LOCK */ TempoSection* prev_t = 0; for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { TempoSection* t; - if ((t = dynamic_cast (*i)) != 0) { + if ((*i)->is_tempo()) { + t = static_cast (*i); if (!t->active()) { continue; } @@ -1655,7 +1713,7 @@ TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame return pulses_in_section + prev_t->pulse(); } -framecnt_t +framepos_t TempoMap::frame_at_pulse (const double& pulse) const { Glib::Threads::RWLock::ReaderLock lm (lock); @@ -1663,7 +1721,7 @@ TempoMap::frame_at_pulse (const double& pulse) const } /* tempo section based */ -framecnt_t +framepos_t TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const { /* HOLD THE READER LOCK */ @@ -1673,7 +1731,8 @@ TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) co for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { TempoSection* t; - if ((t = dynamic_cast (*i)) != 0) { + if ((*i)->is_tempo()) { + t = static_cast (*i); if (!t->active()) { continue; } @@ -1685,12 +1744,9 @@ TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) co } } /* must be treated as constant, irrespective of _type */ - double const pulses_in_section = pulse - prev_t->pulse(); - double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate); - - framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame(); + double const dtime = (pulse - prev_t->pulse()) * prev_t->frames_per_pulse (_frame_rate); - return ret; + return (framecnt_t) floor (dtime) + prev_t->frame(); } double @@ -1711,9 +1767,11 @@ TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& /* because audio-locked meters have 'fake' integral beats, there is no pulse offset here. */ + MeterSection* m; + for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { - MeterSection* m; - if ((m = dynamic_cast (*i)) != 0) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); if (prev_m) { const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar(); if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) { @@ -1745,10 +1803,11 @@ TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const MeterSection* prev_m = 0; const double beats = max (0.0, b); - for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { - MeterSection* m = 0; + MeterSection* m = 0; - if ((m = dynamic_cast (*i)) != 0) { + for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); if (prev_m) { if (m->beat() > beats) { /* this is the meter after the one our beat is on*/ @@ -1807,9 +1866,11 @@ TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& /* because audio-locked meters have 'fake' integral beats, there is no pulse offset here. */ + MeterSection* m; + for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { - MeterSection* m; - if ((m = dynamic_cast (*i)) != 0) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); if (prev_m) { if (m->bbt().bars > bbt.bars) { break; @@ -1839,10 +1900,12 @@ TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) cons { MeterSection* prev_m = 0; + MeterSection* m = 0; + for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { - MeterSection* m = 0; - if ((m = dynamic_cast (*i)) != 0) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); if (prev_m) { double const pulses_to_m = m->pulse() - prev_m->pulse(); @@ -1924,9 +1987,64 @@ TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame) warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg; return bbt; } - const double beat = beat_at_frame_locked (metrics, frame); - return bbt_at_beat_locked (metrics, beat); + const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame); + MeterSection* prev_m = 0; + MeterSection* next_m = 0; + + MeterSection* m; + + for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); + if (prev_m && m->frame() > frame) { + next_m = m; + break; + } + prev_m = m; + } + } + + double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor(); + + /* handle frame before first meter */ + if (frame < prev_m->frame()) { + beat = 0.0; + } + /* audio locked meters fake their beat */ + if (next_m && next_m->beat() < beat) { + beat = next_m->beat(); + } + + beat = max (0.0, beat); + + const double beats_in_ms = beat - prev_m->beat(); + const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar()); + const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1); + const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar()); + const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat; + + BBT_Time ret; + + ret.ticks = (uint32_t) floor (remaining_ticks + 0.5); + ret.beats = (uint32_t) floor (remaining_beats); + ret.bars = total_bars; + + /* 0 0 0 to 1 1 0 - based mapping*/ + ++ret.bars; + ++ret.beats; + + if (ret.ticks >= BBT_Time::ticks_per_beat) { + ++ret.beats; + ret.ticks -= BBT_Time::ticks_per_beat; + } + + if (ret.beats >= prev_m->divisions_per_bar() + 1) { + ++ret.bars; + ret.beats = 1; + } + + return ret; } framepos_t @@ -1964,7 +2082,8 @@ TempoMap::check_solved (const Metrics& metrics) const for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { TempoSection* t; MeterSection* m; - if ((t = dynamic_cast (*i)) != 0) { + if ((*i)->is_tempo()) { + t = static_cast (*i); if (!t->active()) { continue; } @@ -1992,13 +2111,21 @@ TempoMap::check_solved (const Metrics& metrics) const prev_t = t; } - if ((m = dynamic_cast (*i)) != 0) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); if (prev_m && m->position_lock_style() == AudioTime) { - TempoSection* t = const_cast(&tempo_section_at_frame_locked (metrics, m->frame() - 1)); - const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(); - const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate); - - if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) { + const TempoSection* t = &tempo_section_at_frame_locked (metrics, m->frame() - 1); + const framepos_t nascent_m_frame = t->frame_at_pulse (m->pulse(), _frame_rate); + /* Here we check that a preceding section of music doesn't overlap a subsequent one. + It is complicated by the fact that audio locked meters represent a discontinuity in the pulse + (they place an exact pulse at a particular time expressed only in frames). + This has the effect of shifting the calculated frame at the meter pulse (wrt the previous section of music) + away from its actual frame (which is now the frame location of the exact pulse). + This can result in the calculated frame (from the previous musical section) + differing from the exact frame by one sample. + Allow for that. + */ + if (t && (nascent_m_frame > m->frame() + 1 || nascent_m_frame < 0)) { return false; } } @@ -2016,7 +2143,8 @@ TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame) { for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { TempoSection* t; - if ((t = dynamic_cast (*i)) != 0) { + if ((*i)->is_tempo()) { + t = static_cast (*i); if (!t->movable()) { t->set_active (true); continue; @@ -2044,7 +2172,8 @@ TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const fram /* can't move a tempo before the first meter */ for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) { MeterSection* m; - if ((m = dynamic_cast (*i)) != 0) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); if (!m->movable()) { first_m_frame = m->frame(); break; @@ -2060,7 +2189,8 @@ TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const fram for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) { TempoSection* t; - if ((t = dynamic_cast (*i)) != 0) { + if ((*i)->is_tempo()) { + t = static_cast (*i); if (!t->active()) { continue; @@ -2095,7 +2225,7 @@ TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const fram } #if (0) - recompute_tempos (imaginary); + recompute_tempi (imaginary); if (check_solved (imaginary)) { return true; @@ -2107,7 +2237,7 @@ TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const fram MetricSectionFrameSorter fcmp; imaginary.sort (fcmp); - recompute_tempos (imaginary); + recompute_tempi (imaginary); if (check_solved (imaginary)) { return true; @@ -2126,7 +2256,8 @@ TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const doub for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) { TempoSection* t; - if ((t = dynamic_cast (*i)) != 0) { + if ((*i)->is_tempo()) { + t = static_cast (*i); if (!t->active()) { continue; } @@ -2160,7 +2291,7 @@ TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const doub } #if (0) - recompute_tempos (imaginary); + recompute_tempi (imaginary); if (check_solved (imaginary)) { return true; @@ -2172,7 +2303,7 @@ TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const doub MetricSectionSorter cmp; imaginary.sort (cmp); - recompute_tempos (imaginary); + recompute_tempi (imaginary); /* Reordering * XX need a restriction here, but only for this case, * as audio locked tempos don't interact in the same way. @@ -2210,7 +2341,8 @@ TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const fram for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) { TempoSection* t; - if ((t = dynamic_cast (*ii)) != 0) { + if ((*ii)->is_tempo()) { + t = static_cast (*ii); if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) { meter_locked_tempo = t; break; @@ -2229,7 +2361,8 @@ TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const fram for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) { MeterSection* m; - if ((m = dynamic_cast (*i)) != 0) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); if (m == section){ if (prev_m && section->movable()) { const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor(); @@ -2338,7 +2471,8 @@ TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Ti /* disallow setting section to an existing meter's bbt */ for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) { MeterSection* m; - if ((m = dynamic_cast (*i)) != 0) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); if (m != section && m->bbt().bars == when.bars) { return false; } @@ -2350,7 +2484,8 @@ TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Ti for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) { MeterSection* m; - if ((m = dynamic_cast (*i)) != 0) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); pair b_bbt; double new_pulse = 0.0; @@ -2372,7 +2507,8 @@ TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Ti for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) { TempoSection* t; - if ((t = dynamic_cast (*ii)) != 0) { + if ((*ii)->is_tempo()) { + t = static_cast (*ii); if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) { meter_locked_tempo = t; break; @@ -2455,7 +2591,8 @@ TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSe for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { TempoSection* t; MeterSection* m; - if ((t = dynamic_cast (*i)) != 0) { + if ((*i)->is_tempo()) { + t = static_cast (*i); if (t == section) { ret = new TempoSection (*t); copy.push_back (ret); @@ -2465,7 +2602,8 @@ TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSe TempoSection* cp = new TempoSection (*t); copy.push_back (cp); } - if ((m = dynamic_cast (*i)) != 0) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); MeterSection* cp = new MeterSection (*m); copy.push_back (cp); } @@ -2482,12 +2620,14 @@ TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSe for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { TempoSection* t; MeterSection* m; - if ((t = dynamic_cast (*i)) != 0) { + if ((*i)->is_tempo()) { + t = static_cast (*i); TempoSection* cp = new TempoSection (*t); copy.push_back (cp); } - if ((m = dynamic_cast (*i)) != 0) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); if (m == section) { ret = new MeterSection (*m); copy.push_back (ret); @@ -2633,7 +2773,7 @@ TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame) if (solve_map_frame (future_map, copy, frame)) { solve_map_frame (_metrics, ms, frame); - recompute_tempos (_metrics); + recompute_tempi (_metrics); } } } else { @@ -2646,7 +2786,7 @@ TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame) if (solve_map_bbt (future_map, copy, bbt)) { solve_map_bbt (_metrics, ms, bbt); - recompute_tempos (_metrics); + recompute_tempi (_metrics); } } } @@ -2669,7 +2809,7 @@ TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm) Glib::Threads::RWLock::WriterLock lm (lock); TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts); tempo_copy->set_beats_per_minute (bpm.beats_per_minute()); - recompute_tempos (future_map); + recompute_tempi (future_map); if (check_solved (future_map)) { ts->set_beats_per_minute (bpm.beats_per_minute()); @@ -2720,7 +2860,8 @@ TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const fra TempoSection* next_t = 0; for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) { TempoSection* t = 0; - if ((t = dynamic_cast (*i)) != 0) { + if ((*i)->is_tempo()) { + t = static_cast (*i); if (t->frame() > ts->frame()) { next_t = t; break; @@ -2817,12 +2958,12 @@ TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const fra } new_bpm = min (new_bpm, (double) 1000.0); prev_t->set_beats_per_minute (new_bpm); - recompute_tempos (future_map); + recompute_tempi (future_map); recompute_meters (future_map); if (check_solved (future_map)) { ts->set_beats_per_minute (new_bpm); - recompute_tempos (_metrics); + recompute_tempi (_metrics); recompute_meters (_metrics); } } @@ -2837,7 +2978,7 @@ TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const fra } double -TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t& sub_num) +TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) { Glib::Threads::RWLock::ReaderLock lm (lock); @@ -2845,7 +2986,7 @@ TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t& sub_num) } double -TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t& sub_num) +TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) { double beat = beat_at_frame_locked (metrics, frame); if (sub_num > 1) { @@ -3073,13 +3214,14 @@ TempoMap::tempo_section_at_frame (framepos_t frame) const const TempoSection& TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const { - Metrics::const_iterator i; TempoSection* prev = 0; - for (i = metrics.begin(); i != metrics.end(); ++i) { - TempoSection* t; + TempoSection* t; - if ((t = dynamic_cast (*i)) != 0) { + for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { + + if ((*i)->is_tempo()) { + t = static_cast (*i); if (!t->active()) { continue; } @@ -3105,9 +3247,11 @@ TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& be TempoSection* prev_t = 0; const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat); + TempoSection* t; + for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { - TempoSection* t; - if ((t = dynamic_cast (*i)) != 0) { + if ((*i)->is_tempo()) { + t = static_cast (*i); if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) { break; } @@ -3126,21 +3270,23 @@ TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) con { Glib::Threads::RWLock::ReaderLock lm (lock); - const TempoSection* ts_at = &tempo_section_at_frame_locked (_metrics, frame); + const TempoSection* ts_at = 0; const TempoSection* ts_after = 0; Metrics::const_iterator i; + TempoSection* t; for (i = _metrics.begin(); i != _metrics.end(); ++i) { - TempoSection* t; - if ((t = dynamic_cast (*i)) != 0) { + if ((*i)->is_tempo()) { + t = static_cast (*i); if (!t->active()) { continue; } - if ((*i)->frame() > frame) { + if (ts_at && (*i)->frame() > frame) { ts_after = t; break; } + ts_at = t; } } @@ -3157,10 +3303,12 @@ TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t fram Metrics::const_iterator i; MeterSection* prev = 0; + MeterSection* m; + for (i = metrics.begin(); i != metrics.end(); ++i) { - MeterSection* m; - if ((m = dynamic_cast (*i)) != 0) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); if (prev && (*i)->frame() > frame) { break; @@ -3193,7 +3341,8 @@ TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& be for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { MeterSection* m; - if ((m = dynamic_cast (*i)) != 0) { + if (!(*i)->is_tempo()) { + m = static_cast (*i); if (prev_m && m->beat() > beat) { break; } @@ -3322,6 +3471,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/) catch (failed_constructor& err){ error << _("Tempo map: could not set new state, restoring old one.") << endmsg; _metrics = old_metrics; + old_metrics.clear(); break; } @@ -3335,6 +3485,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/) catch (failed_constructor& err) { error << _("Tempo map: could not set new state, restoring old one.") << endmsg; _metrics = old_metrics; + old_metrics.clear(); break; } } @@ -3386,6 +3537,13 @@ TempoMap::set_state (const XMLNode& node, int /*version*/) } recompute_map (_metrics); + + Metrics::const_iterator d = old_metrics.begin(); + while (d != old_metrics.end()) { + delete (*d); + ++d; + } + old_metrics.clear (); } PropertyChanged (PropertyChange ()); @@ -3427,7 +3585,7 @@ TempoMap::n_tempos() const int cnt = 0; for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) { - if (dynamic_cast(*i) != 0) { + if ((*i)->is_tempo()) { cnt++; } } @@ -3442,7 +3600,7 @@ TempoMap::n_meters() const int cnt = 0; for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) { - if (dynamic_cast(*i) != 0) { + if (!(*i)->is_tempo()) { cnt++; } } @@ -3641,11 +3799,32 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount) * pos can be -ve, if required. */ framepos_t -TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const +TempoMap::framepos_plus_beats (framepos_t frame, Evoral::Beats beats) const { Glib::Threads::RWLock::ReaderLock lm (lock); - return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) + beats.to_double()); + const TempoSection& ts = tempo_section_at_frame_locked (_metrics, frame); + MeterSection* prev_m = 0; + MeterSection* next_m = 0; + + for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) { + if (!(*i)->is_tempo()) { + if (prev_m && (*i)->frame() > frame) { + next_m = static_cast (*i); + break; + } + prev_m = static_cast (*i); + } + } + + double pos_beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor(); + + /* audio locked meters fake their beat */ + if (next_m && next_m->beat() < pos_beat) { + pos_beat = next_m->beat(); + } + + return frame_at_beat_locked (_metrics, pos_beat + beats.to_double()); } /** Subtract some (fractional) beats from a frame position, and return the result in frames */