#include <glibmm/threads.h>
#include "pbd/xml++.h"
-#include "evoral/types.hpp"
+#include "evoral/Beats.hpp"
#include "ardour/debug.h"
+#include "ardour/lmath.h"
#include "ardour/tempo.h"
#include "i18n.h"
/***********************************************************************/
-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".
TempoSection::TempoSection (const XMLNode& node)
: MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
{
- const XMLProperty *prop;
+ XMLProperty const * prop;
BBT_Time start;
- LocaleGuard lg (X_("POSIX"));
+ LocaleGuard lg;
if ((prop = node.property ("start")) == 0) {
error << _("TempoSection XML node has no \"start\" property") << endmsg;
{
XMLNode *root = new XMLNode (xml_state_node_name);
char buf[256];
- LocaleGuard lg (X_("POSIX"));
+ LocaleGuard lg;
snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
start().bars,
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()));
}
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);
MeterSection::MeterSection (const XMLNode& node)
: MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
{
- const XMLProperty *prop;
+ XMLProperty const * prop;
BBT_Time start;
- LocaleGuard lg (X_("POSIX"));
+ LocaleGuard lg;
if ((prop = node.property ("start")) == 0) {
error << _("MeterSection XML node has no \"start\" property") << endmsg;
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) {
{
XMLNode *root = new XMLNode (xml_state_node_name);
char buf[256];
- LocaleGuard lg (X_("POSIX"));
+ LocaleGuard lg;
snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
start().bars,
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());
{
Glib::Threads::RWLock::WriterLock lm (lock);
- Metrics::iterator i;
-
- for (i = metrics.begin(); i != metrics.end(); ++i) {
- if (dynamic_cast<TempoSection*> (*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) {
}
}
+bool
+TempoMap::remove_tempo_locked (const TempoSection& tempo)
+{
+ Metrics::iterator i;
+
+ for (i = metrics.begin(); i != metrics.end(); ++i) {
+ if (dynamic_cast<TempoSection*> (*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)
{
{
Glib::Threads::RWLock::WriterLock lm (lock);
- Metrics::iterator i;
-
- for (i = metrics.begin(); i != metrics.end(); ++i) {
- if (dynamic_cast<MeterSection*> (*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) {
}
}
+bool
+TempoMap::remove_meter_locked (const MeterSection& tempo)
+{
+ Metrics::iterator i;
+
+ for (i = metrics.begin(); i != metrics.end(); ++i) {
+ if (dynamic_cast<MeterSection*> (*i) != 0) {
+ if (tempo.frame() == (*i)->frame()) {
+ if ((*i)->movable()) {
+ metrics.erase (i);
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
void
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<MeterSection*>(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
(*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;
- }
+ }
} else if (!iter_is_tempo && !insert_is_tempo) {
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<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
need_add = false;
} else {
metrics.erase (i);
-
+
}
break;
break;
}
}
-
+
metrics.insert (i, section);
}
}
void
TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
{
- 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 */
- *static_cast<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<Tempo*>(&first) = tempo;
+ recompute_map (false);
+ }
}
}
{
{
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<const MeterSection*>(*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;
- ts->update_bar_offset_from_bbt (*meter);
+ TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
- /* and insert it */
-
- do_insert (ts);
+ /* find the meter to use to set the bar offset of this
+ * tempo section.
+ */
- recompute_map (false);
+ 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<const MeterSection*>(*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)
{
- 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 */
*static_cast<Meter*>(&first) = meter;
recompute_map (true);
{
{
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);
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)
{
for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
- {
+ {
Glib::Threads::RWLock::WriterLock lm (lock);
*((Tempo*) t) = newtempo;
recompute_map (false);
{
MeterSection *m = 0;
+ /* CALLER MUST HOLD LOCK */
+
for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
return *m;
{
const TempoSection *t = 0;
+ /* CALLER MUST HOLD LOCK */
+
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
return *t;
*/
int additional_minutes = 1;
-
+
while (1) {
if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
break;
TempoSection* ts;
MeterSection* ms;
-
+
if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
/* reassign the BBT time of this tempo section
return;
}
- BBTPointList::const_iterator i = _map.end();
+ BBTPointList::const_iterator i = _map.end();
Metrics::iterator next_metric;
--i;
}
/* 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) {
}
/* 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<TempoSection*> ((*i).tempo),
+ _extend_map (const_cast<TempoSection*> ((*i).tempo),
const_cast<MeterSection*> ((*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)
{
* 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
* 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<MeterSection*>(*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)) {
}
}
- }
+ }
if (current.beats == 1) {
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
*last = i;
}
}
-
+
return m;
}
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));
}
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");
}
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);
{
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;
/* 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);
++i;
assert (i != _map.end());
the_beat.ticks -= BBT_Time::ticks_per_beat;
- }
+ }
} else if (dir < 0) {
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));
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) {
}
}
- 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);
}
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) {
}
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;
}
}
- 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;
} else {
return (*next).frame;
}
-
+
}
break;
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) {
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 {
the beat before frame.
*/
++next;
-
+
if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
return (*prev).frame;
} else {
}
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);
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<MeterSection*> (*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
metrics.clear();
nlist = node.children();
-
+
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
XMLNode* child = *niter;
if (ts->bar_offset() < 0.0) {
if (last_meter) {
ts->update_bar_offset_from_bbt (*last_meter);
- }
+ }
}
}
if (prev != metrics.end()) {
if (dynamic_cast<MeterSection*>(*prev) && dynamic_cast<MeterSection*>(*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<TempoSection*>(*prev) && dynamic_cast<TempoSection*>(*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;
}
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());
// 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<TempoSection*>(*i)) != 0) {
tempo = t;
// cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
fatal << _("programming error: unhandled MetricSection type") << endmsg;
abort(); /*NOTREACHED*/
}
-
+
prev = (*i);
}
-
+
recompute_map (true);
}
PropertyChanged (PropertyChange ());
}
+bool
+TempoMap::remove_time (framepos_t where, framecnt_t amount)
+{
+ bool moved = false;
+
+ std::list<MetricSection*> 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<TempoSection*> (*i);
+ if (lt)
+ last_tempo = lt;
+ MeterSection *lm = dynamic_cast<MeterSection*> (*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<TempoSection*> (*i);
+ meter_after = dynamic_cast<MeterSection*> (*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<MetricSection*>::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;
if (pos < 0 && f == 0) {
f = pos;
}
-
+
if (f > pos) {
break;
}
-
+
tempo = t;
}
}
framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
/* Distance to the end in beats */
- Evoral::MusicalTime distance_beats = Evoral::MusicalTime::ticks_at_rate(
+ Evoral::Beats distance_beats = Evoral::Beats::ticks_at_rate(
distance_frames, tempo->frames_per_beat (_frame_rate));
/* Amount to subtract this time */
- Evoral::MusicalTime 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()),
while (next_tempo != metrics.end ()) {
++next_tempo;
-
+
if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
break;
}
/** 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;
keep going to get the previous tempo (or
metrics.rend())
*/
-
+
if (f <= pos) {
if (tempo == 0) {
/* first tempo with position at or
*/
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 = Evoral::MusicalTime::ticks_at_rate(
+ Evoral::Beats distance_beats = Evoral::Beats::ticks_at_rate(
distance_frames, tempo->frames_per_beat (_frame_rate));
/* Amount to subtract this time */
- Evoral::MusicalTime 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));
}
} else {
pos -= llrint (beats.to_double() * tempo->frames_per_beat (_frame_rate));
- beats = Evoral::MusicalTime();
+ beats = Evoral::Beats();
}
}
* traversed before we change the
* frames_per_beat value.
*/
-
+
pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
bars = 0;
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));
/** 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);
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 = Evoral::MusicalTime();
+
+ Evoral::Beats beats = Evoral::Beats();
while (distance) {
pos += sub;
distance -= sub;
assert (tempo);
- beats += Evoral::MusicalTime::ticks_at_rate(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));
while (next_tempo != metrics.end ()) {
++next_tempo;
-
+
if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
break;
}
if (pos < 0) {
/* not really correct, but we should catch pos < 0 at a higher
- level
+ level
*/
return _map.begin();
}
}
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 */
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() << ' ';