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 ()
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 (!Profile->get_sae() && 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 (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
714 ARDOUR::TempoMap::BBTPointList::const_iterator& end)
720 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame+current_page_samples(),
723 _bbt_metric->units_per_pixel = samples_per_pixel;
725 if (ruler_bbt_action->get_active()) {
726 bbt_ruler->set_range (leftmost_frame, leftmost_frame+current_page_samples());
732 Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
743 fr = _session->frame_rate();
745 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
746 lower = lower - spacer;
751 upper = upper + spacer;
752 framecnt_t const range = upper - lower;
754 if (range < (2 * _session->frames_per_timecode_frame())) { /* 0 - 2 frames */
755 timecode_ruler_scale = timecode_show_bits;
756 timecode_mark_modulo = 20;
757 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
758 } else if (range <= (fr / 4)) { /* 2 frames - 0.250 second */
759 timecode_ruler_scale = timecode_show_frames;
760 timecode_mark_modulo = 1;
761 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
762 } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
763 timecode_ruler_scale = timecode_show_frames;
764 timecode_mark_modulo = 2;
765 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
766 } else if (range <= fr) { /* 0.5-1 second */
767 timecode_ruler_scale = timecode_show_frames;
768 timecode_mark_modulo = 5;
769 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
770 } else if (range <= 2 * fr) { /* 1-2 seconds */
771 timecode_ruler_scale = timecode_show_frames;
772 timecode_mark_modulo = 10;
773 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
774 } else if (range <= 8 * fr) { /* 2-8 seconds */
775 timecode_ruler_scale = timecode_show_seconds;
776 timecode_mark_modulo = 1;
777 timecode_nmarks = 2 + (range / fr);
778 } else if (range <= 16 * fr) { /* 8-16 seconds */
779 timecode_ruler_scale = timecode_show_seconds;
780 timecode_mark_modulo = 2;
781 timecode_nmarks = 2 + (range / fr);
782 } else if (range <= 30 * fr) { /* 16-30 seconds */
783 timecode_ruler_scale = timecode_show_seconds;
784 timecode_mark_modulo = 5;
785 timecode_nmarks = 2 + (range / fr);
786 } else if (range <= 60 * fr) { /* 30-60 seconds */
787 timecode_ruler_scale = timecode_show_seconds;
788 timecode_mark_modulo = 5;
789 timecode_nmarks = 2 + (range / fr);
790 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
791 timecode_ruler_scale = timecode_show_seconds;
792 timecode_mark_modulo = 15;
793 timecode_nmarks = 2 + (range / fr);
794 } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
795 timecode_ruler_scale = timecode_show_seconds;
796 timecode_mark_modulo = 30;
797 timecode_nmarks = 2 + (range / fr);
798 } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
799 timecode_ruler_scale = timecode_show_minutes;
800 timecode_mark_modulo = 2;
801 timecode_nmarks = 2 + 10;
802 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
803 timecode_ruler_scale = timecode_show_minutes;
804 timecode_mark_modulo = 5;
805 timecode_nmarks = 2 + 30;
806 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
807 timecode_ruler_scale = timecode_show_minutes;
808 timecode_mark_modulo = 10;
809 timecode_nmarks = 2 + 60;
810 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
811 timecode_ruler_scale = timecode_show_minutes;
812 timecode_mark_modulo = 30;
813 timecode_nmarks = 2 + (60 * 4);
814 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
815 timecode_ruler_scale = timecode_show_hours;
816 timecode_mark_modulo = 1;
817 timecode_nmarks = 2 + 8;
818 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
819 timecode_ruler_scale = timecode_show_hours;
820 timecode_mark_modulo = 1;
821 timecode_nmarks = 2 + 24;
824 const framecnt_t hours_in_range = range / (60 * 60 * fr);
825 const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
827 /* Normally we do not need to know anything about the width of the canvas
828 to set the ruler scale, because the caller has already determined
829 the width and set lower + upper arguments to this function to match that.
831 But in this case, where the range defined by lower and uppper can vary
832 substantially (basically anything from 24hrs+ to several billion years)
833 trying to decide which tick marks to show does require us to know
834 about the available width.
837 timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
838 timecode_ruler_scale = timecode_show_many_hours;
839 timecode_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
844 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
848 Timecode::Time timecode;
851 ArdourCanvas::Ruler::Mark mark;
857 if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
858 lower = lower - spacer;
863 pos = (framecnt_t) floor (lower);
865 switch (timecode_ruler_scale) {
866 case timecode_show_bits:
867 // Find timecode time of this sample (pos) with subframe accuracy
868 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */ );
869 for (n = 0; n < timecode_nmarks; n++) {
870 _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
871 if ((timecode.subframes % timecode_mark_modulo) == 0) {
872 if (timecode.subframes == 0) {
873 mark.style = ArdourCanvas::Ruler::Mark::Major;
874 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
876 mark.style = ArdourCanvas::Ruler::Mark::Minor;
877 snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
880 snprintf (buf, sizeof(buf)," ");
881 mark.style = ArdourCanvas::Ruler::Mark::Micro;
885 marks.push_back (mark);
886 // Increment subframes by one
887 Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
891 case timecode_show_frames:
892 // Find timecode time of this sample (pos)
893 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
894 // Go to next whole frame down
895 Timecode::frames_floor( timecode );
896 for (n = 0; n < timecode_nmarks; n++) {
897 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
898 if ((timecode.frames % timecode_mark_modulo) == 0) {
899 if (timecode.frames == 0) {
900 mark.style = ArdourCanvas::Ruler::Mark::Major;
902 mark.style = ArdourCanvas::Ruler::Mark::Minor;
905 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
907 snprintf (buf, sizeof(buf)," ");
908 mark.style = ArdourCanvas::Ruler::Mark::Micro;
912 marks.push_back (mark);
913 Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
917 case timecode_show_seconds:
918 // Find timecode time of this sample (pos)
919 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
920 // Go to next whole second down
921 Timecode::seconds_floor( timecode );
922 for (n = 0; n < timecode_nmarks; n++) {
923 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
924 if ((timecode.seconds % timecode_mark_modulo) == 0) {
925 if (timecode.seconds == 0) {
926 mark.style = ArdourCanvas::Ruler::Mark::Major;
929 mark.style = ArdourCanvas::Ruler::Mark::Minor;
932 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
934 snprintf (buf, sizeof(buf)," ");
935 mark.style = ArdourCanvas::Ruler::Mark::Micro;
939 marks.push_back (mark);
940 Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
944 case timecode_show_minutes:
945 //Find timecode time of this sample (pos)
946 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
947 // Go to next whole minute down
948 Timecode::minutes_floor( timecode );
949 for (n = 0; n < timecode_nmarks; n++) {
950 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
951 if ((timecode.minutes % timecode_mark_modulo) == 0) {
952 if (timecode.minutes == 0) {
953 mark.style = ArdourCanvas::Ruler::Mark::Major;
955 mark.style = ArdourCanvas::Ruler::Mark::Minor;
957 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
959 snprintf (buf, sizeof(buf)," ");
960 mark.style = ArdourCanvas::Ruler::Mark::Micro;
964 marks.push_back (mark);
965 Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
968 case timecode_show_hours:
969 // Find timecode time of this sample (pos)
970 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
971 // Go to next whole hour down
972 Timecode::hours_floor( timecode );
973 for (n = 0; n < timecode_nmarks; n++) {
974 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
975 if ((timecode.hours % timecode_mark_modulo) == 0) {
976 mark.style = ArdourCanvas::Ruler::Mark::Major;
977 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
979 snprintf (buf, sizeof(buf)," ");
980 mark.style = ArdourCanvas::Ruler::Mark::Micro;
984 marks.push_back (mark);
985 Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
988 case timecode_show_many_hours:
989 // Find timecode time of this sample (pos)
990 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
991 // Go to next whole hour down
992 Timecode::hours_floor (timecode);
994 for (n = 0; n < timecode_nmarks; ) {
995 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
996 if ((timecode.hours % timecode_mark_modulo) == 0) {
997 mark.style = ArdourCanvas::Ruler::Mark::Major;
998 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1000 mark.position = pos;
1001 marks.push_back (mark);
1004 /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
1005 and doing it 1 hour at a time is just stupid (and slow).
1007 timecode.hours += timecode_mark_modulo;
1014 Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
1015 ARDOUR::TempoMap::BBTPointList::const_iterator begin,
1016 ARDOUR::TempoMap::BBTPointList::const_iterator end)
1018 if (_session == 0) {
1022 TempoMap::BBTPointList::const_iterator i;
1023 Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
1025 _session->bbt_time (lower, lower_beat);
1026 _session->bbt_time (upper, upper_beat);
1029 bbt_accent_modulo = 1;
1030 bbt_bar_helper_on = false;
1034 bbt_ruler_scale = bbt_show_many;
1036 switch (_snap_type) {
1037 case SnapToBeatDiv2:
1038 bbt_beat_subdivision = 2;
1040 case SnapToBeatDiv3:
1041 bbt_beat_subdivision = 3;
1043 case SnapToBeatDiv4:
1044 bbt_beat_subdivision = 4;
1046 case SnapToBeatDiv5:
1047 bbt_beat_subdivision = 5;
1048 bbt_accent_modulo = 2; // XXX YIKES
1050 case SnapToBeatDiv6:
1051 bbt_beat_subdivision = 6;
1052 bbt_accent_modulo = 2; // XXX YIKES
1054 case SnapToBeatDiv7:
1055 bbt_beat_subdivision = 7;
1056 bbt_accent_modulo = 2; // XXX YIKES
1058 case SnapToBeatDiv8:
1059 bbt_beat_subdivision = 8;
1060 bbt_accent_modulo = 2;
1062 case SnapToBeatDiv10:
1063 bbt_beat_subdivision = 10;
1064 bbt_accent_modulo = 2; // XXX YIKES
1066 case SnapToBeatDiv12:
1067 bbt_beat_subdivision = 12;
1068 bbt_accent_modulo = 3;
1070 case SnapToBeatDiv14:
1071 bbt_beat_subdivision = 14;
1072 bbt_accent_modulo = 3; // XXX YIKES!
1074 case SnapToBeatDiv16:
1075 bbt_beat_subdivision = 16;
1076 bbt_accent_modulo = 4;
1078 case SnapToBeatDiv20:
1079 bbt_beat_subdivision = 20;
1080 bbt_accent_modulo = 5;
1082 case SnapToBeatDiv24:
1083 bbt_beat_subdivision = 24;
1084 bbt_accent_modulo = 6;
1086 case SnapToBeatDiv28:
1087 bbt_beat_subdivision = 28;
1088 bbt_accent_modulo = 7;
1090 case SnapToBeatDiv32:
1091 bbt_beat_subdivision = 32;
1092 bbt_accent_modulo = 8;
1094 case SnapToBeatDiv64:
1095 bbt_beat_subdivision = 64;
1096 bbt_accent_modulo = 8;
1098 case SnapToBeatDiv128:
1099 bbt_beat_subdivision = 128;
1100 bbt_accent_modulo = 8;
1103 bbt_beat_subdivision = 4;
1107 if (distance (begin, end) == 0) {
1113 if ((*i).beat >= (*begin).beat) {
1114 bbt_bars = (*i).bar - (*begin).bar;
1116 bbt_bars = (*i).bar - (*begin).bar - 1;
1118 beats = distance (begin, end) - bbt_bars;
1120 /* Only show the bar helper if there aren't many bars on the screen */
1121 if ((bbt_bars < 2) || (beats < 5)) {
1122 bbt_bar_helper_on = true;
1125 if (bbt_bars > 8192) {
1126 bbt_ruler_scale = bbt_show_many;
1127 } else if (bbt_bars > 1024) {
1128 bbt_ruler_scale = bbt_show_64;
1129 } else if (bbt_bars > 256) {
1130 bbt_ruler_scale = bbt_show_16;
1131 } else if (bbt_bars > 64) {
1132 bbt_ruler_scale = bbt_show_4;
1133 } else if (bbt_bars > 10) {
1134 bbt_ruler_scale = bbt_show_1;
1135 } else if (bbt_bars > 2) {
1136 bbt_ruler_scale = bbt_show_beats;
1137 } else if (bbt_bars > 0) {
1138 bbt_ruler_scale = bbt_show_ticks;
1140 bbt_ruler_scale = bbt_show_ticks_detail;
1143 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)) {
1144 bbt_ruler_scale = bbt_show_ticks_super_detail;
1149 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1151 ArdourCanvas::Ruler::Mark copy = marks.back();
1152 copy.label = newlabel;
1154 marks.push_back (copy);
1158 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1160 if (_session == 0) {
1164 TempoMap::BBTPointList::const_iterator i;
1169 Timecode::BBT_Time next_beat;
1170 framepos_t next_beat_pos;
1175 framepos_t frame_skip;
1176 double frame_skip_error;
1177 double bbt_position_of_helper;
1178 double accumulated_error;
1179 bool i_am_accented = false;
1180 bool helper_active = false;
1181 ArdourCanvas::Ruler::Mark mark;
1183 ARDOUR::TempoMap::BBTPointList::const_iterator begin;
1184 ARDOUR::TempoMap::BBTPointList::const_iterator end;
1186 compute_current_bbt_points (lower, upper, begin, end);
1188 if (distance (begin, end) == 0) {
1192 switch (bbt_ruler_scale) {
1194 case bbt_show_beats:
1195 beats = distance (begin, end);
1196 bbt_nmarks = beats + 2;
1199 mark.position = lower;
1200 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1201 marks.push_back (mark);
1203 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1205 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1206 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1207 edit_last_mark_label (marks, buf);
1208 helper_active = true;
1211 if ((*i).is_bar()) {
1212 mark.style = ArdourCanvas::Ruler::Mark::Major;
1213 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1214 } else if (((*i).beat % 2 == 1)) {
1215 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1218 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1222 mark.position = (*i).frame;
1223 marks.push_back (mark);
1229 case bbt_show_ticks:
1231 beats = distance (begin, end);
1232 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1234 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1236 // could do marks.assign() here to preallocate
1239 mark.position = lower;
1240 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1241 marks.push_back (mark);
1243 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1245 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1246 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1247 edit_last_mark_label (marks, buf);
1248 helper_active = true;
1251 if ((*i).is_bar()) {
1252 mark.style = ArdourCanvas::Ruler::Mark::Major;
1253 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1255 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1256 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1258 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1262 mark.position = (*i).frame;
1263 marks.push_back (mark);
1267 /* Add the tick marks */
1269 /* Find the next beat */
1270 next_beat.beats = (*i).beat;
1271 next_beat.bars = (*i).bar;
1272 next_beat.ticks = 0;
1274 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1275 next_beat.beats += 1;
1277 next_beat.bars += 1;
1278 next_beat.beats = 1;
1281 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1283 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1284 frame_skip_error -= frame_skip;
1285 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1287 pos = (*i).frame + frame_skip;
1288 accumulated_error = frame_skip_error;
1292 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1294 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1295 i_am_accented = true;
1300 /* Error compensation for float to framepos_t*/
1301 accumulated_error += frame_skip_error;
1302 if (accumulated_error > 1) {
1304 accumulated_error -= 1.0f;
1307 mark.position = pos;
1309 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1310 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1312 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1314 i_am_accented = false;
1315 marks.push_back (mark);
1322 case bbt_show_ticks_detail:
1324 beats = distance (begin, end);
1325 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1327 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1330 mark.position = lower;
1331 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1332 marks.push_back (mark);
1334 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1336 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1337 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1338 edit_last_mark_label (marks, buf);
1339 helper_active = true;
1342 if ((*i).is_bar()) {
1343 mark.style = ArdourCanvas::Ruler::Mark::Major;
1344 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1346 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1347 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1349 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1353 mark.position = (*i).frame;
1354 marks.push_back (mark);
1358 /* Add the tick marks */
1360 /* Find the next beat */
1362 next_beat.beats = (*i).beat;
1363 next_beat.bars = (*i).bar;
1365 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1366 next_beat.beats += 1;
1368 next_beat.bars += 1;
1369 next_beat.beats = 1;
1372 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1374 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1375 frame_skip_error -= frame_skip;
1376 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1378 pos = (*i).frame + frame_skip;
1379 accumulated_error = frame_skip_error;
1383 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1385 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1386 i_am_accented = true;
1389 if (i_am_accented && (pos > bbt_position_of_helper)){
1390 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1397 /* Error compensation for float to framepos_t*/
1398 accumulated_error += frame_skip_error;
1399 if (accumulated_error > 1) {
1401 accumulated_error -= 1.0f;
1404 mark.position = pos;
1406 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1407 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1409 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1411 i_am_accented = false;
1418 case bbt_show_ticks_super_detail:
1420 beats = distance (begin, end);
1421 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1423 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1426 mark.position = lower;
1427 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1428 marks.push_back (mark);
1430 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1432 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1433 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1434 edit_last_mark_label (marks, buf);
1435 helper_active = true;
1438 if ((*i).is_bar()) {
1439 mark.style = ArdourCanvas::Ruler::Mark::Major;
1440 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1442 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1443 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1445 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1449 mark.position = (*i).frame;
1450 marks.push_back (mark);
1454 /* Add the tick marks */
1456 /* Find the next beat */
1458 next_beat.beats = (*i).beat;
1459 next_beat.bars = (*i).bar;
1461 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1462 next_beat.beats += 1;
1464 next_beat.bars += 1;
1465 next_beat.beats = 1;
1468 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1470 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1471 frame_skip_error -= frame_skip;
1472 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1474 pos = (*i).frame + frame_skip;
1475 accumulated_error = frame_skip_error;
1479 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1481 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1482 i_am_accented = true;
1485 if (pos > bbt_position_of_helper) {
1486 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1493 /* Error compensation for float to framepos_t*/
1494 accumulated_error += frame_skip_error;
1495 if (accumulated_error > 1) {
1497 accumulated_error -= 1.0f;
1500 mark.position = pos;
1502 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1503 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1505 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1507 i_am_accented = false;
1508 marks.push_back (mark);
1517 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1518 mark.style = ArdourCanvas::Ruler::Mark::Major;
1520 mark.position = lower;
1521 marks.push_back (mark);
1525 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1526 for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
1527 if ((*i).is_bar()) {
1528 if ((*i).bar % 64 == 1) {
1529 if ((*i).bar % 256 == 1) {
1530 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1531 mark.style = ArdourCanvas::Ruler::Mark::Major;
1534 if ((*i).bar % 256 == 129) {
1535 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1537 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1541 mark.position = (*i).frame;
1542 marks.push_back (mark);
1550 bbt_nmarks = (bbt_bars / 16) + 1;
1551 for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
1552 if ((*i).is_bar()) {
1553 if ((*i).bar % 16 == 1) {
1554 if ((*i).bar % 64 == 1) {
1555 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1556 mark.style = ArdourCanvas::Ruler::Mark::Major;
1559 if ((*i).bar % 64 == 33) {
1560 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1562 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1566 mark.position = (*i).frame;
1567 marks.push_back (mark);
1575 bbt_nmarks = (bbt_bars / 4) + 1;
1576 for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
1577 if ((*i).is_bar()) {
1578 if ((*i).bar % 4 == 1) {
1579 if ((*i).bar % 16 == 1) {
1580 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1581 mark.style = ArdourCanvas::Ruler::Mark::Major;
1584 if ((*i).bar % 16 == 9) {
1585 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1587 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1591 mark.position = (*i).frame;
1592 marks.push_back (mark);
1601 bbt_nmarks = bbt_bars + 2;
1602 for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
1603 if ((*i).is_bar()) {
1604 if ((*i).bar % 4 == 1) {
1605 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1606 mark.style = ArdourCanvas::Ruler::Mark::Major;
1609 if ((*i).bar % 4 == 3) {
1610 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1612 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1616 mark.position = (*i).frame;
1617 marks.push_back (mark);
1627 Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
1629 _samples_ruler_interval = (upper - lower) / 5;
1633 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1636 framepos_t const ilower = (framepos_t) floor (lower);
1640 ArdourCanvas::Ruler::Mark mark;
1642 if (_session == 0) {
1647 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1648 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1650 mark.position = pos;
1651 mark.style = ArdourCanvas::Ruler::Mark::Major;
1652 marks.push_back (mark);
1657 sample_to_clock_parts ( framepos_t sample,
1658 framepos_t sample_rate,
1672 hrs = left / (sample_rate * 60 * 60 * 1000);
1673 left -= hrs * sample_rate * 60 * 60 * 1000;
1674 mins = left / (sample_rate * 60 * 1000);
1675 left -= mins * sample_rate * 60 * 1000;
1676 secs = left / (sample_rate * 1000);
1677 left -= secs * sample_rate * 1000;
1678 millisecs = left / sample_rate;
1680 *millisecs_p = millisecs;
1689 Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
1691 framepos_t fr = _session->frame_rate() * 1000;
1694 if (_session == 0) {
1699 /* to prevent 'flashing' */
1700 if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1706 framecnt_t const range = (upper - lower) * 1000;
1708 if (range <= (fr / 10)) { /* 0-0.1 second */
1709 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1710 minsec_ruler_scale = minsec_show_msecs;
1711 minsec_mark_modulo = 10;
1712 minsec_nmarks = 2 + (range / minsec_mark_interval);
1713 } else if (range <= (fr / 2)) { /* 0-0.5 second */
1714 minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1715 minsec_ruler_scale = minsec_show_msecs;
1716 minsec_mark_modulo = 100;
1717 minsec_nmarks = 2 + (range / minsec_mark_interval);
1718 } else if (range <= fr) { /* 0-1 second */
1719 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1720 minsec_ruler_scale = minsec_show_msecs;
1721 minsec_mark_modulo = 200;
1722 minsec_nmarks = 2 + (range / minsec_mark_interval);
1723 } else if (range <= 2 * fr) { /* 1-2 seconds */
1724 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1725 minsec_ruler_scale = minsec_show_msecs;
1726 minsec_mark_modulo = 500;
1727 minsec_nmarks = 2 + (range / minsec_mark_interval);
1728 } else if (range <= 8 * fr) { /* 2-5 seconds */
1729 minsec_mark_interval = fr / 5; /* show 2 seconds */
1730 minsec_ruler_scale = minsec_show_msecs;
1731 minsec_mark_modulo = 1000;
1732 minsec_nmarks = 2 + (range / minsec_mark_interval);
1733 } else if (range <= 16 * fr) { /* 8-16 seconds */
1734 minsec_mark_interval = fr; /* show 1 seconds */
1735 minsec_ruler_scale = minsec_show_seconds;
1736 minsec_mark_modulo = 2;
1737 minsec_nmarks = 2 + (range / minsec_mark_interval);
1738 } else if (range <= 30 * fr) { /* 10-30 seconds */
1739 minsec_mark_interval = fr; /* show 1 seconds */
1740 minsec_ruler_scale = minsec_show_seconds;
1741 minsec_mark_modulo = 5;
1742 minsec_nmarks = 2 + (range / minsec_mark_interval);
1743 } else if (range <= 60 * fr) { /* 30-60 seconds */
1744 minsec_mark_interval = fr; /* show 1 seconds */
1745 minsec_ruler_scale = minsec_show_seconds;
1746 minsec_mark_modulo = 5;
1747 minsec_nmarks = 2 + (range / minsec_mark_interval);
1748 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1749 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1750 minsec_ruler_scale = minsec_show_seconds;
1751 minsec_mark_modulo = 3;
1752 minsec_nmarks = 2 + (range / minsec_mark_interval);
1753 } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1754 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1755 minsec_ruler_scale = minsec_show_seconds;
1756 minsec_mark_modulo = 30;
1757 minsec_nmarks = 2 + (range / minsec_mark_interval);
1758 } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1759 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1760 minsec_ruler_scale = minsec_show_seconds;
1761 minsec_mark_modulo = 120;
1762 minsec_nmarks = 2 + (range / minsec_mark_interval);
1763 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1764 minsec_mark_interval = 60 * fr; /* show 1 minute */
1765 minsec_ruler_scale = minsec_show_minutes;
1766 minsec_mark_modulo = 5;
1767 minsec_nmarks = 2 + (range / minsec_mark_interval);
1768 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1769 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1770 minsec_ruler_scale = minsec_show_minutes;
1771 minsec_mark_modulo = 10;
1772 minsec_nmarks = 2 + (range / minsec_mark_interval);
1773 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1774 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1775 minsec_ruler_scale = minsec_show_minutes;
1776 minsec_mark_modulo = 30;
1777 minsec_nmarks = 2 + (range / minsec_mark_interval);
1778 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1779 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1780 minsec_ruler_scale = minsec_show_minutes;
1781 minsec_mark_modulo = 60;
1782 minsec_nmarks = 2 + (range / minsec_mark_interval);
1783 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1784 minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1785 minsec_ruler_scale = minsec_show_hours;
1786 minsec_mark_modulo = 2;
1787 minsec_nmarks = 2 + (range / minsec_mark_interval);
1790 const framecnt_t hours_in_range = range / (60 * 60 * fr);
1791 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1793 /* Normally we do not need to know anything about the width of the canvas
1794 to set the ruler scale, because the caller has already determined
1795 the width and set lower + upper arguments to this function to match that.
1797 But in this case, where the range defined by lower and uppper can vary
1798 substantially (anything from 24hrs+ to several billion years)
1799 trying to decide which tick marks to show does require us to know
1800 about the available width.
1803 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1804 minsec_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1805 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1806 minsec_ruler_scale = minsec_show_many_hours;
1811 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1815 long hrs, mins, secs, millisecs;
1818 ArdourCanvas::Ruler::Mark mark;
1820 if (_session == 0) {
1824 /* to prevent 'flashing' */
1825 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
1826 lower = lower - spacer;
1831 pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1833 switch (minsec_ruler_scale) {
1835 case minsec_show_msecs:
1836 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1837 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1838 if (millisecs % minsec_mark_modulo == 0) {
1839 if (millisecs == 0) {
1840 mark.style = ArdourCanvas::Ruler::Mark::Major;
1842 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1844 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1847 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1850 mark.position = pos/1000.0;
1851 marks.push_back (mark);
1855 case minsec_show_seconds:
1856 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1857 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1858 if (secs % minsec_mark_modulo == 0) {
1860 mark.style = ArdourCanvas::Ruler::Mark::Major;
1862 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1864 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1867 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1870 mark.position = pos/1000.0;
1871 marks.push_back (mark);
1875 case minsec_show_minutes:
1876 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1877 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1878 if (mins % minsec_mark_modulo == 0) {
1880 mark.style = ArdourCanvas::Ruler::Mark::Major;
1882 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1884 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1887 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1890 mark.position = pos/1000.0;
1891 marks.push_back (mark);
1895 case minsec_show_hours:
1896 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1897 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1898 if (hrs % minsec_mark_modulo == 0) {
1899 mark.style = ArdourCanvas::Ruler::Mark::Major;
1900 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1903 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1906 mark.position = pos/1000.0;
1907 marks.push_back (mark);
1911 case minsec_show_many_hours:
1912 for (n = 0; n < minsec_nmarks; ) {
1913 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1914 if (hrs % minsec_mark_modulo == 0) {
1915 mark.style = ArdourCanvas::Ruler::Mark::Major;
1916 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1918 mark.position = pos/1000.0;
1919 marks.push_back (mark);
1922 pos += minsec_mark_interval;