mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-06 06:44:57 +01:00
merged with 2.0-ongoing changes 2582-2605 (not thoroughly tested but it compiles, start up, and creates a new session)
git-svn-id: svn://localhost/ardour2/trunk@2606 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
df20e5935f
commit
badc087263
34 changed files with 1028 additions and 568 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
@ -259,11 +253,17 @@ Editor::Editor ()
|
|||
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
|
||||
|
|
@ -3228,6 +3245,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 ()
|
||||
{
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -202,7 +202,6 @@ Editor::typed_event (ArdourCanvas::Item* item, GdkEvent *event, ItemType type)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
double delta;
|
||||
|
||||
/* first motion, just set up the variables */
|
||||
|
||||
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;
|
||||
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 {
|
||||
distance = last_scrub_frame - (nframes64_t) drag_info.current_pointer_frame;
|
||||
dir = -1.0;
|
||||
/* 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);
|
||||
}
|
||||
#else
|
||||
if (drag_info.grab_x < drag_info.current_pointer_x) {
|
||||
distance = drag_info.current_pointer_x - drag_info.grab_x;
|
||||
dir = -1.0;
|
||||
|
||||
scrubbing_direction = -1;
|
||||
|
||||
} else {
|
||||
distance = drag_info.grab_x - drag_info.current_pointer_x;
|
||||
dir = 1.0;
|
||||
/* 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);
|
||||
}
|
||||
#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 ();
|
||||
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();
|
||||
}
|
||||
|
||||
nframes64_t where = get_preferred_edit_position();
|
||||
|
||||
if (where >= 0) {
|
||||
|
||||
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));
|
||||
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) (edit_cursor->current_frame * speed));
|
||||
align_region (rv.region(), End, (nframes_t) (where * speed));
|
||||
|
||||
} else {
|
||||
|
||||
align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -1161,6 +1200,8 @@ Editor::temporal_zoom (gdouble fpu)
|
|||
// 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,7 +2332,7 @@ 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));
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
if (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end()) {
|
||||
(*i)->set_selected (true);
|
||||
if (mouse_mode == MouseRange) {
|
||||
(*i)->show_selection (selection->time);
|
||||
} else {
|
||||
(*i)->set_selected (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -652,6 +646,7 @@ Editor::time_selection_changed ()
|
|||
} else {
|
||||
ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
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*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
31
gtk2_ardour/marker_selection.h
Normal file
31
gtk2_ardour/marker_selection.h
Normal file
|
|
@ -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__ */
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -143,6 +143,15 @@ Selection::clear_lines ()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Selection::clear_markers ()
|
||||
{
|
||||
if (!markers.empty()) {
|
||||
markers.clear ();
|
||||
MarkersChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Selection::toggle (boost::shared_ptr<Playlist> pl)
|
||||
{
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,6 +130,7 @@ 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*>&);
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -545,7 +545,11 @@ 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);
|
||||
|
|
@ -571,8 +575,6 @@ TimeAxisView::set_selected (bool yn)
|
|||
for (Children::iterator i = children.begin(); i != children.end(); ++i) {
|
||||
(*i)->set_selected (false);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -96,6 +96,12 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region>
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
51
libs/ardour/ardour/stretch.h
Normal file
51
libs/ardour/ardour/stretch.h
Normal file
|
|
@ -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__ */
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
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)));
|
||||
|
|
|
|||
|
|
@ -190,12 +190,20 @@ MidiRegion::state (bool full)
|
|||
|
||||
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);
|
||||
_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);
|
||||
}
|
||||
|
||||
if (full && _extra_xml) {
|
||||
node.add_child_copy (*_extra_xml);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -517,6 +526,14 @@ Region::nudge_position (long n, void *src)
|
|||
send_change (PositionChanged);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
||||
|
|
@ -1433,6 +1434,26 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool full)
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
227
libs/ardour/stretch.cc
Normal file
227
libs/ardour/stretch.cc
Normal file
|
|
@ -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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue