diff options
34 files changed, 1028 insertions, 568 deletions
diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc index 74b5a70738..7f4a0a1e69 100644 --- a/gtk2_ardour/ardour_ui_ed.cc +++ b/gtk2_ardour/ardour_ui_ed.cc @@ -155,7 +155,7 @@ ARDOUR_UI::install_actions () Glib::RefPtr<ActionGroup> jack_actions = ActionGroup::create (X_("JACK")); ActionManager::register_action (jack_actions, X_("JACK"), _("JACK")); ActionManager::register_action (jack_actions, X_("Latency"), _("Latency")); - + act = ActionManager::register_action (jack_actions, X_("JACKReconnect"), _("Reconnect"), mem_fun (*(ARDOUR_UI::instance()), &ARDOUR_UI::reconnect_to_jack)); ActionManager::jack_opposite_sensitive_actions.push_back (act); diff --git a/gtk2_ardour/editing.h b/gtk2_ardour/editing.h index 93bbb603ee..2b4519ec9c 100644 --- a/gtk2_ardour/editing.h +++ b/gtk2_ardour/editing.h @@ -36,6 +36,7 @@ #define IMPORTMODE(a) /*empty*/ #define IMPORTPOSITION(a) #define IMPORTDISPOSITION(a) +#define EDITPOINT(a) /*empty*/ namespace Editing { @@ -168,6 +169,16 @@ enum ImportDisposition { #undef IMPORTDISPOSITION #define IMPORTDISPOSITION(a) /*empty*/ +// EDITPOINT +#undef EDITPOINT +#define EDITPOINT(a) a, +enum EditPoint { + #include "editing_syms.h" +}; + +#undef EDITPOINT +#define EDITPOINT(a) /*empty*/ + ///////////////////// // These don't need their state saved. yet... enum CutCopyOp { diff --git a/gtk2_ardour/editing_syms.h b/gtk2_ardour/editing_syms.h index f731d33efe..29fd1234a8 100644 --- a/gtk2_ardour/editing_syms.h +++ b/gtk2_ardour/editing_syms.h @@ -71,6 +71,7 @@ ZOOMFOCUS(ZoomFocusLeft) ZOOMFOCUS(ZoomFocusRight) ZOOMFOCUS(ZoomFocusCenter) ZOOMFOCUS(ZoomFocusPlayhead) +ZOOMFOCUS(ZoomFocusMouse) ZOOMFOCUS(ZoomFocusEdit) DISPLAYCONTROL(FollowPlayhead) @@ -95,3 +96,8 @@ IMPORTDISPOSITION(ImportDistinctFiles=0) IMPORTDISPOSITION(ImportMergeFiles=1) IMPORTDISPOSITION(ImportSerializeFiles=2) IMPORTDISPOSITION(ImportDistinctChannels=3) + + +EDITPOINT(EditAtPlayhead) +EDITPOINT(EditAtSelectedMarker) +EDITPOINT(EditAtMouse) diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 1e726277d2..bf05cc5485 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -139,11 +139,19 @@ static const gchar *_snap_mode_strings[] = { 0 }; +static const gchar *_edit_point_strings[] = { + N_("Playhead"), + N_("Marker"), + N_("Mouse"), + 0 +}; + static const gchar *_zoom_focus_strings[] = { N_("Left"), N_("Right"), N_("Center"), N_("Playhead"), + N_("Mouse"), N_("Edit Cursor"), 0 }; @@ -170,20 +178,6 @@ show_me_the_size (Requisition* r, const char* what) cerr << "size of " << what << " = " << r->width << " x " << r->height << endl; } -void -check_adjustment (Gtk::Adjustment* adj) -{ - cerr << "CHANGE adj = " - << adj->get_lower () << ' ' - << adj->get_upper () << ' ' - << adj->get_value () << ' ' - << adj->get_step_increment () << ' ' - << adj->get_page_increment () << ' ' - << adj->get_page_size () << ' ' - << endl; - -} - Editor::Editor () : /* time display buttons */ @@ -256,14 +250,20 @@ Editor::Editor () current_mixer_strip = 0; current_bbt_points = 0; - snap_type_strings = I18N (_snap_type_strings); - snap_mode_strings = I18N (_snap_mode_strings); - zoom_focus_strings = I18N(_zoom_focus_strings); + snap_type_strings = I18N (_snap_type_strings); + snap_mode_strings = I18N (_snap_mode_strings); + zoom_focus_strings = I18N (_zoom_focus_strings); + edit_point_strings = I18N (_edit_point_strings); snap_type = SnapToFrame; set_snap_to (snap_type); + snap_mode = SnapNormal; set_snap_mode (snap_mode); + + _edit_point = EditAtMouse; + set_edit_point (_edit_point); + snap_threshold = 5.0; bbt_beat_subdivision = 4; canvas_width = 0; @@ -329,13 +329,7 @@ Editor::Editor () _dragging_playhead = false; _dragging_hscrollbar = false; - _scrubbing = false; - mouse_direction = 1; - mouse_speed_update = -1; - mouse_speed_size = 16; - mouse_speed = new double[mouse_speed_size]; - memset (mouse_speed, 0, sizeof(double) * mouse_speed_size); - mouse_speed_entries = 0; + scrubbing_direction = 0; sfbrowser = 0; ignore_route_order_sync = false; @@ -2005,6 +1999,18 @@ Editor::set_snap_mode (SnapMode mode) instant_save (); } +void +Editor::set_edit_point (EditPoint ep) +{ + _edit_point = ep; + string str = edit_point_strings[(int)ep]; + + if (str != edit_point_selector.get_active_text ()) { + edit_point_selector.set_active_text (str); + } + + instant_save (); +} int Editor::set_state (const XMLNode& node) @@ -2082,6 +2088,10 @@ Editor::set_state (const XMLNode& node) set_snap_mode ((SnapMode) atoi (prop->value())); } + if ((prop = node.property ("edit-point"))) { + set_edit_point ((EditPoint) string_2_enum (prop->value(), _edit_point)); + } + if ((prop = node.property ("mouse-mode"))) { MouseMode m = str2mousemode(prop->value()); mouse_mode = MouseMode ((int) m + 1); /* lie, force mode switch */ @@ -2233,6 +2243,8 @@ Editor::get_state () snprintf (buf, sizeof(buf), "%d", (int) snap_mode); node->add_property ("snap-mode", buf); + node->add_property ("edit-point", enum_2_string (_edit_point)); + snprintf (buf, sizeof (buf), "%" PRIu32, playhead_cursor->current_frame); node->add_property ("playhead", buf); snprintf (buf, sizeof (buf), "%" PRIu32, edit_cursor->current_frame); @@ -2400,7 +2412,7 @@ Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark) break; case SnapToEditCursor: - start = edit_cursor->current_frame; + start = get_preferred_edit_position (); break; case SnapToMark: @@ -2681,17 +2693,24 @@ Editor::setup_toolbar () Gtkmm2ext::set_size_request_to_display_given_text (snap_type_selector, "SMPTE Seconds", 2+FUDGE, 10); set_popdown_strings (snap_type_selector, snap_type_strings); snap_type_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_type_selection_done)); - ARDOUR_UI::instance()->tooltips().set_tip (snap_type_selector, _("Unit to snap cursors and ranges to")); + ARDOUR_UI::instance()->tooltips().set_tip (snap_type_selector, _("Snap/Grid Units")); snap_mode_selector.set_name ("SnapModeSelector"); Gtkmm2ext::set_size_request_to_display_given_text (snap_mode_selector, "Magnetic Snap", 2+FUDGE, 10); set_popdown_strings (snap_mode_selector, snap_mode_strings); snap_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_mode_selection_done)); + ARDOUR_UI::instance()->tooltips().set_tip (snap_mode_selector, _("Snap/Grid Mode")); + + edit_point_selector.set_name ("SnapModeSelector"); + Gtkmm2ext::set_size_request_to_display_given_text (edit_point_selector, "Playhead", 2+FUDGE, 10); + set_popdown_strings (edit_point_selector, edit_point_strings); + edit_point_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_point_selection_done)); + ARDOUR_UI::instance()->tooltips().set_tip (edit_point_selector, _("Edit point")); snap_box.pack_start (edit_cursor_clock, false, false); snap_box.pack_start (snap_mode_selector, false, false); snap_box.pack_start (snap_type_selector, false, false); - + snap_box.pack_start (edit_point_selector, false, false); /* Nudge */ @@ -2845,8 +2864,6 @@ Editor::convert_drop_to_paths (vector<ustring>& paths, vector<ustring> uris = data.get_uris(); - cerr << "there were " << uris.size() << " in that drag data\n"; - if (uris.empty()) { /* This is seriously fucked up. Nautilus doesn't say that its URI lists @@ -3229,6 +3246,27 @@ Editor::snap_mode_selection_done () } void +Editor::edit_point_selection_done () +{ + string choice = edit_point_selector.get_active_text(); + EditPoint ep = EditAtSelectedMarker; + + if (choice == _("Marker")) { + _edit_point = EditAtSelectedMarker; + } else if (choice == _("Playhead")) { + _edit_point = EditAtPlayhead; + } else { + _edit_point = EditAtMouse; + } + + RefPtr<RadioAction> ract = edit_point_action (ep); + + if (ract) { + ract->set_active (true); + } +} + +void Editor::zoom_focus_selection_done () { string choice = zoom_focus_selector.get_active_text(); @@ -3244,6 +3282,8 @@ Editor::zoom_focus_selection_done () focus_type = ZoomFocusPlayhead; } else if (choice == _("Edit")) { focus_type = ZoomFocusEdit; + } else { + focus_type = ZoomFocusMouse; } RefPtr<RadioAction> ract = zoom_focus_action (focus_type); @@ -4025,6 +4065,35 @@ Editor::edit_cursor_position(bool sync) return edit_cursor->current_frame; } +nframes64_t +Editor::get_preferred_edit_position() const +{ + bool ignored; + nframes64_t where; + + switch (_edit_point) { + case EditAtPlayhead: + return playhead_cursor->current_frame; + + case EditAtSelectedMarker: + if (!selection->markers.empty()) { + bool whocares; + Location* loc = find_location_from_marker (selection->markers.front(), whocares); + if (loc) { + return loc->start(); + } + } + /* fallthru */ + + default: + case EditAtMouse: + if (mouse_frame (where, ignored)) { + return where; + } + } + + return -1; +} void Editor::set_loop_range (nframes_t start, nframes_t end, string cmd) diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 96c1d1004d..9f68929bfb 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -46,6 +46,7 @@ #include <pbd/stateful.h> #include <ardour/session.h> +#include <ardour/stretch.h> #include <ardour/tempo.h> #include <ardour/location.h> #include <ardour/audioregion.h> @@ -183,15 +184,15 @@ class Editor : public PublicEditor /* undo related */ - nframes_t unit_to_frame (double unit) { + nframes_t unit_to_frame (double unit) const { return (nframes_t) rint (unit * frames_per_unit); } - double frame_to_unit (nframes_t frame) { + double frame_to_unit (nframes_t frame) const { return rint ((double) frame / (double) frames_per_unit); } - double frame_to_unit (double frame) { + double frame_to_unit (double frame) const { return rint (frame / frames_per_unit); } @@ -202,7 +203,7 @@ class Editor : public PublicEditor xscroll_adjustment. */ - nframes_t pixel_to_frame (double pixel) { + nframes64_t pixel_to_frame (double pixel) const { /* pixel can be less than zero when motion events are processed. since we've already run the world->canvas @@ -217,7 +218,7 @@ class Editor : public PublicEditor } } - gulong frame_to_pixel (nframes_t frame) { + gulong frame_to_pixel (nframes64_t frame) const { return (gulong) rint ((frame / (frames_per_unit * GNOME_CANVAS(track_canvas.gobj())->pixels_per_unit))); } @@ -354,6 +355,8 @@ class Editor : public PublicEditor void reposition_and_zoom (nframes_t, double); nframes_t edit_cursor_position(bool); + nframes64_t get_preferred_edit_position () const; + bool update_mouse_speed (); bool decelerate_mouse_speed (); @@ -424,8 +427,8 @@ class Editor : public PublicEditor void set_color_rgba (uint32_t); }; - LocationMarkers *find_location_markers (ARDOUR::Location *); - ARDOUR::Location* find_location_from_marker (Marker *, bool& is_start); + LocationMarkers *find_location_markers (ARDOUR::Location *) const; + ARDOUR::Location* find_location_from_marker (Marker *, bool& is_start) const; typedef std::map<ARDOUR::Location*,LocationMarkers *> LocationMarkerMap; LocationMarkerMap location_markers; @@ -1136,16 +1139,8 @@ class Editor : public PublicEditor void stop_scrolling (); bool _scrubbing; - bool have_full_mouse_speed; - nframes64_t last_scrub_frame; - double last_scrub_time; - int mouse_speed_update; - double mouse_direction; - double compute_mouse_speed (); - void add_mouse_speed (double, double); - double* mouse_speed; - size_t mouse_speed_entries; - size_t mouse_speed_size; + double last_scrub_x; + int scrubbing_direction; void keyboard_selection_begin (); void keyboard_selection_finish (bool add); @@ -1834,14 +1829,18 @@ class Editor : public PublicEditor void duplicate_dialog (bool for_region); - nframes_t event_frame (GdkEvent*, double* px = 0, double* py = 0); + nframes64_t event_frame (GdkEvent*, double* px = 0, double* py = 0) const; + + /* returns false if mouse pointer is not in track or marker canvas + */ + bool mouse_frame (nframes64_t&, bool& in_track_canvas) const; void time_fx_motion (ArdourCanvas::Item*, GdkEvent*); void start_time_fx (ArdourCanvas::Item*, GdkEvent*); void end_time_fx (ArdourCanvas::Item*, GdkEvent*); struct TimeStretchDialog : public ArdourDialog { - ARDOUR::Session::TimeStretchRequest request; + ARDOUR::TimeStretchRequest request; Editor& editor; RegionSelection regions; Gtk::ProgressBar progress_bar; @@ -2001,6 +2000,16 @@ class Editor : public PublicEditor void history_changed (); Gtk::HBox status_bar_hpacker; + + Editing::EditPoint _edit_point; + + Gtk::ComboBoxText edit_point_selector; + + void set_edit_point (Editing::EditPoint ep); + void edit_point_selection_done (); + void edit_point_chosen (Editing::EditPoint); + Glib::RefPtr<Gtk::RadioAction> edit_point_action (Editing::EditPoint); + std::vector<std::string> edit_point_strings; }; #endif /* __ardour_editor_h__ */ diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index db81531583..b68f4bf2b7 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -291,6 +291,8 @@ Editor::register_actions () ActionManager::session_sensitive_actions.push_back (act); ActionManager::register_radio_action (zoom_actions, zoom_group, "zoom-focus-playhead", _("Zoom Focus Playhead"), bind (mem_fun(*this, &Editor::zoom_focus_chosen), Editing::ZoomFocusPlayhead)); ActionManager::session_sensitive_actions.push_back (act); + ActionManager::register_radio_action (zoom_actions, zoom_group, "zoom-focus-mouse", _("Zoom Focus Mouse"), bind (mem_fun(*this, &Editor::zoom_focus_chosen), Editing::ZoomFocusMouse)); + ActionManager::session_sensitive_actions.push_back (act); ActionManager::register_radio_action (zoom_actions, zoom_group, "zoom-focus-edit", _("Zoom Focus Edit"), bind (mem_fun(*this, &Editor::zoom_focus_chosen), Editing::ZoomFocusEdit)); ActionManager::session_sensitive_actions.push_back (act); @@ -304,6 +306,11 @@ Editor::register_actions () ActionManager::register_radio_action (mouse_mode_actions, mouse_mode_group, "set-mouse-mode-timefx", _("Timefx Tool"), bind (mem_fun(*this, &Editor::set_mouse_mode), Editing::MouseTimeFX, false)); ActionManager::register_radio_action (mouse_mode_actions, mouse_mode_group, "set-mouse-mode-note", _("Note Tool"), bind (mem_fun(*this, &Editor::set_mouse_mode), Editing::MouseNote, false)); + RadioAction::Group edit_point_group; + ActionManager::register_radio_action (editor_actions, edit_point_group, X_("edit-at-playhead"), _("Playhead"), (bind (mem_fun(*this, &Editor::edit_point_chosen), Editing::EditAtPlayhead))); + ActionManager::register_radio_action (editor_actions, edit_point_group, X_("edit-at-mouse"), _("Mouse"), (bind (mem_fun(*this, &Editor::edit_point_chosen), Editing::EditAtPlayhead))); + ActionManager::register_radio_action (editor_actions, edit_point_group, X_("edit-at-selected-marker"), _("Marker"), (bind (mem_fun(*this, &Editor::edit_point_chosen), Editing::EditAtPlayhead))); + ActionManager::register_action (editor_actions, X_("SnapTo"), _("Snap To")); ActionManager::register_action (editor_actions, X_("SnapMode"), _("Snap Mode")); @@ -829,6 +836,54 @@ Editor::snap_mode_chosen (SnapMode mode) } } +RefPtr<RadioAction> +Editor::edit_point_action (EditPoint ep) +{ + const char* action = 0; + RefPtr<Action> act; + + switch (ep) { + case Editing::EditAtPlayhead: + action = X_("edit-at-playhead"); + break; + case Editing::EditAtSelectedMarker: + action = X_("edit-at-selected-marker"); + break; + case Editing::EditAtMouse: + action = X_("edit-at-mouse"); + break; + default: + fatal << string_compose (_("programming error: %1: %2"), "Editor: impossible edit point type", (int) ep) << endmsg; + /*NOTREACHED*/ + } + + act = ActionManager::get_action (X_("Editor"), action); + + if (act) { + RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act); + return ract; + + } else { + error << string_compose (_("programming error: %1: %2"), "Editor::edit_point_action could not find action to match edit point.", action) << endmsg; + return RefPtr<RadioAction> (); + } +} + +void +Editor::edit_point_chosen (EditPoint ep) +{ + /* this is driven by a toggle on a radio group, and so is invoked twice, + once for the item that became inactive and once for the one that became + active. + */ + + RefPtr<RadioAction> ract = edit_point_action (ep); + + if (ract && ract->get_active()) { + set_edit_point (ep); + } +} + RefPtr<RadioAction> Editor::zoom_focus_action (ZoomFocus focus) @@ -849,6 +904,9 @@ Editor::zoom_focus_action (ZoomFocus focus) case ZoomFocusPlayhead: action = X_("zoom-focus-playhead"); break; + case ZoomFocusMouse: + action = X_("zoom-focus-mouse"); + break; case ZoomFocusEdit: action = X_("zoom-focus-edit"); break; diff --git a/gtk2_ardour/editor_audio_import.cc b/gtk2_ardour/editor_audio_import.cc index 0dc0d707c0..b609918f09 100644 --- a/gtk2_ardour/editor_audio_import.cc +++ b/gtk2_ardour/editor_audio_import.cc @@ -144,7 +144,7 @@ Editor::external_audio_dialog () switch (pos) { case ImportAtEditCursor: - where = edit_cursor->current_frame; + where = get_preferred_edit_position (); break; case ImportAtTimestamp: where = -1; @@ -157,6 +157,10 @@ Editor::external_audio_dialog () break; } + if (where < 0) { + return; + } + SrcQuality quality = sfbrowser->get_src_quality(); if (sfbrowser->copy_files_btn.get_active()) { @@ -591,7 +595,7 @@ Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64 pos = sources[0]->natural_position(); } else { // XXX is this the best alternative ? - pos = edit_cursor->current_frame; + pos = get_preferred_edit_position (); } } diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc index 394d35e245..a22edea7e0 100644 --- a/gtk2_ardour/editor_canvas_events.cc +++ b/gtk2_ardour/editor_canvas_events.cc @@ -202,7 +202,6 @@ Editor::typed_event (ArdourCanvas::Item* item, GdkEvent *event, ItemType type) default: break; } - return ret; } diff --git a/gtk2_ardour/editor_markers.cc b/gtk2_ardour/editor_markers.cc index 724ff2c31a..50bf950054 100644 --- a/gtk2_ardour/editor_markers.cc +++ b/gtk2_ardour/editor_markers.cc @@ -188,9 +188,9 @@ Editor::LocationMarkers::~LocationMarkers () } Editor::LocationMarkers * -Editor::find_location_markers (Location *location) +Editor::find_location_markers (Location *location) const { - LocationMarkerMap::iterator i; + LocationMarkerMap::const_iterator i; for (i = location_markers.begin(); i != location_markers.end(); ++i) { if ((*i).first == location) { @@ -202,9 +202,9 @@ Editor::find_location_markers (Location *location) } Location * -Editor::find_location_from_marker (Marker *marker, bool& is_start) +Editor::find_location_from_marker (Marker *marker, bool& is_start) const { - LocationMarkerMap::iterator i; + LocationMarkerMap::const_iterator i; for (i = location_markers.begin(); i != location_markers.end(); ++i) { LocationMarkers *lm = (*i).second; diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 8efce4daac..7857082be3 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -76,8 +76,46 @@ using namespace Editing; const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0)); -nframes_t -Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) +bool +Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const +{ + int x, y; + double wx, wy; + Gdk::ModifierType mask; + Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas.get_window(); + Glib::RefPtr<const Gdk::Window> pointer_window; + + pointer_window = canvas_window->get_pointer (x, y, mask); + + if (pointer_window == track_canvas.get_bin_window()) { + + track_canvas.window_to_world (x, y, wx, wy); + in_track_canvas = true; + + } else { + in_track_canvas = false; + + if (pointer_window == time_canvas.get_bin_window()) { + time_canvas.window_to_world (x, y, wx, wy); + } else { + return false; + } + } + + wx += horizontal_adjustment.get_value(); + wy += vertical_adjustment.get_value(); + + GdkEvent event; + event.type = GDK_BUTTON_RELEASE; + event.button.x = wx; + event.button.y = wy; + + where = event_frame (&event, 0, 0); + return true; +} + +nframes64_t +Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const { double cx, cy; @@ -773,10 +811,8 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp case MouseAudition: _scrubbing = true; - last_scrub_frame = 0; - last_scrub_time = 0; - have_full_mouse_speed = false; - memset (mouse_speed, 0, sizeof (double) * mouse_speed_size); + last_scrub_x = event->button.x; + scrubbing_direction = 0; /* rest handled in motion & release */ break; @@ -1110,7 +1146,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT case MouseAudition: _scrubbing = false; - if (last_scrub_frame == 0) { + if (scrubbing_direction == 0) { /* no drag, just a click */ switch (item_type) { case RegionItem: @@ -1467,12 +1503,6 @@ Editor::left_automation_track () return false; } -static gboolean -_update_mouse_speed (void *arg) -{ - return static_cast<Editor*>(arg)->update_mouse_speed (); -} - bool Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll) { @@ -1509,56 +1539,53 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item switch (mouse_mode) { case MouseAudition: if (_scrubbing) { - struct timeval tmnow; - - if (last_scrub_frame == 0) { - /* first motion, just set up the variables */ + double delta; - last_scrub_frame = (nframes64_t) drag_info.current_pointer_frame; - gettimeofday (&tmnow, 0); - last_scrub_time = tmnow.tv_sec * 1000000.0 + tmnow.tv_usec; - session->request_locate (last_scrub_frame, true); + if (scrubbing_direction == 0) { + /* first move */ + session->request_locate (drag_info.current_pointer_frame, false); + session->request_transport_speed (0.1); + scrubbing_direction = 1; } else { - /* how fast is the mouse moving ? */ - - double speed; - nframes_t distance; - double time; - double dir; -#if 1 - if (last_scrub_frame < (nframes64_t) drag_info.current_pointer_frame) { - distance = (nframes64_t) drag_info.current_pointer_frame - last_scrub_frame; - dir = 1.0; - } else { - distance = last_scrub_frame - (nframes64_t) drag_info.current_pointer_frame; - dir = -1.0; - } -#else - if (drag_info.grab_x < drag_info.current_pointer_x) { - distance = drag_info.current_pointer_x - drag_info.grab_x; - dir = -1.0; + + if (last_scrub_x > drag_info.current_pointer_x) { + /* move to the left */ + + if (scrubbing_direction > 0) { + /* we reversed direction to go backwards */ + + session->request_transport_speed (-0.1); + + } else { + /* still moving to the left (backwards) */ + + delta = 0.005 * (last_scrub_x - drag_info.current_pointer_x); + session->request_transport_speed (session->transport_speed() - delta); + } + + scrubbing_direction = -1; + } else { - distance = drag_info.grab_x - drag_info.current_pointer_x; - dir = 1.0; - } -#endif - - gettimeofday (&tmnow, 0); - time = (tmnow.tv_sec * 1000000.0 + tmnow.tv_usec) - last_scrub_time; - last_scrub_frame = drag_info.current_pointer_frame; - last_scrub_time = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec; - speed = ((double)distance/session->frame_rate()) / (time/1000000.0); // frames/sec - - add_mouse_speed (speed, dir); - - if (mouse_speed_update < 0) { - mouse_speed_update = g_timeout_add (10, _update_mouse_speed, this); - update_mouse_speed (); + /* move to the right */ + if (scrubbing_direction < 0) { + /* we reversed direction to go forward */ + + session->request_transport_speed (0.1); + } else { + /* still moving to the right */ + + delta = 0.005 * (drag_info.current_pointer_x - last_scrub_x); + session->request_transport_speed (session->transport_speed() + delta); + } + + scrubbing_direction = 1; } } + + last_scrub_x = drag_info.current_pointer_x; } default: @@ -2607,8 +2634,7 @@ Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event) drag_info.grab_x = control_point->get_x(); drag_info.grab_y = control_point->get_y(); control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y); - track_canvas.w2c(drag_info.grab_x, drag_info.grab_y, - drag_info.grab_x, drag_info.grab_y); + track_canvas.w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y); drag_info.grab_frame = pixel_to_frame(drag_info.grab_x); @@ -3757,12 +3783,13 @@ Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent snap_to (start, -1); const Meter& m = session->tempo_map().meter_at(start); const Tempo& t = session->tempo_map().tempo_at(start); - double length = m.frames_per_bar(t, session->frame_rate()); + double length = floor (m.frames_per_bar(t, session->frame_rate())); boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get()); - mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>(RegionFactory::create( - src, 0, length, PBD::basename_nosuffix(src->name()))), start); + mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion> + (RegionFactory::create(src, 0, (nframes_t) length, + PBD::basename_nosuffix(src->name()))), start); XMLNode &after = mtv->playlist()->get_state(); session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after)); commit_reversible_command(); @@ -3788,17 +3815,22 @@ Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event) speed = rtv->get_diskstream()->speed(); } - if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) { - - align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed)); - - } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) { + nframes64_t where = get_preferred_edit_position(); - align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed)); + if (where >= 0) { - } else { - - align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed)); + if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) { + + align_region (rv.region(), SyncPoint, (nframes_t) (where * speed)); + + } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) { + + align_region (rv.region(), End, (nframes_t) (where * speed)); + + } else { + + align_region (rv.region(), Start, (nframes_t) (where * speed)); + } } } } @@ -5197,90 +5229,3 @@ Editor::track_height_step_timeout () return true; } - -void -Editor::add_mouse_speed (double speed, double dir) -{ - size_t index; - - mouse_direction = dir; - - index = mouse_speed_entries; - - if (++index >= mouse_speed_size) { - index = 0; - have_full_mouse_speed = true; - } - - mouse_speed[index] = speed; - mouse_speed_entries = index; -} - -double -Editor::compute_mouse_speed () -{ - double total = 0; - - if (!have_full_mouse_speed) { - - /* partial speed buffer, just use whatever we have so far */ - - if (mouse_speed_entries == 0 ) { - return 0.0; - } - - for (size_t n = 0; n < mouse_speed_entries; ++n) { - total += mouse_speed[n]; - } - - return mouse_direction * total/mouse_speed_entries; - } - - /* compute the average (effectively low-pass filtering) mouse speed - across the entire buffer. - */ - - for (size_t n = 0; n < mouse_speed_size; ++n) { - total += mouse_speed[n]; - } - - - return mouse_direction * total/mouse_speed_size; -} - -bool -Editor::update_mouse_speed () -{ - double speed; - - if (!_scrubbing) { - session->request_transport_speed (0.0); - mouse_speed_update = -1; - return false; - } - - speed = compute_mouse_speed (); - - struct timeval tmnow; - - gettimeofday (&tmnow, 0); - double now = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec; - - if (now - last_scrub_time > 250000) { - - // 0.25 seconds since last mouse motion, start to brake - - if (fabs (speed) < 0.1) { - /* don't asymptotically approach zero */ - memset (mouse_speed, 0, sizeof (double) * mouse_speed_size); - speed = 0.0; - } else if (fabs (speed) < 0.25) { - add_mouse_speed (fabs (speed * 0.2), mouse_direction); - } else { - add_mouse_speed (fabs (speed * 0.6), mouse_direction); - } - } - - session->request_transport_speed (speed); - return _scrubbing; -} diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 2cc30aee17..7ae7706928 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -95,17 +95,10 @@ Editor::redo (uint32_t n) } } -int -Editor::ensure_cursor (nframes_t *pos) -{ - *pos = edit_cursor->current_frame; - return 0; -} - void Editor::split_region () { - split_region_at (edit_cursor->current_frame); + split_region_at (get_preferred_edit_position()); } void @@ -137,8 +130,21 @@ Editor::split_regions_at (nframes_t where, RegionSelection& regions) snap_to (where); } + for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) { - for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ++a) { + RegionSelection::iterator tmp; + + /* XXX this test needs to be more complicated, to make sure we really + have something to split. + */ + + if (!(*a)->region()->covers (where)) { + ++a; + continue; + } + + tmp = a; + ++tmp; boost::shared_ptr<Playlist> pl = (*a)->region()->playlist(); @@ -161,6 +167,7 @@ Editor::split_regions_at (nframes_t where, RegionSelection& regions) session->add_command(new MementoCommand<Playlist>(*pl, &before, &after)); } + a = tmp; } while (used_playlists.size() > 0) { @@ -883,7 +890,7 @@ Editor::cursor_align (bool playhead_to_edit) { if (playhead_to_edit) { if (session) { - session->request_locate (edit_cursor->current_frame); + session->request_locate (get_preferred_edit_position()); } } else { edit_cursor->set_position (playhead_cursor->current_frame); @@ -893,8 +900,8 @@ Editor::cursor_align (bool playhead_to_edit) void Editor::edit_cursor_backward () { - nframes_t pos; - nframes_t cnt; + nframes64_t pos; + nframes64_t cnt; float prefix; bool was_floating; @@ -908,9 +915,11 @@ Editor::edit_cursor_backward () } } - pos = edit_cursor->current_frame; + if ((pos = get_preferred_edit_position()) < 0) { + return; + } - if ((nframes_t) pos < cnt) { + if (pos < cnt) { pos = 0; } else { pos -= cnt; @@ -1099,12 +1108,14 @@ Editor::temporal_zoom (gdouble fpu) { if (!session) return; - nframes_t current_page = current_page_frames(); - nframes_t current_leftmost = leftmost_frame; - nframes_t current_rightmost; - nframes_t current_center; - nframes_t new_page; - nframes_t leftmost_after_zoom = 0; + nframes64_t current_page = current_page_frames(); + nframes64_t current_leftmost = leftmost_frame; + nframes64_t current_rightmost; + nframes64_t current_center; + nframes64_t new_page; + nframes64_t leftmost_after_zoom = 0; + nframes64_t where; + bool in_track_canvas; double nfpu; nfpu = fpu; @@ -1143,10 +1154,38 @@ Editor::temporal_zoom (gdouble fpu) } break; + case ZoomFocusMouse: + /* try to keep the mouse over the same point in the display */ + + if (!mouse_frame (where, in_track_canvas)) { + /* use playhead instead */ + where = playhead_cursor->current_frame; + + if (where > new_page/2) { + leftmost_after_zoom = where - (new_page/2); + } else { + leftmost_after_zoom = 0; + } + + } else { + + double l = - ((new_page * ((where - current_leftmost)/(double)current_page)) - where); + + if (l < 0) { + leftmost_after_zoom = 0; + } else if (l > max_frames) { + leftmost_after_zoom = max_frames - new_page; + } else { + leftmost_after_zoom = (nframes64_t) l; + } + } + + break; + case ZoomFocusEdit: /* try to keep the edit cursor in the center */ - if (edit_cursor->current_frame > new_page/2) { - leftmost_after_zoom = edit_cursor->current_frame - (new_page/2); + if (get_preferred_edit_position() > new_page/2) { + leftmost_after_zoom = get_preferred_edit_position() - (new_page/2); } else { leftmost_after_zoom = 0; } @@ -1160,6 +1199,8 @@ Editor::temporal_zoom (gdouble fpu) // session->add_undo (bind (mem_fun(*this, &Editor::reposition_and_zoom), current_leftmost, frames_per_unit)); // session->add_redo (bind (mem_fun(*this, &Editor::reposition_and_zoom), leftmost_after_zoom, nfpu)); // commit_reversible_command (); + + // cerr << "repos & zoom to " << leftmost_after_zoom << " @ " << nfpu << endl; reposition_and_zoom (leftmost_after_zoom, nfpu); } @@ -1579,7 +1620,7 @@ Editor::insert_region_list_selection (float times) begin_reversible_command (_("insert region")); XMLNode &before = playlist->get_state(); - playlist->add_region ((RegionFactory::create (region)), edit_cursor->current_frame, times); + playlist->add_region ((RegionFactory::create (region)), get_preferred_edit_position(), times); session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state())); commit_reversible_command (); } @@ -1666,7 +1707,7 @@ Editor::play_from_start () void Editor::play_from_edit_cursor () { - session->request_locate (edit_cursor->current_frame, true); + session->request_locate (get_preferred_edit_position(), true); } void @@ -2275,7 +2316,8 @@ Editor::set_region_sync_from_edit_cursor () { /* Check that at the edit cursor is in at least one of the selected regions */ RegionSelection::const_iterator i = selection->regions.begin(); - while (i != selection->regions.end() && !(*i)->region()->covers (edit_cursor->current_frame)) { + + while (i != selection->regions.end() && !(*i)->region()->covers (get_preferred_edit_position())) { ++i; } @@ -2290,11 +2332,11 @@ Editor::set_region_sync_from_edit_cursor () for (RegionSelection::iterator j = selection->regions.begin(); j != selection->regions.end(); ++j) { boost::shared_ptr<Region> r = (*j)->region(); XMLNode &before = r->playlist()->get_state(); - r->set_sync_position (edit_cursor->current_frame); + r->set_sync_position (get_preferred_edit_position()); XMLNode &after = r->playlist()->get_state(); session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after)); } - + commit_reversible_command (); } @@ -2334,13 +2376,13 @@ Editor::naturalize () void Editor::align (RegionPoint what) { - align_selection (what, edit_cursor->current_frame); + align_selection (what, get_preferred_edit_position()); } void Editor::align_relative (RegionPoint what) { - align_selection_relative (what, edit_cursor->current_frame); + align_selection_relative (what, get_preferred_edit_position()); } struct RegionSortByTime { @@ -2481,7 +2523,7 @@ Editor::trim_region_to_edit_cursor () } XMLNode &before = region->playlist()->get_state(); - region->trim_end( session_frame_to_track_frame(edit_cursor->current_frame, speed), this); + region->trim_end( session_frame_to_track_frame(get_preferred_edit_position(), speed), this); XMLNode &after = region->playlist()->get_state(); session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after)); } @@ -2513,12 +2555,10 @@ Editor::trim_region_from_edit_cursor () } XMLNode &before = region->playlist()->get_state(); - region->trim_front ( session_frame_to_track_frame(edit_cursor->current_frame, speed), this); + region->trim_front ( session_frame_to_track_frame(get_preferred_edit_position(), speed), this); XMLNode &after = region->playlist()->get_state(); session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after)); } - - commit_reversible_command (); } /** Unfreeze selected routes */ @@ -2945,26 +2985,19 @@ Editor::cut_copy_ranges (CutCopyOp op) void Editor::paste (float times) { - paste_internal (edit_cursor->current_frame, times); + paste_internal (get_preferred_edit_position(), times); } void Editor::mouse_paste () { - int x, y; - double wx, wy; + nframes64_t where; + bool ignored; - track_canvas.get_pointer (x, y); - track_canvas.window_to_world (x, y, wx, wy); - wx += horizontal_adjustment.get_value(); - wy += vertical_adjustment.get_value(); + if (!mouse_frame (where, ignored)) { + return; + } - GdkEvent event; - event.type = GDK_BUTTON_RELEASE; - event.button.x = wx; - event.button.y = wy; - - nframes_t where = event_frame (&event, 0, 0); snap_to (where); paste_internal (where, 1); } @@ -2979,7 +3012,7 @@ Editor::paste_internal (nframes_t position, float times) } if (position == max_frames) { - position = edit_cursor->current_frame; + position = get_preferred_edit_position(); } begin_reversible_command (_("paste")); @@ -3050,7 +3083,7 @@ Editor::paste_named_selection (float times) ++tmp; XMLNode &before = apl->get_state(); - apl->paste (*chunk, edit_cursor->current_frame, times); + apl->paste (*chunk, get_preferred_edit_position(), times); session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state())); if (tmp != ns->playlists.end()) { @@ -3162,7 +3195,7 @@ Editor::center_edit_cursor () { float page = canvas_width * frames_per_unit; - center_screen_internal (edit_cursor->current_frame, page); + center_screen_internal (get_preferred_edit_position(), page); } void @@ -3185,7 +3218,7 @@ Editor::nudge_selected_tracks (bool use_edit_cursor, bool forwards) nframes_t start; if (use_edit_cursor) { - start = edit_cursor->current_frame; + start = get_preferred_edit_position(); } else { start = 0; } diff --git a/gtk2_ardour/editor_selection.cc b/gtk2_ardour/editor_selection.cc index 910534690e..12ca558ea4 100644 --- a/gtk2_ardour/editor_selection.cc +++ b/gtk2_ardour/editor_selection.cc @@ -616,16 +616,10 @@ Editor::track_selection_changed () } for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->set_selected (false); - if (mouse_mode == MouseRange) { - (*i)->hide_selection (); - } - } - - for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { - (*i)->set_selected (true); - if (mouse_mode == MouseRange) { - (*i)->show_selection (selection->time); + if (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end()) { + (*i)->set_selected (true); + } else { + (*i)->set_selected (false); } } } @@ -652,6 +646,7 @@ Editor::time_selection_changed () } else { ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true); } + } void diff --git a/gtk2_ardour/editor_timefx.cc b/gtk2_ardour/editor_timefx.cc index 2efdc03f8f..df0a73c965 100644 --- a/gtk2_ardour/editor_timefx.cc +++ b/gtk2_ardour/editor_timefx.cc @@ -39,6 +39,7 @@ #include <ardour/audio_track.h> #include <ardour/audioregion.h> #include <ardour/audio_diskstream.h> +#include <ardour/stretch.h> #include "i18n.h" @@ -87,29 +88,29 @@ gint Editor::TimeStretchDialog::update_progress () { progress_bar.set_fraction (request.progress); - return request.running; + return !request.done; } void Editor::TimeStretchDialog::cancel_timestretch_in_progress () { status = -2; - request.running = false; + request.cancel = true; + first_cancel.disconnect(); } gint Editor::TimeStretchDialog::delete_timestretch_in_progress (GdkEventAny* ev) { status = -2; - request.running = false; + request.cancel = true; + first_delete.disconnect(); return TRUE; } int Editor::run_timestretch (RegionSelection& regions, float fraction) { - pthread_t thread; - if (current_timestretch == 0) { current_timestretch = new TimeStretchDialog (*this); } @@ -130,27 +131,30 @@ Editor::run_timestretch (RegionSelection& regions, float fraction) current_timestretch->request.quick_seek = current_timestretch->quick_button.get_active(); current_timestretch->request.antialias = !current_timestretch->antialias_button.get_active(); current_timestretch->request.progress = 0.0f; - current_timestretch->request.running = true; + current_timestretch->request.done = false; + current_timestretch->request.cancel = false; /* re-connect the cancel button and delete events */ current_timestretch->first_cancel.disconnect(); current_timestretch->first_delete.disconnect(); - current_timestretch->cancel_button->signal_clicked().connect (mem_fun (current_timestretch, &TimeStretchDialog::cancel_timestretch_in_progress)); - current_timestretch->signal_delete_event().connect (mem_fun (current_timestretch, &TimeStretchDialog::delete_timestretch_in_progress)); + current_timestretch->first_cancel = current_timestretch->cancel_button->signal_clicked().connect + (mem_fun (current_timestretch, &TimeStretchDialog::cancel_timestretch_in_progress)); + current_timestretch->first_delete = current_timestretch->signal_delete_event().connect + (mem_fun (current_timestretch, &TimeStretchDialog::delete_timestretch_in_progress)); - if (pthread_create_and_store ("timestretch", &thread, 0, timestretch_thread, current_timestretch)) { + if (pthread_create_and_store ("timestretch", ¤t_timestretch->request.thread, 0, timestretch_thread, current_timestretch)) { current_timestretch->hide (); error << _("timestretch cannot be started - thread creation error") << endmsg; return -1; } - pthread_detach (thread); + pthread_detach (current_timestretch->request.thread); sigc::connection c = Glib::signal_timeout().connect (mem_fun (current_timestretch, &TimeStretchDialog::update_progress), 100); - while (current_timestretch->request.running) { + while (!current_timestretch->request.done) { gtk_main_iteration (); } @@ -195,31 +199,34 @@ Editor::do_timestretch (TimeStretchDialog& dialog) continue; } - dialog.request.region = region; - - if (!dialog.request.running) { + if (dialog.request.cancel) { /* we were cancelled */ dialog.status = 1; return; } - if ((new_region = session->tempoize_region (dialog.request)) == 0) { + Stretch stretch (*session, dialog.request); + + if (stretch.run (region)) { dialog.status = -1; - dialog.request.running = false; + dialog.request.done = true; return; } - XMLNode &before = playlist->get_state(); - playlist->replace_region (region, new_region, region->position()); - XMLNode &after = playlist->get_state(); - session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after)); + if (!stretch.results.empty()) { + new_region = stretch.results.front(); + + XMLNode &before = playlist->get_state(); + playlist->replace_region (region, new_region, region->position()); + XMLNode &after = playlist->get_state(); + session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after)); + } i = tmp; } dialog.status = 0; - dialog.request.running = false; - dialog.request.region.reset (); + dialog.request.done = true; } void* diff --git a/gtk2_ardour/enums.cc b/gtk2_ardour/enums.cc index c33b2965c3..f626bfb968 100644 --- a/gtk2_ardour/enums.cc +++ b/gtk2_ardour/enums.cc @@ -38,6 +38,7 @@ setup_gtk_ardour_enums () AudioClock::Mode clock_mode; Width width; ImportMode import_mode; + EditPoint edit_point; #define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear() #define REGISTER_BITS(e) enum_writer.register_bits (typeid(e).name(), i, s); i.clear(); s.clear() @@ -60,4 +61,9 @@ setup_gtk_ardour_enums () REGISTER_ENUM (ImportAsRegion); REGISTER_ENUM (ImportAsTapeTrack); REGISTER (import_mode); + + REGISTER_ENUM (EditAtPlayhead); + REGISTER_ENUM (EditAtMouse); + REGISTER_ENUM (EditAtSelectedMarker); + REGISTER (edit_point); } diff --git a/gtk2_ardour/marker_selection.h b/gtk2_ardour/marker_selection.h new file mode 100644 index 0000000000..aa1413ed3e --- /dev/null +++ b/gtk2_ardour/marker_selection.h @@ -0,0 +1,31 @@ +/* + Copyright (C) 2000-2007 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. + +*/ + +#ifndef __ardour_gtk_marker_selection_h__ +#define __ardour_gtk_marker_selection_h__ + +#include <list> + +#include "marker.h" + +struct MarkerSelection : public std::list<Marker*> +{ +}; + +#endif /* __ardour_gtk_marker_selection_h__ */ diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h index 03d5897c1a..f32c0565e3 100644 --- a/gtk2_ardour/public_editor.h +++ b/gtk2_ardour/public_editor.h @@ -185,11 +185,11 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulThingWithGoingAway virtual void toggle_playback (bool with_abort) = 0; virtual void transition_to_rolling (bool fwd) = 0; - virtual nframes_t unit_to_frame (double unit) = 0; - virtual double frame_to_unit (nframes_t frame) = 0; - virtual double frame_to_unit (double frame) = 0; - virtual nframes_t pixel_to_frame (double pixel) = 0; - virtual gulong frame_to_pixel (nframes_t frame) = 0; + virtual nframes_t unit_to_frame (double unit) const = 0; + virtual double frame_to_unit (nframes_t frame) const = 0; + virtual double frame_to_unit (double frame) const = 0; + virtual nframes64_t pixel_to_frame (double pixel) const = 0; + virtual gulong frame_to_pixel (nframes64_t frame) const = 0; virtual Selection& get_selection () const = 0; virtual Selection& get_cut_buffer () const = 0; virtual bool extend_selection_to_track (TimeAxisView&) = 0; diff --git a/gtk2_ardour/selection.cc b/gtk2_ardour/selection.cc index f0037588a0..c6bb5c37af 100644 --- a/gtk2_ardour/selection.cc +++ b/gtk2_ardour/selection.cc @@ -144,6 +144,15 @@ Selection::clear_lines () } void +Selection::clear_markers () +{ + if (!markers.empty()) { + markers.clear (); + MarkersChanged(); + } +} + +void Selection::toggle (boost::shared_ptr<Playlist> pl) { PlaylistSelection::iterator i; @@ -575,7 +584,8 @@ Selection::empty () playlists.empty () && lines.empty () && time.empty () && - playlists.empty () + playlists.empty () && + markers.empty() ; } @@ -700,3 +710,43 @@ Selection::select_edit_group_regions () add (*i); } } + +void +Selection::set (Marker* m) +{ + clear_markers (); + add (m); +} + +void +Selection::toggle (Marker* m) +{ + MarkerSelection::iterator i; + + if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) { + add (m); + } else { + remove (m); + } +} + +void +Selection::remove (Marker* m) +{ + MarkerSelection::iterator i; + + if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) { + markers.erase (i); + MarkersChanged(); + } +} + + +void +Selection::add (Marker* m) +{ + if (find (markers.begin(), markers.end(), m) == markers.end()) { + markers.push_back (m); + MarkersChanged(); + } +} diff --git a/gtk2_ardour/selection.h b/gtk2_ardour/selection.h index 5e9bc04759..2b2db77360 100644 --- a/gtk2_ardour/selection.h +++ b/gtk2_ardour/selection.h @@ -32,6 +32,7 @@ #include "playlist_selection.h" #include "processor_selection.h" #include "point_selection.h" +#include "marker_selection.h" class TimeAxisView; class RegionView; @@ -71,6 +72,7 @@ class Selection : public sigc::trackable AutomationSelection lines; PlaylistSelection playlists; PointSelection points; + MarkerSelection markers; Selection (PublicEditor const * e) : editor (e), next_time_id (0) { clear(); @@ -84,6 +86,7 @@ class Selection : public sigc::trackable sigc::signal<void> LinesChanged; sigc::signal<void> PlaylistsChanged; sigc::signal<void> PointsChanged; + sigc::signal<void> MarkersChanged; void clear (); bool empty(); @@ -106,6 +109,7 @@ class Selection : public sigc::trackable void set (boost::shared_ptr<ARDOUR::Playlist>); void set (const std::list<boost::shared_ptr<ARDOUR::Playlist> >&); void set (AutomationSelectable*); + void set (Marker*); void toggle (TimeAxisView*); void toggle (const std::list<TimeAxisView*>&); @@ -116,6 +120,7 @@ class Selection : public sigc::trackable void toggle (boost::shared_ptr<ARDOUR::Playlist>); void toggle (const std::list<boost::shared_ptr<ARDOUR::Playlist> >&); void toggle (const std::vector<AutomationSelectable*>&); + void toggle (Marker*); void add (TimeAxisView*); void add (const std::list<TimeAxisView*>&); @@ -125,7 +130,8 @@ class Selection : public sigc::trackable void add (ARDOUR::AutomationList*); void add (boost::shared_ptr<ARDOUR::Playlist>); void add (const std::list<boost::shared_ptr<ARDOUR::Playlist> >&); - + void add (Marker*); + void remove (TimeAxisView*); void remove (const std::list<TimeAxisView*>&); void remove (RegionView*); @@ -135,6 +141,7 @@ class Selection : public sigc::trackable void remove (boost::shared_ptr<ARDOUR::Playlist>); void remove (const std::list<boost::shared_ptr<ARDOUR::Playlist> >&); void remove (const list<Selectable*>&); + void remove (Marker*); void replace (uint32_t time_index, nframes_t start, nframes_t end); @@ -144,6 +151,7 @@ class Selection : public sigc::trackable void clear_lines (); void clear_playlists (); void clear_points (); + void clear_markers (); void foreach_region (void (ARDOUR::Region::*method)(void)); void foreach_regionview (void (RegionView::*method)(void)); diff --git a/gtk2_ardour/time_axis_view.cc b/gtk2_ardour/time_axis_view.cc index 4d2533ebff..cf0e39cdbd 100644 --- a/gtk2_ardour/time_axis_view.cc +++ b/gtk2_ardour/time_axis_view.cc @@ -545,12 +545,16 @@ TimeAxisView::popup_size_menu (guint32 when) void TimeAxisView::set_selected (bool yn) { - AxisView::set_selected (yn); + if (yn == _selected) { + return; + } + + Selectable::set_selected (yn); if (_selected) { controls_ebox.set_name (controls_base_selected_name); controls_frame.set_name (controls_base_selected_name); - + /* propagate any existing selection, if the mode is right */ if (editor.current_mouse_mode() == Editing::MouseRange && !editor.get_selection().time.empty()) { @@ -571,8 +575,6 @@ TimeAxisView::set_selected (bool yn) for (Children::iterator i = children.begin(); i != children.end(); ++i) { (*i)->set_selected (false); } - - } } diff --git a/gtk2_ardour/utils.cc b/gtk2_ardour/utils.cc index 570a481c11..941132396a 100644 --- a/gtk2_ardour/utils.cc +++ b/gtk2_ardour/utils.cc @@ -22,6 +22,7 @@ #include <fstream> #include <sys/stat.h> #include <libart_lgpl/art_misc.h> +#include <gtkmm/rc.h> #include <gtkmm/window.h> #include <gtkmm/combo.h> #include <gtkmm/label.h> @@ -325,6 +326,61 @@ rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, s } } + +Gdk::Color +color_from_style (string widget_style_name, int state, string attr) +{ + GtkStyle* style; + + style = gtk_rc_get_style_by_paths (gtk_settings_get_default(), + widget_style_name.c_str(), + 0, G_TYPE_NONE); + + if (!style) { + error << string_compose (_("no style found for %1, using red"), style) << endmsg; + return Gdk::Color ("red"); + } + + cerr << "got style for " << widget_style_name << endl; + + if (attr == "fg") { + return Gdk::Color (&style->fg[state]); + } + + if (attr == "bg") { + cerr << "returning color from bg\n"; + return Gdk::Color (&style->bg[state]); + } + + if (attr == "light") { + return Gdk::Color (&style->light[state]); + } + + if (attr == "dark") { + return Gdk::Color (&style->dark[state]); + } + + if (attr == "mid") { + return Gdk::Color (&style->mid[state]); + } + + if (attr == "text") { + return Gdk::Color (&style->text[state]); + } + + if (attr == "base") { + return Gdk::Color (&style->base[state]); + } + + if (attr == "text_aa") { + return Gdk::Color (&style->text_aa[state]); + } + + error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg; + return Gdk::Color ("red"); +} + + bool canvas_item_visible (ArdourCanvas::Item* item) { diff --git a/gtk2_ardour/utils.h b/gtk2_ardour/utils.h index cae78f3d0a..74a6eebd21 100644 --- a/gtk2_ardour/utils.h +++ b/gtk2_ardour/utils.h @@ -67,6 +67,8 @@ Pango::FontDescription* get_font_for_style (std::string widgetname); uint32_t rgba_from_style (std::string, uint32_t, uint32_t, uint32_t, uint32_t, std::string = "fg", int = Gtk::STATE_NORMAL, bool = true); +Gdk::Color color_from_style (std::string widget_style_name, int state, std::string attr); + void decorate (Gtk::Window& w, Gdk::WMDecoration d); bool canvas_item_visible (ArdourCanvas::Item* item); diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index f40752167d..2d030ac70a 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -121,7 +121,6 @@ session_process.cc session_state.cc session_state_utils.cc session_time.cc -session_timefx.cc session_transport.cc session_utils.cc silentfilesource.cc @@ -130,6 +129,7 @@ sndfile_helpers.cc sndfilesource.cc source.cc source_factory.cc +stretch.cc tape_file_matcher.cc template_utils.cc tempo.cc diff --git a/libs/ardour/ardour/filter.h b/libs/ardour/ardour/filter.h index 0e4c6b3de0..b659873bdb 100644 --- a/libs/ardour/ardour/filter.h +++ b/libs/ardour/ardour/filter.h @@ -40,8 +40,8 @@ class Filter { protected: Filter (ARDOUR::Session& s) : session(s) {} - int make_new_sources (boost::shared_ptr<ARDOUR::Region>, ARDOUR::SourceList&); - int finish (boost::shared_ptr<ARDOUR::Region>, ARDOUR::SourceList&); + int make_new_sources (boost::shared_ptr<ARDOUR::Region>, ARDOUR::SourceList&, std::string suffix = ""); + int finish (boost::shared_ptr<ARDOUR::Region>, ARDOUR::SourceList&, std::string region_name = ""); ARDOUR::Session& session; }; diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index e62b59ca61..c246da9cce 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -95,7 +95,13 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region> nframes_t start () const { return _start; } nframes_t length() const { return _length; } layer_t layer () const { return _layer; } - + + nframes64_t ancestral_start () const { return _ancestral_start; } + nframes64_t ancestral_length () const { return _ancestral_length; } + float stretch() const { return _stretch; } + + void set_ancestral_data (nframes64_t start, nframes64_t length, float stretch); + nframes_t sync_offset(int& dir) const; nframes_t sync_position() const; @@ -177,11 +183,12 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region> boost::shared_ptr<Source> source (uint32_t n=0) const { return _sources[ (n < _sources.size()) ? n : 0 ]; } uint32_t n_channels() const { return _sources.size(); } - std::vector<string> master_source_names(); - const SourceList& sources() const { return _sources; } const SourceList& master_sources() const { return _master_sources; } + std::vector<string> master_source_names(); + void set_master_sources (SourceList&); + /* serialization */ XMLNode& get_state (); @@ -241,6 +248,9 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region> layer_t _layer; mutable RegionEditState _first_edit; int _frozen; + nframes64_t _ancestral_start; + nframes64_t _ancestral_length; + float _stretch; mutable uint32_t _read_data_count; ///< modified in read() Change _pending_changed; uint64_t _last_layer_op; ///< timestamp diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index e153914a09..579521fd2c 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -860,21 +860,6 @@ class Session : public PBD::StatefulDestructible boost::shared_ptr<IO> click_io() { return _click_io; } - /* tempo FX */ - - struct TimeStretchRequest { - boost::shared_ptr<ARDOUR::AudioRegion> region; - float fraction; /* session: read ; GUI: write */ - float progress; /* session: write ; GUI: read */ - bool running; /* read/write */ - bool quick_seek; /* GUI: write */ - bool antialias; /* GUI: write */ - - TimeStretchRequest () {} - }; - - boost::shared_ptr<AudioRegion> tempoize_region (TimeStretchRequest&); - /* disk, buffer loads */ uint32_t playback_load (); diff --git a/libs/ardour/ardour/stretch.h b/libs/ardour/ardour/stretch.h new file mode 100644 index 0000000000..4d00c9b17b --- /dev/null +++ b/libs/ardour/ardour/stretch.h @@ -0,0 +1,51 @@ +/* + Copyright (C) 2007 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. + +*/ + +#ifndef __ardour_stretch_h__ +#define __ardour_stretch_h__ + +#include <ardour/filter.h> +#include <soundtouch/SoundTouch.h> + +namespace ARDOUR { + +class AudioRegion; + +struct TimeStretchRequest : public InterThreadInfo { + float fraction; + bool quick_seek; + bool antialias; +}; + +class Stretch : public Filter { + public: + Stretch (ARDOUR::Session&, TimeStretchRequest&); + ~Stretch (); + + int run (boost::shared_ptr<ARDOUR::Region>); + + private: + TimeStretchRequest& tsr; + soundtouch::SoundTouch st; + +}; + +} /* namespace */ + +#endif /* __ardour_stretch_h__ */ diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index c6f4d3c480..91654643d9 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -32,6 +32,7 @@ #include <pbd/xml++.h> #include <pbd/stacktrace.h> #include <pbd/enumwriter.h> +#include <pbd/convert.h> #include <ardour/audioregion.h> #include <ardour/session.h> @@ -47,6 +48,7 @@ using namespace std; using namespace ARDOUR; +using namespace PBD; /* a Session will reset these to its chosen defaults by calling AudioRegion::set_default_fade() */ @@ -127,6 +129,23 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0)) , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0)) { + set<boost::shared_ptr<Source> > unique_srcs; + + for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) { + _sources.push_back (*i); + + pair<set<boost::shared_ptr<Source> >::iterator,bool> result; + + result = unique_srcs.insert (*i); + + if (result.second) { + boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*i); + if (afs) { + afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed)); + } + } + } + /* return to default fades if the existing ones are too long */ init (); @@ -494,12 +513,20 @@ AudioRegion::state (bool full) snprintf (buf, sizeof(buf), "%.12g", _scale_amplitude); node.add_property ("scale-gain", buf); + // XXX these should move into Region + for (uint32_t n=0; n < _sources.size(); ++n) { snprintf (buf2, sizeof(buf2), "source-%d", n); _sources[n]->id().print (buf, sizeof (buf)); node.add_property (buf2, buf); } + for (uint32_t n=0; n < _master_sources.size(); ++n) { + snprintf (buf2, sizeof(buf2), "master-source-%d", n); + _master_sources[n]->id().print (buf, sizeof (buf)); + node.add_property (buf2, buf); + } + snprintf (buf, sizeof (buf), "%u", (uint32_t) _sources.size()); node.add_property ("channels", buf); @@ -973,6 +1000,7 @@ AudioRegion::read_raw_internal (Sample* buf, nframes_t pos, nframes_t cnt) const return audio_source()->read (buf, pos, cnt); } + int AudioRegion::exportme (Session& session, AudioExportSpecification& spec) { diff --git a/libs/ardour/filter.cc b/libs/ardour/filter.cc index 208377cc4a..dc8185db95 100644 --- a/libs/ardour/filter.cc +++ b/libs/ardour/filter.cc @@ -35,12 +35,25 @@ using namespace ARDOUR; using namespace PBD; int -Filter::make_new_sources (boost::shared_ptr<Region> region, SourceList& nsrcs) +Filter::make_new_sources (boost::shared_ptr<Region> region, SourceList& nsrcs, string suffix) { vector<string> names = region->master_source_names(); for (uint32_t i = 0; i < region->n_channels(); ++i) { + string name = PBD::basename_nosuffix (names[i]); + + /* remove any existing version of suffix by assuming it starts + with some kind of "special" character. + */ + + if (!suffix.empty()) { + string::size_type pos = name.find (suffix[0]); + if (pos != string::npos && pos > 2) { + name = name.substr (0, pos - 1); + } + } + string path = session.path_from_region_name (region->data_type(), PBD::basename_nosuffix (names[i]), string ("")); @@ -65,10 +78,8 @@ Filter::make_new_sources (boost::shared_ptr<Region> region, SourceList& nsrcs) } int -Filter::finish (boost::shared_ptr<Region> region, SourceList& nsrcs) +Filter::finish (boost::shared_ptr<Region> region, SourceList& nsrcs, string region_name) { - string region_name; - /* update headers on new sources */ time_t xnow; @@ -94,7 +105,9 @@ Filter::finish (boost::shared_ptr<Region> region, SourceList& nsrcs) /* create a new region */ - region_name = session.new_region_name (region->name()); + if (region_name.empty()) { + region_name = session.new_region_name (region->name()); + } results.clear (); results.push_back (RegionFactory::create (nsrcs, 0, region->length(), region_name, 0, Region::Flag (Region::WholeFile|Region::DefaultFlags))); diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc index dc115cd55a..cc1ba4b2a8 100644 --- a/libs/ardour/midi_region.cc +++ b/libs/ardour/midi_region.cc @@ -189,6 +189,8 @@ MidiRegion::state (bool full) LocaleGuard lg (X_("POSIX")); node.add_property ("flags", enum_2_string (_flags)); + + // XXX these should move into Region for (uint32_t n=0; n < _sources.size(); ++n) { snprintf (buf2, sizeof(buf2), "source-%d", n); @@ -196,6 +198,12 @@ MidiRegion::state (bool full) node.add_property (buf2, buf); } + for (uint32_t n=0; n < _master_sources.size(); ++n) { + snprintf (buf2, sizeof(buf2), "master-source-%d", n); + _master_sources[n]->id().print (buf, sizeof (buf)); + node.add_property (buf2, buf); + } + if (full && _extra_xml) { node.add_child_copy (*_extra_xml); } diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 1809af671c..ecc7b5e305 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -83,6 +83,9 @@ Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length , _layer(layer) , _first_edit(EditChangesNothing) , _frozen(0) + , _ancestral_start (start) + , _ancestral_length (length) + , _stretch (1.0) , _read_data_count(0) , _pending_changed(Change (0)) , _last_layer_op(0) @@ -142,6 +145,9 @@ Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes , _layer(layer) , _first_edit(EditChangesNothing) , _frozen(0) + , _ancestral_start (other->_ancestral_start + offset) + , _ancestral_length (length) + , _stretch (1.0) , _read_data_count(0) , _pending_changed(Change (0)) , _last_layer_op(0) @@ -183,6 +189,9 @@ Region::Region (boost::shared_ptr<const Region> other) , _layer(other->_layer) , _first_edit(EditChangesID) , _frozen(0) + , _ancestral_start (_start) + , _ancestral_length (_length) + , _stretch (1.0) , _read_data_count(0) , _pending_changed(Change(0)) , _last_layer_op(other->_last_layer_op) @@ -518,6 +527,14 @@ Region::nudge_position (long n, void *src) } void +Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st) +{ + _ancestral_length = l; + _ancestral_start = s; + _stretch = st; +} + +void Region::set_start (nframes_t pos, void *src) { if (_flags & (Locked|PositionLocked)) { @@ -922,6 +939,12 @@ Region::state (bool full_state) node->add_property ("length", buf); snprintf (buf, sizeof (buf), "%u", _position); node->add_property ("position", buf); + snprintf (buf, sizeof (buf), "%lu", _ancestral_start); + node->add_property ("ancestral-start", buf); + snprintf (buf, sizeof (buf), "%lu", _ancestral_length); + node->add_property ("ancestral-length", buf); + snprintf (buf, sizeof (buf), "%.12g", _stretch); + node->add_property ("stretch", buf); switch (_first_edit) { case EditChangesNothing: @@ -1033,6 +1056,26 @@ Region::set_live_state (const XMLNode& node, Change& what_changed, bool send) /* XXX FIRST EDIT !!! */ + /* these 3 properties never change as a result of any editing */ + + if ((prop = node.property ("ancestral-start")) != 0) { + _ancestral_start = atoi (prop->value()); + } else { + _ancestral_start = _start; + } + + if ((prop = node.property ("ancestral-length")) != 0) { + _ancestral_length = atoi (prop->value()); + } else { + _ancestral_length = _length; + } + + if ((prop = node.property ("stretch")) != 0) { + _stretch = atof (prop->value()); + } else { + _stretch = 1.0; + } + /* note: derived classes set flags */ if (_extra_xml) { @@ -1185,6 +1228,12 @@ Region::master_source_names () return names; } +void +Region::set_master_sources (SourceList& srcs) +{ + _master_sources = srcs; +} + bool Region::source_equivalent (boost::shared_ptr<const Region> other) const { diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index e155800d23..a664ca44c7 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -2113,8 +2113,13 @@ Session::remove_route (shared_ptr<Route> route) /* try to cause everyone to drop their references */ + cerr << "pre drop, Route now has " << route.use_count() << " refs\n"; + cerr << "sig has " << route->GoingAway.size() << endl; + route->drop_references (); + cerr << "route dangling refs = " << route.use_count() << endl; + /* save the new state of the world */ if (save_state (_current_snapshot_name)) { diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index c711b227e7..052931f8da 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -1373,6 +1373,7 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool full) boost::shared_ptr<Source> source; boost::shared_ptr<AudioSource> as; SourceList sources; + SourceList master_sources; uint32_t nchans = 1; char buf[128]; @@ -1432,7 +1433,27 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool full) sources.push_back (as); } } - + + for (uint32_t n=1; n < nchans; ++n) { + snprintf (buf, sizeof(buf), X_("master-source-%d"), n); + if ((prop = node.property (buf)) != 0) { + + PBD::ID id2 (prop->value()); + + if ((source = source_by_id (id2)) == 0) { + error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), id2) << endmsg; + return boost::shared_ptr<AudioRegion>(); + } + + as = boost::dynamic_pointer_cast<AudioSource>(source); + if (!as) { + error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), id2) << endmsg; + return boost::shared_ptr<AudioRegion>(); + } + master_sources.push_back (as); + } + } + try { boost::shared_ptr<AudioRegion> region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (sources, node))); @@ -1447,6 +1468,14 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool full) } } + if (!master_sources.empty()) { + if (master_sources.size() == nchans) { + error << _("Session: XMLNode describing an AudioRegion is missing some master sources; ignored") << endmsg; + } else { + region->set_master_sources (master_sources); + } + } + return region; } diff --git a/libs/ardour/session_timefx.cc b/libs/ardour/session_timefx.cc deleted file mode 100644 index 115d3eeeec..0000000000 --- a/libs/ardour/session_timefx.cc +++ /dev/null @@ -1,236 +0,0 @@ -/* - Copyright (C) 2003 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 <cerrno> -#include <stdexcept> - -#include <pbd/basename.h> - -#include <soundtouch/SoundTouch.h> - -#include <ardour/session.h> -#include <ardour/audioregion.h> -#include <ardour/sndfilesource.h> -#include <ardour/region_factory.h> -#include <ardour/source_factory.h> - -#include "i18n.h" - -using namespace std; -using namespace ARDOUR; -using namespace PBD; -using namespace soundtouch; - -boost::shared_ptr<AudioRegion> -Session::tempoize_region (TimeStretchRequest& tsr) -{ - SourceList sources; - SourceList::iterator it; - boost::shared_ptr<AudioRegion> r; - SoundTouch st; - string region_name; - string ident = X_("-TIMEFX-"); - float percentage; - nframes_t total_frames; - nframes_t done; - int c; - char buf[64]; - string::size_type len; - - /* the soundtouch code wants a *tempo* change percentage, which is - of opposite sign to the length change. - */ - - percentage = -tsr.fraction; - - st.setSampleRate (frame_rate()); - st.setChannels (1); - st.setTempoChange (percentage); - st.setPitchSemiTones (0); - st.setRateChange (0); - - st.setSetting(SETTING_USE_QUICKSEEK, tsr.quick_seek); - st.setSetting(SETTING_USE_AA_FILTER, tsr.antialias); - - vector<string> names = tsr.region->master_source_names(); - - tsr.progress = 0.0f; - total_frames = tsr.region->length() * tsr.region->n_channels(); - done = 0; - - for (uint32_t i = 0; i < tsr.region->n_channels(); ++i) { - - string rstr; - string::size_type existing_ident; - - if ((existing_ident = names[i].find (ident)) != string::npos) { - rstr = names[i].substr (0, existing_ident); - } else { - rstr = names[i]; - } - - string path = path_from_region_name (DataType::AUDIO, PBD::basename_nosuffix (rstr), ident); - - if (path.length() == 0) { - error << string_compose (_("tempoize: error creating name for new audio file based on %1"), tsr.region->name()) - << endmsg; - goto out; - } - - try { - sources.push_back (boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createWritable (DataType::AUDIO, *this, path, false, frame_rate()))); - - } catch (failed_constructor& err) { - error << string_compose (_("tempoize: error creating new audio file %1 (%2)"), path, strerror (errno)) << endmsg; - goto out; - } - - } - - try { - const nframes_t bufsize = 16384; - - for (uint32_t i = 0; i < sources.size(); ++i) { - gain_t gain_buffer[bufsize]; - Sample buffer[bufsize]; - nframes_t pos = 0; - nframes_t this_read = 0; - - boost::shared_ptr<AudioSource> asrc = boost::dynamic_pointer_cast<AudioSource>(sources[i]); - if (!asrc) { - cerr << "FIXME: TimeFX for non-audio" << endl; - continue; - } - - st.clear(); - while (tsr.running && pos < tsr.region->length()) { - nframes_t this_time; - - this_time = min (bufsize, tsr.region->length() - pos); - - /* read from the master (original) sources for the region, - not the ones currently in use, in case it's already been - subject to timefx. */ - - if ((this_read = tsr.region->master_read_at (buffer, buffer, gain_buffer, pos + tsr.region->position(), this_time)) != this_time) { - error << string_compose (_("tempoize: error reading data from %1"), sources[i]->name()) << endmsg; - goto out; - } - - pos += this_read; - done += this_read; - - tsr.progress = (float) done / total_frames; - - st.putSamples (buffer, this_read); - - while ((this_read = st.receiveSamples (buffer, bufsize)) > 0 && tsr.running) { - if (asrc->write (buffer, this_read) != this_read) { - error << string_compose (_("error writing tempo-adjusted data to %1"), sources[i]->name()) << endmsg; - goto out; - } - } - } - - if (tsr.running) { - st.flush (); - } - - while (tsr.running && (this_read = st.receiveSamples (buffer, bufsize)) > 0) { - if (asrc->write (buffer, this_read) != this_read) { - error << string_compose (_("error writing tempo-adjusted data to %1"), sources[i]->name()) << endmsg; - goto out; - } - } - } - } catch (runtime_error& err) { - error << _("timefx code failure. please notify ardour-developers.") << endmsg; - error << err.what() << endmsg; - goto out; - } - - time_t now; - struct tm* xnow; - time (&now); - xnow = localtime (&now); - - for (it = sources.begin(); it != sources.end(); ++it) { - boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*it); - if (afs) { - afs->update_header (tsr.region->position(), *xnow, now); - } - } - - len = tsr.region->name().length(); - - while (--len) { - if (!isdigit (tsr.region->name()[len])) { - break; - } - } - - if (len == 0) { - - region_name = tsr.region->name() + ".t000"; - - } else { - - if (tsr.region->name()[len] == 't') { - c = atoi (tsr.region->name().substr(len+1).c_str()); - - snprintf (buf, sizeof (buf), "t%03d", ++c); - region_name = tsr.region->name().substr (0, len) + buf; - - } else { - - /* not sure what this is, just tack the suffix on to it */ - - region_name = tsr.region->name() + ".t000"; - } - - } - - r = (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (sources, 0, sources.front()->length(), region_name, - 0, AudioRegion::Flag (AudioRegion::DefaultFlags | AudioRegion::WholeFile)))); - - out: - - if (sources.size()) { - - /* if we failed to complete for any reason, mark the new file - for deletion. - */ - - if ((!r || !tsr.running)) { - for (it = sources.begin(); it != sources.end(); ++it) { - (*it)->mark_for_remove (); - } - } - - sources.clear (); - } - - /* if the process was cancelled, delete the region */ - - if (!tsr.running) { - r.reset (); - } - - return r; -} diff --git a/libs/ardour/stretch.cc b/libs/ardour/stretch.cc new file mode 100644 index 0000000000..64b741d1af --- /dev/null +++ b/libs/ardour/stretch.cc @@ -0,0 +1,227 @@ +/* + Copyright (C) 2004-2007 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 <algorithm> +#include <cmath> + +#include <pbd/error.h> + +#include <ardour/types.h> +#include <ardour/stretch.h> +#include <ardour/audiofilesource.h> +#include <ardour/session.h> +#include <ardour/audioregion.h> + +#include "i18n.h" + +using namespace std; +using namespace ARDOUR; +using namespace PBD; +using namespace soundtouch; + +Stretch::Stretch (Session& s, TimeStretchRequest& req) + : Filter (s) + , tsr (req) +{ + float percentage; + + /* the soundtouch code wants a *tempo* change percentage, which is + of opposite sign to the length change. + */ + + percentage = -tsr.fraction; + + st.setSampleRate (s.frame_rate()); + st.setChannels (1); + st.setTempoChange (percentage); + st.setPitchSemiTones (0); + st.setRateChange (0); + + st.setSetting(SETTING_USE_QUICKSEEK, tsr.quick_seek); + st.setSetting(SETTING_USE_AA_FILTER, tsr.antialias); + + tsr.progress = 0.0f; +} + +Stretch::~Stretch () +{ +} + +int +Stretch::run (boost::shared_ptr<Region> r) +{ + boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (r); + + if (!region) { + return -1; + } + + SourceList nsrcs; + nframes_t total_frames; + nframes_t done; + int ret = -1; + const nframes_t bufsize = 16384; + gain_t *gain_buffer = 0; + Sample *buffer = 0; + char suffix[32]; + string new_name; + string::size_type at; + + tsr.progress = 0.0f; + tsr.done = false; + + total_frames = region->length() * region->n_channels(); + done = 0; + + /* the name doesn't need to be super-precise, but allow for 2 fractional + digits just to disambiguate close but not identical stretches. + */ + + snprintf (suffix, sizeof (suffix), "@%d", (int) floor (tsr.fraction * 100.0f)); + + /* create new sources */ + + if (make_new_sources (region, nsrcs, suffix)) { + goto out; + } + + gain_buffer = new gain_t[bufsize]; + buffer = new Sample[bufsize]; + + // soundtouch throws runtime_error on error + + try { + for (uint32_t i = 0; i < nsrcs.size(); ++i) { + + boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (nsrcs[i]); + + if (!afs) { + continue; + } + + nframes_t pos = 0; + nframes_t this_read = 0; + + st.clear(); + + while (!tsr.cancel && pos < region->length()) { + nframes_t this_time; + + this_time = min (bufsize, region->length() - pos); + + /* read from the master (original) sources for the region, + not the ones currently in use, in case it's already been + subject to timefx. + */ + + if ((this_read = region->master_read_at (buffer, buffer, gain_buffer, pos + region->position(), this_time)) != this_time) { + error << string_compose (_("tempoize: error reading data from %1"), afs->name()) << endmsg; + goto out; + } + + pos += this_read; + done += this_read; + + tsr.progress = (float) done / total_frames; + + st.putSamples (buffer, this_read); + + while ((this_read = st.receiveSamples (buffer, bufsize)) > 0 && !tsr.cancel) { + if (afs->write (buffer, this_read) != this_read) { + error << string_compose (_("error writing tempo-adjusted data to %1"), afs->name()) << endmsg; + goto out; + } + } + } + + if (!tsr.cancel) { + st.flush (); + } + + while (!tsr.cancel && (this_read = st.receiveSamples (buffer, bufsize)) > 0) { + if (afs->write (buffer, this_read) != this_read) { + error << string_compose (_("error writing tempo-adjusted data to %1"), afs->name()) << endmsg; + goto out; + } + } + } + + } catch (runtime_error& err) { + error << _("timefx code failure. please notify ardour-developers.") << endmsg; + error << err.what() << endmsg; + goto out; + } + + new_name = region->name(); + at = new_name.find ('@'); + + // remove any existing stretch indicator + + if (at != string::npos && at > 2) { + new_name = new_name.substr (0, at - 1); + } + + new_name += suffix; + + ret = finish (region, nsrcs, new_name); + + /* now reset ancestral data for each new region */ + + for (vector<boost::shared_ptr<Region> >::iterator x = results.begin(); x != results.end(); ++x) { + + boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*x); + + assert (region != 0); + + nframes64_t astart = region->ancestral_start(); + nframes64_t alength = region->ancestral_length(); + nframes_t start; + nframes_t length; + + // note: tsr.fraction is a percentage of original length. 100 = no change, + // 50 is half as long, 200 is twice as long, etc. + + float stretch = region->stretch() * (tsr.fraction/100.0); + + start = (nframes_t) floor (astart + ((astart - region->start()) / stretch)); + length = (nframes_t) floor (alength / stretch); + + region->set_ancestral_data (start, length, stretch); + } + + out: + + if (gain_buffer) { + delete [] gain_buffer; + } + + if (buffer) { + delete [] buffer; + } + + if (ret || tsr.cancel) { + for (SourceList::iterator si = nsrcs.begin(); si != nsrcs.end(); ++si) { + (*si)->mark_for_remove (); + } + } + + tsr.done = true; + + return ret; +} |