/** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
*/
double
-TempoSection::tempo_at_frame (const frameoffset_t& f, const framecnt_t& frame_rate) const
+TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
{
if (_type == Constant || _c_func == 0.0) {
return pulses_per_minute();
}
- return pulse_tempo_at_time (frame_to_minute (f - (frameoffset_t) frame(), frame_rate));
+ return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
}
/** returns the zero-based frame (relative to session)
beat b is only used for constant tempos.
note that the tempo map may have multiple such values.
*/
-frameoffset_t
+framepos_t
TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
{
if (_type == Constant || _c_func == 0.0) {
falls.
*/
-frameoffset_t
+framepos_t
TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
{
if (_type == Constant || _c_func == 0.0) {
- return (frameoffset_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
+ return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
}
return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
}
-frameoffset_t
+framepos_t
TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
{
- return (frameoffset_t) floor ((time * 60.0 * (frameoffset_t) frame_rate) + 0.5);
+ return (framepos_t) floor ((time * 60.0 * frame_rate) + 0.5);
}
double
-TempoSection::frame_to_minute (const frameoffset_t& frame, const framecnt_t& frame_rate) const
+TempoSection::frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const
{
return (frame / (double) frame_rate) / 60.0;
}
TempoMap::~TempoMap ()
{
+ Metrics::const_iterator d = _metrics.begin();
+ while (d != _metrics.end()) {
+ delete (*d);
+ ++d;
+ }
+ _metrics.clear();
}
void
*/
MeterSection* m = 0;
if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
- //assert (m->bbt().ticks == 0);
if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
}
_metrics.insert (i, section);
- //dump (_metrics, std::cerr);
+ //dump (_metrics, std::cout);
}
}
}
MeterSection*
-TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
+TempoMap::add_meter_locked (const Meter& meter, double beat, const 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
*/
- if (where.beats != 1) {
- where.beats = 1;
- where.bars++;
- }
- /* new meters *always* start on a beat. */
- where.ticks = 0;
const double pulse = pulse_at_beat_locked (_metrics, beat);
MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
+
do_insert (new_meter);
if (recompute) {
- solve_map (_metrics, new_meter, pulse);
+ solve_map (_metrics, new_meter, where);
}
return new_meter;
}
MeterSection*
-TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
+TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, const Timecode::BBT_Time& where, bool recompute)
{
+ /* add meter-locked tempo */
+ TempoSection* t = add_tempo_locked (tempo_at_locked (_metrics, frame), frame, true, TempoSection::Ramp);
+ if (t) {
+ t->set_locked_to_meter (true);
+ }
+
MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
- TempoSection* t = 0;
- double pulse = pulse_at_frame_locked (_metrics, frame);
- new_meter->set_pulse (pulse);
+ new_meter->set_pulse (pulse_at_frame_locked (_metrics, frame));
do_insert (new_meter);
- /* add meter-locked tempo */
- t = add_tempo_locked (tempo_at_locked (_metrics, frame), frame, true, TempoSection::Ramp);
- t->set_locked_to_meter (true);
-
if (recompute) {
solve_map (_metrics, new_meter, frame);
}
prev_t->set_c_func (0.0);
}
-/* tempos must be positioned correctly */
+/* tempos must be positioned correctly.
+ the current approach is to use a meter's bbt time as its base position unit.
+ this means that a meter's beat may change, but its bbt may not.
+ an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
+ while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
+*/
void
TempoMap::recompute_meters (Metrics& metrics)
{
if (meter->position_lock_style() == AudioTime) {
double pulse = 0.0;
pair<double, BBT_Time> b_bbt;
- if (meter->movable()) {
- b_bbt = make_pair (meter->beat(), meter->bbt());
- pulse = pulse_at_frame_locked (metrics, meter->frame());
+ 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 ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
+ meter_locked_tempo = t;
+ break;
+ }
+ }
+ }
+
+ if (prev_m) {
+ const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
+ if (beats + prev_m->beat() != meter->beat()) {
+ /* reordering caused a bbt change */
+ b_bbt = make_pair (beats + prev_m->beat()
+ , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
+ pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
+
+ } else if (meter->movable()) {
+ b_bbt = make_pair (meter->beat(), meter->bbt());
+ pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
+ }
} else {
b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
}
+ if (meter_locked_tempo) {
+ meter_locked_tempo->set_pulse (pulse);
+ }
meter->set_beat (b_bbt);
meter->set_pulse (pulse);
+
} else {
+ /* MusicTime */
double pulse = 0.0;
pair<double, BBT_Time> new_beat;
if (prev_m) {
- pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
- //new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
+ const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
+ if (beats + prev_m->beat() != meter->beat()) {
+ /* reordering caused a bbt change */
+ new_beat = make_pair (beats + prev_m->beat()
+ , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
+ } else {
+ new_beat = make_pair (beats + prev_m->beat(), meter->bbt());
+ }
+ pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
} else {
/* shouldn't happen - the first is audio-locked */
pulse = pulse_at_beat_locked (metrics, meter->beat());
new_beat = make_pair (meter->beat(), meter->bbt());
}
- //meter->set_beat (new_beat);
- meter->set_frame (frame_at_pulse_locked (metrics, pulse));
+ meter->set_beat (new_beat);
meter->set_pulse (pulse);
+ meter->set_frame (frame_at_pulse_locked (metrics, pulse));
}
prev_m = meter;
}
}
- //dump (_metrics, std::cerr;
}
void
if (prev_m && m->position_lock_style() == AudioTime) {
TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
- const frameoffset_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
+ 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)) {
return false;
}
recompute_tempos (imaginary);
}
- recompute_meters (imaginary);
if (check_solved (imaginary, true)) {
return true;
}
recompute_tempos (imaginary);
}
- recompute_meters (imaginary);
if (check_solved (imaginary, true)) {
return true;
}
- //dump (imaginary, std::cerr);
-
return false;
}
recompute_tempos (imaginary);
}
- recompute_meters (imaginary);
if (check_solved (imaginary, false)) {
return true;
}
recompute_tempos (imaginary);
}
- recompute_meters (imaginary);
if (check_solved (imaginary, false)) {
return true;
}
- //dump (imaginary, std::cerr);
-
return false;
}
}
}
+ /* it would make sense to bail out if there is no audio-locked meter,
+ however it may be desirable to move a music-locked meter by frame at some point.
+ */
TempoSection* meter_locked_tempo = 0;
- for (Metrics::const_iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
+ for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
TempoSection* t;
- if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
- if (t->frame() == section->frame()) {
- meter_locked_tempo = t;
- break;
- }
+ meter_locked_tempo = t;
+ break;
}
}
}
if (meter_locked_tempo) {
Metrics future_map;
- TempoSection* new_section = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
- if (!new_section) {
- return false;
- }
+ bool solved = false;
+ TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
const double new_pulse = ((section->beat() - prev_m->beat())
/ prev_m->note_divisor()) + prev_m->pulse();
-
- if (solve_map (future_map, new_section, section->frame())) {
+ const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
+ if ((solved = solve_map (future_map, tempo_copy, smallest_frame))) {
meter_locked_tempo->set_pulse (new_pulse);
- solve_map (imaginary, meter_locked_tempo, section->frame());
- section->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
+ solve_map (imaginary, meter_locked_tempo, smallest_frame);
+ section->set_frame (smallest_frame);
section->set_pulse (new_pulse);
} else {
+ solved = false;
+ }
+
+ Metrics::const_iterator d = future_map.begin();
+ while (d != future_map.end()) {
+ delete (*d);
+ ++d;
+ }
+
+ if (!solved) {
return false;
}
}
} else {
if (meter_locked_tempo) {
Metrics future_map;
- TempoSection* new_section = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
- if (!new_section) {
- return false;
- }
- new_section->set_active (true);
+ bool solved = false;
- if (solve_map (future_map, new_section, frame)) {
+ TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
+ MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
+ meter_copy->set_frame (frame);
+
+ if ((solved = solve_map (future_map, tempo_copy, frame))) {
+ section->set_frame (frame);
meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
/ prev_m->note_divisor()) + prev_m->pulse());
solve_map (imaginary, meter_locked_tempo, frame);
} else {
+ solved = false;
+ }
+
+ Metrics::const_iterator d = future_map.begin();
+ while (d != future_map.end()) {
+ delete (*d);
+ ++d;
+ }
+
+ if (!solved) {
return false;
}
}
}
} else {
+ /* not movable (first meter atm) */
if (meter_locked_tempo) {
Metrics future_map;
- TempoSection* new_section = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
- if (!new_section) {
- return false;
- }
- new_section->set_frame (frame);
- new_section->set_pulse (0.0);
- new_section->set_active (true);
+ bool solved = false;
+ TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
+
+ tempo_copy->set_frame (frame);
+ tempo_copy->set_pulse (0.0);
- if (solve_map (future_map, new_section, frame)) {
+ if ((solved = solve_map (future_map, tempo_copy, frame))) {
+ section->set_frame (frame);
meter_locked_tempo->set_frame (frame);
meter_locked_tempo->set_pulse (0.0);
- meter_locked_tempo->set_active (true);
solve_map (imaginary, meter_locked_tempo, frame);
} else {
+ solved = false;
+ }
+
+ Metrics::const_iterator d = future_map.begin();
+ while (d != future_map.end()) {
+ delete (*d);
+ ++d;
+ }
+
+ if (!solved) {
return false;
}
+
} else {
return false;
}
section->set_pulse (0.0);
}
- section->set_frame (frame);
break;
}
} else {
recompute_meters (imaginary);
}
- //dump (imaginary, std::cerr);
+
return true;
}
bool
-TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
+TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
{
- MeterSection* prev_m = 0;
+ /* 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 (m != section && m->bbt().bars == when.bars) {
+ return false;
+ }
+ }
+ }
- section->set_pulse (pulse);
+ MeterSection* prev_m = 0;
+ MeterSection* section_prev = 0;
for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
MeterSection* m;
if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
- double new_pulse = 0.0;
pair<double, BBT_Time> b_bbt;
+ double new_pulse = 0.0;
+
+ if (prev_m && m->bbt().bars > when.bars && !section_prev){
+ section_prev = prev_m;
+ const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
+ const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
+ pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
- if (prev_m && m == section){
- /* the first meter is always audio-locked, so prev_m should exist.
- should we allow setting audio locked meters by pulse?
- */
- const double beats = floor (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + 0.5);
- const int32_t bars = (beats) / prev_m->divisions_per_bar();
- pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), BBT_Time (bars + prev_m->bbt().bars, 1, 0));
section->set_beat (b_bbt);
+ section->set_pulse (pulse);
section->set_frame (frame_at_pulse_locked (imaginary, pulse));
- prev_m = m;
+ prev_m = section;
continue;
}
+
if (m->position_lock_style() == AudioTime) {
- if (m->movable()) {
+ TempoSection* meter_locked_tempo = 0;
+ for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
+ TempoSection* t;
+ if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
+ if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
+ meter_locked_tempo = t;
+ break;
+ }
+ }
+ }
+
+ if (prev_m) {
const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
+
if (beats + prev_m->beat() != m->beat()) {
/* tempo/ meter change caused a change in beat (bar). */
- const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
b_bbt = make_pair (beats + prev_m->beat()
, BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
- } else {
+ } else if (m->movable()) {
b_bbt = make_pair (m->beat(), m->bbt());
- new_pulse = pulse_at_frame_locked (imaginary, m->frame());
+ new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
}
} else {
b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
}
+ if (meter_locked_tempo) {
+ meter_locked_tempo->set_pulse (new_pulse);
+ recompute_tempos (imaginary);
+ }
+ m->set_beat (b_bbt);
+ m->set_pulse (new_pulse);
+
} else {
- new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
- b_bbt = make_pair (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
+ /* MusicTime */
+ const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
+ if (beats + prev_m->beat() != m->beat()) {
+ /* tempo/ meter change caused a change in beat (bar). */
+ b_bbt = make_pair (beats + prev_m->beat()
+ , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
+ } else {
+ b_bbt = make_pair (beats + prev_m->beat()
+ , m->bbt());
+ }
+ new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
+ m->set_beat (b_bbt);
+ m->set_pulse (new_pulse);
+ m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
}
- m->set_beat (b_bbt);
- m->set_pulse (new_pulse);
+
prev_m = m;
}
}
+ if (!section_prev) {
+
+ const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
+ const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
+ pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
+
+ section->set_beat (b_bbt);
+ section->set_pulse (pulse);
+ section->set_frame (frame_at_pulse_locked (imaginary, pulse));
+ }
+
MetricSectionSorter cmp;
imaginary.sort (cmp);
+
if (section->position_lock_style() == AudioTime) {
/* we're setting the pulse */
section->set_position_lock_style (MusicTime);
} else {
recompute_meters (imaginary);
}
+
return true;
}
TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
{
Metrics copy;
- TempoSection* new_section = 0;
+ TempoSection* tempo_copy = 0;
{
Glib::Threads::RWLock::ReaderLock lm (lock);
- new_section = copy_metrics_and_point (_metrics, copy, ts);
- if (!new_section) {
+ tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
+ if (!tempo_copy) {
return false;
}
}
const double beat = bbt_to_beats_locked (copy, bbt);
- const bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
+ const bool ret = solve_map (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
Metrics::const_iterator d = copy.begin();
while (d != copy.end()) {
Glib::Threads::RWLock::ReaderLock lm (lock);
Metrics future_map;
framepos_t ret = 0;
- TempoSection* new_section = copy_metrics_and_point (_metrics, future_map, section);
- if (!new_section) {
+ TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
+ if (!tempo_copy) {
return 0;
}
const double beat = bbt_to_beats_locked (future_map, bbt);
- if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
- ret = new_section->frame();
+ if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
+ ret = tempo_copy->frame();
} else {
- ret = frame_at_beat_locked (_metrics, beat);
+ ret = section->frame();
}
Metrics::const_iterator d = future_map.begin();
Glib::Threads::RWLock::ReaderLock lm (lock);
Metrics future_map;
double ret = 0.0;
- TempoSection* new_section = copy_metrics_and_point (_metrics, future_map, section);
+ TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
- if (solve_map (future_map, new_section, frame)) {
- ret = new_section->pulse();
+ if (solve_map (future_map, tempo_copy, frame)) {
+ ret = tempo_copy->pulse();
} else {
- ret = pulse_at_frame_locked (_metrics, frame);
+ ret = section->pulse();
}
Metrics::const_iterator d = future_map.begin();
Metrics future_map;
{
Glib::Threads::RWLock::WriterLock lm (lock);
- TempoSection* new_section = copy_metrics_and_point (_metrics, future_map, ts);
- if (solve_map (future_map, new_section, frame)) {
+ TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
+ if (solve_map (future_map, tempo_copy, frame)) {
solve_map (_metrics, ts, frame);
recompute_meters (_metrics);
}
Metrics future_map;
{
Glib::Threads::RWLock::WriterLock lm (lock);
- TempoSection* new_section = copy_metrics_and_point (_metrics, future_map, ts);
- if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
+ TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
+ if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
recompute_meters (_metrics);
}
MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
if (solve_map (future_map, copy, frame)) {
solve_map (_metrics, ms, frame);
+ recompute_tempos (_metrics);
}
}
+ Metrics::const_iterator d = future_map.begin();
+ while (d != future_map.end()) {
+ delete (*d);
+ ++d;
+ }
+
MetricPositionChanged (); // Emit Signal
}
void
-TempoMap::gui_move_meter (MeterSection* ms, const double& pulse)
+TempoMap::gui_move_meter (MeterSection* ms, const Timecode::BBT_Time& bbt)
{
+ Metrics future_map;
{
Glib::Threads::RWLock::WriterLock lm (lock);
- solve_map (_metrics, ms, pulse);
+ MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
+ if (solve_map (future_map, copy, bbt)) {
+ solve_map (_metrics, ms, bbt);
+ recompute_tempos (_metrics);
+ }
+ }
+
+ Metrics::const_iterator d = future_map.begin();
+ while (d != future_map.end()) {
+ delete (*d);
+ ++d;
}
MetricPositionChanged (); // Emit Signal
bool can_solve = false;
{
Glib::Threads::RWLock::WriterLock lm (lock);
- TempoSection* new_section = copy_metrics_and_point (_metrics, future_map, ts);
- new_section->set_beats_per_minute (bpm.beats_per_minute());
+ 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);
if (check_solved (future_map, true)) {
Metrics future_map;
TempoSection* ts = 0;
+ if (frame <= first_meter().frame()) {
+ return;
+ }
+
if (ms->position_lock_style() == AudioTime) {
/* disabled for now due to faked tempo locked to meter pulse */
return;
}
+
{
Glib::Threads::RWLock::WriterLock lm (lock);
ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
const frameoffset_t fr_off = frame - ms->frame();
double new_bpm = 0.0;
- if (prev_t) {
+ if (prev_t && prev_t->pulse() > 0.0) {
prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
}
constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
*/
double contribution = 0.0;
- frameoffset_t frame_contribution = 0.0;
- frameoffset_t prev_t_frame_contribution = 0.0;
+ frameoffset_t frame_contribution = 0;
+ frameoffset_t prev_t_frame_contribution = fr_off;
if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
/* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
}
- const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
- if (diff > -0.1 && diff < 0.1) {
- new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
- / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
- }
-
} else if (prev_t->c_func() > 0.0) {
if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
/* prev_to_prev_t is irrelevant */
new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
}
+ }
- /* limits - a bit clunky, but meh */
- const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
- if (diff > -0.1 && diff < 0.1) {
- new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
- / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
- }
+ /* limits - a bit clunky, but meh */
+ const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
+ if (diff > -1.0 && diff < 1.0) {
+ new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
+ / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
}
prev_t->set_beats_per_minute (new_bpm);
}
MetricPositionChanged (); // Emit Signal
- return;
+}
+
+void
+TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
+{
+ /*
+ Ts (future prev_t) Tnext
+ | |
+ | [drag^] |
+ |----------|----------
+ e_f pulse(frame)
+ */
+
+ Metrics future_map;
+
+ {
+ Glib::Threads::RWLock::WriterLock lm (lock);
+
+ if (!ts) {
+ return;
+ }
+
+ TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
+ TempoSection* prev_to_prev_t = 0;
+ const frameoffset_t fr_off = end_frame - frame;
+
+ if (prev_t && prev_t->pulse() > 0.0) {
+ prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
+ }
+
+ 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 (t->frame() > ts->frame()) {
+ next_t = t;
+ break;
+ }
+ }
+ }
+
+ /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
+ constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
+ */
+ double contribution = 0.0;
+ double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
+
+ if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+ contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
+ }
+
+ frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
+ double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
+ double new_bpm;
+
+ if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
+
+ if (prev_t->position_lock_style() == MusicTime) {
+ if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+
+ new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
+ / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
+
+ } else {
+ /* prev to prev is irrelevant */
+
+ if (start_pulse != prev_t->pulse()) {
+ new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
+ } else {
+ new_bpm = prev_t->beats_per_minute();
+ }
+ }
+ } else {
+ /* AudioTime */
+ if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+ new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
+ / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
+ } else {
+ /* prev_to_prev_t is irrelevant */
+
+ if (end_frame != prev_t->frame()) {
+ new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
+ } else {
+ new_bpm = prev_t->beats_per_minute();
+ }
+ }
+ }
+ } else {
+
+ double frame_ratio;
+ double pulse_ratio;
+ const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
+
+ if (prev_to_prev_t) {
+
+ frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
+ pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
+ } else {
+
+ frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
+ pulse_ratio = (start_pulse / end_pulse);
+ }
+ new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
+ }
+
+ prev_t->set_beats_per_minute (new_bpm);
+ recompute_tempos (future_map);
+ recompute_meters (future_map);
+
+ if (check_solved (future_map, true)) {
+ ts->set_beats_per_minute (new_bpm);
+ recompute_tempos (_metrics);
+ recompute_meters (_metrics);
+ }
+ }
+
+ Metrics::const_iterator d = future_map.begin();
+ while (d != future_map.end()) {
+ delete (*d);
+ ++d;
+ }
+
+ MetricPositionChanged (); // Emit Signal
}
framecnt_t
}
void
-TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
+TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num, RoundMode dir)
{
if (sub_num == -1) {
- const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
- if ((double) when.beats > bpb / 2.0) {
+ if (dir > 0) {
++when.bars;
+ when.beats = 1;
+ when.ticks = 0;
+ } else if (dir < 0) {
+ when.beats = 1;
+ when.ticks = 0;
+ } else {
+ const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
+ if ((double) when.beats > bpb / 2.0) {
+ ++when.bars;
+ }
+ when.beats = 1;
+ when.ticks = 0;
}
- when.beats = 1;
- when.ticks = 0;
+
return;
+
} else if (sub_num == 0) {
const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
}
}
when.ticks = 0;
+
return;
}
+
const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
- double rem;
- if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
- /* closer to the next subdivision, so shift forward */
- when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
+ if (dir > 0) {
+ /* round to next (or same iff dir == RoundUpMaybe) */
+
+ uint32_t mod = when.ticks % ticks_one_subdivisions_worth;
- if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
- ++when.beats;
- when.ticks -= Timecode::BBT_Time::ticks_per_beat;
+ if (mod == 0 && dir == RoundUpMaybe) {
+ /* right on the subdivision, which is fine, so do nothing */
+
+ } else if (mod == 0) {
+ /* right on the subdivision, so the difference is just the subdivision ticks */
+ when.ticks += ticks_one_subdivisions_worth;
+
+ } else {
+ /* not on subdivision, compute distance to next subdivision */
+
+ when.ticks += ticks_one_subdivisions_worth - mod;
}
- } else if (rem > 0) {
- /* closer to previous subdivision, so shift backward */
+ if (when.ticks >= BBT_Time::ticks_per_beat) {
+ when.ticks -= BBT_Time::ticks_per_beat;
+ }
- if (rem > when.ticks) {
- if (when.beats == 0) {
- /* can't go backwards past zero, so ... */
- }
- /* step back to previous beat */
- --when.beats;
- when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
+ } else if (dir < 0) {
+ /* round to previous (or same iff dir == RoundDownMaybe) */
+
+ uint32_t difference = when.ticks % ticks_one_subdivisions_worth;
+
+ if (difference == 0 && dir == RoundDownAlways) {
+ /* right on the subdivision, but force-rounding down,
+ so the difference is just the subdivision ticks */
+ difference = ticks_one_subdivisions_worth;
+ }
+
+ if (when.ticks < difference) {
+ when.ticks = BBT_Time::ticks_per_beat - when.ticks;
} else {
- when.ticks = when.ticks - rem;
+ when.ticks -= difference;
+ }
+
+ } else {
+ /* round to nearest */ double rem;
+ if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
+ /* closer to the next subdivision, so shift forward */
+
+ when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
+
+ if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
+ ++when.beats;
+ when.ticks -= Timecode::BBT_Time::ticks_per_beat;
+ }
+
+ } else if (rem > 0) {
+ /* closer to previous subdivision, so shift backward */
+
+ if (rem > when.ticks) {
+ if (when.beats == 0) {
+ /* can't go backwards past zero, so ... */
+ }
+ /* step back to previous beat */
+ --when.beats;
+ when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
+ } else {
+ when.ticks = when.ticks - rem;
+ }
}
}
}