X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Ftempo.cc;h=0bb2fea0cf3e0c6a22a1f25a07a916bdbab49045;hb=142439b9a847a15911be200e657eec0222aba2a7;hp=b00fffb3e0b613403854ba7b68cc86e7aa8e2d35;hpb=28df3238c224c4c9a5dd855b1c4219d44969f5d2;p=ardour.git diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index b00fffb3e0..0bb2fea0cf 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -19,17 +19,15 @@ #include #include +#include #include -#include - -#include +#include #include "pbd/xml++.h" #include "evoral/types.hpp" #include "ardour/debug.h" #include "ardour/tempo.h" -#include "ardour/utils.h" #include "i18n.h" #include @@ -45,12 +43,6 @@ using Timecode::BBT_Time; Meter TempoMap::_default_meter (4.0, 4.0); Tempo TempoMap::_default_tempo (120.0); -double -Tempo::frames_per_beat (framecnt_t sr) const -{ - return (60.0 * sr) / _beats_per_minute; -} - /***********************************************************************/ double @@ -182,8 +174,8 @@ TempoSection::update_bbt_time_from_bar_offset (const Meter& meter) new_start.bars = start().bars; double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset; - new_start.beats = (uint32_t) floor(ticks/BBT_Time::ticks_per_beat); - new_start.ticks = (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat); + new_start.beats = (uint32_t) floor (ticks/BBT_Time::ticks_per_beat); + new_start.ticks = 0; /* (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat); */ /* remember the 1-based counting properties of beats */ new_start.beats += 1; @@ -313,7 +305,7 @@ TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation) bool removed = false; { - Glib::RWLock::WriterLock lm (lock); + Glib::Threads::RWLock::WriterLock lm (lock); Metrics::iterator i; for (i = metrics.begin(); i != metrics.end(); ++i) { @@ -344,7 +336,7 @@ TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation) bool removed = false; { - Glib::RWLock::WriterLock lm (lock); + Glib::Threads::RWLock::WriterLock lm (lock); Metrics::iterator i; for (i = metrics.begin(); i != metrics.end(); ++i) { @@ -399,75 +391,84 @@ TempoMap::do_insert (MetricSection* section) } } - Metrics::iterator i; + /* Look for any existing MetricSection that is of the same type and - at the same time as the new one, and remove it before adding - the new one. + in the same bar as the new one, and remove it before adding + the new one. Note that this means that if we find a matching, + existing section, we can break out of the loop since we're + guaranteed that there is only one such match. */ - Metrics::iterator to_remove = metrics.end (); + for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) { + + bool const iter_is_tempo = dynamic_cast (*i) != 0; + bool const insert_is_tempo = dynamic_cast (section) != 0; - for (i = metrics.begin(); i != metrics.end(); ++i) { + if (iter_is_tempo && insert_is_tempo) { - int const c = (*i)->compare (*section); + /* Tempo sections */ - if (c < 0) { - /* this section is before the one to be added; go back round */ - continue; - } else if (c > 0) { - /* this section is after the one to be added; there can't be any at the same time */ - break; - } + if ((*i)->start().bars == section->start().bars && + (*i)->start().beats == section->start().beats) { - /* hacky comparison of type */ - bool const iter_is_tempo = dynamic_cast (*i) != 0; - bool const insert_is_tempo = dynamic_cast (section) != 0; + if (!(*i)->movable()) { + + /* can't (re)move this section, so overwrite + * its data content (but not its properties as + * a section). + */ + + *(dynamic_cast(*i)) = *(dynamic_cast(section)); + need_add = false; + } else { + metrics.erase (i); + } + break; + } - if (iter_is_tempo == insert_is_tempo) { + } else if (!iter_is_tempo && !insert_is_tempo) { - if (!(*i)->movable()) { + /* Meter Sections */ - /* can't (re)move this section, so overwrite - * its data content (but not its properties as - * a section - */ + if ((*i)->start().bars == section->start().bars) { - if (!iter_is_tempo) { + if (!(*i)->movable()) { + + /* can't (re)move this section, so overwrite + * its data content (but not its properties as + * a section + */ + *(dynamic_cast(*i)) = *(dynamic_cast(section)); + need_add = false; } else { - *(dynamic_cast(*i)) = *(dynamic_cast(section)); + metrics.erase (i); + } - need_add = false; + break; } - - to_remove = i; - break; + } else { + /* non-matching types, so we don't care */ } } - if (to_remove != metrics.end()) { - /* remove the MetricSection at the same time as the one we are about to add */ - metrics.erase (to_remove); - } - - /* Add the given MetricSection */ + /* Add the given MetricSection, if we didn't just reset an existing + * one above + */ if (need_add) { + + Metrics::iterator i; + for (i = metrics.begin(); i != metrics.end(); ++i) { - - if ((*i)->compare (*section) < 0) { - continue; + if ((*i)->start() > section->start()) { + break; } - - metrics.insert (i, section); - break; - } - - if (i == metrics.end()) { - metrics.insert (metrics.end(), section); } + + metrics.insert (i, section); } } @@ -476,12 +477,12 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_T { const TempoSection& first (first_tempo()); - if (ts != first) { + if (ts.start() != first.start()) { remove_tempo (ts, false); add_tempo (tempo, where); } else { { - Glib::RWLock::WriterLock lm (lock); + Glib::Threads::RWLock::WriterLock lm (lock); /* cannot move the first tempo section */ *((Tempo*)&first) = tempo; recompute_map (false); @@ -495,7 +496,7 @@ void TempoMap::add_tempo (const Tempo& tempo, BBT_Time where) { { - Glib::RWLock::WriterLock lm (lock); + Glib::Threads::RWLock::WriterLock lm (lock); /* new tempos always start on a beat */ where.ticks = 0; @@ -546,12 +547,12 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T { const MeterSection& first (first_meter()); - if (ms != first) { + if (ms.start() != first.start()) { remove_meter (ms, false); add_meter (meter, where); } else { { - Glib::RWLock::WriterLock lm (lock); + Glib::Threads::RWLock::WriterLock lm (lock); /* cannot move the first meter section */ *((Meter*)&first) = meter; recompute_map (true); @@ -565,7 +566,7 @@ void TempoMap::add_meter (const Meter& meter, BBT_Time where) { { - Glib::RWLock::WriterLock lm (lock); + Glib::Threads::RWLock::WriterLock lm (lock); /* a new meter always starts a new bar on the first beat. so round the start time appropriately. remember that @@ -605,7 +606,7 @@ TempoMap::change_initial_tempo (double beats_per_minute, double note_type) for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) { if ((t = dynamic_cast (*i)) != 0) { { - Glib::RWLock::WriterLock lm (lock); + Glib::Threads::RWLock::WriterLock lm (lock); *((Tempo*) t) = newtempo; recompute_map (false); } @@ -655,7 +656,7 @@ TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, d /* reset */ { - Glib::RWLock::WriterLock lm (lock); + Glib::Threads::RWLock::WriterLock lm (lock); /* cannot move the first tempo section */ *((Tempo*)prev) = newtempo; recompute_map (false); @@ -699,7 +700,7 @@ TempoMap::first_tempo () const void TempoMap::require_map_to (framepos_t pos) { - Glib::RWLock::WriterLock lm (lock); + Glib::Threads::RWLock::WriterLock lm (lock); if (_map.empty() || _map.back().frame < pos) { extend_map (pos); @@ -709,7 +710,7 @@ TempoMap::require_map_to (framepos_t pos) void TempoMap::require_map_to (const BBT_Time& bbt) { - Glib::RWLock::WriterLock lm (lock); + Glib::Threads::RWLock::WriterLock lm (lock); /* since we have no idea where BBT is if its off the map, see the last * point in the map is past BBT, and if not add an arbitrary amount of @@ -741,12 +742,11 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end) if (end < 0) { - if (_map.empty()) { - /* compute 1 mins worth */ - end = _frame_rate * 60; - } else { - end = _map.back().frame; - } + /* we will actually stop once we hit + the last metric. + */ + end = max_framepos; + } else { if (!_map.empty ()) { /* never allow the map to be shortened */ @@ -883,14 +883,21 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, TempoSection* ts; MeterSection* ms; - double divisions_per_bar; double beat_frames; + framepos_t bar_start_frame; + + DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame)); + + if (current.beats == 1) { + bar_start_frame = current_frame; + } else { + bar_start_frame = 0; + } - divisions_per_bar = meter->divisions_per_bar (); beat_frames = meter->frames_per_grid (*tempo,_frame_rate); while (current_frame < end) { - + current.beats++; current_frame += beat_frames; @@ -907,7 +914,7 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, if (!(current < (*next_metric)->start())) { - set_metrics: + set_metrics: if (((ts = dynamic_cast (*next_metric)) != 0)) { tempo = ts; @@ -932,14 +939,32 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, double next_beat_frames = tempo->frames_per_beat (_frame_rate); DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n", - tempo->start(), current_frame, tempo->bar_offset())); + tempo->start(), current_frame, tempo->bar_offset())); /* back up to previous beat */ current_frame -= beat_frames; - /* set tempo section location based on offset from last beat */ - tempo->set_frame (current_frame + (ts->bar_offset() * beat_frames)); - /* advance to the location of the new (adjusted) beat */ - current_frame += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames); + + /* set tempo section location + * based on offset from last + * bar start + */ + tempo->set_frame (bar_start_frame + + llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames))); + + /* advance to the location of + * the new (adjusted) beat. do + * this by figuring out the + * offset within the beat that + * would have been there + * without the tempo + * change. then stretch the + * beat accordingly. + */ + + double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames; + + current_frame += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames); + /* next metric doesn't have to * match this precisely to * merit a reloop ... @@ -949,7 +974,7 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, } else { DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n", - tempo->start(), current_frame)); + tempo->start(), current_frame)); tempo->set_frame (current_frame); } @@ -962,46 +987,54 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, */ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n", - meter->start(), current, current_frame)); + meter->start(), current, current_frame)); assert (current.beats == 1); meter->set_frame (current_frame); } - divisions_per_bar = meter->divisions_per_bar (); beat_frames = meter->frames_per_grid (*tempo, _frame_rate); DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n", - beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo))); + beat_frames, meter->divisions_per_bar(), *((Meter*)meter), *((Tempo*)tempo))); ++next_metric; if (next_metric != metrics.end() && ((*next_metric)->start() == current)) { /* same position so go back and set this one up before advancing - */ + */ goto set_metrics; } + } - } + } if (current.beats == 1) { DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame)); _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1)); + bar_start_frame = current_frame; } else { DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame)); _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats)); } + + if (next_metric == metrics.end()) { + /* no more metrics - we've timestamped them all, stop here */ + if (end == max_framepos) { + DEBUG_TRACE (DEBUG::TempoMath, string_compose ("stop extending map now that we've reach the end @ %1|%2 = %3\n", + current.bars, current.beats, current_frame)); + break; + } + } } } TempoMetric -TempoMap::metric_at (framepos_t frame) const +TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const { - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); TempoMetric m (first_meter(), first_tempo()); - const Meter* meter; - const Tempo* tempo; /* 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 @@ -1012,33 +1045,25 @@ TempoMap::metric_at (framepos_t frame) const for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { - // cerr << "Looking at a metric section " << **i << endl; - if ((*i)->frame() > frame) { break; } - if ((tempo = dynamic_cast(*i)) != 0) { - m.set_tempo (*tempo); - } else if ((meter = dynamic_cast(*i)) != 0) { - m.set_meter (*meter); - } + m.set_metric(*i); - m.set_frame ((*i)->frame ()); - m.set_start ((*i)->start ()); + if (last) { + *last = i; + } } - // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << " location " << m.frame() << " = " << m.start() << endl; return m; } TempoMetric TempoMap::metric_at (BBT_Time bbt) const { - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); TempoMetric m (first_meter(), first_tempo()); - const Meter* meter; - const Tempo* tempo; /* 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 @@ -1055,14 +1080,7 @@ TempoMap::metric_at (BBT_Time bbt) const break; } - if ((tempo = dynamic_cast(*i)) != 0) { - m.set_tempo (*tempo); - } else if ((meter = dynamic_cast(*i)) != 0) { - m.set_meter (*meter); - } - - m.set_frame ((*i)->frame ()); - m.set_start (section_start); + m.set_metric (*i); } return m; @@ -1073,14 +1091,23 @@ TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt) { require_map_to (frame); - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); + + if (frame < 0) { + bbt.bars = 1; + bbt.beats = 1; + bbt.ticks = 0; + warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg; + return; + } + return bbt_time (frame, bbt, bbt_before_or_at (frame)); } void TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt) { - Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK); + Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK); if (!lm.locked()) { throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map"); @@ -1105,16 +1132,25 @@ TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_i bbt.ticks = 0; } else { bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) * - BBT_Time::ticks_per_beat); + BBT_Time::ticks_per_beat); } } framepos_t TempoMap::frame_time (const BBT_Time& bbt) { + if (bbt.bars < 1) { + warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg; + return 0; + } + + if (bbt.beats < 1) { + throw std::logic_error ("beats are counted from one"); + } + require_map_to (bbt); - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0)); BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0)); @@ -1130,18 +1166,15 @@ TempoMap::frame_time (const BBT_Time& bbt) framecnt_t TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir) { - Glib::RWLock::ReaderLock lm (lock); - framecnt_t frames = 0; BBT_Time when; - bbt_time (pos, when); - frames = bbt_duration_at_unlocked (when, bbt,dir); - - return frames; + + Glib::Threads::RWLock::ReaderLock lm (lock); + return bbt_duration_at_unlocked (when, bbt, dir); } framecnt_t -TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) +TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int /*dir*/) { if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) { return 0; @@ -1150,16 +1183,9 @@ TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, i /* round back to the previous precise beat */ BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0)); BBTPointList::const_iterator start (wi); - double tick_frames = 0; assert (wi != _map.end()); - /* compute how much rounding we did because of non-zero ticks */ - - if (when.ticks != 0) { - tick_frames = (*wi).tempo->frames_per_beat (_frame_rate) * (when.ticks/BBT_Time::ticks_per_beat); - } - uint32_t bars = 0; uint32_t beats = 0; @@ -1180,10 +1206,11 @@ TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, i /* add any additional frames related to ticks in the added value */ if (bbt.ticks != 0) { - tick_frames += (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat); + return ((*wi).frame - (*start).frame) + + (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat); + } else { + return ((*wi).frame - (*start).frame); } - - return ((*wi).frame - (*start).frame) + llrint (tick_frames); } framepos_t @@ -1203,7 +1230,7 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir) { require_map_to (fr); - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); BBTPointList::const_iterator i = bbt_before_or_at (fr); BBT_Time the_beat; uint32_t ticks_one_subdivisions_worth; @@ -1322,7 +1349,7 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) { require_map_to (frame); - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); BBTPointList::const_iterator fi; if (dir > 0) { @@ -1333,7 +1360,8 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) assert (fi != _map.end()); - DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3|%4 @ %5) to bars in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame)); + DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round from %1 (%3|%4 @ %5) to %6 in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame, + (type == Bar ? "bar" : "beat"))); switch (type) { case Bar: @@ -1440,9 +1468,11 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) BBTPointList::const_iterator prev = fi; BBTPointList::const_iterator next = fi; - if (prev != _map.begin()) { - --prev; - } + + /* fi is already the beat before_or_at frame, and + we've just established that its not at frame, so its + the beat before frame. + */ ++next; if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) { @@ -1465,7 +1495,7 @@ TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin, framepos_t lower, framepos_t upper) { { - Glib::RWLock::WriterLock lm (lock); + Glib::Threads::RWLock::WriterLock lm (lock); if (_map.empty() || (_map.back().frame < upper)) { recompute_map (false, upper); } @@ -1478,7 +1508,7 @@ TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin, const TempoSection& TempoMap::tempo_section_at (framepos_t frame) const { - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); Metrics::const_iterator i; TempoSection* prev = 0; @@ -1524,7 +1554,7 @@ TempoMap::get_state () XMLNode *root = new XMLNode ("TempoMap"); { - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); for (i = metrics.begin(); i != metrics.end(); ++i) { root->add_child_nocopy ((*i)->get_state()); } @@ -1537,13 +1567,12 @@ int TempoMap::set_state (const XMLNode& node, int /*version*/) { { - Glib::RWLock::WriterLock lm (lock); + Glib::Threads::RWLock::WriterLock lm (lock); XMLNodeList nlist; XMLNodeConstIterator niter; Metrics old_metrics (metrics); MeterSection* last_meter = 0; - metrics.clear(); nlist = node.children(); @@ -1591,7 +1620,29 @@ TempoMap::set_state (const XMLNode& node, int /*version*/) metrics.sort (cmp); } - recompute_map (true); + /* check for multiple tempo/meters at the same location, which + ardour2 somehow allowed. + */ + + Metrics::iterator prev = metrics.end(); + for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) { + if (prev != metrics.end()) { + if (dynamic_cast(*prev) && dynamic_cast(*i)) { + if ((*prev)->start() == (*i)->start()) { + error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg; + return -1; + } + } else if (dynamic_cast(*prev) && dynamic_cast(*i)) { + if ((*prev)->start() == (*i)->start()) { + error << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg; + return -1; + } + } + } + prev = i; + } + + recompute_map (true, -1); } PropertyChanged (PropertyChange ()); @@ -1602,7 +1653,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/) void TempoMap::dump (std::ostream& o) const { - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK); const MeterSection* m; const TempoSection* t; @@ -1621,7 +1672,7 @@ TempoMap::dump (std::ostream& o) const int TempoMap::n_tempos() const { - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); int cnt = 0; for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { @@ -1636,7 +1687,7 @@ TempoMap::n_tempos() const int TempoMap::n_meters() const { - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); int cnt = 0; for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { @@ -1652,7 +1703,7 @@ void TempoMap::insert_time (framepos_t where, framecnt_t amount) { { - Glib::RWLock::WriterLock lm (lock); + Glib::Threads::RWLock::WriterLock lm (lock); for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) { if ((*i)->frame() >= where && (*i)->movable ()) { (*i)->set_frame ((*i)->frame() + amount); @@ -1748,9 +1799,9 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount) framepos_t TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const { - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); Metrics::const_iterator next_tempo; - const TempoSection* tempo; + const TempoSection* tempo = 0; /* Find the starting tempo metric */ @@ -1785,8 +1836,9 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const next_tempo -> first tempo after "pos", possibly metrics.end() */ - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n", - pos, beats, *((Tempo*)tempo), tempo->frame())); + DEBUG_TRACE (DEBUG::TempoMath, + string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n", + pos, beats, *((const Tempo*)tempo), tempo->frame())); while (beats) { @@ -1816,7 +1868,7 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const tempo = dynamic_cast(*next_tempo); DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n", - *((Tempo*)tempo), tempo->frame(), + *((const Tempo*)tempo), tempo->frame(), tempo->frames_per_beat (_frame_rate))); while (next_tempo != metrics.end ()) { @@ -1833,11 +1885,11 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const return pos; } -/** Subtract some (fractional) beats to a frame position, and return the result in frames */ +/** Subtract some (fractional) beats from a frame position, and return the result in frames */ framepos_t TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const { - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); Metrics::const_reverse_iterator prev_tempo; const TempoSection* tempo = 0; @@ -1884,9 +1936,10 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const } } - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n", - pos, beats, *((Tempo*)tempo), tempo->frame(), - prev_tempo == metrics.rend())); + DEBUG_TRACE (DEBUG::TempoMath, + string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n", + pos, beats, *((const Tempo*)tempo), tempo->frame(), + prev_tempo == metrics.rend())); /* We now have: @@ -1921,9 +1974,10 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const tempo = dynamic_cast(*prev_tempo); - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n", - *((Tempo*)tempo), tempo->frame(), - tempo->frames_per_beat (_frame_rate))); + DEBUG_TRACE (DEBUG::TempoMath, + string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n", + *((const Tempo*)tempo), tempo->frame(), + tempo->frames_per_beat (_frame_rate))); while (prev_tempo != metrics.rend ()) { @@ -1946,13 +2000,14 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const framepos_t TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const { - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); Metrics::const_iterator i; const MeterSection* meter; const MeterSection* m; const TempoSection* tempo; const TempoSection* t; double frames_per_beat; + framepos_t effective_pos = max (pos, (framepos_t) 0); meter = &first_meter (); tempo = &first_tempo (); @@ -1964,7 +2019,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const for (i = metrics.begin(); i != metrics.end(); ++i) { - if ((*i)->frame() > pos) { + if ((*i)->frame() > effective_pos) { break; } @@ -2083,10 +2138,11 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const Evoral::MusicalTime TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const { - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); Metrics::const_iterator next_tempo; - const TempoSection* tempo; - + const TempoSection* tempo = 0; + framepos_t effective_pos = max (pos, (framepos_t) 0); + /* Find the relevant initial tempo metric */ for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) { @@ -2095,7 +2151,7 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const if ((t = dynamic_cast(*next_tempo)) != 0) { - if ((*next_tempo)->frame() > pos) { + if ((*next_tempo)->frame() > effective_pos) { break; } @@ -2109,33 +2165,55 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const next_tempo -> the next tempo after "pos", possibly metrics.end() */ + assert (tempo); + + DEBUG_TRACE (DEBUG::TempoMath, + string_compose ("frame %1 walk by %2 frames, start with tempo = %3 @ %4\n", + pos, distance, *((const Tempo*)tempo), tempo->frame())); + Evoral::MusicalTime beats = 0; while (distance) { /* End of this section */ - framepos_t const end = ((next_tempo == metrics.end()) ? max_framepos : (*next_tempo)->frame ()); - - /* Distance to the end in frames */ - framecnt_t const distance_to_end = end - pos; + framepos_t end; + /* Distance to `end' in frames */ + framepos_t distance_to_end; + + if (next_tempo == metrics.end ()) { + /* We can't do (end - pos) if end is max_framepos, as it will overflow if pos is -ve */ + end = max_framepos; + distance_to_end = max_framepos; + } else { + end = (*next_tempo)->frame (); + distance_to_end = end - pos; + } /* Amount to subtract this time */ double const sub = min (distance, distance_to_end); + DEBUG_TRACE (DEBUG::TempoMath, string_compose ("to reach end at %1 (end ? %2), distance= %3 sub=%4\n", end, (next_tempo == metrics.end()), + distance_to_end, sub)); + /* Update */ pos += sub; distance -= sub; + assert (tempo); beats += sub / tempo->frames_per_beat (_frame_rate); - + + DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1, beats = %2 distance left %3\n", + pos, beats, distance)); + /* Move on if there's anything to move to */ if (next_tempo != metrics.end()) { tempo = dynamic_cast(*next_tempo); - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n", - *((Tempo*)tempo), tempo->frame(), - tempo->frames_per_beat (_frame_rate))); + DEBUG_TRACE (DEBUG::TempoMath, + string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n", + *((const Tempo*)tempo), tempo->frame(), + tempo->frames_per_beat (_frame_rate))); while (next_tempo != metrics.end ()) { @@ -2145,7 +2223,16 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const break; } } + + if (next_tempo == metrics.end()) { + DEBUG_TRACE (DEBUG::TempoMath, "no more tempo sections\n"); + } else { + DEBUG_TRACE (DEBUG::TempoMath, string_compose ("next tempo section is %1 @ %2\n", + **next_tempo, (*next_tempo)->frame())); + } + } + assert (tempo); } return beats; @@ -2158,6 +2245,13 @@ TempoMap::bbt_before_or_at (framepos_t pos) BBTPointList::const_iterator i; + if (pos < 0) { + /* not really correct, but we should catch pos < 0 at a higher + level + */ + return _map.begin(); + } + i = lower_bound (_map.begin(), _map.end(), pos); assert (i != _map.end()); if ((*i).frame > pos) { @@ -2207,38 +2301,6 @@ TempoMap::bbt_after_or_at (framepos_t pos) return i; } -/** Compare the time of this with that of another MetricSection. - * @param with_bbt True to compare using start(), false to use frame(). - * @return -1 for less than, 0 for equal, 1 for greater than. - */ - -int -MetricSection::compare (const MetricSection& other) const -{ - if (start() == other.start()) { - return 0; - } else if (start() < other.start()) { - return -1; - } else { - return 1; - } - - /* NOTREACHED */ - return 0; -} - -bool -MetricSection::operator== (const MetricSection& other) const -{ - return compare (other) == 0; -} - -bool -MetricSection::operator!= (const MetricSection& other) const -{ - return compare (other) != 0; -} - std::ostream& operator<< (std::ostream& o, const Meter& m) { return o << m.divisions_per_bar() << '/' << m.note_divisor(); @@ -2258,9 +2320,9 @@ operator<< (std::ostream& o, const MetricSection& section) { const MeterSection* ms; if ((ts = dynamic_cast (§ion)) != 0) { - o << *((Tempo*) ts); + o << *((const Tempo*) ts); } else if ((ms = dynamic_cast (§ion)) != 0) { - o << *((Meter*) ms); + o << *((const Meter*) ms); } return o;