const string TempoSection::xml_state_node_name = "Tempo";
TempoSection::TempoSection (const XMLNode& node)
- : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
+ : MetricSection (0.0), Tempo (TempoMap::default_tempo())
{
const XMLProperty *prop;
- BBT_Time start;
LocaleGuard lg;
-
- if ((prop = node.property ("start")) == 0) {
+ BBT_Time bbt;
+ double beat;
+
+ if ((prop = node.property ("start")) != 0) {
+ if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+ &bbt.bars,
+ &bbt.beats,
+ &bbt.ticks) == 3) {
+ /* legacy session - start used to be in bbt*/
+ _legacy_bbt = bbt;
+ beat = -1.0;
+ set_beat (beat);
+ }
+ } else {
error << _("TempoSection XML node has no \"start\" property") << endmsg;
- throw failed_constructor();
}
- if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
- &start.bars,
- &start.beats,
- &start.ticks) < 3) {
- error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
- throw failed_constructor();
+
+ if ((prop = node.property ("beat")) != 0) {
+ if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
+ error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
+ } else {
+ set_beat (beat);
+ }
+ } else {
+ error << _("TempoSection XML node has no \"beat\" property") << endmsg;
}
- set_start (start);
if ((prop = node.property ("beats-per-minute")) == 0) {
error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
char buf[256];
LocaleGuard lg;
- snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
- start().bars,
- start().beats,
- start().ticks);
- root->add_property ("start", buf);
+ snprintf (buf, sizeof (buf), "%f", beat());
+ root->add_property ("beat", buf);
snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
root->add_property ("beats-per-minute", buf);
snprintf (buf, sizeof (buf), "%f", _note_type);
TempoSection::update_bar_offset_from_bbt (const Meter& m)
{
- _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) /
+ _bar_offset = (beat() * BBT_Time::ticks_per_beat) /
(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()));
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, beat(), m.divisions_per_bar()));
}
void
_type = type;
}
+/** returns the tempo at the zero-based (relative to tempo section) frame.
+*/
double
TempoSection::tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
{
return tick_tempo_at_time (frame_to_minute (frame, frame_rate), end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)) / BBT_Time::ticks_per_beat;
}
+/** returns the zero-based frame (relative to tempo section)
+ where the tempo occurs.
+*/
framepos_t
TempoSection::frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
{
return minute_to_frame (time_at_tick_tempo (tempo * BBT_Time::ticks_per_beat, end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
}
+/** returns the zero-based tick (relative to tempo section)
+ where the zero-based frame (relative to tempo section)
+ lies.
+*/
double
TempoSection::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
{
return tick_at_time (frame_to_minute (frame, frame_rate), end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
}
+/** returns the zero-based frame (relative to tempo section)
+ where the zero-based tick (relative to tempo section)
+ falls.
+*/
framepos_t
TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
{
return minute_to_frame (time_at_tick (tick, end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
}
-double TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+/** returns the zero-based beat (relative to tempo section)
+ where the zero-based frame (relative to tempo section)
+ lies.
+*/
+double
+TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
{
return tick_at_frame (frame, end_bpm, end_frame, frame_rate) / BBT_Time::ticks_per_beat;
}
-framepos_t TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
+/** returns the zero-based frame (relative to tempo section start frame)
+ where the zero-based beat (relative to tempo section start)
+ falls.
+*/
+
+framepos_t
+TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
{
return frame_at_tick (beat * BBT_Time::ticks_per_beat, end_bpm, end_frame, frame_rate);
}
return (frame / (double) frame_rate) / 60.0;
}
-/* constant for exp */
+/* position function */
double
-TempoSection::a_func (double begin_tpm, double end_tpm, double end_time) const
+TempoSection::a_func (double end_tpm, double c_func) const
{
- return log (end_tpm / ticks_per_minute()) / c_func (end_tpm, end_time);
+ return log (end_tpm / ticks_per_minute()) / c_func;
}
+
+/*function constant*/
double
TempoSection::c_func (double end_tpm, double end_time) const
{
return log (tick_tempo / ticks_per_minute()) / c_func (end_tpm, end_time);
}
-/* tempo in bpm at time in minutes */
-double
-TempoSection::tempo_at_time (double time, double end_bpm, double end_time) const
-{
- return tick_tempo_at_time (time, end_bpm * BBT_Time::ticks_per_beat, end_time) / BBT_Time::ticks_per_beat;
-}
-
-/* time in minutes at tempo in bpm */
-double
-TempoSection::time_at_tempo (double tempo, double end_bpm, double end_time) const
-{
- return time_at_tick_tempo (tempo * BBT_Time::ticks_per_beat, end_bpm * BBT_Time::ticks_per_beat, end_time);
-}
-
/* tick at time in minutes */
double
TempoSection::tick_at_time (double time, double end_tpm, double end_time) const
void
TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
{
- BBT_Time new_start;
+ double new_beat;
if (_bar_offset < 0.0) {
/* not set yet */
return;
}
- new_start.bars = start().bars;
+ new_beat = beat();
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); */
- new_start.ticks = (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat);
+ new_beat = 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",
- _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
+ _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
- set_start (new_start);
+ set_beat (new_beat);
}
/***********************************************************************/
const string MeterSection::xml_state_node_name = "Meter";
MeterSection::MeterSection (const XMLNode& node)
- : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
+ : MetricSection (0.0), Meter (TempoMap::default_meter())
{
XMLProperty const * prop;
BBT_Time start;
LocaleGuard lg;
-
- if ((prop = node.property ("start")) == 0) {
+ const XMLProperty *prop;
+ BBT_Time bbt;
+ double beat = 0.0;
+ pair<double, BBT_Time> start;
+
+ if ((prop = node.property ("start")) != 0) {
+ if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+ &bbt.bars,
+ &bbt.beats,
+ &bbt.ticks) < 3) {
+ error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
+ } else {
+ /* legacy session - start used to be in bbt*/
+ beat = -1.0;
+ }
+ } else {
error << _("MeterSection XML node has no \"start\" property") << endmsg;
- throw failed_constructor();
}
- if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
- &start.bars,
- &start.beats,
- &start.ticks) < 3) {
- error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
+ if ((prop = node.property ("beat")) != 0) {
+ if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
+ error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
+ }
+ } else {
+ error << _("MeterSection XML node has no \"beat\" property") << endmsg;
+ }
+
+ start.first = beat;
+
+ if ((prop = node.property ("bbt")) == 0) {
+ error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
+ } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+ &bbt.bars,
+ &bbt.beats,
+ &bbt.ticks) < 3) {
+ error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
throw failed_constructor();
}
- set_start (start);
+ start.second = bbt;
+
+ set_beat (start);
/* beats-per-bar is old; divisions-per-bar is new */
LocaleGuard lg;
snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
- start().bars,
- start().beats,
- start().ticks);
- root->add_property ("start", buf);
+ bbt().bars,
+ bbt().beats,
+ bbt().ticks);
+ root->add_property ("bbt", buf);
+ snprintf (buf, sizeof (buf), "%lf", beat());
+ root->add_property ("beat", buf);
snprintf (buf, sizeof (buf), "%f", _note_type);
root->add_property ("note-type", buf);
snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
struct MetricSectionSorter {
bool operator() (const MetricSection* a, const MetricSection* b) {
- return a->start() < b->start();
+ return a->beat() < b->beat();
+ }
+};
+
+struct MetricSectionFrameSorter {
+ bool operator() (const MetricSection* a, const MetricSection* b) {
+ return a->frame() < b->frame();
}
};
start.beats = 1;
start.ticks = 0;
- TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Type::Ramp);
- MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
+ TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Type::Constant);
+ MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
t->set_movable (false);
m->set_movable (false);
/* we only allow new meters to be inserted on beat 1 of an existing
* measure.
*/
-
- if (dynamic_cast<MeterSection*>(section)) {
- assert (section->start().ticks == 0);
+ MeterSection* m = 0;
+ if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
+ assert (m->bbt().ticks == 0);
/* 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;
+ if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
+ pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
+ corrected.second.beats = 1;
+ corrected.second.ticks = 0;
+ corrected.first = bbt_to_beats_unlocked (corrected.second);
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);
+ m->bbt(), corrected.second) << endmsg;
+ m->set_beat (corrected);
}
}
for (Metrics::iterator 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;
+ TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
+ TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
- if (iter_is_tempo && insert_is_tempo) {
+ if (tempo && insert_tempo) {
/* Tempo sections */
- if ((*i)->start().bars == section->start().bars &&
- (*i)->start().beats == section->start().beats) {
+ if (tempo->beat() == insert_tempo->beat()) {
- if (!(*i)->movable()) {
+ if (!tempo->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));
+ *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
need_add = false;
} else {
metrics.erase (i);
break;
}
- } else if (!iter_is_tempo && !insert_is_tempo) {
+ } else if (!tempo && !insert_tempo) {
/* Meter Sections */
+ MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
+ MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
+ if (meter->beat() == insert_meter->beat()) {
- if ((*i)->start().bars == section->start().bars) {
-
- if (!(*i)->movable()) {
+ if (!meter->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));
+ *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
need_add = false;
} else {
metrics.erase (i);
*/
if (need_add) {
+ MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
+ TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
Metrics::iterator i;
+ if (insert_meter) {
+ for (i = metrics.begin(); i != metrics.end(); ++i) {
+ MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
- for (i = metrics.begin(); i != metrics.end(); ++i) {
- if ((*i)->start() > section->start()) {
- break;
+ if (meter && meter->beat() > insert_meter->beat()) {
+ break;
+ }
+ }
+ } else if (insert_tempo) {
+ for (i = metrics.begin(); i != metrics.end(); ++i) {
+ TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
+
+ if (tempo) {
+ if (tempo->beat() > insert_tempo->beat()) {
+ break;
+ }
+ }
}
}
}
void
-TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where, TempoSection::Type type)
+TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
{
{
Glib::Threads::RWLock::WriterLock lm (lock);
TempoSection& first (first_tempo());
- if (ts.start() != first.start()) {
+ if (ts.beat() != first.beat()) {
remove_tempo_locked (ts);
add_tempo_locked (tempo, where, true, type);
} else {
}
void
-TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame)
+TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double beat_where)
{
{
- TempoSection& first (first_tempo());
+ Glib::Threads::RWLock::WriterLock lm (lock);
- if (ts.start() != first.start()) {
- BBT_Time bbt;
- bbt_time (frame, bbt);
- {
- Glib::Threads::RWLock::WriterLock lm (lock);
- ts.set_frame (frame);
- ts.set_start (bbt);
+ /* currently this is always done in audio time */
+ //if (ts.position_lock_style() == MusicTime) {
+ if (0) {
+ /* MusicTime */
+ ts.set_beat (beat_where);
+ MetricSectionSorter cmp;
+ metrics.sort (cmp);
+ } else {
+ /*AudioTime*/
+ ts.set_frame (frame);
- recompute_map (false);
+ MetricSectionFrameSorter fcmp;
+ metrics.sort (fcmp);
+
+ Metrics::const_iterator i;
+ TempoSection* prev_ts = 0;
+ TempoSection* next_ts = 0;
+
+ for (i = metrics.begin(); i != metrics.end(); ++i) {
+ TempoSection* t;
+ if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+
+ if (t->frame() >= frame) {
+ break;
+ }
+
+ prev_ts = t;
+ }
+ }
+
+ for (i = metrics.begin(); i != metrics.end(); ++i) {
+ TempoSection* t;
+ if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+
+ if (t->frame() > frame) {
+ next_ts = t;
+ break;
+ }
+ }
+ }
+
+ if (prev_ts) {
+ /* set the start beat */
+ double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate);
+ double beats = beats_to_ts + prev_ts->beat();
+
+ if (next_ts) {
+ if (next_ts->beat() < beats) {
+ /* with frame-based editing, it is possible to get in a
+ situation where if the tempo was placed at the mouse pointer frame,
+ the following music-based tempo would jump to an earlier frame,
+ changing the beat beat of the moved tempo.
+ in this case, we have to do some beat-based comparison TODO
+ */
+ } else if (prev_ts->beat() > beats) {
+ ts.set_beat (prev_ts->beat());
+ } else {
+ ts.set_beat (beats);
+ }
+ } else {
+ ts.set_beat (beats);
+ }
+ MetricSectionSorter cmp;
+ metrics.sort (cmp);
}
}
+
+ recompute_map (false);
}
MetricPositionChanged (); // Emit Signal
}
void
-TempoMap::add_tempo (const Tempo& tempo, BBT_Time where, ARDOUR::TempoSection::Type type)
+TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
{
{
Glib::Threads::RWLock::WriterLock lm (lock);
}
void
-TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute, ARDOUR::TempoSection::Type type)
+TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
{
TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), 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<const MeterSection*>(*i)) != 0) {
- meter = m;
- }
- }
-
- ts->update_bar_offset_from_bbt (*meter);
-
- /* and insert it */
-
do_insert (ts);
if (recompute) {
{
Glib::Threads::RWLock::WriterLock lm (lock);
MeterSection& first (first_meter());
-
- if (ms.start() != first.start()) {
+ if (ms.beat() != first.beat()) {
remove_meter_locked (ms);
- add_meter_locked (meter, where, true);
+ add_meter_locked (meter, bbt_to_beats_unlocked (where), where, true);
} else {
/* cannot move the first meter section */
*static_cast<Meter*>(&first) = meter;
}
void
-TempoMap::add_meter (const Meter& meter, BBT_Time where)
+TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
{
{
Glib::Threads::RWLock::WriterLock lm (lock);
- add_meter_locked (meter, where, true);
+ add_meter_locked (meter, beat, where, true);
}
}
void
-TempoMap::add_meter_locked (const Meter& meter, BBT_Time where, bool recompute)
+TempoMap::add_meter_locked (const Meter& meter, double beat, 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
/* new meters *always* start on a beat. */
where.ticks = 0;
- do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
+ do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
if (recompute) {
recompute_map (true);
{
/* CALLER MUST HOLD WRITE LOCK */
- MeterSection* meter = 0;
- TempoSection* tempo = 0;
- double current_frame;
- BBT_Time current;
- Metrics::iterator next_metric;
-
if (end < 0) {
/* we will actually stop once we hit
}
- MetricSectionSorter cmp;
- metrics.sort (cmp);
-
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;
- }
- }
-
- assert(meter);
-
- for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
- TempoSection* ts;
-
- if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
- tempo = ts;
- break;
- }
+ if (end == 0) {
+ /* silly call from Session::process() during startup
+ */
+ return;
}
- assert(tempo);
+ Metrics::const_iterator i;
- /* assumes that the first meter & tempo are at frame zero */
- current_frame = 0;
- meter->set_frame (0);
- tempo->set_frame (0);
+ TempoSection* prev_ts = 0;
- /* assumes that the first meter & tempo are at 1|1|0 */
- current.bars = 1;
- current.beats = 1;
- current.ticks = 0;
- if (reassign_tempo_bbt) {
+ for (i = metrics.begin(); i != metrics.end(); ++i) {
+ TempoSection* t;
- MeterSection* rmeter = meter;
+ if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
- DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
+ if (prev_ts) {
+ double const beats_relative_to_prev_ts = t->beat() - prev_ts->beat();
+ double const ticks_relative_to_prev_ts = beats_relative_to_prev_ts * BBT_Time::ticks_per_beat;
- for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+ /* assume (falsely) that the target tempo is constant */
+ double const t_fpb = t->frames_per_beat (_frame_rate);
+ double const av_fpb = (prev_ts->frames_per_beat (_frame_rate) + t_fpb) / 2.0;
+ /* this walk shouldn't be needed as given c, time a = log (Ta / T0) / c. what to do? */
+ double length_estimate = beats_relative_to_prev_ts * av_fpb;
- TempoSection* ts;
- MeterSection* ms;
+ if (prev_ts->type() == TempoSection::Type::Constant) {
+ length_estimate = beats_relative_to_prev_ts * prev_ts->frames_per_beat (_frame_rate);
+ }
- if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
+ double const system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute()) * 1.5;
+ double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
- /* reassign the BBT time of this tempo section
- * based on its bar offset position.
- */
+ while (fabs (tick_error) > system_precision_at_target_tempo) {
- ts->update_bbt_time_from_bar_offset (*rmeter);
+ double const actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(),
+ (framepos_t) length_estimate, _frame_rate);
+ tick_error = ticks_relative_to_prev_ts - actual_ticks;
+ length_estimate += tick_error * (t->ticks_per_minute() / _frame_rate);
+ }
- } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
- rmeter = ms;
- } else {
- fatal << _("programming error: unhandled MetricSection type") << endmsg;
- abort(); /*NOTREACHED*/
+ t->set_frame (length_estimate + prev_ts->frame());
}
+ prev_ts = t;
}
}
- DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
-
- next_metric = metrics.begin();
- ++next_metric; // skip meter (or tempo)
- ++next_metric; // skip tempo (or meter)
-
- DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
-
- if (end == 0) {
- /* silly call from Session::process() during startup
- */
- return;
- }
-
- _extend_map (tempo, meter, next_metric, current, current_frame, end);
-}
-
-void
-TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
- Metrics::iterator next_metric,
- BBT_Time current, framepos_t current_frame, framepos_t end)
-{
- /* CALLER MUST HOLD WRITE LOCK */
-
- uint32_t first_tick_in_new_meter = 0;
- Metrics::const_iterator i;
Metrics::const_iterator mi;
-
- TempoSection* prev_ts = tempo;
+ MeterSection* meter = 0;
for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
- MeterSection* m = 0;
-
- if ((m = dynamic_cast<MeterSection*> (*mi)) != 0) {
-
- if (m->start() >= prev_ts->start()) {
- first_tick_in_new_meter = ((((m->start().bars - 1) * meter->divisions_per_bar()) + (m->start().beats - 1)) * BBT_Time::ticks_per_beat) + m->start().ticks; // expressed in ticks from the previous meter
- for (i = metrics.begin(); i != metrics.end(); ++i) {
- TempoSection* t;
-
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-
- if (t->start() >= m->start() && t->start() > prev_ts->start()) {
- //cerr << "new ts start bars = " << t->start().bars << " beats = " << t->start().beats << " ticks = " << t->start().ticks << endl;
- //cerr << "prev ts start bars = " << prev_ts->start().bars << " beats = " << prev_ts->start().beats << " ticks = " << prev_ts->start().ticks << endl;
-
- /*tempo section (t) lies in the previous meter */
- double const ticks_at_ts = ((((t->start().bars - 1 ) * meter->divisions_per_bar()) + (t->start().beats - 1) ) * BBT_Time::ticks_per_beat) + t->start().ticks;
- double const ticks_at_prev_ts = ((((prev_ts->start().bars - 1) * meter->divisions_per_bar()) + (prev_ts->start().beats - 1)) * BBT_Time::ticks_per_beat) + prev_ts->start().ticks;
- double const ticks_relative_to_prev_ts = ticks_at_ts - ticks_at_prev_ts;
- /* assume (falsely) that the target tempo is constant */
- double length_estimate = (ticks_relative_to_prev_ts / BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate);
- if (prev_ts->type() == TempoSection::Type::Constant) {
- length_estimate = (ticks_relative_to_prev_ts / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
- }
- cerr<< "initial length extimate = " << length_estimate << " ticks_relative_to_prev_ts " << ticks_relative_to_prev_ts << endl;
- double const system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute());
- cerr << " system_precision_at_target_tempo = " << system_precision_at_target_tempo << endl;
- double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
-
- while (fabs (tick_error) >= system_precision_at_target_tempo) {
-
- double const actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate);
- tick_error = ticks_relative_to_prev_ts - actual_ticks;
- length_estimate += (tick_error / BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate);
- //cerr << "actual ticks = " << actual_ticks << endl;
-
- //cerr << "tick error = " << tick_error << endl;
- }
- cerr << "setting t frame to " << length_estimate + prev_ts->frame() << " tick error = " << tick_error << endl;
- t->set_frame (length_estimate + prev_ts->frame());
-
- if (m->start() < t->start() && m->start() == prev_ts->start()) {
- m->set_frame (prev_ts->frame());
- } else if (m->start() < t->start() && m->start() > prev_ts->start()) {
- cerr << "recompute map - setting meter frame to " << prev_ts->frame_at_tick ((first_tick_in_new_meter - ticks_at_prev_ts), t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate) << " ticks = " << first_tick_in_new_meter - ticks_at_prev_ts << endl;
-
- m->set_frame (prev_ts->frame_at_tick ((first_tick_in_new_meter - ticks_at_prev_ts), t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate));
- }
- }
- prev_ts = t;
- }
- }
- }
- meter = m;
+ /* we can do this beacuse we have the tempo section frames set */
+ if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
+ meter->set_frame (frame_at_tick (meter->beat() * BBT_Time::ticks_per_beat));
}
}
}
return m;
}
-
+/* XX meters only */
TempoMetric
TempoMap::metric_at (BBT_Time bbt) const
{
*/
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+ MeterSection* mw;
+ if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
+ BBT_Time section_start (mw->bbt());
- BBT_Time section_start ((*i)->start());
+ if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
+ break;
+ }
- if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
- break;
+ m.set_metric (*i);
}
-
- m.set_metric (*i);
}
return m;
warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
return;
}
- bbt = beats_to_bbt (beat_at_frame (frame));
+ bbt = beats_to_bbt_unlocked (beat_at_frame (frame));
}
-int32_t
-TempoMap::bars_in_meter_section (MeterSection* ms) const
+double
+TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
{
- /* YOU MUST HAVE THE READ LOCK */
- Metrics::const_iterator i;
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ return bbt_to_beats_unlocked (bbt);
+}
- MeterSection* next_ms = 0;
- const MeterSection* prev_ms = &first_meter();
+double
+TempoMap::bbt_to_beats_unlocked (Timecode::BBT_Time bbt)
+{
+ /* CALLER HOLDS READ LOCK */
+
+ double accumulated_beats = 0.0;
+ double accumulated_bars = 0.0;
+ MeterSection* prev_ms = 0;
+
+ Metrics::const_iterator i;
for (i = metrics.begin(); i != metrics.end(); ++i) {
MeterSection* m;
if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
- if (ms->frame() < m->frame()) {
- next_ms = m;
+ double bars_to_m = 0.0;
+ if (prev_ms) {
+ bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
+ }
+ if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
break;
}
+ if (prev_ms) {
+ accumulated_beats += m->beat() - prev_ms->beat();
+ accumulated_bars += bars_to_m;
+ }
prev_ms = m;
}
}
- if (next_ms) {
- double ticks_at_next = tick_at_frame (next_ms->frame());
- double ticks_at_prev = tick_at_frame (prev_ms->frame());
- double ticks_in_meter = ticks_at_next - ticks_at_prev;
- return (int32_t) floor ((ticks_in_meter / BBT_Time::ticks_per_beat) / prev_ms->note_divisor());
- }
- return -1;
+ double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
+ double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
+ double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
+ return ret;
}
Timecode::BBT_Time
TempoMap::beats_to_bbt (double beats)
{
- /* CALLER HOLDS READ LOCK */
- BBT_Time ret;
- MeterSection* prev_ms = &first_meter();
-
- framecnt_t frame = frame_at_beat (beats);
- uint32_t cnt = 0;
- /* XX most of this is utter crap */
- if (n_meters() == 1) {
- uint32_t bars = (uint32_t) floor (beats / prev_ms->note_divisor());
- double remaining_beats = beats - (bars * prev_ms->note_divisor());
- double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
-
- ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
- ret.beats = (uint32_t) floor (remaining_beats);
- ret.bars = bars;
-
- /* 0 0 0 to 1 1 0 - based mapping*/
- ++ret.bars;
- ++ret.beats;
-
- if (ret.ticks >= BBT_Time::ticks_per_beat) {
- ++ret.beats;
- ret.ticks -= BBT_Time::ticks_per_beat;
- }
-
- if (ret.beats > prev_ms->note_divisor()) {
- ++ret.bars;
- ret.beats = 1;
- }
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ return beats_to_bbt_unlocked (beats);
+}
- return ret;
- }
+Timecode::BBT_Time
+TempoMap::beats_to_bbt_unlocked (double beats)
+{
+ /* CALLER HOLDS READ LOCK */
- uint32_t first_beat_in_meter = 0;
+ MeterSection* prev_ms = 0;
uint32_t accumulated_bars = 0;
+
Metrics::const_iterator i;
for (i = metrics.begin(); i != metrics.end(); ++i) {
MeterSection* m = 0;
if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
- first_beat_in_meter = beat_at_frame (m->frame());
- if (beats < first_beat_in_meter) {
+ if (beats < m->beat()) {
/* this is the meter after the one our beat is on*/
break;
}
- int32_t const bars_in_ms = bars_in_meter_section (m);
- if (bars_in_ms > 0) {
- accumulated_bars += bars_in_ms;
+ if (prev_ms) {
+ /* we need a whole number of bars. */
+ accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
}
prev_ms = m;
- ++cnt;
- }
- }
- //cerr << "beats to bbr with beats = " << beats << " first_beat_in_meter = " << first_beat_in_meter << " accumulated_bars = " << accumulated_bars << endl;
-
- if (beats > first_beat_in_meter) {
- /* prev_ms is the relevant one here */
-
- /* now get the ticks at frame */
- double ticks_at_frame = tick_at_frame (frame);
-
- /* find the number of ticks at the beginning of the meter section (bar 1)*/
- double ticks_at_ms = tick_at_frame (prev_ms->frame());
-
- double beats_used_by_ms = (ticks_at_frame - ticks_at_ms) / BBT_Time::ticks_per_beat;
-
- uint32_t bars = (uint32_t) floor (beats_used_by_ms / prev_ms->note_divisor());
- double remaining_beats = beats_used_by_ms - (bars * prev_ms->note_divisor());
- double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
-
- ret.bars = bars + accumulated_bars;
- ret.beats = (uint32_t) floor (remaining_beats);
- ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
-
- /* now ensure we srtart at 1 1 0 */
- ++ret.bars;
- ++ret.beats;
- //cerr << "part 1 ret bars = " << ret.bars << " ret beats = " << ret.beats << " ret ticks = " << ret.ticks << endl;
- if (ret.ticks >= BBT_Time::ticks_per_beat) {
- ++ret.beats;
- ret.ticks -= BBT_Time::ticks_per_beat;
- }
-
- if (ret.beats > prev_ms->note_divisor()) {
- ++ret.bars;
- ret.beats = 1;
}
-
- return ret;
}
- /* find the number of ticks at the beginning of the meter section (bar 1)*/
- double ticks_at_ms = tick_at_frame (prev_ms->frame());
-
- /* now get the ticks at frame */
- double ticks_at_frame = tick_at_frame (frame);
-
- double ticks_within_ms = ticks_at_frame - ticks_at_ms;
+ double const beats_in_ms = beats - prev_ms->beat();
+ uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
+ uint32_t const total_bars = bars_in_ms + accumulated_bars;
+ double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
+ double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
- ret.bars = (uint32_t) floor (((ticks_within_ms / BBT_Time::ticks_per_beat) / prev_ms->note_divisor())) + accumulated_bars;
- uint32_t remaining_ticks = ticks_within_ms - (ret.bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat);
- ret.beats = (uint32_t) floor (remaining_ticks);
- remaining_ticks -= ret.beats * BBT_Time::ticks_per_beat;
+ BBT_Time ret;
- /* only round ticks */
ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
+ ret.beats = (uint32_t) floor (remaining_beats);
+ ret.bars = total_bars;
- /* now ensure we srtart at 1 1 0 */
+ /* 0 0 0 to 1 1 0 - based mapping*/
++ret.bars;
++ret.beats;
+
if (ret.ticks >= BBT_Time::ticks_per_beat) {
++ret.beats;
ret.ticks -= BBT_Time::ticks_per_beat;
}
- if (ret.beats > prev_ms->note_divisor()) {
+ if (ret.beats > prev_ms->divisions_per_bar()) {
++ret.bars;
ret.beats = 1;
}
double
TempoMap::tick_at_frame (framecnt_t frame) const
{
- Glib::Threads::RWLock::ReaderLock lm (lock);
+ /* HOLD (at least) THE READER LOCK */
Metrics::const_iterator i;
- const TempoSection* prev_ts = &first_tempo();
+ TempoSection* prev_ts = 0;
double accumulated_ticks = 0.0;
- uint32_t cnt = 0;
for (i = metrics.begin(); i != metrics.end(); ++i) {
TempoSection* t;
if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
- if (frame < t->frame()) {
+ if ((prev_ts) && frame < t->frame()) {
/*the previous ts is the one containing the frame */
- framepos_t time = frame - prev_ts->frame();
- framepos_t last_frame = t->frame() - prev_ts->frame();
- double last_beats_per_minute = t->beats_per_minute();
+ framepos_t const time = frame - prev_ts->frame();
+ framepos_t const last_frame = t->frame() - prev_ts->frame();
+ double const last_beats_per_minute = t->beats_per_minute();
return prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate) + accumulated_ticks;
}
- if (cnt > 0 && t->frame() > prev_ts->frame()) {
- framepos_t time = t->frame() - prev_ts->frame();
- framepos_t last_frame = t->frame() - prev_ts->frame();
- double last_beats_per_minute = t->beats_per_minute();
- accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate);
+ if (prev_ts && t->frame() > prev_ts->frame()) {
+ accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
}
prev_ts = t;
- ++cnt;
}
}
- /* treated s linear for this ts */
- framecnt_t frames_in_section = frame - prev_ts->frame();
- double ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
+ /* treated as constant for this ts */
+ framecnt_t const frames_in_section = frame - prev_ts->frame();
+ double const ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
return ticks_in_section + accumulated_ticks;
double accumulated_ticks = 0.0;
double accumulated_ticks_to_prev = 0.0;
-
- const TempoSection* prev_ts = &first_tempo();
- uint32_t cnt = 0;
+ const TempoSection* prev_ts = 0;
Metrics::const_iterator i;
TempoSection* t;
if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
- if (cnt > 0 && t->frame() > prev_ts->frame()) {
- framepos_t time = t->frame() - prev_ts->frame();
- framepos_t last_time = t->frame() - prev_ts->frame();
- double last_beats_per_minute = t->beats_per_minute();
- accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
+ if (prev_ts && t->frame() > prev_ts->frame()) {
+ accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
}
- if (tick < accumulated_ticks) {
+ if (prev_ts && tick < accumulated_ticks) {
/* prev_ts is the one affecting us. */
- double ticks_in_section = tick - accumulated_ticks_to_prev;
- framepos_t section_start = prev_ts->frame();
- framepos_t last_time = t->frame() - prev_ts->frame();
- double last_beats_per_minute = t->beats_per_minute();
- return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start;
+ double const ticks_in_section = tick - accumulated_ticks_to_prev;
+ framepos_t const last_time = t->frame() - prev_ts->frame();
+ double const last_beats_per_minute = t->beats_per_minute();
+
+ return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + prev_ts->frame();
}
accumulated_ticks_to_prev = accumulated_ticks;
-
prev_ts = t;
- ++cnt;
}
}
- double ticks_in_section = tick - accumulated_ticks_to_prev;
- double dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate);
- framecnt_t ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
+ /* must be treated as constant, irrespective of _type */
+ double const ticks_in_section = tick - accumulated_ticks_to_prev;
+ double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
+
+ framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
return ret;
}
if (bbt.beats < 1) {
throw std::logic_error ("beats are counted from one");
}
-
Glib::Threads::RWLock::ReaderLock lm (lock);
- Metrics::const_iterator i;
- uint32_t accumulated_bars = 0;
-
- MeterSection* prev_ms = &first_meter();
-
- for (i = metrics.begin(); i != metrics.end(); ++i) {
- MeterSection* m;
- if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
- int32_t const bims = bars_in_meter_section (m);
-
- if (bims < 0 || bbt.bars <= (accumulated_bars + bims)) {
- break;
- }
- if (bims > 0 ) {
- accumulated_bars += bims;
- }
- prev_ms = m;
- }
- }
-
- uint32_t remaining_bars = bbt.bars - accumulated_bars - 1; // back to zero - based bars
- double const ticks_within_prev_taken_by_remaining_bars = remaining_bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat;
- double const ticks_after_space_used_by_bars = ((bbt.beats - 1) * BBT_Time::ticks_per_beat) + bbt.ticks; // back to zero - based beats
- double const ticks_target = ticks_within_prev_taken_by_remaining_bars + ticks_after_space_used_by_bars;
-
- TempoSection* prev_ts = &first_tempo();
- double accumulated_ticks = 0.0;
- double accumulated_ticks_to_prev = 0.0;
-
- uint32_t cnt = 0;
-
- for (i = metrics.begin(); i != metrics.end(); ++i) {
- TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
- if (t->frame() < prev_ms->frame()) {
- continue;
- }
+ framepos_t const ret = frame_at_beat (bbt_to_beats_unlocked (bbt));
- if (cnt > 0 && t->frame() > prev_ts->frame()) {
- /*find the number of ticke in this section */
- framepos_t const time = t->frame() - prev_ts->frame();
- framepos_t const last_time = t->frame() - prev_ts->frame();
- double const last_beats_per_minute = t->beats_per_minute();
- accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
- }
-
- if (ticks_target < accumulated_ticks) {
- double const ticks_in_section = ticks_target - accumulated_ticks_to_prev;
- framepos_t const section_start_time = prev_ts->frame();
- framepos_t const last_time = t->frame() - prev_ts->frame();
- double const last_beats_per_minute = t->beats_per_minute();
- framepos_t const ret = prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start_time;
- return ret;
- }
- accumulated_ticks_to_prev = accumulated_ticks;
- prev_ts = t;
- ++cnt;
- }
- }
-
- /*treat this ts as constant tempo */
- double const ticks_in_this_ts = ticks_target - accumulated_ticks_to_prev;
- double const dtime = (ticks_in_this_ts / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate);
- framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
return ret;
}
double const beat_at_framepos = beat_at_frame (frame);
- BBT_Time bbt (beats_to_bbt (beat_at_framepos));
+ BBT_Time bbt (beats_to_bbt_unlocked (beat_at_framepos));
switch (type) {
case Bar:
framecnt_t const pos = frame_at_beat (cnt);
MeterSection const meter = meter_section_at (pos);
Tempo const tempo = tempo_at (pos);
- BBT_Time const bbt = beats_to_bbt ((double) cnt);
+ BBT_Time const bbt = beats_to_bbt_unlocked ((double) cnt);
points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
++cnt;
}
}
-TempoSection*
-TempoMap::tempo_section_after (framepos_t frame) const
-{
- Glib::Threads::RWLock::ReaderLock lm (lock);
-
- Metrics::const_iterator i;
- TempoSection* next = 0;
-
- for (i = metrics.begin(); i != metrics.end(); ++i) {
- TempoSection* t;
-
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
-
- if ((*i)->frame() > frame) {
- next = t;
- break;
- }
- }
- }
-
- return next;
-}
-
-
const TempoSection&
TempoMap::tempo_section_at (framepos_t frame) const
{
Glib::Threads::RWLock::ReaderLock lm (lock);
const TempoSection* ts_at = &tempo_section_at (frame);
- const TempoSection* ts_after = tempo_section_after (frame);
+ const TempoSection* ts_after = 0;
+ Metrics::const_iterator i;
+
+ for (i = metrics.begin(); i != metrics.end(); ++i) {
+ TempoSection* t;
+
+ if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+
+ if ((*i)->frame() > frame) {
+ ts_after = t;
+ break;
+ }
+ }
+ }
if (ts_after) {
return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), ts_after->beats_per_minute(), ts_after->frame(), _frame_rate));
if (ts->bar_offset() < 0.0) {
if (last_meter) {
- ts->update_bar_offset_from_bbt (*last_meter);
+ //ts->update_bar_offset_from_bbt (*last_meter);
}
}
}
MetricSectionSorter cmp;
metrics.sort (cmp);
}
+ /* check for legacy sessions where bbt was the base musical unit for tempo */
+ for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
+ MeterSection* prev_ms;
+ TempoSection* prev_ts;
+ if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
+ if (prev_ms->beat() < 0.0) {
+ /*XX we cannot possibly make this work??. */
+ pair<double, BBT_Time> start = make_pair (((prev_ms->bbt().bars - 1) * 4.0) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt());
+ prev_ms->set_beat (start);
+ }
+ } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
+ if (prev_ts->beat() < 0.0) {
+ double const start = ((prev_ts->legacy_bbt().bars - 1) * 4.0) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
+ prev_ts->set_beat (start);
+ }
+ }
+ }
/* 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()) {
- cerr << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
- error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
+ MeterSection* ms;
+ MeterSection* prev_ms;
+ TempoSection* ts;
+ TempoSection* prev_ts;
+ if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
+ if (prev_ms->beat() == ms->beat()) {
+ cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
+ error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << 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;
+ } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
+ if (prev_ts->beat() == ts->beat()) {
+ cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
+ error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
return -1;
}
}
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
- o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (movable? "
+ o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->beat() << " frame= " << t->frame() << " (movable? "
<< t->movable() << ')' << endl;
} else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
- o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
+ o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
<< " (movable? " << m->movable() << ')' << endl;
}
}
for (i = metrics.begin(); i != metrics.end(); ++i) {
BBT_Time bbt;
- TempoMetric metric (*meter, *tempo);
-
+ //TempoMetric metric (*meter, *tempo);
+ MeterSection* ms = const_cast<MeterSection*>(meter);
+ TempoSection* ts = const_cast<TempoSection*>(tempo);
if (prev) {
- metric.set_start (prev->start());
- metric.set_frame (prev->frame());
+ if (ts){
+ if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
+ ts->set_beat (t->beat());
+ }
+ if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
+ ts->set_beat (m->beat());
+ }
+ ts->set_frame (prev->frame());
+
+ }
+ if (ms) {
+ if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
+ pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
+ ms->set_beat (start);
+ }
+ if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
+ pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_unlocked (t->beat()));
+ ms->set_beat (start);
+ }
+ ms->set_frame (prev->frame());
+ }
+
} else {
// metric will be at frames=0 bbt=1|1|0 by default
// which is correct for our purpose
}
- bbt_time ((*i)->frame(), bbt);
-
- // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
-
- if (first) {
- first = false;
- } else {
+ // cerr << bbt << endl;
- if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
- /* round up to next beat */
- bbt.beats += 1;
- }
+ if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+ t->set_beat (beat_at_frame (m->frame()));
+ tempo = t;
+ // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
+ } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
+ bbt_time (m->frame(), bbt);
- bbt.ticks = 0;
+ // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
- if (bbt.beats != 1) {
- /* round up to next bar */
- bbt.bars += 1;
- bbt.beats = 1;
- }
- }
+ if (first) {
+ first = false;
+ } else {
- // cerr << bbt << endl;
+ if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
+ /* round up to next beat */
+ bbt.beats += 1;
+ }
- (*i)->set_start (bbt);
+ bbt.ticks = 0;
- if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
- tempo = t;
- // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
- } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
+ if (bbt.beats != 1) {
+ /* round up to next bar */
+ bbt.bars += 1;
+ bbt.beats = 1;
+ }
+ }
+ pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
+ m->set_beat (start);
meter = m;
- // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
+ // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
} else {
fatal << _("programming error: unhandled MetricSection type") << endmsg;
abort(); /*NOTREACHED*/
std::ostream&
operator<< (std::ostream& o, const MetricSection& section) {
- o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
+ o << "MetricSection @ " << section.frame() << ' ';
const TempoSection* ts;
const MeterSection* ms;
if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
o << *((const Tempo*) ts);
} else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
- o << *((const Meter*) ms);
+ //o << *((const Meter*) ms);
}
return o;