2 * Copyright (C) 2005-2017 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2005 Taybin Rutkin <taybin@taybin.com>
4 * Copyright (C) 2006-2010 David Robillard <d@drobilla.net>
5 * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
6 * Copyright (C) 2013-2017 Tim Mayberry <mojofunk@gmail.com>
7 * Copyright (C) 2013-2018 Robin Gareus <robin@gareus.org>
8 * Copyright (C) 2015-2016 Nick Mainsbridge <mainsbridge@gmail.com>
9 * Copyright (C) 2015-2018 Ben Loftis <ben@harrisonconsoles.com>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "gtk2ardour-config.h"
30 #include <cstdio> // for sprintf, grrr
36 #include <gtk/gtkaction.h>
38 #include "canvas/container.h"
39 #include "canvas/canvas.h"
40 #include "canvas/ruler.h"
41 #include "canvas/debug.h"
42 #include "canvas/scroll_group.h"
44 #include "ardour/session.h"
45 #include "ardour/tempo.h"
46 #include "ardour/profile.h"
48 #include "gtkmm2ext/gtk_ui.h"
49 #include "gtkmm2ext/keyboard.h"
51 #include "ardour_ui.h"
55 #include "gui_thread.h"
56 #include "ruler_dialog.h"
57 #include "time_axis_view.h"
58 #include "editor_drag.h"
59 #include "editor_cursors.h"
60 #include "ui_config.h"
64 using namespace ARDOUR;
67 using namespace Editing;
69 /* the order here must match the "metric" enums in editor.h */
71 class TimecodeMetric : public ArdourCanvas::Ruler::Metric
74 TimecodeMetric (Editor* e) : _editor (e) {}
76 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
77 _editor->metric_get_timecode (marks, lower, upper, maxchars);
84 class SamplesMetric : public ArdourCanvas::Ruler::Metric
87 SamplesMetric (Editor* e) : _editor (e) {}
89 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
90 _editor->metric_get_samples (marks, lower, upper, maxchars);
97 class BBTMetric : public ArdourCanvas::Ruler::Metric
100 BBTMetric (Editor* e) : _editor (e) {}
102 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
103 _editor->metric_get_bbt (marks, lower, upper, maxchars);
110 class MinsecMetric : public ArdourCanvas::Ruler::Metric
113 MinsecMetric (Editor* e) : _editor (e) {}
115 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
116 _editor->metric_get_minsec (marks, lower, upper, maxchars);
123 static ArdourCanvas::Ruler::Metric* _bbt_metric;
124 static ArdourCanvas::Ruler::Metric* _timecode_metric;
125 static ArdourCanvas::Ruler::Metric* _samples_metric;
126 static ArdourCanvas::Ruler::Metric* _minsec_metric;
129 Editor::initialize_rulers ()
131 ruler_grabbed_widget = 0;
133 Pango::FontDescription font (UIConfiguration::instance().get_SmallerFont());
135 _timecode_metric = new TimecodeMetric (this);
136 _bbt_metric = new BBTMetric (this);
137 _minsec_metric = new MinsecMetric (this);
138 _samples_metric = new SamplesMetric (this);
140 timecode_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_timecode_metric,
141 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
142 timecode_ruler->set_font_description (font);
143 CANVAS_DEBUG_NAME (timecode_ruler, "timecode ruler");
146 samples_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_samples_metric,
147 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
148 samples_ruler->set_font_description (font);
149 CANVAS_DEBUG_NAME (samples_ruler, "samples ruler");
151 minsec_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_minsec_metric,
152 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
153 minsec_ruler->set_font_description (font);
154 CANVAS_DEBUG_NAME (minsec_ruler, "minsec ruler");
157 bbt_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_bbt_metric,
158 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
159 bbt_ruler->set_font_description (font);
160 CANVAS_DEBUG_NAME (bbt_ruler, "bbt ruler");
163 using namespace Box_Helpers;
164 BoxList & lab_children = time_bars_vbox.children();
166 lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
167 lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
168 lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
169 lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
170 lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
171 lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
172 lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
173 lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
174 lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
175 lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
176 lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
178 /* 1 event handler to bind them all ... */
180 timecode_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), timecode_ruler, TimecodeRulerItem));
181 minsec_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), minsec_ruler, MinsecRulerItem));
182 bbt_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), bbt_ruler, BBTRulerItem));
183 samples_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), samples_ruler, SamplesRulerItem));
185 visible_timebars = 0; /*this will be changed below */
189 Editor::ruler_label_button_release (GdkEventButton* ev)
191 if (Gtkmm2ext::Keyboard::is_context_menu_event (ev)) {
193 ruler_dialog = new RulerDialog ();
195 ruler_dialog->present ();
202 Editor::popup_ruler_menu (samplepos_t where, ItemType t)
204 using namespace Menu_Helpers;
206 if (editor_ruler_menu == 0) {
207 editor_ruler_menu = new Menu;
208 editor_ruler_menu->set_name ("ArdourContextMenu");
211 // always build from scratch
212 MenuList& ruler_items = editor_ruler_menu->items();
213 editor_ruler_menu->set_name ("ArdourContextMenu");
218 ruler_items.push_back (MenuElem (_("New location marker"), sigc::bind (sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, false)));
219 ruler_items.push_back (MenuElem (_("Clear all locations"), sigc::mem_fun(*this, &Editor::clear_markers)));
220 ruler_items.push_back (MenuElem (_("Unhide locations"), sigc::mem_fun(*this, &Editor::unhide_markers)));
223 case RangeMarkerBarItem:
224 ruler_items.push_back (MenuElem (_("New range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_range), where)));
225 ruler_items.push_back (MenuElem (_("Clear all ranges"), sigc::mem_fun(*this, &Editor::clear_ranges)));
226 ruler_items.push_back (MenuElem (_("Unhide ranges"), sigc::mem_fun(*this, &Editor::unhide_ranges)));
229 case TransportMarkerBarItem:
230 ruler_items.push_back (MenuElem (_("New Loop range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_loop), where)));
231 ruler_items.push_back (MenuElem (_("New Punch range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_punch), where)));
234 case CdMarkerBarItem:
236 ruler_items.push_back (MenuElem (_("New CD track marker"), sigc::bind (sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, true)));
240 ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind (sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
244 ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind (sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
248 /* proper headings would be nice
249 * but AFAICT the only way to get them will be to define a
250 * special GTK style for insensitive Elements or subclass MenuItem
252 //ruler_items.push_back (MenuElem (_("Timeline height"))); // heading
253 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
254 ruler_items.push_back (CheckMenuElem (_("Large"), sigc::bind (sigc::mem_fun(*this, &Editor::set_video_timeline_height), 6)));
255 if (videotl_bar_height == 6) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
256 ruler_items.push_back (CheckMenuElem (_("Normal"), sigc::bind (sigc::mem_fun(*this, &Editor::set_video_timeline_height), 4)));
257 if (videotl_bar_height == 4) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
258 ruler_items.push_back (CheckMenuElem (_("Small"), sigc::bind (sigc::mem_fun(*this, &Editor::set_video_timeline_height), 3)));
259 if (videotl_bar_height == 3) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
261 ruler_items.push_back (SeparatorElem ());
263 //ruler_items.push_back (MenuElem (_("Align Video Track"))); // heading
264 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
265 ruler_items.push_back (CheckMenuElem (_("Lock")));
267 Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
268 vtl_lock->set_active(is_video_timeline_locked());
269 vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
272 ruler_items.push_back (SeparatorElem ());
274 //ruler_items.push_back (MenuElem (_("Video Monitor"))); // heading
275 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
276 ruler_items.push_back (CheckMenuElem (_("Video Monitor")));
278 Gtk::CheckMenuItem* xjadeo_toggle = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
279 if (!ARDOUR_UI::instance()->video_timeline->found_xjadeo()) {
280 xjadeo_toggle->set_sensitive(false);
282 xjadeo_toggle->set_active(xjadeo_proc_action->get_active());
283 xjadeo_toggle->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &Editor::toggle_xjadeo_proc), -1));
291 if (!ruler_items.empty()) {
292 editor_ruler_menu->popup (1, gtk_get_current_event_time());
295 no_ruler_shown_update = false;
299 Editor::store_ruler_visibility ()
301 XMLNode* node = new XMLNode(X_("RulerVisibility"));
303 node->set_property (X_("timecode"), ruler_timecode_action->get_active());
304 node->set_property (X_("bbt"), ruler_bbt_action->get_active());
305 node->set_property (X_("samples"), ruler_samples_action->get_active());
306 node->set_property (X_("minsec"), ruler_minsec_action->get_active());
307 node->set_property (X_("tempo"), ruler_tempo_action->get_active());
308 node->set_property (X_("meter"), ruler_meter_action->get_active());
309 node->set_property (X_("marker"), ruler_marker_action->get_active());
310 node->set_property (X_("rangemarker"), ruler_range_action->get_active());
311 node->set_property (X_("transportmarker"), ruler_loop_punch_action->get_active());
312 node->set_property (X_("cdmarker"), ruler_cd_marker_action->get_active());
313 node->set_property (X_("videotl"), ruler_video_action->get_active());
315 _session->add_extra_xml (*node);
319 Editor::restore_ruler_visibility ()
321 XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
323 no_ruler_shown_update = true;
327 if (node->get_property ("timecode", yn)) {
328 ruler_timecode_action->set_active (yn);
330 if (node->get_property ("bbt", yn)) {
331 ruler_bbt_action->set_active (yn);
333 if (node->get_property ("samples", yn)) {
334 ruler_samples_action->set_active (yn);
336 if (node->get_property ("minsec", yn)) {
337 ruler_minsec_action->set_active (yn);
339 if (node->get_property ("tempo", yn)) {
340 ruler_tempo_action->set_active (yn);
342 if (node->get_property ("meter", yn)) {
343 ruler_meter_action->set_active (yn);
345 if (node->get_property ("marker", yn)) {
346 ruler_marker_action->set_active (yn);
348 if (node->get_property ("rangemarker", yn)) {
349 ruler_range_action->set_active (yn);
351 if (node->get_property ("transportmarker", yn)) {
352 ruler_loop_punch_action->set_active (yn);
355 if (node->get_property ("cdmarker", yn)) {
356 ruler_cd_marker_action->set_active (yn);
358 // this _session doesn't yet know about the cdmarker ruler
359 // as a benefit to the user who doesn't know the feature exists, show the ruler if
360 // any cd marks exist
361 ruler_cd_marker_action->set_active (false);
362 const Locations::LocationList & locs = _session->locations()->list();
363 for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
364 if ((*i)->is_cd_marker()) {
365 ruler_cd_marker_action->set_active (true);
371 if (node->get_property ("videotl", yn)) {
372 ruler_video_action->set_active (yn);
377 no_ruler_shown_update = false;
378 update_ruler_visibility ();
382 Editor::update_ruler_visibility ()
384 int visible_timebars = 0;
386 if (no_ruler_shown_update) {
390 /* the order of the timebars is fixed, so we have to go through each one
391 * and adjust its position depending on what is shown.
393 * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
394 * loop/punch, cd markers, location markers
402 /* gtk update probs require this (damn) */
405 range_mark_label.hide();
406 transport_mark_label.hide();
407 cd_mark_label.hide();
409 videotl_label.hide();
412 if (ruler_minsec_action->get_active()) {
413 old_unit_pos = minsec_ruler->position().y;
414 if (tbpos != old_unit_pos) {
415 minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
417 minsec_ruler->show();
419 tbpos += timebar_height;
420 tbgpos += timebar_height;
423 minsec_ruler->hide();
427 if (ruler_timecode_action->get_active()) {
428 old_unit_pos = timecode_ruler->position().y;
429 if (tbpos != old_unit_pos) {
430 timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
432 timecode_ruler->show();
433 timecode_label.show();
434 tbpos += timebar_height;
435 tbgpos += timebar_height;
438 timecode_ruler->hide();
439 timecode_label.hide();
442 if (ruler_samples_action->get_active()) {
443 old_unit_pos = samples_ruler->position().y;
444 if (tbpos != old_unit_pos) {
445 samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
447 samples_ruler->show();
448 samples_label.show();
449 tbpos += timebar_height;
450 tbgpos += timebar_height;
453 samples_ruler->hide();
454 samples_label.hide();
457 if (ruler_bbt_action->get_active()) {
458 old_unit_pos = bbt_ruler->position().y;
459 if (tbpos != old_unit_pos) {
460 bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
464 tbpos += timebar_height;
465 tbgpos += timebar_height;
472 if (ruler_meter_action->get_active()) {
473 old_unit_pos = meter_group->position().y;
474 if (tbpos != old_unit_pos) {
475 meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
479 tbpos += timebar_height;
480 tbgpos += timebar_height;
487 if (ruler_tempo_action->get_active()) {
488 old_unit_pos = tempo_group->position().y;
489 if (tbpos != old_unit_pos) {
490 tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
494 tbpos += timebar_height;
495 tbgpos += timebar_height;
502 if (ruler_range_action->get_active()) {
503 old_unit_pos = range_marker_group->position().y;
504 if (tbpos != old_unit_pos) {
505 range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
507 range_marker_group->show();
508 range_mark_label.show();
510 tbpos += timebar_height;
511 tbgpos += timebar_height;
514 range_marker_group->hide();
515 range_mark_label.hide();
518 if (ruler_loop_punch_action->get_active()) {
519 old_unit_pos = transport_marker_group->position().y;
520 if (tbpos != old_unit_pos) {
521 transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
523 transport_marker_group->show();
524 transport_mark_label.show();
525 tbpos += timebar_height;
526 tbgpos += timebar_height;
529 transport_marker_group->hide();
530 transport_mark_label.hide();
533 if (ruler_cd_marker_action->get_active()) {
534 old_unit_pos = cd_marker_group->position().y;
535 if (tbpos != old_unit_pos) {
536 cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
538 cd_marker_group->show();
539 cd_mark_label.show();
540 tbpos += timebar_height;
541 tbgpos += timebar_height;
543 // make sure all cd markers show up in their respective places
544 update_cd_marker_display();
546 cd_marker_group->hide();
547 cd_mark_label.hide();
548 // make sure all cd markers show up in their respective places
549 update_cd_marker_display();
552 if (ruler_marker_action->get_active()) {
553 old_unit_pos = marker_group->position().y;
554 if (tbpos != old_unit_pos) {
555 marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
557 marker_group->show();
559 tbpos += timebar_height;
560 tbgpos += timebar_height;
563 marker_group->hide();
567 if (ruler_video_action->get_active()) {
568 old_unit_pos = videotl_group->position().y;
569 if (tbpos != old_unit_pos) {
570 videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
572 videotl_group->show();
573 videotl_label.show();
574 tbpos += timebar_height * videotl_bar_height;
575 tbgpos += timebar_height * videotl_bar_height;
576 visible_timebars+=videotl_bar_height;
577 queue_visual_videotimeline_update();
579 videotl_group->hide();
580 videotl_label.hide();
581 update_video_timeline(true);
584 time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
586 /* move hv_scroll_group (trackviews) to the end of the timebars
589 hv_scroll_group->set_y_position (timebar_height * visible_timebars);
591 compute_fixed_ruler_scale ();
592 update_fixed_rulers();
593 redisplay_grid (false);
595 /* Changing ruler visibility means that any lines on markers might need updating */
596 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
597 i->second->setup_lines ();
602 Editor::update_just_timecode ()
604 ENSURE_GUI_THREAD (*this, &Editor::update_just_timecode)
610 samplepos_t rightmost_sample = _leftmost_sample + current_page_samples();
612 if (ruler_timecode_action->get_active()) {
613 timecode_ruler->set_range (_leftmost_sample, rightmost_sample);
618 Editor::compute_fixed_ruler_scale ()
624 if (ruler_timecode_action->get_active()) {
625 set_timecode_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
628 if (ruler_minsec_action->get_active()) {
629 set_minsec_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
632 if (ruler_samples_action->get_active()) {
633 set_samples_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
638 Editor::update_fixed_rulers ()
640 samplepos_t rightmost_sample;
646 compute_fixed_ruler_scale ();
648 _timecode_metric->units_per_pixel = samples_per_pixel;
649 _samples_metric->units_per_pixel = samples_per_pixel;
650 _minsec_metric->units_per_pixel = samples_per_pixel;
652 rightmost_sample = _leftmost_sample + current_page_samples();
654 /* these force a redraw, which in turn will force execution of the metric callbacks
655 to compute the relevant ticks to display.
658 if (ruler_timecode_action->get_active()) {
659 timecode_ruler->set_range (_leftmost_sample, rightmost_sample);
662 if (ruler_samples_action->get_active()) {
663 samples_ruler->set_range (_leftmost_sample, rightmost_sample);
666 if (ruler_minsec_action->get_active()) {
667 minsec_ruler->set_range (_leftmost_sample, rightmost_sample);
672 Editor::update_tempo_based_rulers ()
678 _bbt_metric->units_per_pixel = samples_per_pixel;
680 if (ruler_bbt_action->get_active()) {
681 bbt_ruler->set_range (_leftmost_sample, _leftmost_sample+current_page_samples());
687 Editor::set_timecode_ruler_scale (samplepos_t lower, samplepos_t upper)
698 fr = _session->sample_rate();
700 if (lower > (spacer = (samplepos_t) (128 * Editor::get_current_zoom ()))) {
701 lower = lower - spacer;
706 upper = upper + spacer;
707 samplecnt_t const range = upper - lower;
709 if (range < (2 * _session->samples_per_timecode_frame())) { /* 0 - 2 samples */
710 timecode_ruler_scale = timecode_show_bits;
711 timecode_mark_modulo = 20;
712 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
713 } else if (range <= (fr / 4)) { /* 2 samples - 0.250 second */
714 timecode_ruler_scale = timecode_show_samples;
715 timecode_mark_modulo = 1;
716 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
717 } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
718 timecode_ruler_scale = timecode_show_samples;
719 timecode_mark_modulo = 2;
720 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
721 } else if (range <= fr) { /* 0.5-1 second */
722 timecode_ruler_scale = timecode_show_samples;
723 timecode_mark_modulo = 5;
724 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
725 } else if (range <= 2 * fr) { /* 1-2 seconds */
726 timecode_ruler_scale = timecode_show_samples;
727 timecode_mark_modulo = 10;
728 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
729 } else if (range <= 8 * fr) { /* 2-8 seconds */
730 timecode_ruler_scale = timecode_show_seconds;
731 timecode_mark_modulo = 1;
732 timecode_nmarks = 2 + (range / fr);
733 } else if (range <= 16 * fr) { /* 8-16 seconds */
734 timecode_ruler_scale = timecode_show_seconds;
735 timecode_mark_modulo = 2;
736 timecode_nmarks = 2 + (range / fr);
737 } else if (range <= 30 * fr) { /* 16-30 seconds */
738 timecode_ruler_scale = timecode_show_seconds;
739 timecode_mark_modulo = 5;
740 timecode_nmarks = 2 + (range / fr);
741 } else if (range <= 60 * fr) { /* 30-60 seconds */
742 timecode_ruler_scale = timecode_show_seconds;
743 timecode_mark_modulo = 5;
744 timecode_nmarks = 2 + (range / fr);
745 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
746 timecode_ruler_scale = timecode_show_seconds;
747 timecode_mark_modulo = 15;
748 timecode_nmarks = 2 + (range / fr);
749 } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
750 timecode_ruler_scale = timecode_show_seconds;
751 timecode_mark_modulo = 30;
752 timecode_nmarks = 2 + (range / fr);
753 } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
754 timecode_ruler_scale = timecode_show_minutes;
755 timecode_mark_modulo = 2;
756 timecode_nmarks = 2 + 10;
757 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
758 timecode_ruler_scale = timecode_show_minutes;
759 timecode_mark_modulo = 5;
760 timecode_nmarks = 2 + 30;
761 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
762 timecode_ruler_scale = timecode_show_minutes;
763 timecode_mark_modulo = 10;
764 timecode_nmarks = 2 + 60;
765 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
766 timecode_ruler_scale = timecode_show_minutes;
767 timecode_mark_modulo = 30;
768 timecode_nmarks = 2 + (60 * 4);
769 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
770 timecode_ruler_scale = timecode_show_hours;
771 timecode_mark_modulo = 1;
772 timecode_nmarks = 2 + 8;
773 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
774 timecode_ruler_scale = timecode_show_hours;
775 timecode_mark_modulo = 1;
776 timecode_nmarks = 2 + 24;
779 const samplecnt_t hours_in_range = range / (60 * 60 * fr);
780 const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
782 /* Normally we do not need to know anything about the width of the canvas
783 to set the ruler scale, because the caller has already determined
784 the width and set lower + upper arguments to this function to match that.
786 But in this case, where the range defined by lower and uppper can vary
787 substantially (basically anything from 24hrs+ to several billion years)
788 trying to decide which tick marks to show does require us to know
789 about the available width.
792 timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
793 timecode_ruler_scale = timecode_show_many_hours;
794 timecode_mark_modulo = max ((samplecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
799 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
803 Timecode::Time timecode;
806 ArdourCanvas::Ruler::Mark mark;
812 if (lower > (spacer = (samplecnt_t)(128 * Editor::get_current_zoom ()))) {
813 lower = lower - spacer;
818 pos = (samplecnt_t) floor (lower);
820 switch (timecode_ruler_scale) {
821 case timecode_show_bits:
822 // Find timecode time of this sample (pos) with subframe accuracy
823 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */);
824 for (n = 0; n < timecode_nmarks; n++) {
825 _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */);
826 if ((timecode.subframes % timecode_mark_modulo) == 0) {
827 if (timecode.subframes == 0) {
828 mark.style = ArdourCanvas::Ruler::Mark::Major;
829 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
831 mark.style = ArdourCanvas::Ruler::Mark::Minor;
832 snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
835 snprintf (buf, sizeof(buf)," ");
836 mark.style = ArdourCanvas::Ruler::Mark::Micro;
840 marks.push_back (mark);
841 // Increment subframes by one
842 Timecode::increment_subframes (timecode, _session->config.get_subframes_per_frame());
846 case timecode_show_samples:
847 // Find timecode time of this sample (pos)
848 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
849 // Go to next whole sample down
850 Timecode::frames_floot (timecode);
851 for (n = 0; n < timecode_nmarks; n++) {
852 _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
853 if ((timecode.frames % timecode_mark_modulo) == 0) {
854 if (timecode.frames == 0) {
855 mark.style = ArdourCanvas::Ruler::Mark::Major;
857 mark.style = ArdourCanvas::Ruler::Mark::Minor;
860 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
862 snprintf (buf, sizeof(buf)," ");
863 mark.style = ArdourCanvas::Ruler::Mark::Micro;
867 marks.push_back (mark);
868 Timecode::increment (timecode, _session->config.get_subframes_per_frame());
872 case timecode_show_seconds:
873 // Find timecode time of this sample (pos)
874 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
875 // Go to next whole second down
876 Timecode::seconds_floor (timecode);
877 for (n = 0; n < timecode_nmarks; n++) {
878 _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
879 if ((timecode.seconds % timecode_mark_modulo) == 0) {
880 if (timecode.seconds == 0) {
881 mark.style = ArdourCanvas::Ruler::Mark::Major;
884 mark.style = ArdourCanvas::Ruler::Mark::Minor;
887 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
889 snprintf (buf, sizeof(buf)," ");
890 mark.style = ArdourCanvas::Ruler::Mark::Micro;
894 marks.push_back (mark);
895 Timecode::increment_seconds (timecode, _session->config.get_subframes_per_frame());
899 case timecode_show_minutes:
900 //Find timecode time of this sample (pos)
901 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
902 // Go to next whole minute down
903 Timecode::minutes_floor (timecode);
904 for (n = 0; n < timecode_nmarks; n++) {
905 _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
906 if ((timecode.minutes % timecode_mark_modulo) == 0) {
907 if (timecode.minutes == 0) {
908 mark.style = ArdourCanvas::Ruler::Mark::Major;
910 mark.style = ArdourCanvas::Ruler::Mark::Minor;
912 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
914 snprintf (buf, sizeof(buf)," ");
915 mark.style = ArdourCanvas::Ruler::Mark::Micro;
919 marks.push_back (mark);
920 Timecode::increment_minutes (timecode, _session->config.get_subframes_per_frame());
923 case timecode_show_hours:
924 // Find timecode time of this sample (pos)
925 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
926 // Go to next whole hour down
927 Timecode::hours_floor (timecode);
928 for (n = 0; n < timecode_nmarks; n++) {
929 _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
930 if ((timecode.hours % timecode_mark_modulo) == 0) {
931 mark.style = ArdourCanvas::Ruler::Mark::Major;
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_hours (timecode, _session->config.get_subframes_per_frame());
943 case timecode_show_many_hours:
944 // Find timecode time of this sample (pos)
945 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
946 // Go to next whole hour down
947 Timecode::hours_floor (timecode);
949 for (n = 0; n < timecode_nmarks;) {
950 _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
951 if ((timecode.hours % timecode_mark_modulo) == 0) {
952 mark.style = ArdourCanvas::Ruler::Mark::Major;
953 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
956 marks.push_back (mark);
959 /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
960 * and doing it 1 hour at a time is just stupid (and slow).
962 timecode.hours += timecode_mark_modulo - (timecode.hours % timecode_mark_modulo);
969 Editor::compute_bbt_ruler_scale (samplepos_t lower, samplepos_t upper)
975 std::vector<TempoMap::BBTPoint>::const_iterator i;
976 Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
977 double floor_lower_beat = floor(max (0.0, _session->tempo_map().beat_at_sample (lower)));
979 if (floor_lower_beat < 0.0) {
980 floor_lower_beat = 0.0;
983 const samplecnt_t beat_before_lower_pos = _session->tempo_map().sample_at_beat (floor_lower_beat);
984 const samplecnt_t beat_after_upper_pos = _session->tempo_map().sample_at_beat (floor (max (0.0, _session->tempo_map().beat_at_sample (upper))) + 1.0);
986 _session->bbt_time (beat_before_lower_pos, lower_beat);
987 _session->bbt_time (beat_after_upper_pos, upper_beat);
990 bbt_accent_modulo = 1;
991 bbt_bar_helper_on = false;
995 bbt_ruler_scale = bbt_show_many;
997 switch (_grid_type) {
998 case GridTypeBeatDiv2:
999 bbt_beat_subdivision = 2;
1001 case GridTypeBeatDiv3:
1002 bbt_beat_subdivision = 3;
1004 case GridTypeBeatDiv4:
1005 bbt_beat_subdivision = 4;
1007 case GridTypeBeatDiv5:
1008 bbt_beat_subdivision = 5;
1009 bbt_accent_modulo = 2; // XXX YIKES
1011 case GridTypeBeatDiv6:
1012 bbt_beat_subdivision = 3;
1013 bbt_accent_modulo = 2; // XXX YIKES
1015 case GridTypeBeatDiv7:
1016 bbt_beat_subdivision = 7;
1017 bbt_accent_modulo = 2; // XXX YIKES
1019 case GridTypeBeatDiv8:
1020 bbt_beat_subdivision = 4;
1021 bbt_accent_modulo = 2;
1023 case GridTypeBeatDiv10:
1024 bbt_beat_subdivision = 5;
1025 bbt_accent_modulo = 2; // XXX YIKES
1027 case GridTypeBeatDiv12:
1028 bbt_beat_subdivision = 3;
1029 bbt_accent_modulo = 3;
1031 case GridTypeBeatDiv14:
1032 bbt_beat_subdivision = 7;
1033 bbt_accent_modulo = 3; // XXX YIKES!
1035 case GridTypeBeatDiv16:
1036 bbt_beat_subdivision = 4;
1037 bbt_accent_modulo = 4;
1039 case GridTypeBeatDiv20:
1040 bbt_beat_subdivision = 5;
1041 bbt_accent_modulo = 5;
1043 case GridTypeBeatDiv24:
1044 bbt_beat_subdivision = 6;
1045 bbt_accent_modulo = 6;
1047 case GridTypeBeatDiv28:
1048 bbt_beat_subdivision = 7;
1049 bbt_accent_modulo = 7;
1051 case GridTypeBeatDiv32:
1052 bbt_beat_subdivision = 4;
1053 bbt_accent_modulo = 8;
1057 bbt_beat_subdivision = 4;
1060 case GridTypeTimecode:
1061 case GridTypeMinSec:
1062 case GridTypeCDFrame:
1063 bbt_beat_subdivision = 4;
1067 const double ceil_upper_beat = floor (max (0.0, _session->tempo_map().beat_at_sample (upper))) + 1.0;
1068 if (ceil_upper_beat == floor_lower_beat) {
1072 bbt_bars = _session->tempo_map().bbt_at_beat (ceil_upper_beat).bars - _session->tempo_map().bbt_at_beat (floor_lower_beat).bars;
1074 beats = (ceil_upper_beat - floor_lower_beat);// - bbt_bars; possible thinko; this fixes the problem (for me) where measure lines alternately appear&disappear while playing at certain zoom scales
1075 double beat_density = ((beats + 1) * ((double) (upper - lower) / (double) (1 + beat_after_upper_pos - beat_before_lower_pos))) / 5.0;
1077 /* Only show the bar helper if there aren't many bars on the screen */
1078 if ((bbt_bars < 2) || (beats < 5)) {
1079 bbt_bar_helper_on = true;
1082 //set upper limits on the beat_density based on the user's grid selection
1083 if (_grid_type == GridTypeBar) {
1084 beat_density = fmax (beat_density, 16.01);
1085 } else if (_grid_type == GridTypeBeat) {
1086 beat_density = fmax (beat_density, 4.01);
1087 } else if (_grid_type == GridTypeBeatDiv2) {
1088 beat_density = fmax (beat_density, 2.01);
1089 } else if (_grid_type == GridTypeBeatDiv4) {
1090 beat_density = fmax (beat_density, 1.001);
1091 } else if (_grid_type == GridTypeBeatDiv8) {
1092 beat_density = fmax (beat_density, 0.501);
1093 } else if (_grid_type == GridTypeBeatDiv16) {
1094 beat_density = fmax (beat_density, 0.2501);
1095 } else if (_grid_type == GridTypeBeatDiv32) {
1096 beat_density = fmax (beat_density, 0.12501);
1099 if (beat_density > 2048) {
1100 bbt_ruler_scale = bbt_show_many;
1101 } else if (beat_density > 512) {
1102 bbt_ruler_scale = bbt_show_64;
1103 } else if (beat_density > 256) {
1104 bbt_ruler_scale = bbt_show_16;
1105 } else if (beat_density > 64) {
1106 bbt_ruler_scale = bbt_show_4;
1107 } else if (beat_density > 16) {
1108 bbt_ruler_scale = bbt_show_1;
1109 } else if (beat_density > 4) {
1110 bbt_ruler_scale = bbt_show_quarters;
1111 } else if (beat_density > 2) {
1112 bbt_ruler_scale = bbt_show_eighths;
1113 } else if (beat_density > 1) {
1114 bbt_ruler_scale = bbt_show_sixteenths;
1116 bbt_ruler_scale = bbt_show_thirtyseconds;
1121 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1123 ArdourCanvas::Ruler::Mark copy = marks.back();
1124 copy.label = newlabel;
1126 marks.push_back (copy);
1130 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1132 if (_session == 0) {
1136 std::vector<TempoMap::BBTPoint>::const_iterator i;
1141 Timecode::BBT_Time next_beat;
1146 double bbt_position_of_helper;
1147 bool i_am_accented = false;
1148 bool helper_active = false;
1149 ArdourCanvas::Ruler::Mark mark;
1151 std::vector<TempoMap::BBTPoint> grid;
1153 compute_current_bbt_points (grid, lower, upper);
1155 if (distance (grid.begin(), grid.end()) == 0) {
1159 switch (bbt_ruler_scale) {
1161 case bbt_show_quarters:
1163 beats = distance (grid.begin(), grid.end());
1164 bbt_nmarks = beats + 2;
1167 mark.position = lower;
1168 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1169 marks.push_back (mark);
1171 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1173 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1174 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1175 edit_last_mark_label (marks, buf);
1178 if ((*i).is_bar()) {
1179 mark.style = ArdourCanvas::Ruler::Mark::Major;
1180 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1181 } else if (((*i).beat % 2 == 1)) {
1182 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1185 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1189 mark.position = (*i).sample;
1190 marks.push_back (mark);
1196 case bbt_show_eighths:
1198 beats = distance (grid.begin(), grid.end());
1199 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1201 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1203 // could do marks.assign() here to preallocate
1206 mark.position = lower;
1207 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1208 marks.push_back (mark);
1210 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1212 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1213 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1214 edit_last_mark_label (marks, buf);
1215 helper_active = true;
1218 if ((*i).is_bar()) {
1219 mark.style = ArdourCanvas::Ruler::Mark::Major;
1220 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1222 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1223 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1225 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1229 mark.position = (*i).sample;
1230 marks.push_back (mark);
1234 /* Add the tick marks */
1235 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1236 tick = skip; // the first non-beat tick
1238 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1240 next_beat.beats = (*i).beat;
1241 next_beat.bars = (*i).bar;
1242 next_beat.ticks = tick;
1243 pos = _session->tempo_map().sample_at_bbt (next_beat);
1245 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1246 i_am_accented = true;
1249 mark.position = pos;
1251 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1252 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1254 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1256 i_am_accented = false;
1257 marks.push_back (mark);
1267 case bbt_show_sixteenths:
1269 beats = distance (grid.begin(), grid.end());
1270 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1272 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1275 mark.position = lower;
1276 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1277 marks.push_back (mark);
1279 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1281 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1282 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1283 edit_last_mark_label (marks, buf);
1284 helper_active = true;
1287 if ((*i).is_bar()) {
1288 mark.style = ArdourCanvas::Ruler::Mark::Major;
1289 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1291 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1292 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1294 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1298 mark.position = (*i).sample;
1299 marks.push_back (mark);
1303 /* Add the tick marks */
1304 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1305 tick = skip; // the first non-beat tick
1308 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1310 next_beat.beats = (*i).beat;
1311 next_beat.bars = (*i).bar;
1312 next_beat.ticks = tick;
1313 pos = _session->tempo_map().sample_at_bbt (next_beat);
1315 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1316 i_am_accented = true;
1318 if (i_am_accented && (pos > bbt_position_of_helper)){
1319 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1325 mark.position = pos;
1327 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1328 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1330 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1332 i_am_accented = false;
1333 marks.push_back (mark);
1343 case bbt_show_thirtyseconds:
1345 beats = distance (grid.begin(), grid.end());
1346 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1348 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1351 mark.position = lower;
1352 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1353 marks.push_back (mark);
1355 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1357 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1358 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1359 edit_last_mark_label (marks, buf);
1360 helper_active = true;
1363 if ((*i).is_bar()) {
1364 mark.style = ArdourCanvas::Ruler::Mark::Major;
1365 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1367 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1368 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1370 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1374 mark.position = (*i).sample;
1375 marks.push_back (mark);
1379 /* Add the tick marks */
1380 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1382 next_beat.beats = (*i).beat;
1383 next_beat.bars = (*i).bar;
1384 tick = skip; // the first non-beat tick
1386 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1388 next_beat.ticks = tick;
1389 pos = _session->tempo_map().sample_at_bbt (next_beat);
1390 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1391 i_am_accented = true;
1394 if (pos > bbt_position_of_helper) {
1395 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1401 mark.position = pos;
1403 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1404 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1406 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1408 i_am_accented = false;
1409 marks.push_back (mark);
1421 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars);
1422 mark.style = ArdourCanvas::Ruler::Mark::Major;
1424 mark.position = lower;
1425 marks.push_back (mark);
1429 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1430 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1431 if ((*i).is_bar()) {
1432 if ((*i).bar % 64 == 1) {
1433 if ((*i).bar % 256 == 1) {
1434 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1435 mark.style = ArdourCanvas::Ruler::Mark::Major;
1438 if ((*i).bar % 256 == 129) {
1439 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1441 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1445 mark.position = (*i).sample;
1446 marks.push_back (mark);
1454 bbt_nmarks = (bbt_bars / 16) + 1;
1455 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1456 if ((*i).is_bar()) {
1457 if ((*i).bar % 16 == 1) {
1458 if ((*i).bar % 64 == 1) {
1459 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1460 mark.style = ArdourCanvas::Ruler::Mark::Major;
1463 if ((*i).bar % 64 == 33) {
1464 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1466 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1470 mark.position = (*i).sample;
1471 marks.push_back (mark);
1479 bbt_nmarks = (bbt_bars / 4) + 1;
1480 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1481 if ((*i).is_bar()) {
1482 if ((*i).bar % 4 == 1) {
1483 if ((*i).bar % 16 == 1) {
1484 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1485 mark.style = ArdourCanvas::Ruler::Mark::Major;
1488 if ((*i).bar % 16 == 9) {
1489 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1491 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1495 mark.position = (*i).sample;
1496 marks.push_back (mark);
1505 bbt_nmarks = bbt_bars + 2;
1506 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1507 if ((*i).is_bar()) {
1508 if ((*i).bar % 4 == 1) {
1509 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1510 mark.style = ArdourCanvas::Ruler::Mark::Major;
1513 if ((*i).bar % 4 == 3) {
1514 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1516 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1520 mark.position = (*i).sample;
1521 marks.push_back (mark);
1531 Editor::set_samples_ruler_scale (samplepos_t lower, samplepos_t upper)
1533 _samples_ruler_interval = (upper - lower) / 5;
1537 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1540 samplepos_t const ilower = (samplepos_t) floor (lower);
1544 ArdourCanvas::Ruler::Mark mark;
1546 if (_session == 0) {
1551 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1552 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1554 mark.position = pos;
1555 mark.style = ArdourCanvas::Ruler::Mark::Major;
1556 marks.push_back (mark);
1561 sample_to_clock_parts (samplepos_t sample,
1562 samplepos_t sample_rate,
1575 hrs = left / (sample_rate * 60 * 60 * 1000);
1576 left -= hrs * sample_rate * 60 * 60 * 1000;
1577 mins = left / (sample_rate * 60 * 1000);
1578 left -= mins * sample_rate * 60 * 1000;
1579 secs = left / (sample_rate * 1000);
1580 left -= secs * sample_rate * 1000;
1581 millisecs = left / sample_rate;
1583 *millisecs_p = millisecs;
1592 Editor::set_minsec_ruler_scale (samplepos_t lower, samplepos_t upper)
1594 samplepos_t fr = _session->sample_rate() * 1000;
1597 if (_session == 0) {
1602 /* to prevent 'flashing' */
1603 if (lower > (spacer = (samplepos_t)(128 * Editor::get_current_zoom ()))) {
1609 samplecnt_t const range = (upper - lower) * 1000;
1611 if (range <= (fr / 10)) { /* 0-0.1 second */
1612 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1613 minsec_ruler_scale = minsec_show_msecs;
1614 minsec_mark_modulo = 10;
1615 minsec_nmarks = 2 + (range / minsec_mark_interval);
1616 } else if (range <= (fr / 2)) { /* 0-0.5 second */
1617 minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1618 minsec_ruler_scale = minsec_show_msecs;
1619 minsec_mark_modulo = 100;
1620 minsec_nmarks = 2 + (range / minsec_mark_interval);
1621 } else if (range <= fr) { /* 0-1 second */
1622 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1623 minsec_ruler_scale = minsec_show_msecs;
1624 minsec_mark_modulo = 200;
1625 minsec_nmarks = 2 + (range / minsec_mark_interval);
1626 } else if (range <= 2 * fr) { /* 1-2 seconds */
1627 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1628 minsec_ruler_scale = minsec_show_msecs;
1629 minsec_mark_modulo = 500;
1630 minsec_nmarks = 2 + (range / minsec_mark_interval);
1631 } else if (range <= 8 * fr) { /* 2-5 seconds */
1632 minsec_mark_interval = fr / 5; /* show 2 seconds */
1633 minsec_ruler_scale = minsec_show_msecs;
1634 minsec_mark_modulo = 1000;
1635 minsec_nmarks = 2 + (range / minsec_mark_interval);
1636 } else if (range <= 16 * fr) { /* 8-16 seconds */
1637 minsec_mark_interval = fr; /* show 1 seconds */
1638 minsec_ruler_scale = minsec_show_seconds;
1639 minsec_mark_modulo = 2;
1640 minsec_nmarks = 2 + (range / minsec_mark_interval);
1641 } else if (range <= 30 * fr) { /* 10-30 seconds */
1642 minsec_mark_interval = fr; /* show 1 seconds */
1643 minsec_ruler_scale = minsec_show_seconds;
1644 minsec_mark_modulo = 5;
1645 minsec_nmarks = 2 + (range / minsec_mark_interval);
1646 } else if (range <= 60 * fr) { /* 30-60 seconds */
1647 minsec_mark_interval = fr; /* show 1 seconds */
1648 minsec_ruler_scale = minsec_show_seconds;
1649 minsec_mark_modulo = 5;
1650 minsec_nmarks = 2 + (range / minsec_mark_interval);
1651 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1652 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1653 minsec_ruler_scale = minsec_show_seconds;
1654 minsec_mark_modulo = 3;
1655 minsec_nmarks = 2 + (range / minsec_mark_interval);
1656 } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1657 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1658 minsec_ruler_scale = minsec_show_seconds;
1659 minsec_mark_modulo = 30;
1660 minsec_nmarks = 2 + (range / minsec_mark_interval);
1661 } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1662 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1663 minsec_ruler_scale = minsec_show_seconds;
1664 minsec_mark_modulo = 120;
1665 minsec_nmarks = 2 + (range / minsec_mark_interval);
1666 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1667 minsec_mark_interval = 60 * fr; /* show 1 minute */
1668 minsec_ruler_scale = minsec_show_minutes;
1669 minsec_mark_modulo = 5;
1670 minsec_nmarks = 2 + (range / minsec_mark_interval);
1671 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1672 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1673 minsec_ruler_scale = minsec_show_minutes;
1674 minsec_mark_modulo = 10;
1675 minsec_nmarks = 2 + (range / minsec_mark_interval);
1676 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1677 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1678 minsec_ruler_scale = minsec_show_minutes;
1679 minsec_mark_modulo = 30;
1680 minsec_nmarks = 2 + (range / minsec_mark_interval);
1681 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1682 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1683 minsec_ruler_scale = minsec_show_minutes;
1684 minsec_mark_modulo = 60;
1685 minsec_nmarks = 2 + (range / minsec_mark_interval);
1686 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1687 minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1688 minsec_ruler_scale = minsec_show_hours;
1689 minsec_mark_modulo = 2;
1690 minsec_nmarks = 2 + (range / minsec_mark_interval);
1693 const samplecnt_t hours_in_range = range / (60 * 60 * fr);
1694 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1696 /* Normally we do not need to know anything about the width of the canvas
1697 to set the ruler scale, because the caller has already determined
1698 the width and set lower + upper arguments to this function to match that.
1700 But in this case, where the range defined by lower and uppper can vary
1701 substantially (anything from 24hrs+ to several billion years)
1702 trying to decide which tick marks to show does require us to know
1703 about the available width.
1706 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1707 minsec_mark_modulo = max ((samplecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1708 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1709 minsec_ruler_scale = minsec_show_many_hours;
1714 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1718 long hrs, mins, secs, millisecs;
1721 ArdourCanvas::Ruler::Mark mark;
1723 if (_session == 0) {
1727 /* to prevent 'flashing' */
1728 if (lower > (spacer = (samplepos_t) (128 * Editor::get_current_zoom ()))) {
1729 lower = lower - spacer;
1734 if (minsec_mark_interval == 0) { //we got here too early; divide-by-zero imminent
1738 pos = (((1000 * (samplepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1740 switch (minsec_ruler_scale) {
1742 case minsec_show_msecs:
1743 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1744 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1745 if (millisecs % minsec_mark_modulo == 0) {
1746 if (millisecs == 0) {
1747 mark.style = ArdourCanvas::Ruler::Mark::Major;
1749 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1751 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1754 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1757 mark.position = pos/1000.0;
1758 marks.push_back (mark);
1762 case minsec_show_seconds:
1763 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1764 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1765 if (secs % minsec_mark_modulo == 0) {
1767 mark.style = ArdourCanvas::Ruler::Mark::Major;
1769 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1771 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1774 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1777 mark.position = pos/1000.0;
1778 marks.push_back (mark);
1782 case minsec_show_minutes:
1783 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1784 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1785 if (mins % minsec_mark_modulo == 0) {
1787 mark.style = ArdourCanvas::Ruler::Mark::Major;
1789 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1791 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1794 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1797 mark.position = pos/1000.0;
1798 marks.push_back (mark);
1802 case minsec_show_hours:
1803 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1804 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1805 if (hrs % minsec_mark_modulo == 0) {
1806 mark.style = ArdourCanvas::Ruler::Mark::Major;
1807 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1810 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1813 mark.position = pos/1000.0;
1814 marks.push_back (mark);
1818 case minsec_show_many_hours:
1819 for (n = 0; n < minsec_nmarks;) {
1820 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1821 if (hrs % minsec_mark_modulo == 0) {
1822 mark.style = ArdourCanvas::Ruler::Mark::Major;
1823 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1825 mark.position = pos/1000.0;
1826 marks.push_back (mark);
1829 pos += minsec_mark_interval;