#include <unistd.h>
#include <glibmm/threads.h>
+
+#include "pbd/enumwriter.h"
#include "pbd/xml++.h"
#include "evoral/Beats.hpp"
+
#include "ardour/debug.h"
#include "ardour/lmath.h"
#include "ardour/tempo.h"
: MetricSection (0.0), Tempo (TempoMap::default_tempo())
{
const XMLProperty *prop;
- BBT_Time start;
LocaleGuard lg;
-
- if ((prop = node.property ("start")) == 0) {
- error << _("MeterSection XML node has no \"start\" property") << endmsg;
- throw failed_constructor();
+ 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 {
+ warning << _("TempoSection XML node has no \"start\" property") << endmsg;
}
- 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;
- start = -1.0;
- } else if (sscanf (prop->value().c_str(), "%lf", &start) != 1 || start < 0.0) {
- error << _("TempoSection 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 << _("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;
}
if ((prop = node.property ("tempo-type")) == 0) {
- _type = Type::Constant;
+ _type = Constant;
} else {
- if (strstr(prop->value().c_str(),"Constant")) {
- _type = Type::Constant;
- } else {
- _type = Type::Ramp;
- }
+ _type = Type (string_2_enum (prop->value(), _type));
}
}
LocaleGuard lg;
snprintf (buf, sizeof (buf), "%f", beat());
- root->add_property ("start", buf);
+ 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);
// root->add_property ("bar-offset", buf);
snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
root->add_property ("movable", buf);
-
- snprintf (buf, sizeof (buf), "%s", _type == Constant?"Constant":"Ramp");
- root->add_property ("tempo-type", buf);
+ root->add_property ("tempo-type", enum_2_string (_type));
return *root;
}
TempoSection::update_bar_offset_from_bbt (const Meter& m)
{
- _bar_offset = (start() * BBT_Time::ticks_per_beat) /
+ _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
void
TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
{
- double new_start;
+ double new_beat;
if (_bar_offset < 0.0) {
/* not set yet */
return;
}
- new_start = start();
+ new_beat = beat();
double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
- new_start = ticks / BBT_Time::ticks_per_beat;
+ new_beat = ticks / BBT_Time::ticks_per_beat;
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, new_start));
+ _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
- set_start (new_start);
+ set_beat (new_beat);
}
/***********************************************************************/
double beat = 0.0;
pair<double, BBT_Time> start;
- if ((prop = node.property ("start")) == 0) {
- error << _("MeterSection XML node has no \"start\" property") << endmsg;
- throw failed_constructor();
- }
- if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+ 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*/
- beat = -1.0;
- } else if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
- error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
- throw failed_constructor();
+ &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;
}
+
+ 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 an illegal \"bbt\" value") << endmsg;
throw failed_constructor();
}
+
start.second = bbt;
- set_start (start);
+ set_beat (start);
/* beats-per-bar is old; divisions-per-bar is new */
bbt().beats,
bbt().ticks);
root->add_property ("bbt", buf);
- snprintf (buf, sizeof (buf), "%f", start());
- root->add_property ("start", 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();
}
};
start.beats = 1;
start.ticks = 0;
- TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Type::Ramp);
+ TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
t->set_movable (false);
if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
- pair<double, BBT_Time> corrected = make_pair (m->start(), m->bbt());
+ pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
corrected.second.beats = 1;
corrected.second.ticks = 0;
-
+ corrected.first = bbt_to_beats_locked (corrected.second);
warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
m->bbt(), corrected.second) << endmsg;
- m->set_start (corrected);
+ m->set_beat (corrected);
}
}
/* Tempo sections */
- if (tempo->start() == insert_tempo->start()) {
+ if (tempo->beat() == insert_tempo->beat()) {
if (!tempo->movable()) {
/* Meter Sections */
MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
- if (meter->start() == insert_meter->start()) {
+ if (meter->beat() == insert_meter->beat()) {
if (!meter->movable()) {
for (i = metrics.begin(); i != metrics.end(); ++i) {
MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
- if (meter && meter->start() > insert_meter->start()) {
+ if (meter && meter->beat() > insert_meter->beat()) {
break;
}
}
TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
if (tempo) {
- if (tempo->start() > insert_tempo->start()) {
+ if (tempo->beat() > insert_tempo->beat()) {
break;
}
}
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, double beat_where)
{
- TempoSection& first (first_tempo());
- if (ts.frame() != first.frame()) {
- {
- Glib::Threads::RWLock::WriterLock lm (lock);
- /* currently this is always done in audio time */
- if (0) {
- //if (ts.position_lock_style() == AudioTime) {
- /* MusicTime */
- ts.set_start (beat_where);
- MetricSectionSorter cmp;
- metrics.sort (cmp);
- } else {
- /*AudioTime*/
- ts.set_frame (frame);
+ {
+ Glib::Threads::RWLock::WriterLock lm (lock);
- MetricSectionFrameSorter fcmp;
- metrics.sort (fcmp);
+ /* 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);
- Metrics::const_iterator i;
- TempoSection* prev_ts = 0;
- TempoSection* next_ts = 0;
+ MetricSectionFrameSorter fcmp;
+ metrics.sort (fcmp);
- for (i = metrics.begin(); i != metrics.end(); ++i) {
- TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ Metrics::const_iterator i;
+ TempoSection* prev_ts = 0;
+ TempoSection* next_ts = 0;
- if (t->frame() >= frame) {
- break;
- }
+ for (i = metrics.begin(); i != metrics.end(); ++i) {
+ TempoSection* t;
+ if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
- prev_ts = t;
+ 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) {
+ 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 (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->start();
-
- if (next_ts) {
- if (next_ts->start() < 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 start beat of the moved tempo.
- in this case, we have to do some beat-based comparison TODO
- */
-
- ts.set_start (next_ts->start());
- } else if (prev_ts->start() > beats) {
- ts.set_start (prev_ts->start());
- } else {
- ts.set_start (beats);
- }
+ 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_start (beats);
+ ts.set_beat (beats);
}
- MetricSectionSorter cmp;
- metrics.sort (cmp);
+ } else {
+ ts.set_beat (beats);
}
+ MetricSectionSorter cmp;
+ metrics.sort (cmp);
}
-
- recompute_map (false);
}
+
+ recompute_map (false);
}
MetricPositionChanged (); // Emit Signal
}
void
-TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const double& start, const BBT_Time& where)
+TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
{
{
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, start, where, true);
+ add_meter_locked (meter, bbt_to_beats_locked (where), where, true);
} else {
/* cannot move the first meter section */
*static_cast<Meter*>(&first) = meter;
}
void
-TempoMap::add_meter (const Meter& meter, double start, BBT_Time where)
+TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
{
{
Glib::Threads::RWLock::WriterLock lm (lock);
- add_meter_locked (meter, start, where, true);
+ add_meter_locked (meter, beat, where, true);
}
}
void
-TempoMap::add_meter_locked (const Meter& meter, double start, 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 (start, 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
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;
- }
- }
-
- assert(tempo);
-
- /* assumes that the first meter & tempo are at frame zero */
- current_frame = 0;
- meter->set_frame (0);
- tempo->set_frame (0);
-
- /* assumes that the first meter & tempo are at 1|1|0 */
- current.bars = 1;
- current.beats = 1;
- current.ticks = 0;
-
- 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 */
-
Metrics::const_iterator i;
- Metrics::const_iterator mi;
-
- TempoSection* prev_ts = tempo;
-
- for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
- MeterSection* m = 0;
-
- if ((m = dynamic_cast<MeterSection*> (*mi)) != 0) {
- for (i = metrics.begin(); i != metrics.end(); ++i) {
- TempoSection* t;
+ TempoSection* prev_ts = 0;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ for (i = metrics.begin(); i != metrics.end(); ++i) {
+ TempoSection* t;
- if (t->start() > prev_ts->start()) {
+ if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
- /*tempo section (t) lies in the previous meter */
- double const beats_relative_to_prev_ts = t->start() - prev_ts->start();
- double const ticks_relative_to_prev_ts = beats_relative_to_prev_ts * BBT_Time::ticks_per_beat;
+ 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;
- /* 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;
+ /* 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;
- if (prev_ts->type() == TempoSection::Type::Constant) {
- length_estimate = beats_relative_to_prev_ts * prev_ts->frames_per_beat (_frame_rate);
- }
+ if (prev_ts->type() == TempoSection::Constant) {
+ length_estimate = beats_relative_to_prev_ts * prev_ts->frames_per_beat (_frame_rate);
+ }
- 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
+ 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
- while (fabs (tick_error) > system_precision_at_target_tempo) {
+ 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 * (t->ticks_per_minute() / _frame_rate);
- }
+ 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);
+ }
- t->set_frame (length_estimate + prev_ts->frame());
+ t->set_frame (length_estimate + prev_ts->frame());
+ }
+ prev_ts = t;
+ }
+ }
- double const meter_start_beats = m->start();
- if (meter_start_beats < t->start() && meter_start_beats == prev_ts->start()) {
+ Metrics::const_iterator mi;
+ MeterSection* meter = 0;
- m->set_frame (prev_ts->frame());
- } else if (meter_start_beats < t->start() && meter_start_beats > prev_ts->start()) {
- framepos_t new_frame = prev_ts->frame_at_beat (m->start() - prev_ts->start(),
- t->beats_per_minute(),
- t->frame() - prev_ts->frame(),
- _frame_rate) + prev_ts->frame();
- m->set_frame (new_frame);
- }
- }
- prev_ts = t;
- }
- }
- meter = m;
+ for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
+ /* 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));
}
}
}
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_locked (beat_at_frame (frame));
}
double
TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ return bbt_to_beats_locked (bbt);
+}
+
+double
+TempoMap::bbt_to_beats_locked (Timecode::BBT_Time bbt)
{
/* CALLER HOLDS READ LOCK */
if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
double bars_to_m = 0.0;
if (prev_ms) {
- bars_to_m = (m->start() - prev_ms->start()) / prev_ms->divisions_per_bar();
+ 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->start() - prev_ms->start();
+ accumulated_beats += m->beat() - prev_ms->beat();
accumulated_bars += bars_to_m;
}
prev_ms = m;
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.ticks / BBT_Time::ticks_per_beat);
-
+ 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)
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+ return beats_to_bbt_locked (beats);
+}
+
+Timecode::BBT_Time
+TempoMap::beats_to_bbt_locked (double beats)
{
/* CALLER HOLDS READ LOCK */
if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
- if (beats < m->start()) {
+ if (beats < m->beat()) {
/* this is the meter after the one our beat is on*/
break;
}
if (prev_ms) {
/* we need a whole number of bars. */
- accumulated_bars += ((m->start() - prev_ms->start()) + 1) / prev_ms->divisions_per_bar();
+ accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
}
prev_ms = m;
}
}
- double const beats_in_ms = beats - prev_ms->start();
+ 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
TempoMap::tick_at_frame (framecnt_t frame) const
{
+ /* HOLD (at least) THE READER LOCK */
Metrics::const_iterator i;
- const TempoSection* prev_ts = 0;
+ TempoSection* prev_ts = 0;
double accumulated_ticks = 0.0;
for (i = metrics.begin(); i != metrics.end(); ++i) {
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 (prev_ts && 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);
+ accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
}
prev_ts = t;
}
}
- /* 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;
if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
if (prev_ts && t->frame() > prev_ts->frame()) {
- 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);
+ accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
}
if (prev_ts && tick < accumulated_ticks) {
/* prev_ts is the one affecting us. */
double const ticks_in_section = tick - accumulated_ticks_to_prev;
- framepos_t const section_start = prev_ts->frame();
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) + section_start;
+ 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;
}
}
+ /* 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);
+ 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;
}
Glib::Threads::RWLock::ReaderLock lm (lock);
- framepos_t const ret = frame_at_beat (bbt_to_beats (bbt));
+ framepos_t const ret = frame_at_beat (bbt_to_beats_locked (bbt));
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_locked (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_locked ((double) cnt);
points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
++cnt;
MeterSection* prev_ms;
TempoSection* prev_ts;
if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
- if (prev_ms->start() < 0.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_start (start);
+ prev_ms->set_beat (start);
}
} else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
- if (prev_ts->start() < 0.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_start (start);
+ prev_ts->set_beat (start);
}
}
TempoSection* ts;
TempoSection* prev_ts;
if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
- if (prev_ms->start() == ms->start()) {
- cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->start()) << endmsg;
- error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->start()) << endmsg;
+ 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 ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
- if (prev_ts->start() == ts->start()) {
- cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->start()) << endmsg;
- error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->start()) << endmsg;
+ 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->bbt() << " frame= " << m->frame()
if (prev) {
if (ts){
if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
- ts->set_start (t->start());
+ ts->set_beat (t->beat());
}
if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
- ts->set_start (m->start());
+ 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->start(), m->bbt());
- ms->set_start (start);
+ 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->start(), beats_to_bbt (t->start()));
- ms->set_start (start);
+ pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (t->beat()));
+ ms->set_beat (start);
}
ms->set_frame (prev->frame());
}
// cerr << bbt << endl;
if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
- t->set_start (beat_at_frame (m->frame()));
+ t->set_beat (beat_at_frame (m->frame()));
tempo = t;
- // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
+ // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
} else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
bbt_time (m->frame(), bbt);
}
}
pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
- m->set_start (start);
+ 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*/