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)));
215 ruler_items.push_back (SeparatorElem ());
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)));
221 ruler_items.push_back (SeparatorElem ());
224 case TransportMarkerBarItem:
225 ruler_items.push_back (MenuElem (_("New Loop range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_loop), where)));
226 ruler_items.push_back (MenuElem (_("New Punch range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_punch), where)));
229 case CdMarkerBarItem:
231 ruler_items.push_back (MenuElem (_("New CD track marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, true)));
236 ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
240 ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
244 /* proper headings would be nice
245 * but AFAICT the only way to get them will be to define a
246 * special GTK style for insensitive Elements or subclass MenuItem
248 //ruler_items.push_back (MenuElem (_("Timeline height"))); // heading
249 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
250 ruler_items.push_back (CheckMenuElem (_("Large"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 6)));
251 if (videotl_bar_height == 6) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
252 ruler_items.push_back (CheckMenuElem (_("Normal"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 4)));
253 if (videotl_bar_height == 4) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
254 ruler_items.push_back (CheckMenuElem (_("Small"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 3)));
255 if (videotl_bar_height == 3) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
257 ruler_items.push_back (SeparatorElem ());
259 //ruler_items.push_back (MenuElem (_("Align Video Track"))); // heading
260 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
261 ruler_items.push_back (CheckMenuElem (_("Lock")));
263 Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
264 vtl_lock->set_active(is_video_timeline_locked());
265 vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
268 ruler_items.push_back (SeparatorElem ());
270 //ruler_items.push_back (MenuElem (_("Video Monitor"))); // heading
271 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
272 ruler_items.push_back (CheckMenuElem (_("Video Monitor")));
274 Gtk::CheckMenuItem* xjadeo_toggle = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
275 if (!ARDOUR_UI::instance()->video_timeline->found_xjadeo()) {
276 xjadeo_toggle->set_sensitive(false);
278 xjadeo_toggle->set_active(xjadeo_proc_action->get_active());
279 xjadeo_toggle->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &Editor::toggle_xjadeo_proc), -1));
287 if (!ruler_items.empty()) {
288 editor_ruler_menu->popup (1, gtk_get_current_event_time());
291 no_ruler_shown_update = false;
295 Editor::store_ruler_visibility ()
297 XMLNode* node = new XMLNode(X_("RulerVisibility"));
299 node->add_property (X_("timecode"), ruler_timecode_action->get_active() ? "yes": "no");
300 node->add_property (X_("bbt"), ruler_bbt_action->get_active() ? "yes": "no");
301 node->add_property (X_("samples"), ruler_samples_action->get_active() ? "yes": "no");
302 node->add_property (X_("minsec"), ruler_minsec_action->get_active() ? "yes": "no");
303 node->add_property (X_("tempo"), ruler_tempo_action->get_active() ? "yes": "no");
304 node->add_property (X_("meter"), ruler_meter_action->get_active() ? "yes": "no");
305 node->add_property (X_("marker"), ruler_marker_action->get_active() ? "yes": "no");
306 node->add_property (X_("rangemarker"), ruler_range_action->get_active() ? "yes": "no");
307 node->add_property (X_("transportmarker"), ruler_loop_punch_action->get_active() ? "yes": "no");
308 node->add_property (X_("cdmarker"), ruler_cd_marker_action->get_active() ? "yes": "no");
309 node->add_property (X_("videotl"), ruler_video_action->get_active() ? "yes": "no");
311 _session->add_extra_xml (*node);
312 _session->set_dirty ();
316 Editor::restore_ruler_visibility ()
319 XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
321 no_ruler_shown_update = true;
324 if ((prop = node->property ("timecode")) != 0) {
325 if (string_is_affirmative (prop->value())) {
326 ruler_timecode_action->set_active (true);
328 ruler_timecode_action->set_active (false);
331 if ((prop = node->property ("bbt")) != 0) {
332 if (string_is_affirmative (prop->value())) {
333 ruler_bbt_action->set_active (true);
335 ruler_bbt_action->set_active (false);
338 if ((prop = node->property ("samples")) != 0) {
339 if (string_is_affirmative (prop->value())) {
340 ruler_samples_action->set_active (true);
342 ruler_samples_action->set_active (false);
345 if ((prop = node->property ("minsec")) != 0) {
346 if (string_is_affirmative (prop->value())) {
347 ruler_minsec_action->set_active (true);
349 ruler_minsec_action->set_active (false);
352 if ((prop = node->property ("tempo")) != 0) {
353 if (string_is_affirmative (prop->value())) {
354 ruler_tempo_action->set_active (true);
356 ruler_tempo_action->set_active (false);
359 if ((prop = node->property ("meter")) != 0) {
360 if (string_is_affirmative (prop->value())) {
361 ruler_meter_action->set_active (true);
363 ruler_meter_action->set_active (false);
366 if ((prop = node->property ("marker")) != 0) {
367 if (string_is_affirmative (prop->value())) {
368 ruler_marker_action->set_active (true);
370 ruler_marker_action->set_active (false);
373 if ((prop = node->property ("rangemarker")) != 0) {
374 if (string_is_affirmative (prop->value())) {
375 ruler_range_action->set_active (true);
377 ruler_range_action->set_active (false);
381 if ((prop = node->property ("transportmarker")) != 0) {
382 if (string_is_affirmative (prop->value())) {
383 ruler_loop_punch_action->set_active (true);
385 ruler_loop_punch_action->set_active (false);
389 if ((prop = node->property ("cdmarker")) != 0) {
390 if (string_is_affirmative (prop->value())) {
391 ruler_cd_marker_action->set_active (true);
393 ruler_cd_marker_action->set_active (false);
397 // this _session doesn't yet know about the cdmarker ruler
398 // as a benefit to the user who doesn't know the feature exists, show the ruler if
399 // any cd marks exist
400 ruler_cd_marker_action->set_active (false);
401 const Locations::LocationList & locs = _session->locations()->list();
402 for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
403 if ((*i)->is_cd_marker()) {
404 ruler_cd_marker_action->set_active (true);
410 if ((prop = node->property ("videotl")) != 0) {
411 if (string_is_affirmative (prop->value())) {
412 ruler_video_action->set_active (true);
414 ruler_video_action->set_active (false);
420 no_ruler_shown_update = false;
421 update_ruler_visibility ();
425 Editor::update_ruler_visibility ()
427 int visible_timebars = 0;
429 if (no_ruler_shown_update) {
433 /* the order of the timebars is fixed, so we have to go through each one
434 * and adjust its position depending on what is shown.
436 * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
437 * loop/punch, cd markers, location markers
445 /* gtk update probs require this (damn) */
448 range_mark_label.hide();
449 transport_mark_label.hide();
450 cd_mark_label.hide();
452 videotl_label.hide();
455 if (ruler_minsec_action->get_active()) {
456 old_unit_pos = minsec_ruler->position().y;
457 if (tbpos != old_unit_pos) {
458 minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
460 minsec_ruler->show();
462 tbpos += timebar_height;
463 tbgpos += timebar_height;
466 minsec_ruler->hide();
470 if (ruler_timecode_action->get_active()) {
471 old_unit_pos = timecode_ruler->position().y;
472 if (tbpos != old_unit_pos) {
473 timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
475 timecode_ruler->show();
476 timecode_label.show();
477 tbpos += timebar_height;
478 tbgpos += timebar_height;
481 timecode_ruler->hide();
482 timecode_label.hide();
485 if (ruler_samples_action->get_active()) {
486 old_unit_pos = samples_ruler->position().y;
487 if (tbpos != old_unit_pos) {
488 samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
490 samples_ruler->show();
491 samples_label.show();
492 tbpos += timebar_height;
493 tbgpos += timebar_height;
496 samples_ruler->hide();
497 samples_label.hide();
500 if (ruler_bbt_action->get_active()) {
501 old_unit_pos = bbt_ruler->position().y;
502 if (tbpos != old_unit_pos) {
503 bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
507 tbpos += timebar_height;
508 tbgpos += timebar_height;
515 if (ruler_meter_action->get_active()) {
516 old_unit_pos = meter_group->position().y;
517 if (tbpos != old_unit_pos) {
518 meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
522 tbpos += timebar_height;
523 tbgpos += timebar_height;
530 if (ruler_tempo_action->get_active()) {
531 old_unit_pos = tempo_group->position().y;
532 if (tbpos != old_unit_pos) {
533 tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
537 tbpos += timebar_height;
538 tbgpos += timebar_height;
545 if (!Profile->get_sae() && ruler_range_action->get_active()) {
546 old_unit_pos = range_marker_group->position().y;
547 if (tbpos != old_unit_pos) {
548 range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
550 range_marker_group->show();
551 range_mark_label.show();
553 tbpos += timebar_height;
554 tbgpos += timebar_height;
557 range_marker_group->hide();
558 range_mark_label.hide();
561 if (ruler_loop_punch_action->get_active()) {
562 old_unit_pos = transport_marker_group->position().y;
563 if (tbpos != old_unit_pos) {
564 transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
566 transport_marker_group->show();
567 transport_mark_label.show();
568 tbpos += timebar_height;
569 tbgpos += timebar_height;
572 transport_marker_group->hide();
573 transport_mark_label.hide();
576 if (ruler_cd_marker_action->get_active()) {
577 old_unit_pos = cd_marker_group->position().y;
578 if (tbpos != old_unit_pos) {
579 cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
581 cd_marker_group->show();
582 cd_mark_label.show();
583 tbpos += timebar_height;
584 tbgpos += timebar_height;
586 // make sure all cd markers show up in their respective places
587 update_cd_marker_display();
589 cd_marker_group->hide();
590 cd_mark_label.hide();
591 // make sure all cd markers show up in their respective places
592 update_cd_marker_display();
595 if (ruler_marker_action->get_active()) {
596 old_unit_pos = marker_group->position().y;
597 if (tbpos != old_unit_pos) {
598 marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
600 marker_group->show();
602 tbpos += timebar_height;
603 tbgpos += timebar_height;
606 marker_group->hide();
610 if (ruler_video_action->get_active()) {
611 old_unit_pos = videotl_group->position().y;
612 if (tbpos != old_unit_pos) {
613 videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
615 videotl_group->show();
616 videotl_label.show();
617 tbpos += timebar_height * videotl_bar_height;
618 tbgpos += timebar_height * videotl_bar_height;
619 visible_timebars+=videotl_bar_height;
620 queue_visual_videotimeline_update();
622 videotl_group->hide();
623 videotl_label.hide();
624 update_video_timeline(true);
627 time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
629 /* move hv_scroll_group (trackviews) to the end of the timebars
632 hv_scroll_group->set_y_position (timebar_height * visible_timebars);
634 compute_fixed_ruler_scale ();
635 update_fixed_rulers();
636 redisplay_tempo (false);
638 /* Changing ruler visibility means that any lines on markers might need updating */
639 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
640 i->second->setup_lines ();
645 Editor::update_just_timecode ()
647 ENSURE_GUI_THREAD (*this, &Editor::update_just_timecode)
653 framepos_t rightmost_frame = leftmost_frame + current_page_samples();
655 if (ruler_timecode_action->get_active()) {
656 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
661 Editor::compute_fixed_ruler_scale ()
667 if (ruler_timecode_action->get_active()) {
668 set_timecode_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
671 if (ruler_minsec_action->get_active()) {
672 set_minsec_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
675 if (ruler_samples_action->get_active()) {
676 set_samples_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
681 Editor::update_fixed_rulers ()
683 framepos_t rightmost_frame;
689 compute_fixed_ruler_scale ();
691 _timecode_metric->units_per_pixel = samples_per_pixel;
692 _samples_metric->units_per_pixel = samples_per_pixel;
693 _minsec_metric->units_per_pixel = samples_per_pixel;
695 rightmost_frame = leftmost_frame + current_page_samples();
697 /* these force a redraw, which in turn will force execution of the metric callbacks
698 to compute the relevant ticks to display.
701 if (ruler_timecode_action->get_active()) {
702 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
705 if (ruler_samples_action->get_active()) {
706 samples_ruler->set_range (leftmost_frame, rightmost_frame);
709 if (ruler_minsec_action->get_active()) {
710 minsec_ruler->set_range (leftmost_frame, rightmost_frame);
715 Editor::update_tempo_based_rulers (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
716 ARDOUR::TempoMap::BBTPointList::const_iterator& end)
722 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame+current_page_samples(),
725 _bbt_metric->units_per_pixel = samples_per_pixel;
727 if (ruler_bbt_action->get_active()) {
728 bbt_ruler->set_range (leftmost_frame, leftmost_frame+current_page_samples());
734 Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
745 fr = _session->frame_rate();
747 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
748 lower = lower - spacer;
753 upper = upper + spacer;
754 framecnt_t const range = upper - lower;
756 if (range < (2 * _session->frames_per_timecode_frame())) { /* 0 - 2 frames */
757 timecode_ruler_scale = timecode_show_bits;
758 timecode_mark_modulo = 20;
759 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
760 } else if (range <= (fr / 4)) { /* 2 frames - 0.250 second */
761 timecode_ruler_scale = timecode_show_frames;
762 timecode_mark_modulo = 1;
763 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
764 } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
765 timecode_ruler_scale = timecode_show_frames;
766 timecode_mark_modulo = 2;
767 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
768 } else if (range <= fr) { /* 0.5-1 second */
769 timecode_ruler_scale = timecode_show_frames;
770 timecode_mark_modulo = 5;
771 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
772 } else if (range <= 2 * fr) { /* 1-2 seconds */
773 timecode_ruler_scale = timecode_show_frames;
774 timecode_mark_modulo = 10;
775 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
776 } else if (range <= 8 * fr) { /* 2-8 seconds */
777 timecode_ruler_scale = timecode_show_seconds;
778 timecode_mark_modulo = 1;
779 timecode_nmarks = 2 + (range / fr);
780 } else if (range <= 16 * fr) { /* 8-16 seconds */
781 timecode_ruler_scale = timecode_show_seconds;
782 timecode_mark_modulo = 2;
783 timecode_nmarks = 2 + (range / fr);
784 } else if (range <= 30 * fr) { /* 16-30 seconds */
785 timecode_ruler_scale = timecode_show_seconds;
786 timecode_mark_modulo = 5;
787 timecode_nmarks = 2 + (range / fr);
788 } else if (range <= 60 * fr) { /* 30-60 seconds */
789 timecode_ruler_scale = timecode_show_seconds;
790 timecode_mark_modulo = 5;
791 timecode_nmarks = 2 + (range / fr);
792 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
793 timecode_ruler_scale = timecode_show_seconds;
794 timecode_mark_modulo = 15;
795 timecode_nmarks = 2 + (range / fr);
796 } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
797 timecode_ruler_scale = timecode_show_seconds;
798 timecode_mark_modulo = 30;
799 timecode_nmarks = 2 + (range / fr);
800 } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
801 timecode_ruler_scale = timecode_show_minutes;
802 timecode_mark_modulo = 2;
803 timecode_nmarks = 2 + 10;
804 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
805 timecode_ruler_scale = timecode_show_minutes;
806 timecode_mark_modulo = 5;
807 timecode_nmarks = 2 + 30;
808 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
809 timecode_ruler_scale = timecode_show_minutes;
810 timecode_mark_modulo = 10;
811 timecode_nmarks = 2 + 60;
812 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
813 timecode_ruler_scale = timecode_show_minutes;
814 timecode_mark_modulo = 30;
815 timecode_nmarks = 2 + (60 * 4);
816 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
817 timecode_ruler_scale = timecode_show_hours;
818 timecode_mark_modulo = 1;
819 timecode_nmarks = 2 + 8;
820 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
821 timecode_ruler_scale = timecode_show_hours;
822 timecode_mark_modulo = 1;
823 timecode_nmarks = 2 + 24;
826 const framecnt_t hours_in_range = range / (60 * 60 * fr);
827 const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
829 /* Normally we do not need to know anything about the width of the canvas
830 to set the ruler scale, because the caller has already determined
831 the width and set lower + upper arguments to this function to match that.
833 But in this case, where the range defined by lower and uppper can vary
834 substantially (basically anything from 24hrs+ to several billion years)
835 trying to decide which tick marks to show does require us to know
836 about the available width.
839 timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
840 timecode_ruler_scale = timecode_show_many_hours;
841 timecode_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
846 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
850 Timecode::Time timecode;
853 ArdourCanvas::Ruler::Mark mark;
859 if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
860 lower = lower - spacer;
865 pos = (framecnt_t) floor (lower);
867 switch (timecode_ruler_scale) {
868 case timecode_show_bits:
869 // Find timecode time of this sample (pos) with subframe accuracy
870 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */ );
871 for (n = 0; n < timecode_nmarks; n++) {
872 _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
873 if ((timecode.subframes % timecode_mark_modulo) == 0) {
874 if (timecode.subframes == 0) {
875 mark.style = ArdourCanvas::Ruler::Mark::Major;
876 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
878 mark.style = ArdourCanvas::Ruler::Mark::Minor;
879 snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
882 snprintf (buf, sizeof(buf)," ");
883 mark.style = ArdourCanvas::Ruler::Mark::Micro;
887 marks.push_back (mark);
888 // Increment subframes by one
889 Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
893 case timecode_show_frames:
894 // Find timecode time of this sample (pos)
895 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
896 // Go to next whole frame down
897 Timecode::frames_floor( timecode );
898 for (n = 0; n < timecode_nmarks; n++) {
899 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
900 if ((timecode.frames % timecode_mark_modulo) == 0) {
901 if (timecode.frames == 0) {
902 mark.style = ArdourCanvas::Ruler::Mark::Major;
904 mark.style = ArdourCanvas::Ruler::Mark::Minor;
907 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
909 snprintf (buf, sizeof(buf)," ");
910 mark.style = ArdourCanvas::Ruler::Mark::Micro;
914 marks.push_back (mark);
915 Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
919 case timecode_show_seconds:
920 // Find timecode time of this sample (pos)
921 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
922 // Go to next whole second down
923 Timecode::seconds_floor( timecode );
924 for (n = 0; n < timecode_nmarks; n++) {
925 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
926 if ((timecode.seconds % timecode_mark_modulo) == 0) {
927 if (timecode.seconds == 0) {
928 mark.style = ArdourCanvas::Ruler::Mark::Major;
931 mark.style = ArdourCanvas::Ruler::Mark::Minor;
934 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
936 snprintf (buf, sizeof(buf)," ");
937 mark.style = ArdourCanvas::Ruler::Mark::Micro;
941 marks.push_back (mark);
942 Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
946 case timecode_show_minutes:
947 //Find timecode time of this sample (pos)
948 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
949 // Go to next whole minute down
950 Timecode::minutes_floor( timecode );
951 for (n = 0; n < timecode_nmarks; n++) {
952 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
953 if ((timecode.minutes % timecode_mark_modulo) == 0) {
954 if (timecode.minutes == 0) {
955 mark.style = ArdourCanvas::Ruler::Mark::Major;
957 mark.style = ArdourCanvas::Ruler::Mark::Minor;
959 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
961 snprintf (buf, sizeof(buf)," ");
962 mark.style = ArdourCanvas::Ruler::Mark::Micro;
966 marks.push_back (mark);
967 Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
970 case timecode_show_hours:
971 // Find timecode time of this sample (pos)
972 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
973 // Go to next whole hour down
974 Timecode::hours_floor( timecode );
975 for (n = 0; n < timecode_nmarks; n++) {
976 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
977 if ((timecode.hours % timecode_mark_modulo) == 0) {
978 mark.style = ArdourCanvas::Ruler::Mark::Major;
979 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
981 snprintf (buf, sizeof(buf)," ");
982 mark.style = ArdourCanvas::Ruler::Mark::Micro;
986 marks.push_back (mark);
987 Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
990 case timecode_show_many_hours:
991 // Find timecode time of this sample (pos)
992 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
993 // Go to next whole hour down
994 Timecode::hours_floor (timecode);
996 for (n = 0; n < timecode_nmarks; ) {
997 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
998 if ((timecode.hours % timecode_mark_modulo) == 0) {
999 mark.style = ArdourCanvas::Ruler::Mark::Major;
1000 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1002 mark.position = pos;
1003 marks.push_back (mark);
1006 /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
1007 and doing it 1 hour at a time is just stupid (and slow).
1009 timecode.hours += timecode_mark_modulo;
1016 Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
1017 ARDOUR::TempoMap::BBTPointList::const_iterator begin,
1018 ARDOUR::TempoMap::BBTPointList::const_iterator end)
1020 if (_session == 0) {
1024 TempoMap::BBTPointList::const_iterator i;
1025 Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
1027 _session->bbt_time (lower, lower_beat);
1028 _session->bbt_time (upper, upper_beat);
1031 bbt_accent_modulo = 1;
1032 bbt_bar_helper_on = false;
1036 bbt_ruler_scale = bbt_show_many;
1038 switch (_snap_type) {
1039 case SnapToBeatDiv2:
1040 bbt_beat_subdivision = 2;
1042 case SnapToBeatDiv3:
1043 bbt_beat_subdivision = 3;
1045 case SnapToBeatDiv4:
1046 bbt_beat_subdivision = 4;
1048 case SnapToBeatDiv5:
1049 bbt_beat_subdivision = 5;
1050 bbt_accent_modulo = 2; // XXX YIKES
1052 case SnapToBeatDiv6:
1053 bbt_beat_subdivision = 6;
1054 bbt_accent_modulo = 2; // XXX YIKES
1056 case SnapToBeatDiv7:
1057 bbt_beat_subdivision = 7;
1058 bbt_accent_modulo = 2; // XXX YIKES
1060 case SnapToBeatDiv8:
1061 bbt_beat_subdivision = 8;
1062 bbt_accent_modulo = 2;
1064 case SnapToBeatDiv10:
1065 bbt_beat_subdivision = 10;
1066 bbt_accent_modulo = 2; // XXX YIKES
1068 case SnapToBeatDiv12:
1069 bbt_beat_subdivision = 12;
1070 bbt_accent_modulo = 3;
1072 case SnapToBeatDiv14:
1073 bbt_beat_subdivision = 14;
1074 bbt_accent_modulo = 3; // XXX YIKES!
1076 case SnapToBeatDiv16:
1077 bbt_beat_subdivision = 16;
1078 bbt_accent_modulo = 4;
1080 case SnapToBeatDiv20:
1081 bbt_beat_subdivision = 20;
1082 bbt_accent_modulo = 5;
1084 case SnapToBeatDiv24:
1085 bbt_beat_subdivision = 24;
1086 bbt_accent_modulo = 6;
1088 case SnapToBeatDiv28:
1089 bbt_beat_subdivision = 28;
1090 bbt_accent_modulo = 7;
1092 case SnapToBeatDiv32:
1093 bbt_beat_subdivision = 32;
1094 bbt_accent_modulo = 8;
1096 case SnapToBeatDiv64:
1097 bbt_beat_subdivision = 64;
1098 bbt_accent_modulo = 8;
1100 case SnapToBeatDiv128:
1101 bbt_beat_subdivision = 128;
1102 bbt_accent_modulo = 8;
1105 bbt_beat_subdivision = 4;
1109 if (distance (begin, end) == 0) {
1115 if ((*i).beat >= (*begin).beat) {
1116 bbt_bars = (*i).bar - (*begin).bar;
1118 bbt_bars = (*i).bar - (*begin).bar - 1;
1120 beats = distance (begin, end) - bbt_bars;
1122 /* Only show the bar helper if there aren't many bars on the screen */
1123 if ((bbt_bars < 2) || (beats < 5)) {
1124 bbt_bar_helper_on = true;
1127 if (bbt_bars > 8192) {
1128 bbt_ruler_scale = bbt_show_many;
1129 } else if (bbt_bars > 1024) {
1130 bbt_ruler_scale = bbt_show_64;
1131 } else if (bbt_bars > 256) {
1132 bbt_ruler_scale = bbt_show_16;
1133 } else if (bbt_bars > 64) {
1134 bbt_ruler_scale = bbt_show_4;
1135 } else if (bbt_bars > 10) {
1136 bbt_ruler_scale = bbt_show_1;
1137 } else if (bbt_bars > 2) {
1138 bbt_ruler_scale = bbt_show_beats;
1139 } else if (bbt_bars > 0) {
1140 bbt_ruler_scale = bbt_show_ticks;
1142 bbt_ruler_scale = bbt_show_ticks_detail;
1145 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)) {
1146 bbt_ruler_scale = bbt_show_ticks_super_detail;
1151 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1153 ArdourCanvas::Ruler::Mark copy = marks.back();
1154 copy.label = newlabel;
1156 marks.push_back (copy);
1160 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1162 if (_session == 0) {
1166 TempoMap::BBTPointList::const_iterator i;
1171 Timecode::BBT_Time next_beat;
1172 framepos_t next_beat_pos;
1177 framepos_t frame_skip;
1178 double frame_skip_error;
1179 double bbt_position_of_helper;
1180 double accumulated_error;
1181 bool i_am_accented = false;
1182 bool helper_active = false;
1183 ArdourCanvas::Ruler::Mark mark;
1185 ARDOUR::TempoMap::BBTPointList::const_iterator begin;
1186 ARDOUR::TempoMap::BBTPointList::const_iterator end;
1188 compute_current_bbt_points (lower, upper, begin, end);
1190 if (distance (begin, end) == 0) {
1194 switch (bbt_ruler_scale) {
1196 case bbt_show_beats:
1197 beats = distance (begin, 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 = begin; n < bbt_nmarks && i != 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 (begin, 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 = begin; n < bbt_nmarks && i != 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 */
1271 /* Find the next beat */
1272 next_beat.beats = (*i).beat;
1273 next_beat.bars = (*i).bar;
1274 next_beat.ticks = 0;
1276 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1277 next_beat.beats += 1;
1279 next_beat.bars += 1;
1280 next_beat.beats = 1;
1283 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1285 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1286 frame_skip_error -= frame_skip;
1287 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1289 pos = (*i).frame + frame_skip;
1290 accumulated_error = frame_skip_error;
1294 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1296 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1297 i_am_accented = true;
1302 /* Error compensation for float to framepos_t*/
1303 accumulated_error += frame_skip_error;
1304 if (accumulated_error > 1) {
1306 accumulated_error -= 1.0f;
1309 mark.position = pos;
1311 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1312 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1314 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1316 i_am_accented = false;
1317 marks.push_back (mark);
1324 case bbt_show_ticks_detail:
1326 beats = distance (begin, end);
1327 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1329 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1332 mark.position = lower;
1333 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1334 marks.push_back (mark);
1336 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1338 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1339 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1340 edit_last_mark_label (marks, buf);
1341 helper_active = true;
1344 if ((*i).is_bar()) {
1345 mark.style = ArdourCanvas::Ruler::Mark::Major;
1346 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1348 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1349 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1351 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1355 mark.position = (*i).frame;
1356 marks.push_back (mark);
1360 /* Add the tick marks */
1362 /* Find the next beat */
1364 next_beat.beats = (*i).beat;
1365 next_beat.bars = (*i).bar;
1367 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1368 next_beat.beats += 1;
1370 next_beat.bars += 1;
1371 next_beat.beats = 1;
1374 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1376 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1377 frame_skip_error -= frame_skip;
1378 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1380 pos = (*i).frame + frame_skip;
1381 accumulated_error = frame_skip_error;
1385 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1387 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1388 i_am_accented = true;
1391 if (i_am_accented && (pos > bbt_position_of_helper)){
1392 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1399 /* Error compensation for float to framepos_t*/
1400 accumulated_error += frame_skip_error;
1401 if (accumulated_error > 1) {
1403 accumulated_error -= 1.0f;
1406 mark.position = pos;
1408 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1409 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1411 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1413 i_am_accented = false;
1420 case bbt_show_ticks_super_detail:
1422 beats = distance (begin, end);
1423 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1425 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1428 mark.position = lower;
1429 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1430 marks.push_back (mark);
1432 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1434 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1435 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1436 edit_last_mark_label (marks, buf);
1437 helper_active = true;
1440 if ((*i).is_bar()) {
1441 mark.style = ArdourCanvas::Ruler::Mark::Major;
1442 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1444 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1445 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1447 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1451 mark.position = (*i).frame;
1452 marks.push_back (mark);
1456 /* Add the tick marks */
1458 /* Find the next beat */
1460 next_beat.beats = (*i).beat;
1461 next_beat.bars = (*i).bar;
1463 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1464 next_beat.beats += 1;
1466 next_beat.bars += 1;
1467 next_beat.beats = 1;
1470 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1472 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1473 frame_skip_error -= frame_skip;
1474 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1476 pos = (*i).frame + frame_skip;
1477 accumulated_error = frame_skip_error;
1481 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1483 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1484 i_am_accented = true;
1487 if (pos > bbt_position_of_helper) {
1488 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1495 /* Error compensation for float to framepos_t*/
1496 accumulated_error += frame_skip_error;
1497 if (accumulated_error > 1) {
1499 accumulated_error -= 1.0f;
1502 mark.position = pos;
1504 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1505 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1507 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1509 i_am_accented = false;
1510 marks.push_back (mark);
1519 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1520 mark.style = ArdourCanvas::Ruler::Mark::Major;
1522 mark.position = lower;
1523 marks.push_back (mark);
1527 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1528 for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
1529 if ((*i).is_bar()) {
1530 if ((*i).bar % 64 == 1) {
1531 if ((*i).bar % 256 == 1) {
1532 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1533 mark.style = ArdourCanvas::Ruler::Mark::Major;
1536 if ((*i).bar % 256 == 129) {
1537 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1539 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1543 mark.position = (*i).frame;
1544 marks.push_back (mark);
1552 bbt_nmarks = (bbt_bars / 16) + 1;
1553 for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
1554 if ((*i).is_bar()) {
1555 if ((*i).bar % 16 == 1) {
1556 if ((*i).bar % 64 == 1) {
1557 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1558 mark.style = ArdourCanvas::Ruler::Mark::Major;
1561 if ((*i).bar % 64 == 33) {
1562 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1564 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1568 mark.position = (*i).frame;
1569 marks.push_back (mark);
1577 bbt_nmarks = (bbt_bars / 4) + 1;
1578 for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
1579 if ((*i).is_bar()) {
1580 if ((*i).bar % 4 == 1) {
1581 if ((*i).bar % 16 == 1) {
1582 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1583 mark.style = ArdourCanvas::Ruler::Mark::Major;
1586 if ((*i).bar % 16 == 9) {
1587 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1589 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1593 mark.position = (*i).frame;
1594 marks.push_back (mark);
1603 bbt_nmarks = bbt_bars + 2;
1604 for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
1605 if ((*i).is_bar()) {
1606 if ((*i).bar % 4 == 1) {
1607 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1608 mark.style = ArdourCanvas::Ruler::Mark::Major;
1611 if ((*i).bar % 4 == 3) {
1612 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1614 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1618 mark.position = (*i).frame;
1619 marks.push_back (mark);
1629 Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
1631 _samples_ruler_interval = (upper - lower) / 5;
1635 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1638 framepos_t const ilower = (framepos_t) floor (lower);
1642 ArdourCanvas::Ruler::Mark mark;
1644 if (_session == 0) {
1649 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1650 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1652 mark.position = pos;
1653 mark.style = ArdourCanvas::Ruler::Mark::Major;
1654 marks.push_back (mark);
1659 sample_to_clock_parts ( framepos_t sample,
1660 framepos_t sample_rate,
1674 hrs = left / (sample_rate * 60 * 60 * 1000);
1675 left -= hrs * sample_rate * 60 * 60 * 1000;
1676 mins = left / (sample_rate * 60 * 1000);
1677 left -= mins * sample_rate * 60 * 1000;
1678 secs = left / (sample_rate * 1000);
1679 left -= secs * sample_rate * 1000;
1680 millisecs = left / sample_rate;
1682 *millisecs_p = millisecs;
1691 Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
1693 framepos_t fr = _session->frame_rate() * 1000;
1696 if (_session == 0) {
1701 /* to prevent 'flashing' */
1702 if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1708 framecnt_t const range = (upper - lower) * 1000;
1710 if (range <= (fr / 10)) { /* 0-0.1 second */
1711 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1712 minsec_ruler_scale = minsec_show_msecs;
1713 minsec_mark_modulo = 10;
1714 minsec_nmarks = 2 + (range / minsec_mark_interval);
1715 } else if (range <= (fr / 2)) { /* 0-0.5 second */
1716 minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1717 minsec_ruler_scale = minsec_show_msecs;
1718 minsec_mark_modulo = 100;
1719 minsec_nmarks = 2 + (range / minsec_mark_interval);
1720 } else if (range <= fr) { /* 0-1 second */
1721 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1722 minsec_ruler_scale = minsec_show_msecs;
1723 minsec_mark_modulo = 200;
1724 minsec_nmarks = 2 + (range / minsec_mark_interval);
1725 } else if (range <= 2 * fr) { /* 1-2 seconds */
1726 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1727 minsec_ruler_scale = minsec_show_msecs;
1728 minsec_mark_modulo = 500;
1729 minsec_nmarks = 2 + (range / minsec_mark_interval);
1730 } else if (range <= 8 * fr) { /* 2-5 seconds */
1731 minsec_mark_interval = fr / 5; /* show 2 seconds */
1732 minsec_ruler_scale = minsec_show_msecs;
1733 minsec_mark_modulo = 1000;
1734 minsec_nmarks = 2 + (range / minsec_mark_interval);
1735 } else if (range <= 16 * fr) { /* 8-16 seconds */
1736 minsec_mark_interval = fr; /* show 1 seconds */
1737 minsec_ruler_scale = minsec_show_seconds;
1738 minsec_mark_modulo = 2;
1739 minsec_nmarks = 2 + (range / minsec_mark_interval);
1740 } else if (range <= 30 * fr) { /* 10-30 seconds */
1741 minsec_mark_interval = fr; /* show 1 seconds */
1742 minsec_ruler_scale = minsec_show_seconds;
1743 minsec_mark_modulo = 5;
1744 minsec_nmarks = 2 + (range / minsec_mark_interval);
1745 } else if (range <= 60 * fr) { /* 30-60 seconds */
1746 minsec_mark_interval = fr; /* show 1 seconds */
1747 minsec_ruler_scale = minsec_show_seconds;
1748 minsec_mark_modulo = 5;
1749 minsec_nmarks = 2 + (range / minsec_mark_interval);
1750 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1751 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1752 minsec_ruler_scale = minsec_show_seconds;
1753 minsec_mark_modulo = 3;
1754 minsec_nmarks = 2 + (range / minsec_mark_interval);
1755 } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1756 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1757 minsec_ruler_scale = minsec_show_seconds;
1758 minsec_mark_modulo = 30;
1759 minsec_nmarks = 2 + (range / minsec_mark_interval);
1760 } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1761 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1762 minsec_ruler_scale = minsec_show_seconds;
1763 minsec_mark_modulo = 120;
1764 minsec_nmarks = 2 + (range / minsec_mark_interval);
1765 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1766 minsec_mark_interval = 60 * fr; /* show 1 minute */
1767 minsec_ruler_scale = minsec_show_minutes;
1768 minsec_mark_modulo = 5;
1769 minsec_nmarks = 2 + (range / minsec_mark_interval);
1770 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1771 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1772 minsec_ruler_scale = minsec_show_minutes;
1773 minsec_mark_modulo = 10;
1774 minsec_nmarks = 2 + (range / minsec_mark_interval);
1775 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1776 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1777 minsec_ruler_scale = minsec_show_minutes;
1778 minsec_mark_modulo = 30;
1779 minsec_nmarks = 2 + (range / minsec_mark_interval);
1780 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1781 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1782 minsec_ruler_scale = minsec_show_minutes;
1783 minsec_mark_modulo = 60;
1784 minsec_nmarks = 2 + (range / minsec_mark_interval);
1785 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1786 minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1787 minsec_ruler_scale = minsec_show_hours;
1788 minsec_mark_modulo = 2;
1789 minsec_nmarks = 2 + (range / minsec_mark_interval);
1792 const framecnt_t hours_in_range = range / (60 * 60 * fr);
1793 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1795 /* Normally we do not need to know anything about the width of the canvas
1796 to set the ruler scale, because the caller has already determined
1797 the width and set lower + upper arguments to this function to match that.
1799 But in this case, where the range defined by lower and uppper can vary
1800 substantially (anything from 24hrs+ to several billion years)
1801 trying to decide which tick marks to show does require us to know
1802 about the available width.
1805 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1806 minsec_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1807 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1808 minsec_ruler_scale = minsec_show_many_hours;
1813 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1817 long hrs, mins, secs, millisecs;
1820 ArdourCanvas::Ruler::Mark mark;
1822 if (_session == 0) {
1826 /* to prevent 'flashing' */
1827 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
1828 lower = lower - spacer;
1833 pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1835 switch (minsec_ruler_scale) {
1837 case minsec_show_msecs:
1838 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1839 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1840 if (millisecs % minsec_mark_modulo == 0) {
1841 if (millisecs == 0) {
1842 mark.style = ArdourCanvas::Ruler::Mark::Major;
1844 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1846 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1849 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1852 mark.position = pos/1000.0;
1853 marks.push_back (mark);
1857 case minsec_show_seconds:
1858 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1859 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1860 if (secs % minsec_mark_modulo == 0) {
1862 mark.style = ArdourCanvas::Ruler::Mark::Major;
1864 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1866 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1869 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1872 mark.position = pos/1000.0;
1873 marks.push_back (mark);
1877 case minsec_show_minutes:
1878 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1879 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1880 if (mins % minsec_mark_modulo == 0) {
1882 mark.style = ArdourCanvas::Ruler::Mark::Major;
1884 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1886 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1889 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1892 mark.position = pos/1000.0;
1893 marks.push_back (mark);
1897 case minsec_show_hours:
1898 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1899 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1900 if (hrs % minsec_mark_modulo == 0) {
1901 mark.style = ArdourCanvas::Ruler::Mark::Major;
1902 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1905 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1908 mark.position = pos/1000.0;
1909 marks.push_back (mark);
1913 case minsec_show_many_hours:
1914 for (n = 0; n < minsec_nmarks; ) {
1915 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1916 if (hrs % minsec_mark_modulo == 0) {
1917 mark.style = ArdourCanvas::Ruler::Mark::Major;
1918 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1920 mark.position = pos/1000.0;
1921 marks.push_back (mark);
1924 pos += minsec_mark_interval;