#include <algorithm>
#include <stdexcept>
+#include <cmath>
#include <unistd.h>
-#include <cmath>
-
-#include <glibmm/thread.h>
+#include <glibmm/threads.h>
#include "pbd/xml++.h"
#include "evoral/types.hpp"
#include "ardour/debug.h"
#include "ardour/tempo.h"
-#include "ardour/utils.h"
#include "i18n.h"
#include <locale.h>
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
-Meter::frames_per_division (const Tempo& tempo, framecnt_t sr) const
+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
+ grid that is constructed from tempo and meter sections.
+
+ The return value IS NOT interpretable in terms of "beats".
+ */
+
return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
}
double
Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
{
- return frames_per_division (tempo, sr) * _divisions_per_bar;
+ return frames_per_grid (tempo, sr) * _divisions_per_bar;
}
/***********************************************************************/
TempoSection::update_bar_offset_from_bbt (const Meter& m)
{
- _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_bar_division + start().ticks) /
- (m.divisions_per_bar() * BBT_Time::ticks_per_bar_division);
+ _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()));
}
new_start.bars = start().bars;
- double ticks = BBT_Time::ticks_per_bar_division * meter.divisions_per_bar() * _bar_offset;
- new_start.beats = (uint32_t) floor(ticks/BBT_Time::ticks_per_bar_division);
- new_start.ticks = (uint32_t) fmod (ticks, BBT_Time::ticks_per_bar_division);
+ 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;
bool removed = false;
{
- Glib::RWLock::WriterLock lm (lock);
+ Glib::Threads::RWLock::WriterLock lm (lock);
Metrics::iterator i;
for (i = metrics.begin(); i != metrics.end(); ++i) {
bool removed = false;
{
- Glib::RWLock::WriterLock lm (lock);
+ Glib::Threads::RWLock::WriterLock lm (lock);
Metrics::iterator i;
for (i = metrics.begin(); i != metrics.end(); ++i) {
}
}
- Metrics::iterator i;
+
/* Look for any existing MetricSection that is of the same type and
- at the same time as the new one, and remove it before adding
- the new one.
+ in the same bar as the new one, and remove it before adding
+ the new one. Note that this means that if we find a matching,
+ existing section, we can break out of the loop since we're
+ guaranteed that there is only one such match.
*/
- Metrics::iterator to_remove = metrics.end ();
+ for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
- for (i = metrics.begin(); i != metrics.end(); ++i) {
+ bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
+ bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
- int const c = (*i)->compare (*section);
+ if (iter_is_tempo && insert_is_tempo) {
- if (c < 0) {
- /* this section is before the one to be added; go back round */
- continue;
- } else if (c > 0) {
- /* this section is after the one to be added; there can't be any at the same time */
- break;
- }
+ /* Tempo sections */
- /* hacky comparison of type */
- bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
- bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
+ if ((*i)->start().bars == section->start().bars &&
+ (*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<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
+ need_add = false;
+ } else {
+ metrics.erase (i);
+ }
+ break;
+ }
- if (iter_is_tempo == insert_is_tempo) {
+ } else if (!iter_is_tempo && !insert_is_tempo) {
- if (!(*i)->movable()) {
+ /* Meter Sections */
- /* can't (re)move this section, so overwrite it
- */
+ if ((*i)->start().bars == section->start().bars) {
- if (!iter_is_tempo) {
- *(dynamic_cast<MeterSection*>(*i)) = *(dynamic_cast<MeterSection*>(section));
+ if (!(*i)->movable()) {
+
+ /* can't (re)move this section, so overwrite
+ * its data content (but not its properties as
+ * a section
+ */
+
+ *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
+ need_add = false;
} else {
- *(dynamic_cast<TempoSection*>(*i)) = *(dynamic_cast<TempoSection*>(section));
+ metrics.erase (i);
+
}
- need_add = false;
+
break;
}
-
- to_remove = i;
- break;
+ } else {
+ /* non-matching types, so we don't care */
}
}
- if (to_remove != metrics.end()) {
- /* remove the MetricSection at the same time as the one we are about to add */
- metrics.erase (to_remove);
- }
-
- /* Add the given MetricSection */
+ /* Add the given MetricSection, if we didn't just reset an existing
+ * one above
+ */
if (need_add) {
+
+ Metrics::iterator i;
+
for (i = metrics.begin(); i != metrics.end(); ++i) {
-
- if ((*i)->compare (*section) < 0) {
- continue;
+ if ((*i)->start() > section->start()) {
+ break;
}
-
- metrics.insert (i, section);
- break;
- }
-
- if (i == metrics.end()) {
- metrics.insert (metrics.end(), section);
}
+
+ metrics.insert (i, section);
}
}
{
const TempoSection& first (first_tempo());
- if (ts != first) {
+ if (ts.start() != first.start()) {
remove_tempo (ts, false);
add_tempo (tempo, where);
} else {
{
- Glib::RWLock::WriterLock lm (lock);
+ Glib::Threads::RWLock::WriterLock lm (lock);
/* cannot move the first tempo section */
*((Tempo*)&first) = tempo;
recompute_map (false);
TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
{
{
- Glib::RWLock::WriterLock lm (lock);
+ Glib::Threads::RWLock::WriterLock lm (lock);
/* new tempos always start on a beat */
where.ticks = 0;
{
const MeterSection& first (first_meter());
- if (ms != first) {
+ if (ms.start() != first.start()) {
remove_meter (ms, false);
add_meter (meter, where);
} else {
{
- Glib::RWLock::WriterLock lm (lock);
+ Glib::Threads::RWLock::WriterLock lm (lock);
/* cannot move the first meter section */
*((Meter*)&first) = meter;
recompute_map (true);
TempoMap::add_meter (const Meter& meter, BBT_Time where)
{
{
- Glib::RWLock::WriterLock lm (lock);
+ Glib::Threads::RWLock::WriterLock lm (lock);
/* a new meter always starts a new bar on the first beat. so
round the start time appropriately. remember that
for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
{
- Glib::RWLock::WriterLock lm (lock);
+ Glib::Threads::RWLock::WriterLock lm (lock);
*((Tempo*) t) = newtempo;
recompute_map (false);
}
/* reset */
{
- Glib::RWLock::WriterLock lm (lock);
+ Glib::Threads::RWLock::WriterLock lm (lock);
/* cannot move the first tempo section */
*((Tempo*)prev) = newtempo;
recompute_map (false);
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);
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
{
/* CALLER MUST HOLD WRITE LOCK */
- MeterSection* meter;
- TempoSection* tempo;
- TempoSection* ts;
- MeterSection* ms;
+ MeterSection* meter = 0;
+ TempoSection* tempo = 0;
double current_frame;
BBT_Time current;
Metrics::iterator next_metric;
- if (end == 0) {
- /* silly call from Session::process() during startup
- */
- return;
- }
-
if (end < 0) {
- if (_map.empty()) {
- /* compute 1 mins worth */
- end = _frame_rate * 60;
- } else {
- end = _map.back().frame;
- }
+ /* we will actually stop once we hit
+ the last metric.
+ */
+ end = max_framepos;
+
} else {
if (!_map.empty ()) {
/* never allow the map to be shortened */
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+ MeterSection* ms;
+
if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
meter = ms;
break;
}
for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+ TempoSection* ts;
+
if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
tempo = ts;
break;
DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
-
+
+ TempoSection* ts;
+ MeterSection* ms;
+
if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
/* reassign the BBT time of this tempo section
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
_map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
+ if (end == 0) {
+ /* silly call from Session::process() during startup
+ */
+ return;
+ }
+
_extend_map (tempo, meter, next_metric, current, current_frame, end);
}
TempoSection* ts;
MeterSection* ms;
- double divisions_per_bar;
double beat_frames;
+ framepos_t bar_start_frame;
+
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
+
+ if (current.beats == 1) {
+ bar_start_frame = current_frame;
+ } else {
+ bar_start_frame = 0;
+ }
- divisions_per_bar = meter->divisions_per_bar ();
- beat_frames = meter->frames_per_division (*tempo,_frame_rate);
+ beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
while (current_frame < end) {
-
+
current.beats++;
current_frame += beat_frames;
if (!(current < (*next_metric)->start())) {
- set_metrics:
+ set_metrics:
if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
tempo = ts;
if (tempo->start().ticks != 0) {
- double next_beat_frames = meter->frames_per_division (*tempo,_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()));
+ tempo->start(), current_frame, tempo->bar_offset()));
/* back up to previous beat */
current_frame -= beat_frames;
- /* set tempo section location based on offset from last beat */
- tempo->set_frame (current_frame + (ts->bar_offset() * beat_frames));
- /* advance to the location of the new (adjusted) beat */
- current_frame += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames);
+
+ /* set tempo section location
+ * based on offset from last
+ * bar start
+ */
+ tempo->set_frame (bar_start_frame +
+ llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames)));
+
+ /* advance to the location of
+ * the new (adjusted) beat. do
+ * this by figuring out the
+ * offset within the beat that
+ * would have been there
+ * without the tempo
+ * change. then stretch the
+ * beat accordingly.
+ */
+
+ double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
+
+ current_frame += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
+
/* next metric doesn't have to
* match this precisely to
* merit a reloop ...
} 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);
}
*/
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
- meter->start(), current, current_frame));
+ meter->start(), current, current_frame));
assert (current.beats == 1);
meter->set_frame (current_frame);
}
- divisions_per_bar = meter->divisions_per_bar ();
- beat_frames = meter->frames_per_division (*tempo, _frame_rate);
+ beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
- beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
+ beat_frames, meter->divisions_per_bar(), *((Meter*)meter), *((Tempo*)tempo)));
++next_metric;
if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
/* same position so go back and set this one up before advancing
- */
+ */
goto set_metrics;
}
+
}
- }
+ }
if (current.beats == 1) {
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
_map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
+ bar_start_frame = current_frame;
} else {
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
_map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
}
+
+ if (next_metric == metrics.end()) {
+ /* no more metrics - we've timestamped them all, stop here */
+ if (end == max_framepos) {
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("stop extending map now that we've reach the end @ %1|%2 = %3\n",
+ current.bars, current.beats, current_frame));
+ break;
+ }
+ }
}
}
TempoMetric
-TempoMap::metric_at (framepos_t frame) const
+TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
{
- Glib::RWLock::ReaderLock lm (lock);
+ Glib::Threads::RWLock::ReaderLock lm (lock);
TempoMetric m (first_meter(), first_tempo());
- const Meter* meter;
- const Tempo* tempo;
/* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
at something, because we insert the default tempo and meter during
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
- // cerr << "Looking at a metric section " << **i << endl;
-
if ((*i)->frame() > frame) {
break;
}
- if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
- m.set_tempo (*tempo);
- } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
- m.set_meter (*meter);
- }
+ m.set_metric(*i);
- m.set_frame ((*i)->frame ());
- m.set_start ((*i)->start ());
+ if (last) {
+ *last = i;
+ }
}
- // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << " location " << m.frame() << " = " << m.start() << endl;
return m;
}
TempoMetric
TempoMap::metric_at (BBT_Time bbt) const
{
- Glib::RWLock::ReaderLock lm (lock);
+ Glib::Threads::RWLock::ReaderLock lm (lock);
TempoMetric m (first_meter(), first_tempo());
- const Meter* meter;
- const Tempo* tempo;
/* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
at something, because we insert the default tempo and meter during
break;
}
- if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
- m.set_tempo (*tempo);
- } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
- m.set_meter (*meter);
- }
-
- m.set_frame ((*i)->frame ());
- m.set_start (section_start);
+ m.set_metric (*i);
}
return m;
{
require_map_to (frame);
- Glib::RWLock::ReaderLock lm (lock);
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+
+ if (frame < 0) {
+ bbt.bars = 1;
+ bbt.beats = 1;
+ bbt.ticks = 0;
+ warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
+ return;
+ }
+
return bbt_time (frame, bbt, bbt_before_or_at (frame));
}
void
TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
{
- Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
+ Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
if (!lm.locked()) {
throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
if ((*i).frame == frame) {
bbt.ticks = 0;
} else {
- bbt.ticks = llrint (((frame - (*i).frame) / (*i).meter->frames_per_division(*((*i).tempo), _frame_rate)) *
- BBT_Time::ticks_per_bar_division);
+ bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
+ BBT_Time::ticks_per_beat);
}
}
framepos_t
TempoMap::frame_time (const BBT_Time& bbt)
{
+ if (bbt.bars < 1) {
+ warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
+ return 0;
+ }
+
+ if (bbt.beats < 1) {
+ throw std::logic_error ("beats are counted from one");
+ }
+
require_map_to (bbt);
- Glib::RWLock::ReaderLock lm (lock);
+ Glib::Threads::RWLock::ReaderLock lm (lock);
BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
if (bbt.ticks != 0) {
return ((*e).frame - (*s).frame) +
- llrint ((*e).meter->frames_per_division (*(*e).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division));
+ llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
} else {
return ((*e).frame - (*s).frame);
}
framecnt_t
TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
{
- Glib::RWLock::ReaderLock lm (lock);
- framecnt_t frames = 0;
BBT_Time when;
-
bbt_time (pos, when);
- frames = bbt_duration_at_unlocked (when, bbt,dir);
-
- return frames;
+
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ return bbt_duration_at_unlocked (when, bbt, dir);
}
framecnt_t
-TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir)
+TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int /*dir*/)
{
if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
return 0;
/* 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).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (when.ticks/BBT_Time::ticks_per_bar_division);
- }
-
uint32_t bars = 0;
uint32_t beats = 0;
/* add any additional frames related to ticks in the added value */
if (bbt.ticks != 0) {
- tick_frames += (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division);
+ return ((*wi).frame - (*start).frame) +
+ (*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
{
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;
DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
- ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_bar_division / sub_num;
+ ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
if (dir > 0) {
the_beat.ticks += ticks_one_subdivisions_worth - mod;
}
- if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
+ if (the_beat.ticks > BBT_Time::ticks_per_beat) {
assert (i != _map.end());
++i;
assert (i != _map.end());
- the_beat.ticks -= BBT_Time::ticks_per_bar_division;
+ the_beat.ticks -= BBT_Time::ticks_per_beat;
}
return fr;
}
--i;
- the_beat.ticks = BBT_Time::ticks_per_bar_division - the_beat.ticks;
+ the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
} else {
the_beat.ticks -= difference;
}
DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
- if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
+ if (the_beat.ticks > BBT_Time::ticks_per_beat) {
assert (i != _map.end());
++i;
assert (i != _map.end());
- the_beat.ticks -= BBT_Time::ticks_per_bar_division;
+ the_beat.ticks -= BBT_Time::ticks_per_beat;
DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
}
}
/* step back to previous beat */
--i;
- the_beat.ticks = lrint (BBT_Time::ticks_per_bar_division - rem);
+ the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
} else {
the_beat.ticks = lrint (the_beat.ticks - rem);
}
}
- return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_bar_division) *
- (*i).meter->frames_per_division (*((*i).tempo), _frame_rate);
+ return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
+ (*i).tempo->frames_per_beat (_frame_rate);
}
framepos_t
{
require_map_to (frame);
- Glib::RWLock::ReaderLock lm (lock);
+ Glib::Threads::RWLock::ReaderLock lm (lock);
BBTPointList::const_iterator fi;
if (dir > 0) {
assert (fi != _map.end());
- DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3|%4 @ %5) to bars in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame));
+ DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round from %1 (%3|%4 @ %5) to %6 in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame,
+ (type == Bar ? "bar" : "beat")));
switch (type) {
case Bar:
if (dir < 0) {
/* find bar previous to 'frame' */
+ if (fi == _map.begin()) {
+ return 0;
+ }
+
if ((*fi).is_bar() && (*fi).frame == frame) {
--fi;
}
prev--;
}
- while ((*next).beat != 1) {
+ while ((next != _map.end()) && (*next).beat != 1) {
next++;
- if (next == _map.end()) {
- --next;
- break;
- }
}
- if ((frame - (*prev).frame) < ((*next).frame - frame)) {
+ if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
return (*prev).frame;
} else {
return (*next).frame;
case Beat:
if (dir < 0) {
+
+ if (fi == _map.begin()) {
+ return 0;
+ }
+
if ((*fi).frame >= frame) {
DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
--fi;
BBTPointList::const_iterator prev = fi;
BBTPointList::const_iterator next = fi;
- --prev;
+
+ /* fi is already the beat before_or_at frame, and
+ we've just established that its not at frame, so its
+ the beat before frame.
+ */
++next;
- if ((frame - (*prev).frame) < ((*next).frame - frame)) {
+ if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
return (*prev).frame;
} else {
return (*next).frame;
}
void
-TempoMap::map (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::RWLock::WriterLock lm (lock);
+ Glib::Threads::RWLock::WriterLock lm (lock);
if (_map.empty() || (_map.back().frame < upper)) {
recompute_map (false, upper);
}
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;
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());
}
TempoMap::set_state (const XMLNode& node, int /*version*/)
{
{
- Glib::RWLock::WriterLock lm (lock);
+ Glib::Threads::RWLock::WriterLock lm (lock);
XMLNodeList nlist;
XMLNodeConstIterator niter;
Metrics old_metrics (metrics);
MeterSection* last_meter = 0;
-
metrics.clear();
nlist = node.children();
metrics.sort (cmp);
}
- recompute_map (true);
+ /* check for multiple tempo/meters at the same location, which
+ ardour2 somehow allowed.
+ */
+
+ Metrics::iterator prev = metrics.end();
+ for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+ if (prev != metrics.end()) {
+ if (dynamic_cast<MeterSection*>(*prev) && dynamic_cast<MeterSection*>(*i)) {
+ if ((*prev)->start() == (*i)->start()) {
+ error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
+ return -1;
+ }
+ } else if (dynamic_cast<TempoSection*>(*prev) && dynamic_cast<TempoSection*>(*i)) {
+ if ((*prev)->start() == (*i)->start()) {
+ error << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
+ return -1;
+ }
+ }
+ }
+ prev = i;
+ }
+
+ recompute_map (true, -1);
}
PropertyChanged (PropertyChange ());
void
TempoMap::dump (std::ostream& o) const
{
- Glib::RWLock::ReaderLock lm (lock);
+ Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
const MeterSection* m;
const TempoSection* t;
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) {
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) {
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);
first = false;
} else {
- if (bbt.ticks > BBT_Time::ticks_per_bar_division/2) {
+ if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
/* round up to next beat */
bbt.beats += 1;
}
* pos can be -ve, if required.
*/
framepos_t
-TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats)
+TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
{
- return framepos_plus_bbt (pos, BBT_Time (beats));
-}
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ Metrics::const_iterator next_tempo;
+ const TempoSection* tempo = 0;
-/** Subtract some (fractional) beats to a frame position, and return the result in frames */
-framepos_t
-TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats)
-{
- return framepos_minus_bbt (pos, BBT_Time (beats));
+ /* Find the starting tempo metric */
+
+ for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
+
+ const TempoSection* t;
+
+ if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
+
+ /* This is a bit of a hack, but pos could be -ve, and if it is,
+ we consider the initial metric changes (at time 0) to actually
+ be in effect at pos.
+ */
+
+ framepos_t f = (*next_tempo)->frame ();
+
+ if (pos < 0 && f == 0) {
+ f = pos;
+ }
+
+ if (f > pos) {
+ break;
+ }
+
+ tempo = t;
+ }
+ }
+
+ /* We now have:
+
+ tempo -> the Tempo for "pos"
+ next_tempo -> first tempo after "pos", possibly metrics.end()
+ */
+
+ DEBUG_TRACE (DEBUG::TempoMath,
+ string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
+ pos, beats, *((const Tempo*)tempo), tempo->frame()));
+
+ 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);
+
+ /* Amount to subtract this time */
+ double 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()),
+ distance_frames, distance_beats));
+
+ /* Update */
+ beats -= delta;
+ pos += delta * tempo->frames_per_beat (_frame_rate);
+
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
+
+ /* step forwards to next tempo section */
+
+ if (next_tempo != metrics.end()) {
+
+ tempo = dynamic_cast<const TempoSection*>(*next_tempo);
+
+ 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 ()) {
+
+ ++next_tempo;
+
+ if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
+ break;
+ }
+ }
+ }
+ }
+
+ return pos;
}
+/** Subtract some (fractional) beats from a frame position, and return the result in frames */
framepos_t
-TempoMap::framepos_minus_bbt (framepos_t pos, BBT_Time op)
+TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
{
- Glib::RWLock::ReaderLock lm (lock);
- BBTPointList::const_iterator i;
- framecnt_t extra_frames = 0;
- bool had_bars = (op.bars != 0);
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ Metrics::const_reverse_iterator prev_tempo;
+ const TempoSection* tempo = 0;
- /* start from the bar|beat right before (or at) pos */
+ /* Find the starting tempo metric */
- i = bbt_before_or_at (pos);
-
- /* we know that (*i).frame is less than or equal to pos */
- extra_frames = pos - (*i).frame;
-
- /* walk backwards */
+ for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
- while (i != _map.begin() && (op.bars || op.beats)) {
- --i;
+ const TempoSection* t;
- if (had_bars) {
- if ((*i).is_bar()) {
- if (op.bars) {
- op.bars--;
- }
+ if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
+
+ /* This is a bit of a hack, but pos could be -ve, and if it is,
+ we consider the initial metric changes (at time 0) to actually
+ be in effect at pos.
+ */
+
+ framepos_t f = (*prev_tempo)->frame ();
+
+ if (pos < 0 && f == 0) {
+ f = pos;
}
- }
- if ((had_bars && op.bars == 0) || !had_bars) {
- /* finished counting bars, or none to count,
- so decrement beat count
+ /* this is slightly more complex than the forward case
+ because we reach the tempo in effect at pos after
+ passing through pos (rather before, as in the
+ forward case). having done that, we then need to
+ keep going to get the previous tempo (or
+ metrics.rend())
*/
- if (op.beats) {
- op.beats--;
+
+ if (f <= pos) {
+ if (tempo == 0) {
+ /* first tempo with position at or
+ before pos
+ */
+ tempo = t;
+ } else if (f < pos) {
+ /* some other tempo section that
+ is even earlier than 'tempo'
+ */
+ break;
+ }
}
}
}
-
- /* handle ticks (assumed to be less than
- * BBT_Time::ticks_per_bar_division, as always.
- */
- if (op.ticks) {
- frameoffset_t tick_frames = llrint ((*i).meter->frames_per_division (*(*i).tempo, _frame_rate) * (op.ticks/BBT_Time::ticks_per_bar_division));
- framepos_t pre_tick_frames = (*i).frame + extra_frames;
- if (tick_frames < pre_tick_frames) {
- return pre_tick_frames - tick_frames;
- }
- return 0;
- } else {
- return (*i).frame + extra_frames;
+ 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:
+
+ tempo -> the Tempo for "pos"
+ prev_tempo -> the first metric before "pos", possibly metrics.rend()
+ */
+
+ 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);
+
+ /* Amount to subtract this time */
+ double 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);
+
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
+ (prev_tempo == metrics.rend())));
+
+ /* step backwards to prior TempoSection */
+
+ if (prev_tempo != metrics.rend()) {
+
+ tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
+
+ 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 ()) {
+
+ ++prev_tempo;
+
+ if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
+ break;
+ }
+ }
+ } else {
+ pos -= llrint (beats * tempo->frames_per_beat (_frame_rate));
+ beats = 0;
+ }
}
+
+ return pos;
}
/** Add the BBT interval op to pos and return the result */
framepos_t
-TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op)
+TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
{
- Glib::RWLock::ReaderLock lm (lock);
- BBT_Time op_copy (op);
- int additional_minutes = 1;
- BBTPointList::const_iterator i;
- framecnt_t backup_frames = 0;
- bool had_bars = (op.bars != 0);
-
- while (true) {
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ Metrics::const_iterator i;
+ const MeterSection* meter;
+ const MeterSection* m;
+ const TempoSection* tempo;
+ const TempoSection* t;
+ double frames_per_beat;
+ framepos_t effective_pos = max (pos, (framepos_t) 0);
- i = bbt_before_or_at (pos);
+ meter = &first_meter ();
+ tempo = &first_tempo ();
- op = op_copy;
+ assert (meter);
+ assert (tempo);
- /* we know that (*i).frame is before or equal to pos */
- backup_frames = pos - (*i).frame;
+ /* find the starting metrics for tempo & meter */
- while (i != _map.end() && (op.bars || op.beats)) {
+ for (i = metrics.begin(); i != metrics.end(); ++i) {
- ++i;
+ if ((*i)->frame() > effective_pos) {
+ break;
+ }
- if (had_bars) {
- if ((*i).is_bar()) {
- if (op.bars) {
- op.bars--;
- }
- }
- }
-
- if ((had_bars && op.bars == 0) || !had_bars) {
- /* finished counting bars, or none to count,
- so decrement beat count
- */
+ if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ meter = m;
+ }
+ }
+
+ /* We now have:
+
+ meter -> the Meter for "pos"
+ tempo -> the Tempo for "pos"
+ i -> for first new metric after "pos", possibly metrics.end()
+ */
+
+ /* now comes the complicated part. we have to add one beat a time,
+ checking for a new metric on every beat.
+ */
+
+ frames_per_beat = tempo->frames_per_beat (_frame_rate);
+
+ uint64_t bars = 0;
+
+ while (op.bars) {
- if (op.beats) {
- op.beats--;
+ bars++;
+ op.bars--;
+
+ /* check if we need to use a new metric section: has adding frames moved us
+ to or after the start of the next metric section? in which case, use it.
+ */
+
+ if (i != metrics.end()) {
+ if ((*i)->frame() <= pos) {
+
+ /* about to change tempo or meter, so add the
+ * number of frames for the bars we've just
+ * traversed before we change the
+ * frames_per_beat value.
+ */
+
+ pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
+ bars = 0;
+
+ if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ meter = m;
}
+ ++i;
+ frames_per_beat = tempo->frames_per_beat (_frame_rate);
+
}
}
-
- if (i != _map.end()) {
- break;
- }
- /* we hit the end of the map before finish the bbt walk.
- */
+ }
- recompute_map (false, pos + (_frame_rate * 60 * additional_minutes));
- additional_minutes *= 2;
+ pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
+
+ uint64_t beats = 0;
+
+ while (op.beats) {
+
+ /* given the current meter, have we gone past the end of the bar ? */
+
+ beats++;
+ op.beats--;
+
+ /* check if we need to use a new metric section: has adding frames moved us
+ to or after the start of the next metric section? in which case, use it.
+ */
+
+ if (i != metrics.end()) {
+ if ((*i)->frame() <= pos) {
+
+ /* about to change tempo or meter, so add the
+ * number of frames for the beats we've just
+ * traversed before we change the
+ * frames_per_beat value.
+ */
+
+ pos += llrint (beats * frames_per_beat);
+ beats = 0;
- /* go back and try again */
- warning << "reached end of map with op now at " << op << " end = "
- << _map.back().frame << ' ' << _map.back().bar << '|' << _map.back().beat << ", trying to walk "
- << op_copy << " ... retry"
- << endmsg;
+ if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ meter = m;
+ }
+ ++i;
+ frames_per_beat = tempo->frames_per_beat (_frame_rate);
+ }
+ }
}
-
+
+ pos += llrint (beats * frames_per_beat);
+
if (op.ticks) {
- return (*i).frame - backup_frames +
- llrint ((*i).meter->frames_per_division (*(*i).tempo, _frame_rate) * (op.ticks/BBT_Time::ticks_per_bar_division));
- } else {
- return (*i).frame - backup_frames;
+ 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) /
+ (double) BBT_Time::ticks_per_beat)));
+ } else {
+ pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
+ }
}
+
+ return pos;
}
/** Count the number of beats that are equivalent to distance when going forward,
starting at pos.
*/
Evoral::MusicalTime
-TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance)
+TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
{
- framepos_t end = pos + distance;
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ Metrics::const_iterator next_tempo;
+ const TempoSection* tempo = 0;
+ framepos_t effective_pos = max (pos, (framepos_t) 0);
- require_map_to (end);
+ /* Find the relevant initial tempo metric */
- Glib::RWLock::ReaderLock lm (lock);
- BBTPointList::const_iterator i = bbt_after_or_at (pos);
- Evoral::MusicalTime beats = 0;
+ for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
- /* if our starting BBTPoint is after pos, add a fractional beat
- to represent that distance.
- */
+ const TempoSection* t;
- if ((*i).frame != pos) {
- beats += ((*i).frame - pos) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
- }
+ if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
- while (i != _map.end() && (*i).frame < end) {
- ++i;
- beats++;
+ if ((*next_tempo)->frame() > effective_pos) {
+ break;
+ }
+
+ tempo = t;
+ }
}
- assert (i != _map.end());
-
- /* if our ending BBTPoint is after the end, subtract a fractional beat
- to represent that distance.
+ /* We now have:
+
+ tempo -> the Tempo for "pos"
+ next_tempo -> the next tempo after "pos", possibly metrics.end()
*/
- if ((*i).frame > end) {
- beats -= ((*i).frame - end) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
+ assert (tempo);
+
+ DEBUG_TRACE (DEBUG::TempoMath,
+ string_compose ("frame %1 walk by %2 frames, start with tempo = %3 @ %4\n",
+ pos, distance, *((const Tempo*)tempo), tempo->frame()));
+
+ Evoral::MusicalTime beats = 0;
+
+ while (distance) {
+
+ /* End of this section */
+ framepos_t end;
+ /* Distance to `end' in frames */
+ framepos_t distance_to_end;
+
+ if (next_tempo == metrics.end ()) {
+ /* We can't do (end - pos) if end is max_framepos, as it will overflow if pos is -ve */
+ end = max_framepos;
+ distance_to_end = max_framepos;
+ } else {
+ end = (*next_tempo)->frame ();
+ distance_to_end = end - pos;
+ }
+
+ /* Amount to subtract this time */
+ double const sub = min (distance, distance_to_end);
+
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("to reach end at %1 (end ? %2), distance= %3 sub=%4\n", end, (next_tempo == metrics.end()),
+ distance_to_end, sub));
+
+ /* Update */
+ pos += sub;
+ distance -= sub;
+ assert (tempo);
+ beats += sub / tempo->frames_per_beat (_frame_rate);
+
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1, beats = %2 distance left %3\n",
+ pos, beats, distance));
+
+ /* Move on if there's anything to move to */
+
+ if (next_tempo != metrics.end()) {
+
+ tempo = dynamic_cast<const TempoSection*>(*next_tempo);
+
+ 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 ()) {
+
+ ++next_tempo;
+
+ if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
+ break;
+ }
+ }
+
+ if (next_tempo == metrics.end()) {
+ DEBUG_TRACE (DEBUG::TempoMath, "no more tempo sections\n");
+ } else {
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("next tempo section is %1 @ %2\n",
+ **next_tempo, (*next_tempo)->frame()));
+ }
+
+ }
+ assert (tempo);
}
return beats;
BBTPointList::const_iterator i;
+ if (pos < 0) {
+ /* not really correct, but we should catch pos < 0 at a higher
+ level
+ */
+ return _map.begin();
+ }
+
i = lower_bound (_map.begin(), _map.end(), pos);
assert (i != _map.end());
if ((*i).frame > pos) {
return i;
}
-/** Compare the time of this with that of another MetricSection.
- * @param with_bbt True to compare using start(), false to use frame().
- * @return -1 for less than, 0 for equal, 1 for greater than.
- */
-
-int
-MetricSection::compare (const MetricSection& other) const
-{
- if (start() == other.start()) {
- return 0;
- } else if (start() < other.start()) {
- return -1;
- } else {
- return 1;
- }
-
- /* NOTREACHED */
- return 0;
-}
-
-bool
-MetricSection::operator== (const MetricSection& other) const
-{
- return compare (other) == 0;
-}
-
-bool
-MetricSection::operator!= (const MetricSection& other) const
-{
- return compare (other) != 0;
-}
-
std::ostream&
operator<< (std::ostream& o, const Meter& m) {
return o << m.divisions_per_bar() << '/' << m.note_divisor();
const MeterSection* ms;
if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
- o << *((Tempo*) ts);
+ o << *((const Tempo*) ts);
} else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
- o << *((Meter*) ms);
+ o << *((const Meter*) ms);
}
return o;