Tempo ramps - add visualtempo curve, dragging bbt or music rulers with constraint modifier dilates previous tempo.

This commit is contained in:
nick_m
2016-05-08 03:03:12 +10:00
parent 652a59b317
commit 86b0268e8b
15 changed files with 576 additions and 14 deletions

View File

@@ -399,6 +399,7 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible
void gui_move_meter (MeterSection*, const Timecode::BBT_Time& bbt);
bool gui_change_tempo (TempoSection*, const Tempo& bpm);
void gui_dilate_tempo (MeterSection*, const framepos_t& frame);
void gui_dilate_tempo (const framepos_t& frame, const framepos_t& end_frame);
bool can_solve_bbt (TempoSection* section, const Timecode::BBT_Time& bbt);

View File

@@ -2654,6 +2654,10 @@ TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
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;
@@ -2678,8 +2682,8 @@ TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
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. */
@@ -2778,6 +2782,133 @@ TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
MetricPositionChanged (); // Emit Signal
}
void
TempoMap::gui_dilate_tempo (const framepos_t& frame, const framepos_t& end_frame)
{
Metrics future_map;
TempoSection* ts = 0;
{
Glib::Threads::RWLock::WriterLock lm (lock);
ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, frame - 1));
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;
double new_bpm = 0.0;
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));
}
/* 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;
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. */
contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (frame - prev_to_prev_t->frame());
frame_contribution = contribution * (double) fr_off;
prev_t_frame_contribution -= frame_contribution;
}
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_t->frame())
/ (double) (frame + prev_t_frame_contribution - prev_t->frame()));
} else {
/* prev to prev is irrelevant */
const double meter_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
const double frame_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
if (frame_pulse != prev_t->pulse()) {
new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_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 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();
} else {
/* 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();
}
const double end_minute = ((end_frame - prev_t->frame()) / (double) _frame_rate) / 60.0;
const double end_tempo = prev_t->tempo_at_frame (end_frame, _frame_rate);
const double future_c = log (end_tempo / (new_bpm / (double) prev_t->note_type())) / end_minute;
/* limits - a bit clunky, but meh */
if (future_c > -20.1 && future_c < 20.1) {
new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
/ (double) ((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();
} else {
/* 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();
}
const double end_minute = ((end_frame - prev_t->frame()) / (double) _frame_rate) / 60.0;
const double end_tempo = prev_t->tempo_at_frame (end_frame, _frame_rate);
const double future_c = log (end_tempo / (new_bpm / (double) prev_t->note_type())) / end_minute;
/* limits - a bit clunky, but meh */
if (future_c > -20.1 && future_c < 20.1) {
new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
/ (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
}
}
prev_t->set_beats_per_minute (new_bpm);
recompute_tempos (future_map);
recompute_meters (future_map);
if (check_solved (future_map, true)) {
prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, frame - 1));
prev_t->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
TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
{

View File

@@ -194,7 +194,7 @@ Curve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
window_space = item_to_window (Duple (samples[left].x, samples[left].y));
context->move_to (window_space.x, window_space.y);
for (uint32_t idx = left + 1; idx < right; ++idx) {
window_space = item_to_window (Duple (samples[idx].x, samples[idx].y));
window_space = item_to_window (Duple (samples[idx].x, samples[idx].y), false);
context->line_to (window_space.x, window_space.y);
}