diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 73ea53b4b8..efda94defc 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -2492,6 +2492,7 @@ private: friend class BBTRulerDrag; friend class MeterMarkerDrag; friend class TempoMarkerDrag; + friend class TempoTwistDrag; friend class CursorDrag; friend class FadeInDrag; friend class FadeOutDrag; diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index f100e2eeb1..899fd88fa3 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -3807,15 +3807,9 @@ BBTRulerDrag::aborted (bool moved) TempoMap::abort_update (); } -#warning NUTEMPO fixme no tempo twist drag for now -#if 0 - TempoTwistDrag::TempoTwistDrag (Editor* e, ArdourCanvas::Item* i) : Drag (e, i, Temporal::BeatTime) - , _grab_qn (0.0) - , _grab_tempo (0.0) , _tempo (0) - , _next_tempo (0) , _drag_valid (true) , _before_state (0) { @@ -3827,120 +3821,97 @@ void TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) { Drag::start_grab (event, cursor); - TempoMap& map (_editor->session()->tempo_map()); - /* get current state */ - _before_state = &map.get_state(); - _tempo = const_cast (&map.tempo_section_at_sample (raw_grab_sample())); - - if (_tempo->locked_to_meter()) { - _drag_valid = false; - return; - } - - _next_tempo = map.next_tempo_section (_tempo); - if (_next_tempo) { - if (!map.next_tempo_section (_next_tempo)) { - _drag_valid = false; - finished (event, false); - - return; - } - _editor->tempo_curve_selected (_tempo, true); - _editor->tempo_curve_selected (_next_tempo, true); - - ostringstream sstr; - sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n"; - sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n"; - sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute(); - show_verbose_cursor_text (sstr.str()); - } else { - _drag_valid = false; - } - - _grab_tempo = Tempo (_tempo->note_types_per_minute(), _tempo->note_type()); } void TempoTwistDrag::setup_pointer_offset () { - TempoMap& map (_editor->session()->tempo_map()); - const double beat_at_sample = max (0.0, map.beat_at_sample (raw_grab_sample())); - const uint32_t divisions = _editor->get_grid_beat_divisions (0); - double beat = 0.0; - - if (divisions > 0) { - beat = floor (beat_at_sample) + (floor (((beat_at_sample - floor (beat_at_sample)) * divisions)) / divisions); - } else { - /* while it makes some sense for the user to determine the division to 'grab', - grabbing a bar often leads to confusing results wrt the actual tempo section being altered - and the result over steep tempo curves. Use sixteenths. - */ - beat = floor (beat_at_sample) + (floor (((beat_at_sample - floor (beat_at_sample)) * 4)) / 4); - } - - _grab_qn = map.quarters_at_beat (beat); - - _pointer_offset = raw_grab_sample() - map.sample_at_quarter_note (_grab_qn); - + // _pointer_offset = timepos_t (_grab_qn).distance (raw_grab_time()); } void TempoTwistDrag::motion (GdkEvent* event, bool first_move) { - - if (!_next_tempo || !_drag_valid) { + if (!_drag_valid) { return; } - TempoMap& map (_editor->session()->tempo_map()); - if (first_move) { - _editor->begin_reversible_command (_("twist tempo")); + map = _editor->begin_tempo_map_edit (); + /* Get the tempo point that starts this section */ + + _tempo = const_cast (&map->tempo_at (raw_grab_time())); + assert (_tempo); + + if (!(_next_tempo = const_cast (map->next_tempo (*_tempo)))) { + _drag_valid = false; + return; + } + + _grab_qn = _tempo->beats(); + + if (_tempo->locked_to_meter() || _next_tempo->locked_to_meter()) { + _drag_valid = false; + return; + } } - samplepos_t pf; + /* get current state */ + _before_state = &map->get_state(); + + _editor->tempo_curve_selected (_tempo, true); + if (_next_tempo) { + _editor->tempo_curve_selected (_next_tempo, true); + } + + Beats mouse_beats; if (_editor->grid_musical()) { - pf = adjusted_current_sample (event, false); + mouse_beats = adjusted_current_time (event, false).beats(); } else { - pf = adjusted_current_sample (event); + mouse_beats = adjusted_current_time (event).beats(); } - /* adjust this and the next tempi to match pointer sample */ - double new_bpm = max (1.5, _grab_tempo.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0)); - _editor->session()->tempo_map().gui_twist_tempi (_tempo, new_bpm, map.sample_at_quarter_note (_grab_qn), pf); + + map->twist_tempi (_tempo, timepos_t (_tempo->beats()), timepos_t (mouse_beats)); ostringstream sstr; sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n"; sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n"; - sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute(); + if (_next_tempo) { + sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute(); + } show_verbose_cursor_text (sstr.str()); + + _editor->mid_tempo_change (); } void TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred) { if (!movement_occurred || !_drag_valid) { + aborted (false); return; } _editor->tempo_curve_selected (_tempo, false); - _editor->tempo_curve_selected (_next_tempo, false); + if (_next_tempo) { + _editor->tempo_curve_selected (_next_tempo, false); + } - TempoMap& map (_editor->session()->tempo_map()); - XMLNode &after = map.get_state(); + _editor->begin_reversible_command (_("twist tempo")); + XMLNode &after = map->get_state(); _editor->session()->add_command (new Temporal::TempoCommand (_("twist tempo"), _before_state, &after)); _editor->commit_reversible_command (); + + TempoMap::update (map); } void TempoTwistDrag::aborted (bool moved) { - if (moved) { - _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version); - } + _editor->abort_tempo_map_edit (); } -#endif TempoEndDrag::TempoEndDrag (Editor* e, ArdourCanvas::Item* i) : Drag (e, i, Temporal::BeatTime) diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h index d1f9df5874..9c0c2c94ad 100644 --- a/gtk2_ardour/editor_drag.h +++ b/gtk2_ardour/editor_drag.h @@ -923,8 +923,6 @@ private: bool _drag_valid; }; -#warning NUTEMPO may or may not need this in the new world -#if 0 /** tempo curve twist drag */ class TempoTwistDrag : public Drag { @@ -948,13 +946,13 @@ public: private: Temporal::Beats _grab_qn; - Temporal::Tempo _grab_tempo; Temporal::TempoPoint* _tempo; - Temporal::TempoPoint* _next_tempo; + Temporal::TempoPoint* _grab_tempo; + Temporal::TempoPoint const * _next_tempo; + Temporal::TempoMap::WritableSharedPtr map; bool _drag_valid; XMLNode* _before_state; }; -#endif /** tempo curve twist drag */ class TempoEndDrag : public Drag diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 8d4212af0b..d39aef441f 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -829,7 +829,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT _drags->set (new CursorDrag (this, *_playhead_cursor, false), event); } else if (ArdourKeyboard::indicates_constraint (event->button.state) && Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) { - // _drags->set (new TempoTwistDrag (this, item), event); + _drags->set (new TempoTwistDrag (this, item), event); } else if (ArdourKeyboard::indicates_constraint (event->button.state)) { _drags->set (new BBTRulerDrag (this, item), event); } diff --git a/libs/temporal/tempo.cc b/libs/temporal/tempo.cc index 90f3ea01ab..babb2a5b4a 100644 --- a/libs/temporal/tempo.cc +++ b/libs/temporal/tempo.cc @@ -3026,201 +3026,54 @@ TempoMap::set_ramped (TempoPoint & tp, bool yn) reset_starting_at (tp.sclock() + 1); } -#if 0 -bool -TempoMap::twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame) +void +TempoMap::twist_tempi (TempoPoint* ts, timepos_t const & start, timepos_t const & end) { - TempoSection* next_t = 0; - TempoSection* next_to_next_t = 0; - Metrics future_map; - bool can_solve = false; + assert (start.time_domain() == end.time_domain()); - /* minimum allowed measurement distance in frames */ - framepos_t const min_dframe = 2; + TempoPoint* nxt = const_cast (next_tempo (*ts)); + assert (nxt); - if (!ts) { - return false; - } + superclock_t end_scpqn; - TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts); - TempoSection* prev_to_prev_t = 0; - const frameoffset_t fr_off = end_frame - frame; - - if (!tempo_copy) { - return false; - } - - if (tempo_copy->pulse() > 0.0) { - prev_to_prev_t = const_cast(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1))); - } - - for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) { - if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) { - next_t = static_cast (*i); - break; - } - } - - if (!next_t) { - return false; - } - - for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) { - if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) { - next_to_next_t = static_cast (*i); - break; - } - } - - if (!next_to_next_t) { - return false; - } - - double prev_contribution = 0.0; - - if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) { - prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame()); - } - - const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off); - - - framepos_t old_tc_minute = tempo_copy->minute(); - double old_next_minute = next_t->minute(); - double old_next_to_next_minute = next_to_next_t->minute(); - - double new_bpm; - double new_next_bpm; - double new_copy_end_bpm; - - if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) { - new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame()) - / (double) (end_frame - tempo_copy->frame())); + if (ts->clamped()) { + end_scpqn = ts->end_superclocks_per_quarter_note(); } else { - new_bpm = tempo_copy->note_types_per_minute(); + end_scpqn = nxt->superclocks_per_quarter_note (); } - /* don't clamp and proceed here. - testing has revealed that this can go negative, - which is an entirely different thing to just being too low. - */ - if (new_bpm < 0.5) { - return false; + if (!ts->ramped()) { + ((Rampable*) ts)->set_ramped (true); } - new_bpm = min (new_bpm, (double) 1000.0); - - tempo_copy->set_note_types_per_minute (new_bpm); - if (tempo_copy->type() == TempoSection::Constant) { - tempo_copy->set_end_note_types_per_minute (new_bpm); - } - - recompute_tempi (future_map); - - if (check_solved (future_map)) { - - if (!next_t) { - return false; - } - - ts->set_note_types_per_minute (new_bpm); - if (ts->type() == TempoSection::Constant) { - ts->set_end_note_types_per_minute (new_bpm); - } - - recompute_map (_metrics); - - can_solve = true; - } - - if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) { - if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) { - - new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute) - / (double) ((old_next_to_next_minute) - old_next_minute)); - - } else { - new_next_bpm = next_t->note_types_per_minute(); - } - - next_t->set_note_types_per_minute (new_next_bpm); - recompute_tempi (future_map); - - if (check_solved (future_map)) { - for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) { - if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) { - next_t = static_cast (*i); - break; - } - } - - if (!next_t) { - return false; - } - next_t->set_note_types_per_minute (new_next_bpm); - recompute_map (_metrics); - can_solve = true; - } + if (start.time_domain() == Temporal::AudioTime) { + ts->compute_omega_from_audio_duration (end.samples() - start.samples(), end_scpqn); } else { - double next_frame_ratio = 1.0; - double copy_frame_ratio = 1.0; + ts->compute_omega_from_quarter_duration (end.beats() - start.beats(), end_scpqn); + } - if (next_to_next_t) { - next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute); + return; - copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute)); - } + nxt->set (samples_to_superclock (end.samples(), TEMPORAL_SAMPLE_RATE), nxt->beats(), nxt->bbt()); - new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio; - new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio; + if (nxt->ramped()) { + TempoPoint const * nxtnxt = next_tempo (*nxt); + assert (nxtnxt); - tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm); - - if (next_t->clamped()) { - next_t->set_note_types_per_minute (new_copy_end_bpm); + if (ts->clamped()) { + end_scpqn = nxt->end_superclocks_per_quarter_note(); } else { - next_t->set_note_types_per_minute (new_next_bpm); + end_scpqn = nxtnxt->superclocks_per_quarter_note (); } - recompute_tempi (future_map); - - if (check_solved (future_map)) { - for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) { - if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) { - next_t = static_cast (*i); - break; - } - } - - if (!next_t) { - return false; - } - - if (next_t->clamped()) { - next_t->set_note_types_per_minute (new_copy_end_bpm); - } else { - next_t->set_note_types_per_minute (new_next_bpm); - } - - ts->set_end_note_types_per_minute (new_copy_end_bpm); - recompute_map (_metrics); - can_solve = true; + if (start.time_domain() == Temporal::AudioTime) { + nxt->compute_omega_from_audio_duration (superclock_to_samples (nxtnxt->sclock(), TEMPORAL_SAMPLE_RATE) - end.samples(), end_scpqn); + } else { + nxt->compute_omega_from_quarter_duration (nxtnxt->beats() - end.beats(), end_scpqn); } } - - Metrics::const_iterator d = future_map.begin(); - while (d != future_map.end()) { - delete (*d); - ++d; - } - - MetricPositionChanged (PropertyChange ()); // Emit Signal - - return can_solve; } -#endif - void TempoMap::init () { diff --git a/libs/temporal/temporal/tempo.h b/libs/temporal/temporal/tempo.h index 09d4ae55a0..44cf2c5df7 100644 --- a/libs/temporal/temporal/tempo.h +++ b/libs/temporal/temporal/tempo.h @@ -733,6 +733,8 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible LIBTEMPORAL_API void set_time_domain (TimeDomain td); LIBTEMPORAL_API int set_state (XMLNode const&, int version); + void twist_tempi (TempoPoint* ts, timepos_t const & start, timepos_t const & end); + /* END OF MODIFYING METHODS */ LIBTEMPORAL_API TimeDomain time_domain() const { return _time_domain; }