summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/ardour_ui_ed.cc2
-rw-r--r--gtk2_ardour/editing.h11
-rw-r--r--gtk2_ardour/editing_syms.h6
-rw-r--r--gtk2_ardour/editor.cc127
-rw-r--r--gtk2_ardour/editor.h47
-rw-r--r--gtk2_ardour/editor_actions.cc58
-rw-r--r--gtk2_ardour/editor_audio_import.cc8
-rw-r--r--gtk2_ardour/editor_canvas_events.cc1
-rw-r--r--gtk2_ardour/editor_markers.cc8
-rw-r--r--gtk2_ardour/editor_mouse.cc257
-rw-r--r--gtk2_ardour/editor_ops.cc133
-rw-r--r--gtk2_ardour/editor_selection.cc15
-rw-r--r--gtk2_ardour/editor_timefx.cc51
-rw-r--r--gtk2_ardour/enums.cc6
-rw-r--r--gtk2_ardour/marker_selection.h31
-rw-r--r--gtk2_ardour/public_editor.h10
-rw-r--r--gtk2_ardour/selection.cc52
-rw-r--r--gtk2_ardour/selection.h10
-rw-r--r--gtk2_ardour/time_axis_view.cc10
-rw-r--r--gtk2_ardour/utils.cc56
-rw-r--r--gtk2_ardour/utils.h2
-rw-r--r--libs/ardour/SConscript2
-rw-r--r--libs/ardour/ardour/filter.h4
-rw-r--r--libs/ardour/ardour/region.h16
-rw-r--r--libs/ardour/ardour/session.h15
-rw-r--r--libs/ardour/ardour/stretch.h51
-rw-r--r--libs/ardour/audioregion.cc28
-rw-r--r--libs/ardour/filter.cc23
-rw-r--r--libs/ardour/midi_region.cc8
-rw-r--r--libs/ardour/region.cc49
-rw-r--r--libs/ardour/session.cc5
-rw-r--r--libs/ardour/session_state.cc31
-rw-r--r--libs/ardour/session_timefx.cc236
-rw-r--r--libs/ardour/stretch.cc227
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", &current_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;
+}