#include "ardour/lmath.h"
#include "ardour/tempo.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
#include <locale.h>
using namespace std;
const string TempoSection::xml_state_node_name = "Tempo";
TempoSection::TempoSection (const XMLNode& node)
- : MetricSection (0.0, 0, MusicTime)
+ : MetricSection (0.0, 0, MusicTime, true)
, Tempo (TempoMap::default_tempo())
, _c_func (0.0)
, _active (true)
char buf[256];
LocaleGuard lg;
- snprintf (buf, sizeof (buf), "%f", pulse());
+ snprintf (buf, sizeof (buf), "%lf", pulse());
root->add_property ("pulse", buf);
snprintf (buf, sizeof (buf), "%li", frame());
root->add_property ("frame", buf);
- snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
+ snprintf (buf, sizeof (buf), "%lf", _beats_per_minute);
root->add_property ("beats-per-minute", buf);
- snprintf (buf, sizeof (buf), "%f", _note_type);
+ snprintf (buf, sizeof (buf), "%lf", _note_type);
root->add_property ("note-type", buf);
snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
root->add_property ("movable", buf);
const string MeterSection::xml_state_node_name = "Meter";
MeterSection::MeterSection (const XMLNode& node)
- : MetricSection (0.0, 0, MusicTime), Meter (TempoMap::default_meter())
+ : MetricSection (0.0, 0, MusicTime, false), Meter (TempoMap::default_meter())
{
XMLProperty const * prop;
LocaleGuard lg;
root->add_property ("bbt", buf);
snprintf (buf, sizeof (buf), "%lf", beat());
root->add_property ("beat", buf);
- snprintf (buf, sizeof (buf), "%f", _note_type);
+ snprintf (buf, sizeof (buf), "%lf", _note_type);
root->add_property ("note-type", buf);
snprintf (buf, sizeof (buf), "%li", frame());
root->add_property ("frame", buf);
root->add_property ("lock-style", enum_2_string (position_lock_style()));
- snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
+ snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
root->add_property ("divisions-per-bar", buf);
snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
root->add_property ("movable", buf);
/*
Tempo Map Overview
+ The Shaggs - Things I Wonder
+ https://www.youtube.com/watch?v=9wQK6zMJOoQ
+
Tempo is the rate of the musical pulse.
Meters divide the pulses into measures and beats.
TempoSections - provide pulses in the form of beats_per_minute() and note_type() where note_type is the division of a whole pulse,
and beats_per_minute is the number of note_types in one minute (unlike what its name suggests).
- Note that Tempo::beats_per_minute() has nothing to do with musical beats.
+ Note that Tempo::beats_per_minute() has nothing to do with musical beats. It has been left that way because
+ a shorter one hasn't been found yet (pulse_divisions_per_minute()?).
MeterSecions - divide pulses into measures (via divisions_per_bar) and beats (via note_divisor).
Because ramped MusicTime and AudioTime tempos can interact with each other,
reordering is frequent. Care must be taken to keep _metrics in a solved state.
Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
+
+ Music and Audio
+
+ Music and audio-locked objects may seem interchangeable on the surface, but when translating
+ between audio samples and beats, keep in mind that a sample is only a quantised approximation
+ of the actual time (in minutes) of a beat.
+ Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
+ mean that this frame is the actual location in time of 1|3|0.
+
+ You cannot use a frame measurement to determine beat distance except under special circumstances
+ (e.g. where the user has requested that a beat lie on a SMPTE frame or if the tempo is known to be constant over the duration).
+
+ This means is that a user operating on a musical grid must supply the desired beat position and/or current beat quantization in order for the
+ sample space the user is operating at to be translated correctly to the object.
+
+ The current approach is to interpret the supplied frame using the grid division the user has currently selected.
+ If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
+ the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
+
+ tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
+
+ When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
+ The result is rounded to audio frames.
+ When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
+
+ So :
+ frame_at_beat (beat_at_frame (frame)) == frame
+ but :
+ beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
+
+ Doing the second one will result in a beat distance error of up to 0.5 audio samples.
+ So instead work in pulses and/or beats and only use beat position to caclulate frame position (e.g. after tempo change).
+ For audio-locked objects, use frame position to calculate beat position.
+
+ The above pointless example would then do:
+ beat_at_pulse (pulse_at_beat (beat)) to avoid rounding.
+
*/
struct MetricSectionSorter {
bool operator() (const MetricSection* a, const MetricSection* b) {
bool
TempoMap::remove_meter_locked (const MeterSection& meter)
{
- Metrics::iterator i;
- for (i = _metrics.begin(); i != _metrics.end(); ++i) {
- TempoSection* t = 0;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
- if (meter.frame() == (*i)->frame()) {
- if (t->locked_to_meter()) {
+ if (meter.position_lock_style() == AudioTime) {
+ /* remove meter-locked tempo */
+ for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
+ TempoSection* t = 0;
+ if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
delete (*i);
_metrics.erase (i);
break;
}
}
- for (i = _metrics.begin(); i != _metrics.end(); ++i) {
+ for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
if (dynamic_cast<MeterSection*> (*i) != 0) {
if (meter.frame() == (*i)->frame()) {
if ((*i)->movable()) {
{
TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
t->set_locked_to_meter (locked_to_meter);
+ bool solved = false;
do_insert (t);
if (recompute) {
if (pls == AudioTime) {
- solve_map_frame (_metrics, t, t->frame());
+ solved = solve_map_frame (_metrics, t, t->frame());
} else {
- solve_map_pulse (_metrics, t, t->pulse());
+ solved = solve_map_pulse (_metrics, t, t->pulse());
}
recompute_meters (_metrics);
}
+ if (!solved && recompute) {
+ warning << "Adding tempo may have left the tempo map unsolved." << endmsg;
+ recompute_map (_metrics);
+ }
+
return t;
}
}
MeterSection*
-TempoMap::add_meter_locked (const Meter& meter, double beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
+TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
{
const MeterSection& prev_m = meter_section_at_frame_locked (_metrics, frame - 1);
const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
+ TempoSection* mlt = 0;
if (pls == AudioTime) {
/* add meter-locked tempo */
- add_tempo_locked (tempo_at_frame_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true);
+ mlt = add_tempo_locked (tempo_at_frame_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true);
+
+ if (!mlt) {
+ return 0;
+ }
+
}
MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
+ bool solved = false;
do_insert (new_meter);
if (recompute) {
if (pls == AudioTime) {
- solve_map_frame (_metrics, new_meter, frame);
+ solved = solve_map_frame (_metrics, new_meter, frame);
} else {
- solve_map_bbt (_metrics, new_meter, where);
+ solved = solve_map_bbt (_metrics, new_meter, where);
+ /* required due to resetting the pulse of meter-locked tempi above.
+ Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
+ but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
+ */
+ recompute_map (_metrics);
}
}
+ if (!solved && recompute) {
+ /* if this has failed to solve, there is little we can do other than to ensure that
+ the new map is recalculated.
+ */
+ warning << "Adding meter may have left the tempo map unsolved." << endmsg;
+ recompute_map (_metrics);
+ }
+
return new_meter;
}
return *t;
}
void
-TempoMap::recompute_tempos (Metrics& metrics)
+TempoMap::recompute_tempi (Metrics& metrics)
{
TempoSection* prev_t = 0;
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
if (!t->active()) {
continue;
}
MeterSection* prev_m = 0;
for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
- if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
+ if (!(*mi)->is_tempo()) {
+ meter = static_cast<MeterSection*> (*mi);
if (meter->position_lock_style() == AudioTime) {
double pulse = 0.0;
pair<double, BBT_Time> b_bbt;
TempoSection* meter_locked_tempo = 0;
for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
+ if ((*ii)->is_tempo()) {
+ t = static_cast<TempoSection*> (*ii);
if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
meter_locked_tempo = t;
break;
return;
}
- recompute_tempos (metrics);
+ recompute_tempi (metrics);
recompute_meters (metrics);
}
for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
MeterSection* mw;
- if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
+ if (!(*i)->is_tempo()) {
+ mw = static_cast<MeterSection*> (*i);
BBT_Time section_start (mw->bbt());
if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
MeterSection* next_m = 0;
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
- MeterSection* m;
- if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
- if (prev_m && m->frame() > frame) {
- next_m = m;
+ if (!(*i)->is_tempo()) {
+ if (prev_m && (*i)->frame() > frame) {
+ next_m = static_cast<MeterSection*> (*i);
break;
}
- prev_m = m;
+ prev_m = static_cast<MeterSection*> (*i);
}
}
if (frame < prev_m->frame()) {
return beat;
}
-framecnt_t
+framepos_t
TempoMap::frame_at_beat (const double& beat) const
{
Glib::Threads::RWLock::ReaderLock lm (lock);
return frame_at_beat_locked (_metrics, beat);
}
-/* meter section based */
-framecnt_t
+/* meter & tempo section based */
+framepos_t
TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
{
- const TempoSection* prev_t = &tempo_section_at_beat_locked (metrics, beat);
- const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
+ MeterSection* prev_m = 0;
+ TempoSection* prev_t = 0;
+
+ MeterSection* m;
+
+ for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection*> (*i);
+ if (prev_m && m->beat() > beat) {
+ break;
+ }
+ prev_m = m;
+ }
+ }
+
+ TempoSection* t;
+
+ for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
+ if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
+ break;
+ }
+ prev_t = t;
+ }
+
+ }
return prev_t->frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
}
{
TempoSection* prev_t = 0;
+ TempoSection* t;
+
for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
- TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
if (!t->active()) {
continue;
}
for (i = _metrics.begin(); i != _metrics.end(); ++i) {
TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
if (!t->active()) {
continue;
const double prev_t_ppm = prev_t->beats_per_minute() / prev_t->note_type();
if ((t_ppm > tempo_ppm && prev_t_ppm < tempo_ppm) || (t_ppm < tempo_ppm && prev_t_ppm > tempo_ppm)) {
- const framepos_t ret_frame = prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate);
- return ret_frame;
+ return prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate);
}
}
prev_t = t;
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
MeterSection* m;
- if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection*> (*i);
if (prev_m && m->pulse() > pulse) {
if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
break;
}
double
-TempoMap::pulse_at_frame (const framecnt_t& frame) const
+TempoMap::pulse_at_frame (const framepos_t& frame) const
{
Glib::Threads::RWLock::ReaderLock lm (lock);
return pulse_at_frame_locked (_metrics, frame);
/* tempo section based */
double
-TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
+TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
{
/* HOLD (at least) THE READER LOCK */
TempoSection* prev_t = 0;
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
if (!t->active()) {
continue;
}
return pulses_in_section + prev_t->pulse();
}
-framecnt_t
+framepos_t
TempoMap::frame_at_pulse (const double& pulse) const
{
Glib::Threads::RWLock::ReaderLock lm (lock);
}
/* tempo section based */
-framecnt_t
+framepos_t
TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
{
/* HOLD THE READER LOCK */
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
if (!t->active()) {
continue;
}
}
}
/* must be treated as constant, irrespective of _type */
- double const pulses_in_section = pulse - prev_t->pulse();
- double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
-
- framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
+ double const dtime = (pulse - prev_t->pulse()) * prev_t->frames_per_pulse (_frame_rate);
- return ret;
+ return (framecnt_t) floor (dtime) + prev_t->frame();
}
double
/* because audio-locked meters have 'fake' integral beats,
there is no pulse offset here.
*/
+ MeterSection* m;
+
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
- MeterSection* m;
- if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection*> (*i);
if (prev_m) {
const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
MeterSection* prev_m = 0;
const double beats = max (0.0, b);
- for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
- MeterSection* m = 0;
+ MeterSection* m = 0;
- if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+ for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection*> (*i);
if (prev_m) {
if (m->beat() > beats) {
/* this is the meter after the one our beat is on*/
/* because audio-locked meters have 'fake' integral beats,
there is no pulse offset here.
*/
+ MeterSection* m;
+
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
- MeterSection* m;
- if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection*> (*i);
if (prev_m) {
if (m->bbt().bars > bbt.bars) {
break;
{
MeterSection* prev_m = 0;
+ MeterSection* m = 0;
+
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
- MeterSection* m = 0;
- if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection*> (*i);
if (prev_m) {
double const pulses_to_m = m->pulse() - prev_m->pulse();
warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
return bbt;
}
- const double beat = beat_at_frame_locked (metrics, frame);
- return bbt_at_beat_locked (metrics, beat);
+ const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
+ MeterSection* prev_m = 0;
+ MeterSection* next_m = 0;
+
+ MeterSection* m;
+
+ for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection*> (*i);
+ if (prev_m && m->frame() > frame) {
+ next_m = m;
+ break;
+ }
+ prev_m = m;
+ }
+ }
+
+ double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
+
+ /* handle frame before first meter */
+ if (frame < prev_m->frame()) {
+ beat = 0.0;
+ }
+ /* audio locked meters fake their beat */
+ if (next_m && next_m->beat() < beat) {
+ beat = next_m->beat();
+ }
+
+ beat = max (0.0, beat);
+
+ const double beats_in_ms = beat - prev_m->beat();
+ const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
+ const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
+ const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
+ const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
+
+ BBT_Time ret;
+
+ ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
+ ret.beats = (uint32_t) floor (remaining_beats);
+ ret.bars = total_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_m->divisions_per_bar() + 1) {
+ ++ret.bars;
+ ret.beats = 1;
+ }
+
+ return ret;
}
framepos_t
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
TempoSection* t;
MeterSection* m;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
if (!t->active()) {
continue;
}
prev_t = t;
}
- if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection*> (*i);
if (prev_m && m->position_lock_style() == AudioTime) {
- TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (metrics, m->frame() - 1));
- const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
- const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
-
- if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
+ const TempoSection* t = &tempo_section_at_frame_locked (metrics, m->frame() - 1);
+ const framepos_t nascent_m_frame = t->frame_at_pulse (m->pulse(), _frame_rate);
+ /* Here we check that a preceding section of music doesn't overlap a subsequent one.
+ It is complicated by the fact that audio locked meters represent a discontinuity in the pulse
+ (they place an exact pulse at a particular time expressed only in frames).
+ This has the effect of shifting the calculated frame at the meter pulse (wrt the previous section of music)
+ away from its actual frame (which is now the frame location of the exact pulse).
+ This can result in the calculated frame (from the previous musical section)
+ differing from the exact frame by one sample.
+ Allow for that.
+ */
+ if (t && (nascent_m_frame > m->frame() + 1 || nascent_m_frame < 0)) {
return false;
}
}
{
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
if (!t->movable()) {
t->set_active (true);
continue;
/* can't move a tempo before the first meter */
for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
MeterSection* m;
- if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection*> (*i);
if (!m->movable()) {
first_m_frame = m->frame();
break;
for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
if (!t->active()) {
continue;
}
#if (0)
- recompute_tempos (imaginary);
+ recompute_tempi (imaginary);
if (check_solved (imaginary)) {
return true;
MetricSectionFrameSorter fcmp;
imaginary.sort (fcmp);
- recompute_tempos (imaginary);
+ recompute_tempi (imaginary);
if (check_solved (imaginary)) {
return true;
for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
if (!t->active()) {
continue;
}
}
#if (0)
- recompute_tempos (imaginary);
+ recompute_tempi (imaginary);
if (check_solved (imaginary)) {
return true;
MetricSectionSorter cmp;
imaginary.sort (cmp);
- recompute_tempos (imaginary);
+ recompute_tempi (imaginary);
/* Reordering
* XX need a restriction here, but only for this case,
* as audio locked tempos don't interact in the same way.
for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
+ if ((*ii)->is_tempo()) {
+ t = static_cast<TempoSection*> (*ii);
if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
meter_locked_tempo = t;
break;
for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
MeterSection* m;
- if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection*> (*i);
if (m == section){
if (prev_m && section->movable()) {
const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
/* disallow setting section to an existing meter's bbt */
for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
MeterSection* m;
- if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection*> (*i);
if (m != section && m->bbt().bars == when.bars) {
return false;
}
for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
MeterSection* m;
- if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection*> (*i);
pair<double, BBT_Time> b_bbt;
double new_pulse = 0.0;
for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
+ if ((*ii)->is_tempo()) {
+ t = static_cast<TempoSection*> (*ii);
if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
meter_locked_tempo = t;
break;
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
TempoSection* t;
MeterSection* m;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
if (t == section) {
ret = new TempoSection (*t);
copy.push_back (ret);
TempoSection* cp = new TempoSection (*t);
copy.push_back (cp);
}
- if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection *> (*i);
MeterSection* cp = new MeterSection (*m);
copy.push_back (cp);
}
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
TempoSection* t;
MeterSection* m;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
TempoSection* cp = new TempoSection (*t);
copy.push_back (cp);
}
- if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection *> (*i);
if (m == section) {
ret = new MeterSection (*m);
copy.push_back (ret);
/* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
Glib::Threads::RWLock::WriterLock lm (lock);
TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
- double beat = beat_at_frame_locked (future_map, frame);
-
- if (sub_num > 1) {
- beat = floor (beat) + (floor (((beat - floor (beat)) * (double) sub_num) + 0.5) / sub_num);
- } else if (sub_num == 1) {
- /* snap to beat */
- beat = floor (beat + 0.5);
- }
-
+ const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
double pulse = pulse_at_beat_locked (future_map, beat);
- if (sub_num == -1) {
- /* snap to bar */
- pulse = floor (pulse + 0.5);
- }
-
if (solve_map_pulse (future_map, tempo_copy, pulse)) {
solve_map_pulse (_metrics, ts, pulse);
recompute_meters (_metrics);
if (solve_map_frame (future_map, copy, frame)) {
solve_map_frame (_metrics, ms, frame);
- recompute_tempos (_metrics);
+ recompute_tempi (_metrics);
}
}
} else {
if (solve_map_bbt (future_map, copy, bbt)) {
solve_map_bbt (_metrics, ms, bbt);
- recompute_tempos (_metrics);
+ recompute_tempi (_metrics);
}
}
}
Glib::Threads::RWLock::WriterLock lm (lock);
TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
- recompute_tempos (future_map);
+ recompute_tempi (future_map);
if (check_solved (future_map)) {
ts->set_beats_per_minute (bpm.beats_per_minute());
TempoSection* next_t = 0;
for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
TempoSection* t = 0;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
if (t->frame() > ts->frame()) {
next_t = t;
break;
}
new_bpm = min (new_bpm, (double) 1000.0);
prev_t->set_beats_per_minute (new_bpm);
- recompute_tempos (future_map);
+ recompute_tempi (future_map);
recompute_meters (future_map);
if (check_solved (future_map)) {
ts->set_beats_per_minute (new_bpm);
- recompute_tempos (_metrics);
+ recompute_tempi (_metrics);
recompute_meters (_metrics);
}
}
MetricPositionChanged (); // Emit Signal
}
+double
+TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num)
+{
+ Glib::Threads::RWLock::ReaderLock lm (lock);
+
+ return exact_beat_at_frame_locked (_metrics, frame, sub_num);
+}
+
+double
+TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num)
+{
+ double beat = beat_at_frame_locked (metrics, frame);
+ if (sub_num > 1) {
+ beat = floor (beat) + (floor (((beat - floor (beat)) * (double) sub_num) + 0.5) / sub_num);
+ } else if (sub_num == 1) {
+ /* snap to beat */
+ beat = floor (beat + 0.5);
+ } else if (sub_num == -1) {
+ /* snap to bar */
+ Timecode::BBT_Time bbt = bbt_at_beat_locked (metrics, beat);
+ bbt.beats = 1;
+ bbt.ticks = 0;
+ beat = beat_at_bbt_locked (metrics, bbt);
+ }
+ return beat;
+}
+
framecnt_t
TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
{
const TempoSection&
TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
{
- Metrics::const_iterator i;
TempoSection* prev = 0;
- for (i = metrics.begin(); i != metrics.end(); ++i) {
- TempoSection* t;
+ TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
if (!t->active()) {
continue;
}
TempoSection* prev_t = 0;
const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
+ TempoSection* t;
+
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
- TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
break;
}
{
Glib::Threads::RWLock::ReaderLock lm (lock);
- const TempoSection* ts_at = &tempo_section_at_frame_locked (_metrics, frame);
+ const TempoSection* ts_at = 0;
const TempoSection* ts_after = 0;
Metrics::const_iterator i;
+ TempoSection* t;
for (i = _metrics.begin(); i != _metrics.end(); ++i) {
- TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if ((*i)->is_tempo()) {
+ t = static_cast<TempoSection*> (*i);
if (!t->active()) {
continue;
}
- if ((*i)->frame() > frame) {
+ if (ts_at && (*i)->frame() > frame) {
ts_after = t;
break;
}
+ ts_at = t;
}
}
Metrics::const_iterator i;
MeterSection* prev = 0;
+ MeterSection* m;
+
for (i = metrics.begin(); i != metrics.end(); ++i) {
- MeterSection* m;
- if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection*> (*i);
if (prev && (*i)->frame() > frame) {
break;
for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
MeterSection* m;
- if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
+ if (!(*i)->is_tempo()) {
+ m = static_cast<MeterSection*> (*i);
if (prev_m && m->beat() > beat) {
break;
}
int cnt = 0;
for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
- if (dynamic_cast<const TempoSection*>(*i) != 0) {
+ if ((*i)->is_tempo()) {
cnt++;
}
}
int cnt = 0;
for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
- if (dynamic_cast<const MeterSection*>(*i) != 0) {
+ if (!(*i)->is_tempo()) {
cnt++;
}
}