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 (samplepos_t where, ItemType t)
198 using namespace Menu_Helpers;
200 if (editor_ruler_menu == 0) {
201 editor_ruler_menu = new Menu;
202 editor_ruler_menu->set_name ("ArdourContextMenu");
205 // always build from scratch
206 MenuList& ruler_items = editor_ruler_menu->items();
207 editor_ruler_menu->set_name ("ArdourContextMenu");
212 ruler_items.push_back (MenuElem (_("New location marker"), sigc::bind (sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, false)));
213 ruler_items.push_back (MenuElem (_("Clear all locations"), sigc::mem_fun(*this, &Editor::clear_markers)));
214 ruler_items.push_back (MenuElem (_("Unhide locations"), sigc::mem_fun(*this, &Editor::unhide_markers)));
217 case RangeMarkerBarItem:
218 ruler_items.push_back (MenuElem (_("New range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_range), where)));
219 ruler_items.push_back (MenuElem (_("Clear all ranges"), sigc::mem_fun(*this, &Editor::clear_ranges)));
220 ruler_items.push_back (MenuElem (_("Unhide ranges"), sigc::mem_fun(*this, &Editor::unhide_ranges)));
223 case TransportMarkerBarItem:
224 ruler_items.push_back (MenuElem (_("New Loop range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_loop), where)));
225 ruler_items.push_back (MenuElem (_("New Punch range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_punch), where)));
228 case CdMarkerBarItem:
230 ruler_items.push_back (MenuElem (_("New CD track marker"), sigc::bind (sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, true)));
234 ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind (sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
238 ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind (sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
242 /* proper headings would be nice
243 * but AFAICT the only way to get them will be to define a
244 * special GTK style for insensitive Elements or subclass MenuItem
246 //ruler_items.push_back (MenuElem (_("Timeline height"))); // heading
247 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
248 ruler_items.push_back (CheckMenuElem (_("Large"), sigc::bind (sigc::mem_fun(*this, &Editor::set_video_timeline_height), 6)));
249 if (videotl_bar_height == 6) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
250 ruler_items.push_back (CheckMenuElem (_("Normal"), sigc::bind (sigc::mem_fun(*this, &Editor::set_video_timeline_height), 4)));
251 if (videotl_bar_height == 4) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
252 ruler_items.push_back (CheckMenuElem (_("Small"), sigc::bind (sigc::mem_fun(*this, &Editor::set_video_timeline_height), 3)));
253 if (videotl_bar_height == 3) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
255 ruler_items.push_back (SeparatorElem ());
257 //ruler_items.push_back (MenuElem (_("Align Video Track"))); // heading
258 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
259 ruler_items.push_back (CheckMenuElem (_("Lock")));
261 Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
262 vtl_lock->set_active(is_video_timeline_locked());
263 vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
266 ruler_items.push_back (SeparatorElem ());
268 //ruler_items.push_back (MenuElem (_("Video Monitor"))); // heading
269 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
270 ruler_items.push_back (CheckMenuElem (_("Video Monitor")));
272 Gtk::CheckMenuItem* xjadeo_toggle = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
273 if (!ARDOUR_UI::instance()->video_timeline->found_xjadeo()) {
274 xjadeo_toggle->set_sensitive(false);
276 xjadeo_toggle->set_active(xjadeo_proc_action->get_active());
277 xjadeo_toggle->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &Editor::toggle_xjadeo_proc), -1));
285 if (!ruler_items.empty()) {
286 editor_ruler_menu->popup (1, gtk_get_current_event_time());
289 no_ruler_shown_update = false;
293 Editor::store_ruler_visibility ()
295 XMLNode* node = new XMLNode(X_("RulerVisibility"));
297 node->set_property (X_("timecode"), ruler_timecode_action->get_active());
298 node->set_property (X_("bbt"), ruler_bbt_action->get_active());
299 node->set_property (X_("samples"), ruler_samples_action->get_active());
300 node->set_property (X_("minsec"), ruler_minsec_action->get_active());
301 node->set_property (X_("tempo"), ruler_tempo_action->get_active());
302 node->set_property (X_("meter"), ruler_meter_action->get_active());
303 node->set_property (X_("marker"), ruler_marker_action->get_active());
304 node->set_property (X_("rangemarker"), ruler_range_action->get_active());
305 node->set_property (X_("transportmarker"), ruler_loop_punch_action->get_active());
306 node->set_property (X_("cdmarker"), ruler_cd_marker_action->get_active());
307 node->set_property (X_("videotl"), ruler_video_action->get_active());
309 _session->add_extra_xml (*node);
310 _session->set_dirty ();
314 Editor::restore_ruler_visibility ()
316 XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
318 no_ruler_shown_update = true;
322 if (node->get_property ("timecode", yn)) {
323 ruler_timecode_action->set_active (yn);
325 if (node->get_property ("bbt", yn)) {
326 ruler_bbt_action->set_active (yn);
328 if (node->get_property ("samples", yn)) {
329 ruler_samples_action->set_active (yn);
331 if (node->get_property ("minsec", yn)) {
332 ruler_minsec_action->set_active (yn);
334 if (node->get_property ("tempo", yn)) {
335 ruler_tempo_action->set_active (yn);
337 if (node->get_property ("meter", yn)) {
338 ruler_meter_action->set_active (yn);
340 if (node->get_property ("marker", yn)) {
341 ruler_marker_action->set_active (yn);
343 if (node->get_property ("rangemarker", yn)) {
344 ruler_range_action->set_active (yn);
346 if (node->get_property ("transportmarker", yn)) {
347 ruler_loop_punch_action->set_active (yn);
350 if (node->get_property ("cdmarker", yn)) {
351 ruler_cd_marker_action->set_active (yn);
353 // this _session doesn't yet know about the cdmarker ruler
354 // as a benefit to the user who doesn't know the feature exists, show the ruler if
355 // any cd marks exist
356 ruler_cd_marker_action->set_active (false);
357 const Locations::LocationList & locs = _session->locations()->list();
358 for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
359 if ((*i)->is_cd_marker()) {
360 ruler_cd_marker_action->set_active (true);
366 if (node->get_property ("videotl", yn)) {
367 ruler_video_action->set_active (yn);
372 no_ruler_shown_update = false;
373 update_ruler_visibility ();
377 Editor::update_ruler_visibility ()
379 int visible_timebars = 0;
381 if (no_ruler_shown_update) {
385 /* the order of the timebars is fixed, so we have to go through each one
386 * and adjust its position depending on what is shown.
388 * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
389 * loop/punch, cd markers, location markers
397 /* gtk update probs require this (damn) */
400 range_mark_label.hide();
401 transport_mark_label.hide();
402 cd_mark_label.hide();
404 videotl_label.hide();
407 if (ruler_minsec_action->get_active()) {
408 old_unit_pos = minsec_ruler->position().y;
409 if (tbpos != old_unit_pos) {
410 minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
412 minsec_ruler->show();
414 tbpos += timebar_height;
415 tbgpos += timebar_height;
418 minsec_ruler->hide();
422 if (ruler_timecode_action->get_active()) {
423 old_unit_pos = timecode_ruler->position().y;
424 if (tbpos != old_unit_pos) {
425 timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
427 timecode_ruler->show();
428 timecode_label.show();
429 tbpos += timebar_height;
430 tbgpos += timebar_height;
433 timecode_ruler->hide();
434 timecode_label.hide();
437 if (ruler_samples_action->get_active()) {
438 old_unit_pos = samples_ruler->position().y;
439 if (tbpos != old_unit_pos) {
440 samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
442 samples_ruler->show();
443 samples_label.show();
444 tbpos += timebar_height;
445 tbgpos += timebar_height;
448 samples_ruler->hide();
449 samples_label.hide();
452 if (ruler_bbt_action->get_active()) {
453 old_unit_pos = bbt_ruler->position().y;
454 if (tbpos != old_unit_pos) {
455 bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
459 tbpos += timebar_height;
460 tbgpos += timebar_height;
467 if (ruler_meter_action->get_active()) {
468 old_unit_pos = meter_group->position().y;
469 if (tbpos != old_unit_pos) {
470 meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
474 tbpos += timebar_height;
475 tbgpos += timebar_height;
482 if (ruler_tempo_action->get_active()) {
483 old_unit_pos = tempo_group->position().y;
484 if (tbpos != old_unit_pos) {
485 tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
489 tbpos += timebar_height;
490 tbgpos += timebar_height;
497 if (ruler_range_action->get_active()) {
498 old_unit_pos = range_marker_group->position().y;
499 if (tbpos != old_unit_pos) {
500 range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
502 range_marker_group->show();
503 range_mark_label.show();
505 tbpos += timebar_height;
506 tbgpos += timebar_height;
509 range_marker_group->hide();
510 range_mark_label.hide();
513 if (ruler_loop_punch_action->get_active()) {
514 old_unit_pos = transport_marker_group->position().y;
515 if (tbpos != old_unit_pos) {
516 transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
518 transport_marker_group->show();
519 transport_mark_label.show();
520 tbpos += timebar_height;
521 tbgpos += timebar_height;
524 transport_marker_group->hide();
525 transport_mark_label.hide();
528 if (ruler_cd_marker_action->get_active()) {
529 old_unit_pos = cd_marker_group->position().y;
530 if (tbpos != old_unit_pos) {
531 cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
533 cd_marker_group->show();
534 cd_mark_label.show();
535 tbpos += timebar_height;
536 tbgpos += timebar_height;
538 // make sure all cd markers show up in their respective places
539 update_cd_marker_display();
541 cd_marker_group->hide();
542 cd_mark_label.hide();
543 // make sure all cd markers show up in their respective places
544 update_cd_marker_display();
547 if (ruler_marker_action->get_active()) {
548 old_unit_pos = marker_group->position().y;
549 if (tbpos != old_unit_pos) {
550 marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
552 marker_group->show();
554 tbpos += timebar_height;
555 tbgpos += timebar_height;
558 marker_group->hide();
562 if (ruler_video_action->get_active()) {
563 old_unit_pos = videotl_group->position().y;
564 if (tbpos != old_unit_pos) {
565 videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
567 videotl_group->show();
568 videotl_label.show();
569 tbpos += timebar_height * videotl_bar_height;
570 tbgpos += timebar_height * videotl_bar_height;
571 visible_timebars+=videotl_bar_height;
572 queue_visual_videotimeline_update();
574 videotl_group->hide();
575 videotl_label.hide();
576 update_video_timeline(true);
579 time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
581 /* move hv_scroll_group (trackviews) to the end of the timebars
584 hv_scroll_group->set_y_position (timebar_height * visible_timebars);
586 compute_fixed_ruler_scale ();
587 update_fixed_rulers();
588 redisplay_grid (false);
590 /* Changing ruler visibility means that any lines on markers might need updating */
591 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
592 i->second->setup_lines ();
597 Editor::update_just_timecode ()
599 ENSURE_GUI_THREAD (*this, &Editor::update_just_timecode)
605 samplepos_t rightmost_sample = _leftmost_sample + current_page_samples();
607 if (ruler_timecode_action->get_active()) {
608 timecode_ruler->set_range (_leftmost_sample, rightmost_sample);
613 Editor::compute_fixed_ruler_scale ()
619 if (ruler_timecode_action->get_active()) {
620 set_timecode_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
623 if (ruler_minsec_action->get_active()) {
624 set_minsec_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
627 if (ruler_samples_action->get_active()) {
628 set_samples_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
633 Editor::update_fixed_rulers ()
635 samplepos_t rightmost_sample;
641 compute_fixed_ruler_scale ();
643 _timecode_metric->units_per_pixel = samples_per_pixel;
644 _samples_metric->units_per_pixel = samples_per_pixel;
645 _minsec_metric->units_per_pixel = samples_per_pixel;
647 rightmost_sample = _leftmost_sample + current_page_samples();
649 /* these force a redraw, which in turn will force execution of the metric callbacks
650 to compute the relevant ticks to display.
653 if (ruler_timecode_action->get_active()) {
654 timecode_ruler->set_range (_leftmost_sample, rightmost_sample);
657 if (ruler_samples_action->get_active()) {
658 samples_ruler->set_range (_leftmost_sample, rightmost_sample);
661 if (ruler_minsec_action->get_active()) {
662 minsec_ruler->set_range (_leftmost_sample, rightmost_sample);
667 Editor::update_tempo_based_rulers ()
673 _bbt_metric->units_per_pixel = samples_per_pixel;
675 if (ruler_bbt_action->get_active()) {
676 bbt_ruler->set_range (_leftmost_sample, _leftmost_sample+current_page_samples());
682 Editor::set_timecode_ruler_scale (samplepos_t lower, samplepos_t upper)
693 fr = _session->sample_rate();
695 if (lower > (spacer = (samplepos_t) (128 * Editor::get_current_zoom ()))) {
696 lower = lower - spacer;
701 upper = upper + spacer;
702 samplecnt_t const range = upper - lower;
704 if (range < (2 * _session->samples_per_timecode_frame())) { /* 0 - 2 samples */
705 timecode_ruler_scale = timecode_show_bits;
706 timecode_mark_modulo = 20;
707 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
708 } else if (range <= (fr / 4)) { /* 2 samples - 0.250 second */
709 timecode_ruler_scale = timecode_show_samples;
710 timecode_mark_modulo = 1;
711 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
712 } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
713 timecode_ruler_scale = timecode_show_samples;
714 timecode_mark_modulo = 2;
715 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
716 } else if (range <= fr) { /* 0.5-1 second */
717 timecode_ruler_scale = timecode_show_samples;
718 timecode_mark_modulo = 5;
719 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
720 } else if (range <= 2 * fr) { /* 1-2 seconds */
721 timecode_ruler_scale = timecode_show_samples;
722 timecode_mark_modulo = 10;
723 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
724 } else if (range <= 8 * fr) { /* 2-8 seconds */
725 timecode_ruler_scale = timecode_show_seconds;
726 timecode_mark_modulo = 1;
727 timecode_nmarks = 2 + (range / fr);
728 } else if (range <= 16 * fr) { /* 8-16 seconds */
729 timecode_ruler_scale = timecode_show_seconds;
730 timecode_mark_modulo = 2;
731 timecode_nmarks = 2 + (range / fr);
732 } else if (range <= 30 * fr) { /* 16-30 seconds */
733 timecode_ruler_scale = timecode_show_seconds;
734 timecode_mark_modulo = 5;
735 timecode_nmarks = 2 + (range / fr);
736 } else if (range <= 60 * fr) { /* 30-60 seconds */
737 timecode_ruler_scale = timecode_show_seconds;
738 timecode_mark_modulo = 5;
739 timecode_nmarks = 2 + (range / fr);
740 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
741 timecode_ruler_scale = timecode_show_seconds;
742 timecode_mark_modulo = 15;
743 timecode_nmarks = 2 + (range / fr);
744 } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
745 timecode_ruler_scale = timecode_show_seconds;
746 timecode_mark_modulo = 30;
747 timecode_nmarks = 2 + (range / fr);
748 } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
749 timecode_ruler_scale = timecode_show_minutes;
750 timecode_mark_modulo = 2;
751 timecode_nmarks = 2 + 10;
752 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
753 timecode_ruler_scale = timecode_show_minutes;
754 timecode_mark_modulo = 5;
755 timecode_nmarks = 2 + 30;
756 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
757 timecode_ruler_scale = timecode_show_minutes;
758 timecode_mark_modulo = 10;
759 timecode_nmarks = 2 + 60;
760 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
761 timecode_ruler_scale = timecode_show_minutes;
762 timecode_mark_modulo = 30;
763 timecode_nmarks = 2 + (60 * 4);
764 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
765 timecode_ruler_scale = timecode_show_hours;
766 timecode_mark_modulo = 1;
767 timecode_nmarks = 2 + 8;
768 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
769 timecode_ruler_scale = timecode_show_hours;
770 timecode_mark_modulo = 1;
771 timecode_nmarks = 2 + 24;
774 const samplecnt_t hours_in_range = range / (60 * 60 * fr);
775 const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
777 /* Normally we do not need to know anything about the width of the canvas
778 to set the ruler scale, because the caller has already determined
779 the width and set lower + upper arguments to this function to match that.
781 But in this case, where the range defined by lower and uppper can vary
782 substantially (basically anything from 24hrs+ to several billion years)
783 trying to decide which tick marks to show does require us to know
784 about the available width.
787 timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
788 timecode_ruler_scale = timecode_show_many_hours;
789 timecode_mark_modulo = max ((samplecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
794 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
798 Timecode::Time timecode;
801 ArdourCanvas::Ruler::Mark mark;
807 if (lower > (spacer = (samplecnt_t)(128 * Editor::get_current_zoom ()))) {
808 lower = lower - spacer;
813 pos = (samplecnt_t) floor (lower);
815 switch (timecode_ruler_scale) {
816 case timecode_show_bits:
817 // Find timecode time of this sample (pos) with subframe accuracy
818 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */);
819 for (n = 0; n < timecode_nmarks; n++) {
820 _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */);
821 if ((timecode.subframes % timecode_mark_modulo) == 0) {
822 if (timecode.subframes == 0) {
823 mark.style = ArdourCanvas::Ruler::Mark::Major;
824 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
826 mark.style = ArdourCanvas::Ruler::Mark::Minor;
827 snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
830 snprintf (buf, sizeof(buf)," ");
831 mark.style = ArdourCanvas::Ruler::Mark::Micro;
835 marks.push_back (mark);
836 // Increment subframes by one
837 Timecode::increment_subframes (timecode, _session->config.get_subframes_per_frame());
841 case timecode_show_samples:
842 // Find timecode time of this sample (pos)
843 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
844 // Go to next whole sample down
845 Timecode::frames_floot (timecode);
846 for (n = 0; n < timecode_nmarks; n++) {
847 _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
848 if ((timecode.frames % timecode_mark_modulo) == 0) {
849 if (timecode.frames == 0) {
850 mark.style = ArdourCanvas::Ruler::Mark::Major;
852 mark.style = ArdourCanvas::Ruler::Mark::Minor;
855 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
857 snprintf (buf, sizeof(buf)," ");
858 mark.style = ArdourCanvas::Ruler::Mark::Micro;
862 marks.push_back (mark);
863 Timecode::increment (timecode, _session->config.get_subframes_per_frame());
867 case timecode_show_seconds:
868 // Find timecode time of this sample (pos)
869 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
870 // Go to next whole second down
871 Timecode::seconds_floor (timecode);
872 for (n = 0; n < timecode_nmarks; n++) {
873 _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
874 if ((timecode.seconds % timecode_mark_modulo) == 0) {
875 if (timecode.seconds == 0) {
876 mark.style = ArdourCanvas::Ruler::Mark::Major;
879 mark.style = ArdourCanvas::Ruler::Mark::Minor;
882 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
884 snprintf (buf, sizeof(buf)," ");
885 mark.style = ArdourCanvas::Ruler::Mark::Micro;
889 marks.push_back (mark);
890 Timecode::increment_seconds (timecode, _session->config.get_subframes_per_frame());
894 case timecode_show_minutes:
895 //Find timecode time of this sample (pos)
896 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
897 // Go to next whole minute down
898 Timecode::minutes_floor (timecode);
899 for (n = 0; n < timecode_nmarks; n++) {
900 _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
901 if ((timecode.minutes % timecode_mark_modulo) == 0) {
902 if (timecode.minutes == 0) {
903 mark.style = ArdourCanvas::Ruler::Mark::Major;
905 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_minutes (timecode, _session->config.get_subframes_per_frame());
918 case timecode_show_hours:
919 // Find timecode time of this sample (pos)
920 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
921 // Go to next whole hour down
922 Timecode::hours_floor (timecode);
923 for (n = 0; n < timecode_nmarks; n++) {
924 _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
925 if ((timecode.hours % timecode_mark_modulo) == 0) {
926 mark.style = ArdourCanvas::Ruler::Mark::Major;
927 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
929 snprintf (buf, sizeof(buf)," ");
930 mark.style = ArdourCanvas::Ruler::Mark::Micro;
934 marks.push_back (mark);
935 Timecode::increment_hours (timecode, _session->config.get_subframes_per_frame());
938 case timecode_show_many_hours:
939 // Find timecode time of this sample (pos)
940 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
941 // Go to next whole hour down
942 Timecode::hours_floor (timecode);
944 for (n = 0; n < timecode_nmarks;) {
945 _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
946 if ((timecode.hours % timecode_mark_modulo) == 0) {
947 mark.style = ArdourCanvas::Ruler::Mark::Major;
948 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
951 marks.push_back (mark);
954 /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
955 * and doing it 1 hour at a time is just stupid (and slow).
957 timecode.hours += timecode_mark_modulo - (timecode.hours % timecode_mark_modulo);
964 Editor::compute_bbt_ruler_scale (samplepos_t lower, samplepos_t upper)
970 std::vector<TempoMap::BBTPoint>::const_iterator i;
971 Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
972 double floor_lower_beat = floor(max (0.0, _session->tempo_map().beat_at_sample (lower)));
974 if (floor_lower_beat < 0.0) {
975 floor_lower_beat = 0.0;
978 const samplecnt_t beat_before_lower_pos = _session->tempo_map().sample_at_beat (floor_lower_beat);
979 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);
981 _session->bbt_time (beat_before_lower_pos, lower_beat);
982 _session->bbt_time (beat_after_upper_pos, upper_beat);
985 bbt_accent_modulo = 1;
986 bbt_bar_helper_on = false;
990 bbt_ruler_scale = bbt_show_many;
992 switch (_grid_type) {
993 case GridTypeBeatDiv2:
994 bbt_beat_subdivision = 2;
996 case GridTypeBeatDiv3:
997 bbt_beat_subdivision = 3;
999 case GridTypeBeatDiv4:
1000 bbt_beat_subdivision = 4;
1002 case GridTypeBeatDiv5:
1003 bbt_beat_subdivision = 5;
1004 bbt_accent_modulo = 2; // XXX YIKES
1006 case GridTypeBeatDiv6:
1007 bbt_beat_subdivision = 3;
1008 bbt_accent_modulo = 2; // XXX YIKES
1010 case GridTypeBeatDiv7:
1011 bbt_beat_subdivision = 7;
1012 bbt_accent_modulo = 2; // XXX YIKES
1014 case GridTypeBeatDiv8:
1015 bbt_beat_subdivision = 4;
1016 bbt_accent_modulo = 2;
1018 case GridTypeBeatDiv10:
1019 bbt_beat_subdivision = 5;
1020 bbt_accent_modulo = 2; // XXX YIKES
1022 case GridTypeBeatDiv12:
1023 bbt_beat_subdivision = 3;
1024 bbt_accent_modulo = 3;
1026 case GridTypeBeatDiv14:
1027 bbt_beat_subdivision = 7;
1028 bbt_accent_modulo = 3; // XXX YIKES!
1030 case GridTypeBeatDiv16:
1031 bbt_beat_subdivision = 4;
1032 bbt_accent_modulo = 4;
1034 case GridTypeBeatDiv20:
1035 bbt_beat_subdivision = 5;
1036 bbt_accent_modulo = 5;
1038 case GridTypeBeatDiv24:
1039 bbt_beat_subdivision = 6;
1040 bbt_accent_modulo = 6;
1042 case GridTypeBeatDiv28:
1043 bbt_beat_subdivision = 7;
1044 bbt_accent_modulo = 7;
1046 case GridTypeBeatDiv32:
1047 bbt_beat_subdivision = 4;
1048 bbt_accent_modulo = 8;
1052 bbt_beat_subdivision = 4;
1055 case GridTypeTimecode:
1056 case GridTypeMinSec:
1057 case GridTypeCDFrame:
1058 bbt_beat_subdivision = 4;
1062 const double ceil_upper_beat = floor (max (0.0, _session->tempo_map().beat_at_sample (upper))) + 1.0;
1063 if (ceil_upper_beat == floor_lower_beat) {
1067 bbt_bars = _session->tempo_map().bbt_at_beat (ceil_upper_beat).bars - _session->tempo_map().bbt_at_beat (floor_lower_beat).bars;
1069 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
1070 double beat_density = ((beats + 1) * ((double) (upper - lower) / (double) (1 + beat_after_upper_pos - beat_before_lower_pos))) / 5.0;
1072 /* Only show the bar helper if there aren't many bars on the screen */
1073 if ((bbt_bars < 2) || (beats < 5)) {
1074 bbt_bar_helper_on = true;
1077 //set upper limits on the beat_density based on the user's grid selection
1078 if (_grid_type == GridTypeBar) {
1079 beat_density = fmax (beat_density, 16.01);
1080 } else if (_grid_type == GridTypeBeat) {
1081 beat_density = fmax (beat_density, 4.01);
1082 } else if (_grid_type == GridTypeBeatDiv2) {
1083 beat_density = fmax (beat_density, 2.01);
1084 } else if (_grid_type == GridTypeBeatDiv4) {
1085 beat_density = fmax (beat_density, 1.001);
1086 } else if (_grid_type == GridTypeBeatDiv8) {
1087 beat_density = fmax (beat_density, 0.501);
1088 } else if (_grid_type == GridTypeBeatDiv16) {
1089 beat_density = fmax (beat_density, 0.2501);
1090 } else if (_grid_type == GridTypeBeatDiv32) {
1091 beat_density = fmax (beat_density, 0.12501);
1094 if (beat_density > 2048) {
1095 bbt_ruler_scale = bbt_show_many;
1096 } else if (beat_density > 512) {
1097 bbt_ruler_scale = bbt_show_64;
1098 } else if (beat_density > 256) {
1099 bbt_ruler_scale = bbt_show_16;
1100 } else if (beat_density > 64) {
1101 bbt_ruler_scale = bbt_show_4;
1102 } else if (beat_density > 16) {
1103 bbt_ruler_scale = bbt_show_1;
1104 } else if (beat_density > 4) {
1105 bbt_ruler_scale = bbt_show_quarters;
1106 } else if (beat_density > 2) {
1107 bbt_ruler_scale = bbt_show_eighths;
1108 } else if (beat_density > 1) {
1109 bbt_ruler_scale = bbt_show_sixteenths;
1111 bbt_ruler_scale = bbt_show_thirtyseconds;
1116 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1118 ArdourCanvas::Ruler::Mark copy = marks.back();
1119 copy.label = newlabel;
1121 marks.push_back (copy);
1125 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1127 if (_session == 0) {
1131 std::vector<TempoMap::BBTPoint>::const_iterator i;
1136 Timecode::BBT_Time next_beat;
1141 double bbt_position_of_helper;
1142 bool i_am_accented = false;
1143 bool helper_active = false;
1144 ArdourCanvas::Ruler::Mark mark;
1146 std::vector<TempoMap::BBTPoint> grid;
1148 compute_current_bbt_points (grid, lower, upper);
1150 if (distance (grid.begin(), grid.end()) == 0) {
1154 switch (bbt_ruler_scale) {
1156 case bbt_show_quarters:
1158 beats = distance (grid.begin(), grid.end());
1159 bbt_nmarks = beats + 2;
1162 mark.position = lower;
1163 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1164 marks.push_back (mark);
1166 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1168 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1169 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1170 edit_last_mark_label (marks, buf);
1173 if ((*i).is_bar()) {
1174 mark.style = ArdourCanvas::Ruler::Mark::Major;
1175 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1176 } else if (((*i).beat % 2 == 1)) {
1177 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1180 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1184 mark.position = (*i).sample;
1185 marks.push_back (mark);
1191 case bbt_show_eighths:
1193 beats = distance (grid.begin(), grid.end());
1194 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1196 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1198 // could do marks.assign() here to preallocate
1201 mark.position = lower;
1202 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1203 marks.push_back (mark);
1205 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1207 if ((*i).sample < 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);
1217 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1218 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1220 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1224 mark.position = (*i).sample;
1225 marks.push_back (mark);
1229 /* Add the tick marks */
1230 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1231 tick = skip; // the first non-beat tick
1233 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1235 next_beat.beats = (*i).beat;
1236 next_beat.bars = (*i).bar;
1237 next_beat.ticks = tick;
1238 pos = _session->tempo_map().sample_at_bbt (next_beat);
1240 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1241 i_am_accented = true;
1244 mark.position = pos;
1246 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1247 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1249 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1251 i_am_accented = false;
1252 marks.push_back (mark);
1262 case bbt_show_sixteenths:
1264 beats = distance (grid.begin(), grid.end());
1265 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1267 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1270 mark.position = lower;
1271 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1272 marks.push_back (mark);
1274 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1276 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1277 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1278 edit_last_mark_label (marks, buf);
1279 helper_active = true;
1282 if ((*i).is_bar()) {
1283 mark.style = ArdourCanvas::Ruler::Mark::Major;
1284 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1286 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1287 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1289 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1293 mark.position = (*i).sample;
1294 marks.push_back (mark);
1298 /* Add the tick marks */
1299 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1300 tick = skip; // the first non-beat tick
1303 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1305 next_beat.beats = (*i).beat;
1306 next_beat.bars = (*i).bar;
1307 next_beat.ticks = tick;
1308 pos = _session->tempo_map().sample_at_bbt (next_beat);
1310 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1311 i_am_accented = true;
1313 if (i_am_accented && (pos > bbt_position_of_helper)){
1314 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1320 mark.position = pos;
1322 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1323 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1325 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1327 i_am_accented = false;
1328 marks.push_back (mark);
1338 case bbt_show_thirtyseconds:
1340 beats = distance (grid.begin(), grid.end());
1341 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1343 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1346 mark.position = lower;
1347 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1348 marks.push_back (mark);
1350 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1352 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1353 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1354 edit_last_mark_label (marks, buf);
1355 helper_active = true;
1358 if ((*i).is_bar()) {
1359 mark.style = ArdourCanvas::Ruler::Mark::Major;
1360 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1362 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1363 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1365 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1369 mark.position = (*i).sample;
1370 marks.push_back (mark);
1374 /* Add the tick marks */
1375 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1377 next_beat.beats = (*i).beat;
1378 next_beat.bars = (*i).bar;
1379 tick = skip; // the first non-beat tick
1381 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1383 next_beat.ticks = tick;
1384 pos = _session->tempo_map().sample_at_bbt (next_beat);
1385 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1386 i_am_accented = true;
1389 if (pos > bbt_position_of_helper) {
1390 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1396 mark.position = pos;
1398 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1399 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1401 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1403 i_am_accented = false;
1404 marks.push_back (mark);
1416 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars);
1417 mark.style = ArdourCanvas::Ruler::Mark::Major;
1419 mark.position = lower;
1420 marks.push_back (mark);
1424 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1425 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1426 if ((*i).is_bar()) {
1427 if ((*i).bar % 64 == 1) {
1428 if ((*i).bar % 256 == 1) {
1429 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1430 mark.style = ArdourCanvas::Ruler::Mark::Major;
1433 if ((*i).bar % 256 == 129) {
1434 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1436 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1440 mark.position = (*i).sample;
1441 marks.push_back (mark);
1449 bbt_nmarks = (bbt_bars / 16) + 1;
1450 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1451 if ((*i).is_bar()) {
1452 if ((*i).bar % 16 == 1) {
1453 if ((*i).bar % 64 == 1) {
1454 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1455 mark.style = ArdourCanvas::Ruler::Mark::Major;
1458 if ((*i).bar % 64 == 33) {
1459 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1461 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1465 mark.position = (*i).sample;
1466 marks.push_back (mark);
1474 bbt_nmarks = (bbt_bars / 4) + 1;
1475 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1476 if ((*i).is_bar()) {
1477 if ((*i).bar % 4 == 1) {
1478 if ((*i).bar % 16 == 1) {
1479 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1480 mark.style = ArdourCanvas::Ruler::Mark::Major;
1483 if ((*i).bar % 16 == 9) {
1484 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1486 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1490 mark.position = (*i).sample;
1491 marks.push_back (mark);
1500 bbt_nmarks = bbt_bars + 2;
1501 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1502 if ((*i).is_bar()) {
1503 if ((*i).bar % 4 == 1) {
1504 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1505 mark.style = ArdourCanvas::Ruler::Mark::Major;
1508 if ((*i).bar % 4 == 3) {
1509 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1511 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1515 mark.position = (*i).sample;
1516 marks.push_back (mark);
1526 Editor::set_samples_ruler_scale (samplepos_t lower, samplepos_t upper)
1528 _samples_ruler_interval = (upper - lower) / 5;
1532 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1535 samplepos_t const ilower = (samplepos_t) floor (lower);
1539 ArdourCanvas::Ruler::Mark mark;
1541 if (_session == 0) {
1546 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1547 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1549 mark.position = pos;
1550 mark.style = ArdourCanvas::Ruler::Mark::Major;
1551 marks.push_back (mark);
1556 sample_to_clock_parts (samplepos_t sample,
1557 samplepos_t sample_rate,
1570 hrs = left / (sample_rate * 60 * 60 * 1000);
1571 left -= hrs * sample_rate * 60 * 60 * 1000;
1572 mins = left / (sample_rate * 60 * 1000);
1573 left -= mins * sample_rate * 60 * 1000;
1574 secs = left / (sample_rate * 1000);
1575 left -= secs * sample_rate * 1000;
1576 millisecs = left / sample_rate;
1578 *millisecs_p = millisecs;
1587 Editor::set_minsec_ruler_scale (samplepos_t lower, samplepos_t upper)
1589 samplepos_t fr = _session->sample_rate() * 1000;
1592 if (_session == 0) {
1597 /* to prevent 'flashing' */
1598 if (lower > (spacer = (samplepos_t)(128 * Editor::get_current_zoom ()))) {
1604 samplecnt_t const range = (upper - lower) * 1000;
1606 if (range <= (fr / 10)) { /* 0-0.1 second */
1607 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1608 minsec_ruler_scale = minsec_show_msecs;
1609 minsec_mark_modulo = 10;
1610 minsec_nmarks = 2 + (range / minsec_mark_interval);
1611 } else if (range <= (fr / 2)) { /* 0-0.5 second */
1612 minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1613 minsec_ruler_scale = minsec_show_msecs;
1614 minsec_mark_modulo = 100;
1615 minsec_nmarks = 2 + (range / minsec_mark_interval);
1616 } else if (range <= fr) { /* 0-1 second */
1617 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1618 minsec_ruler_scale = minsec_show_msecs;
1619 minsec_mark_modulo = 200;
1620 minsec_nmarks = 2 + (range / minsec_mark_interval);
1621 } else if (range <= 2 * fr) { /* 1-2 seconds */
1622 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1623 minsec_ruler_scale = minsec_show_msecs;
1624 minsec_mark_modulo = 500;
1625 minsec_nmarks = 2 + (range / minsec_mark_interval);
1626 } else if (range <= 8 * fr) { /* 2-5 seconds */
1627 minsec_mark_interval = fr / 5; /* show 2 seconds */
1628 minsec_ruler_scale = minsec_show_msecs;
1629 minsec_mark_modulo = 1000;
1630 minsec_nmarks = 2 + (range / minsec_mark_interval);
1631 } else if (range <= 16 * fr) { /* 8-16 seconds */
1632 minsec_mark_interval = fr; /* show 1 seconds */
1633 minsec_ruler_scale = minsec_show_seconds;
1634 minsec_mark_modulo = 2;
1635 minsec_nmarks = 2 + (range / minsec_mark_interval);
1636 } else if (range <= 30 * fr) { /* 10-30 seconds */
1637 minsec_mark_interval = fr; /* show 1 seconds */
1638 minsec_ruler_scale = minsec_show_seconds;
1639 minsec_mark_modulo = 5;
1640 minsec_nmarks = 2 + (range / minsec_mark_interval);
1641 } else if (range <= 60 * fr) { /* 30-60 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 <= 2 * 60 * fr) { /* 1-2 minutes */
1647 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1648 minsec_ruler_scale = minsec_show_seconds;
1649 minsec_mark_modulo = 3;
1650 minsec_nmarks = 2 + (range / minsec_mark_interval);
1651 } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1652 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1653 minsec_ruler_scale = minsec_show_seconds;
1654 minsec_mark_modulo = 30;
1655 minsec_nmarks = 2 + (range / minsec_mark_interval);
1656 } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1657 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1658 minsec_ruler_scale = minsec_show_seconds;
1659 minsec_mark_modulo = 120;
1660 minsec_nmarks = 2 + (range / minsec_mark_interval);
1661 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1662 minsec_mark_interval = 60 * fr; /* show 1 minute */
1663 minsec_ruler_scale = minsec_show_minutes;
1664 minsec_mark_modulo = 5;
1665 minsec_nmarks = 2 + (range / minsec_mark_interval);
1666 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1667 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1668 minsec_ruler_scale = minsec_show_minutes;
1669 minsec_mark_modulo = 10;
1670 minsec_nmarks = 2 + (range / minsec_mark_interval);
1671 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1672 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1673 minsec_ruler_scale = minsec_show_minutes;
1674 minsec_mark_modulo = 30;
1675 minsec_nmarks = 2 + (range / minsec_mark_interval);
1676 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1677 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1678 minsec_ruler_scale = minsec_show_minutes;
1679 minsec_mark_modulo = 60;
1680 minsec_nmarks = 2 + (range / minsec_mark_interval);
1681 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1682 minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1683 minsec_ruler_scale = minsec_show_hours;
1684 minsec_mark_modulo = 2;
1685 minsec_nmarks = 2 + (range / minsec_mark_interval);
1688 const samplecnt_t hours_in_range = range / (60 * 60 * fr);
1689 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1691 /* Normally we do not need to know anything about the width of the canvas
1692 to set the ruler scale, because the caller has already determined
1693 the width and set lower + upper arguments to this function to match that.
1695 But in this case, where the range defined by lower and uppper can vary
1696 substantially (anything from 24hrs+ to several billion years)
1697 trying to decide which tick marks to show does require us to know
1698 about the available width.
1701 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1702 minsec_mark_modulo = max ((samplecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1703 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1704 minsec_ruler_scale = minsec_show_many_hours;
1709 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1713 long hrs, mins, secs, millisecs;
1716 ArdourCanvas::Ruler::Mark mark;
1718 if (_session == 0) {
1722 /* to prevent 'flashing' */
1723 if (lower > (spacer = (samplepos_t) (128 * Editor::get_current_zoom ()))) {
1724 lower = lower - spacer;
1729 if (minsec_mark_interval == 0) { //we got here too early; divide-by-zero imminent
1733 pos = (((1000 * (samplepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1735 switch (minsec_ruler_scale) {
1737 case minsec_show_msecs:
1738 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1739 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1740 if (millisecs % minsec_mark_modulo == 0) {
1741 if (millisecs == 0) {
1742 mark.style = ArdourCanvas::Ruler::Mark::Major;
1744 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1746 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1749 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1752 mark.position = pos/1000.0;
1753 marks.push_back (mark);
1757 case minsec_show_seconds:
1758 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1759 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1760 if (secs % minsec_mark_modulo == 0) {
1762 mark.style = ArdourCanvas::Ruler::Mark::Major;
1764 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1766 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1769 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1772 mark.position = pos/1000.0;
1773 marks.push_back (mark);
1777 case minsec_show_minutes:
1778 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1779 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1780 if (mins % minsec_mark_modulo == 0) {
1782 mark.style = ArdourCanvas::Ruler::Mark::Major;
1784 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1786 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1789 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1792 mark.position = pos/1000.0;
1793 marks.push_back (mark);
1797 case minsec_show_hours:
1798 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1799 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1800 if (hrs % minsec_mark_modulo == 0) {
1801 mark.style = ArdourCanvas::Ruler::Mark::Major;
1802 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1805 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1808 mark.position = pos/1000.0;
1809 marks.push_back (mark);
1813 case minsec_show_many_hours:
1814 for (n = 0; n < minsec_nmarks;) {
1815 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1816 if (hrs % minsec_mark_modulo == 0) {
1817 mark.style = ArdourCanvas::Ruler::Mark::Major;
1818 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1820 mark.position = pos/1000.0;
1821 marks.push_back (mark);
1824 pos += minsec_mark_interval;