Tempo ramps - tempo drags respect the snap modifier. add some documentation.

This commit is contained in:
nick_m 2016-03-29 05:31:06 +11:00
parent 354567e5a7
commit a9714de9b9
3 changed files with 65 additions and 37 deletions

View File

@ -3318,29 +3318,6 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
framepos_t pf;
double beat = 0.0;
if (!_editor->snap_musical()) {
pf = adjusted_current_frame (event);
} else {
pf = adjusted_current_frame (event, false);
Timecode::BBT_Time when;
_editor->session()->tempo_map().bbt_time (pf, when);
if (_real_section->position_lock_style() == MusicTime) {
if (_editor->snap_type() == SnapToBar) {
_editor->session()->tempo_map().round_bbt (when, -1);
} else {
_editor->session()->tempo_map().round_bbt (when, _editor->get_grid_beat_divisions (0));
}
beat = _editor->session()->tempo_map().bbt_to_beats (when);
} else {
if (_editor->snap_type() == SnapToBar) {
_editor->session()->tempo_map().round_bbt (when, -1);
} else {
_editor->session()->tempo_map().round_bbt (when, _editor->get_grid_beat_divisions (0));
}
pf = _editor->session()->tempo_map().predict_tempo_frame (_real_section, Tempo (_real_section->beats_per_minute(), _real_section->note_type()), when);
}
}
Tempo const tp = _marker->tempo();
if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
@ -3350,12 +3327,47 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
strs << new_bpm;
show_verbose_cursor_text (strs.str());
} else if (_movable) {
if (_real_section->position_lock_style() == MusicTime) {
_editor->session()->tempo_map().gui_move_tempo_beat (_real_section, tp, beat);
if (!_editor->snap_musical()) {
pf = adjusted_current_frame (event);
} else {
_editor->session()->tempo_map().gui_move_tempo_frame (_real_section, tp, pf);
pf = adjusted_current_frame (event, false);
bool use_snap;
if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::snap_modifier ())) {
if (_editor->snap_mode() == Editing::SnapOff) {
use_snap = true;
} else {
use_snap = false;
}
} else {
if (_editor->snap_mode() == Editing::SnapOff) {
use_snap = false;
} else {
use_snap = true;
}
}
Timecode::BBT_Time when;
_editor->session()->tempo_map().bbt_time (pf, when);
if (_real_section->position_lock_style() == MusicTime) {
if (use_snap && _editor->snap_type() == SnapToBar) {
_editor->session()->tempo_map().round_bbt (when, -1);
} else if (use_snap) {
_editor->session()->tempo_map().round_bbt (when, _editor->get_grid_beat_divisions (0));
}
beat = _editor->session()->tempo_map().bbt_to_beats (when);
_editor->session()->tempo_map().gui_move_tempo_beat (_real_section, tp, beat);
} else {
if (use_snap && _editor->snap_type() == SnapToBar) {
_editor->session()->tempo_map().round_bbt (when, -1);
} else if (use_snap) {
_editor->session()->tempo_map().round_bbt (when, _editor->get_grid_beat_divisions (0));
}
pf = _editor->session()->tempo_map().predict_tempo_frame (_real_section, Tempo (_real_section->beats_per_minute(), _real_section->note_type()), when);
_editor->session()->tempo_map().gui_move_tempo_frame (_real_section, tp, pf);
}
}
show_verbose_cursor_time (pf);
}
_marker->set_position (pf);

View File

@ -55,6 +55,9 @@ class LIBARDOUR_API Tempo {
Tempo (double bpm, double type=4.0) // defaulting to quarter note
: _beats_per_minute (bpm), _note_type(type) {}
/* ..or more aptly 'pulse divisions per minute'.
Nothing to do with actual beats, which are defined by the meter and tempo.
*/
double beats_per_minute () const { return _beats_per_minute; }
void set_beats_per_minute (double bpm) { _beats_per_minute = bpm; }
double note_type () const { return _note_type; }

View File

@ -604,7 +604,28 @@ MeterSection::get_state() const
}
/***********************************************************************/
/*
Tempo Map Overview
We have tempos, which are nice to think of in whole pulses per minute,
and meters which divide tempo pulses into bars (via divisions_per_bar)
and beats (via note_divisor).
Tempos and meters may be locked to audio or music.
Because the notion of a beat cannot be determined without both tempo and meter, the first tempo
and first meter are special. they must move together, and must be locked to audio.
Audio locked tempos which lie before the first meter are made inactive.
They will be re-activated if the first meter is again placed before them.
Both tempos and meters have a pulse position and a frame position.
Recomputing the tempo map is the process where the 'missing' position
(pulse in the case of AudioTime and frame for MusicTime) is calculated
based on the lock preference (position_lock_style).
It is important to keep the _metrics in an order that makes sense.
Because ramped MusicTime and AudioTime tempos can interact with each other
and cause reordering, 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()).
*/
struct MetricSectionSorter {
bool operator() (const MetricSection* a, const MetricSection* b) {
return a->pulse() < b->pulse();
@ -2004,18 +2025,10 @@ TempoMap::check_solved (Metrics& metrics, bool by_frame)
if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->pulse() < prev_ts->pulse())) {
return false;
}
if (by_frame && t->frame() != prev_ts->frame_at_pulse (t->pulse(), _frame_rate)) {
/* precision check ensures pulses and frames align independent of lock style.*/
if (t->frame() != prev_ts->frame_at_pulse (t->pulse(), _frame_rate)) {
return false;
}
/*
if (!by_frame && fabs (t->pulse() - prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate)) > 0.00001) {
std::cerr << "beat precision too low for bpm: " << t->beats_per_minute() << std::endl <<
" |error :" << t->pulse() - prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) << std::endl <<
"|frame at beat :" << prev_ts->frame_at_pulse (t->pulse(), _frame_rate) << std::endl <<
" |frame at tempo : " << prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
return false;
}
*/
}
prev_ts = t;
}