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;
126 Pango::FontDescription font (ARDOUR_UI::config()->get_SmallerFont());
128 _timecode_metric = new TimecodeMetric (this);
129 _bbt_metric = new BBTMetric (this);
130 _minsec_metric = new MinsecMetric (this);
131 _samples_metric = new SamplesMetric (this);
133 timecode_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_timecode_metric,
134 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
135 timecode_ruler->set_font_description (font);
136 CANVAS_DEBUG_NAME (timecode_ruler, "timecode ruler");
139 samples_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_samples_metric,
140 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
141 samples_ruler->set_font_description (font);
142 CANVAS_DEBUG_NAME (samples_ruler, "samples ruler");
144 minsec_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_minsec_metric,
145 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
146 minsec_ruler->set_font_description (font);
147 CANVAS_DEBUG_NAME (minsec_ruler, "minsec ruler");
150 bbt_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_bbt_metric,
151 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
152 bbt_ruler->set_font_description (font);
153 CANVAS_DEBUG_NAME (bbt_ruler, "bbt ruler");
156 using namespace Box_Helpers;
157 BoxList & lab_children = time_bars_vbox.children();
159 lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
160 lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
161 lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
162 lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
163 lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
164 lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
165 lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
166 lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
167 lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
168 lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
169 lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
171 /* 1 event handler to bind them all ... */
173 timecode_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), timecode_ruler, TimecodeRulerItem));
174 minsec_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), minsec_ruler, MinsecRulerItem));
175 bbt_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), bbt_ruler, BBTRulerItem));
176 samples_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), samples_ruler, SamplesRulerItem));
178 visible_timebars = 0; /*this will be changed below */
182 Editor::ruler_label_button_release (GdkEventButton* ev)
184 if (Gtkmm2ext::Keyboard::is_context_menu_event (ev)) {
186 ruler_dialog = new RulerDialog ();
188 ruler_dialog->present ();
195 Editor::popup_ruler_menu (framepos_t where, ItemType t)
197 using namespace Menu_Helpers;
199 if (editor_ruler_menu == 0) {
200 editor_ruler_menu = new Menu;
201 editor_ruler_menu->set_name ("ArdourContextMenu");
204 // always build from scratch
205 MenuList& ruler_items = editor_ruler_menu->items();
206 editor_ruler_menu->set_name ("ArdourContextMenu");
211 ruler_items.push_back (MenuElem (_("New location marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, false, false)));
212 ruler_items.push_back (MenuElem (_("Clear all locations"), sigc::mem_fun(*this, &Editor::clear_markers)));
213 ruler_items.push_back (MenuElem (_("Unhide locations"), sigc::mem_fun(*this, &Editor::unhide_markers)));
214 ruler_items.push_back (SeparatorElem ());
216 case RangeMarkerBarItem:
217 ruler_items.push_back (MenuElem (_("New range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_range), where)));
218 ruler_items.push_back (MenuElem (_("Clear all ranges"), sigc::mem_fun(*this, &Editor::clear_ranges)));
219 ruler_items.push_back (MenuElem (_("Unhide ranges"), sigc::mem_fun(*this, &Editor::unhide_ranges)));
220 ruler_items.push_back (SeparatorElem ());
223 case TransportMarkerBarItem:
227 case CdMarkerBarItem:
229 ruler_items.push_back (MenuElem (_("New CD track marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, true, false)));
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 ruler_items.push_back (MenuElem (_("Timeline height")));
243 static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
244 ruler_items.push_back (CheckMenuElem (_("Large"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 6)));
245 if (videotl_bar_height == 6) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
246 ruler_items.push_back (CheckMenuElem (_("Normal"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 4)));
247 if (videotl_bar_height == 4) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
248 ruler_items.push_back (CheckMenuElem (_("Small"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 3)));
249 if (videotl_bar_height == 3) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
250 ruler_items.push_back (SeparatorElem ());
252 ruler_items.push_back (MenuElem (_("Align Video Track")));
253 static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
255 ruler_items.push_back (CheckMenuElem (_("Lock")));
257 Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
258 vtl_lock->set_active(is_video_timeline_locked());
259 vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
267 if (!ruler_items.empty()) {
268 editor_ruler_menu->popup (1, gtk_get_current_event_time());
271 no_ruler_shown_update = false;
275 Editor::store_ruler_visibility ()
277 XMLNode* node = new XMLNode(X_("RulerVisibility"));
279 node->add_property (X_("timecode"), ruler_timecode_action->get_active() ? "yes": "no");
280 node->add_property (X_("bbt"), ruler_bbt_action->get_active() ? "yes": "no");
281 node->add_property (X_("samples"), ruler_samples_action->get_active() ? "yes": "no");
282 node->add_property (X_("minsec"), ruler_minsec_action->get_active() ? "yes": "no");
283 node->add_property (X_("tempo"), ruler_tempo_action->get_active() ? "yes": "no");
284 node->add_property (X_("meter"), ruler_meter_action->get_active() ? "yes": "no");
285 node->add_property (X_("marker"), ruler_marker_action->get_active() ? "yes": "no");
286 node->add_property (X_("rangemarker"), ruler_range_action->get_active() ? "yes": "no");
287 node->add_property (X_("transportmarker"), ruler_loop_punch_action->get_active() ? "yes": "no");
288 node->add_property (X_("cdmarker"), ruler_cd_marker_action->get_active() ? "yes": "no");
289 node->add_property (X_("videotl"), ruler_video_action->get_active() ? "yes": "no");
291 _session->add_extra_xml (*node);
292 _session->set_dirty ();
296 Editor::restore_ruler_visibility ()
299 XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
301 no_ruler_shown_update = true;
304 if ((prop = node->property ("timecode")) != 0) {
305 if (string_is_affirmative (prop->value())) {
306 ruler_timecode_action->set_active (true);
308 ruler_timecode_action->set_active (false);
311 if ((prop = node->property ("bbt")) != 0) {
312 if (string_is_affirmative (prop->value())) {
313 ruler_bbt_action->set_active (true);
315 ruler_bbt_action->set_active (false);
318 if ((prop = node->property ("samples")) != 0) {
319 if (string_is_affirmative (prop->value())) {
320 ruler_samples_action->set_active (true);
322 ruler_samples_action->set_active (false);
325 if ((prop = node->property ("minsec")) != 0) {
326 if (string_is_affirmative (prop->value())) {
327 ruler_minsec_action->set_active (true);
329 ruler_minsec_action->set_active (false);
332 if ((prop = node->property ("tempo")) != 0) {
333 if (string_is_affirmative (prop->value())) {
334 ruler_tempo_action->set_active (true);
336 ruler_tempo_action->set_active (false);
339 if ((prop = node->property ("meter")) != 0) {
340 if (string_is_affirmative (prop->value())) {
341 ruler_meter_action->set_active (true);
343 ruler_meter_action->set_active (false);
346 if ((prop = node->property ("marker")) != 0) {
347 if (string_is_affirmative (prop->value())) {
348 ruler_marker_action->set_active (true);
350 ruler_marker_action->set_active (false);
353 if ((prop = node->property ("rangemarker")) != 0) {
354 if (string_is_affirmative (prop->value())) {
355 ruler_range_action->set_active (true);
357 ruler_range_action->set_active (false);
361 if ((prop = node->property ("transportmarker")) != 0) {
362 if (string_is_affirmative (prop->value())) {
363 ruler_loop_punch_action->set_active (true);
365 ruler_loop_punch_action->set_active (false);
369 if ((prop = node->property ("cdmarker")) != 0) {
370 if (string_is_affirmative (prop->value())) {
371 ruler_cd_marker_action->set_active (true);
373 ruler_cd_marker_action->set_active (false);
377 // this _session doesn't yet know about the cdmarker ruler
378 // as a benefit to the user who doesn't know the feature exists, show the ruler if
379 // any cd marks exist
380 ruler_cd_marker_action->set_active (false);
381 const Locations::LocationList & locs = _session->locations()->list();
382 for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
383 if ((*i)->is_cd_marker()) {
384 ruler_cd_marker_action->set_active (true);
390 if ((prop = node->property ("videotl")) != 0) {
391 if (string_is_affirmative (prop->value())) {
392 ruler_video_action->set_active (true);
394 ruler_video_action->set_active (false);
400 no_ruler_shown_update = false;
401 update_ruler_visibility ();
405 Editor::update_ruler_visibility ()
407 int visible_timebars = 0;
409 if (no_ruler_shown_update) {
413 /* the order of the timebars is fixed, so we have to go through each one
414 * and adjust its position depending on what is shown.
416 * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
417 * loop/punch, cd markers, location markers
425 /* gtk update probs require this (damn) */
428 range_mark_label.hide();
429 transport_mark_label.hide();
430 cd_mark_label.hide();
432 videotl_label.hide();
435 if (ruler_minsec_action->get_active()) {
436 old_unit_pos = minsec_ruler->position().y;
437 if (tbpos != old_unit_pos) {
438 minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
440 minsec_ruler->show();
442 tbpos += timebar_height;
443 tbgpos += timebar_height;
446 minsec_ruler->hide();
450 if (ruler_timecode_action->get_active()) {
451 old_unit_pos = timecode_ruler->position().y;
452 if (tbpos != old_unit_pos) {
453 timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
455 timecode_ruler->show();
456 timecode_label.show();
457 tbpos += timebar_height;
458 tbgpos += timebar_height;
461 timecode_ruler->hide();
462 timecode_label.hide();
465 if (ruler_samples_action->get_active()) {
466 old_unit_pos = samples_ruler->position().y;
467 if (tbpos != old_unit_pos) {
468 samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
470 samples_ruler->show();
471 samples_label.show();
472 tbpos += timebar_height;
473 tbgpos += timebar_height;
476 samples_ruler->hide();
477 samples_label.hide();
480 if (ruler_bbt_action->get_active()) {
481 old_unit_pos = bbt_ruler->position().y;
482 if (tbpos != old_unit_pos) {
483 bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
487 tbpos += timebar_height;
488 tbgpos += timebar_height;
495 if (ruler_meter_action->get_active()) {
496 old_unit_pos = meter_group->position().y;
497 if (tbpos != old_unit_pos) {
498 meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
502 tbpos += timebar_height;
503 tbgpos += timebar_height;
510 if (ruler_tempo_action->get_active()) {
511 old_unit_pos = tempo_group->position().y;
512 if (tbpos != old_unit_pos) {
513 tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
517 tbpos += timebar_height;
518 tbgpos += timebar_height;
525 if (!Profile->get_sae() && ruler_range_action->get_active()) {
526 old_unit_pos = range_marker_group->position().y;
527 if (tbpos != old_unit_pos) {
528 range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
530 range_marker_group->show();
531 range_mark_label.show();
533 tbpos += timebar_height;
534 tbgpos += timebar_height;
537 range_marker_group->hide();
538 range_mark_label.hide();
541 if (ruler_loop_punch_action->get_active()) {
542 old_unit_pos = transport_marker_group->position().y;
543 if (tbpos != old_unit_pos) {
544 transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
546 transport_marker_group->show();
547 transport_mark_label.show();
548 tbpos += timebar_height;
549 tbgpos += timebar_height;
552 transport_marker_group->hide();
553 transport_mark_label.hide();
556 if (ruler_cd_marker_action->get_active()) {
557 old_unit_pos = cd_marker_group->position().y;
558 if (tbpos != old_unit_pos) {
559 cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
561 cd_marker_group->show();
562 cd_mark_label.show();
563 tbpos += timebar_height;
564 tbgpos += timebar_height;
566 // make sure all cd markers show up in their respective places
567 update_cd_marker_display();
569 cd_marker_group->hide();
570 cd_mark_label.hide();
571 // make sure all cd markers show up in their respective places
572 update_cd_marker_display();
575 if (ruler_marker_action->get_active()) {
576 old_unit_pos = marker_group->position().y;
577 if (tbpos != old_unit_pos) {
578 marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
580 marker_group->show();
582 tbpos += timebar_height;
583 tbgpos += timebar_height;
586 marker_group->hide();
590 if (ruler_video_action->get_active()) {
591 old_unit_pos = videotl_group->position().y;
592 if (tbpos != old_unit_pos) {
593 videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
595 videotl_group->show();
596 videotl_label.show();
597 tbpos += timebar_height * videotl_bar_height;
598 tbgpos += timebar_height * videotl_bar_height;
599 visible_timebars+=videotl_bar_height;
600 queue_visual_videotimeline_update();
602 videotl_group->hide();
603 videotl_label.hide();
604 update_video_timeline(true);
607 time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
609 /* move hv_scroll_group (trackviews) to the end of the timebars
612 hv_scroll_group->set_y_position (timebar_height * visible_timebars);
614 compute_fixed_ruler_scale ();
615 update_fixed_rulers();
616 redisplay_tempo (false);
618 /* Changing ruler visibility means that any lines on markers might need updating */
619 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
620 i->second->setup_lines ();
625 Editor::update_just_timecode ()
627 ENSURE_GUI_THREAD (*this, &Editor::update_just_timecode)
633 framepos_t rightmost_frame = leftmost_frame + current_page_samples();
635 if (ruler_timecode_action->get_active()) {
636 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
641 Editor::compute_fixed_ruler_scale ()
647 if (ruler_timecode_action->get_active()) {
648 set_timecode_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
651 if (ruler_minsec_action->get_active()) {
652 set_minsec_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
655 if (ruler_samples_action->get_active()) {
656 set_samples_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
661 Editor::update_fixed_rulers ()
663 framepos_t rightmost_frame;
669 compute_fixed_ruler_scale ();
671 _timecode_metric->units_per_pixel = samples_per_pixel;
672 _samples_metric->units_per_pixel = samples_per_pixel;
673 _minsec_metric->units_per_pixel = samples_per_pixel;
675 rightmost_frame = leftmost_frame + current_page_samples();
677 /* these force a redraw, which in turn will force execution of the metric callbacks
678 to compute the relevant ticks to display.
681 if (ruler_timecode_action->get_active()) {
682 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
685 if (ruler_samples_action->get_active()) {
686 samples_ruler->set_range (leftmost_frame, rightmost_frame);
689 if (ruler_minsec_action->get_active()) {
690 minsec_ruler->set_range (leftmost_frame, rightmost_frame);
695 Editor::update_tempo_based_rulers (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
696 ARDOUR::TempoMap::BBTPointList::const_iterator& end)
702 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame+current_page_samples(),
705 _bbt_metric->units_per_pixel = samples_per_pixel;
707 if (ruler_bbt_action->get_active()) {
708 bbt_ruler->set_range (leftmost_frame, leftmost_frame+current_page_samples());
714 Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
725 fr = _session->frame_rate();
727 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
728 lower = lower - spacer;
733 upper = upper + spacer;
734 framecnt_t const range = upper - lower;
736 if (range < (2 * _session->frames_per_timecode_frame())) { /* 0 - 2 frames */
737 timecode_ruler_scale = timecode_show_bits;
738 timecode_mark_modulo = 20;
739 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
740 } else if (range <= (fr / 4)) { /* 2 frames - 0.250 second */
741 timecode_ruler_scale = timecode_show_frames;
742 timecode_mark_modulo = 1;
743 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
744 } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
745 timecode_ruler_scale = timecode_show_frames;
746 timecode_mark_modulo = 2;
747 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
748 } else if (range <= fr) { /* 0.5-1 second */
749 timecode_ruler_scale = timecode_show_frames;
750 timecode_mark_modulo = 5;
751 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
752 } else if (range <= 2 * fr) { /* 1-2 seconds */
753 timecode_ruler_scale = timecode_show_frames;
754 timecode_mark_modulo = 10;
755 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
756 } else if (range <= 8 * fr) { /* 2-8 seconds */
757 timecode_ruler_scale = timecode_show_seconds;
758 timecode_mark_modulo = 1;
759 timecode_nmarks = 2 + (range / fr);
760 } else if (range <= 16 * fr) { /* 8-16 seconds */
761 timecode_ruler_scale = timecode_show_seconds;
762 timecode_mark_modulo = 2;
763 timecode_nmarks = 2 + (range / fr);
764 } else if (range <= 30 * fr) { /* 16-30 seconds */
765 timecode_ruler_scale = timecode_show_seconds;
766 timecode_mark_modulo = 5;
767 timecode_nmarks = 2 + (range / fr);
768 } else if (range <= 60 * fr) { /* 30-60 seconds */
769 timecode_ruler_scale = timecode_show_seconds;
770 timecode_mark_modulo = 5;
771 timecode_nmarks = 2 + (range / fr);
772 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
773 timecode_ruler_scale = timecode_show_seconds;
774 timecode_mark_modulo = 15;
775 timecode_nmarks = 2 + (range / fr);
776 } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
777 timecode_ruler_scale = timecode_show_seconds;
778 timecode_mark_modulo = 30;
779 timecode_nmarks = 2 + (range / fr);
780 } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
781 timecode_ruler_scale = timecode_show_minutes;
782 timecode_mark_modulo = 2;
783 timecode_nmarks = 2 + 10;
784 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
785 timecode_ruler_scale = timecode_show_minutes;
786 timecode_mark_modulo = 5;
787 timecode_nmarks = 2 + 30;
788 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
789 timecode_ruler_scale = timecode_show_minutes;
790 timecode_mark_modulo = 10;
791 timecode_nmarks = 2 + 60;
792 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
793 timecode_ruler_scale = timecode_show_minutes;
794 timecode_mark_modulo = 30;
795 timecode_nmarks = 2 + (60 * 4);
796 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
797 timecode_ruler_scale = timecode_show_hours;
798 timecode_mark_modulo = 1;
799 timecode_nmarks = 2 + 8;
800 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
801 timecode_ruler_scale = timecode_show_hours;
802 timecode_mark_modulo = 1;
803 timecode_nmarks = 2 + 24;
806 const framecnt_t hours_in_range = range / (60 * 60 * fr);
807 const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
809 /* Normally we do not need to know anything about the width of the canvas
810 to set the ruler scale, because the caller has already determined
811 the width and set lower + upper arguments to this function to match that.
813 But in this case, where the range defined by lower and uppper can vary
814 substantially (basically anything from 24hrs+ to several billion years)
815 trying to decide which tick marks to show does require us to know
816 about the available width.
819 timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
820 timecode_ruler_scale = timecode_show_many_hours;
821 timecode_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
826 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
830 Timecode::Time timecode;
833 ArdourCanvas::Ruler::Mark mark;
839 if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
840 lower = lower - spacer;
845 pos = (framecnt_t) floor (lower);
847 switch (timecode_ruler_scale) {
848 case timecode_show_bits:
849 // Find timecode time of this sample (pos) with subframe accuracy
850 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */ );
851 for (n = 0; n < timecode_nmarks; n++) {
852 _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
853 if ((timecode.subframes % timecode_mark_modulo) == 0) {
854 if (timecode.subframes == 0) {
855 mark.style = ArdourCanvas::Ruler::Mark::Major;
856 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
858 mark.style = ArdourCanvas::Ruler::Mark::Minor;
859 snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
862 snprintf (buf, sizeof(buf)," ");
863 mark.style = ArdourCanvas::Ruler::Mark::Micro;
867 marks.push_back (mark);
868 // Increment subframes by one
869 Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
873 case timecode_show_frames:
874 // Find timecode time of this sample (pos)
875 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
876 // Go to next whole frame down
877 Timecode::frames_floor( timecode );
878 for (n = 0; n < timecode_nmarks; n++) {
879 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
880 if ((timecode.frames % timecode_mark_modulo) == 0) {
881 if (timecode.frames == 0) {
882 mark.style = ArdourCanvas::Ruler::Mark::Major;
884 mark.style = ArdourCanvas::Ruler::Mark::Minor;
887 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
889 snprintf (buf, sizeof(buf)," ");
890 mark.style = ArdourCanvas::Ruler::Mark::Micro;
894 marks.push_back (mark);
895 Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
899 case timecode_show_seconds:
900 // Find timecode time of this sample (pos)
901 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
902 // Go to next whole second down
903 Timecode::seconds_floor( timecode );
904 for (n = 0; n < timecode_nmarks; n++) {
905 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
906 if ((timecode.seconds % timecode_mark_modulo) == 0) {
907 if (timecode.seconds == 0) {
908 mark.style = ArdourCanvas::Ruler::Mark::Major;
911 mark.style = ArdourCanvas::Ruler::Mark::Minor;
914 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
916 snprintf (buf, sizeof(buf)," ");
917 mark.style = ArdourCanvas::Ruler::Mark::Micro;
921 marks.push_back (mark);
922 Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
926 case timecode_show_minutes:
927 //Find timecode time of this sample (pos)
928 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
929 // Go to next whole minute down
930 Timecode::minutes_floor( timecode );
931 for (n = 0; n < timecode_nmarks; n++) {
932 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
933 if ((timecode.minutes % timecode_mark_modulo) == 0) {
934 if (timecode.minutes == 0) {
935 mark.style = ArdourCanvas::Ruler::Mark::Major;
937 mark.style = ArdourCanvas::Ruler::Mark::Minor;
939 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
941 snprintf (buf, sizeof(buf)," ");
942 mark.style = ArdourCanvas::Ruler::Mark::Micro;
946 marks.push_back (mark);
947 Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
950 case timecode_show_hours:
951 // Find timecode time of this sample (pos)
952 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
953 // Go to next whole hour down
954 Timecode::hours_floor( timecode );
955 for (n = 0; n < timecode_nmarks; n++) {
956 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
957 if ((timecode.hours % timecode_mark_modulo) == 0) {
958 mark.style = ArdourCanvas::Ruler::Mark::Major;
959 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
961 snprintf (buf, sizeof(buf)," ");
962 mark.style = ArdourCanvas::Ruler::Mark::Micro;
966 marks.push_back (mark);
967 Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
970 case timecode_show_many_hours:
971 // Find timecode time of this sample (pos)
972 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
973 // Go to next whole hour down
974 Timecode::hours_floor (timecode);
976 for (n = 0; n < timecode_nmarks; ) {
977 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
978 if ((timecode.hours % timecode_mark_modulo) == 0) {
979 mark.style = ArdourCanvas::Ruler::Mark::Major;
980 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
983 marks.push_back (mark);
986 /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
987 and doing it 1 hour at a time is just stupid (and slow).
989 timecode.hours += timecode_mark_modulo;
996 Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
997 ARDOUR::TempoMap::BBTPointList::const_iterator begin,
998 ARDOUR::TempoMap::BBTPointList::const_iterator end)
1000 if (_session == 0) {
1004 TempoMap::BBTPointList::const_iterator i;
1005 Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
1007 _session->bbt_time (lower, lower_beat);
1008 _session->bbt_time (upper, upper_beat);
1011 bbt_accent_modulo = 1;
1012 bbt_bar_helper_on = false;
1016 bbt_ruler_scale = bbt_show_many;
1018 switch (_snap_type) {
1019 case SnapToBeatDiv2:
1020 bbt_beat_subdivision = 2;
1022 case SnapToBeatDiv3:
1023 bbt_beat_subdivision = 3;
1025 case SnapToBeatDiv4:
1026 bbt_beat_subdivision = 4;
1028 case SnapToBeatDiv5:
1029 bbt_beat_subdivision = 5;
1030 bbt_accent_modulo = 2; // XXX YIKES
1032 case SnapToBeatDiv6:
1033 bbt_beat_subdivision = 6;
1034 bbt_accent_modulo = 2; // XXX YIKES
1036 case SnapToBeatDiv7:
1037 bbt_beat_subdivision = 7;
1038 bbt_accent_modulo = 2; // XXX YIKES
1040 case SnapToBeatDiv8:
1041 bbt_beat_subdivision = 8;
1042 bbt_accent_modulo = 2;
1044 case SnapToBeatDiv10:
1045 bbt_beat_subdivision = 10;
1046 bbt_accent_modulo = 2; // XXX YIKES
1048 case SnapToBeatDiv12:
1049 bbt_beat_subdivision = 12;
1050 bbt_accent_modulo = 3;
1052 case SnapToBeatDiv14:
1053 bbt_beat_subdivision = 14;
1054 bbt_accent_modulo = 3; // XXX YIKES!
1056 case SnapToBeatDiv16:
1057 bbt_beat_subdivision = 16;
1058 bbt_accent_modulo = 4;
1060 case SnapToBeatDiv20:
1061 bbt_beat_subdivision = 20;
1062 bbt_accent_modulo = 5;
1064 case SnapToBeatDiv24:
1065 bbt_beat_subdivision = 24;
1066 bbt_accent_modulo = 6;
1068 case SnapToBeatDiv28:
1069 bbt_beat_subdivision = 28;
1070 bbt_accent_modulo = 7;
1072 case SnapToBeatDiv32:
1073 bbt_beat_subdivision = 32;
1074 bbt_accent_modulo = 8;
1076 case SnapToBeatDiv64:
1077 bbt_beat_subdivision = 64;
1078 bbt_accent_modulo = 8;
1080 case SnapToBeatDiv128:
1081 bbt_beat_subdivision = 128;
1082 bbt_accent_modulo = 8;
1085 bbt_beat_subdivision = 4;
1089 if (distance (begin, end) == 0) {
1095 if ((*i).beat >= (*begin).beat) {
1096 bbt_bars = (*i).bar - (*begin).bar;
1098 bbt_bars = (*i).bar - (*begin).bar - 1;
1100 beats = distance (begin, end) - bbt_bars;
1102 /* Only show the bar helper if there aren't many bars on the screen */
1103 if ((bbt_bars < 2) || (beats < 5)) {
1104 bbt_bar_helper_on = true;
1107 if (bbt_bars > 8192) {
1108 bbt_ruler_scale = bbt_show_many;
1109 } else if (bbt_bars > 1024) {
1110 bbt_ruler_scale = bbt_show_64;
1111 } else if (bbt_bars > 256) {
1112 bbt_ruler_scale = bbt_show_16;
1113 } else if (bbt_bars > 64) {
1114 bbt_ruler_scale = bbt_show_4;
1115 } else if (bbt_bars > 10) {
1116 bbt_ruler_scale = bbt_show_1;
1117 } else if (bbt_bars > 2) {
1118 bbt_ruler_scale = bbt_show_beats;
1119 } else if (bbt_bars > 0) {
1120 bbt_ruler_scale = bbt_show_ticks;
1122 bbt_ruler_scale = bbt_show_ticks_detail;
1125 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)) {
1126 bbt_ruler_scale = bbt_show_ticks_super_detail;
1131 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1133 ArdourCanvas::Ruler::Mark copy = marks.back();
1134 copy.label = newlabel;
1136 marks.push_back (copy);
1140 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1142 if (_session == 0) {
1146 TempoMap::BBTPointList::const_iterator i;
1151 Timecode::BBT_Time next_beat;
1152 framepos_t next_beat_pos;
1157 framepos_t frame_skip;
1158 double frame_skip_error;
1159 double bbt_position_of_helper;
1160 double accumulated_error;
1161 bool i_am_accented = false;
1162 bool helper_active = false;
1163 ArdourCanvas::Ruler::Mark mark;
1165 ARDOUR::TempoMap::BBTPointList::const_iterator begin;
1166 ARDOUR::TempoMap::BBTPointList::const_iterator end;
1168 compute_current_bbt_points (lower, upper, begin, end);
1170 if (distance (begin, end) == 0) {
1174 switch (bbt_ruler_scale) {
1176 case bbt_show_beats:
1177 beats = distance (begin, end);
1178 bbt_nmarks = beats + 2;
1181 mark.position = lower;
1182 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1183 marks.push_back (mark);
1185 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1187 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1188 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1189 edit_last_mark_label (marks, buf);
1190 helper_active = true;
1193 if ((*i).is_bar()) {
1194 mark.style = ArdourCanvas::Ruler::Mark::Major;
1195 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1196 } else if (((*i).beat % 2 == 1)) {
1197 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1200 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1204 mark.position = (*i).frame;
1205 marks.push_back (mark);
1211 case bbt_show_ticks:
1213 beats = distance (begin, end);
1214 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1216 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1218 // could do marks.assign() here to preallocate
1221 mark.position = lower;
1222 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1223 marks.push_back (mark);
1225 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1227 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1228 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1229 edit_last_mark_label (marks, buf);
1230 helper_active = true;
1233 if ((*i).is_bar()) {
1234 mark.style = ArdourCanvas::Ruler::Mark::Major;
1235 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1237 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1238 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1240 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1244 mark.position = (*i).frame;
1245 marks.push_back (mark);
1249 /* Add the tick marks */
1251 /* Find the next beat */
1252 next_beat.beats = (*i).beat;
1253 next_beat.bars = (*i).bar;
1254 next_beat.ticks = 0;
1256 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1257 next_beat.beats += 1;
1259 next_beat.bars += 1;
1260 next_beat.beats = 1;
1263 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1265 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1266 frame_skip_error -= frame_skip;
1267 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1269 pos = (*i).frame + frame_skip;
1270 accumulated_error = frame_skip_error;
1274 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1276 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1277 i_am_accented = true;
1282 /* Error compensation for float to framepos_t*/
1283 accumulated_error += frame_skip_error;
1284 if (accumulated_error > 1) {
1286 accumulated_error -= 1.0f;
1289 mark.position = pos;
1291 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1292 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1294 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1296 i_am_accented = false;
1297 marks.push_back (mark);
1304 case bbt_show_ticks_detail:
1306 beats = distance (begin, end);
1307 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1309 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1312 mark.position = lower;
1313 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1314 marks.push_back (mark);
1316 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1318 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1319 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1320 edit_last_mark_label (marks, buf);
1321 helper_active = true;
1324 if ((*i).is_bar()) {
1325 mark.style = ArdourCanvas::Ruler::Mark::Major;
1326 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1328 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1329 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1331 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1335 mark.position = (*i).frame;
1336 marks.push_back (mark);
1340 /* Add the tick marks */
1342 /* Find the next beat */
1344 next_beat.beats = (*i).beat;
1345 next_beat.bars = (*i).bar;
1347 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1348 next_beat.beats += 1;
1350 next_beat.bars += 1;
1351 next_beat.beats = 1;
1354 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1356 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1357 frame_skip_error -= frame_skip;
1358 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1360 pos = (*i).frame + frame_skip;
1361 accumulated_error = frame_skip_error;
1365 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1367 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1368 i_am_accented = true;
1371 if (i_am_accented && (pos > bbt_position_of_helper)){
1372 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1379 /* Error compensation for float to framepos_t*/
1380 accumulated_error += frame_skip_error;
1381 if (accumulated_error > 1) {
1383 accumulated_error -= 1.0f;
1386 mark.position = pos;
1388 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1389 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1391 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1393 i_am_accented = false;
1400 case bbt_show_ticks_super_detail:
1402 beats = distance (begin, end);
1403 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1405 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1408 mark.position = lower;
1409 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1410 marks.push_back (mark);
1412 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1414 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1415 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1416 edit_last_mark_label (marks, buf);
1417 helper_active = true;
1420 if ((*i).is_bar()) {
1421 mark.style = ArdourCanvas::Ruler::Mark::Major;
1422 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1424 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1425 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1427 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1431 mark.position = (*i).frame;
1432 marks.push_back (mark);
1436 /* Add the tick marks */
1438 /* Find the next beat */
1440 next_beat.beats = (*i).beat;
1441 next_beat.bars = (*i).bar;
1443 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1444 next_beat.beats += 1;
1446 next_beat.bars += 1;
1447 next_beat.beats = 1;
1450 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1452 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1453 frame_skip_error -= frame_skip;
1454 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1456 pos = (*i).frame + frame_skip;
1457 accumulated_error = frame_skip_error;
1461 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1463 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1464 i_am_accented = true;
1467 if (pos > bbt_position_of_helper) {
1468 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1475 /* Error compensation for float to framepos_t*/
1476 accumulated_error += frame_skip_error;
1477 if (accumulated_error > 1) {
1479 accumulated_error -= 1.0f;
1482 mark.position = pos;
1484 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1485 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1487 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1489 i_am_accented = false;
1490 marks.push_back (mark);
1499 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1500 mark.style = ArdourCanvas::Ruler::Mark::Major;
1502 mark.position = lower;
1503 marks.push_back (mark);
1507 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1508 for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
1509 if ((*i).is_bar()) {
1510 if ((*i).bar % 64 == 1) {
1511 if ((*i).bar % 256 == 1) {
1512 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1513 mark.style = ArdourCanvas::Ruler::Mark::Major;
1516 if ((*i).bar % 256 == 129) {
1517 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1519 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1523 mark.position = (*i).frame;
1524 marks.push_back (mark);
1532 bbt_nmarks = (bbt_bars / 16) + 1;
1533 for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
1534 if ((*i).is_bar()) {
1535 if ((*i).bar % 16 == 1) {
1536 if ((*i).bar % 64 == 1) {
1537 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1538 mark.style = ArdourCanvas::Ruler::Mark::Major;
1541 if ((*i).bar % 64 == 33) {
1542 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1544 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1548 mark.position = (*i).frame;
1549 marks.push_back (mark);
1557 bbt_nmarks = (bbt_bars / 4) + 1;
1558 for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
1559 if ((*i).is_bar()) {
1560 if ((*i).bar % 4 == 1) {
1561 if ((*i).bar % 16 == 1) {
1562 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1563 mark.style = ArdourCanvas::Ruler::Mark::Major;
1566 if ((*i).bar % 16 == 9) {
1567 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1569 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1573 mark.position = (*i).frame;
1574 marks.push_back (mark);
1583 bbt_nmarks = bbt_bars + 2;
1584 for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
1585 if ((*i).is_bar()) {
1586 if ((*i).bar % 4 == 1) {
1587 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1588 mark.style = ArdourCanvas::Ruler::Mark::Major;
1591 if ((*i).bar % 4 == 3) {
1592 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1594 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1598 mark.position = (*i).frame;
1599 marks.push_back (mark);
1609 Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
1611 _samples_ruler_interval = (upper - lower) / 5;
1615 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1618 framepos_t const ilower = (framepos_t) floor (lower);
1622 ArdourCanvas::Ruler::Mark mark;
1624 if (_session == 0) {
1629 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1630 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1632 mark.position = pos;
1633 mark.style = ArdourCanvas::Ruler::Mark::Major;
1634 marks.push_back (mark);
1639 sample_to_clock_parts ( framepos_t sample,
1640 framepos_t sample_rate,
1654 hrs = left / (sample_rate * 60 * 60 * 1000);
1655 left -= hrs * sample_rate * 60 * 60 * 1000;
1656 mins = left / (sample_rate * 60 * 1000);
1657 left -= mins * sample_rate * 60 * 1000;
1658 secs = left / (sample_rate * 1000);
1659 left -= secs * sample_rate * 1000;
1660 millisecs = left / sample_rate;
1662 *millisecs_p = millisecs;
1671 Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
1673 framepos_t fr = _session->frame_rate() * 1000;
1676 if (_session == 0) {
1681 /* to prevent 'flashing' */
1682 if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1688 framecnt_t const range = (upper - lower) * 1000;
1690 if (range <= (fr / 10)) { /* 0-0.1 second */
1691 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1692 minsec_ruler_scale = minsec_show_msecs;
1693 minsec_mark_modulo = 10;
1694 minsec_nmarks = 2 + (range / minsec_mark_interval);
1695 } else if (range <= (fr / 2)) { /* 0-0.5 second */
1696 minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1697 minsec_ruler_scale = minsec_show_msecs;
1698 minsec_mark_modulo = 100;
1699 minsec_nmarks = 2 + (range / minsec_mark_interval);
1700 } else if (range <= fr) { /* 0-1 second */
1701 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1702 minsec_ruler_scale = minsec_show_msecs;
1703 minsec_mark_modulo = 200;
1704 minsec_nmarks = 2 + (range / minsec_mark_interval);
1705 } else if (range <= 2 * fr) { /* 1-2 seconds */
1706 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1707 minsec_ruler_scale = minsec_show_msecs;
1708 minsec_mark_modulo = 500;
1709 minsec_nmarks = 2 + (range / minsec_mark_interval);
1710 } else if (range <= 8 * fr) { /* 2-5 seconds */
1711 minsec_mark_interval = fr / 5; /* show 2 seconds */
1712 minsec_ruler_scale = minsec_show_msecs;
1713 minsec_mark_modulo = 1000;
1714 minsec_nmarks = 2 + (range / minsec_mark_interval);
1715 } else if (range <= 16 * fr) { /* 8-16 seconds */
1716 minsec_mark_interval = fr; /* show 1 seconds */
1717 minsec_ruler_scale = minsec_show_seconds;
1718 minsec_mark_modulo = 2;
1719 minsec_nmarks = 2 + (range / minsec_mark_interval);
1720 } else if (range <= 30 * fr) { /* 10-30 seconds */
1721 minsec_mark_interval = fr; /* show 1 seconds */
1722 minsec_ruler_scale = minsec_show_seconds;
1723 minsec_mark_modulo = 5;
1724 minsec_nmarks = 2 + (range / minsec_mark_interval);
1725 } else if (range <= 60 * fr) { /* 30-60 seconds */
1726 minsec_mark_interval = fr; /* show 1 seconds */
1727 minsec_ruler_scale = minsec_show_seconds;
1728 minsec_mark_modulo = 5;
1729 minsec_nmarks = 2 + (range / minsec_mark_interval);
1730 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1731 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1732 minsec_ruler_scale = minsec_show_seconds;
1733 minsec_mark_modulo = 3;
1734 minsec_nmarks = 2 + (range / minsec_mark_interval);
1735 } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1736 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1737 minsec_ruler_scale = minsec_show_seconds;
1738 minsec_mark_modulo = 30;
1739 minsec_nmarks = 2 + (range / minsec_mark_interval);
1740 } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1741 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1742 minsec_ruler_scale = minsec_show_seconds;
1743 minsec_mark_modulo = 120;
1744 minsec_nmarks = 2 + (range / minsec_mark_interval);
1745 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1746 minsec_mark_interval = 60 * fr; /* show 1 minute */
1747 minsec_ruler_scale = minsec_show_minutes;
1748 minsec_mark_modulo = 5;
1749 minsec_nmarks = 2 + (range / minsec_mark_interval);
1750 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1751 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1752 minsec_ruler_scale = minsec_show_minutes;
1753 minsec_mark_modulo = 10;
1754 minsec_nmarks = 2 + (range / minsec_mark_interval);
1755 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1756 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1757 minsec_ruler_scale = minsec_show_minutes;
1758 minsec_mark_modulo = 30;
1759 minsec_nmarks = 2 + (range / minsec_mark_interval);
1760 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1761 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1762 minsec_ruler_scale = minsec_show_minutes;
1763 minsec_mark_modulo = 60;
1764 minsec_nmarks = 2 + (range / minsec_mark_interval);
1765 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1766 minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1767 minsec_ruler_scale = minsec_show_hours;
1768 minsec_mark_modulo = 2;
1769 minsec_nmarks = 2 + (range / minsec_mark_interval);
1772 const framecnt_t hours_in_range = range / (60 * 60 * fr);
1773 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1775 /* Normally we do not need to know anything about the width of the canvas
1776 to set the ruler scale, because the caller has already determined
1777 the width and set lower + upper arguments to this function to match that.
1779 But in this case, where the range defined by lower and uppper can vary
1780 substantially (anything from 24hrs+ to several billion years)
1781 trying to decide which tick marks to show does require us to know
1782 about the available width.
1785 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1786 minsec_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1787 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1788 minsec_ruler_scale = minsec_show_many_hours;
1793 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1797 long hrs, mins, secs, millisecs;
1800 ArdourCanvas::Ruler::Mark mark;
1802 if (_session == 0) {
1806 /* to prevent 'flashing' */
1807 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
1808 lower = lower - spacer;
1813 pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1815 switch (minsec_ruler_scale) {
1817 case minsec_show_msecs:
1818 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1819 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1820 if (millisecs % minsec_mark_modulo == 0) {
1821 if (millisecs == 0) {
1822 mark.style = ArdourCanvas::Ruler::Mark::Major;
1824 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1826 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1829 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1832 mark.position = pos/1000.0;
1833 marks.push_back (mark);
1837 case minsec_show_seconds:
1838 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1839 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1840 if (secs % minsec_mark_modulo == 0) {
1842 mark.style = ArdourCanvas::Ruler::Mark::Major;
1844 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1846 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1849 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1852 mark.position = pos/1000.0;
1853 marks.push_back (mark);
1857 case minsec_show_minutes:
1858 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1859 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1860 if (mins % minsec_mark_modulo == 0) {
1862 mark.style = ArdourCanvas::Ruler::Mark::Major;
1864 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1866 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1869 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1872 mark.position = pos/1000.0;
1873 marks.push_back (mark);
1877 case minsec_show_hours:
1878 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1879 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1880 if (hrs % minsec_mark_modulo == 0) {
1881 mark.style = ArdourCanvas::Ruler::Mark::Major;
1882 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1885 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1888 mark.position = pos/1000.0;
1889 marks.push_back (mark);
1893 case minsec_show_many_hours:
1894 for (n = 0; n < minsec_nmarks; ) {
1895 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1896 if (hrs % minsec_mark_modulo == 0) {
1897 mark.style = ArdourCanvas::Ruler::Mark::Major;
1898 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1900 mark.position = pos/1000.0;
1901 marks.push_back (mark);
1904 pos += minsec_mark_interval;