X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Ftempo.cc;h=2a0ac50cc2759d31ce19b1caf5f4f84122b87ec2;hb=d01cb7910faddbf13b7190ceca990a2cafb71f95;hp=521b91147d50974b3ecb2d55e913c2b12ea495cc;hpb=9a4827374ca4e4c310d02adf65c727d92b56724c;p=ardour.git diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index 521b91147d..2a0ac50cc2 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -25,7 +25,7 @@ #include #include "pbd/xml++.h" -#include "evoral/types.hpp" +#include "evoral/Beats.hpp" #include "ardour/debug.h" #include "ardour/lmath.h" #include "ardour/tempo.h" @@ -46,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". @@ -156,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())); @@ -173,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); @@ -219,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) { @@ -284,6 +284,7 @@ TempoMap::TempoMap (framecnt_t fr) start.beats = 1; start.ticks = 0; + // these leak memory, well Metrics does TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type()); MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor()); @@ -336,8 +337,8 @@ TempoMap::remove_tempo_locked (const TempoSection& tempo) } return false; -} - +} + void TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation) { @@ -361,7 +362,7 @@ 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()) { @@ -384,7 +385,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)) { @@ -392,21 +393,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 @@ -428,19 +429,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) { @@ -449,17 +450,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; @@ -482,7 +483,7 @@ TempoMap::do_insert (MetricSection* section) break; } } - + metrics.insert (i, section); } } @@ -493,7 +494,7 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_T { 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); @@ -526,45 +527,45 @@ 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) { - + const MeterSection* m; - + if (where < (*i)->start()) { break; } - + 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); } -} +} void TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where) @@ -572,7 +573,7 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T { 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); @@ -594,7 +595,7 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where) add_meter_locked (meter, where, true); } - + #ifndef NDEBUG if (DEBUG_ENABLED(DEBUG::TempoMap)) { dump (std::cerr); @@ -611,23 +612,23 @@ TempoMap::add_meter_locked (const Meter& meter, BBT_Time where, bool recompute) 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 @@ -638,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::Threads::RWLock::WriterLock lm (lock); *((Tempo*) t) = newtempo; recompute_map (false); @@ -738,7 +739,7 @@ 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; @@ -787,7 +788,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; @@ -867,7 +868,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 @@ -915,7 +916,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; @@ -929,7 +930,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) { @@ -939,16 +940,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) { @@ -1006,28 +1007,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 @@ -1048,35 +1049,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)) { @@ -1086,7 +1087,7 @@ 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)); @@ -1133,7 +1134,7 @@ TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const *last = i; } } - + return m; } @@ -1190,7 +1191,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)); } @@ -1221,7 +1222,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"); } @@ -1234,7 +1235,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); @@ -1246,13 +1247,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; @@ -1284,7 +1285,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); @@ -1344,7 +1345,7 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir) ++i; assert (i != _map.end()); the_beat.ticks -= BBT_Time::ticks_per_beat; - } + } } else if (dir < 0) { @@ -1376,9 +1377,9 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode 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)); @@ -1391,10 +1392,10 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode 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) { @@ -1415,7 +1416,7 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode 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); } @@ -1437,7 +1438,7 @@ TempoMap::round_to_type (framepos_t frame, RoundMode 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) { @@ -1460,7 +1461,7 @@ TempoMap::round_to_type (framepos_t frame, RoundMode 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; @@ -1483,12 +1484,12 @@ TempoMap::round_to_type (framepos_t frame, RoundMode 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; @@ -1514,7 +1515,7 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type) } else { return (*next).frame; } - + } break; @@ -1530,7 +1531,7 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type) 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) { @@ -1538,7 +1539,7 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type) 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 { @@ -1555,7 +1556,7 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type) the beat before frame. */ ++next; - + if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) { return (*prev).frame; } else { @@ -1570,11 +1571,11 @@ TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type) } 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); @@ -1620,6 +1621,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 @@ -1657,7 +1685,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; @@ -1670,7 +1698,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); - } + } } } @@ -1710,11 +1738,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; } @@ -1801,23 +1831,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()); @@ -1825,34 +1855,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. @@ -1902,11 +1992,11 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const if (pos < 0 && f == 0) { f = pos; } - + if (f > pos) { break; } - + tempo = t; } } @@ -1957,7 +2047,7 @@ TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const while (next_tempo != metrics.end ()) { ++next_tempo; - + if (next_tempo != metrics.end() && dynamic_cast(*next_tempo)) { break; } @@ -2002,7 +2092,7 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const keep going to get the previous tempo (or metrics.rend()) */ - + if (f <= pos) { if (tempo == 0) { /* first tempo with position at or @@ -2032,7 +2122,7 @@ TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const */ while (!!beats) { - + /* Distance to the start of this section in frames */ framecnt_t distance_frames = (pos - tempo->frame()); @@ -2147,7 +2237,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; @@ -2207,7 +2297,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)); @@ -2254,7 +2344,7 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const 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::Beats beats = Evoral::Beats(); while (distance) { @@ -2302,7 +2392,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; } @@ -2331,7 +2421,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(); } @@ -2367,7 +2457,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 */ @@ -2385,17 +2475,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() << ' ';