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:
Paul Davis 2007-11-08 01:40:25 +00:00
parent df20e5935f
commit badc087263
34 changed files with 1028 additions and 568 deletions

View file

@ -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);

View file

@ -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 {

View file

@ -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)

View file

@ -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
@ -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)

View file

@ -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__ */

View file

@ -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;

View file

@ -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 ();
}
}

View file

@ -202,7 +202,6 @@ Editor::typed_event (ArdourCanvas::Item* item, GdkEvent *event, ItemType type)
default:
break;
}
return ret;
}

View file

@ -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;

View file

@ -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 {
/* 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 = 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;
} 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))) {
nframes64_t where = get_preferred_edit_position();
align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
if (where >= 0) {
} 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));
} 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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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*

View file

@ -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);
}

View 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__ */

View file

@ -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;

View file

@ -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();
}
}

View file

@ -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));

View file

@ -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);
}
}
}

View file

@ -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)
{

View file

@ -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);

View file

@ -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

View file

@ -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;
};

View file

@ -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

View file

@ -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 ();

View 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__ */

View file

@ -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)
{

View file

@ -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)));

View file

@ -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);
}

View file

@ -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
{

View file

@ -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)) {

View file

@ -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;
}

View file

@ -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
View 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;
}