X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Ftempo.cc;h=b3207e15a5e2c741251eb253f5e074fa82827aa6;hb=420cc9b4478059cfeabd8da5b2a25595a1de29fb;hp=a2a400ad32bad2e8f719d0110510e957e3d05eef;hpb=0aa2ed582aebade98b4ea3c276985551dbbfa3dd;p=ardour.git diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index a2a400ad32..b3207e15a5 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -27,6 +27,7 @@ #include "pbd/xml++.h" #include "evoral/types.hpp" #include "ardour/debug.h" +#include "ardour/lmath.h" #include "ardour/tempo.h" #include "i18n.h" @@ -45,11 +46,11 @@ Tempo TempoMap::_default_tempo (120.0); /***********************************************************************/ -double +double Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const { /* This is tempo- and meter-sensitive. The number it returns - is based on the interval between any two lines in the + is based on the interval between any two lines in the grid that is constructed from tempo and meter sections. The return value IS NOT interpretable in terms of "beats". @@ -73,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; @@ -132,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, @@ -155,7 +156,7 @@ void TempoSection::update_bar_offset_from_bbt (const Meter& m) { - _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) / + _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) / (m.divisions_per_bar() * BBT_Time::ticks_per_beat); DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar())); @@ -172,15 +173,15 @@ 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 = 0; /* (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat); */ /* remember the 1-based counting properties of beats */ new_start.beats += 1; - - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n", + + DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n", _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats)); set_start (new_start); @@ -195,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; @@ -218,7 +219,7 @@ MeterSection::MeterSection (const XMLNode& node) if ((prop = node.property ("beats-per-bar")) == 0) { error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg; throw failed_constructor(); - } + } } if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) { @@ -249,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, @@ -306,23 +307,11 @@ TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation) { Glib::Threads::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; - } - } + if ((removed = remove_tempo_locked (tempo))) { + if (complete_operation) { + recompute_map (true); } } - - if (removed && complete_operation) { - recompute_map (false); - } } if (removed && complete_operation) { @@ -330,6 +319,25 @@ 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) { @@ -337,23 +345,11 @@ TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation) { Glib::Threads::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; - } - } + if ((removed = remove_meter_locked (tempo))) { + if (complete_operation) { + recompute_map (true); } } - - if (removed && complete_operation) { - recompute_map (true); - } } if (removed && complete_operation) { @@ -361,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) { @@ -369,7 +384,7 @@ TempoMap::do_insert (MetricSection* section) assert (section->start().ticks == 0); /* we only allow new meters to be inserted on beat 1 of an existing - * measure. + * measure. */ if (dynamic_cast(section)) { @@ -377,21 +392,21 @@ TempoMap::do_insert (MetricSection* section) /* we need to (potentially) update the BBT times of tempo sections based on this new meter. */ - + if ((section->start().beats != 1) || (section->start().ticks != 0)) { - + BBT_Time corrected = section->start(); corrected.beats = 1; corrected.ticks = 0; - + warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"), section->start(), corrected) << endmsg; - + section->set_start (corrected); } } - + /* Look for any existing MetricSection that is of the same type and in the same bar as the new one, and remove it before adding @@ -413,19 +428,19 @@ TempoMap::do_insert (MetricSection* section) (*i)->start().beats == section->start().beats) { 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; - } + } } else if (!iter_is_tempo && !insert_is_tempo) { @@ -434,17 +449,17 @@ TempoMap::do_insert (MetricSection* section) if ((*i)->start().bars == section->start().bars) { 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; @@ -467,7 +482,7 @@ TempoMap::do_insert (MetricSection* section) break; } } - + metrics.insert (i, section); } } @@ -475,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()); + { + Glib::Threads::RWLock::WriterLock lm (lock); + TempoSection& first (first_tempo()); - if (ts.start() != first.start()) { - remove_tempo (ts, false); - add_tempo (tempo, where); - } else { - { - Glib::Threads::RWLock::WriterLock lm (lock); - /* cannot move the first tempo section */ - *((Tempo*)&first) = tempo; - recompute_map (false); + 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); + } } } @@ -497,64 +514,71 @@ TempoMap::add_tempo (const Tempo& tempo, BBT_Time where) { { 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(); - - /* 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) { - - const MeterSection* m; - - if (where < (*i)->start()) { - break; - } - - if ((m = dynamic_cast(*i)) != 0) { - meter = m; - } - } +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()); - ts->update_bar_offset_from_bbt (*meter); + /* find the meter to use to set the bar offset of this + * tempo section. + */ - /* and insert it */ - - do_insert (ts); + const Meter* meter = &first_meter(); - recompute_map (false); + /* 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) { + + const MeterSection* m; + + if (where < (*i)->start()) { + break; + } + + if ((m = dynamic_cast(*i)) != 0) { + meter = m; + } } + ts->update_bar_offset_from_bbt (*meter); - PropertyChanged (PropertyChange ()); + /* and insert it */ + + do_insert (ts); + + if (recompute) { + recompute_map (false); + } } void TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where) { - const MeterSection& first (first_meter()); + { + Glib::Threads::RWLock::WriterLock lm (lock); + MeterSection& first (first_meter()); - if (ms.start() != first.start()) { - remove_meter (ms, false); - add_meter (meter, where); - } else { - { - Glib::Threads::RWLock::WriterLock lm (lock); + 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); } } @@ -567,27 +591,10 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where) { { 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 - `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); + add_meter_locked (meter, where, true); } - + #ifndef NDEBUG if (DEBUG_ENABLED(DEBUG::TempoMap)) { dump (std::cerr); @@ -597,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) { @@ -605,7 +638,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::Threads::RWLock::WriterLock lm (lock); *((Tempo*) t) = newtempo; recompute_map (false); @@ -677,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; } @@ -686,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; @@ -693,7 +746,23 @@ 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; } @@ -718,7 +787,7 @@ TempoMap::require_map_to (const BBT_Time& bbt) */ int additional_minutes = 1; - + while (1) { if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) { break; @@ -765,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; @@ -774,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); @@ -794,7 +867,7 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end) TempoSection* ts; MeterSection* ms; - + if ((ts = dynamic_cast(*i)) != 0) { /* reassign the BBT time of this tempo section @@ -807,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*/ } } } @@ -842,7 +915,7 @@ TempoMap::extend_map (framepos_t end) return; } - BBTPointList::const_iterator i = _map.end(); + BBTPointList::const_iterator i = _map.end(); Metrics::iterator next_metric; --i; @@ -856,7 +929,7 @@ TempoMap::extend_map (framepos_t end) } /* find the metric immediately after the tempo + meter sections for the - * last point in the map + * last point in the map */ for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) { @@ -866,16 +939,16 @@ TempoMap::extend_map (framepos_t end) } /* we cast away const here because this is the one place where we need - * to actually modify the frame time of each metric section. + * to actually modify the frame time of each metric section. */ - _extend_map (const_cast ((*i).tempo), + _extend_map (const_cast ((*i).tempo), const_cast ((*i).meter), next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end); } void -TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, +TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, Metrics::iterator next_metric, BBT_Time current, framepos_t current_frame, framepos_t end) { @@ -933,28 +1006,28 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, * is within, which will be different * from the preceding following ones * since it takes part of its duration - * from the preceding tempo and part + * from the preceding tempo and part * from this new tempo. */ if (tempo->start().ticks != 0) { - - double next_beat_frames = tempo->frames_per_beat (_frame_rate); - + + 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())); - + /* back up to previous beat */ current_frame_exact -= beat_frames; current_frame = llrint(current_frame_exact); /* set tempo section location * based on offset from last - * bar start + * bar start */ - tempo->set_frame (bar_start_frame + + 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 @@ -975,35 +1048,35 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, * merit a reloop ... */ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame)); - + } else { - + DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n", tempo->start(), current_frame)); tempo->set_frame (current_frame); } } else if ((ms = dynamic_cast(*next_metric)) != 0) { - + meter = ms; /* new meter section: always defines the * start of a bar. */ - + DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n", meter->start(), current, current_frame)); - + assert (current.beats == 1); meter->set_frame (current_frame); } - + 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", + + 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))); - + ++next_metric; if (next_metric != metrics.end() && ((*next_metric)->start() == current)) { @@ -1013,15 +1086,15 @@ 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()) { @@ -1060,7 +1133,7 @@ TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const *last = i; } } - + return m; } @@ -1117,7 +1190,7 @@ TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt) if (!lm.locked()) { throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map"); } - + if (_map.empty() || _map.back().frame < frame) { throw std::logic_error (string_compose ("map not long enough to reach %1", frame)); } @@ -1148,7 +1221,7 @@ TempoMap::frame_time (const BBT_Time& bbt) 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"); } @@ -1161,7 +1234,7 @@ TempoMap::frame_time (const BBT_Time& bbt) BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0)); if (bbt.ticks != 0) { - return ((*e).frame - (*s).frame) + + return ((*e).frame - (*s).frame) + llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat)); } else { return ((*e).frame - (*s).frame); @@ -1173,13 +1246,13 @@ TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir) { BBT_Time when; bbt_time (pos, when); - + 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; @@ -1211,7 +1284,7 @@ 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) { - return ((*wi).frame - (*start).frame) + + 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); @@ -1219,19 +1292,19 @@ TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, i } 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); @@ -1239,7 +1312,6 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir) 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); @@ -1250,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; @@ -1269,24 +1344,19 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir) ++i; assert (i != _map.end()); the_beat.ticks -= BBT_Time::ticks_per_beat; - } + } } 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) { @@ -1306,9 +1376,9 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir) double rem; /* compute the distance to the previous and next subdivision */ - + if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) { - + /* closer to the next subdivision, so shift forward */ the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem)); @@ -1321,10 +1391,10 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir) assert (i != _map.end()); the_beat.ticks -= BBT_Time::ticks_per_beat; DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat)); - } + } } else if (rem > 0) { - + /* closer to previous subdivision, so shift backward */ if (rem > the_beat.ticks) { @@ -1345,12 +1415,12 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir) } } - return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) * + return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) * (*i).tempo->frames_per_beat (_frame_rate); } 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); @@ -1367,7 +1437,7 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) 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: if (dir < 0) { @@ -1378,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; } @@ -1387,7 +1460,7 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) } fi--; } - DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n", + 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; @@ -1396,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; } @@ -1407,12 +1483,12 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) } } - DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n", + 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 { - + /* true rounding: find nearest bar */ BBTPointList::const_iterator prev = fi; @@ -1438,7 +1514,7 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) } else { return (*next).frame; } - + } break; @@ -1450,19 +1526,19 @@ 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; } - DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n", + 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) { - 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; } - DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n", + 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 { @@ -1479,7 +1555,7 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) the beat before frame. */ ++next; - + if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) { return (*prev).frame; } else { @@ -1489,17 +1565,16 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) break; } - /* NOTREACHED */ - assert (false); + abort(); /* NOTREACHED */ return 0; } void -TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin, - TempoMap::BBTPointList::const_iterator& end, - framepos_t lower, framepos_t upper) +TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin, + TempoMap::BBTPointList::const_iterator& end, + framepos_t lower, framepos_t upper) { - { + { Glib::Threads::RWLock::WriterLock lm (lock); if (_map.empty() || (_map.back().frame < upper)) { recompute_map (false, upper); @@ -1532,6 +1607,7 @@ TempoMap::tempo_section_at (framepos_t frame) const if (prev == 0) { fatal << endmsg; + abort(); /*NOTREACHED*/ } return *prev; @@ -1544,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 @@ -1581,7 +1684,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/) metrics.clear(); nlist = node.children(); - + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { XMLNode* child = *niter; @@ -1594,7 +1697,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/) if (ts->bar_offset() < 0.0) { if (last_meter) { ts->update_bar_offset_from_bbt (*last_meter); - } + } } } @@ -1634,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; } @@ -1725,23 +1830,23 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount) const TempoSection* tempo; MeterSection *m; TempoSection *t; - + meter = &first_meter (); tempo = &first_tempo (); - + BBT_Time start; BBT_Time end; - + // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl; - + bool first = true; MetricSection* prev = 0; - + for (i = metrics.begin(); i != metrics.end(); ++i) { - + BBT_Time bbt; TempoMetric metric (*meter, *tempo); - + if (prev) { metric.set_start (prev->start()); metric.set_frame (prev->frame()); @@ -1749,34 +1854,34 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount) // metric will be at frames=0 bbt=1|1|0 by default // which is correct for our purpose } - + BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame()); bbt_time ((*i)->frame(), bbt, bi); - + // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => "; - + if (first) { first = false; } else { - + if (bbt.ticks > BBT_Time::ticks_per_beat/2) { /* round up to next beat */ bbt.beats += 1; } - + bbt.ticks = 0; - + if (bbt.beats != 1) { /* round up to next bar */ bbt.bars += 1; bbt.beats = 1; } } - + // cerr << bbt << endl; - + (*i)->set_start (bbt); - + if ((t = dynamic_cast(*i)) != 0) { tempo = t; // cerr << "NEW TEMPO, 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::Threads::RWLock::ReaderLock lm (lock); Metrics::const_iterator next_tempo; @@ -1826,11 +1991,11 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const if (pos < 0 && f == 0) { f = pos; } - + if (f > pos) { break; } - + tempo = t; } } @@ -1840,21 +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, *((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()), @@ -1862,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)); @@ -1879,7 +2046,7 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const while (next_tempo != metrics.end ()) { ++next_tempo; - + if (next_tempo != metrics.end() && dynamic_cast(*next_tempo)) { break; } @@ -1892,7 +2059,7 @@ 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::Threads::RWLock::ReaderLock lm (lock); Metrics::const_reverse_iterator prev_tempo; @@ -1924,7 +2091,7 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const keep going to get the previous tempo (or metrics.rend()) */ - + if (f <= pos) { if (tempo == 0) { /* first tempo with position at or @@ -1941,6 +2108,7 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const } } + 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(), @@ -1952,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()))); @@ -1993,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(); } } @@ -2067,7 +2236,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const * traversed before we change the * frames_per_beat value. */ - + pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar())); bars = 0; @@ -2127,7 +2296,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const if (op.ticks) { if (op.ticks >= BBT_Time::ticks_per_beat) { pos += llrint (frames_per_beat + /* extra beat */ - (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) / + (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) / (double) BBT_Time::ticks_per_beat))); } else { pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat)); @@ -2140,7 +2309,7 @@ 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::Threads::RWLock::ReaderLock lm (lock); @@ -2169,14 +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, *((const Tempo*)tempo), tempo->frame())); - - Evoral::MusicalTime beats = 0; + + Evoral::Beats beats = Evoral::Beats(); while (distance) { @@ -2194,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)); @@ -2204,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)); @@ -2223,7 +2391,7 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const while (next_tempo != metrics.end ()) { ++next_tempo; - + if (next_tempo != metrics.end() && dynamic_cast(*next_tempo)) { break; } @@ -2252,7 +2420,7 @@ TempoMap::bbt_before_or_at (framepos_t pos) if (pos < 0) { /* not really correct, but we should catch pos < 0 at a higher - level + level */ return _map.begin(); } @@ -2288,7 +2456,7 @@ TempoMap::bbt_before_or_at (const BBT_Time& bbt) } TempoMap::BBTPointList::const_iterator -TempoMap::bbt_after_or_at (framepos_t pos) +TempoMap::bbt_after_or_at (framepos_t pos) { /* CALLER MUST HOLD READ LOCK */ @@ -2306,17 +2474,17 @@ TempoMap::bbt_after_or_at (framepos_t pos) return i; } -std::ostream& +std::ostream& operator<< (std::ostream& o, const Meter& m) { return o << m.divisions_per_bar() << '/' << m.note_divisor(); } -std::ostream& +std::ostream& operator<< (std::ostream& o, const Tempo& t) { return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute"; } -std::ostream& +std::ostream& operator<< (std::ostream& o, const MetricSection& section) { o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';