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"
57 using namespace ARDOUR;
60 using namespace Editing;
62 /* the order here must match the "metric" enums in editor.h */
64 class TimecodeMetric : public ArdourCanvas::Ruler::Metric
67 TimecodeMetric (Editor* e) : _editor (e) {}
69 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
70 _editor->metric_get_timecode (marks, lower, upper, maxchars);
77 class SamplesMetric : public ArdourCanvas::Ruler::Metric
80 SamplesMetric (Editor* e) : _editor (e) {}
82 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
83 _editor->metric_get_samples (marks, lower, upper, maxchars);
90 class BBTMetric : public ArdourCanvas::Ruler::Metric
93 BBTMetric (Editor* e) : _editor (e) {}
95 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
96 _editor->metric_get_bbt (marks, lower, upper, maxchars);
103 class MinsecMetric : public ArdourCanvas::Ruler::Metric
106 MinsecMetric (Editor* e) : _editor (e) {}
108 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
109 _editor->metric_get_minsec (marks, lower, upper, maxchars);
116 static ArdourCanvas::Ruler::Metric* _bbt_metric;
117 static ArdourCanvas::Ruler::Metric* _timecode_metric;
118 static ArdourCanvas::Ruler::Metric* _samples_metric;
119 static ArdourCanvas::Ruler::Metric* _minsec_metric;
122 Editor::initialize_rulers ()
124 ruler_grabbed_widget = 0;
125 /* Not really sure why we can't get this right in a cross-platform way,
129 Pango::FontDescription font (ARDOUR_UI::config()->get_SmallerFont());
131 Pango::FontDescription font (ARDOUR_UI::config()->get_SmallFont());
133 _timecode_metric = new TimecodeMetric (this);
134 _bbt_metric = new BBTMetric (this);
135 _minsec_metric = new MinsecMetric (this);
136 _samples_metric = new SamplesMetric (this);
138 timecode_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_timecode_metric,
139 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
140 timecode_ruler->set_font_description (font);
141 CANVAS_DEBUG_NAME (timecode_ruler, "timecode ruler");
144 samples_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_samples_metric,
145 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
146 samples_ruler->set_font_description (font);
147 CANVAS_DEBUG_NAME (samples_ruler, "samples ruler");
149 minsec_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_minsec_metric,
150 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
151 minsec_ruler->set_font_description (font);
152 CANVAS_DEBUG_NAME (minsec_ruler, "minsec ruler");
155 bbt_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_bbt_metric,
156 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
157 bbt_ruler->set_font_description (font);
158 CANVAS_DEBUG_NAME (bbt_ruler, "bbt ruler");
161 using namespace Box_Helpers;
162 BoxList & lab_children = time_bars_vbox.children();
164 lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
165 lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
166 lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
167 lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
168 lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
169 lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
170 lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
171 lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
172 lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
173 lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
174 lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
176 /* 1 event handler to bind them all ... */
178 timecode_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), timecode_ruler, TimecodeRulerItem));
179 minsec_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), minsec_ruler, MinsecRulerItem));
180 bbt_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), bbt_ruler, BBTRulerItem));
181 samples_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), samples_ruler, SamplesRulerItem));
183 visible_timebars = 0; /*this will be changed below */
187 Editor::ruler_label_button_release (GdkEventButton* ev)
189 if (Gtkmm2ext::Keyboard::is_context_menu_event (ev)) {
191 ruler_dialog = new RulerDialog ();
193 ruler_dialog->present ();
200 Editor::popup_ruler_menu (framepos_t where, ItemType t)
202 using namespace Menu_Helpers;
204 if (editor_ruler_menu == 0) {
205 editor_ruler_menu = new Menu;
206 editor_ruler_menu->set_name ("ArdourContextMenu");
209 // always build from scratch
210 MenuList& ruler_items = editor_ruler_menu->items();
211 editor_ruler_menu->set_name ("ArdourContextMenu");
216 ruler_items.push_back (MenuElem (_("New location marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, false, false)));
217 ruler_items.push_back (MenuElem (_("Clear all locations"), sigc::mem_fun(*this, &Editor::clear_markers)));
218 ruler_items.push_back (MenuElem (_("Unhide locations"), sigc::mem_fun(*this, &Editor::unhide_markers)));
219 ruler_items.push_back (SeparatorElem ());
221 case RangeMarkerBarItem:
222 ruler_items.push_back (MenuElem (_("New range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_range), where)));
223 ruler_items.push_back (MenuElem (_("Clear all ranges"), sigc::mem_fun(*this, &Editor::clear_ranges)));
224 ruler_items.push_back (MenuElem (_("Unhide ranges"), sigc::mem_fun(*this, &Editor::unhide_ranges)));
225 ruler_items.push_back (SeparatorElem ());
228 case TransportMarkerBarItem:
232 case CdMarkerBarItem:
234 ruler_items.push_back (MenuElem (_("New CD track marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, true, false)));
239 ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
243 ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
247 ruler_items.push_back (MenuElem (_("Timeline height")));
248 static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
249 ruler_items.push_back (CheckMenuElem (_("Large"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 6)));
250 if (videotl_bar_height == 6) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
251 ruler_items.push_back (CheckMenuElem (_("Normal"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 4)));
252 if (videotl_bar_height == 4) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
253 ruler_items.push_back (CheckMenuElem (_("Small"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 3)));
254 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")));
258 static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
260 ruler_items.push_back (CheckMenuElem (_("Lock")));
262 Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
263 vtl_lock->set_active(is_video_timeline_locked());
264 vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
272 if (!ruler_items.empty()) {
273 editor_ruler_menu->popup (1, gtk_get_current_event_time());
276 no_ruler_shown_update = false;
280 Editor::store_ruler_visibility ()
282 XMLNode* node = new XMLNode(X_("RulerVisibility"));
284 node->add_property (X_("timecode"), ruler_timecode_action->get_active() ? "yes": "no");
285 node->add_property (X_("bbt"), ruler_bbt_action->get_active() ? "yes": "no");
286 node->add_property (X_("samples"), ruler_samples_action->get_active() ? "yes": "no");
287 node->add_property (X_("minsec"), ruler_minsec_action->get_active() ? "yes": "no");
288 node->add_property (X_("tempo"), ruler_tempo_action->get_active() ? "yes": "no");
289 node->add_property (X_("meter"), ruler_meter_action->get_active() ? "yes": "no");
290 node->add_property (X_("marker"), ruler_marker_action->get_active() ? "yes": "no");
291 node->add_property (X_("rangemarker"), ruler_range_action->get_active() ? "yes": "no");
292 node->add_property (X_("transportmarker"), ruler_loop_punch_action->get_active() ? "yes": "no");
293 node->add_property (X_("cdmarker"), ruler_cd_marker_action->get_active() ? "yes": "no");
294 node->add_property (X_("videotl"), ruler_video_action->get_active() ? "yes": "no");
296 _session->add_extra_xml (*node);
297 _session->set_dirty ();
301 Editor::restore_ruler_visibility ()
304 XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
306 no_ruler_shown_update = true;
309 if ((prop = node->property ("timecode")) != 0) {
310 if (string_is_affirmative (prop->value())) {
311 ruler_timecode_action->set_active (true);
313 ruler_timecode_action->set_active (false);
316 if ((prop = node->property ("bbt")) != 0) {
317 if (string_is_affirmative (prop->value())) {
318 ruler_bbt_action->set_active (true);
320 ruler_bbt_action->set_active (false);
323 if ((prop = node->property ("samples")) != 0) {
324 if (string_is_affirmative (prop->value())) {
325 ruler_samples_action->set_active (true);
327 ruler_samples_action->set_active (false);
330 if ((prop = node->property ("minsec")) != 0) {
331 if (string_is_affirmative (prop->value())) {
332 ruler_minsec_action->set_active (true);
334 ruler_minsec_action->set_active (false);
337 if ((prop = node->property ("tempo")) != 0) {
338 if (string_is_affirmative (prop->value())) {
339 ruler_tempo_action->set_active (true);
341 ruler_tempo_action->set_active (false);
344 if ((prop = node->property ("meter")) != 0) {
345 if (string_is_affirmative (prop->value())) {
346 ruler_meter_action->set_active (true);
348 ruler_meter_action->set_active (false);
351 if ((prop = node->property ("marker")) != 0) {
352 if (string_is_affirmative (prop->value())) {
353 ruler_marker_action->set_active (true);
355 ruler_marker_action->set_active (false);
358 if ((prop = node->property ("rangemarker")) != 0) {
359 if (string_is_affirmative (prop->value())) {
360 ruler_range_action->set_active (true);
362 ruler_range_action->set_active (false);
366 if ((prop = node->property ("transportmarker")) != 0) {
367 if (string_is_affirmative (prop->value())) {
368 ruler_loop_punch_action->set_active (true);
370 ruler_loop_punch_action->set_active (false);
374 if ((prop = node->property ("cdmarker")) != 0) {
375 if (string_is_affirmative (prop->value())) {
376 ruler_cd_marker_action->set_active (true);
378 ruler_cd_marker_action->set_active (false);
382 // this _session doesn't yet know about the cdmarker ruler
383 // as a benefit to the user who doesn't know the feature exists, show the ruler if
384 // any cd marks exist
385 ruler_cd_marker_action->set_active (false);
386 const Locations::LocationList & locs = _session->locations()->list();
387 for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
388 if ((*i)->is_cd_marker()) {
389 ruler_cd_marker_action->set_active (true);
395 if ((prop = node->property ("videotl")) != 0) {
396 if (string_is_affirmative (prop->value())) {
397 ruler_video_action->set_active (true);
399 ruler_video_action->set_active (false);
405 no_ruler_shown_update = false;
406 update_ruler_visibility ();
410 Editor::update_ruler_visibility ()
412 int visible_timebars = 0;
414 if (no_ruler_shown_update) {
418 /* the order of the timebars is fixed, so we have to go through each one
419 * and adjust its position depending on what is shown.
421 * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
422 * loop/punch, cd markers, location markers
430 /* gtk update probs require this (damn) */
433 range_mark_label.hide();
434 transport_mark_label.hide();
435 cd_mark_label.hide();
437 videotl_label.hide();
440 if (ruler_minsec_action->get_active()) {
441 old_unit_pos = minsec_ruler->position().y;
442 if (tbpos != old_unit_pos) {
443 minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
445 minsec_ruler->show();
447 tbpos += timebar_height;
448 tbgpos += timebar_height;
451 minsec_ruler->hide();
455 if (ruler_timecode_action->get_active()) {
456 old_unit_pos = timecode_ruler->position().y;
457 if (tbpos != old_unit_pos) {
458 timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
460 timecode_ruler->show();
461 timecode_label.show();
462 tbpos += timebar_height;
463 tbgpos += timebar_height;
466 timecode_ruler->hide();
467 timecode_label.hide();
470 if (ruler_samples_action->get_active()) {
471 old_unit_pos = samples_ruler->position().y;
472 if (tbpos != old_unit_pos) {
473 samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
475 samples_ruler->show();
476 samples_label.show();
477 tbpos += timebar_height;
478 tbgpos += timebar_height;
481 samples_ruler->hide();
482 samples_label.hide();
485 if (ruler_bbt_action->get_active()) {
486 old_unit_pos = bbt_ruler->position().y;
487 if (tbpos != old_unit_pos) {
488 bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
492 tbpos += timebar_height;
493 tbgpos += timebar_height;
500 if (ruler_meter_action->get_active()) {
501 old_unit_pos = meter_group->position().y;
502 if (tbpos != old_unit_pos) {
503 meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
507 tbpos += timebar_height;
508 tbgpos += timebar_height;
515 if (ruler_tempo_action->get_active()) {
516 old_unit_pos = tempo_group->position().y;
517 if (tbpos != old_unit_pos) {
518 tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
522 tbpos += timebar_height;
523 tbgpos += timebar_height;
530 if (!Profile->get_sae() && ruler_range_action->get_active()) {
531 old_unit_pos = range_marker_group->position().y;
532 if (tbpos != old_unit_pos) {
533 range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
535 range_marker_group->show();
536 range_mark_label.show();
538 tbpos += timebar_height;
539 tbgpos += timebar_height;
542 range_marker_group->hide();
543 range_mark_label.hide();
546 if (ruler_loop_punch_action->get_active()) {
547 old_unit_pos = transport_marker_group->position().y;
548 if (tbpos != old_unit_pos) {
549 transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
551 transport_marker_group->show();
552 transport_mark_label.show();
553 tbpos += timebar_height;
554 tbgpos += timebar_height;
557 transport_marker_group->hide();
558 transport_mark_label.hide();
561 if (ruler_cd_marker_action->get_active()) {
562 old_unit_pos = cd_marker_group->position().y;
563 if (tbpos != old_unit_pos) {
564 cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
566 cd_marker_group->show();
567 cd_mark_label.show();
568 tbpos += timebar_height;
569 tbgpos += timebar_height;
571 // make sure all cd markers show up in their respective places
572 update_cd_marker_display();
574 cd_marker_group->hide();
575 cd_mark_label.hide();
576 // make sure all cd markers show up in their respective places
577 update_cd_marker_display();
580 if (ruler_marker_action->get_active()) {
581 old_unit_pos = marker_group->position().y;
582 if (tbpos != old_unit_pos) {
583 marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
585 marker_group->show();
587 tbpos += timebar_height;
588 tbgpos += timebar_height;
591 marker_group->hide();
595 if (ruler_video_action->get_active()) {
596 old_unit_pos = videotl_group->position().y;
597 if (tbpos != old_unit_pos) {
598 videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
600 videotl_group->show();
601 videotl_label.show();
602 tbpos += timebar_height * videotl_bar_height;
603 tbgpos += timebar_height * videotl_bar_height;
604 visible_timebars+=videotl_bar_height;
605 queue_visual_videotimeline_update();
607 videotl_group->hide();
608 videotl_label.hide();
609 update_video_timeline(true);
612 time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
614 /* move hv_scroll_group (trackviews) to the end of the timebars
617 hv_scroll_group->set_y_position (timebar_height * visible_timebars);
619 compute_fixed_ruler_scale ();
620 update_fixed_rulers();
621 redisplay_tempo (false);
623 /* Changing ruler visibility means that any lines on markers might need updating */
624 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
625 i->second->setup_lines ();
630 Editor::update_just_timecode ()
632 ENSURE_GUI_THREAD (*this, &Editor::update_just_timecode)
638 framepos_t rightmost_frame = leftmost_frame + current_page_samples();
640 if (ruler_timecode_action->get_active()) {
641 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
646 Editor::compute_fixed_ruler_scale ()
652 if (ruler_timecode_action->get_active()) {
653 set_timecode_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
656 if (ruler_minsec_action->get_active()) {
657 set_minsec_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
660 if (ruler_samples_action->get_active()) {
661 set_samples_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
666 Editor::update_fixed_rulers ()
668 framepos_t rightmost_frame;
674 compute_fixed_ruler_scale ();
676 _timecode_metric->units_per_pixel = samples_per_pixel;
677 _samples_metric->units_per_pixel = samples_per_pixel;
678 _minsec_metric->units_per_pixel = samples_per_pixel;
680 rightmost_frame = leftmost_frame + current_page_samples();
682 /* these force a redraw, which in turn will force execution of the metric callbacks
683 to compute the relevant ticks to display.
686 if (ruler_timecode_action->get_active()) {
687 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
690 if (ruler_samples_action->get_active()) {
691 samples_ruler->set_range (leftmost_frame, rightmost_frame);
694 if (ruler_minsec_action->get_active()) {
695 minsec_ruler->set_range (leftmost_frame, rightmost_frame);
700 Editor::update_tempo_based_rulers (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
701 ARDOUR::TempoMap::BBTPointList::const_iterator& end)
707 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame+current_page_samples(),
710 _bbt_metric->units_per_pixel = samples_per_pixel;
712 if (ruler_bbt_action->get_active()) {
713 bbt_ruler->set_range (leftmost_frame, leftmost_frame+current_page_samples());
719 Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
730 fr = _session->frame_rate();
732 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
733 lower = lower - spacer;
738 upper = upper + spacer;
739 framecnt_t const range = upper - lower;
741 if (range < (2 * _session->frames_per_timecode_frame())) { /* 0 - 2 frames */
742 timecode_ruler_scale = timecode_show_bits;
743 timecode_mark_modulo = 20;
744 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
745 } else if (range <= (fr / 4)) { /* 2 frames - 0.250 second */
746 timecode_ruler_scale = timecode_show_frames;
747 timecode_mark_modulo = 1;
748 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
749 } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
750 timecode_ruler_scale = timecode_show_frames;
751 timecode_mark_modulo = 2;
752 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
753 } else if (range <= fr) { /* 0.5-1 second */
754 timecode_ruler_scale = timecode_show_frames;
755 timecode_mark_modulo = 5;
756 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
757 } else if (range <= 2 * fr) { /* 1-2 seconds */
758 timecode_ruler_scale = timecode_show_frames;
759 timecode_mark_modulo = 10;
760 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
761 } else if (range <= 8 * fr) { /* 2-8 seconds */
762 timecode_ruler_scale = timecode_show_seconds;
763 timecode_mark_modulo = 1;
764 timecode_nmarks = 2 + (range / fr);
765 } else if (range <= 16 * fr) { /* 8-16 seconds */
766 timecode_ruler_scale = timecode_show_seconds;
767 timecode_mark_modulo = 2;
768 timecode_nmarks = 2 + (range / fr);
769 } else if (range <= 30 * fr) { /* 16-30 seconds */
770 timecode_ruler_scale = timecode_show_seconds;
771 timecode_mark_modulo = 5;
772 timecode_nmarks = 2 + (range / fr);
773 } else if (range <= 60 * fr) { /* 30-60 seconds */
774 timecode_ruler_scale = timecode_show_seconds;
775 timecode_mark_modulo = 5;
776 timecode_nmarks = 2 + (range / fr);
777 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
778 timecode_ruler_scale = timecode_show_seconds;
779 timecode_mark_modulo = 15;
780 timecode_nmarks = 2 + (range / fr);
781 } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
782 timecode_ruler_scale = timecode_show_seconds;
783 timecode_mark_modulo = 30;
784 timecode_nmarks = 2 + (range / fr);
785 } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
786 timecode_ruler_scale = timecode_show_minutes;
787 timecode_mark_modulo = 2;
788 timecode_nmarks = 2 + 10;
789 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
790 timecode_ruler_scale = timecode_show_minutes;
791 timecode_mark_modulo = 5;
792 timecode_nmarks = 2 + 30;
793 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
794 timecode_ruler_scale = timecode_show_minutes;
795 timecode_mark_modulo = 10;
796 timecode_nmarks = 2 + 60;
797 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
798 timecode_ruler_scale = timecode_show_minutes;
799 timecode_mark_modulo = 30;
800 timecode_nmarks = 2 + (60 * 4);
801 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
802 timecode_ruler_scale = timecode_show_hours;
803 timecode_mark_modulo = 1;
804 timecode_nmarks = 2 + 8;
805 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
806 timecode_ruler_scale = timecode_show_hours;
807 timecode_mark_modulo = 1;
808 timecode_nmarks = 2 + 24;
811 const framecnt_t hours_in_range = range / (60 * 60 * fr);
812 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
814 /* Normally we do not need to know anything about the width of the canvas
815 to set the ruler scale, because the caller has already determined
816 the width and set lower + upper arguments to this function to match that.
818 But in this case, where the range defined by lower and uppper can vary
819 substantially (basically anything from 24hrs+ to several billion years)
820 trying to decide which tick marks to show does require us to know
821 about the available width.
824 timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
825 timecode_ruler_scale = timecode_show_many_hours;
826 timecode_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
831 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
835 Timecode::Time timecode;
838 ArdourCanvas::Ruler::Mark mark;
844 if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
845 lower = lower - spacer;
850 pos = (framecnt_t) floor (lower);
852 switch (timecode_ruler_scale) {
853 case timecode_show_bits:
855 // Find timecode time of this sample (pos) with subframe accuracy
856 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */ );
858 for (n = 0; n < timecode_nmarks; n++) {
859 _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
860 if ((timecode.subframes % timecode_mark_modulo) == 0) {
861 if (timecode.subframes == 0) {
862 mark.style = ArdourCanvas::Ruler::Mark::Major;
863 if (timecode.hours) {
864 snprintf (buf, sizeof(buf), "%s%u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds);
866 snprintf (buf, sizeof(buf), "%s%u:%02u", timecode.negative ? "-" : "", timecode.minutes, timecode.seconds);
870 mark.style = ArdourCanvas::Ruler::Mark::Minor;
871 snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
874 snprintf (buf, sizeof(buf)," ");
875 mark.style = ArdourCanvas::Ruler::Mark::Micro;
881 marks.push_back (mark);
883 // Increment subframes by one
884 Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
887 case timecode_show_seconds:
888 // Find timecode time of this sample (pos)
889 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
890 // Go to next whole second down
891 Timecode::seconds_floor( timecode );
893 for (n = 0; n < timecode_nmarks; n++) {
894 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
895 if ((timecode.seconds % timecode_mark_modulo) == 0) {
896 if (timecode.seconds == 0) {
897 mark.style = ArdourCanvas::Ruler::Mark::Major;
900 mark.style = ArdourCanvas::Ruler::Mark::Minor;
903 if (timecode.hours) {
904 snprintf (buf, sizeof(buf), "%s%u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds);
906 snprintf (buf, sizeof(buf), "%s%u:%02u", timecode.negative ? "-" : "", timecode.minutes, timecode.seconds);
909 snprintf (buf, sizeof(buf)," ");
910 mark.style = ArdourCanvas::Ruler::Mark::Micro;
915 marks.push_back (mark);
917 Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
920 case timecode_show_minutes:
921 // Find timecode time of this sample (pos)
922 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
923 // Go to next whole minute down
924 Timecode::minutes_floor( timecode );
926 for (n = 0; n < timecode_nmarks; n++) {
927 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
928 if ((timecode.minutes % timecode_mark_modulo) == 0) {
929 if (timecode.minutes == 0) {
930 mark.style = ArdourCanvas::Ruler::Mark::Major;
932 mark.style = ArdourCanvas::Ruler::Mark::Minor;
934 if (timecode.hours) {
935 snprintf (buf, sizeof(buf), "%s%u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds);
937 snprintf (buf, sizeof(buf), "%s%u:%02u", timecode.negative ? "-" : "", timecode.minutes, timecode.seconds);
940 snprintf (buf, sizeof(buf)," ");
941 mark.style = ArdourCanvas::Ruler::Mark::Micro;
946 marks.push_back (mark);
947 Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
951 case timecode_show_hours:
952 // Find timecode time of this sample (pos)
953 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
954 // Go to next whole hour down
955 Timecode::hours_floor( timecode );
957 for (n = 0; n < timecode_nmarks; n++) {
958 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
959 if ((timecode.hours % timecode_mark_modulo) == 0) {
960 mark.style = ArdourCanvas::Ruler::Mark::Major;
961 if (timecode.hours) {
962 snprintf (buf, sizeof(buf), "%s%u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds);
964 snprintf (buf, sizeof(buf), "%s%u:%02u", timecode.negative ? "-" : "", timecode.minutes, timecode.seconds);
967 snprintf (buf, sizeof(buf)," ");
968 mark.style = ArdourCanvas::Ruler::Mark::Micro;
973 marks.push_back (mark);
974 /* Move to next hour */
975 Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
979 case timecode_show_many_hours:
980 // Find timecode time of this sample (pos)
981 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
982 // Go to next whole hour down
983 Timecode::hours_floor (timecode);
985 for (n = 0; n < timecode_nmarks; ) {
986 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
987 if ((timecode.hours % timecode_mark_modulo) == 0) {
988 mark.style = ArdourCanvas::Ruler::Mark::Major;
989 if (timecode.hours) {
990 snprintf (buf, sizeof(buf), "%s%u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds);
992 snprintf (buf, sizeof(buf), "%s%u:%02u", timecode.negative ? "-" : "", timecode.minutes, timecode.seconds);
996 marks.push_back (mark);
999 /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
1000 and doing it 1 hour at a time is just stupid (and slow).
1002 timecode.hours += timecode_mark_modulo;
1006 case timecode_show_frames:
1007 // Find timecode time of this sample (pos)
1008 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
1009 // Go to next whole frame down
1010 Timecode::frames_floor( timecode );
1012 for (n = 0; n < timecode_nmarks; n++) {
1013 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
1014 if ((timecode.frames % timecode_mark_modulo) == 0) {
1015 if (timecode.frames == 0) {
1016 mark.style = ArdourCanvas::Ruler::Mark::Major;
1018 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1020 mark.position = pos;
1021 if (timecode.hours) {
1022 snprintf (buf, sizeof(buf), "%s%u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds);
1024 snprintf (buf, sizeof(buf), "%s%u:%02u", timecode.negative ? "-" : "", timecode.minutes, timecode.seconds);
1027 snprintf (buf, sizeof(buf)," ");
1028 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1029 mark.position = pos;
1033 marks.push_back (mark);
1034 Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
1044 Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
1045 ARDOUR::TempoMap::BBTPointList::const_iterator begin,
1046 ARDOUR::TempoMap::BBTPointList::const_iterator end)
1048 if (_session == 0) {
1052 TempoMap::BBTPointList::const_iterator i;
1053 Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
1055 _session->bbt_time (lower, lower_beat);
1056 _session->bbt_time (upper, upper_beat);
1059 bbt_accent_modulo = 1;
1060 bbt_bar_helper_on = false;
1064 bbt_ruler_scale = bbt_show_many;
1066 switch (_snap_type) {
1067 case SnapToBeatDiv2:
1068 bbt_beat_subdivision = 2;
1070 case SnapToBeatDiv3:
1071 bbt_beat_subdivision = 3;
1073 case SnapToBeatDiv4:
1074 bbt_beat_subdivision = 4;
1076 case SnapToBeatDiv5:
1077 bbt_beat_subdivision = 5;
1078 bbt_accent_modulo = 2; // XXX YIKES
1080 case SnapToBeatDiv6:
1081 bbt_beat_subdivision = 6;
1082 bbt_accent_modulo = 2; // XXX YIKES
1084 case SnapToBeatDiv7:
1085 bbt_beat_subdivision = 7;
1086 bbt_accent_modulo = 2; // XXX YIKES
1088 case SnapToBeatDiv8:
1089 bbt_beat_subdivision = 8;
1090 bbt_accent_modulo = 2;
1092 case SnapToBeatDiv10:
1093 bbt_beat_subdivision = 10;
1094 bbt_accent_modulo = 2; // XXX YIKES
1096 case SnapToBeatDiv12:
1097 bbt_beat_subdivision = 12;
1098 bbt_accent_modulo = 3;
1100 case SnapToBeatDiv14:
1101 bbt_beat_subdivision = 14;
1102 bbt_accent_modulo = 3; // XXX YIKES!
1104 case SnapToBeatDiv16:
1105 bbt_beat_subdivision = 16;
1106 bbt_accent_modulo = 4;
1108 case SnapToBeatDiv20:
1109 bbt_beat_subdivision = 20;
1110 bbt_accent_modulo = 5;
1112 case SnapToBeatDiv24:
1113 bbt_beat_subdivision = 24;
1114 bbt_accent_modulo = 6;
1116 case SnapToBeatDiv28:
1117 bbt_beat_subdivision = 28;
1118 bbt_accent_modulo = 7;
1120 case SnapToBeatDiv32:
1121 bbt_beat_subdivision = 32;
1122 bbt_accent_modulo = 8;
1124 case SnapToBeatDiv64:
1125 bbt_beat_subdivision = 64;
1126 bbt_accent_modulo = 8;
1128 case SnapToBeatDiv128:
1129 bbt_beat_subdivision = 128;
1130 bbt_accent_modulo = 8;
1133 bbt_beat_subdivision = 4;
1137 if (distance (begin, end) == 0) {
1143 if ((*i).beat >= (*begin).beat) {
1144 bbt_bars = (*i).bar - (*begin).bar;
1146 bbt_bars = (*i).bar - (*begin).bar - 1;
1148 beats = distance (begin, end) - bbt_bars;
1150 /* Only show the bar helper if there aren't many bars on the screen */
1151 if ((bbt_bars < 2) || (beats < 5)) {
1152 bbt_bar_helper_on = true;
1155 if (bbt_bars > 8192) {
1156 bbt_ruler_scale = bbt_show_many;
1157 } else if (bbt_bars > 1024) {
1158 bbt_ruler_scale = bbt_show_64;
1159 } else if (bbt_bars > 256) {
1160 bbt_ruler_scale = bbt_show_16;
1161 } else if (bbt_bars > 64) {
1162 bbt_ruler_scale = bbt_show_4;
1163 } else if (bbt_bars > 10) {
1164 bbt_ruler_scale = bbt_show_1;
1165 } else if (bbt_bars > 2) {
1166 bbt_ruler_scale = bbt_show_beats;
1167 } else if (bbt_bars > 0) {
1168 bbt_ruler_scale = bbt_show_ticks;
1170 bbt_ruler_scale = bbt_show_ticks_detail;
1173 if ((bbt_ruler_scale == bbt_show_ticks_detail) && (lower_beat.beats == upper_beat.beats) && (upper_beat.ticks - lower_beat.ticks <= Timecode::BBT_Time::ticks_per_beat / 4)) {
1174 bbt_ruler_scale = bbt_show_ticks_super_detail;
1179 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1181 ArdourCanvas::Ruler::Mark copy = marks.back();
1182 copy.label = newlabel;
1184 marks.push_back (copy);
1188 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1190 if (_session == 0) {
1194 TempoMap::BBTPointList::const_iterator i;
1199 Timecode::BBT_Time next_beat;
1200 framepos_t next_beat_pos;
1205 framepos_t frame_skip;
1206 double frame_skip_error;
1207 double bbt_position_of_helper;
1208 double accumulated_error;
1209 bool i_am_accented = false;
1210 bool helper_active = false;
1211 ArdourCanvas::Ruler::Mark mark;
1213 ARDOUR::TempoMap::BBTPointList::const_iterator begin;
1214 ARDOUR::TempoMap::BBTPointList::const_iterator end;
1216 compute_current_bbt_points (lower, upper, begin, end);
1218 if (distance (begin, end) == 0) {
1222 switch (bbt_ruler_scale) {
1224 case bbt_show_beats:
1225 beats = distance (begin, end);
1226 bbt_nmarks = beats + 2;
1229 mark.position = lower;
1230 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1231 marks.push_back (mark);
1233 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1235 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1236 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1237 edit_last_mark_label (marks, buf);
1238 helper_active = true;
1241 if ((*i).is_bar()) {
1242 mark.style = ArdourCanvas::Ruler::Mark::Major;
1243 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1244 } else if (((*i).beat % 2 == 1)) {
1245 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1248 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1252 mark.position = (*i).frame;
1253 marks.push_back (mark);
1259 case bbt_show_ticks:
1261 beats = distance (begin, end);
1262 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1264 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1266 // could do marks.assign() here to preallocate
1269 mark.position = lower;
1270 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1271 marks.push_back (mark);
1273 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1275 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1276 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1277 edit_last_mark_label (marks, buf);
1278 helper_active = true;
1281 if ((*i).is_bar()) {
1282 mark.style = ArdourCanvas::Ruler::Mark::Major;
1283 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1285 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1286 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1288 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1292 mark.position = (*i).frame;
1293 marks.push_back (mark);
1297 /* Add the tick marks */
1299 /* Find the next beat */
1300 next_beat.beats = (*i).beat;
1301 next_beat.bars = (*i).bar;
1302 next_beat.ticks = 0;
1304 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1305 next_beat.beats += 1;
1307 next_beat.bars += 1;
1308 next_beat.beats = 1;
1311 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1313 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1314 frame_skip_error -= frame_skip;
1315 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1317 pos = (*i).frame + frame_skip;
1318 accumulated_error = frame_skip_error;
1322 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1324 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1325 i_am_accented = true;
1330 /* Error compensation for float to framepos_t*/
1331 accumulated_error += frame_skip_error;
1332 if (accumulated_error > 1) {
1334 accumulated_error -= 1.0f;
1337 mark.position = pos;
1339 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1340 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1342 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1344 i_am_accented = false;
1345 marks.push_back (mark);
1352 case bbt_show_ticks_detail:
1354 beats = distance (begin, end);
1355 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1357 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1360 mark.position = lower;
1361 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1362 marks.push_back (mark);
1364 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1366 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1367 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1368 edit_last_mark_label (marks, buf);
1369 helper_active = true;
1372 if ((*i).is_bar()) {
1373 mark.style = ArdourCanvas::Ruler::Mark::Major;
1374 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1376 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1377 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1379 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1383 mark.position = (*i).frame;
1384 marks.push_back (mark);
1388 /* Add the tick marks */
1390 /* Find the next beat */
1392 next_beat.beats = (*i).beat;
1393 next_beat.bars = (*i).bar;
1395 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1396 next_beat.beats += 1;
1398 next_beat.bars += 1;
1399 next_beat.beats = 1;
1402 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1404 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1405 frame_skip_error -= frame_skip;
1406 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1408 pos = (*i).frame + frame_skip;
1409 accumulated_error = frame_skip_error;
1413 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1415 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1416 i_am_accented = true;
1419 if (i_am_accented && (pos > bbt_position_of_helper)){
1420 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1427 /* Error compensation for float to framepos_t*/
1428 accumulated_error += frame_skip_error;
1429 if (accumulated_error > 1) {
1431 accumulated_error -= 1.0f;
1434 mark.position = pos;
1436 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1437 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1439 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1441 i_am_accented = false;
1448 case bbt_show_ticks_super_detail:
1450 beats = distance (begin, end);
1451 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1453 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1456 mark.position = lower;
1457 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1458 marks.push_back (mark);
1460 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1462 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1463 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1464 edit_last_mark_label (marks, buf);
1465 helper_active = true;
1468 if ((*i).is_bar()) {
1469 mark.style = ArdourCanvas::Ruler::Mark::Major;
1470 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1472 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1473 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1475 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1479 mark.position = (*i).frame;
1480 marks.push_back (mark);
1484 /* Add the tick marks */
1486 /* Find the next beat */
1488 next_beat.beats = (*i).beat;
1489 next_beat.bars = (*i).bar;
1491 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1492 next_beat.beats += 1;
1494 next_beat.bars += 1;
1495 next_beat.beats = 1;
1498 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1500 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1501 frame_skip_error -= frame_skip;
1502 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1504 pos = (*i).frame + frame_skip;
1505 accumulated_error = frame_skip_error;
1509 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1511 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1512 i_am_accented = true;
1515 if (pos > bbt_position_of_helper) {
1516 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1523 /* Error compensation for float to framepos_t*/
1524 accumulated_error += frame_skip_error;
1525 if (accumulated_error > 1) {
1527 accumulated_error -= 1.0f;
1530 mark.position = pos;
1532 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1533 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1535 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1537 i_am_accented = false;
1538 marks.push_back (mark);
1547 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1548 mark.style = ArdourCanvas::Ruler::Mark::Major;
1550 mark.position = lower;
1551 marks.push_back (mark);
1555 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1556 for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
1557 if ((*i).is_bar()) {
1558 if ((*i).bar % 64 == 1) {
1559 if ((*i).bar % 256 == 1) {
1560 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1561 mark.style = ArdourCanvas::Ruler::Mark::Major;
1564 if ((*i).bar % 256 == 129) {
1565 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1567 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1571 mark.position = (*i).frame;
1572 marks.push_back (mark);
1580 bbt_nmarks = (bbt_bars / 16) + 1;
1581 for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
1582 if ((*i).is_bar()) {
1583 if ((*i).bar % 16 == 1) {
1584 if ((*i).bar % 64 == 1) {
1585 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1586 mark.style = ArdourCanvas::Ruler::Mark::Major;
1589 if ((*i).bar % 64 == 33) {
1590 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1592 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1596 mark.position = (*i).frame;
1597 marks.push_back (mark);
1605 bbt_nmarks = (bbt_bars / 4) + 1;
1606 for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
1607 if ((*i).is_bar()) {
1608 if ((*i).bar % 4 == 1) {
1609 if ((*i).bar % 16 == 1) {
1610 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1611 mark.style = ArdourCanvas::Ruler::Mark::Major;
1614 if ((*i).bar % 16 == 9) {
1615 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1617 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1621 mark.position = (*i).frame;
1622 marks.push_back (mark);
1631 bbt_nmarks = bbt_bars + 2;
1632 for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
1633 if ((*i).is_bar()) {
1634 if ((*i).bar % 4 == 1) {
1635 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1636 mark.style = ArdourCanvas::Ruler::Mark::Major;
1639 if ((*i).bar % 4 == 3) {
1640 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1642 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1646 mark.position = (*i).frame;
1647 marks.push_back (mark);
1657 Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
1659 _samples_ruler_interval = (upper - lower) / 5;
1663 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1666 framepos_t const ilower = (framepos_t) floor (lower);
1670 ArdourCanvas::Ruler::Mark mark;
1672 if (_session == 0) {
1677 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1678 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1680 mark.position = pos;
1681 mark.style = ArdourCanvas::Ruler::Mark::Major;
1682 marks.push_back (mark);
1687 sample_to_clock_parts ( framepos_t sample,
1688 framepos_t sample_rate,
1702 hrs = left / (sample_rate * 60 * 60 * 1000);
1703 left -= hrs * sample_rate * 60 * 60 * 1000;
1704 mins = left / (sample_rate * 60 * 1000);
1705 left -= mins * sample_rate * 60 * 1000;
1706 secs = left / (sample_rate * 1000);
1707 left -= secs * sample_rate * 1000;
1708 millisecs = left / sample_rate;
1710 *millisecs_p = millisecs;
1719 Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
1721 framepos_t fr = _session->frame_rate() * 1000;
1724 if (_session == 0) {
1729 /* to prevent 'flashing' */
1730 if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1736 framecnt_t const range = (upper - lower) * 1000;
1738 if (range <= (fr / 10)) { /* 0-0.1 second */
1739 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1740 minsec_ruler_scale = minsec_show_msecs;
1741 minsec_mark_modulo = 10;
1742 minsec_nmarks = 2 + (range / minsec_mark_interval);
1743 } else if (range <= (fr / 2)) { /* 0-0.5 second */
1744 minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1745 minsec_ruler_scale = minsec_show_msecs;
1746 minsec_mark_modulo = 100;
1747 minsec_nmarks = 2 + (range / minsec_mark_interval);
1748 } else if (range <= fr) { /* 0-1 second */
1749 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1750 minsec_ruler_scale = minsec_show_msecs;
1751 minsec_mark_modulo = 200;
1752 minsec_nmarks = 2 + (range / minsec_mark_interval);
1753 } else if (range <= 2 * fr) { /* 1-2 seconds */
1754 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1755 minsec_ruler_scale = minsec_show_msecs;
1756 minsec_mark_modulo = 500;
1757 minsec_nmarks = 2 + (range / minsec_mark_interval);
1758 } else if (range <= 8 * fr) { /* 2-5 seconds */
1759 minsec_mark_interval = fr / 5; /* show 2 seconds */
1760 minsec_ruler_scale = minsec_show_msecs;
1761 minsec_mark_modulo = 1000;
1762 minsec_nmarks = 2 + (range / minsec_mark_interval);
1763 } else if (range <= 16 * fr) { /* 8-16 seconds */
1764 minsec_mark_interval = fr; /* show 1 seconds */
1765 minsec_ruler_scale = minsec_show_seconds;
1766 minsec_mark_modulo = 2;
1767 minsec_nmarks = 2 + (range / minsec_mark_interval);
1768 } else if (range <= 30 * fr) { /* 10-30 seconds */
1769 minsec_mark_interval = fr; /* show 1 seconds */
1770 minsec_ruler_scale = minsec_show_seconds;
1771 minsec_mark_modulo = 5;
1772 minsec_nmarks = 2 + (range / minsec_mark_interval);
1773 } else if (range <= 60 * fr) { /* 30-60 seconds */
1774 minsec_mark_interval = fr; /* show 1 seconds */
1775 minsec_ruler_scale = minsec_show_seconds;
1776 minsec_mark_modulo = 5;
1777 minsec_nmarks = 2 + (range / minsec_mark_interval);
1778 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1779 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1780 minsec_ruler_scale = minsec_show_seconds;
1781 minsec_mark_modulo = 3;
1782 minsec_nmarks = 2 + (range / minsec_mark_interval);
1783 } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1784 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1785 minsec_ruler_scale = minsec_show_seconds;
1786 minsec_mark_modulo = 30;
1787 minsec_nmarks = 2 + (range / minsec_mark_interval);
1788 } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1789 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1790 minsec_ruler_scale = minsec_show_seconds;
1791 minsec_mark_modulo = 120;
1792 minsec_nmarks = 2 + (range / minsec_mark_interval);
1793 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1794 minsec_mark_interval = 60 * fr; /* show 1 minute */
1795 minsec_ruler_scale = minsec_show_minutes;
1796 minsec_mark_modulo = 5;
1797 minsec_nmarks = 2 + (range / minsec_mark_interval);
1798 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1799 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1800 minsec_ruler_scale = minsec_show_minutes;
1801 minsec_mark_modulo = 10;
1802 minsec_nmarks = 2 + (range / minsec_mark_interval);
1803 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1804 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1805 minsec_ruler_scale = minsec_show_minutes;
1806 minsec_mark_modulo = 30;
1807 minsec_nmarks = 2 + (range / minsec_mark_interval);
1808 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1809 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1810 minsec_ruler_scale = minsec_show_minutes;
1811 minsec_mark_modulo = 60;
1812 minsec_nmarks = 2 + (range / minsec_mark_interval);
1813 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1814 minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1815 minsec_ruler_scale = minsec_show_hours;
1816 minsec_mark_modulo = 2;
1817 minsec_nmarks = 2 + (range / minsec_mark_interval);
1820 const framecnt_t hours_in_range = range / (60 * 60 * fr);
1821 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1823 /* Normally we do not need to know anything about the width of the canvas
1824 to set the ruler scale, because the caller has already determined
1825 the width and set lower + upper arguments to this function to match that.
1827 But in this case, where the range defined by lower and uppper can vary
1828 substantially (anything from 24hrs+ to several billion years)
1829 trying to decide which tick marks to show does require us to know
1830 about the available width.
1833 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1834 minsec_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1835 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1836 minsec_ruler_scale = minsec_show_many_hours;
1841 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1845 long hrs, mins, secs, millisecs;
1848 ArdourCanvas::Ruler::Mark mark;
1850 if (_session == 0) {
1854 /* to prevent 'flashing' */
1855 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
1856 lower = lower - spacer;
1861 pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1863 switch (minsec_ruler_scale) {
1865 case minsec_show_msecs:
1866 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1867 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1868 if (millisecs % minsec_mark_modulo == 0) {
1869 if (millisecs == 0) {
1870 mark.style = ArdourCanvas::Ruler::Mark::Major;
1872 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1874 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1877 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1880 mark.position = pos/1000.0;
1881 marks.push_back (mark);
1885 case minsec_show_seconds:
1886 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1887 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1888 if (secs % minsec_mark_modulo == 0) {
1890 mark.style = ArdourCanvas::Ruler::Mark::Major;
1892 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1894 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1897 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1900 mark.position = pos/1000.0;
1901 marks.push_back (mark);
1905 case minsec_show_minutes:
1906 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1907 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1908 if (mins % minsec_mark_modulo == 0) {
1910 mark.style = ArdourCanvas::Ruler::Mark::Major;
1912 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1914 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1917 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1920 mark.position = pos/1000.0;
1921 marks.push_back (mark);
1925 case minsec_show_hours:
1926 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1927 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1928 if (hrs % minsec_mark_modulo == 0) {
1929 mark.style = ArdourCanvas::Ruler::Mark::Major;
1930 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1933 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1936 mark.position = pos/1000.0;
1937 marks.push_back (mark);
1941 case minsec_show_many_hours:
1942 for (n = 0; n < minsec_nmarks; ) {
1943 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1944 if (hrs % minsec_mark_modulo == 0) {
1945 mark.style = ArdourCanvas::Ruler::Mark::Major;
1946 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1948 mark.position = pos/1000.0;
1949 marks.push_back (mark);
1952 pos += minsec_mark_interval;