2 Copyright (C) 2000 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
24 #include <cstdio> // for sprintf, grrr
30 #include <gtk/gtkaction.h>
32 #include "canvas/container.h"
33 #include "canvas/canvas.h"
34 #include "canvas/ruler.h"
35 #include "canvas/debug.h"
36 #include "canvas/scroll_group.h"
38 #include "ardour/session.h"
39 #include "ardour/tempo.h"
40 #include "ardour/profile.h"
42 #include "gtkmm2ext/gtk_ui.h"
43 #include "gtkmm2ext/keyboard.h"
45 #include "ardour_ui.h"
49 #include "gui_thread.h"
50 #include "ruler_dialog.h"
51 #include "time_axis_view.h"
52 #include "editor_drag.h"
53 #include "editor_cursors.h"
57 using namespace ARDOUR;
60 using namespace Editing;
62 /* the order here must match the "metric" enums in editor.h */
64 class TimecodeMetric : public ArdourCanvas::Ruler::Metric
67 TimecodeMetric (Editor* e) : _editor (e) {}
69 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
70 _editor->metric_get_timecode (marks, lower, upper, maxchars);
77 class SamplesMetric : public ArdourCanvas::Ruler::Metric
80 SamplesMetric (Editor* e) : _editor (e) {}
82 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
83 _editor->metric_get_samples (marks, lower, upper, maxchars);
90 class BBTMetric : public ArdourCanvas::Ruler::Metric
93 BBTMetric (Editor* e) : _editor (e) {}
95 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
96 _editor->metric_get_bbt (marks, lower, upper, maxchars);
103 class MinsecMetric : public ArdourCanvas::Ruler::Metric
106 MinsecMetric (Editor* e) : _editor (e) {}
108 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
109 _editor->metric_get_minsec (marks, lower, upper, maxchars);
116 static ArdourCanvas::Ruler::Metric* _bbt_metric;
117 static ArdourCanvas::Ruler::Metric* _timecode_metric;
118 static ArdourCanvas::Ruler::Metric* _samples_metric;
119 static ArdourCanvas::Ruler::Metric* _minsec_metric;
122 Editor::initialize_rulers ()
124 ruler_grabbed_widget = 0;
126 Pango::FontDescription font (ARDOUR_UI::config()->get_SmallerFont());
128 _timecode_metric = new TimecodeMetric (this);
129 _bbt_metric = new BBTMetric (this);
130 _minsec_metric = new MinsecMetric (this);
131 _samples_metric = new SamplesMetric (this);
133 timecode_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_timecode_metric,
134 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
135 timecode_ruler->set_font_description (font);
136 CANVAS_DEBUG_NAME (timecode_ruler, "timecode ruler");
139 samples_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_samples_metric,
140 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
141 samples_ruler->set_font_description (font);
142 CANVAS_DEBUG_NAME (samples_ruler, "samples ruler");
144 minsec_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_minsec_metric,
145 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
146 minsec_ruler->set_font_description (font);
147 CANVAS_DEBUG_NAME (minsec_ruler, "minsec ruler");
150 bbt_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_bbt_metric,
151 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
152 bbt_ruler->set_font_description (font);
153 CANVAS_DEBUG_NAME (bbt_ruler, "bbt ruler");
156 using namespace Box_Helpers;
157 BoxList & lab_children = time_bars_vbox.children();
159 lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
160 lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
161 lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
162 lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
163 lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
164 lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
165 lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
166 lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
167 lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
168 lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
169 lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
171 /* 1 event handler to bind them all ... */
173 timecode_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), timecode_ruler, TimecodeRulerItem));
174 minsec_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), minsec_ruler, MinsecRulerItem));
175 bbt_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), bbt_ruler, BBTRulerItem));
176 samples_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), samples_ruler, SamplesRulerItem));
178 visible_timebars = 0; /*this will be changed below */
182 Editor::ruler_label_button_release (GdkEventButton* ev)
184 if (Gtkmm2ext::Keyboard::is_context_menu_event (ev)) {
186 ruler_dialog = new RulerDialog ();
188 ruler_dialog->present ();
195 Editor::popup_ruler_menu (framepos_t where, ItemType t)
197 using namespace Menu_Helpers;
199 if (editor_ruler_menu == 0) {
200 editor_ruler_menu = new Menu;
201 editor_ruler_menu->set_name ("ArdourContextMenu");
204 // always build from scratch
205 MenuList& ruler_items = editor_ruler_menu->items();
206 editor_ruler_menu->set_name ("ArdourContextMenu");
211 ruler_items.push_back (MenuElem (_("New location marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, false, false)));
212 ruler_items.push_back (MenuElem (_("Clear all locations"), sigc::mem_fun(*this, &Editor::clear_markers)));
213 ruler_items.push_back (MenuElem (_("Unhide locations"), sigc::mem_fun(*this, &Editor::unhide_markers)));
214 ruler_items.push_back (SeparatorElem ());
216 case RangeMarkerBarItem:
217 ruler_items.push_back (MenuElem (_("New range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_range), where)));
218 ruler_items.push_back (MenuElem (_("Clear all ranges"), sigc::mem_fun(*this, &Editor::clear_ranges)));
219 ruler_items.push_back (MenuElem (_("Unhide ranges"), sigc::mem_fun(*this, &Editor::unhide_ranges)));
220 ruler_items.push_back (SeparatorElem ());
223 case TransportMarkerBarItem:
224 ruler_items.push_back (MenuElem (_("Make Loop range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_loop), where)));
225 ruler_items.push_back (MenuElem (_("Make Punch range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_punch), where)));
228 case CdMarkerBarItem:
230 ruler_items.push_back (MenuElem (_("New CD track marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, true, false)));
235 ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
239 ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
243 /* proper headings would be nice
244 * but AFAICT the only way to get them will be to define a
245 * special GTK style for insensitive Elements or subclass MenuItem
247 //ruler_items.push_back (MenuElem (_("Timeline height"))); // heading
248 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
249 ruler_items.push_back (CheckMenuElem (_("Large"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 6)));
250 if (videotl_bar_height == 6) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
251 ruler_items.push_back (CheckMenuElem (_("Normal"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 4)));
252 if (videotl_bar_height == 4) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
253 ruler_items.push_back (CheckMenuElem (_("Small"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 3)));
254 if (videotl_bar_height == 3) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
256 ruler_items.push_back (SeparatorElem ());
258 //ruler_items.push_back (MenuElem (_("Align Video Track"))); // heading
259 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
260 ruler_items.push_back (CheckMenuElem (_("Lock")));
262 Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
263 vtl_lock->set_active(is_video_timeline_locked());
264 vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
267 ruler_items.push_back (SeparatorElem ());
269 //ruler_items.push_back (MenuElem (_("Video Monitor"))); // heading
270 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
271 ruler_items.push_back (CheckMenuElem (_("Video Monitor")));
273 Gtk::CheckMenuItem* xjadeo_toggle = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
274 if (!ARDOUR_UI::instance()->video_timeline->found_xjadeo()) {
275 xjadeo_toggle->set_sensitive(false);
277 xjadeo_toggle->set_active(xjadeo_proc_action->get_active());
278 xjadeo_toggle->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &Editor::toggle_xjadeo_proc), -1));
286 if (!ruler_items.empty()) {
287 editor_ruler_menu->popup (1, gtk_get_current_event_time());
290 no_ruler_shown_update = false;
294 Editor::store_ruler_visibility ()
296 XMLNode* node = new XMLNode(X_("RulerVisibility"));
298 node->add_property (X_("timecode"), ruler_timecode_action->get_active() ? "yes": "no");
299 node->add_property (X_("bbt"), ruler_bbt_action->get_active() ? "yes": "no");
300 node->add_property (X_("samples"), ruler_samples_action->get_active() ? "yes": "no");
301 node->add_property (X_("minsec"), ruler_minsec_action->get_active() ? "yes": "no");
302 node->add_property (X_("tempo"), ruler_tempo_action->get_active() ? "yes": "no");
303 node->add_property (X_("meter"), ruler_meter_action->get_active() ? "yes": "no");
304 node->add_property (X_("marker"), ruler_marker_action->get_active() ? "yes": "no");
305 node->add_property (X_("rangemarker"), ruler_range_action->get_active() ? "yes": "no");
306 node->add_property (X_("transportmarker"), ruler_loop_punch_action->get_active() ? "yes": "no");
307 node->add_property (X_("cdmarker"), ruler_cd_marker_action->get_active() ? "yes": "no");
308 node->add_property (X_("videotl"), ruler_video_action->get_active() ? "yes": "no");
310 _session->add_extra_xml (*node);
311 _session->set_dirty ();
315 Editor::restore_ruler_visibility ()
318 XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
320 no_ruler_shown_update = true;
323 if ((prop = node->property ("timecode")) != 0) {
324 if (string_is_affirmative (prop->value())) {
325 ruler_timecode_action->set_active (true);
327 ruler_timecode_action->set_active (false);
330 if ((prop = node->property ("bbt")) != 0) {
331 if (string_is_affirmative (prop->value())) {
332 ruler_bbt_action->set_active (true);
334 ruler_bbt_action->set_active (false);
337 if ((prop = node->property ("samples")) != 0) {
338 if (string_is_affirmative (prop->value())) {
339 ruler_samples_action->set_active (true);
341 ruler_samples_action->set_active (false);
344 if ((prop = node->property ("minsec")) != 0) {
345 if (string_is_affirmative (prop->value())) {
346 ruler_minsec_action->set_active (true);
348 ruler_minsec_action->set_active (false);
351 if ((prop = node->property ("tempo")) != 0) {
352 if (string_is_affirmative (prop->value())) {
353 ruler_tempo_action->set_active (true);
355 ruler_tempo_action->set_active (false);
358 if ((prop = node->property ("meter")) != 0) {
359 if (string_is_affirmative (prop->value())) {
360 ruler_meter_action->set_active (true);
362 ruler_meter_action->set_active (false);
365 if ((prop = node->property ("marker")) != 0) {
366 if (string_is_affirmative (prop->value())) {
367 ruler_marker_action->set_active (true);
369 ruler_marker_action->set_active (false);
372 if ((prop = node->property ("rangemarker")) != 0) {
373 if (string_is_affirmative (prop->value())) {
374 ruler_range_action->set_active (true);
376 ruler_range_action->set_active (false);
380 if ((prop = node->property ("transportmarker")) != 0) {
381 if (string_is_affirmative (prop->value())) {
382 ruler_loop_punch_action->set_active (true);
384 ruler_loop_punch_action->set_active (false);
388 if ((prop = node->property ("cdmarker")) != 0) {
389 if (string_is_affirmative (prop->value())) {
390 ruler_cd_marker_action->set_active (true);
392 ruler_cd_marker_action->set_active (false);
396 // this _session doesn't yet know about the cdmarker ruler
397 // as a benefit to the user who doesn't know the feature exists, show the ruler if
398 // any cd marks exist
399 ruler_cd_marker_action->set_active (false);
400 const Locations::LocationList & locs = _session->locations()->list();
401 for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
402 if ((*i)->is_cd_marker()) {
403 ruler_cd_marker_action->set_active (true);
409 if ((prop = node->property ("videotl")) != 0) {
410 if (string_is_affirmative (prop->value())) {
411 ruler_video_action->set_active (true);
413 ruler_video_action->set_active (false);
419 no_ruler_shown_update = false;
420 update_ruler_visibility ();
424 Editor::update_ruler_visibility ()
426 int visible_timebars = 0;
428 if (no_ruler_shown_update) {
432 /* the order of the timebars is fixed, so we have to go through each one
433 * and adjust its position depending on what is shown.
435 * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
436 * loop/punch, cd markers, location markers
444 /* gtk update probs require this (damn) */
447 range_mark_label.hide();
448 transport_mark_label.hide();
449 cd_mark_label.hide();
451 videotl_label.hide();
454 if (ruler_minsec_action->get_active()) {
455 old_unit_pos = minsec_ruler->position().y;
456 if (tbpos != old_unit_pos) {
457 minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
459 minsec_ruler->show();
461 tbpos += timebar_height;
462 tbgpos += timebar_height;
465 minsec_ruler->hide();
469 if (ruler_timecode_action->get_active()) {
470 old_unit_pos = timecode_ruler->position().y;
471 if (tbpos != old_unit_pos) {
472 timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
474 timecode_ruler->show();
475 timecode_label.show();
476 tbpos += timebar_height;
477 tbgpos += timebar_height;
480 timecode_ruler->hide();
481 timecode_label.hide();
484 if (ruler_samples_action->get_active()) {
485 old_unit_pos = samples_ruler->position().y;
486 if (tbpos != old_unit_pos) {
487 samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
489 samples_ruler->show();
490 samples_label.show();
491 tbpos += timebar_height;
492 tbgpos += timebar_height;
495 samples_ruler->hide();
496 samples_label.hide();
499 if (ruler_bbt_action->get_active()) {
500 old_unit_pos = bbt_ruler->position().y;
501 if (tbpos != old_unit_pos) {
502 bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
506 tbpos += timebar_height;
507 tbgpos += timebar_height;
514 if (ruler_meter_action->get_active()) {
515 old_unit_pos = meter_group->position().y;
516 if (tbpos != old_unit_pos) {
517 meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
521 tbpos += timebar_height;
522 tbgpos += timebar_height;
529 if (ruler_tempo_action->get_active()) {
530 old_unit_pos = tempo_group->position().y;
531 if (tbpos != old_unit_pos) {
532 tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
536 tbpos += timebar_height;
537 tbgpos += timebar_height;
544 if (!Profile->get_sae() && ruler_range_action->get_active()) {
545 old_unit_pos = range_marker_group->position().y;
546 if (tbpos != old_unit_pos) {
547 range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
549 range_marker_group->show();
550 range_mark_label.show();
552 tbpos += timebar_height;
553 tbgpos += timebar_height;
556 range_marker_group->hide();
557 range_mark_label.hide();
560 if (ruler_loop_punch_action->get_active()) {
561 old_unit_pos = transport_marker_group->position().y;
562 if (tbpos != old_unit_pos) {
563 transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
565 transport_marker_group->show();
566 transport_mark_label.show();
567 tbpos += timebar_height;
568 tbgpos += timebar_height;
571 transport_marker_group->hide();
572 transport_mark_label.hide();
575 if (ruler_cd_marker_action->get_active()) {
576 old_unit_pos = cd_marker_group->position().y;
577 if (tbpos != old_unit_pos) {
578 cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
580 cd_marker_group->show();
581 cd_mark_label.show();
582 tbpos += timebar_height;
583 tbgpos += timebar_height;
585 // make sure all cd markers show up in their respective places
586 update_cd_marker_display();
588 cd_marker_group->hide();
589 cd_mark_label.hide();
590 // make sure all cd markers show up in their respective places
591 update_cd_marker_display();
594 if (ruler_marker_action->get_active()) {
595 old_unit_pos = marker_group->position().y;
596 if (tbpos != old_unit_pos) {
597 marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
599 marker_group->show();
601 tbpos += timebar_height;
602 tbgpos += timebar_height;
605 marker_group->hide();
609 if (ruler_video_action->get_active()) {
610 old_unit_pos = videotl_group->position().y;
611 if (tbpos != old_unit_pos) {
612 videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
614 videotl_group->show();
615 videotl_label.show();
616 tbpos += timebar_height * videotl_bar_height;
617 tbgpos += timebar_height * videotl_bar_height;
618 visible_timebars+=videotl_bar_height;
619 queue_visual_videotimeline_update();
621 videotl_group->hide();
622 videotl_label.hide();
623 update_video_timeline(true);
626 time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
628 /* move hv_scroll_group (trackviews) to the end of the timebars
631 hv_scroll_group->set_y_position (timebar_height * visible_timebars);
633 compute_fixed_ruler_scale ();
634 update_fixed_rulers();
635 redisplay_tempo (false);
637 /* Changing ruler visibility means that any lines on markers might need updating */
638 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
639 i->second->setup_lines ();
644 Editor::update_just_timecode ()
646 ENSURE_GUI_THREAD (*this, &Editor::update_just_timecode)
652 framepos_t rightmost_frame = leftmost_frame + current_page_samples();
654 if (ruler_timecode_action->get_active()) {
655 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
660 Editor::compute_fixed_ruler_scale ()
666 if (ruler_timecode_action->get_active()) {
667 set_timecode_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
670 if (ruler_minsec_action->get_active()) {
671 set_minsec_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
674 if (ruler_samples_action->get_active()) {
675 set_samples_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
680 Editor::update_fixed_rulers ()
682 framepos_t rightmost_frame;
688 compute_fixed_ruler_scale ();
690 _timecode_metric->units_per_pixel = samples_per_pixel;
691 _samples_metric->units_per_pixel = samples_per_pixel;
692 _minsec_metric->units_per_pixel = samples_per_pixel;
694 rightmost_frame = leftmost_frame + current_page_samples();
696 /* these force a redraw, which in turn will force execution of the metric callbacks
697 to compute the relevant ticks to display.
700 if (ruler_timecode_action->get_active()) {
701 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
704 if (ruler_samples_action->get_active()) {
705 samples_ruler->set_range (leftmost_frame, rightmost_frame);
708 if (ruler_minsec_action->get_active()) {
709 minsec_ruler->set_range (leftmost_frame, rightmost_frame);
714 Editor::update_tempo_based_rulers (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
715 ARDOUR::TempoMap::BBTPointList::const_iterator& end)
721 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame+current_page_samples(),
724 _bbt_metric->units_per_pixel = samples_per_pixel;
726 if (ruler_bbt_action->get_active()) {
727 bbt_ruler->set_range (leftmost_frame, leftmost_frame+current_page_samples());
733 Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
744 fr = _session->frame_rate();
746 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
747 lower = lower - spacer;
752 upper = upper + spacer;
753 framecnt_t const range = upper - lower;
755 if (range < (2 * _session->frames_per_timecode_frame())) { /* 0 - 2 frames */
756 timecode_ruler_scale = timecode_show_bits;
757 timecode_mark_modulo = 20;
758 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
759 } else if (range <= (fr / 4)) { /* 2 frames - 0.250 second */
760 timecode_ruler_scale = timecode_show_frames;
761 timecode_mark_modulo = 1;
762 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
763 } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
764 timecode_ruler_scale = timecode_show_frames;
765 timecode_mark_modulo = 2;
766 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
767 } else if (range <= fr) { /* 0.5-1 second */
768 timecode_ruler_scale = timecode_show_frames;
769 timecode_mark_modulo = 5;
770 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
771 } else if (range <= 2 * fr) { /* 1-2 seconds */
772 timecode_ruler_scale = timecode_show_frames;
773 timecode_mark_modulo = 10;
774 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
775 } else if (range <= 8 * fr) { /* 2-8 seconds */
776 timecode_ruler_scale = timecode_show_seconds;
777 timecode_mark_modulo = 1;
778 timecode_nmarks = 2 + (range / fr);
779 } else if (range <= 16 * fr) { /* 8-16 seconds */
780 timecode_ruler_scale = timecode_show_seconds;
781 timecode_mark_modulo = 2;
782 timecode_nmarks = 2 + (range / fr);
783 } else if (range <= 30 * fr) { /* 16-30 seconds */
784 timecode_ruler_scale = timecode_show_seconds;
785 timecode_mark_modulo = 5;
786 timecode_nmarks = 2 + (range / fr);
787 } else if (range <= 60 * fr) { /* 30-60 seconds */
788 timecode_ruler_scale = timecode_show_seconds;
789 timecode_mark_modulo = 5;
790 timecode_nmarks = 2 + (range / fr);
791 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
792 timecode_ruler_scale = timecode_show_seconds;
793 timecode_mark_modulo = 15;
794 timecode_nmarks = 2 + (range / fr);
795 } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
796 timecode_ruler_scale = timecode_show_seconds;
797 timecode_mark_modulo = 30;
798 timecode_nmarks = 2 + (range / fr);
799 } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
800 timecode_ruler_scale = timecode_show_minutes;
801 timecode_mark_modulo = 2;
802 timecode_nmarks = 2 + 10;
803 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
804 timecode_ruler_scale = timecode_show_minutes;
805 timecode_mark_modulo = 5;
806 timecode_nmarks = 2 + 30;
807 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
808 timecode_ruler_scale = timecode_show_minutes;
809 timecode_mark_modulo = 10;
810 timecode_nmarks = 2 + 60;
811 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
812 timecode_ruler_scale = timecode_show_minutes;
813 timecode_mark_modulo = 30;
814 timecode_nmarks = 2 + (60 * 4);
815 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
816 timecode_ruler_scale = timecode_show_hours;
817 timecode_mark_modulo = 1;
818 timecode_nmarks = 2 + 8;
819 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
820 timecode_ruler_scale = timecode_show_hours;
821 timecode_mark_modulo = 1;
822 timecode_nmarks = 2 + 24;
825 const framecnt_t hours_in_range = range / (60 * 60 * fr);
826 const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
828 /* Normally we do not need to know anything about the width of the canvas
829 to set the ruler scale, because the caller has already determined
830 the width and set lower + upper arguments to this function to match that.
832 But in this case, where the range defined by lower and uppper can vary
833 substantially (basically anything from 24hrs+ to several billion years)
834 trying to decide which tick marks to show does require us to know
835 about the available width.
838 timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
839 timecode_ruler_scale = timecode_show_many_hours;
840 timecode_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
845 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
849 Timecode::Time timecode;
852 ArdourCanvas::Ruler::Mark mark;
858 if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
859 lower = lower - spacer;
864 pos = (framecnt_t) floor (lower);
866 switch (timecode_ruler_scale) {
867 case timecode_show_bits:
868 // Find timecode time of this sample (pos) with subframe accuracy
869 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */ );
870 for (n = 0; n < timecode_nmarks; n++) {
871 _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
872 if ((timecode.subframes % timecode_mark_modulo) == 0) {
873 if (timecode.subframes == 0) {
874 mark.style = ArdourCanvas::Ruler::Mark::Major;
875 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
877 mark.style = ArdourCanvas::Ruler::Mark::Minor;
878 snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
881 snprintf (buf, sizeof(buf)," ");
882 mark.style = ArdourCanvas::Ruler::Mark::Micro;
886 marks.push_back (mark);
887 // Increment subframes by one
888 Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
892 case timecode_show_frames:
893 // Find timecode time of this sample (pos)
894 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
895 // Go to next whole frame down
896 Timecode::frames_floor( timecode );
897 for (n = 0; n < timecode_nmarks; n++) {
898 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
899 if ((timecode.frames % timecode_mark_modulo) == 0) {
900 if (timecode.frames == 0) {
901 mark.style = ArdourCanvas::Ruler::Mark::Major;
903 mark.style = ArdourCanvas::Ruler::Mark::Minor;
906 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
908 snprintf (buf, sizeof(buf)," ");
909 mark.style = ArdourCanvas::Ruler::Mark::Micro;
913 marks.push_back (mark);
914 Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
918 case timecode_show_seconds:
919 // Find timecode time of this sample (pos)
920 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
921 // Go to next whole second down
922 Timecode::seconds_floor( timecode );
923 for (n = 0; n < timecode_nmarks; n++) {
924 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
925 if ((timecode.seconds % timecode_mark_modulo) == 0) {
926 if (timecode.seconds == 0) {
927 mark.style = ArdourCanvas::Ruler::Mark::Major;
930 mark.style = ArdourCanvas::Ruler::Mark::Minor;
933 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
935 snprintf (buf, sizeof(buf)," ");
936 mark.style = ArdourCanvas::Ruler::Mark::Micro;
940 marks.push_back (mark);
941 Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
945 case timecode_show_minutes:
946 //Find timecode time of this sample (pos)
947 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
948 // Go to next whole minute down
949 Timecode::minutes_floor( timecode );
950 for (n = 0; n < timecode_nmarks; n++) {
951 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
952 if ((timecode.minutes % timecode_mark_modulo) == 0) {
953 if (timecode.minutes == 0) {
954 mark.style = ArdourCanvas::Ruler::Mark::Major;
956 mark.style = ArdourCanvas::Ruler::Mark::Minor;
958 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
960 snprintf (buf, sizeof(buf)," ");
961 mark.style = ArdourCanvas::Ruler::Mark::Micro;
965 marks.push_back (mark);
966 Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
969 case timecode_show_hours:
970 // Find timecode time of this sample (pos)
971 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
972 // Go to next whole hour down
973 Timecode::hours_floor( timecode );
974 for (n = 0; n < timecode_nmarks; n++) {
975 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
976 if ((timecode.hours % timecode_mark_modulo) == 0) {
977 mark.style = ArdourCanvas::Ruler::Mark::Major;
978 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
980 snprintf (buf, sizeof(buf)," ");
981 mark.style = ArdourCanvas::Ruler::Mark::Micro;
985 marks.push_back (mark);
986 Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
989 case timecode_show_many_hours:
990 // Find timecode time of this sample (pos)
991 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
992 // Go to next whole hour down
993 Timecode::hours_floor (timecode);
995 for (n = 0; n < timecode_nmarks; ) {
996 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
997 if ((timecode.hours % timecode_mark_modulo) == 0) {
998 mark.style = ArdourCanvas::Ruler::Mark::Major;
999 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1001 mark.position = pos;
1002 marks.push_back (mark);
1005 /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
1006 and doing it 1 hour at a time is just stupid (and slow).
1008 timecode.hours += timecode_mark_modulo;
1015 Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
1016 ARDOUR::TempoMap::BBTPointList::const_iterator begin,
1017 ARDOUR::TempoMap::BBTPointList::const_iterator end)
1019 if (_session == 0) {
1023 TempoMap::BBTPointList::const_iterator i;
1024 Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
1026 _session->bbt_time (lower, lower_beat);
1027 _session->bbt_time (upper, upper_beat);
1030 bbt_accent_modulo = 1;
1031 bbt_bar_helper_on = false;
1035 bbt_ruler_scale = bbt_show_many;
1037 switch (_snap_type) {
1038 case SnapToBeatDiv2:
1039 bbt_beat_subdivision = 2;
1041 case SnapToBeatDiv3:
1042 bbt_beat_subdivision = 3;
1044 case SnapToBeatDiv4:
1045 bbt_beat_subdivision = 4;
1047 case SnapToBeatDiv5:
1048 bbt_beat_subdivision = 5;
1049 bbt_accent_modulo = 2; // XXX YIKES
1051 case SnapToBeatDiv6:
1052 bbt_beat_subdivision = 6;
1053 bbt_accent_modulo = 2; // XXX YIKES
1055 case SnapToBeatDiv7:
1056 bbt_beat_subdivision = 7;
1057 bbt_accent_modulo = 2; // XXX YIKES
1059 case SnapToBeatDiv8:
1060 bbt_beat_subdivision = 8;
1061 bbt_accent_modulo = 2;
1063 case SnapToBeatDiv10:
1064 bbt_beat_subdivision = 10;
1065 bbt_accent_modulo = 2; // XXX YIKES
1067 case SnapToBeatDiv12:
1068 bbt_beat_subdivision = 12;
1069 bbt_accent_modulo = 3;
1071 case SnapToBeatDiv14:
1072 bbt_beat_subdivision = 14;
1073 bbt_accent_modulo = 3; // XXX YIKES!
1075 case SnapToBeatDiv16:
1076 bbt_beat_subdivision = 16;
1077 bbt_accent_modulo = 4;
1079 case SnapToBeatDiv20:
1080 bbt_beat_subdivision = 20;
1081 bbt_accent_modulo = 5;
1083 case SnapToBeatDiv24:
1084 bbt_beat_subdivision = 24;
1085 bbt_accent_modulo = 6;
1087 case SnapToBeatDiv28:
1088 bbt_beat_subdivision = 28;
1089 bbt_accent_modulo = 7;
1091 case SnapToBeatDiv32:
1092 bbt_beat_subdivision = 32;
1093 bbt_accent_modulo = 8;
1095 case SnapToBeatDiv64:
1096 bbt_beat_subdivision = 64;
1097 bbt_accent_modulo = 8;
1099 case SnapToBeatDiv128:
1100 bbt_beat_subdivision = 128;
1101 bbt_accent_modulo = 8;
1104 bbt_beat_subdivision = 4;
1108 if (distance (begin, end) == 0) {
1114 if ((*i).beat >= (*begin).beat) {
1115 bbt_bars = (*i).bar - (*begin).bar;
1117 bbt_bars = (*i).bar - (*begin).bar - 1;
1119 beats = distance (begin, end) - bbt_bars;
1121 /* Only show the bar helper if there aren't many bars on the screen */
1122 if ((bbt_bars < 2) || (beats < 5)) {
1123 bbt_bar_helper_on = true;
1126 if (bbt_bars > 8192) {
1127 bbt_ruler_scale = bbt_show_many;
1128 } else if (bbt_bars > 1024) {
1129 bbt_ruler_scale = bbt_show_64;
1130 } else if (bbt_bars > 256) {
1131 bbt_ruler_scale = bbt_show_16;
1132 } else if (bbt_bars > 64) {
1133 bbt_ruler_scale = bbt_show_4;
1134 } else if (bbt_bars > 10) {
1135 bbt_ruler_scale = bbt_show_1;
1136 } else if (bbt_bars > 2) {
1137 bbt_ruler_scale = bbt_show_beats;
1138 } else if (bbt_bars > 0) {
1139 bbt_ruler_scale = bbt_show_ticks;
1141 bbt_ruler_scale = bbt_show_ticks_detail;
1144 if ((bbt_ruler_scale == bbt_show_ticks_detail) && (lower_beat.beats == upper_beat.beats) && (upper_beat.ticks - lower_beat.ticks <= Timecode::BBT_Time::ticks_per_beat / 4)) {
1145 bbt_ruler_scale = bbt_show_ticks_super_detail;
1150 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1152 ArdourCanvas::Ruler::Mark copy = marks.back();
1153 copy.label = newlabel;
1155 marks.push_back (copy);
1159 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1161 if (_session == 0) {
1165 TempoMap::BBTPointList::const_iterator i;
1170 Timecode::BBT_Time next_beat;
1171 framepos_t next_beat_pos;
1176 framepos_t frame_skip;
1177 double frame_skip_error;
1178 double bbt_position_of_helper;
1179 double accumulated_error;
1180 bool i_am_accented = false;
1181 bool helper_active = false;
1182 ArdourCanvas::Ruler::Mark mark;
1184 ARDOUR::TempoMap::BBTPointList::const_iterator begin;
1185 ARDOUR::TempoMap::BBTPointList::const_iterator end;
1187 compute_current_bbt_points (lower, upper, begin, end);
1189 if (distance (begin, end) == 0) {
1193 switch (bbt_ruler_scale) {
1195 case bbt_show_beats:
1196 beats = distance (begin, end);
1197 bbt_nmarks = beats + 2;
1200 mark.position = lower;
1201 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1202 marks.push_back (mark);
1204 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1206 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1207 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1208 edit_last_mark_label (marks, buf);
1209 helper_active = true;
1212 if ((*i).is_bar()) {
1213 mark.style = ArdourCanvas::Ruler::Mark::Major;
1214 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1215 } else if (((*i).beat % 2 == 1)) {
1216 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1219 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1223 mark.position = (*i).frame;
1224 marks.push_back (mark);
1230 case bbt_show_ticks:
1232 beats = distance (begin, end);
1233 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1235 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1237 // could do marks.assign() here to preallocate
1240 mark.position = lower;
1241 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1242 marks.push_back (mark);
1244 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1246 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1247 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1248 edit_last_mark_label (marks, buf);
1249 helper_active = true;
1252 if ((*i).is_bar()) {
1253 mark.style = ArdourCanvas::Ruler::Mark::Major;
1254 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1256 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1257 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1259 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1263 mark.position = (*i).frame;
1264 marks.push_back (mark);
1268 /* Add the tick marks */
1270 /* Find the next beat */
1271 next_beat.beats = (*i).beat;
1272 next_beat.bars = (*i).bar;
1273 next_beat.ticks = 0;
1275 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1276 next_beat.beats += 1;
1278 next_beat.bars += 1;
1279 next_beat.beats = 1;
1282 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1284 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1285 frame_skip_error -= frame_skip;
1286 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1288 pos = (*i).frame + frame_skip;
1289 accumulated_error = frame_skip_error;
1293 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1295 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1296 i_am_accented = true;
1301 /* Error compensation for float to framepos_t*/
1302 accumulated_error += frame_skip_error;
1303 if (accumulated_error > 1) {
1305 accumulated_error -= 1.0f;
1308 mark.position = pos;
1310 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1311 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1313 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1315 i_am_accented = false;
1316 marks.push_back (mark);
1323 case bbt_show_ticks_detail:
1325 beats = distance (begin, end);
1326 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1328 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1331 mark.position = lower;
1332 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1333 marks.push_back (mark);
1335 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1337 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1338 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1339 edit_last_mark_label (marks, buf);
1340 helper_active = true;
1343 if ((*i).is_bar()) {
1344 mark.style = ArdourCanvas::Ruler::Mark::Major;
1345 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1347 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1348 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1350 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1354 mark.position = (*i).frame;
1355 marks.push_back (mark);
1359 /* Add the tick marks */
1361 /* Find the next beat */
1363 next_beat.beats = (*i).beat;
1364 next_beat.bars = (*i).bar;
1366 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1367 next_beat.beats += 1;
1369 next_beat.bars += 1;
1370 next_beat.beats = 1;
1373 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1375 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1376 frame_skip_error -= frame_skip;
1377 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1379 pos = (*i).frame + frame_skip;
1380 accumulated_error = frame_skip_error;
1384 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1386 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1387 i_am_accented = true;
1390 if (i_am_accented && (pos > bbt_position_of_helper)){
1391 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1398 /* Error compensation for float to framepos_t*/
1399 accumulated_error += frame_skip_error;
1400 if (accumulated_error > 1) {
1402 accumulated_error -= 1.0f;
1405 mark.position = pos;
1407 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1408 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1410 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1412 i_am_accented = false;
1419 case bbt_show_ticks_super_detail:
1421 beats = distance (begin, end);
1422 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1424 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1427 mark.position = lower;
1428 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1429 marks.push_back (mark);
1431 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1433 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1434 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1435 edit_last_mark_label (marks, buf);
1436 helper_active = true;
1439 if ((*i).is_bar()) {
1440 mark.style = ArdourCanvas::Ruler::Mark::Major;
1441 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1443 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1444 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1446 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1450 mark.position = (*i).frame;
1451 marks.push_back (mark);
1455 /* Add the tick marks */
1457 /* Find the next beat */
1459 next_beat.beats = (*i).beat;
1460 next_beat.bars = (*i).bar;
1462 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1463 next_beat.beats += 1;
1465 next_beat.bars += 1;
1466 next_beat.beats = 1;
1469 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1471 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1472 frame_skip_error -= frame_skip;
1473 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1475 pos = (*i).frame + frame_skip;
1476 accumulated_error = frame_skip_error;
1480 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1482 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1483 i_am_accented = true;
1486 if (pos > bbt_position_of_helper) {
1487 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1494 /* Error compensation for float to framepos_t*/
1495 accumulated_error += frame_skip_error;
1496 if (accumulated_error > 1) {
1498 accumulated_error -= 1.0f;
1501 mark.position = pos;
1503 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1504 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1506 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1508 i_am_accented = false;
1509 marks.push_back (mark);
1518 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1519 mark.style = ArdourCanvas::Ruler::Mark::Major;
1521 mark.position = lower;
1522 marks.push_back (mark);
1526 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1527 for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
1528 if ((*i).is_bar()) {
1529 if ((*i).bar % 64 == 1) {
1530 if ((*i).bar % 256 == 1) {
1531 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1532 mark.style = ArdourCanvas::Ruler::Mark::Major;
1535 if ((*i).bar % 256 == 129) {
1536 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1538 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1542 mark.position = (*i).frame;
1543 marks.push_back (mark);
1551 bbt_nmarks = (bbt_bars / 16) + 1;
1552 for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
1553 if ((*i).is_bar()) {
1554 if ((*i).bar % 16 == 1) {
1555 if ((*i).bar % 64 == 1) {
1556 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1557 mark.style = ArdourCanvas::Ruler::Mark::Major;
1560 if ((*i).bar % 64 == 33) {
1561 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1563 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1567 mark.position = (*i).frame;
1568 marks.push_back (mark);
1576 bbt_nmarks = (bbt_bars / 4) + 1;
1577 for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
1578 if ((*i).is_bar()) {
1579 if ((*i).bar % 4 == 1) {
1580 if ((*i).bar % 16 == 1) {
1581 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1582 mark.style = ArdourCanvas::Ruler::Mark::Major;
1585 if ((*i).bar % 16 == 9) {
1586 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1588 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1592 mark.position = (*i).frame;
1593 marks.push_back (mark);
1602 bbt_nmarks = bbt_bars + 2;
1603 for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
1604 if ((*i).is_bar()) {
1605 if ((*i).bar % 4 == 1) {
1606 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1607 mark.style = ArdourCanvas::Ruler::Mark::Major;
1610 if ((*i).bar % 4 == 3) {
1611 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1613 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1617 mark.position = (*i).frame;
1618 marks.push_back (mark);
1628 Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
1630 _samples_ruler_interval = (upper - lower) / 5;
1634 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1637 framepos_t const ilower = (framepos_t) floor (lower);
1641 ArdourCanvas::Ruler::Mark mark;
1643 if (_session == 0) {
1648 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1649 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1651 mark.position = pos;
1652 mark.style = ArdourCanvas::Ruler::Mark::Major;
1653 marks.push_back (mark);
1658 sample_to_clock_parts ( framepos_t sample,
1659 framepos_t sample_rate,
1673 hrs = left / (sample_rate * 60 * 60 * 1000);
1674 left -= hrs * sample_rate * 60 * 60 * 1000;
1675 mins = left / (sample_rate * 60 * 1000);
1676 left -= mins * sample_rate * 60 * 1000;
1677 secs = left / (sample_rate * 1000);
1678 left -= secs * sample_rate * 1000;
1679 millisecs = left / sample_rate;
1681 *millisecs_p = millisecs;
1690 Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
1692 framepos_t fr = _session->frame_rate() * 1000;
1695 if (_session == 0) {
1700 /* to prevent 'flashing' */
1701 if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1707 framecnt_t const range = (upper - lower) * 1000;
1709 if (range <= (fr / 10)) { /* 0-0.1 second */
1710 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1711 minsec_ruler_scale = minsec_show_msecs;
1712 minsec_mark_modulo = 10;
1713 minsec_nmarks = 2 + (range / minsec_mark_interval);
1714 } else if (range <= (fr / 2)) { /* 0-0.5 second */
1715 minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1716 minsec_ruler_scale = minsec_show_msecs;
1717 minsec_mark_modulo = 100;
1718 minsec_nmarks = 2 + (range / minsec_mark_interval);
1719 } else if (range <= fr) { /* 0-1 second */
1720 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1721 minsec_ruler_scale = minsec_show_msecs;
1722 minsec_mark_modulo = 200;
1723 minsec_nmarks = 2 + (range / minsec_mark_interval);
1724 } else if (range <= 2 * fr) { /* 1-2 seconds */
1725 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1726 minsec_ruler_scale = minsec_show_msecs;
1727 minsec_mark_modulo = 500;
1728 minsec_nmarks = 2 + (range / minsec_mark_interval);
1729 } else if (range <= 8 * fr) { /* 2-5 seconds */
1730 minsec_mark_interval = fr / 5; /* show 2 seconds */
1731 minsec_ruler_scale = minsec_show_msecs;
1732 minsec_mark_modulo = 1000;
1733 minsec_nmarks = 2 + (range / minsec_mark_interval);
1734 } else if (range <= 16 * fr) { /* 8-16 seconds */
1735 minsec_mark_interval = fr; /* show 1 seconds */
1736 minsec_ruler_scale = minsec_show_seconds;
1737 minsec_mark_modulo = 2;
1738 minsec_nmarks = 2 + (range / minsec_mark_interval);
1739 } else if (range <= 30 * fr) { /* 10-30 seconds */
1740 minsec_mark_interval = fr; /* show 1 seconds */
1741 minsec_ruler_scale = minsec_show_seconds;
1742 minsec_mark_modulo = 5;
1743 minsec_nmarks = 2 + (range / minsec_mark_interval);
1744 } else if (range <= 60 * fr) { /* 30-60 seconds */
1745 minsec_mark_interval = fr; /* show 1 seconds */
1746 minsec_ruler_scale = minsec_show_seconds;
1747 minsec_mark_modulo = 5;
1748 minsec_nmarks = 2 + (range / minsec_mark_interval);
1749 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1750 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1751 minsec_ruler_scale = minsec_show_seconds;
1752 minsec_mark_modulo = 3;
1753 minsec_nmarks = 2 + (range / minsec_mark_interval);
1754 } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1755 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1756 minsec_ruler_scale = minsec_show_seconds;
1757 minsec_mark_modulo = 30;
1758 minsec_nmarks = 2 + (range / minsec_mark_interval);
1759 } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1760 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1761 minsec_ruler_scale = minsec_show_seconds;
1762 minsec_mark_modulo = 120;
1763 minsec_nmarks = 2 + (range / minsec_mark_interval);
1764 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1765 minsec_mark_interval = 60 * fr; /* show 1 minute */
1766 minsec_ruler_scale = minsec_show_minutes;
1767 minsec_mark_modulo = 5;
1768 minsec_nmarks = 2 + (range / minsec_mark_interval);
1769 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1770 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1771 minsec_ruler_scale = minsec_show_minutes;
1772 minsec_mark_modulo = 10;
1773 minsec_nmarks = 2 + (range / minsec_mark_interval);
1774 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1775 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1776 minsec_ruler_scale = minsec_show_minutes;
1777 minsec_mark_modulo = 30;
1778 minsec_nmarks = 2 + (range / minsec_mark_interval);
1779 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1780 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1781 minsec_ruler_scale = minsec_show_minutes;
1782 minsec_mark_modulo = 60;
1783 minsec_nmarks = 2 + (range / minsec_mark_interval);
1784 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1785 minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1786 minsec_ruler_scale = minsec_show_hours;
1787 minsec_mark_modulo = 2;
1788 minsec_nmarks = 2 + (range / minsec_mark_interval);
1791 const framecnt_t hours_in_range = range / (60 * 60 * fr);
1792 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1794 /* Normally we do not need to know anything about the width of the canvas
1795 to set the ruler scale, because the caller has already determined
1796 the width and set lower + upper arguments to this function to match that.
1798 But in this case, where the range defined by lower and uppper can vary
1799 substantially (anything from 24hrs+ to several billion years)
1800 trying to decide which tick marks to show does require us to know
1801 about the available width.
1804 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1805 minsec_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1806 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1807 minsec_ruler_scale = minsec_show_many_hours;
1812 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1816 long hrs, mins, secs, millisecs;
1819 ArdourCanvas::Ruler::Mark mark;
1821 if (_session == 0) {
1825 /* to prevent 'flashing' */
1826 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
1827 lower = lower - spacer;
1832 pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1834 switch (minsec_ruler_scale) {
1836 case minsec_show_msecs:
1837 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1838 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1839 if (millisecs % minsec_mark_modulo == 0) {
1840 if (millisecs == 0) {
1841 mark.style = ArdourCanvas::Ruler::Mark::Major;
1843 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1845 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1848 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1851 mark.position = pos/1000.0;
1852 marks.push_back (mark);
1856 case minsec_show_seconds:
1857 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1858 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1859 if (secs % minsec_mark_modulo == 0) {
1861 mark.style = ArdourCanvas::Ruler::Mark::Major;
1863 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1865 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1868 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1871 mark.position = pos/1000.0;
1872 marks.push_back (mark);
1876 case minsec_show_minutes:
1877 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1878 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1879 if (mins % minsec_mark_modulo == 0) {
1881 mark.style = ArdourCanvas::Ruler::Mark::Major;
1883 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1885 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1888 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1891 mark.position = pos/1000.0;
1892 marks.push_back (mark);
1896 case minsec_show_hours:
1897 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1898 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1899 if (hrs % minsec_mark_modulo == 0) {
1900 mark.style = ArdourCanvas::Ruler::Mark::Major;
1901 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1904 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1907 mark.position = pos/1000.0;
1908 marks.push_back (mark);
1912 case minsec_show_many_hours:
1913 for (n = 0; n < minsec_nmarks; ) {
1914 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1915 if (hrs % minsec_mark_modulo == 0) {
1916 mark.style = ArdourCanvas::Ruler::Mark::Major;
1917 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1919 mark.position = pos/1000.0;
1920 marks.push_back (mark);
1923 pos += minsec_mark_interval;