/* Copyright (C) 1999 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include // for sprintf #include #include "pbd/convert.h" #include "pbd/enumwriter.h" #include #include "gtkmm2ext/cairocell.h" #include "gtkmm2ext/utils.h" #include "gtkmm2ext/rgb_macros.h" #include "ardour/ardour.h" #include "ardour/session.h" #include "ardour/tempo.h" #include "ardour/profile.h" #include #include "ardour_ui.h" #include "audio_clock.h" #include "utils.h" #include "keyboard.h" #include "gui_thread.h" #include "i18n.h" using namespace ARDOUR; using namespace PBD; using namespace Gtk; using namespace std; using Gtkmm2ext::Keyboard; sigc::signal AudioClock::ModeChanged; vector AudioClock::clocks; const double AudioClock::info_font_scale_factor = 0.6; const double AudioClock::separator_height = 2.0; AudioClock::AudioClock (const string& clock_name, bool transient, const string& widget_name, bool allow_edit, bool follows_playhead, bool duration, bool with_info) : _name (clock_name) , is_transient (transient) , is_duration (duration) , editable (allow_edit) , _follows_playhead (follows_playhead) , _off (false) , _need_bg (true) , ops_menu (0) , editing_attr (0) , background_attr (0) , foreground_attr (0) , mode_based_info_ratio (1.0) , editing (false) , bbt_reference_time (-1) , last_when(0) , last_pdelta (0) , last_sdelta (0) , dragging (false) , drag_field (Field (0)) , _canonical_time_is_displayed (true) , _canonical_time (0) { set_flags (CAN_FOCUS); _layout = Pango::Layout::create (get_pango_context()); _layout->set_attributes (normal_attributes); if (with_info) { _left_layout = Pango::Layout::create (get_pango_context()); _right_layout = Pango::Layout::create (get_pango_context()); } set_widget_name (widget_name); _mode = BBT; /* lie to force mode switch */ set_mode (Timecode); set (last_when, true); if (!is_transient) { clocks.push_back (this); } } AudioClock::~AudioClock () { delete background_attr; delete foreground_attr; delete editing_attr; } void AudioClock::set_widget_name (const string& str) { if (str.empty()) { set_name ("clock"); } else { set_name (str + " clock"); } } void AudioClock::on_realize () { CairoWidget::on_realize (); set_font (); set_colors (); } void AudioClock::set_font () { Glib::RefPtr style = get_style (); Pango::FontDescription font; Pango::AttrFontDesc* font_attr; if (!is_realized()) { font = get_font_for_style (get_name()); } else { font = style->get_font(); } font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font)); normal_attributes.change (*font_attr); editing_attributes.change (*font_attr); /* now a smaller version of the same font */ delete font_attr; font.set_size ((int) lrint (font.get_size() * info_font_scale_factor)); font.set_weight (Pango::WEIGHT_NORMAL); font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font)); info_attributes.change (*font_attr); delete font_attr; } void AudioClock::set_active_state (Gtkmm2ext::ActiveState s) { CairoWidget::set_active_state (s); set_colors (); } void AudioClock::set_colors () { int r, g, b, a; uint32_t bg_color; uint32_t text_color; uint32_t editing_color; if (active_state()) { bg_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: background", get_name())); text_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: text", get_name())); editing_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: edited text", get_name())); } else { bg_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: background", get_name())); text_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: text", get_name())); editing_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: edited text", get_name())); } UINT_TO_RGBA (bg_color, &r, &g, &b, &a); r = lrint ((r/256.0) * 65535.0); g = lrint ((g/256.0) * 65535.0); b = lrint ((b/256.0) * 65535.0); background_attr = new Pango::AttrColor (Pango::Attribute::create_attr_background (r, g, b)); /* store for bg in ::render() */ bg_r = r/256.0; bg_g = g/256.0; bg_b = b/256.0; bg_a = a/256.0; UINT_TO_RGBA (text_color, &r, &g, &b, &a); r = lrint ((r/256.0) * 65535.0); g = lrint ((g/256.0) * 65535.0); b = lrint ((b/256.0) * 65535.0); foreground_attr = new Pango::AttrColor (Pango::Attribute::create_attr_foreground (r, g, b)); UINT_TO_RGBA (editing_color, &r, &g, &b, &a); r = lrint ((r/256.0) * 65535.0); g = lrint ((g/256.0) * 65535.0); b = lrint ((b/256.0) * 65535.0); editing_attr = new Pango::AttrColor (Pango::Attribute::create_attr_foreground (r, g, b)); normal_attributes.change (*background_attr); normal_attributes.change (*foreground_attr); info_attributes.change (*background_attr); info_attributes.change (*foreground_attr); editing_attributes.change (*background_attr); editing_attributes.change (*foreground_attr); editing_attributes.change (*editing_attr); if (!editing) { _layout->set_attributes (normal_attributes); } else { _layout->set_attributes (editing_attributes); } if (_left_layout) { _left_layout->set_attributes (info_attributes); _right_layout->set_attributes (info_attributes); } queue_draw (); } void AudioClock::render (cairo_t* cr) { if (_need_bg) { /* paint entire area the color of the parent window bg XXX try to optimize this so that we just paint the corners and other areas that may be exposed by rounded corners. */ Gdk::Color bg (get_parent_bg()); cairo_rectangle (cr, 0, 0, _width, _height); cairo_stroke_preserve (cr); cairo_set_source_rgb (cr, bg.get_red_p(), bg.get_green_p(), bg.get_blue_p()); cairo_fill (cr); } /* main layout: rounded rect, plus the text */ cairo_set_source_rgba (cr, bg_r, bg_g, bg_b, bg_a); Gtkmm2ext::rounded_rectangle (cr, 0, 0, _width, upper_height, 9); cairo_move_to (cr, 6, (upper_height - layout_height) / 2.0); pango_cairo_show_layout (cr, _layout->gobj()); if (_left_layout) { double h = _height - upper_height - separator_height; if (mode_based_info_ratio != 1.0) { double left_rect_width = round (((_width - separator_height) * mode_based_info_ratio) + 0.5); cairo_set_source_rgba (cr, bg_r, bg_g, bg_b, bg_a); Gtkmm2ext::rounded_rectangle (cr, 0, upper_height + separator_height, left_rect_width, h, 9); cairo_move_to (cr, 6, upper_height + separator_height + ((h - info_height)/2.0)); pango_cairo_show_layout (cr, _left_layout->gobj()); Gtkmm2ext::rounded_rectangle (cr, left_rect_width + separator_height, upper_height + separator_height, _width - separator_height - left_rect_width, h, 9); cairo_move_to (cr, 6 + left_rect_width + separator_height, upper_height + separator_height + ((h - info_height)/2.0)); pango_cairo_show_layout (cr, _right_layout->gobj()); } else { /* no info to display, or just one */ cairo_set_source_rgba (cr, bg_r, bg_g, bg_b, bg_a); Gtkmm2ext::rounded_rectangle (cr, 0, upper_height + separator_height, _width, h, 9); } } } void AudioClock::on_size_allocate (Gtk::Allocation& alloc) { CairoWidget::on_size_allocate (alloc); if (_left_layout) { upper_height = (_height/2.0) - 1.0; } else { upper_height = _height; } } void AudioClock::on_size_request (Gtk::Requisition* req) { Glib::RefPtr tmp; Glib::RefPtr style = get_style (); Pango::FontDescription font; tmp = Pango::Layout::create (get_pango_context()); if (!is_realized()) { font = get_font_for_style (get_name()); } else { font = style->get_font(); } tmp->set_font_description (font); /* this string is the longest thing we will ever display, and also includes the BBT "|" that may descends below the baseline a bit, and a comma for the minsecs mode where we printf a fractional value (XXX or should) */ tmp->set_text (" 88|88:88:88,88"); tmp->get_pixel_size (req->width, req->height); layout_height = req->height; layout_width = req->width; /* now tackle height, for which we need to know the height of the lower * layout */ if (_left_layout) { int w; font.set_size ((int) lrint (font.get_size() * info_font_scale_factor)); font.set_weight (Pango::WEIGHT_NORMAL); tmp->set_font_description (font); /* we only care about height, so put as much stuff in here as possible that might change the height. */ tmp->set_text ("qyhH|"); /* one ascender, one descender */ tmp->get_pixel_size (w, info_height); info_height += 4; req->height += info_height; req->height += separator_height; } } void AudioClock::show_edit_status (int length) { editing_attr->set_start_index (edit_string.length() - length); editing_attr->set_end_index (edit_string.length()); editing_attributes.change (*background_attr); editing_attributes.change (*foreground_attr); editing_attributes.change (*editing_attr); _layout->set_attributes (editing_attributes); } void AudioClock::start_edit () { edit_string = _layout->get_text (); pre_edit_string = edit_string; input_string.clear (); editing = true; show_edit_status (1); Keyboard::magic_widget_grab_focus (); grab_focus (); } void AudioClock::end_edit (bool modify) { if (modify) { bool ok = true; switch (_mode) { case Timecode: ok = timecode_validate_edit (edit_string); break; case BBT: ok = bbt_validate_edit (edit_string); break; case MinSec: break; case Frames: break; } if (!ok) { edit_string = pre_edit_string; input_string.clear (); _layout->set_text (edit_string); } else { editing = false; _layout->set_attributes (normal_attributes); ValueChanged(); /* EMIT_SIGNAL */ } } else { editing = false; _layout->set_text (pre_edit_string); } queue_draw (); if (!editing) { /* move focus back to the default widget in the top level window */ Keyboard::magic_widget_drop_focus (); Widget* top = get_toplevel(); if (top->is_toplevel ()) { Window* win = dynamic_cast (top); win->grab_focus (); } } } void AudioClock::session_configuration_changed (std::string p) { if (p != "timecode-offset" && p != "timecode-offset-negative") { return; } framecnt_t current; switch (_mode) { case Timecode: if (is_duration) { current = current_duration (); } else { current = current_time (); } set (current, true); break; default: break; } } void AudioClock::set (framepos_t when, bool force, framecnt_t offset, char which) { if ((!force && !is_visible()) || _session == 0) { return; } bool const pdelta = Config->get_primary_clock_delta_edit_cursor (); bool const sdelta = Config->get_secondary_clock_delta_edit_cursor (); if (offset && which == 'p' && pdelta) { when = (when > offset) ? when - offset : offset - when; } else if (offset && which == 's' && sdelta) { when = (when > offset) ? when - offset : offset - when; } if (when == last_when && !force) { return; } if (which == 'p' && pdelta && !last_pdelta) { set_name("TransportClockDisplayDelta"); last_pdelta = true; } else if (which == 'p' && !pdelta && last_pdelta) { set_name("TransportClockDisplay"); last_pdelta = false; } else if (which == 's' && sdelta && !last_sdelta) { set_name("SecondaryClockDisplayDelta"); last_sdelta = true; } else if (which == 's' && !sdelta && last_sdelta) { set_name("SecondaryClockDisplay"); last_sdelta = false; } if (!editing) { switch (_mode) { case Timecode: set_timecode (when, force); break; case BBT: set_bbt (when, force); break; case MinSec: set_minsec (when, force); break; case Frames: set_frames (when, force); break; } } if (when != last_when || force) { queue_draw (); } last_when = when; /* we're setting the time from a frames value, so keep it as the canonical value */ _canonical_time = when; _canonical_time_is_displayed = false; } void AudioClock::set_frames (framepos_t when, bool /*force*/) { char buf[32]; if (_off) { /* XXX with cairo, we can do better, surely? */ _layout->set_text ("-----------"); if (_left_layout) { _left_layout->set_text (""); _right_layout->set_text (""); } return; } snprintf (buf, sizeof (buf), "%10" PRId64, when); _layout->set_text (buf); if (_left_layout) { framecnt_t rate = _session->frame_rate(); if (fmod (rate, 1000.0) == 0.000) { sprintf (buf, "%" PRId64 "K", rate/1000); } else { sprintf (buf, "%" PRId64, rate); } _left_layout->set_text (buf); float vid_pullup = _session->config.get_video_pullup(); if (vid_pullup == 0.0) { _right_layout->set_text (_("none")); } else { sprintf (buf, "%-6.4f", vid_pullup); _right_layout->set_text (buf); } } } void AudioClock::set_minsec (framepos_t when, bool force) { char buf[32]; framecnt_t left; int hrs; int mins; int secs; int millisecs; if (_off) { _layout->set_text ("--:--:--"); if (_left_layout) { _left_layout->set_text (""); _right_layout->set_text (""); } return; } left = when; hrs = (int) floor (left / (_session->frame_rate() * 60.0f * 60.0f)); left -= (framecnt_t) floor (hrs * _session->frame_rate() * 60.0f * 60.0f); mins = (int) floor (left / (_session->frame_rate() * 60.0f)); left -= (framecnt_t) floor (mins * _session->frame_rate() * 60.0f); secs = (int) floor (left / (float) _session->frame_rate()); left -= (framecnt_t) floor (secs * _session->frame_rate()); millisecs = floor (left * 1000.0 / (float) _session->frame_rate()); snprintf (buf, sizeof (buf), "%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ".%03" PRIu32, hrs, mins, secs, millisecs); _layout->set_text (buf); } void AudioClock::set_timecode (framepos_t when, bool force) { char buf[32]; Timecode::Time TC; if (_off) { _layout->set_text ("--:--:--:--"); if (_left_layout) { _left_layout->set_text (""); _right_layout->set_text (""); } return; } if (is_duration) { _session->timecode_duration (when, TC); } else { _session->timecode_time (when, TC); } if (TC.negative) { snprintf (buf, sizeof (buf), "-%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32, TC.hours, TC.minutes, TC.seconds, TC.frames); } else { snprintf (buf, sizeof (buf), " %02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32, TC.hours, TC.minutes, TC.seconds, TC.frames); } _layout->set_text (buf); if (_right_layout) { double timecode_frames = _session->timecode_frames_per_second(); if (fmod(timecode_frames, 1.0) == 0.0) { sprintf (buf, "FPS %u %s", int (timecode_frames), (_session->timecode_drop_frames() ? "D" : "")); } else { sprintf (buf, "%.2f %s", timecode_frames, (_session->timecode_drop_frames() ? "D" : "")); } _right_layout->set_text (buf); } } void AudioClock::set_bbt (framepos_t when, bool force) { char buf[16]; Timecode::BBT_Time BBT; if (_off) { _layout->set_text ("--|--|--"); if (_left_layout) { _left_layout->set_text (""); _right_layout->set_text (""); } return; } /* handle a common case */ if (is_duration) { if (when == 0) { BBT.bars = 0; BBT.beats = 0; BBT.ticks = 0; } else { _session->tempo_map().bbt_time (when, BBT); BBT.bars--; BBT.beats--; } } else { _session->tempo_map().bbt_time (when, BBT); } snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%04" PRIu32, BBT.bars, BBT.beats, BBT.ticks); _layout->set_text (buf); if (_right_layout) { framepos_t pos; if (bbt_reference_time < 0) { pos = when; } else { pos = bbt_reference_time; } TempoMetric m (_session->tempo_map().metric_at (pos)); sprintf (buf, "%-5.2f", m.tempo().beats_per_minute()); _left_layout->set_text (buf); sprintf (buf, "%g|%g", m.meter().beats_per_bar(), m.meter().note_divisor()); _right_layout->set_text (buf); } } void AudioClock::set_session (Session *s) { SessionHandlePtr::set_session (s); if (_session) { _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&AudioClock::session_configuration_changed, this, _1), gui_context()); const XMLProperty* prop; XMLNode* node = _session->extra_xml (X_("ClockModes")); AudioClock::Mode amode; if (node) { for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) { if ((prop = (*i)->property (X_("name"))) && prop->value() == _name) { if ((prop = (*i)->property (X_("mode"))) != 0) { amode = AudioClock::Mode (string_2_enum (prop->value(), amode)); set_mode (amode); } if ((prop = (*i)->property (X_("on"))) != 0) { set_off (!string_is_affirmative (prop->value())); } break; } } } set (last_when, true); } } bool AudioClock::on_key_press_event (GdkEventKey* ev) { if (!editing) { return false; } /* return true for keys that we MIGHT use at release */ switch (ev->keyval) { case GDK_0: case GDK_KP_0: case GDK_1: case GDK_KP_1: case GDK_2: case GDK_KP_2: case GDK_3: case GDK_KP_3: case GDK_4: case GDK_KP_4: case GDK_5: case GDK_KP_5: case GDK_6: case GDK_KP_6: case GDK_7: case GDK_KP_7: case GDK_8: case GDK_KP_8: case GDK_9: case GDK_KP_9: case GDK_period: case GDK_comma: case GDK_KP_Decimal: case GDK_Tab: case GDK_Return: case GDK_KP_Enter: case GDK_Escape: return true; default: return false; } } bool AudioClock::on_key_release_event (GdkEventKey *ev) { if (!editing) { return false; } string new_text; char new_char = 0; switch (ev->keyval) { case GDK_0: case GDK_KP_0: new_char = '0'; break; case GDK_1: case GDK_KP_1: new_char = '1'; break; case GDK_2: case GDK_KP_2: new_char = '2'; break; case GDK_3: case GDK_KP_3: new_char = '3'; break; case GDK_4: case GDK_KP_4: new_char = '4'; break; case GDK_5: case GDK_KP_5: new_char = '5'; break; case GDK_6: case GDK_KP_6: new_char = '6'; break; case GDK_7: case GDK_KP_7: new_char = '7'; break; case GDK_8: case GDK_KP_8: new_char = '8'; break; case GDK_9: case GDK_KP_9: new_char = '9'; break; case GDK_Tab: case GDK_Return: case GDK_KP_Enter: end_edit (true); return true; break; case GDK_Escape: end_edit (false); ChangeAborted(); /* EMIT SIGNAL */ return true; default: return false; } if (input_string.length() >= insert_max) { /* eat the key event, but do no nothing with it */ return true; } input_string.insert (input_string.begin(), new_char); string::reverse_iterator ri; vector insert_at; int highlight_length; /* merge with pre-edit-string into edit string */ switch (_mode) { case Frames: edit_string = input_string; highlight_length = edit_string.length(); break; default: edit_string = pre_edit_string; /* backup through the original string, till we have * enough digits locations to put all the digits from * the input string. */ for (ri = edit_string.rbegin(); ri != edit_string.rend(); ++ri) { if (isdigit (*ri)) { insert_at.push_back (edit_string.length() - (ri - edit_string.rbegin()) - 1); if (insert_at.size() == input_string.length()) { break; } } } if (insert_at.size() != input_string.length()) { error << "something went wrong " << endmsg; } else { for (int i = input_string.length() - 1; i >= 0; --i) { edit_string[insert_at[i]] = input_string[i]; } highlight_length = edit_string.length() - insert_at.back(); } break; } if (edit_string != _layout->get_text()) { show_edit_status (highlight_length); _layout->set_text (edit_string); queue_draw (); } return true; } AudioClock::Field AudioClock::index_to_field (int index) const { switch (_mode) { case Timecode: if (index < 4) { return Timecode_Hours; } else if (index < 7) { return Timecode_Minutes; } else if (index < 10) { return Timecode_Seconds; } else if (index < 13) { return Timecode_Frames; } else { return Field (0); } break; case BBT: if (index < 5) { return Bars; } else if (index < 7) { return Beats; } else if (index < 12) { return Ticks; } else { return Field (0); } break; case MinSec: if (index < 3) { return Timecode_Hours; } else if (index < 6) { return MS_Minutes; } else if (index < 9) { return MS_Seconds; } else if (index < 12) { return MS_Milliseconds; } else { return Field (0); } break; case Frames: return AudioFrames; break; } } bool AudioClock::on_button_press_event (GdkEventButton *ev) { switch (ev->button) { case 1: if (editable) { dragging = true; /* make absolutely sure that the pointer is grabbed */ gdk_pointer_grab(ev->window,false , GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK), NULL,NULL,ev->time); drag_accum = 0; drag_start_y = ev->y; drag_y = ev->y; int index; int trailing; if (_layout->xy_to_index (ev->x * PANGO_SCALE, ev->y * PANGO_SCALE, index, trailing)) { drag_field = index_to_field (index); } else { drag_field = Field (0); } } break; default: return false; break; } return true; } bool AudioClock::on_button_release_event (GdkEventButton *ev) { if (editable) { if (dragging) { gdk_pointer_ungrab (GDK_CURRENT_TIME); dragging = false; if (ev->y > drag_start_y+1 || ev->y < drag_start_y-1 || Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)){ // we actually dragged so return without // setting editing focus, or we shift clicked return true; } else { if (ev->button == 1) { start_edit (); } } } } if (Keyboard::is_context_menu_event (ev)) { if (ops_menu == 0) { build_ops_menu (); } ops_menu->popup (1, ev->time); return true; } return false; } bool AudioClock::on_scroll_event (GdkEventScroll *ev) { int index; int trailing; if (_session == 0 || !editable) { return false; } if (!_layout->xy_to_index (ev->x * PANGO_SCALE, ev->y * PANGO_SCALE, index, trailing)) { /* not in the main layout */ return false; } Field f = index_to_field (index); framepos_t frames = 0; switch (ev->direction) { case GDK_SCROLL_UP: frames = get_frame_step (f); if (frames != 0) { if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { frames *= 10; } set (current_time() + frames, true); ValueChanged (); /* EMIT_SIGNAL */ } break; case GDK_SCROLL_DOWN: frames = get_frame_step (f); if (frames != 0) { if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { frames *= 10; } if ((double)current_time() - (double)frames < 0.0) { set (0, true); } else { set (current_time() - frames, true); } ValueChanged (); /* EMIT_SIGNAL */ } break; default: return false; break; } return true; } bool AudioClock::on_motion_notify_event (GdkEventMotion *ev) { if (_session == 0 || !dragging) { return false; } float pixel_frame_scale_factor = 0.2f; if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { pixel_frame_scale_factor = 0.1f; } if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) { pixel_frame_scale_factor = 0.025f; } double y_delta = ev->y - drag_y; drag_accum += y_delta*pixel_frame_scale_factor; drag_y = ev->y; if (trunc (drag_accum) != 0) { framepos_t frames; framepos_t pos; int dir; dir = (drag_accum < 0 ? 1:-1); pos = current_time(); frames = get_frame_step (drag_field,pos,dir); if (frames != 0 && frames * drag_accum < current_time()) { set ((framepos_t) floor (pos - drag_accum * frames), false); // minus because up is negative in GTK } else { set (0 , false); } drag_accum= 0; ValueChanged(); /* EMIT_SIGNAL */ } return true; } framepos_t AudioClock::get_frame_step (Field field, framepos_t pos, int dir) { framecnt_t f = 0; Timecode::BBT_Time BBT; switch (field) { case Timecode_Hours: f = (framecnt_t) floor (3600.0 * _session->frame_rate()); break; case Timecode_Minutes: f = (framecnt_t) floor (60.0 * _session->frame_rate()); break; case Timecode_Seconds: f = _session->frame_rate(); break; case Timecode_Frames: f = (framecnt_t) floor (_session->frame_rate() / _session->timecode_frames_per_second()); break; case AudioFrames: f = 1; break; case MS_Hours: f = (framecnt_t) floor (3600.0 * _session->frame_rate()); break; case MS_Minutes: f = (framecnt_t) floor (60.0 * _session->frame_rate()); break; case MS_Seconds: f = (framecnt_t) _session->frame_rate(); break; case MS_Milliseconds: f = (framecnt_t) floor (_session->frame_rate() / 1000.0); break; case Bars: BBT.bars = 1; BBT.beats = 0; BBT.ticks = 0; f = _session->tempo_map().bbt_duration_at (pos,BBT,dir); break; case Beats: BBT.bars = 0; BBT.beats = 1; BBT.ticks = 0; f = _session->tempo_map().bbt_duration_at(pos,BBT,dir); break; case Ticks: BBT.bars = 0; BBT.beats = 0; BBT.ticks = 1; f = _session->tempo_map().bbt_duration_at(pos,BBT,dir); break; default: error << string_compose (_("programming error: %1"), "attempt to get frames from non-text field!") << endmsg; f = 0; break; } return f; } framepos_t AudioClock::current_time (framepos_t pos) const { // if (!_canonical_time_is_displayed) { // return _canonical_time; //} framepos_t ret = 0; switch (_mode) { case Timecode: ret = timecode_frame_from_display (); break; case BBT: ret = bbt_frame_from_display (pos); break; case MinSec: ret = minsec_frame_from_display (); break; case Frames: ret = audio_frame_from_display (); break; } return ret; } framepos_t AudioClock::current_duration (framepos_t pos) const { framepos_t ret = 0; switch (_mode) { case Timecode: ret = timecode_frame_from_display (); break; case BBT: ret = bbt_frame_duration_from_display (pos); break; case MinSec: ret = minsec_frame_from_display (); break; case Frames: ret = audio_frame_from_display (); break; } return ret; } bool AudioClock::bbt_validate_edit (const string& str) { AnyTime any; sscanf (str.c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &any.bbt.bars, &any.bbt.beats, &any.bbt.ticks); if (!is_duration && any.bbt.bars == 0) { return false; } if (!is_duration && any.bbt.beats == 0) { return false; } return true; } bool AudioClock::timecode_validate_edit (const string& str) { Timecode::Time TC; if (sscanf (_layout->get_text().c_str(), "%" PRId32 ":%" PRId32 ":%" PRId32 ":%" PRId32, &TC.hours, &TC.minutes, &TC.seconds, &TC.frames) != 4) { return false; } if (TC.minutes > 59 || TC.seconds > 59) { return false; } if (TC.frames > (long)rint(_session->timecode_frames_per_second()) - 1) { return false; } if (_session->timecode_drop_frames()) { if (TC.minutes % 10 && TC.seconds == 0 && TC.frames < 2) { return false; } } return true; } framepos_t AudioClock::timecode_frame_from_display () const { if (_session == 0) { return 0; } Timecode::Time TC; framepos_t sample; sscanf (_layout->get_text().c_str(), "%d:%d:%d:%d", &TC.hours, &TC.minutes, &TC.seconds, &TC.frames); TC.rate = _session->timecode_frames_per_second(); TC.drop= _session->timecode_drop_frames(); _session->timecode_to_sample (TC, sample, false /* use_offset */, false /* use_subframes */ ); // timecode_tester (); return sample; } framepos_t AudioClock::minsec_frame_from_display () const { if (_session == 0) { return 0; } int hrs, mins, secs, millisecs; framecnt_t sr = _session->frame_rate(); sscanf (_layout->get_text().c_str(), "%d:%d:%d:%d", &hrs, &mins, &secs, &millisecs); return (framepos_t) floor ((hrs * 60.0f * 60.0f * sr) + (mins * 60.0f * sr) + (secs * sr) + (millisecs * sr / 1000.0)); } framepos_t AudioClock::bbt_frame_from_display (framepos_t pos) const { if (_session == 0) { error << "AudioClock::current_time() called with BBT mode but without session!" << endmsg; return 0; } AnyTime any; any.type = AnyTime::BBT; sscanf (_layout->get_text().c_str(), "%" PRId32 "|%" PRId32 "|%" PRId32, &any.bbt.bars, &any.bbt.beats, &any.bbt.ticks); if (is_duration) { any.bbt.bars++; any.bbt.beats++; return _session->any_duration_to_frames (pos, any); } else { return _session->convert_to_frames (any); } } framepos_t AudioClock::bbt_frame_duration_from_display (framepos_t pos) const { if (_session == 0) { error << "AudioClock::current_time() called with BBT mode but without session!" << endmsg; return 0; } Timecode::BBT_Time bbt; sscanf (_layout->get_text().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &bbt.bars, &bbt.beats, &bbt.ticks); return _session->tempo_map().bbt_duration_at(pos,bbt,1); } framepos_t AudioClock::audio_frame_from_display () const { framepos_t f; sscanf (_layout->get_text().c_str(), "%" PRId64, &f); return f; } void AudioClock::build_ops_menu () { using namespace Menu_Helpers; ops_menu = new Menu; MenuList& ops_items = ops_menu->items(); ops_menu->set_name ("ArdourContextMenu"); if (!Profile->get_sae()) { ops_items.push_back (MenuElem (_("Timecode"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Timecode))); } ops_items.push_back (MenuElem (_("Bars:Beats"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), BBT))); ops_items.push_back (MenuElem (_("Minutes:Seconds"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), MinSec))); ops_items.push_back (MenuElem (_("Samples"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Frames))); if (editable && !is_duration && !_follows_playhead) { ops_items.push_back (SeparatorElem()); ops_items.push_back (MenuElem (_("Set From Playhead"), sigc::mem_fun(*this, &AudioClock::set_from_playhead))); ops_items.push_back (MenuElem (_("Locate to This Time"), sigc::mem_fun(*this, &AudioClock::locate))); } } void AudioClock::set_from_playhead () { if (!_session) { return; } set (_session->transport_frame()); ValueChanged (); } void AudioClock::locate () { if (!_session || is_duration) { return; } _session->request_locate (current_time(), _session->transport_rolling ()); } void AudioClock::set_mode (Mode m) { if (_mode == m) { return; } _mode = m; switch (_mode) { case Timecode: insert_max = 9; // 8 digits + sign [-]2:2:2:2 mode_based_info_ratio = 0.5; break; case BBT: insert_max = 8; // 8 digits, 2|2|4 mode_based_info_ratio = 0.5; break; case MinSec: insert_max = 9; // 7 digits 2:2:2.3 mode_based_info_ratio = 1.0; break; case Frames: insert_max = INT_MAX; mode_based_info_ratio = 1.0; break; } set (last_when, true); if (!is_transient) { ModeChanged (); /* EMIT SIGNAL (the static one)*/ } mode_changed (); /* EMIT SIGNAL (the member one) */ } void AudioClock::set_bbt_reference (framepos_t pos) { bbt_reference_time = pos; } void AudioClock::on_style_changed (const Glib::RefPtr& old_style) { CairoWidget::on_style_changed (old_style); set_font (); set_colors (); } void AudioClock::set_is_duration (bool yn) { if (yn == is_duration) { return; } is_duration = yn; set (last_when, true, 0, 's'); } void AudioClock::set_off (bool yn) { if (_off == yn) { return; } _off = yn; if (_off) { _canonical_time = current_time (); _canonical_time_is_displayed = false; } else { _canonical_time_is_displayed = true; } /* force a possible redraw */ set (_canonical_time, true); } void AudioClock::focus () { start_edit (); } void AudioClock::set_draw_background (bool yn) { _need_bg = yn; } void AudioClock::timecode_tester () { #if 0 #define Timecode_SAMPLE_TEST_1 #define Timecode_SAMPLE_TEST_2 #define Timecode_SAMPLE_TEST_3 #define Timecode_SAMPLE_TEST_4 #define Timecode_SAMPLE_TEST_5 #define Timecode_SAMPLE_TEST_6 #define Timecode_SAMPLE_TEST_7 // Testcode for timecode<->sample conversions (P.S.) Timecode::Time timecode1; framepos_t sample1; framepos_t oldsample = 0; Timecode::Time timecode2; framecnt_t sample_increment; sample_increment = (framecnt_t)rint(_session->frame_rate() / _session->timecode_frames_per_second); #ifdef Timecode_SAMPLE_TEST_1 // Test 1: use_offset = false, use_subframes = false cout << "use_offset = false, use_subframes = false" << endl; for (int i = 0; i < 108003; i++) { _session->timecode_to_sample( timecode1, sample1, false /* use_offset */, false /* use_subframes */ ); _session->sample_to_timecode( sample1, timecode2, false /* use_offset */, false /* use_subframes */ ); if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1)))) { cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl; cout << "timecode1: " << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; cout << "sample: " << sample1 << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode2: " << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; break; } if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) { cout << "ERROR: timecode2 not equal timecode1" << endl; cout << "timecode1: " << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; cout << "sample: " << sample1 << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode2: " << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; break; } oldsample = sample1; _session->timecode_increment( timecode1 ); } cout << "sample_increment: " << sample_increment << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode: " << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; #endif #ifdef Timecode_SAMPLE_TEST_2 // Test 2: use_offset = true, use_subframes = false cout << "use_offset = true, use_subframes = false" << endl; timecode1.hours = 0; timecode1.minutes = 0; timecode1.seconds = 0; timecode1.frames = 0; timecode1.subframes = 0; sample1 = oldsample = 0; _session->sample_to_timecode( sample1, timecode1, true /* use_offset */, false /* use_subframes */ ); cout << "Starting at sample: " << sample1 << " -> "; cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl; for (int i = 0; i < 108003; i++) { _session->timecode_to_sample( timecode1, sample1, true /* use_offset */, false /* use_subframes */ ); _session->sample_to_timecode( sample1, timecode2, true /* use_offset */, false /* use_subframes */ ); // cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; // cout << "sample: " << sample1 << endl; // cout << "sample: " << sample1 << " -> "; // cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1)))) { cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl; cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; cout << "sample: " << sample1 << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; break; } if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) { cout << "ERROR: timecode2 not equal timecode1" << endl; cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; cout << "sample: " << sample1 << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; break; } oldsample = sample1; _session->timecode_increment( timecode1 ); } cout << "sample_increment: " << sample_increment << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; #endif #ifdef Timecode_SAMPLE_TEST_3 // Test 3: use_offset = true, use_subframes = false, decrement cout << "use_offset = true, use_subframes = false, decrement" << endl; _session->sample_to_timecode( sample1, timecode1, true /* use_offset */, false /* use_subframes */ ); cout << "Starting at sample: " << sample1 << " -> "; cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl; for (int i = 0; i < 108003; i++) { _session->timecode_to_sample( timecode1, sample1, true /* use_offset */, false /* use_subframes */ ); _session->sample_to_timecode( sample1, timecode2, true /* use_offset */, false /* use_subframes */ ); // cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; // cout << "sample: " << sample1 << endl; // cout << "sample: " << sample1 << " -> "; // cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; if ((i > 0) && ( ((oldsample - sample1) != sample_increment) && ((oldsample - sample1) != (sample_increment + 1)) && ((oldsample - sample1) != (sample_increment - 1)))) { cout << "ERROR: sample increment not right: " << (oldsample - sample1) << " != " << sample_increment << endl; cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; cout << "sample: " << sample1 << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; break; } if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) { cout << "ERROR: timecode2 not equal timecode1" << endl; cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; cout << "sample: " << sample1 << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; break; } oldsample = sample1; _session->timecode_decrement( timecode1 ); } cout << "sample_decrement: " << sample_increment << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; #endif #ifdef Timecode_SAMPLE_TEST_4 // Test 4: use_offset = true, use_subframes = true cout << "use_offset = true, use_subframes = true" << endl; for (long sub = 5; sub < 80; sub += 5) { timecode1.hours = 0; timecode1.minutes = 0; timecode1.seconds = 0; timecode1.frames = 0; timecode1.subframes = 0; sample1 = oldsample = (sample_increment * sub) / 80; _session->sample_to_timecode( sample1, timecode1, true /* use_offset */, true /* use_subframes */ ); cout << "starting at sample: " << sample1 << " -> "; cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl; for (int i = 0; i < 108003; i++) { _session->timecode_to_sample( timecode1, sample1, true /* use_offset */, true /* use_subframes */ ); _session->sample_to_timecode( sample1, timecode2, true /* use_offset */, true /* use_subframes */ ); if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1)))) { cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl; cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; cout << "sample: " << sample1 << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; //break; } if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames || timecode2.subframes != timecode1.subframes) { cout << "ERROR: timecode2 not equal timecode1" << endl; cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; cout << "sample: " << sample1 << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; break; } oldsample = sample1; _session->timecode_increment( timecode1 ); } cout << "sample_increment: " << sample_increment << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; for (int i = 0; i < 108003; i++) { _session->timecode_to_sample( timecode1, sample1, true /* use_offset */, true /* use_subframes */ ); _session->sample_to_timecode( sample1, timecode2, true /* use_offset */, true /* use_subframes */ ); if ((i > 0) && ( ((oldsample - sample1) != sample_increment) && ((oldsample - sample1) != (sample_increment + 1)) && ((oldsample - sample1) != (sample_increment - 1)))) { cout << "ERROR: sample increment not right: " << (oldsample - sample1) << " != " << sample_increment << endl; cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; cout << "sample: " << sample1 << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; //break; } if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames || timecode2.subframes != timecode1.subframes) { cout << "ERROR: timecode2 not equal timecode1" << endl; cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; cout << "sample: " << sample1 << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; break; } oldsample = sample1; _session->timecode_decrement( timecode1 ); } cout << "sample_decrement: " << sample_increment << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; } #endif #ifdef Timecode_SAMPLE_TEST_5 // Test 5: use_offset = true, use_subframes = false, increment seconds cout << "use_offset = true, use_subframes = false, increment seconds" << endl; timecode1.hours = 0; timecode1.minutes = 0; timecode1.seconds = 0; timecode1.frames = 0; timecode1.subframes = 0; sample1 = oldsample = 0; sample_increment = _session->frame_rate(); _session->sample_to_timecode( sample1, timecode1, true /* use_offset */, false /* use_subframes */ ); cout << "Starting at sample: " << sample1 << " -> "; cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl; for (int i = 0; i < 3600; i++) { _session->timecode_to_sample( timecode1, sample1, true /* use_offset */, false /* use_subframes */ ); _session->sample_to_timecode( sample1, timecode2, true /* use_offset */, false /* use_subframes */ ); // cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; // cout << "sample: " << sample1 << endl; // cout << "sample: " << sample1 << " -> "; // cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; // if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1)))) // { // cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl; // break; // } if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) { cout << "ERROR: timecode2 not equal timecode1" << endl; cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; cout << "sample: " << sample1 << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; break; } oldsample = sample1; _session->timecode_increment_seconds( timecode1 ); } cout << "sample_increment: " << sample_increment << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; #endif #ifdef Timecode_SAMPLE_TEST_6 // Test 6: use_offset = true, use_subframes = false, increment minutes cout << "use_offset = true, use_subframes = false, increment minutes" << endl; timecode1.hours = 0; timecode1.minutes = 0; timecode1.seconds = 0; timecode1.frames = 0; timecode1.subframes = 0; sample1 = oldsample = 0; sample_increment = _session->frame_rate() * 60; _session->sample_to_timecode( sample1, timecode1, true /* use_offset */, false /* use_subframes */ ); cout << "Starting at sample: " << sample1 << " -> "; cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl; for (int i = 0; i < 60; i++) { _session->timecode_to_sample( timecode1, sample1, true /* use_offset */, false /* use_subframes */ ); _session->sample_to_timecode( sample1, timecode2, true /* use_offset */, false /* use_subframes */ ); // cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; // cout << "sample: " << sample1 << endl; // cout << "sample: " << sample1 << " -> "; // cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; // if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1)))) // { // cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl; // break; // } if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) { cout << "ERROR: timecode2 not equal timecode1" << endl; cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; cout << "sample: " << sample1 << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; break; } oldsample = sample1; _session->timecode_increment_minutes( timecode1 ); } cout << "sample_increment: " << sample_increment << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; #endif #ifdef Timecode_SAMPLE_TEST_7 // Test 7: use_offset = true, use_subframes = false, increment hours cout << "use_offset = true, use_subframes = false, increment hours" << endl; timecode1.hours = 0; timecode1.minutes = 0; timecode1.seconds = 0; timecode1.frames = 0; timecode1.subframes = 0; sample1 = oldsample = 0; sample_increment = _session->frame_rate() * 60 * 60; _session->sample_to_timecode( sample1, timecode1, true /* use_offset */, false /* use_subframes */ ); cout << "Starting at sample: " << sample1 << " -> "; cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl; for (int i = 0; i < 10; i++) { _session->timecode_to_sample( timecode1, sample1, true /* use_offset */, false /* use_subframes */ ); _session->sample_to_timecode( sample1, timecode2, true /* use_offset */, false /* use_subframes */ ); // cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; // cout << "sample: " << sample1 << endl; // cout << "sample: " << sample1 << " -> "; // cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; // if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1)))) // { // cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl; // break; // } if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) { cout << "ERROR: timecode2 not equal timecode1" << endl; cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; cout << "sample: " << sample1 << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; break; } oldsample = sample1; _session->timecode_increment_hours( timecode1 ); } cout << "sample_increment: " << sample_increment << endl; cout << "sample: " << sample1 << " -> "; cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; #endif #endif }