From c916d3d9526e21500d879a1ffe53fd7cd986fd3f Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 18 Jan 2022 19:45:18 -0700 Subject: [PATCH] Substantial overhaul of tempo display code Major changes: do not delete and recreate markers and curves for every tempo map change, attach tempo curves directly to their (preceding) tempo marker, notable cleanup of TempoCurve class to remove unnecessary members. More tweaks expected since algorithm for matching markers and tempo map points is not correct yet. --- gtk2_ardour/editor.h | 31 ++- gtk2_ardour/editor_drag.cc | 84 +------ gtk2_ardour/editor_drag.h | 4 +- gtk2_ardour/editor_markers.cc | 16 +- gtk2_ardour/editor_tempodisplay.cc | 385 ++++++++++++++++++++++------- gtk2_ardour/marker.cc | 53 +++- gtk2_ardour/marker.h | 45 ++-- gtk2_ardour/tempo_curve.cc | 87 +++---- gtk2_ardour/tempo_curve.h | 26 +- 9 files changed, 445 insertions(+), 286 deletions(-) diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index d7cec55753..4c0606e26c 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -1723,6 +1723,11 @@ private: PBD::Signal0 EditorFreeze; PBD::Signal0 EditorThaw; + void begin_tempo_map_edit (); + void abort_tempo_map_edit (); + void commit_tempo_map_edit (); + void mid_tempo_change (); + private: friend class DragManager; friend class EditorRouteGroups; @@ -1777,8 +1782,8 @@ private: void remove_tempo_marker (ArdourCanvas::Item*); void remove_meter_marker (ArdourCanvas::Item*); - gint real_remove_tempo_marker (Temporal::TempoPoint*); - gint real_remove_meter_marker (Temporal::MeterPoint*); + gint real_remove_tempo_marker (Temporal::TempoPoint const *); + gint real_remove_meter_marker (Temporal::MeterPoint const *); void edit_tempo_marker (TempoMarker&); void edit_meter_marker (MeterMarker&); @@ -1834,22 +1839,26 @@ private: Gtk::Menu* new_transport_marker_menu; ArdourCanvas::Item* marker_menu_item; - typedef std::list Marks; - Marks metric_marks; - - typedef std::list Curves; - Curves tempo_curves; + typedef std::list Marks; + Marks tempo_marks; + Marks meter_marks; + Marks bbt_marks; void remove_metric_marks (); void draw_metric_marks (Temporal::TempoMap::Metrics const & metrics); + void draw_tempo_marks (); + void draw_meter_marks (); + void draw_bbt_marks (); void compute_current_bbt_points (Temporal::TempoMapPoints& grid, samplepos_t left, samplepos_t right); void reassociate_metric_markers (Temporal::TempoMap::SharedPtr const &); - void reassociate_metric_marker (Temporal::TempoMap::SharedPtr const & tmap, Temporal::TempoMap::Metrics & metric, ArdourMarker& marker); - void begin_tempo_map_edit (); - void abort_tempo_map_edit (); - void commit_tempo_map_edit (); + void reassociate_metric_marker (Temporal::TempoMap::SharedPtr const & tmap, Temporal::TempoMap::Metrics & metric, MetricMarker& marker); + void make_bbt_marker (Temporal::MusicTimePoint const *); + void make_meter_marker (Temporal::MeterPoint const *); + void make_tempo_marker (Temporal::TempoPoint const * ts, double& min_tempo, double& max_tempo, Temporal::TempoPoint const *& prev_ts, uint32_t tc_color, samplecnt_t sr); + void update_tempo_curves (double min_tempo, double max_tempo, samplecnt_t sr); + void tempo_map_changed (); void redisplay_grid (bool immediate_redraw); diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 0a5f4b1a9d..c67798fa1f 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -3600,73 +3600,15 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move) TempoMap::SharedPtr map (TempoMap::use()); if (first_move) { - - // mvc drag - create a dummy marker to catch events, hide it. - - char name[64]; - snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute()); - - _marker = new TempoMarker ( - *_editor, - *_editor->tempo_group, - UIConfiguration::instance().color ("tempo marker"), - name, - _marker->tempo() - ); - - /* use the new marker for the grab */ - swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME); - _marker->hide(); - /* get current state */ _before_state = &map->get_state(); - - if (!_copy) { - - _editor->begin_reversible_command (_("move tempo mark")); - - } else { - - timepos_t const pointer = adjusted_current_time (event, false); - BBT_Time pointer_bbt = map->bbt_at (pointer); - Temporal::TempoMetric metric = map->metric_at (pointer); - Temporal::MeterPoint const & meter = metric.meter(); - Temporal::TempoPoint const & tempo = metric.tempo(); - BBT_Time bbt = tempo.bbt(); - - /* we can't add a tempo where one currently exists */ - if (bbt < pointer_bbt) { - bbt = meter.bbt_add (bbt, BBT_Offset (0, 1, 0)); - } else { - bbt = meter.bbt_add (bbt, BBT_Offset (0, -1, 0)); - } - - _editor->begin_reversible_command (_("copy tempo mark")); - - timepos_t pos; - - if (map->time_domain() == AudioTime) { - pos = timepos_t (map->sample_at (bbt)); - } else { - pos = timepos_t (map->quarters_at (bbt)); - } - - _marker->reset_tempo (map->set_tempo (tempo, pos)); - -#warning paul, need a return status from set_tempo -#if 0 - if (!) { - aborted (true); - return; - } -#endif - } + _editor->begin_reversible_command (_("move tempo mark")); } if (ArdourKeyboard::indicates_constraint (event->button.state) && ArdourKeyboard::indicates_copy (event->button.state)) { double new_bpm = max (1.5, _grab_bpm.end_note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0)); stringstream strs; - map->change_tempo (_marker->tempo(), Tempo (_marker->tempo().note_types_per_minute(), _marker->tempo().note_type(), new_bpm)); + map->change_tempo (const_cast(_marker->tempo()), Tempo (_marker->tempo().note_types_per_minute(), _marker->tempo().note_type(), new_bpm)); strs << "end:" << fixed << setprecision(3) << new_bpm; show_verbose_cursor_text (strs.str()); @@ -3674,31 +3616,19 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move) /* use vertical movement to alter tempo .. should be log */ double new_bpm = max (1.5, _grab_bpm.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0)); stringstream strs; - map->change_tempo (_marker->tempo(), Tempo (new_bpm, _marker->tempo().note_type(), _marker->tempo().end_note_types_per_minute())); + map->change_tempo (const_cast(_marker->tempo()), Tempo (new_bpm, _marker->tempo().note_type(), _marker->tempo().end_note_types_per_minute())); strs << "start:" << fixed << setprecision(3) << new_bpm; show_verbose_cursor_text (strs.str()); } else if (_movable) { - timepos_t pos = adjusted_current_time (event); - - std::cerr << " going to move " << &_marker->tempo() << std::endl; map->move_tempo (_marker->tempo(), pos, false); - - show_verbose_cursor_time (_marker->tempo().time()); - } - - - if (_movable && (!first_move || !_copy)) { - - timepos_t pos = adjusted_current_time (event); - - map->move_tempo (_marker->tempo(), pos, false); - show_verbose_cursor_time (_marker->tempo().time()); + _editor->mid_tempo_change (); } } + void TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred) { @@ -3727,10 +3657,6 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred) _editor->session()->add_command (new MementoCommand (new Temporal::TempoMap::MementoBinder(), _before_state, &after)); _editor->commit_reversible_command (); - - // delete the dummy marker we used for visual representation while moving. - // a new visual marker will show up automatically. - delete _marker; } void diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h index 0c51ef9d4a..bce6d88b57 100644 --- a/gtk2_ardour/editor_drag.h +++ b/gtk2_ardour/editor_drag.h @@ -875,7 +875,7 @@ public: private: TempoMarker* _marker; - Temporal::TempoPoint* _real_section; + Temporal::TempoPoint const * _real_section; bool _copy; bool _movable; @@ -968,7 +968,7 @@ public: private: Temporal::Beats _grab_qn; - Temporal::TempoPoint* _tempo; + Temporal::TempoPoint const * _tempo; XMLNode* _before_state; bool _drag_valid; }; diff --git a/gtk2_ardour/editor_markers.cc b/gtk2_ardour/editor_markers.cc index 8bb5ef51b3..25ef61eee0 100644 --- a/gtk2_ardour/editor_markers.cc +++ b/gtk2_ardour/editor_markers.cc @@ -1531,9 +1531,9 @@ Editor::marker_menu_edit () dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm); if (mm) { - edit_meter_section (mm->meter()); + edit_meter_section (const_cast(mm->meter())); } else if (tm) { - edit_tempo_section (tm->tempo()); + edit_tempo_section (const_cast(tm->tempo())); } } @@ -1567,11 +1567,11 @@ Editor::toggle_tempo_type () TempoMap::SharedPtr tmap (TempoMap::write_copy()); reassociate_metric_markers (tmap); - Temporal::TempoPoint & tempo = tm->tempo(); + Temporal::TempoPoint const & tempo = tm->tempo(); XMLNode &before = tmap->get_state(); - tmap->set_ramped (tempo, !tempo.ramped()); + tmap->set_ramped (const_cast(tempo), !tempo.ramped()); XMLNode &after = tmap->get_state(); _session->add_command (new MementoCommand (new Temporal::TempoMap::MementoBinder(), &before, &after)); @@ -1595,9 +1595,9 @@ Editor::toggle_tempo_clamped () XMLNode &before = tmap->get_state(); reassociate_metric_markers (tmap); - Temporal::Tempo & tempo (tm->tempo()); + Temporal::Tempo const & tempo (tm->tempo()); - tempo.set_clamped (!tempo.clamped()); + const_cast(tempo).set_clamped (!tempo.clamped()); XMLNode &after = tmap->get_state(); _session->add_command (new MementoCommand (new Temporal::TempoMap::MementoBinder(), &before, &after)); @@ -1622,9 +1622,9 @@ Editor::ramp_to_next_tempo () XMLNode &before = tmap->get_state(); reassociate_metric_markers (tmap); - Temporal::TempoPoint & tempo (tm->tempo()); + Temporal::TempoPoint const & tempo (tm->tempo()); - tmap->set_ramped (tempo, !tempo.ramped()); + tmap->set_ramped (const_cast(tempo), !tempo.ramped()); XMLNode &after = tmap->get_state(); _session->add_command (new MementoCommand (new Temporal::TempoMap::MementoBinder(), &before, &after)); diff --git a/gtk2_ardour/editor_tempodisplay.cc b/gtk2_ardour/editor_tempodisplay.cc index 5e4f4a6d9b..4329f0e030 100644 --- a/gtk2_ardour/editor_tempodisplay.cc +++ b/gtk2_ardour/editor_tempodisplay.cc @@ -73,36 +73,40 @@ Editor::remove_metric_marks () { /* don't delete these while handling events, just punt till the GUI is idle */ - for (Marks::iterator x = metric_marks.begin(); x != metric_marks.end(); ++x) { - delete_when_idle (*x); + for (auto & m : tempo_marks) { + delete_when_idle (m); + } + for (auto & m : meter_marks) { + delete_when_idle (m); + } + for (auto & m : bbt_marks) { + delete_when_idle (m); } - metric_marks.clear (); - for (Curves::iterator x = tempo_curves.begin(); x != tempo_curves.end(); ++x) { - delete (*x); - } - tempo_curves.clear (); + tempo_marks.clear (); + meter_marks.clear (); + bbt_marks.clear (); } -struct CurveComparator { - bool operator() (TempoCurve const * a, TempoCurve const * b) { - return a->tempo().sclock() < b->tempo().sclock(); - } -}; - void Editor::reassociate_metric_markers (TempoMap::SharedPtr const & tmap) { TempoMap::Metrics metrics; tmap->get_metrics (metrics); - for (auto & marker : metric_marks) { - reassociate_metric_marker (tmap, metrics, *marker); + for (auto & m : tempo_marks) { + reassociate_metric_marker (tmap, metrics, *m); + } + for (auto & m : meter_marks) { + reassociate_metric_marker (tmap, metrics, *m); + } + for (auto & m : bbt_marks) { + reassociate_metric_marker (tmap, metrics, *m); } } void -Editor::reassociate_metric_marker (TempoMap::SharedPtr const & tmap, TempoMap::Metrics & metrics, ArdourMarker& marker) +Editor::reassociate_metric_marker (TempoMap::SharedPtr const & tmap, TempoMap::Metrics & metrics, MetricMarker& marker) { TempoMarker* tm; MeterMarker* mm; @@ -157,70 +161,266 @@ Editor::reassociate_metric_marker (TempoMap::SharedPtr const & tmap, TempoMap::M } void -Editor::draw_metric_marks (TempoMap::Metrics const & metrics) +Editor::make_bbt_marker (MusicTimePoint const * mtp) +{ + if (mtp->map().time_domain() == BeatTime) { + bbt_marks.push_back (new BBTMarker (*this, *bbt_ruler, UIConfiguration::instance().color ("meter marker music"), "bar!", *mtp)); + } else { + bbt_marks.push_back (new BBTMarker (*this, *bbt_ruler, UIConfiguration::instance().color ("meter marker"), "foo!", *mtp)); + } +} + +void +Editor::make_meter_marker (Temporal::MeterPoint const * ms) +{ + char buf[64]; + + snprintf (buf, sizeof(buf), "%d/%d", ms->divisions_per_bar(), ms->note_value ()); + if (ms->map().time_domain() == BeatTime) { + meter_marks.push_back (new MeterMarker (*this, *meter_group, UIConfiguration::instance().color ("meter marker music"), buf, *ms)); + } else { + meter_marks.push_back (new MeterMarker (*this, *meter_group, UIConfiguration::instance().color ("meter marker"), buf, *ms)); + } +} + +void +Editor::make_tempo_marker (Temporal::TempoPoint const * ts, double& min_tempo, double& max_tempo, TempoPoint const *& prev_ts, uint32_t tc_color, samplecnt_t sr) +{ + max_tempo = max (max_tempo, ts->note_types_per_minute()); + max_tempo = max (max_tempo, ts->end_note_types_per_minute()); + min_tempo = min (min_tempo, ts->note_types_per_minute()); + min_tempo = min (min_tempo, ts->end_note_types_per_minute()); + + const std::string tname (X_("")); + char const * color_name; + + /* XXX not sure this is the right thing to do here (differentiate time + * domains with color). + */ + + if (ts->map().time_domain() == BeatTime) { + color_name = X_("tempo marker music"); + } else { + color_name = X_("tempo marker music"); + } + + tempo_marks.push_back (new TempoMarker (*this, *tempo_group, UIConfiguration::instance().color (color_name), tname, *ts, ts->sample (sr), tc_color)); + + /* XXX the point of this code was "a jump in tempo by more than 1 ntpm results in a red + tempo mark pointer." (3a7bc1fd3f32f0) + */ + + if (prev_ts && abs (prev_ts->end_note_types_per_minute() - ts->note_types_per_minute()) < 1.0) { + tempo_marks.back()->set_points_color (UIConfiguration::instance().color ("tempo marker music")); + } else { + tempo_marks.back()->set_points_color (UIConfiguration::instance().color ("tempo marker")); + } + + prev_ts = ts; +} + +void +Editor::draw_metric_marks (Temporal::TempoMap::Metrics const &) +{ + draw_tempo_marks (); + draw_meter_marks (); + draw_bbt_marks (); +} + +void +Editor::draw_tempo_marks () { if (!_session) { return; } - char buf[64]; - TempoPoint* prev_ts = 0; + const uint32_t tc_color = UIConfiguration::instance().color ("tempo curve"); + const samplecnt_t sr (_session->sample_rate()); + TempoPoint const * prev_ts = 0; + Temporal::TempoMap::SharedPtr tmap (TempoMap::use()); + TempoMap::Tempos const & tempi (tmap->tempos()); + TempoMap::Tempos::const_iterator t = tempi.begin(); + Marks::iterator mm = tempo_marks.begin(); double max_tempo = 0.0; double min_tempo = DBL_MAX; - const samplecnt_t sr (_session->sample_rate()); - remove_metric_marks (); // also clears tempo curves + std::cerr << "**** BEGIN DRAW TEMPO\n"; - for (TempoMap::Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) { - Temporal::MeterPoint *ms; - Temporal::TempoPoint *ts; - Temporal::MusicTimePoint *mtp; + while (t != tempi.end() && mm != tempo_marks.end()) { - /* must check MusicTimePoint first, since it IS-A TempoPoint - * and MeterPoint. - */ + Temporal::Point const & mark_point ((*mm)->point()); + Temporal::TempoPoint const & metric_point (*t); - if ((mtp = dynamic_cast(*i)) != 0) { + std::cerr << "\tmark @ " << mark_point.sclock() << " tempo @ " << metric_point.sclock() << std::endl; - if (mtp->map().time_domain() == BeatTime) { - metric_marks.push_back (new BBTMarker (*this, *bbt_ruler, UIConfiguration::instance().color ("meter marker music"), "bar!", *mtp)); - } else { - metric_marks.push_back (new BBTMarker (*this, *bbt_ruler, UIConfiguration::instance().color ("meter marker"), "foo!", *mtp)); - } - } else if ((ms = dynamic_cast(*i)) != 0) { - snprintf (buf, sizeof(buf), "%d/%d", ms->divisions_per_bar(), ms->note_value ()); - if (ms->map().time_domain() == BeatTime) { - metric_marks.push_back (new MeterMarker (*this, *meter_group, UIConfiguration::instance().color ("meter marker music"), buf, *ms)); - } else { - metric_marks.push_back (new MeterMarker (*this, *meter_group, UIConfiguration::instance().color ("meter marker"), buf, *ms)); - } - } else if ((ts = dynamic_cast(*i)) != 0) { - max_tempo = max (max_tempo, ts->note_types_per_minute()); - max_tempo = max (max_tempo, ts->end_note_types_per_minute()); - min_tempo = min (min_tempo, ts->note_types_per_minute()); - min_tempo = min (min_tempo, ts->end_note_types_per_minute()); - uint32_t const tc_color = UIConfiguration::instance().color ("tempo curve"); + if (mark_point.sclock() < metric_point.sclock()) { - tempo_curves.push_back (new TempoCurve (*this, *tempo_group, tc_color, *ts, ts->sample (sr), false)); + /* advance through markers, deleting the unused ones */ - const std::string tname (X_("")); - if (ts->map().time_domain() == BeatTime) { - metric_marks.push_back (new TempoMarker (*this, *tempo_group, UIConfiguration::instance().color ("tempo marker music"), tname, *ts)); + std::cerr << "\tDeleting marker that doesn't match a tempo point\n"; + delete *mm; + mm = tempo_marks.erase (mm); - } else { - metric_marks.push_back (new TempoMarker (*this, *tempo_group, UIConfiguration::instance().color ("tempo marker"), tname, *ts)); - } - if (prev_ts && abs (prev_ts->end_note_types_per_minute() - ts->note_types_per_minute()) < 1.0) { - metric_marks.back()->set_points_color (UIConfiguration::instance().color ("tempo marker music")); - } else { - metric_marks.back()->set_points_color (UIConfiguration::instance().color ("tempo marker")); - } - prev_ts = ts; + + } else if (metric_point.sclock() < mark_point.sclock()) { + + std::cerr << "\tCreating a marker for " << metric_point << " @ " << metric_point.sample (sr) << " next marker @ " << mark_point.sample (sr) << std::endl; + make_tempo_marker (&metric_point, min_tempo, max_tempo, prev_ts, tc_color, sr); + ++t; + + } else { + /* marker represents an existing point, update text, properties etc */ + /* XXX left/right text stuff */ + // (*mm)->set_name ((*m)->name()); + std::cerr << "\tMoving marker to " << t->time() << std::endl; + (*mm)->set_position (t->time()); + + max_tempo = max (max_tempo, t->note_types_per_minute()); + max_tempo = max (max_tempo, t->end_note_types_per_minute()); + min_tempo = min (min_tempo, t->note_types_per_minute()); + min_tempo = min (min_tempo, t->end_note_types_per_minute()); + + ++t; + ++mm; } - } - tempo_curves.sort (CurveComparator()); + if ((t == tempi.end()) && (mm != tempo_marks.end())) { + while (mm != tempo_marks.end()) { + std::cerr << "\tdrop excess tempo marker @ " << (*mm)->point().time() << std::endl; + delete *mm; + mm = tempo_marks.erase (mm); + } + } + + if ((mm == tempo_marks.end()) && (t != tempi.end())) { + while (t != tempi.end()) { + std::cerr << "\tmake new tempo marker @ " << t->time() << std::endl; + make_tempo_marker (&*t, min_tempo, max_tempo, prev_ts, tc_color, sr); + ++t; + } + } + + update_tempo_curves (min_tempo, max_tempo, sr); + + for (auto & m : tempo_marks) { + TempoMarker* tm = static_cast (m); + tm->update_height_mark ((tm->tempo().note_types_per_minute() - min_tempo) / max (10.0, max_tempo - min_tempo)); + std::cerr << "TEMPO:\n\t" << tm->tempo() << std::endl; + } +} + +void +Editor::draw_meter_marks () +{ + if (!_session) { + return; + } + + Temporal::TempoMap::SharedPtr tmap (TempoMap::use()); + TempoMap::Meters const & meters (tmap->meters()); + TempoMap::Meters::const_iterator m = meters.begin(); + Marks::iterator mm = meter_marks.begin(); + + while (m != meters.end() && mm != meter_marks.end()) { + + Temporal::Point const & mark_point ((*mm)->point()); + Temporal::MeterPoint const & metric_point (*m); + + if (mark_point.sclock() < metric_point.sclock()) { + + /* advance through markers, deleting the unused ones */ + + delete *mm; + mm = meter_marks.erase (mm); + + } else if (metric_point.sclock() < mark_point.sclock()) { + + make_meter_marker (&metric_point); + ++m; + + } else { + /* marker represents an existing point, update text, properties etc */ + /* XXX left/right text stuff */ + // (*mm)->set_name ((*m)->name()); + (*mm)->set_position (m->time()); + ++m; + ++mm; + } + } + + if ((m == meters.end()) && (mm != meter_marks.end())) { + while (mm != meter_marks.end()) { + delete *mm; + mm = meter_marks.erase (mm); + } + } + + if ((mm == tempo_marks.end()) && (m != meters.end())) { + while (m != meters.end()) { + make_meter_marker (&*m); + ++m; + } + } +} + +void +Editor::draw_bbt_marks () +{ + if (!_session) { + return; + } + + Temporal::TempoMap::SharedPtr tmap (TempoMap::use()); + TempoMap::MusicTimes const & bartimes (tmap->bartimes()); + TempoMap::MusicTimes::const_iterator m = bartimes.begin(); + Marks::iterator mm = bbt_marks.begin(); + + while (m != bartimes.end() && mm != bbt_marks.end()) { + + Temporal::Point const & mark_point ((*mm)->point()); + Temporal::MeterPoint const & metric_point (*m); + + if (mark_point.sclock() < metric_point.sclock()) { + + /* advance through markers, deleting the unused ones */ + + delete *mm; + mm = bbt_marks.erase (mm); + + } else if (metric_point.sclock() < mark_point.sclock()) { + + make_meter_marker (&metric_point); + ++m; + + } else { + /* marker represents an existing point, update text, properties etc */ + /* XXX left/right text stuff */ + // (*mm)->set_name ((*m)->name()); + (*mm)->set_position (m->time()); + ++m; + ++mm; + } + } + + if ((m == bartimes.end()) && (mm != bbt_marks.end())) { + while (mm != bbt_marks.end()) { + delete *mm; + mm = bbt_marks.erase (mm); + } + } + + if ((mm == tempo_marks.end()) && (m != bartimes.end())) { + while (m != bartimes.end()) { + make_meter_marker (&*m); + ++m; + } + } +} + +void +Editor::update_tempo_curves (double min_tempo, double max_tempo, samplecnt_t sr) +{ const double min_tempo_range = 5.0; const double tempo_delta = fabs (max_tempo - min_tempo); @@ -229,31 +429,28 @@ Editor::draw_metric_marks (TempoMap::Metrics const & metrics) min_tempo += tempo_delta - min_tempo_range; } - for (Curves::iterator x = tempo_curves.begin(); x != tempo_curves.end(); ) { - Curves::iterator tmp = x; - (*x)->set_max_tempo (max_tempo); - (*x)->set_min_tempo (min_tempo); + for (Marks::iterator m = tempo_marks.begin(); m != tempo_marks.end(); ++m) { + + TempoMarker* tm = static_cast(*m); + Marks::iterator tmp = m; ++tmp; - if (tmp != tempo_curves.end()) { - (*x)->set_position ((*x)->tempo().sample(sr), (*tmp)->tempo().sample(sr)); + + TempoCurve& curve (tm->curve()); + + curve.set_max_tempo (max_tempo); + curve.set_min_tempo (min_tempo); + + if (tmp != tempo_marks.end()) { + TempoMarker* nxt = static_cast(*tmp); + curve.set_duration (nxt->tempo().sample(sr) - tm->tempo().sample(sr)); } else { - (*x)->set_position ((*x)->tempo().sample(sr), UINT32_MAX); + curve.set_duration (samplecnt_t (UINT32_MAX)); } - if (!(*x)->tempo().active()) { - (*x)->hide(); + if (!tm->tempo().active()) { + curve.hide(); } else { - (*x)->show(); - } - - ++x; - } - - for (Marks::iterator x = metric_marks.begin(); x != metric_marks.end(); ++x) { - TempoMarker* tempo_marker; - - if ((tempo_marker = dynamic_cast (*x)) != 0) { - tempo_marker->update_height_mark ((tempo_marker->tempo().note_types_per_minute() - min_tempo) / max (10.0, max_tempo - min_tempo)); + curve.show(); } } } @@ -294,12 +491,13 @@ Editor::tempo_curve_selected (Temporal::TempoPoint const * ts, bool yn) return; } - for (Curves::iterator x = tempo_curves.begin(); x != tempo_curves.end(); ++x) { - if (&(*x)->tempo() == ts) { + for (Marks::iterator x = tempo_marks.begin(); x != tempo_marks.end(); ++x) { + TempoMarker* tm = static_cast (*x); + if (&tm->tempo() == ts) { if (yn) { - (*x)->set_color_rgba (UIConfiguration::instance().color ("location marker")); + tm->curve().set_color_rgba (UIConfiguration::instance().color ("location marker")); } else { - (*x)->set_color_rgba (UIConfiguration::instance().color ("tempo curve")); + tm->curve().set_color_rgba (UIConfiguration::instance().color ("tempo curve")); } break; } @@ -565,17 +763,17 @@ Editor::edit_tempo_section (TempoPoint& section) void Editor::edit_tempo_marker (TempoMarker& tm) { - edit_tempo_section (tm.tempo()); + edit_tempo_section (const_cast(tm.tempo())); } void Editor::edit_meter_marker (MeterMarker& mm) { - edit_meter_section (mm.meter()); + edit_meter_section (const_cast(mm.meter())); } gint -Editor::real_remove_tempo_marker (TempoPoint *section) +Editor::real_remove_tempo_marker (TempoPoint const * section) { begin_reversible_command (_("remove tempo mark")); TempoMap::SharedPtr tmap (TempoMap::write_copy()); @@ -612,7 +810,7 @@ Editor::remove_meter_marker (ArdourCanvas::Item* item) } gint -Editor::real_remove_meter_marker (Temporal::MeterPoint *section) +Editor::real_remove_meter_marker (Temporal::MeterPoint const * section) { begin_reversible_command (_("remove tempo mark")); TempoMap::SharedPtr tmap (TempoMap::write_copy()); @@ -651,3 +849,10 @@ Editor::commit_tempo_map_edit () TempoMap::SharedPtr tmap (TempoMap::use()); TempoMap::update (tmap); } + +void +Editor::mid_tempo_change () +{ + std::cerr << "============== MID TEMPO\n"; + draw_tempo_marks (); +} diff --git a/gtk2_ardour/marker.cc b/gtk2_ardour/marker.cc index fdda34b748..910fc2e79c 100644 --- a/gtk2_ardour/marker.cc +++ b/gtk2_ardour/marker.cc @@ -51,6 +51,7 @@ #include "public_editor.h" #include "utils.h" #include "rgb_macros.h" +#include "tempo_curve.h" #include @@ -535,6 +536,7 @@ void ArdourMarker::set_position (timepos_t const & pos) { unit_position = editor.sample_to_pixel (pos.samples()) - _shift; + cerr << "marker @ " << this << " set pos to " << unit_position << endl; group->set_x_position (unit_position); setup_line (); _position = pos; @@ -631,18 +633,32 @@ ArdourMarker::set_right_label_limit (double p) } } +MetricMarker::MetricMarker (PublicEditor& ed, ArdourCanvas::Item& parent, guint32 rgba, const string& annotation, + Type type, timepos_t const & pos, bool handle_events) + : ArdourMarker (ed, parent, rgba, annotation, type, pos, false) +{ +} + /***********************************************************************/ -TempoMarker::TempoMarker (PublicEditor& editor, ArdourCanvas::Item& parent, guint32 rgba, const string& text, - Temporal::TempoPoint& temp) - : ArdourMarker (editor, parent, rgba, text, Tempo, temp.time(), false) +TempoMarker::TempoMarker (PublicEditor& editor, ArdourCanvas::Item& parent, guint32 rgba, const string& text, Temporal::TempoPoint const & temp, samplepos_t sample, uint32_t curve_color) + : MetricMarker (editor, parent, rgba, text, Tempo, temp.time(), false) , _tempo (&temp) { group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_tempo_marker_event), group, this)); + /* points[1].x gives the width of the marker */ + _curve = new TempoCurve (editor, *group, curve_color, temp, true, (*points)[1].x); } TempoMarker::~TempoMarker () { + delete _curve; +} + +TempoCurve& +TempoMarker::curve() +{ + return *_curve; } void @@ -666,16 +682,21 @@ TempoMarker::update_height_mark (const double ratio) } void -TempoMarker::reset_tempo (Temporal::TempoPoint & t) +TempoMarker::reset_tempo (Temporal::TempoPoint const & t) { _tempo = &t; } +Temporal::Point const & +TempoMarker::point() const +{ + return *_tempo; +} /***********************************************************************/ -MeterMarker::MeterMarker (PublicEditor& editor, ArdourCanvas::Item& parent, guint32 rgba, const string& text, Temporal::MeterPoint& m) - : ArdourMarker (editor, parent, rgba, text, Meter, m.time(), false) +MeterMarker::MeterMarker (PublicEditor& editor, ArdourCanvas::Item& parent, guint32 rgba, const string& text, Temporal::MeterPoint const & m) + : MetricMarker (editor, parent, rgba, text, Meter, m.time(), false) , _meter (&m) { group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_meter_marker_event), group, this)); @@ -686,15 +707,21 @@ MeterMarker::~MeterMarker () } void -MeterMarker::reset_meter (Temporal::MeterPoint & m) +MeterMarker::reset_meter (Temporal::MeterPoint const & m) { _meter = &m; } +Temporal::Point const & +MeterMarker::point() const +{ + return *_meter; +} + /***********************************************************************/ -BBTMarker::BBTMarker (PublicEditor& editor, ArdourCanvas::Item& parent, guint32 rgba, const string& text, Temporal::MusicTimePoint& p) - : ArdourMarker (editor, parent, rgba, text, BBTPosition, p.time(), false) +BBTMarker::BBTMarker (PublicEditor& editor, ArdourCanvas::Item& parent, guint32 rgba, const string& text, Temporal::MusicTimePoint const & p) + : MetricMarker (editor, parent, rgba, text, BBTPosition, p.time(), false) , _point (&p) { group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_bbt_marker_event), group, this)); @@ -705,7 +732,13 @@ BBTMarker::~BBTMarker () } void -BBTMarker::reset_point (Temporal::MusicTimePoint & p) +BBTMarker::reset_point (Temporal::MusicTimePoint const & p) { _point = &p; } + +Temporal::Point const & +BBTMarker::point() const +{ + return *_point; +} diff --git a/gtk2_ardour/marker.h b/gtk2_ardour/marker.h index 7cef154858..dd67b14780 100644 --- a/gtk2_ardour/marker.h +++ b/gtk2_ardour/marker.h @@ -36,6 +36,7 @@ #include "canvas/types.h" namespace Temporal { + class Point; class TempoPoint; class MeterPoint; class MusicTimePoint; @@ -43,6 +44,7 @@ namespace Temporal { class PublicEditor; class RegionView; +class TempoCurve; /** Location Marker * @@ -156,47 +158,60 @@ private: ArdourMarker & operator= (ArdourMarker const &); }; -class TempoMarker : public ArdourMarker +class MetricMarker : public ArdourMarker { public: - TempoMarker (PublicEditor& editor, ArdourCanvas::Item &, guint32 rgba, const std::string& text, Temporal::TempoPoint&); + MetricMarker (PublicEditor& ed, ArdourCanvas::Item& parent, guint32 rgba, const std::string& annotation, Type type, Temporal::timepos_t const & pos, bool handle_events); + virtual Temporal::Point const & point() const = 0; +}; + +class TempoMarker : public MetricMarker +{ + public: + TempoMarker (PublicEditor& editor, ArdourCanvas::Item &, guint32 rgba, const std::string& text, Temporal::TempoPoint const &, samplepos_t sample, uint32_t curve_color); ~TempoMarker (); - void reset_tempo (Temporal::TempoPoint & t); + void reset_tempo (Temporal::TempoPoint const & t); - Temporal::TempoPoint& tempo() const { return *_tempo; } + Temporal::TempoPoint const & tempo() const { return *_tempo; } + Temporal::Point const & point() const; void update_height_mark (const double ratio); + TempoCurve& curve(); + private: - Temporal::TempoPoint* _tempo; + Temporal::TempoPoint const * _tempo; + TempoCurve* _curve; }; -class MeterMarker : public ArdourMarker +class MeterMarker : public MetricMarker { public: - MeterMarker (PublicEditor& editor, ArdourCanvas::Item &, guint32 rgba, const std::string& text, Temporal::MeterPoint&); + MeterMarker (PublicEditor& editor, ArdourCanvas::Item &, guint32 rgba, const std::string& text, Temporal::MeterPoint const &); ~MeterMarker (); - void reset_meter (Temporal::MeterPoint & m); + void reset_meter (Temporal::MeterPoint const & m); - Temporal::MeterPoint& meter() const { return *_meter; } + Temporal::MeterPoint const & meter() const { return *_meter; } + Temporal::Point const & point() const; private: - Temporal::MeterPoint* _meter; + Temporal::MeterPoint const * _meter; }; -class BBTMarker : public ArdourMarker +class BBTMarker : public MetricMarker { public: - BBTMarker (PublicEditor& editor, ArdourCanvas::Item &, guint32 rgba, const std::string& text, Temporal::MusicTimePoint&); + BBTMarker (PublicEditor& editor, ArdourCanvas::Item &, guint32 rgba, const std::string& text, Temporal::MusicTimePoint const &); ~BBTMarker (); - void reset_point (Temporal::MusicTimePoint &); + void reset_point (Temporal::MusicTimePoint const &); - Temporal::MusicTimePoint& point() const { return *_point; } + Temporal::MusicTimePoint const & mt_point() const { return *_point; } + Temporal::Point const & point() const; private: - Temporal::MusicTimePoint* _point; + Temporal::MusicTimePoint const * _point; }; #endif /* __gtk_ardour_marker_h__ */ diff --git a/gtk2_ardour/tempo_curve.cc b/gtk2_ardour/tempo_curve.cc index 739b6c7251..713e326eab 100644 --- a/gtk2_ardour/tempo_curve.cc +++ b/gtk2_ardour/tempo_curve.cc @@ -44,17 +44,21 @@ PBD::Signal1 TempoCurve::CatchDeletion; static double curve_height = 13.0; -void TempoCurve::setup_sizes(const double timebar_height) +void +TempoCurve::setup_sizes(const double timebar_height) { - curve_height = floor (timebar_height) - 2.5; + const double ui_scale = UIConfiguration::instance ().get_ui_scale (); + curve_height = floor (timebar_height) - (2.5 * ui_scale); } + /* ignores Tempo note type - only note_types_per_minute is potentially curved */ -TempoCurve::TempoCurve (PublicEditor& ed, ArdourCanvas::Container& parent, guint32 rgba, TempoPoint& temp, samplepos_t sample, bool handle_events) +TempoCurve::TempoCurve (PublicEditor& ed, ArdourCanvas::Item& parent, guint32 rgba, TempoPoint const & temp, bool handle_events, ArdourCanvas::Distance marker_width) : editor (ed) , _parent (&parent) , _curve (0) - , _shown (false) + , _duration (UINT32_MAX) + , _marker_width (marker_width) , _color (rgba) , _min_tempo (temp.note_types_per_minute()) , _max_tempo (temp.note_types_per_minute()) @@ -62,10 +66,8 @@ TempoCurve::TempoCurve (PublicEditor& ed, ArdourCanvas::Container& parent, guint , _start_text (0) , _end_text (0) { - sample_position = sample; - unit_position = editor.sample_to_pixel (sample); - - group = new ArdourCanvas::Container (&parent, ArdourCanvas::Duple (unit_position, 1)); + /* XXX x arg for Duple should probably be marker width, passed in from owner */ + group = new ArdourCanvas::Container (&parent, ArdourCanvas::Duple (marker_width, 1)); #ifdef CANVAS_DEBUG group->name = string_compose ("TempoCurve::group for %1", _tempo.note_types_per_minute()); #endif @@ -75,8 +77,7 @@ TempoCurve::TempoCurve (PublicEditor& ed, ArdourCanvas::Container& parent, guint _curve->name = string_compose ("TempoCurve::curve for %1", _tempo.note_types_per_minute()); #endif _curve->set_points_per_segment (3); - points = new ArdourCanvas::Points (); - _curve->set (*points); + _curve->set (points); _start_text = new ArdourCanvas::Text (group); _end_text = new ArdourCanvas::Text (group); @@ -116,18 +117,6 @@ TempoCurve::~TempoCurve () delete group; } -void TempoCurve::reparent(ArdourCanvas::Container & parent) -{ - group->reparent (&parent); - _parent = &parent; -} - -void -TempoCurve::canvas_height_set (double h) -{ - _canvas_height = h; -} - ArdourCanvas::Item& TempoCurve::the_item() const { @@ -135,42 +124,31 @@ TempoCurve::the_item() const } void -TempoCurve::set_position (samplepos_t sample, samplepos_t end_sample) +TempoCurve::set_duration (samplecnt_t duration) { - unit_position = editor.sample_to_pixel (sample); - group->set_x_position (unit_position); - sample_position = sample; - _end_sample = end_sample; + points.clear(); + points.push_back (ArdourCanvas::Duple (0.0, curve_height)); - points->clear(); - points = new ArdourCanvas::Points (); + ArdourCanvas::Coord duration_pixels = editor.sample_to_pixel (duration); - points->push_back (ArdourCanvas::Duple (0.0, curve_height)); + if (!_tempo.ramped()) { - if (sample >= end_sample) { - /* shouldn't happen but ..*/ const double tempo_at = _tempo.note_types_per_minute(); const double y_pos = (curve_height) - (((tempo_at - _min_tempo) / (_max_tempo - _min_tempo)) * curve_height); - points->push_back (ArdourCanvas::Duple (0.0, y_pos)); - points->push_back (ArdourCanvas::Duple (1.0, y_pos)); + points.push_back (ArdourCanvas::Duple (0.0, y_pos)); + points.push_back (ArdourCanvas::Duple (duration_pixels, y_pos)); - } else if (!_tempo.ramped()) { - const double tempo_at = _tempo.note_types_per_minute(); - const double y_pos = (curve_height) - (((tempo_at - _min_tempo) / (_max_tempo - _min_tempo)) * curve_height); - - points->push_back (ArdourCanvas::Duple (0.0, y_pos)); - points->push_back (ArdourCanvas::Duple (editor.sample_to_pixel (end_sample - sample), y_pos)); } else { - const samplepos_t sample_step = std::max ((end_sample - sample) / 5, (samplepos_t) 1); - samplepos_t current_sample = sample; + const samplepos_t sample_step = std::max ((duration) / 5, (samplepos_t) 1); + samplepos_t current_sample = 0; - while (current_sample < end_sample) { + while (current_sample < duration) { const double tempo_at = _tempo.note_types_per_minute_at_DOUBLE (timepos_t (current_sample)); const double y_pos = std::max ((curve_height) - (((tempo_at - _min_tempo) / (_max_tempo - _min_tempo)) * curve_height), 0.0); - points->push_back (ArdourCanvas::Duple (editor.sample_to_pixel (current_sample - sample), std::min (y_pos, curve_height))); + points.push_back (ArdourCanvas::Duple (editor.sample_to_pixel (current_sample), std::min (y_pos, curve_height))); current_sample += sample_step; } @@ -178,48 +156,49 @@ TempoCurve::set_position (samplepos_t sample, samplepos_t end_sample) const double tempo_at = _tempo.note_types_per_minute(); const double y_pos = std::max ((curve_height) - (((tempo_at - _min_tempo) / (_max_tempo - _min_tempo)) * curve_height), 0.0); - points->push_back (ArdourCanvas::Duple (editor.sample_to_pixel (end_sample - sample), std::min (y_pos, curve_height))); + points.push_back (ArdourCanvas::Duple (duration_pixels, std::min (y_pos, curve_height))); } - _curve->set (*points); + _curve->set (points); char buf[10]; + snprintf (buf, sizeof (buf), "%.3f/%d", _tempo.note_types_per_minute(), _tempo.note_type()); _start_text->set (buf); snprintf (buf, sizeof (buf), "%.3f", _tempo.end_note_types_per_minute()); _end_text->set (buf); - _start_text->set_position (ArdourCanvas::Duple (10, .5 )); - _end_text->set_position (ArdourCanvas::Duple (editor.sample_to_pixel (end_sample - sample) - _end_text->text_width() - 10, .5 )); + const double ui_scale = UIConfiguration::instance ().get_ui_scale (); - if (_end_text->text_width() + _start_text->text_width() + 20 > editor.sample_to_pixel (end_sample - sample)) { + _start_text->set_position (ArdourCanvas::Duple (_marker_width + (10 * ui_scale), (.5 * ui_scale))); + _end_text->set_position (ArdourCanvas::Duple (duration_pixels - _end_text->text_width() - _marker_width - (10. * ui_scale), (.5 * ui_scale))); + + if (_end_text->text_width() + _start_text->text_width() + (20.0 * ui_scale) > duration_pixels) { _start_text->hide(); _end_text->hide(); } else { _start_text->show(); _end_text->show(); } + + _duration = duration; } void TempoCurve::reposition () { - set_position (sample_position, _end_sample); + set_duration (_duration); } void TempoCurve::show () { - _shown = true; - group->show (); } void TempoCurve::hide () { - _shown = false; - group->hide (); } diff --git a/gtk2_ardour/tempo_curve.h b/gtk2_ardour/tempo_curve.h index 956426d3c4..272769858e 100644 --- a/gtk2_ardour/tempo_curve.h +++ b/gtk2_ardour/tempo_curve.h @@ -40,7 +40,7 @@ class PublicEditor; class TempoCurve : public sigc::trackable { public: - TempoCurve (PublicEditor& editor, ArdourCanvas::Container &, guint32 rgba, Temporal::TempoPoint& temp, samplepos_t sample, bool handle_events); + TempoCurve (PublicEditor& editor, ArdourCanvas::Item &, guint32 rgba, Temporal::TempoPoint const & temp, bool handle_events , ArdourCanvas::Distance marker_width); ~TempoCurve (); static PBD::Signal1 CatchDeletion; @@ -48,19 +48,14 @@ public: static void setup_sizes (const double timebar_height); ArdourCanvas::Item& the_item() const; - void canvas_height_set (double); - void set_position (samplepos_t lower, samplepos_t upper); + void set_duration (ARDOUR::samplecnt_t duration); void set_color_rgba (uint32_t rgba); - samplepos_t position() const { return sample_position; } - - ArdourCanvas::Container* get_parent() { return _parent; } - void reparent (ArdourCanvas::Container& parent); void hide (); void show (); - Temporal::TempoPoint& tempo () const { return _tempo; } + Temporal::TempoPoint const & tempo () const { return _tempo; } void set_max_tempo (const double& max) { _max_tempo = max; } void set_min_tempo (const double& min) { _min_tempo = min; } @@ -68,17 +63,14 @@ public: protected: PublicEditor& editor; - ArdourCanvas::Container* _parent; + ArdourCanvas::Item* _parent; ArdourCanvas::Container* group; - ArdourCanvas::Points* points; + ArdourCanvas::Points points; ArdourCanvas::FramedCurve* _curve; - double unit_position; - samplepos_t sample_position; - samplepos_t _end_sample; - bool _shown; - double _canvas_height; - uint32_t _color; + ARDOUR::samplecnt_t _duration; + ArdourCanvas::Distance _marker_width; + uint32_t _color; void reposition (); @@ -91,7 +83,7 @@ private: double _min_tempo; double _max_tempo; - Temporal::TempoPoint& _tempo; + Temporal::TempoPoint const & _tempo; ArdourCanvas::Text* _start_text; ArdourCanvas::Text* _end_text; };