From f135947606e8d8374ff5567cf4bb0e0450ed3f84 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 2 Jan 2012 23:32:33 +0000 Subject: intermediate commit as all tempo/meter stuff starts to walk the precompute Bars|Beats list. Still have ::round_to_beat_subdivision() to fix. haven't really done any thorough testing at this point, but basic stuff seems OK git-svn-id: svn://localhost/ardour2/branches/3.0@11131 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/beats_frames_converter.h | 4 +- libs/ardour/ardour/tempo.h | 62 +-- libs/ardour/session_time.cc | 3 +- libs/ardour/tempo.cc | 608 ++++++++++++---------------- libs/timecode/timecode/bbt_time.h | 18 + 5 files changed, 303 insertions(+), 392 deletions(-) diff --git a/libs/ardour/ardour/beats_frames_converter.h b/libs/ardour/ardour/beats_frames_converter.h index ee79430e4e..e331b8411d 100644 --- a/libs/ardour/ardour/beats_frames_converter.h +++ b/libs/ardour/ardour/beats_frames_converter.h @@ -35,7 +35,7 @@ class TempoMap; */ class BeatsFramesConverter : public Evoral::TimeConverter { public: - BeatsFramesConverter (const TempoMap& tempo_map, framepos_t origin) + BeatsFramesConverter (TempoMap& tempo_map, framepos_t origin) : Evoral::TimeConverter (origin) , _tempo_map(tempo_map) {} @@ -44,7 +44,7 @@ public: double from (framepos_t frames) const; private: - const TempoMap& _tempo_map; + TempoMap& _tempo_map; }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index 87dc77780e..cd6c51df86 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -199,16 +199,20 @@ class TempoMap : public PBD::StatefulDestructible }; struct BBTPoint { - BBTPointType type; - framepos_t frame; - const Meter* meter; - const Tempo* tempo; - uint32_t bar; - uint32_t beat; - - BBTPoint (const Meter& m, const Tempo& t, framepos_t f, - BBTPointType ty, uint32_t b, uint32_t e) - : type (ty), frame (f), meter (&m), tempo (&t), bar (b), beat (e) {} + BBTPointType type; + framepos_t frame; + const Meter* meter; + const Tempo* tempo; + uint32_t bar; + uint32_t beat; + + Timecode::BBT_Time bbt() const { return Timecode::BBT_Time (bar, beat, 0); } + operator Timecode::BBT_Time() const { return bbt(); } + operator framepos_t() const { return frame; } + + BBTPoint (const Meter& m, const Tempo& t, framepos_t f, + BBTPointType ty, uint32_t b, uint32_t e) + : type (ty), frame (f), meter (&m), tempo (&t), bar (b), beat (e) {} }; typedef std::vector BBTPointList; @@ -221,9 +225,9 @@ class TempoMap : public PBD::StatefulDestructible const BBTPointList& map() const { return _map ; } void map (BBTPointList&, framepos_t start, framepos_t end); - void bbt_time (framepos_t when, Timecode::BBT_Time&) const; - framecnt_t frame_time (const Timecode::BBT_Time&) const; - framecnt_t bbt_duration_at (framepos_t, const Timecode::BBT_Time&, int dir) const; + void bbt_time (framepos_t when, Timecode::BBT_Time&); + framecnt_t frame_time (const Timecode::BBT_Time&); + framecnt_t bbt_duration_at (framepos_t, const Timecode::BBT_Time&, int dir); static const Tempo& default_tempo() { return _default_tempo; } static const Meter& default_meter() { return _default_meter; } @@ -257,16 +261,11 @@ class TempoMap : public PBD::StatefulDestructible TempoMetric metric_at (Timecode::BBT_Time bbt) const; TempoMetric metric_at (framepos_t) const; - void bbt_time_with_metric (framepos_t, Timecode::BBT_Time&, const TempoMetric&) const; - Timecode::BBT_Time bbt_add (const Timecode::BBT_Time&, const Timecode::BBT_Time&, const TempoMetric&) const; - Timecode::BBT_Time bbt_add (const Timecode::BBT_Time& a, const Timecode::BBT_Time& b) const; - Timecode::BBT_Time bbt_subtract (const Timecode::BBT_Time&, const Timecode::BBT_Time&) const; - - framepos_t framepos_plus_bbt (framepos_t pos, Timecode::BBT_Time b) const; - framepos_t framepos_plus_beats (framepos_t, Evoral::MusicalTime) const; - framepos_t framepos_minus_beats (framepos_t, Evoral::MusicalTime) const; - Evoral::MusicalTime framewalk_to_beats (framepos_t pos, framecnt_t distance) const; + framepos_t framepos_plus_bbt (framepos_t pos, Timecode::BBT_Time b); + framepos_t framepos_plus_beats (framepos_t, Evoral::MusicalTime); + framepos_t framepos_minus_beats (framepos_t, Evoral::MusicalTime); + Evoral::MusicalTime framewalk_to_beats (framepos_t pos, framecnt_t distance); void change_existing_tempo_at (framepos_t, double bpm, double note_type); void change_initial_tempo (double bpm, double note_type); @@ -291,25 +290,30 @@ class TempoMap : public PBD::StatefulDestructible BBTPointList _map; void recompute_map (bool reassign_tempo_bbt, framepos_t end = -1); + void require_map_to (framepos_t pos); + void require_map_to (const Timecode::BBT_Time&); + + BBTPointList::const_iterator bbt_before_or_at (framepos_t); + BBTPointList::const_iterator bbt_after_or_at (framepos_t); + BBTPointList::const_iterator bbt_point_for (const Timecode::BBT_Time&); void timestamp_metrics_from_audio_time (); framepos_t round_to_type (framepos_t fr, int dir, BBTPointType); - framepos_t frame_time_unlocked (const Timecode::BBT_Time&) const; - - void bbt_time_unlocked (framepos_t, Timecode::BBT_Time&) const; + void bbt_time_unlocked (framepos_t, Timecode::BBT_Time&); - framecnt_t bbt_duration_at_unlocked (const Timecode::BBT_Time& when, const Timecode::BBT_Time& bbt, int dir) const; + framecnt_t bbt_duration_at_unlocked (const Timecode::BBT_Time& when, const Timecode::BBT_Time& bbt, int dir); const MeterSection& first_meter() const; const TempoSection& first_tempo() const; - framecnt_t count_frames_between (const Timecode::BBT_Time&, const Timecode::BBT_Time&) const; - framecnt_t count_frames_with_metrics (const TempoMetric&, const TempoMetric&, const Timecode::BBT_Time&, const Timecode::BBT_Time&) const; - int move_metric_section (MetricSection&, const Timecode::BBT_Time& to); void do_insert (MetricSection* section); + + Timecode::BBT_Time bbt_add (const Timecode::BBT_Time&, const Timecode::BBT_Time&, const TempoMetric&) const; + Timecode::BBT_Time bbt_add (const Timecode::BBT_Time& a, const Timecode::BBT_Time& b) const; + Timecode::BBT_Time bbt_subtract (const Timecode::BBT_Time&, const Timecode::BBT_Time&) const; }; }; /* namespace ARDOUR */ diff --git a/libs/ardour/session_time.cc b/libs/ardour/session_time.cc index cf8ef084b2..bf7a43845b 100644 --- a/libs/ardour/session_time.cc +++ b/libs/ardour/session_time.cc @@ -484,7 +484,8 @@ Session::jack_timebase_callback (jack_transport_state_t /*state*/, if (_tempo_map) { TempoMetric metric (_tempo_map->metric_at (_transport_frame)); - _tempo_map->bbt_time_with_metric (_transport_frame, bbt, metric); + + _tempo_map->bbt_time (_transport_frame, bbt); pos->bar = bbt.bars; pos->beat = bbt.beats; diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index 2ebe85f27b..95deb48ef5 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -322,14 +322,15 @@ TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation) } } } - } - if (removed) { - if (complete_operation) { + if (removed && complete_operation) { recompute_map (false); - PropertyChanged (PropertyChange ()); } } + + if (removed && complete_operation) { + PropertyChanged (PropertyChange ()); + } } void @@ -352,19 +353,24 @@ TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation) } } } - } - - if (removed) { - if (complete_operation) { + + if (removed && complete_operation) { recompute_map (true); - PropertyChanged (PropertyChange ()); } + + + } + + if (removed && complete_operation) { + PropertyChanged (PropertyChange ()); } } void TempoMap::do_insert (MetricSection* section) { + /* CALLER MUST HOLD WRITE LOCK */ + bool reassign_tempo_bbt = false; assert (section->start().ticks == 0); @@ -458,6 +464,7 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_T remove_tempo (ts, false); add_tempo (tempo, where); } else { + Glib::RWLock::WriterLock lm (lock); /* cannot move the first tempo section */ *((Tempo*)&first) = tempo; recompute_map (false); @@ -522,6 +529,7 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T remove_meter (ms, false); add_meter (meter, where); } else { + Glib::RWLock::WriterLock lm (lock); /* cannot move the first meter section */ *((Meter*)&first) = meter; recompute_map (true); @@ -571,7 +579,11 @@ 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) { - *((Tempo*) t) = newtempo; + { + Glib::RWLock::WriterLock lm (lock); + *((Tempo*) t) = newtempo; + recompute_map (false); + } PropertyChanged (PropertyChange ()); break; } @@ -617,7 +629,13 @@ TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, d /* reset */ - *((Tempo*)prev) = newtempo; + { + Glib::RWLock::WriterLock lm (lock); + /* cannot move the first tempo section */ + *((Tempo*)prev) = newtempo; + recompute_map (false); + } + PropertyChanged (PropertyChange ()); } @@ -686,8 +704,8 @@ TempoMap::timestamp_metrics_from_audio_time () // which is correct for our purpose } - bbt_time_with_metric ((*i)->frame(), bbt, metric); - + bbt_time_unlocked ((*i)->frame(), bbt); + // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => "; if (first) { @@ -734,6 +752,22 @@ TempoMap::timestamp_metrics_from_audio_time () } +void +TempoMap::require_map_to (framepos_t pos) +{ + if (_map.empty() || _map.back().frame < pos) { + recompute_map (false, pos); + } +} + +void +TempoMap::require_map_to (const BBT_Time& bbt) +{ + if (_map.empty() || _map.back().bbt() < bbt) { + recompute_map (false, 99); + } +} + void TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end) { @@ -925,6 +959,7 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end) TempoMetric TempoMap::metric_at (framepos_t frame) const { + Glib::RWLock::ReaderLock lm (lock); TempoMetric m (first_meter(), first_tempo()); const Meter* meter; const Tempo* tempo; @@ -961,6 +996,7 @@ TempoMap::metric_at (framepos_t frame) const TempoMetric TempoMap::metric_at (BBT_Time bbt) const { + Glib::RWLock::ReaderLock lm (lock); TempoMetric m (first_meter(), first_tempo()); const Meter* meter; const Tempo* tempo; @@ -994,7 +1030,7 @@ TempoMap::metric_at (BBT_Time bbt) const } void -TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt) const +TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt) { { Glib::RWLock::ReaderLock lm (lock); @@ -1003,261 +1039,95 @@ TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt) const } void -TempoMap::bbt_time_unlocked (framepos_t frame, BBT_Time& bbt) const +TempoMap::bbt_time_unlocked (framepos_t frame, BBT_Time& bbt) { - bbt_time_with_metric (frame, bbt, metric_at (frame)); -} - -void -TempoMap::bbt_time_with_metric (framepos_t frame, BBT_Time& bbt, const TempoMetric& metric) const -{ - const double divisions_per_bar = metric.meter().divisions_per_bar(); - const double frames_per_tick = metric.meter().frames_per_division (metric.tempo(),_frame_rate) / BBT_Time::ticks_per_bar_division; - - /* now compute how far beyond the metric we actually are, and add the - * relevant number of ticks to the metric's BBT time - */ - - framecnt_t frame_diff = frame - metric.frame(); - uint32_t tick_diff = (uint32_t) lrint ((double) frame_diff / frames_per_tick); - - bbt.ticks = metric.start().ticks + tick_diff; - uint32_t beat_overflow = bbt.ticks / (uint32_t) BBT_Time::ticks_per_bar_division; - bbt.ticks = bbt.ticks % (uint32_t) BBT_Time::ticks_per_bar_division; - bbt.beats = metric.start().beats + beat_overflow; - /* bbt.beats uses 1-based counting, so adjust to get the right answer */ - uint32_t bar_overflow = (bbt.beats - 1) / (uint32_t) divisions_per_bar; - bbt.bars = metric.start().bars + bar_overflow; - - /* fmod will map bbt.beats as follows: - - Beats divisions per bar Normalized beat - 0 N => 0 - 1 N => 1 - 2 N => 2 - 3 N => 3 - . - . - . - N-1 N => N-1 - N N => 0 - N+1 N => 1 - . - . - . - 2N-1 N => N-1 - 2N N => 0 - - so, the only special cases are 0, N, 2N etc. however bbt.beats is - never zero, so the only actual special cases are N, 2N and so on, - allowing us to use a special case check for fmod () == 0 and - changing the value to divisions per bar - */ - - bbt.beats = (uint32_t) fmod (bbt.beats, divisions_per_bar); + BBTPointList::const_iterator i = bbt_before_or_at (frame); + + bbt.bars = (*i).bar; + bbt.beats = (*i).beat; - if (bbt.beats == 0) { - bbt.beats = divisions_per_bar; + if ((*i).frame == frame) { + bbt.ticks = 0; + } else { + bbt.ticks = llrint (((frame - (*i).frame) / (*i).meter->frames_per_division(*((*i).tempo), _frame_rate)) / + BBT_Time::ticks_per_bar_division); } } -framecnt_t -TempoMap::count_frames_between (const BBT_Time& start, const BBT_Time& end) const -{ - TempoMetric bm = metric_at (start); - TempoMetric em = metric_at (end); - - return count_frames_with_metrics (bm, em, start, end); -} - -framecnt_t -TempoMap::count_frames_with_metrics (const TempoMetric& bm, const TempoMetric& em, const BBT_Time& start, const BBT_Time& end) const -{ - framecnt_t frames = 0; - framepos_t start_frame = 0; - framepos_t end_frame = 0; - - uint32_t bar_offset = start.bars - bm.start().bars; - - double beat_offset = bar_offset*bm.meter().divisions_per_bar() - (bm.start().beats-1) + (start.beats -1) - + start.ticks/BBT_Time::ticks_per_bar_division; - - start_frame = bm.frame() + (framepos_t) rint(beat_offset * bm.meter().frames_per_division(bm.tempo(),_frame_rate)); - -#if 0 - cerr << "from start " << start << " compute frame = " << start_frame - << " from metric at " << bm.frame() << " tempo = " << bm.tempo().beats_per_minute () << " meter " - << bm.meter().divisions_per_bar() << '/' << bm.meter().note_divisor() - << endl; -#endif - - bar_offset = end.bars - em.start().bars; - - beat_offset = bar_offset * em.meter().divisions_per_bar() - (em.start().beats -1) + (end.beats - 1) - + end.ticks/BBT_Time::ticks_per_bar_division; - - end_frame = em.frame() + (framepos_t) rint(beat_offset * em.meter().frames_per_division(em.tempo(),_frame_rate)); - -#if 0 - cerr << "from end " << end << " compute frame = " << end_frame - << " from metric at " << em.frame() << " tempo = " << em.tempo().beats_per_minute () << " meter " - << em.meter().divisions_per_bar() << '/' << em.meter().note_divisor() - << endl; -#endif - - frames = end_frame - start_frame; - - return frames; -} - framepos_t -TempoMap::frame_time (const BBT_Time& bbt) const +TempoMap::frame_time (const BBT_Time& bbt) { - BBT_Time start ; /* 1|1|0 */ + Glib::RWLock::ReaderLock lm (lock); - return count_frames_between (start, bbt); + BBTPointList::const_iterator s = bbt_point_for (BBT_Time (1, 1, 0)); + BBTPointList::const_iterator e = bbt_point_for (BBT_Time (bbt.bars, bbt.beats, 0)); + + if (bbt.ticks != 0) { + return ((*e).frame - (*s).frame) + + llrint ((*e).meter->frames_per_division (*(*e).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division)); + } else { + return ((*e).frame - (*s).frame); + } } framecnt_t -TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir) const +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); - { - Glib::RWLock::ReaderLock lm (lock); - frames = bbt_duration_at_unlocked (when, bbt,dir); - } + bbt_time (pos, when); + frames = bbt_duration_at_unlocked (when, bbt,dir); return frames; } framecnt_t -TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const +TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) { - framecnt_t frames = 0; - - double divisions_per_bar; - BBT_Time result; - - result.bars = max(1U, when.bars + dir * bbt.bars) ; - result.beats = 1; - result.ticks = 0; - - TempoMetric metric = metric_at(result); - divisions_per_bar = metric.meter().divisions_per_bar(); - - /* Reduce things to legal bbt values we have to handle possible - fractional=shorter beats at the end of measures and things like 0|11|9000 - as a duration in a 4.5/4 measure the musical decision is that the - fractional beat is also a beat , although a shorter one - */ - - if (dir >= 0) { - result.beats = when.beats + bbt.beats; - result.ticks = when.ticks + bbt.ticks; - - while (result.beats >= (divisions_per_bar + 1)) { - result.bars++; - result.beats -= (uint32_t) ceil(divisions_per_bar); - metric = metric_at(result); // maybe there is a meter change - divisions_per_bar = metric.meter().divisions_per_bar(); - - } - - /* We now counted the beats and landed in the target measure, now deal - with ticks this seems complicated, but we want to deal with the - corner case of a sequence of time signatures like 0.2/4-0.7/4 and - with request like bbt = 3|2|9000 ,so we repeat the same loop but add - ticks - */ - - /* of course gtk_ardour only allows bar with at least 1.0 beats ..... - */ - - uint32_t ticks_at_beat = (uint32_t) (result.beats == ceil(divisions_per_bar) ? - (1 - (ceil(divisions_per_bar) - divisions_per_bar))* BBT_Time::ticks_per_bar_division - : BBT_Time::ticks_per_bar_division ); - - while (result.ticks >= ticks_at_beat) { - result.beats++; - result.ticks -= ticks_at_beat; - if (result.beats >= (divisions_per_bar + 1)) { - result.bars++; - result.beats = 1; - metric = metric_at(result); // maybe there is a meter change - divisions_per_bar = metric.meter().divisions_per_bar(); - } - ticks_at_beat= (uint32_t) (result.beats == ceil(divisions_per_bar) ? - (1 - (ceil(divisions_per_bar) - divisions_per_bar) ) * BBT_Time::ticks_per_bar_division - : BBT_Time::ticks_per_bar_division); - } - - - } else { - uint32_t b = bbt.beats; - - /* count beats */ - while (b > when.beats) { - --result.bars; - result.bars = max(1U, result.bars); - metric = metric_at(result); // maybe there is a meter change - divisions_per_bar = metric.meter().divisions_per_bar(); - if (b >= ceil(divisions_per_bar)) { - b -= (uint32_t) ceil(divisions_per_bar); - } else { - b = (uint32_t) ceil(divisions_per_bar) - b + when.beats ; - } - } - result.beats = when.beats - b; + if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) { + return 0; + } - /* count ticks */ + /* round back to the previous precise beat */ + BBTPointList::const_iterator wi = bbt_point_for (BBT_Time (when.bars, when.beats, 0)); + BBTPointList::const_iterator start (wi); + double tick_frames = 0; - if (bbt.ticks <= when.ticks) { - result.ticks = when.ticks - bbt.ticks; - } else { + assert (wi != _map.end()); - uint32_t ticks_at_beat= (uint32_t) BBT_Time::ticks_per_bar_division; - uint32_t t = bbt.ticks - when.ticks; - - do { - - if (result.beats == 1) { - --result.bars; - result.bars = max(1U, result.bars) ; - metric = metric_at(result); // maybe there is a meter change - divisions_per_bar = metric.meter().divisions_per_bar(); - result.beats = (uint32_t) ceil(divisions_per_bar); - ticks_at_beat = (uint32_t) ((1 - (ceil(divisions_per_bar) - divisions_per_bar)) * BBT_Time::ticks_per_bar_division) ; - } else { - --result.beats; - ticks_at_beat = (uint32_t) BBT_Time::ticks_per_bar_division; - } + /* compute how much rounding we did because of non-zero ticks */ - if (t <= ticks_at_beat) { - result.ticks = ticks_at_beat - t; - } else { - t-= ticks_at_beat; - } - } while (t > ticks_at_beat); + if (when.ticks != 0) { + tick_frames = (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (when.ticks/BBT_Time::ticks_per_bar_division); + } + + uint32_t bars = 0; + uint32_t beats = 0; + while (wi != _map.end() && bars < bbt.bars) { + ++wi; + if ((*wi).type == Bar) { + ++bars; } - - } + assert (wi != _map.end()); - if (dir < 0) { - frames = count_frames_between(result, when); - } else { - frames = count_frames_between(when,result); + while (wi != _map.end() && beats < bbt.beats) { + ++wi; + ++beats; } + assert (wi != _map.end()); - return frames; -} + /* add any additional frames related to ticks in the added value */ + if (bbt.ticks != 0) { + tick_frames += (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division); + } + return ((*wi).frame - (*start).frame) + llrint (tick_frames); +} framepos_t TempoMap::round_to_bar (framepos_t fr, int dir) @@ -1268,7 +1138,6 @@ TempoMap::round_to_bar (framepos_t fr, int dir) } } - framepos_t TempoMap::round_to_beat (framepos_t fr, int dir) { @@ -1351,121 +1220,125 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir) framepos_t TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) { - TempoMetric metric = metric_at (frame); - BBT_Time bbt; - BBT_Time start; - BBT_Time one_bar (1,0,0); - BBT_Time one_beat (0,1,0); + BBTPointList::const_iterator fi; + + if (dir > 0) { + fi = bbt_after_or_at (frame); + } else { + fi = bbt_before_or_at (frame); + } - bbt_time_with_metric (frame, bbt, metric); + 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)); + switch (type) { case Bar: - DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to bars in direction %2\n", frame, dir, bbt)); - if (dir < 0) { + /* find bar previous to 'frame' */ - /* find bar position preceding frame */ - - try { - bbt = bbt_subtract (bbt, one_bar); + if ((*fi).beat == 1 && (*fi).frame == frame) { + --fi; } - catch (...) { - return frame; + while ((*fi).beat > 1) { + if (fi == _map.begin()) { + break; + } + fi--; } - + DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n", + (*fi).bar, (*fi).beat, (*fi).frame)); + return (*fi).frame; } else if (dir > 0) { - /* find bar position following frame */ + /* find bar following 'frame' */ - try { - bbt = bbt_add (bbt, one_bar, metric); + if ((*fi).beat == 1 && (*fi).frame == frame) { + ++fi; } - catch (...) { - return frame; + + while ((*fi).beat != 1) { + fi++; + if (fi == _map.end()) { + --fi; + break; + } } - } else { + DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n", + (*fi).bar, (*fi).beat, (*fi).frame)); + return (*fi).frame; - /* "true" rounding */ + } else { + + /* true rounding: find nearest bar */ - float midbar_beats; - float midbar_ticks; + BBTPointList::const_iterator prev = fi; + BBTPointList::const_iterator next = fi; - midbar_beats = metric.meter().divisions_per_bar() / 2 + 1; - midbar_ticks = BBT_Time::ticks_per_bar_division * fmod (midbar_beats, 1.0f); - midbar_beats = floor (midbar_beats); + while ((*prev).beat != 1) { + if (prev == _map.begin()) { + break; + } + prev--; + } - BBT_Time midbar (bbt.bars, lrintf (midbar_beats), lrintf (midbar_ticks)); + while ((*next).beat != 1) { + next++; + if (next == _map.end()) { + --next; + break; + } + } - if (bbt < midbar) { - /* round down */ - bbt.beats = 1; - bbt.ticks = 0; + if ((frame - (*prev).frame) < ((*next).frame - frame)) { + return (*prev).frame; } else { - /* round up */ - bbt.bars++; - bbt.beats = 1; - bbt.ticks = 0; + return (*next).frame; } + } - /* force beats & ticks to their values at the start of a bar */ - bbt.beats = 1; - bbt.ticks = 0; + break; case Beat: - DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to beat in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt)); - if (dir < 0) { - - /* find beat position preceding frame */ - - try { - bbt = bbt_subtract (bbt, one_beat); - } - - catch (...) { - return frame; + if ((*fi).frame >= frame) { + DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n"); + --fi; } - - + DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n", + (*fi).bar, (*fi).beat, (*fi).frame)); + return (*fi).frame; } else if (dir > 0) { - - /* find beat position following frame */ - - try { - bbt = bbt_add (bbt, one_beat, metric); + if ((*fi).frame <= frame) { + DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n"); + ++fi; } - catch (...) { + DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n", + (*fi).bar, (*fi).beat, (*fi).frame)); + return (*fi).frame; + } else { + /* find beat nearest to frame */ + if ((*fi).frame == frame) { return frame; } - } else { - - /* "true" rounding */ - - /* round to nearest beat */ - if (bbt.ticks >= (BBT_Time::ticks_per_bar_division/2)) { - - try { - bbt = bbt_add (bbt, one_beat, metric); - } - catch (...) { - return frame; - } + BBTPointList::const_iterator prev = fi; + BBTPointList::const_iterator next = fi; + --prev; + ++next; + + if ((frame - (*prev).frame) < ((*next).frame - frame)) { + return (*prev).frame; + } else { + return (*next).frame; } } - /* force ticks to the value at the start of a beat */ - bbt.ticks = 0; break; - } - - DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("\tat %1 count frames from %2 to %3 = %4\n", metric.frame(), metric.start(), bbt, count_frames_between (metric.start(), bbt))); - return metric.frame() + count_frames_between (metric.start(), bbt); } void @@ -1797,6 +1670,7 @@ TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const result.ticks -= op.ticks; } + /* now comes the complicated part. we have to subtract one beat a time, checking for a new metric on every beat. */ @@ -1895,7 +1769,7 @@ TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const * pos can be -ve, if required. */ framepos_t -TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const +TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) { Metrics::const_iterator i; const TempoSection* tempo; @@ -1962,7 +1836,7 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const /** Subtract some (fractional) beats to a frame position, and return the result in frames */ framepos_t -TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const +TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) { Metrics::const_iterator i; const TempoSection* tempo = 0; @@ -2045,7 +1919,7 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const /** Add the BBT interval op to pos and return the result */ framepos_t -TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const +TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) { Metrics::const_iterator i; const MeterSection* meter; @@ -2181,67 +2055,81 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const starting at pos. */ Evoral::MusicalTime -TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const +TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) { - Metrics::const_iterator i; - const TempoSection* tempo = 0; - - /* Find the starting tempo */ - - for (i = metrics->begin(); i != metrics->end(); ++i) { + BBTPointList::const_iterator i = bbt_after_or_at (pos); + Evoral::MusicalTime beats = 0; + framepos_t end = pos + distance; - if ((*i)->frame() > pos) { - break; - } + require_map_to (end); - const TempoSection* t; + /* if our starting BBTPoint is after pos, add a fractional beat + to represent that distance. + */ - if ((t = dynamic_cast(*i)) != 0) { - tempo = t; - } + if ((*i).frame != pos) { + beats += ((*i).frame - pos) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate); } - assert (tempo); - - /* We now have: - - tempo -> the Tempo for "pos" - i -> the first metric after "pos", possibly metrics->end() + while (i != _map.end() && (*i).frame < end) { + ++i; + beats++; + } + assert (i != _map.end()); + + /* if our ending BBTPoint is after the end, subtract a fractional beat + to represent that distance. */ - Evoral::MusicalTime beats = 0; - - while (distance) { + if ((*i).frame > end) { + beats -= ((*i).frame - end) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate); + } - /* End of this section */ - framepos_t const end = i == metrics->end() ? max_framepos : (*i)->frame (); + return beats; +} - /* Distance to the end in frames */ - framecnt_t const distance_to_end = end - pos; +TempoMap::BBTPointList::const_iterator +TempoMap::bbt_before_or_at (framepos_t pos) +{ + require_map_to (pos); + BBTPointList::const_iterator i = lower_bound (_map.begin(), _map.end(), pos); + assert (i != _map.end()); + return i; +} - /* Amount to subtract this time */ - double const sub = min (distance, distance_to_end); +TempoMap::BBTPointList::const_iterator +TempoMap::bbt_after_or_at (framepos_t pos) +{ + require_map_to (pos); + BBTPointList::const_iterator i = upper_bound (_map.begin(), _map.end(), pos); + assert (i != _map.end()); + return i; +} - /* Update */ - pos += sub; - distance -= sub; - beats += sub / tempo->frames_per_beat (_frame_rate); +struct bbtcmp { + bool operator() (const BBT_Time& a, const BBT_Time& b) { + return a < b; + } +}; - /* Move on if there's anything to move to */ - if (i != metrics->end ()) { - const TempoSection* t; - - if ((t = dynamic_cast(*i)) != 0) { - tempo = t; - } +TempoMap::BBTPointList::const_iterator +TempoMap::bbt_point_for (const BBT_Time& bbt) +{ + bbtcmp cmp; + int additional_minutes = 1; - ++i; - } + while (_map.empty() || _map.back().bar < (bbt.bars + 1)) { + /* add some more distance, using bigger steps each time */ + require_map_to (_map.back().frame + (_frame_rate * 60 * additional_minutes)); + additional_minutes *= 2; } - return beats; + BBTPointList::const_iterator i = lower_bound (_map.begin(), _map.end(), bbt, cmp); + assert (i != _map.end()); + 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. diff --git a/libs/timecode/timecode/bbt_time.h b/libs/timecode/timecode/bbt_time.h index 46307dbc04..030ac18afc 100644 --- a/libs/timecode/timecode/bbt_time.h +++ b/libs/timecode/timecode/bbt_time.h @@ -46,6 +46,24 @@ struct BBT_Time { (bars == other.bars && beats < other.beats) || (bars == other.bars && beats == other.beats && ticks < other.ticks); } + + bool operator<= (const BBT_Time& other) const { + return bars < other.bars || + (bars <= other.bars && beats <= other.beats) || + (bars <= other.bars && beats <= other.beats && ticks <= other.ticks); + } + + bool operator> (const BBT_Time& other) const { + return bars > other.bars || + (bars == other.bars && beats > other.beats) || + (bars == other.bars && beats == other.beats && ticks > other.ticks); + } + + bool operator>= (const BBT_Time& other) const { + return bars > other.bars || + (bars >= other.bars && beats >= other.beats) || + (bars >= other.bars && beats >= other.beats && ticks >= other.ticks); + } bool operator== (const BBT_Time& other) const { return bars == other.bars && beats == other.beats && ticks == other.ticks; -- cgit v1.2.3