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"
54 #include "ui_config.h"
58 using namespace ARDOUR;
61 using namespace Editing;
63 /* the order here must match the "metric" enums in editor.h */
65 class TimecodeMetric : public ArdourCanvas::Ruler::Metric
68 TimecodeMetric (Editor* e) : _editor (e) {}
70 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
71 _editor->metric_get_timecode (marks, lower, upper, maxchars);
78 class SamplesMetric : public ArdourCanvas::Ruler::Metric
81 SamplesMetric (Editor* e) : _editor (e) {}
83 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
84 _editor->metric_get_samples (marks, lower, upper, maxchars);
91 class BBTMetric : public ArdourCanvas::Ruler::Metric
94 BBTMetric (Editor* e) : _editor (e) {}
96 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
97 _editor->metric_get_bbt (marks, lower, upper, maxchars);
104 class MinsecMetric : public ArdourCanvas::Ruler::Metric
107 MinsecMetric (Editor* e) : _editor (e) {}
109 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
110 _editor->metric_get_minsec (marks, lower, upper, maxchars);
117 static ArdourCanvas::Ruler::Metric* _bbt_metric;
118 static ArdourCanvas::Ruler::Metric* _timecode_metric;
119 static ArdourCanvas::Ruler::Metric* _samples_metric;
120 static ArdourCanvas::Ruler::Metric* _minsec_metric;
123 Editor::initialize_rulers ()
125 ruler_grabbed_widget = 0;
127 Pango::FontDescription font (UIConfiguration::instance().get_SmallerFont());
129 _timecode_metric = new TimecodeMetric (this);
130 _bbt_metric = new BBTMetric (this);
131 _minsec_metric = new MinsecMetric (this);
132 _samples_metric = new SamplesMetric (this);
134 timecode_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_timecode_metric,
135 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
136 timecode_ruler->set_font_description (font);
137 CANVAS_DEBUG_NAME (timecode_ruler, "timecode ruler");
140 samples_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_samples_metric,
141 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
142 samples_ruler->set_font_description (font);
143 CANVAS_DEBUG_NAME (samples_ruler, "samples ruler");
145 minsec_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_minsec_metric,
146 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
147 minsec_ruler->set_font_description (font);
148 CANVAS_DEBUG_NAME (minsec_ruler, "minsec ruler");
151 bbt_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_bbt_metric,
152 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
153 bbt_ruler->set_font_description (font);
154 CANVAS_DEBUG_NAME (bbt_ruler, "bbt ruler");
157 using namespace Box_Helpers;
158 BoxList & lab_children = time_bars_vbox.children();
160 lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
161 lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
162 lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
163 lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
164 lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
165 lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
166 lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
167 lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
168 lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
169 lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
170 lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
172 /* 1 event handler to bind them all ... */
174 timecode_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), timecode_ruler, TimecodeRulerItem));
175 minsec_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), minsec_ruler, MinsecRulerItem));
176 bbt_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), bbt_ruler, BBTRulerItem));
177 samples_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), samples_ruler, SamplesRulerItem));
179 visible_timebars = 0; /*this will be changed below */
183 Editor::ruler_label_button_release (GdkEventButton* ev)
185 if (Gtkmm2ext::Keyboard::is_context_menu_event (ev)) {
187 ruler_dialog = new RulerDialog ();
189 ruler_dialog->present ();
196 Editor::popup_ruler_menu (framepos_t where, ItemType t)
198 using namespace Menu_Helpers;
200 if (editor_ruler_menu == 0) {
201 editor_ruler_menu = new Menu;
202 editor_ruler_menu->set_name ("ArdourContextMenu");
205 // always build from scratch
206 MenuList& ruler_items = editor_ruler_menu->items();
207 editor_ruler_menu->set_name ("ArdourContextMenu");
212 ruler_items.push_back (MenuElem (_("New location marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, false)));
213 ruler_items.push_back (MenuElem (_("Clear all locations"), sigc::mem_fun(*this, &Editor::clear_markers)));
214 ruler_items.push_back (MenuElem (_("Unhide locations"), sigc::mem_fun(*this, &Editor::unhide_markers)));
217 case RangeMarkerBarItem:
218 ruler_items.push_back (MenuElem (_("New range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_range), where)));
219 ruler_items.push_back (MenuElem (_("Clear all ranges"), sigc::mem_fun(*this, &Editor::clear_ranges)));
220 ruler_items.push_back (MenuElem (_("Unhide ranges"), sigc::mem_fun(*this, &Editor::unhide_ranges)));
223 case TransportMarkerBarItem:
224 ruler_items.push_back (MenuElem (_("New Loop range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_loop), where)));
225 ruler_items.push_back (MenuElem (_("New 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)));
234 ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
238 ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
242 /* proper headings would be nice
243 * but AFAICT the only way to get them will be to define a
244 * special GTK style for insensitive Elements or subclass MenuItem
246 //ruler_items.push_back (MenuElem (_("Timeline height"))); // heading
247 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
248 ruler_items.push_back (CheckMenuElem (_("Large"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 6)));
249 if (videotl_bar_height == 6) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
250 ruler_items.push_back (CheckMenuElem (_("Normal"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 4)));
251 if (videotl_bar_height == 4) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
252 ruler_items.push_back (CheckMenuElem (_("Small"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 3)));
253 if (videotl_bar_height == 3) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
255 ruler_items.push_back (SeparatorElem ());
257 //ruler_items.push_back (MenuElem (_("Align Video Track"))); // heading
258 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
259 ruler_items.push_back (CheckMenuElem (_("Lock")));
261 Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
262 vtl_lock->set_active(is_video_timeline_locked());
263 vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
266 ruler_items.push_back (SeparatorElem ());
268 //ruler_items.push_back (MenuElem (_("Video Monitor"))); // heading
269 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
270 ruler_items.push_back (CheckMenuElem (_("Video Monitor")));
272 Gtk::CheckMenuItem* xjadeo_toggle = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
273 if (!ARDOUR_UI::instance()->video_timeline->found_xjadeo()) {
274 xjadeo_toggle->set_sensitive(false);
276 xjadeo_toggle->set_active(xjadeo_proc_action->get_active());
277 xjadeo_toggle->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &Editor::toggle_xjadeo_proc), -1));
285 if (!ruler_items.empty()) {
286 editor_ruler_menu->popup (1, gtk_get_current_event_time());
289 no_ruler_shown_update = false;
293 Editor::store_ruler_visibility ()
295 XMLNode* node = new XMLNode(X_("RulerVisibility"));
297 node->add_property (X_("timecode"), ruler_timecode_action->get_active() ? "yes": "no");
298 node->add_property (X_("bbt"), ruler_bbt_action->get_active() ? "yes": "no");
299 node->add_property (X_("samples"), ruler_samples_action->get_active() ? "yes": "no");
300 node->add_property (X_("minsec"), ruler_minsec_action->get_active() ? "yes": "no");
301 node->add_property (X_("tempo"), ruler_tempo_action->get_active() ? "yes": "no");
302 node->add_property (X_("meter"), ruler_meter_action->get_active() ? "yes": "no");
303 node->add_property (X_("marker"), ruler_marker_action->get_active() ? "yes": "no");
304 node->add_property (X_("rangemarker"), ruler_range_action->get_active() ? "yes": "no");
305 node->add_property (X_("transportmarker"), ruler_loop_punch_action->get_active() ? "yes": "no");
306 node->add_property (X_("cdmarker"), ruler_cd_marker_action->get_active() ? "yes": "no");
307 node->add_property (X_("videotl"), ruler_video_action->get_active() ? "yes": "no");
309 _session->add_extra_xml (*node);
310 _session->set_dirty ();
314 Editor::restore_ruler_visibility ()
316 XMLProperty const * prop;
317 XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
319 no_ruler_shown_update = true;
322 if ((prop = node->property ("timecode")) != 0) {
323 if (string_is_affirmative (prop->value())) {
324 ruler_timecode_action->set_active (true);
326 ruler_timecode_action->set_active (false);
329 if ((prop = node->property ("bbt")) != 0) {
330 if (string_is_affirmative (prop->value())) {
331 ruler_bbt_action->set_active (true);
333 ruler_bbt_action->set_active (false);
336 if ((prop = node->property ("samples")) != 0) {
337 if (string_is_affirmative (prop->value())) {
338 ruler_samples_action->set_active (true);
340 ruler_samples_action->set_active (false);
343 if ((prop = node->property ("minsec")) != 0) {
344 if (string_is_affirmative (prop->value())) {
345 ruler_minsec_action->set_active (true);
347 ruler_minsec_action->set_active (false);
350 if ((prop = node->property ("tempo")) != 0) {
351 if (string_is_affirmative (prop->value())) {
352 ruler_tempo_action->set_active (true);
354 ruler_tempo_action->set_active (false);
357 if ((prop = node->property ("meter")) != 0) {
358 if (string_is_affirmative (prop->value())) {
359 ruler_meter_action->set_active (true);
361 ruler_meter_action->set_active (false);
364 if ((prop = node->property ("marker")) != 0) {
365 if (string_is_affirmative (prop->value())) {
366 ruler_marker_action->set_active (true);
368 ruler_marker_action->set_active (false);
371 if ((prop = node->property ("rangemarker")) != 0) {
372 if (string_is_affirmative (prop->value())) {
373 ruler_range_action->set_active (true);
375 ruler_range_action->set_active (false);
379 if ((prop = node->property ("transportmarker")) != 0) {
380 if (string_is_affirmative (prop->value())) {
381 ruler_loop_punch_action->set_active (true);
383 ruler_loop_punch_action->set_active (false);
387 if ((prop = node->property ("cdmarker")) != 0) {
388 if (string_is_affirmative (prop->value())) {
389 ruler_cd_marker_action->set_active (true);
391 ruler_cd_marker_action->set_active (false);
395 // this _session doesn't yet know about the cdmarker ruler
396 // as a benefit to the user who doesn't know the feature exists, show the ruler if
397 // any cd marks exist
398 ruler_cd_marker_action->set_active (false);
399 const Locations::LocationList & locs = _session->locations()->list();
400 for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
401 if ((*i)->is_cd_marker()) {
402 ruler_cd_marker_action->set_active (true);
408 if ((prop = node->property ("videotl")) != 0) {
409 if (string_is_affirmative (prop->value())) {
410 ruler_video_action->set_active (true);
412 ruler_video_action->set_active (false);
418 no_ruler_shown_update = false;
419 update_ruler_visibility ();
423 Editor::update_ruler_visibility ()
425 int visible_timebars = 0;
427 if (no_ruler_shown_update) {
431 /* the order of the timebars is fixed, so we have to go through each one
432 * and adjust its position depending on what is shown.
434 * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
435 * loop/punch, cd markers, location markers
443 /* gtk update probs require this (damn) */
446 range_mark_label.hide();
447 transport_mark_label.hide();
448 cd_mark_label.hide();
450 videotl_label.hide();
453 if (ruler_minsec_action->get_active()) {
454 old_unit_pos = minsec_ruler->position().y;
455 if (tbpos != old_unit_pos) {
456 minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
458 minsec_ruler->show();
460 tbpos += timebar_height;
461 tbgpos += timebar_height;
464 minsec_ruler->hide();
468 if (ruler_timecode_action->get_active()) {
469 old_unit_pos = timecode_ruler->position().y;
470 if (tbpos != old_unit_pos) {
471 timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
473 timecode_ruler->show();
474 timecode_label.show();
475 tbpos += timebar_height;
476 tbgpos += timebar_height;
479 timecode_ruler->hide();
480 timecode_label.hide();
483 if (ruler_samples_action->get_active()) {
484 old_unit_pos = samples_ruler->position().y;
485 if (tbpos != old_unit_pos) {
486 samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
488 samples_ruler->show();
489 samples_label.show();
490 tbpos += timebar_height;
491 tbgpos += timebar_height;
494 samples_ruler->hide();
495 samples_label.hide();
498 if (ruler_bbt_action->get_active()) {
499 old_unit_pos = bbt_ruler->position().y;
500 if (tbpos != old_unit_pos) {
501 bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
505 tbpos += timebar_height;
506 tbgpos += timebar_height;
513 if (ruler_meter_action->get_active()) {
514 old_unit_pos = meter_group->position().y;
515 if (tbpos != old_unit_pos) {
516 meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
520 tbpos += timebar_height;
521 tbgpos += timebar_height;
528 if (ruler_tempo_action->get_active()) {
529 old_unit_pos = tempo_group->position().y;
530 if (tbpos != old_unit_pos) {
531 tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
535 tbpos += timebar_height;
536 tbgpos += timebar_height;
543 if (ruler_range_action->get_active()) {
544 old_unit_pos = range_marker_group->position().y;
545 if (tbpos != old_unit_pos) {
546 range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
548 range_marker_group->show();
549 range_mark_label.show();
551 tbpos += timebar_height;
552 tbgpos += timebar_height;
555 range_marker_group->hide();
556 range_mark_label.hide();
559 if (ruler_loop_punch_action->get_active()) {
560 old_unit_pos = transport_marker_group->position().y;
561 if (tbpos != old_unit_pos) {
562 transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
564 transport_marker_group->show();
565 transport_mark_label.show();
566 tbpos += timebar_height;
567 tbgpos += timebar_height;
570 transport_marker_group->hide();
571 transport_mark_label.hide();
574 if (ruler_cd_marker_action->get_active()) {
575 old_unit_pos = cd_marker_group->position().y;
576 if (tbpos != old_unit_pos) {
577 cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
579 cd_marker_group->show();
580 cd_mark_label.show();
581 tbpos += timebar_height;
582 tbgpos += timebar_height;
584 // make sure all cd markers show up in their respective places
585 update_cd_marker_display();
587 cd_marker_group->hide();
588 cd_mark_label.hide();
589 // make sure all cd markers show up in their respective places
590 update_cd_marker_display();
593 if (ruler_marker_action->get_active()) {
594 old_unit_pos = marker_group->position().y;
595 if (tbpos != old_unit_pos) {
596 marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
598 marker_group->show();
600 tbpos += timebar_height;
601 tbgpos += timebar_height;
604 marker_group->hide();
608 if (ruler_video_action->get_active()) {
609 old_unit_pos = videotl_group->position().y;
610 if (tbpos != old_unit_pos) {
611 videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
613 videotl_group->show();
614 videotl_label.show();
615 tbpos += timebar_height * videotl_bar_height;
616 tbgpos += timebar_height * videotl_bar_height;
617 visible_timebars+=videotl_bar_height;
618 queue_visual_videotimeline_update();
620 videotl_group->hide();
621 videotl_label.hide();
622 update_video_timeline(true);
625 time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
627 /* move hv_scroll_group (trackviews) to the end of the timebars
630 hv_scroll_group->set_y_position (timebar_height * visible_timebars);
632 compute_fixed_ruler_scale ();
633 update_fixed_rulers();
634 redisplay_tempo (false);
636 /* Changing ruler visibility means that any lines on markers might need updating */
637 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
638 i->second->setup_lines ();
643 Editor::update_just_timecode ()
645 ENSURE_GUI_THREAD (*this, &Editor::update_just_timecode)
651 framepos_t rightmost_frame = leftmost_frame + current_page_samples();
653 if (ruler_timecode_action->get_active()) {
654 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
659 Editor::compute_fixed_ruler_scale ()
665 if (ruler_timecode_action->get_active()) {
666 set_timecode_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
669 if (ruler_minsec_action->get_active()) {
670 set_minsec_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
673 if (ruler_samples_action->get_active()) {
674 set_samples_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
679 Editor::update_fixed_rulers ()
681 framepos_t rightmost_frame;
687 compute_fixed_ruler_scale ();
689 _timecode_metric->units_per_pixel = samples_per_pixel;
690 _samples_metric->units_per_pixel = samples_per_pixel;
691 _minsec_metric->units_per_pixel = samples_per_pixel;
693 rightmost_frame = leftmost_frame + current_page_samples();
695 /* these force a redraw, which in turn will force execution of the metric callbacks
696 to compute the relevant ticks to display.
699 if (ruler_timecode_action->get_active()) {
700 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
703 if (ruler_samples_action->get_active()) {
704 samples_ruler->set_range (leftmost_frame, rightmost_frame);
707 if (ruler_minsec_action->get_active()) {
708 minsec_ruler->set_range (leftmost_frame, rightmost_frame);
713 Editor::update_tempo_based_rulers (std::vector<TempoMap::BBTPoint>& grid)
719 compute_bbt_ruler_scale (grid, leftmost_frame, leftmost_frame+current_page_samples());
721 _bbt_metric->units_per_pixel = samples_per_pixel;
723 if (ruler_bbt_action->get_active()) {
724 bbt_ruler->set_range (leftmost_frame, leftmost_frame+current_page_samples());
730 Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
741 fr = _session->frame_rate();
743 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
744 lower = lower - spacer;
749 upper = upper + spacer;
750 framecnt_t const range = upper - lower;
752 if (range < (2 * _session->frames_per_timecode_frame())) { /* 0 - 2 frames */
753 timecode_ruler_scale = timecode_show_bits;
754 timecode_mark_modulo = 20;
755 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
756 } else if (range <= (fr / 4)) { /* 2 frames - 0.250 second */
757 timecode_ruler_scale = timecode_show_frames;
758 timecode_mark_modulo = 1;
759 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
760 } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
761 timecode_ruler_scale = timecode_show_frames;
762 timecode_mark_modulo = 2;
763 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
764 } else if (range <= fr) { /* 0.5-1 second */
765 timecode_ruler_scale = timecode_show_frames;
766 timecode_mark_modulo = 5;
767 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
768 } else if (range <= 2 * fr) { /* 1-2 seconds */
769 timecode_ruler_scale = timecode_show_frames;
770 timecode_mark_modulo = 10;
771 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
772 } else if (range <= 8 * fr) { /* 2-8 seconds */
773 timecode_ruler_scale = timecode_show_seconds;
774 timecode_mark_modulo = 1;
775 timecode_nmarks = 2 + (range / fr);
776 } else if (range <= 16 * fr) { /* 8-16 seconds */
777 timecode_ruler_scale = timecode_show_seconds;
778 timecode_mark_modulo = 2;
779 timecode_nmarks = 2 + (range / fr);
780 } else if (range <= 30 * fr) { /* 16-30 seconds */
781 timecode_ruler_scale = timecode_show_seconds;
782 timecode_mark_modulo = 5;
783 timecode_nmarks = 2 + (range / fr);
784 } else if (range <= 60 * fr) { /* 30-60 seconds */
785 timecode_ruler_scale = timecode_show_seconds;
786 timecode_mark_modulo = 5;
787 timecode_nmarks = 2 + (range / fr);
788 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
789 timecode_ruler_scale = timecode_show_seconds;
790 timecode_mark_modulo = 15;
791 timecode_nmarks = 2 + (range / fr);
792 } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
793 timecode_ruler_scale = timecode_show_seconds;
794 timecode_mark_modulo = 30;
795 timecode_nmarks = 2 + (range / fr);
796 } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
797 timecode_ruler_scale = timecode_show_minutes;
798 timecode_mark_modulo = 2;
799 timecode_nmarks = 2 + 10;
800 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
801 timecode_ruler_scale = timecode_show_minutes;
802 timecode_mark_modulo = 5;
803 timecode_nmarks = 2 + 30;
804 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
805 timecode_ruler_scale = timecode_show_minutes;
806 timecode_mark_modulo = 10;
807 timecode_nmarks = 2 + 60;
808 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
809 timecode_ruler_scale = timecode_show_minutes;
810 timecode_mark_modulo = 30;
811 timecode_nmarks = 2 + (60 * 4);
812 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
813 timecode_ruler_scale = timecode_show_hours;
814 timecode_mark_modulo = 1;
815 timecode_nmarks = 2 + 8;
816 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
817 timecode_ruler_scale = timecode_show_hours;
818 timecode_mark_modulo = 1;
819 timecode_nmarks = 2 + 24;
822 const framecnt_t hours_in_range = range / (60 * 60 * fr);
823 const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
825 /* Normally we do not need to know anything about the width of the canvas
826 to set the ruler scale, because the caller has already determined
827 the width and set lower + upper arguments to this function to match that.
829 But in this case, where the range defined by lower and uppper can vary
830 substantially (basically anything from 24hrs+ to several billion years)
831 trying to decide which tick marks to show does require us to know
832 about the available width.
835 timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
836 timecode_ruler_scale = timecode_show_many_hours;
837 timecode_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
842 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
846 Timecode::Time timecode;
849 ArdourCanvas::Ruler::Mark mark;
855 if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
856 lower = lower - spacer;
861 pos = (framecnt_t) floor (lower);
863 switch (timecode_ruler_scale) {
864 case timecode_show_bits:
865 // Find timecode time of this sample (pos) with subframe accuracy
866 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */ );
867 for (n = 0; n < timecode_nmarks; n++) {
868 _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
869 if ((timecode.subframes % timecode_mark_modulo) == 0) {
870 if (timecode.subframes == 0) {
871 mark.style = ArdourCanvas::Ruler::Mark::Major;
872 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
874 mark.style = ArdourCanvas::Ruler::Mark::Minor;
875 snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
878 snprintf (buf, sizeof(buf)," ");
879 mark.style = ArdourCanvas::Ruler::Mark::Micro;
883 marks.push_back (mark);
884 // Increment subframes by one
885 Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
889 case timecode_show_frames:
890 // Find timecode time of this sample (pos)
891 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
892 // Go to next whole frame down
893 Timecode::frames_floor( timecode );
894 for (n = 0; n < timecode_nmarks; n++) {
895 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
896 if ((timecode.frames % timecode_mark_modulo) == 0) {
897 if (timecode.frames == 0) {
898 mark.style = ArdourCanvas::Ruler::Mark::Major;
900 mark.style = ArdourCanvas::Ruler::Mark::Minor;
903 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
905 snprintf (buf, sizeof(buf)," ");
906 mark.style = ArdourCanvas::Ruler::Mark::Micro;
910 marks.push_back (mark);
911 Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
915 case timecode_show_seconds:
916 // Find timecode time of this sample (pos)
917 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
918 // Go to next whole second down
919 Timecode::seconds_floor( timecode );
920 for (n = 0; n < timecode_nmarks; n++) {
921 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
922 if ((timecode.seconds % timecode_mark_modulo) == 0) {
923 if (timecode.seconds == 0) {
924 mark.style = ArdourCanvas::Ruler::Mark::Major;
927 mark.style = ArdourCanvas::Ruler::Mark::Minor;
930 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
932 snprintf (buf, sizeof(buf)," ");
933 mark.style = ArdourCanvas::Ruler::Mark::Micro;
937 marks.push_back (mark);
938 Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
942 case timecode_show_minutes:
943 //Find timecode time of this sample (pos)
944 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
945 // Go to next whole minute down
946 Timecode::minutes_floor( timecode );
947 for (n = 0; n < timecode_nmarks; n++) {
948 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
949 if ((timecode.minutes % timecode_mark_modulo) == 0) {
950 if (timecode.minutes == 0) {
951 mark.style = ArdourCanvas::Ruler::Mark::Major;
953 mark.style = ArdourCanvas::Ruler::Mark::Minor;
955 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
957 snprintf (buf, sizeof(buf)," ");
958 mark.style = ArdourCanvas::Ruler::Mark::Micro;
962 marks.push_back (mark);
963 Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
966 case timecode_show_hours:
967 // Find timecode time of this sample (pos)
968 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
969 // Go to next whole hour down
970 Timecode::hours_floor( timecode );
971 for (n = 0; n < timecode_nmarks; n++) {
972 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
973 if ((timecode.hours % timecode_mark_modulo) == 0) {
974 mark.style = ArdourCanvas::Ruler::Mark::Major;
975 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
977 snprintf (buf, sizeof(buf)," ");
978 mark.style = ArdourCanvas::Ruler::Mark::Micro;
982 marks.push_back (mark);
983 Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
986 case timecode_show_many_hours:
987 // Find timecode time of this sample (pos)
988 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
989 // Go to next whole hour down
990 Timecode::hours_floor (timecode);
992 for (n = 0; n < timecode_nmarks; ) {
993 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
994 if ((timecode.hours % timecode_mark_modulo) == 0) {
995 mark.style = ArdourCanvas::Ruler::Mark::Major;
996 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
999 marks.push_back (mark);
1002 /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
1003 and doing it 1 hour at a time is just stupid (and slow).
1005 timecode.hours += timecode_mark_modulo;
1012 Editor::compute_bbt_ruler_scale (std::vector<ARDOUR::TempoMap::BBTPoint>& grid, framepos_t lower, framepos_t upper)
1014 if (_session == 0) {
1018 std::vector<TempoMap::BBTPoint>::const_iterator i;
1019 Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
1020 double floor_lower_beat = floor(max (0.0, _session->tempo_map().beat_at_frame (lower)));
1022 if (floor_lower_beat < 0.0) {
1023 floor_lower_beat = 0.0;
1026 const framecnt_t beat_before_lower_pos = _session->tempo_map().frame_at_beat (floor_lower_beat);
1027 const framecnt_t beat_after_upper_pos = _session->tempo_map().frame_at_beat (floor (max (0.0, _session->tempo_map().beat_at_frame (upper))) + 1.0);
1029 _session->bbt_time (beat_before_lower_pos, lower_beat);
1030 _session->bbt_time (beat_after_upper_pos, upper_beat);
1033 bbt_accent_modulo = 1;
1034 bbt_bar_helper_on = false;
1038 bbt_ruler_scale = bbt_show_many;
1040 switch (_snap_type) {
1041 case SnapToBeatDiv2:
1042 bbt_beat_subdivision = 2;
1044 case SnapToBeatDiv3:
1045 bbt_beat_subdivision = 3;
1047 case SnapToBeatDiv4:
1048 bbt_beat_subdivision = 4;
1050 case SnapToBeatDiv5:
1051 bbt_beat_subdivision = 5;
1052 bbt_accent_modulo = 2; // XXX YIKES
1054 case SnapToBeatDiv6:
1055 bbt_beat_subdivision = 6;
1056 bbt_accent_modulo = 2; // XXX YIKES
1058 case SnapToBeatDiv7:
1059 bbt_beat_subdivision = 7;
1060 bbt_accent_modulo = 2; // XXX YIKES
1062 case SnapToBeatDiv8:
1063 bbt_beat_subdivision = 8;
1064 bbt_accent_modulo = 2;
1066 case SnapToBeatDiv10:
1067 bbt_beat_subdivision = 10;
1068 bbt_accent_modulo = 2; // XXX YIKES
1070 case SnapToBeatDiv12:
1071 bbt_beat_subdivision = 12;
1072 bbt_accent_modulo = 3;
1074 case SnapToBeatDiv14:
1075 bbt_beat_subdivision = 14;
1076 bbt_accent_modulo = 3; // XXX YIKES!
1078 case SnapToBeatDiv16:
1079 bbt_beat_subdivision = 16;
1080 bbt_accent_modulo = 4;
1082 case SnapToBeatDiv20:
1083 bbt_beat_subdivision = 20;
1084 bbt_accent_modulo = 5;
1086 case SnapToBeatDiv24:
1087 bbt_beat_subdivision = 24;
1088 bbt_accent_modulo = 6;
1090 case SnapToBeatDiv28:
1091 bbt_beat_subdivision = 28;
1092 bbt_accent_modulo = 7;
1094 case SnapToBeatDiv32:
1095 bbt_beat_subdivision = 32;
1096 bbt_accent_modulo = 8;
1098 case SnapToBeatDiv64:
1099 bbt_beat_subdivision = 64;
1100 bbt_accent_modulo = 8;
1102 case SnapToBeatDiv128:
1103 bbt_beat_subdivision = 128;
1104 bbt_accent_modulo = 8;
1107 bbt_beat_subdivision = 4;
1110 if (distance (grid.begin(), grid.end()) == 0) {
1118 if ((*i).beat >= (*grid.begin()).beat) {
1119 bbt_bars = (*i).bar - (*grid.begin()).bar;
1121 bbt_bars = (*i).bar - (*grid.begin()).bar;
1124 beats = distance (grid.begin(), grid.end()) - bbt_bars;
1125 double beat_density = ((distance (grid.begin(), grid.end()) + 1) * ((double) (upper - lower) / (double) (1 + grid.back().frame - grid.front().frame))) / 5.0;
1126 /* Only show the bar helper if there aren't many bars on the screen */
1127 if ((bbt_bars < 2) || (beats < 5)) {
1128 bbt_bar_helper_on = true;
1131 if (beat_density > 8192) {
1132 bbt_ruler_scale = bbt_show_many;
1133 } else if (beat_density > 1024) {
1134 bbt_ruler_scale = bbt_show_64;
1135 } else if (beat_density > 512) {
1136 bbt_ruler_scale = bbt_show_16;
1137 } else if (beat_density > 128) {
1138 bbt_ruler_scale = bbt_show_4;
1139 } else if (beat_density > 16) {
1140 bbt_ruler_scale = bbt_show_1;
1141 } else if (beat_density > 2) {
1142 bbt_ruler_scale = bbt_show_beats;
1143 } else if (beat_density > 0.5) {
1144 bbt_ruler_scale = bbt_show_ticks;
1146 bbt_ruler_scale = bbt_show_ticks_detail;
1149 if ((bbt_ruler_scale == bbt_show_ticks_detail) && beats < 3) {
1150 bbt_ruler_scale = bbt_show_ticks_super_detail;
1155 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1157 ArdourCanvas::Ruler::Mark copy = marks.back();
1158 copy.label = newlabel;
1160 marks.push_back (copy);
1164 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1166 if (_session == 0) {
1170 std::vector<TempoMap::BBTPoint>::const_iterator i;
1175 Timecode::BBT_Time next_beat;
1180 double bbt_position_of_helper;
1181 bool i_am_accented = false;
1182 bool helper_active = false;
1183 ArdourCanvas::Ruler::Mark mark;
1185 std::vector<TempoMap::BBTPoint> grid;
1187 compute_current_bbt_points (grid, lower, upper);
1189 if (distance (grid.begin(), grid.end()) == 0) {
1193 switch (bbt_ruler_scale) {
1195 case bbt_show_beats:
1197 beats = distance (grid.begin(), grid.end());
1198 bbt_nmarks = beats + 2;
1201 mark.position = lower;
1202 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1203 marks.push_back (mark);
1205 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1207 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1208 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1209 edit_last_mark_label (marks, buf);
1210 helper_active = true;
1213 if ((*i).is_bar()) {
1214 mark.style = ArdourCanvas::Ruler::Mark::Major;
1215 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1216 } else if (((*i).beat % 2 == 1)) {
1217 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1220 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1224 mark.position = (*i).frame;
1225 marks.push_back (mark);
1231 case bbt_show_ticks:
1233 beats = distance (grid.begin(), grid.end());
1234 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1236 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1238 // could do marks.assign() here to preallocate
1241 mark.position = lower;
1242 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1243 marks.push_back (mark);
1245 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1247 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1248 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1249 edit_last_mark_label (marks, buf);
1250 helper_active = true;
1253 if ((*i).is_bar()) {
1254 mark.style = ArdourCanvas::Ruler::Mark::Major;
1255 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1257 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1258 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1260 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1264 mark.position = (*i).frame;
1265 marks.push_back (mark);
1269 /* Add the tick marks */
1270 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1271 tick = skip; // the first non-beat tick
1273 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1275 next_beat.beats = (*i).beat;
1276 next_beat.bars = (*i).bar;
1277 next_beat.ticks = tick;
1278 pos = _session->tempo_map().frame_at_bbt (next_beat);
1280 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1281 i_am_accented = true;
1284 mark.position = pos;
1286 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1287 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1289 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1291 i_am_accented = false;
1292 marks.push_back (mark);
1302 case bbt_show_ticks_detail:
1304 beats = distance (grid.begin(), grid.end());
1305 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1307 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1310 mark.position = lower;
1311 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1312 marks.push_back (mark);
1314 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1316 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1317 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1318 edit_last_mark_label (marks, buf);
1319 helper_active = true;
1322 if ((*i).is_bar()) {
1323 mark.style = ArdourCanvas::Ruler::Mark::Major;
1324 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1326 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1327 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1329 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1333 mark.position = (*i).frame;
1334 marks.push_back (mark);
1338 /* Add the tick marks */
1339 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1340 tick = skip; // the first non-beat tick
1343 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1345 next_beat.beats = (*i).beat;
1346 next_beat.bars = (*i).bar;
1347 next_beat.ticks = tick;
1348 pos = _session->tempo_map().frame_at_bbt (next_beat);
1350 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1351 i_am_accented = true;
1353 if (i_am_accented && (pos > bbt_position_of_helper)){
1354 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1360 mark.position = pos;
1362 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1363 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1365 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1367 i_am_accented = false;
1368 marks.push_back (mark);
1378 case bbt_show_ticks_super_detail:
1380 beats = distance (grid.begin(), grid.end());
1381 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1383 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1386 mark.position = lower;
1387 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1388 marks.push_back (mark);
1390 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1392 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1393 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1394 edit_last_mark_label (marks, buf);
1395 helper_active = true;
1398 if ((*i).is_bar()) {
1399 mark.style = ArdourCanvas::Ruler::Mark::Major;
1400 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1402 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1403 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1405 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1409 mark.position = (*i).frame;
1410 marks.push_back (mark);
1414 /* Add the tick marks */
1415 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1417 next_beat.beats = (*i).beat;
1418 next_beat.bars = (*i).bar;
1419 tick = skip; // the first non-beat tick
1421 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1423 next_beat.ticks = tick;
1424 pos = _session->tempo_map().frame_at_bbt (next_beat);
1425 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1426 i_am_accented = true;
1429 if (pos > bbt_position_of_helper) {
1430 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1436 mark.position = pos;
1438 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1439 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1441 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1443 i_am_accented = false;
1444 marks.push_back (mark);
1456 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1457 mark.style = ArdourCanvas::Ruler::Mark::Major;
1459 mark.position = lower;
1460 marks.push_back (mark);
1464 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1465 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1466 if ((*i).is_bar()) {
1467 if ((*i).bar % 64 == 1) {
1468 if ((*i).bar % 256 == 1) {
1469 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1470 mark.style = ArdourCanvas::Ruler::Mark::Major;
1473 if ((*i).bar % 256 == 129) {
1474 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1476 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1480 mark.position = (*i).frame;
1481 marks.push_back (mark);
1489 bbt_nmarks = (bbt_bars / 16) + 1;
1490 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1491 if ((*i).is_bar()) {
1492 if ((*i).bar % 16 == 1) {
1493 if ((*i).bar % 64 == 1) {
1494 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1495 mark.style = ArdourCanvas::Ruler::Mark::Major;
1498 if ((*i).bar % 64 == 33) {
1499 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1501 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1505 mark.position = (*i).frame;
1506 marks.push_back (mark);
1514 bbt_nmarks = (bbt_bars / 4) + 1;
1515 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1516 if ((*i).is_bar()) {
1517 if ((*i).bar % 4 == 1) {
1518 if ((*i).bar % 16 == 1) {
1519 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1520 mark.style = ArdourCanvas::Ruler::Mark::Major;
1523 if ((*i).bar % 16 == 9) {
1524 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1526 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1530 mark.position = (*i).frame;
1531 marks.push_back (mark);
1540 bbt_nmarks = bbt_bars + 2;
1541 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1542 if ((*i).is_bar()) {
1543 if ((*i).bar % 4 == 1) {
1544 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1545 mark.style = ArdourCanvas::Ruler::Mark::Major;
1548 if ((*i).bar % 4 == 3) {
1549 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1551 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1555 mark.position = (*i).frame;
1556 marks.push_back (mark);
1566 Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
1568 _samples_ruler_interval = (upper - lower) / 5;
1572 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1575 framepos_t const ilower = (framepos_t) floor (lower);
1579 ArdourCanvas::Ruler::Mark mark;
1581 if (_session == 0) {
1586 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1587 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1589 mark.position = pos;
1590 mark.style = ArdourCanvas::Ruler::Mark::Major;
1591 marks.push_back (mark);
1596 sample_to_clock_parts ( framepos_t sample,
1597 framepos_t sample_rate,
1611 hrs = left / (sample_rate * 60 * 60 * 1000);
1612 left -= hrs * sample_rate * 60 * 60 * 1000;
1613 mins = left / (sample_rate * 60 * 1000);
1614 left -= mins * sample_rate * 60 * 1000;
1615 secs = left / (sample_rate * 1000);
1616 left -= secs * sample_rate * 1000;
1617 millisecs = left / sample_rate;
1619 *millisecs_p = millisecs;
1628 Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
1630 framepos_t fr = _session->frame_rate() * 1000;
1633 if (_session == 0) {
1638 /* to prevent 'flashing' */
1639 if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1645 framecnt_t const range = (upper - lower) * 1000;
1647 if (range <= (fr / 10)) { /* 0-0.1 second */
1648 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1649 minsec_ruler_scale = minsec_show_msecs;
1650 minsec_mark_modulo = 10;
1651 minsec_nmarks = 2 + (range / minsec_mark_interval);
1652 } else if (range <= (fr / 2)) { /* 0-0.5 second */
1653 minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1654 minsec_ruler_scale = minsec_show_msecs;
1655 minsec_mark_modulo = 100;
1656 minsec_nmarks = 2 + (range / minsec_mark_interval);
1657 } else if (range <= fr) { /* 0-1 second */
1658 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1659 minsec_ruler_scale = minsec_show_msecs;
1660 minsec_mark_modulo = 200;
1661 minsec_nmarks = 2 + (range / minsec_mark_interval);
1662 } else if (range <= 2 * fr) { /* 1-2 seconds */
1663 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1664 minsec_ruler_scale = minsec_show_msecs;
1665 minsec_mark_modulo = 500;
1666 minsec_nmarks = 2 + (range / minsec_mark_interval);
1667 } else if (range <= 8 * fr) { /* 2-5 seconds */
1668 minsec_mark_interval = fr / 5; /* show 2 seconds */
1669 minsec_ruler_scale = minsec_show_msecs;
1670 minsec_mark_modulo = 1000;
1671 minsec_nmarks = 2 + (range / minsec_mark_interval);
1672 } else if (range <= 16 * fr) { /* 8-16 seconds */
1673 minsec_mark_interval = fr; /* show 1 seconds */
1674 minsec_ruler_scale = minsec_show_seconds;
1675 minsec_mark_modulo = 2;
1676 minsec_nmarks = 2 + (range / minsec_mark_interval);
1677 } else if (range <= 30 * fr) { /* 10-30 seconds */
1678 minsec_mark_interval = fr; /* show 1 seconds */
1679 minsec_ruler_scale = minsec_show_seconds;
1680 minsec_mark_modulo = 5;
1681 minsec_nmarks = 2 + (range / minsec_mark_interval);
1682 } else if (range <= 60 * fr) { /* 30-60 seconds */
1683 minsec_mark_interval = fr; /* show 1 seconds */
1684 minsec_ruler_scale = minsec_show_seconds;
1685 minsec_mark_modulo = 5;
1686 minsec_nmarks = 2 + (range / minsec_mark_interval);
1687 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1688 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1689 minsec_ruler_scale = minsec_show_seconds;
1690 minsec_mark_modulo = 3;
1691 minsec_nmarks = 2 + (range / minsec_mark_interval);
1692 } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1693 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1694 minsec_ruler_scale = minsec_show_seconds;
1695 minsec_mark_modulo = 30;
1696 minsec_nmarks = 2 + (range / minsec_mark_interval);
1697 } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1698 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1699 minsec_ruler_scale = minsec_show_seconds;
1700 minsec_mark_modulo = 120;
1701 minsec_nmarks = 2 + (range / minsec_mark_interval);
1702 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1703 minsec_mark_interval = 60 * fr; /* show 1 minute */
1704 minsec_ruler_scale = minsec_show_minutes;
1705 minsec_mark_modulo = 5;
1706 minsec_nmarks = 2 + (range / minsec_mark_interval);
1707 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1708 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1709 minsec_ruler_scale = minsec_show_minutes;
1710 minsec_mark_modulo = 10;
1711 minsec_nmarks = 2 + (range / minsec_mark_interval);
1712 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1713 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1714 minsec_ruler_scale = minsec_show_minutes;
1715 minsec_mark_modulo = 30;
1716 minsec_nmarks = 2 + (range / minsec_mark_interval);
1717 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1718 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1719 minsec_ruler_scale = minsec_show_minutes;
1720 minsec_mark_modulo = 60;
1721 minsec_nmarks = 2 + (range / minsec_mark_interval);
1722 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1723 minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1724 minsec_ruler_scale = minsec_show_hours;
1725 minsec_mark_modulo = 2;
1726 minsec_nmarks = 2 + (range / minsec_mark_interval);
1729 const framecnt_t hours_in_range = range / (60 * 60 * fr);
1730 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1732 /* Normally we do not need to know anything about the width of the canvas
1733 to set the ruler scale, because the caller has already determined
1734 the width and set lower + upper arguments to this function to match that.
1736 But in this case, where the range defined by lower and uppper can vary
1737 substantially (anything from 24hrs+ to several billion years)
1738 trying to decide which tick marks to show does require us to know
1739 about the available width.
1742 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1743 minsec_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1744 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1745 minsec_ruler_scale = minsec_show_many_hours;
1750 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1754 long hrs, mins, secs, millisecs;
1757 ArdourCanvas::Ruler::Mark mark;
1759 if (_session == 0) {
1763 /* to prevent 'flashing' */
1764 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
1765 lower = lower - spacer;
1770 pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1772 switch (minsec_ruler_scale) {
1774 case minsec_show_msecs:
1775 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1776 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1777 if (millisecs % minsec_mark_modulo == 0) {
1778 if (millisecs == 0) {
1779 mark.style = ArdourCanvas::Ruler::Mark::Major;
1781 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1783 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1786 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1789 mark.position = pos/1000.0;
1790 marks.push_back (mark);
1794 case minsec_show_seconds:
1795 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1796 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1797 if (secs % minsec_mark_modulo == 0) {
1799 mark.style = ArdourCanvas::Ruler::Mark::Major;
1801 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1803 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1806 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1809 mark.position = pos/1000.0;
1810 marks.push_back (mark);
1814 case minsec_show_minutes:
1815 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1816 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1817 if (mins % minsec_mark_modulo == 0) {
1819 mark.style = ArdourCanvas::Ruler::Mark::Major;
1821 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1823 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1826 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1829 mark.position = pos/1000.0;
1830 marks.push_back (mark);
1834 case minsec_show_hours:
1835 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1836 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1837 if (hrs % minsec_mark_modulo == 0) {
1838 mark.style = ArdourCanvas::Ruler::Mark::Major;
1839 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1842 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1845 mark.position = pos/1000.0;
1846 marks.push_back (mark);
1850 case minsec_show_many_hours:
1851 for (n = 0; n < minsec_nmarks; ) {
1852 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1853 if (hrs % minsec_mark_modulo == 0) {
1854 mark.style = ArdourCanvas::Ruler::Mark::Major;
1855 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1857 mark.position = pos/1000.0;
1858 marks.push_back (mark);
1861 pos += minsec_mark_interval;