X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Ftempo.cc;h=82520c19ea864ac6b4b4199dc3f398d077027261;hb=6349570fb24b03694e814dfff344faea92659054;hp=f96bb5a91aaf33bb632760e55f2c0faf8c44a6d0;hpb=b18fbc8c68f545c1fa8982ebc639fedef1566340;p=ardour.git diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index f96bb5a91a..82520c19ea 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -23,10 +23,11 @@ #include -#include +#include #include "pbd/xml++.h" #include "evoral/types.hpp" #include "ardour/debug.h" +#include "ardour/lmath.h" #include "ardour/tempo.h" #include "i18n.h" @@ -43,12 +44,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 @@ -79,7 +74,7 @@ TempoSection::TempoSection (const XMLNode& node) { const XMLProperty *prop; BBT_Time start; - LocaleGuard lg (X_("POSIX")); + LocaleGuard lg (X_("C")); if ((prop = node.property ("start")) == 0) { error << _("TempoSection XML node has no \"start\" property") << endmsg; @@ -138,7 +133,7 @@ TempoSection::get_state() const { XMLNode *root = new XMLNode (xml_state_node_name); char buf[256]; - LocaleGuard lg (X_("POSIX")); + LocaleGuard lg (X_("C")); snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, start().bars, @@ -201,7 +196,7 @@ MeterSection::MeterSection (const XMLNode& node) { const XMLProperty *prop; BBT_Time start; - LocaleGuard lg (X_("POSIX")); + LocaleGuard lg (X_("C")); if ((prop = node.property ("start")) == 0) { error << _("MeterSection XML node has no \"start\" property") << endmsg; @@ -255,7 +250,7 @@ MeterSection::get_state() const { XMLNode *root = new XMLNode (xml_state_node_name); char buf[256]; - LocaleGuard lg (X_("POSIX")); + LocaleGuard lg (X_("C")); snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, start().bars, @@ -311,24 +306,12 @@ TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation) bool removed = false; { - Glib::RWLock::WriterLock lm (lock); - Metrics::iterator i; - - for (i = metrics.begin(); i != metrics.end(); ++i) { - if (dynamic_cast (*i) != 0) { - if (tempo.frame() == (*i)->frame()) { - if ((*i)->movable()) { - metrics.erase (i); - removed = true; - break; - } - } + Glib::Threads::RWLock::WriterLock lm (lock); + if ((removed = remove_tempo_locked (tempo))) { + if (complete_operation) { + recompute_map (true); } } - - if (removed && complete_operation) { - recompute_map (false); - } } if (removed && complete_operation) { @@ -336,30 +319,37 @@ TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation) } } +bool +TempoMap::remove_tempo_locked (const TempoSection& tempo) +{ + Metrics::iterator i; + + for (i = metrics.begin(); i != metrics.end(); ++i) { + if (dynamic_cast (*i) != 0) { + if (tempo.frame() == (*i)->frame()) { + if ((*i)->movable()) { + metrics.erase (i); + return true; + } + } + } + } + + return false; +} + void TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation) { bool removed = false; { - Glib::RWLock::WriterLock lm (lock); - Metrics::iterator i; - - for (i = metrics.begin(); i != metrics.end(); ++i) { - if (dynamic_cast (*i) != 0) { - if (tempo.frame() == (*i)->frame()) { - if ((*i)->movable()) { - metrics.erase (i); - removed = true; - break; - } - } + Glib::Threads::RWLock::WriterLock lm (lock); + if ((removed = remove_meter_locked (tempo))) { + if (complete_operation) { + recompute_map (true); } } - - if (removed && complete_operation) { - recompute_map (true); - } } if (removed && complete_operation) { @@ -367,6 +357,25 @@ TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation) } } +bool +TempoMap::remove_meter_locked (const MeterSection& tempo) +{ + Metrics::iterator i; + + for (i = metrics.begin(); i != metrics.end(); ++i) { + if (dynamic_cast (*i) != 0) { + if (tempo.frame() == (*i)->frame()) { + if ((*i)->movable()) { + metrics.erase (i); + return true; + } + } + } + } + + return false; +} + void TempoMap::do_insert (MetricSection* section) { @@ -481,17 +490,19 @@ TempoMap::do_insert (MetricSection* section) void TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where) { - const TempoSection& first (first_tempo()); - - if (ts.start() != first.start()) { - 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); + { + Glib::Threads::RWLock::WriterLock lm (lock); + TempoSection& first (first_tempo()); + + if (ts.start() != first.start()) { + remove_tempo_locked (ts); + add_tempo_locked (tempo, where, true); + } else { + { + /* cannot move the first tempo section */ + *static_cast(&first) = tempo; + recompute_map (false); + } } } @@ -502,65 +513,72 @@ void TempoMap::add_tempo (const Tempo& tempo, BBT_Time where) { { - Glib::RWLock::WriterLock lm (lock); + Glib::Threads::RWLock::WriterLock lm (lock); + add_tempo_locked (tempo, where, true); + } - /* new tempos always start on a beat */ - where.ticks = 0; - TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()); - - /* find the meter to use to set the bar offset of this - * tempo section. - */ + PropertyChanged (PropertyChange ()); +} - const Meter* meter = &first_meter(); +void +TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute) +{ + /* new tempos always start on a beat */ + where.ticks = 0; + + TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()); + + /* find the meter to use to set the bar offset of this + * tempo section. + */ + + const Meter* meter = &first_meter(); + + /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing + at something, because we insert the default tempo and meter during + TempoMap construction. + + now see if we can find better candidates. + */ + + for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { - /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing - at something, because we insert the default tempo and meter during - TempoMap construction. - - now see if we can find better candidates. - */ + const MeterSection* m; - for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { - - const MeterSection* m; - - if (where < (*i)->start()) { - break; - } - - if ((m = dynamic_cast(*i)) != 0) { - meter = m; - } + if (where < (*i)->start()) { + break; } - - ts->update_bar_offset_from_bbt (*meter); - - /* and insert it */ - do_insert (ts); + if ((m = dynamic_cast(*i)) != 0) { + meter = m; + } + } + + ts->update_bar_offset_from_bbt (*meter); + + /* and insert it */ + + do_insert (ts); + if (recompute) { recompute_map (false); } - - - PropertyChanged (PropertyChange ()); -} +} void TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where) { - const MeterSection& first (first_meter()); - - 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); + MeterSection& first (first_meter()); + + if (ms.start() != first.start()) { + remove_meter_locked (ms); + add_meter_locked (meter, where, true); + } else { /* cannot move the first meter section */ - *((Meter*)&first) = meter; + *static_cast(&first) = meter; recompute_map (true); } } @@ -572,25 +590,8 @@ void TempoMap::add_meter (const Meter& meter, BBT_Time where) { { - Glib::RWLock::WriterLock lm (lock); - - /* a new meter always starts a new bar on the first beat. so - round the start time appropriately. remember that - `where' is based on the existing tempo map, not - the result after we insert the new meter. - - */ - - if (where.beats != 1) { - where.beats = 1; - where.bars++; - } - - /* new meters *always* start on a beat. */ - where.ticks = 0; - - do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor())); - recompute_map (true); + Glib::Threads::RWLock::WriterLock lm (lock); + add_meter_locked (meter, where, true); } @@ -603,6 +604,32 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where) PropertyChanged (PropertyChange ()); } +void +TempoMap::add_meter_locked (const Meter& meter, BBT_Time where, bool recompute) +{ + /* a new meter always starts a new bar on the first beat. so + round the start time appropriately. remember that + `where' is based on the existing tempo map, not + the result after we insert the new meter. + + */ + + if (where.beats != 1) { + where.beats = 1; + where.bars++; + } + + /* new meters *always* start on a beat. */ + where.ticks = 0; + + do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor())); + + if (recompute) { + recompute_map (true); + } + +} + void TempoMap::change_initial_tempo (double beats_per_minute, double note_type) { @@ -612,7 +639,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); } @@ -662,7 +689,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); @@ -683,7 +710,25 @@ TempoMap::first_meter () const } fatal << _("programming error: no tempo section in tempo map!") << endmsg; - /*NOTREACHED*/ + abort(); /*NOTREACHED*/ + return *m; +} + +MeterSection& +TempoMap::first_meter () +{ + MeterSection *m = 0; + + /* CALLER MUST HOLD LOCK */ + + for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) { + if ((m = dynamic_cast (*i)) != 0) { + return *m; + } + } + + fatal << _("programming error: no tempo section in tempo map!") << endmsg; + abort(); /*NOTREACHED*/ return *m; } @@ -692,6 +737,8 @@ TempoMap::first_tempo () const { const TempoSection *t = 0; + /* CALLER MUST HOLD LOCK */ + for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { if ((t = dynamic_cast (*i)) != 0) { return *t; @@ -699,14 +746,30 @@ TempoMap::first_tempo () const } fatal << _("programming error: no tempo section in tempo map!") << endmsg; - /*NOTREACHED*/ + abort(); /*NOTREACHED*/ + return *t; +} + +TempoSection& +TempoMap::first_tempo () +{ + TempoSection *t = 0; + + for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { + if ((t = dynamic_cast (*i)) != 0) { + return *t; + } + } + + fatal << _("programming error: no tempo section in tempo map!") << endmsg; + abort(); /*NOTREACHED*/ return *t; } 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); @@ -716,7 +779,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 @@ -771,6 +834,8 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end) } } + assert(meter); + for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) { TempoSection* ts; @@ -780,6 +845,8 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end) } } + assert(tempo); + /* assumes that the first meter & tempo are at frame zero */ current_frame = 0; meter->set_frame (0); @@ -813,7 +880,7 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end) rmeter = ms; } else { fatal << _("programming error: unhandled MetricSection type") << endmsg; - /*NOTREACHED*/ + abort(); /*NOTREACHED*/ } } } @@ -890,6 +957,7 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, TempoSection* ts; MeterSection* ms; double beat_frames; + double current_frame_exact; framepos_t bar_start_frame; DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame)); @@ -901,11 +969,13 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, } beat_frames = meter->frames_per_grid (*tempo,_frame_rate); + current_frame_exact = current_frame; while (current_frame < end) { current.beats++; - current_frame += beat_frames; + current_frame_exact += beat_frames; + current_frame = llrint(current_frame_exact); if (current.beats > meter->divisions_per_bar()) { current.bars++; @@ -920,7 +990,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; @@ -945,17 +1015,18 @@ 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; + current_frame_exact -= beat_frames; + current_frame = llrint(current_frame_exact); /* 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))); + llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames))); /* advance to the location of * the new (adjusted) beat. do @@ -969,7 +1040,8 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, 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); + current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames); + current_frame = llrint(current_frame_exact); /* next metric doesn't have to * match this precisely to @@ -980,7 +1052,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); } @@ -993,7 +1065,7 @@ 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); @@ -1003,13 +1075,13 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, 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, meter->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; } @@ -1018,18 +1090,18 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, 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)); + _map.push_back (BBTPoint (*meter, *tempo, 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)); + _map.push_back (BBTPoint (*meter, *tempo, 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)); + current.bars, current.beats, current_frame)); break; } } @@ -1037,12 +1109,10 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, } 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 @@ -1057,14 +1127,11 @@ TempoMap::metric_at (framepos_t frame) 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_metric(*i); - m.set_frame ((*i)->frame ()); - m.set_start ((*i)->start ()); + if (last) { + *last = i; + } } return m; @@ -1073,10 +1140,8 @@ TempoMap::metric_at (framepos_t frame) const 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 @@ -1093,14 +1158,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; @@ -1111,7 +1169,7 @@ 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; @@ -1127,7 +1185,7 @@ TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt) 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"); @@ -1152,7 +1210,7 @@ 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); } } @@ -1170,7 +1228,7 @@ TempoMap::frame_time (const BBT_Time& bbt) 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)); @@ -1189,7 +1247,7 @@ TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir) BBT_Time when; bbt_time (pos, when); - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); return bbt_duration_at_unlocked (when, bbt, dir); } @@ -1203,16 +1261,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; @@ -1233,34 +1284,34 @@ 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 -TempoMap::round_to_bar (framepos_t fr, int dir) +TempoMap::round_to_bar (framepos_t fr, RoundMode dir) { return round_to_type (fr, dir, Bar); } framepos_t -TempoMap::round_to_beat (framepos_t fr, int dir) +TempoMap::round_to_beat (framepos_t fr, RoundMode dir) { return round_to_type (fr, dir, Beat); } framepos_t -TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir) +TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode 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; - uint32_t difference; bbt_time (fr, the_beat, i); @@ -1271,11 +1322,14 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir) if (dir > 0) { - /* round to next (even if we're on a subdivision */ + /* round to next (or same iff dir == RoundUpMaybe) */ uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth; - if (mod == 0) { + if (mod == 0 && dir == RoundUpMaybe) { + /* right on the subdivision, which is fine, so do nothing */ + + } else if (mod == 0) { /* right on the subdivision, so the difference is just the subdivision ticks */ the_beat.ticks += ticks_one_subdivisions_worth; @@ -1295,19 +1349,14 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir) } else if (dir < 0) { - /* round to previous (even if we're on a subdivision) */ + /* round to previous (or same iff dir == RoundDownMaybe) */ - uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth; + uint32_t difference = the_beat.ticks % ticks_one_subdivisions_worth; - if (mod == 0) { - /* right on the subdivision, so the difference is just the subdivision ticks */ + if (difference == 0 && dir == RoundDownAlways) { + /* right on the subdivision, but force-rounding down, + so the difference is just the subdivision ticks */ difference = ticks_one_subdivisions_worth; - } else { - /* not on subdivision, compute distance to previous subdivision, which - is just the modulus. - */ - - difference = mod; } if (the_beat.ticks < difference) { @@ -1371,11 +1420,11 @@ 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) +TempoMap::round_to_type (framepos_t frame, RoundMode 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) { @@ -1399,6 +1448,9 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) } if ((*fi).is_bar() && (*fi).frame == frame) { + if (dir == RoundDownMaybe) { + return frame; + } --fi; } @@ -1417,6 +1469,9 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) /* find bar following 'frame' */ if ((*fi).is_bar() && (*fi).frame == frame) { + if (dir == RoundUpMaybe) { + return frame; + } ++fi; } @@ -1471,7 +1526,7 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) return 0; } - if ((*fi).frame >= frame) { + if ((*fi).frame > frame || ((*fi).frame == frame && dir == RoundDownAlways)) { DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n"); --fi; } @@ -1479,7 +1534,7 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) (*fi).bar, (*fi).beat, (*fi).frame)); return (*fi).frame; } else if (dir > 0) { - if ((*fi).frame <= frame) { + if ((*fi).frame < frame || ((*fi).frame == frame && dir == RoundUpAlways)) { DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n"); ++fi; } @@ -1510,8 +1565,7 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) break; } - /* NOTREACHED */ - assert (false); + abort(); /* NOTREACHED */ return 0; } @@ -1521,7 +1575,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); } @@ -1534,7 +1588,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; @@ -1553,6 +1607,7 @@ TempoMap::tempo_section_at (framepos_t frame) const if (prev == 0) { fatal << endmsg; + abort(); /*NOTREACHED*/ } return *prev; @@ -1565,6 +1620,33 @@ TempoMap::tempo_at (framepos_t frame) const return m.tempo(); } +const MeterSection& +TempoMap::meter_section_at (framepos_t frame) const +{ + Glib::Threads::RWLock::ReaderLock lm (lock); + Metrics::const_iterator i; + MeterSection* prev = 0; + + for (i = metrics.begin(); i != metrics.end(); ++i) { + MeterSection* t; + + if ((t = dynamic_cast (*i)) != 0) { + + if ((*i)->frame() > frame) { + break; + } + + prev = t; + } + } + + if (prev == 0) { + fatal << endmsg; + abort(); /*NOTREACHED*/ + } + + return *prev; +} const Meter& TempoMap::meter_at (framepos_t frame) const @@ -1580,7 +1662,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()); } @@ -1593,7 +1675,7 @@ int TempoMap::set_state (const XMLNode& node, int /*version*/) { { - Glib::RWLock::WriterLock lm (lock); + Glib::Threads::RWLock::WriterLock lm (lock); XMLNodeList nlist; XMLNodeConstIterator niter; @@ -1655,11 +1737,13 @@ TempoMap::set_state (const XMLNode& node, int /*version*/) if (prev != metrics.end()) { if (dynamic_cast(*prev) && dynamic_cast(*i)) { if ((*prev)->start() == (*i)->start()) { + cerr << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg; 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()) { + cerr << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg; error << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg; return -1; } @@ -1679,7 +1763,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/) void TempoMap::dump (std::ostream& o) const { - Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK); + Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK); const MeterSection* m; const TempoSection* t; @@ -1698,7 +1782,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) { @@ -1713,7 +1797,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) { @@ -1729,7 +1813,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); @@ -1806,7 +1890,7 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount) // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() < metric_kill_list; + + TempoSection* last_tempo = NULL; + MeterSection* last_meter = NULL; + bool tempo_after = false; // is there a tempo marker at the first sample after the removed range? + bool meter_after = false; // is there a meter marker likewise? + { + Glib::Threads::RWLock::WriterLock lm (lock); + for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) { + if ((*i)->frame() >= where && (*i)->frame() < where+amount) { + metric_kill_list.push_back(*i); + TempoSection *lt = dynamic_cast (*i); + if (lt) + last_tempo = lt; + MeterSection *lm = dynamic_cast (*i); + if (lm) + last_meter = lm; + } + else if ((*i)->frame() >= where) { + // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries + (*i)->set_frame ((*i)->frame() - amount); + if ((*i)->frame() == where) { + // marker was immediately after end of range + tempo_after = dynamic_cast (*i); + meter_after = dynamic_cast (*i); + } + moved = true; + } + } + + //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct + if (last_tempo && !tempo_after) { + metric_kill_list.remove(last_tempo); + last_tempo->set_frame(where); + moved = true; + } + if (last_meter && !meter_after) { + metric_kill_list.remove(last_meter); + last_meter->set_frame(where); + moved = true; + } + + //remove all the remaining metrics + for (std::list::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) { + metrics.remove(*i); + moved = true; + } + + if (moved) { + recompute_map (true); + } + } + PropertyChanged (PropertyChange ()); + return moved; +} /** Add some (fractional) beats to a session frame position, and return the result in frames. * 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::Beats beats) const { - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); Metrics::const_iterator next_tempo; const TempoSection* tempo = 0; @@ -1861,20 +2005,23 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const tempo -> the Tempo for "pos" next_tempo -> first tempo after "pos", possibly metrics.end() */ + assert(tempo); - 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) { + while (!!beats) { /* Distance to the end of this section in frames */ framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos)); /* Distance to the end in beats */ - Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate); + Evoral::Beats distance_beats = Evoral::Beats::ticks_at_rate( + distance_frames, tempo->frames_per_beat (_frame_rate)); /* Amount to subtract this time */ - double const delta = min (distance_beats, beats); + Evoral::Beats const delta = min (distance_beats, beats); DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n", (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()), @@ -1882,7 +2029,7 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const /* Update */ beats -= delta; - pos += delta * tempo->frames_per_beat (_frame_rate); + pos += delta.to_ticks(tempo->frames_per_beat (_frame_rate)); DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats)); @@ -1893,7 +2040,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 ()) { @@ -1912,9 +2059,9 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const /** 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 +TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const { - Glib::RWLock::ReaderLock lm (lock); + Glib::Threads::RWLock::ReaderLock lm (lock); Metrics::const_reverse_iterator prev_tempo; const TempoSection* tempo = 0; @@ -1961,9 +2108,11 @@ 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())); + assert(tempo); + 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: @@ -1971,23 +2120,24 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const prev_tempo -> the first metric before "pos", possibly metrics.rend() */ - while (beats) { + while (!!beats) { /* Distance to the start of this section in frames */ framecnt_t distance_frames = (pos - tempo->frame()); /* Distance to the start in beats */ - Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate); + Evoral::Beats distance_beats = Evoral::Beats::ticks_at_rate( + distance_frames, tempo->frames_per_beat (_frame_rate)); /* Amount to subtract this time */ - double const sub = min (distance_beats, beats); + Evoral::Beats const sub = min (distance_beats, beats); DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n", tempo->frame(), distance_frames, distance_beats)); /* Update */ beats -= sub; - pos -= sub * tempo->frames_per_beat (_frame_rate); + pos -= sub.to_double() * tempo->frames_per_beat (_frame_rate); DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats, (prev_tempo == metrics.rend()))); @@ -1998,9 +2148,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 ()) { @@ -2011,8 +2162,8 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const } } } else { - pos -= llrint (beats * tempo->frames_per_beat (_frame_rate)); - beats = 0; + pos -= llrint (beats.to_double() * tempo->frames_per_beat (_frame_rate)); + beats = Evoral::Beats(); } } @@ -2023,7 +2174,7 @@ 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; @@ -2158,10 +2309,10 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const /** Count the number of beats that are equivalent to distance when going forward, starting at pos. */ -Evoral::MusicalTime +Evoral::Beats 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 = 0; framepos_t effective_pos = max (pos, (framepos_t) 0); @@ -2187,13 +2338,13 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const tempo -> the Tempo for "pos" 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, *((Tempo*)tempo), tempo->frame())); + 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; + Evoral::Beats beats = Evoral::Beats(); while (distance) { @@ -2211,8 +2362,8 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const distance_to_end = end - pos; } - /* Amount to subtract this time */ - double const sub = min (distance, distance_to_end); + /* Amount to subtract this time in frames */ + framecnt_t 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)); @@ -2221,7 +2372,7 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const pos += sub; distance -= sub; assert (tempo); - beats += sub / tempo->frames_per_beat (_frame_rate); + beats += Evoral::Beats::ticks_at_rate(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)); @@ -2232,9 +2383,10 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const 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 ()) { @@ -2341,9 +2493,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;