improved (though not perfect) mouse scrubbing

git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2460 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2007-09-11 20:55:16 +00:00
parent 40533b553e
commit 0a38cee852
3 changed files with 151 additions and 12 deletions

View file

@ -313,7 +313,15 @@ Editor::Editor ()
canvas_idle_queued = false;
_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;
sfbrowser = 0;
ignore_route_order_sync = false;

View file

@ -342,6 +342,8 @@ class Editor : public PublicEditor
void reposition_and_zoom (nframes_t, double);
nframes_t edit_cursor_position(bool);
bool update_mouse_speed ();
bool decelerate_mouse_speed ();
protected:
void map_transport_state ();
@ -1071,8 +1073,16 @@ class Editor : public PublicEditor
void stop_scrolling ();
bool _scrubbing;
bool have_full_mouse_speed;
nframes64_t last_scrub_frame;
uint32_t last_scrub_time;
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;
void keyboard_selection_begin ();
void keyboard_selection_finish (bool add);

View file

@ -24,6 +24,7 @@
#include <set>
#include <string>
#include <algorithm>
#include <sys/time.h>
#include <pbd/error.h>
#include <gtkmm2ext/utils.h>
@ -672,6 +673,8 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
_scrubbing = true;
last_scrub_frame = 0;
last_scrub_time = 0;
have_full_mouse_speed = false;
memset (mouse_speed, 0, sizeof (double) * mouse_speed_size);
/* rest handled in motion & release */
break;
@ -773,6 +776,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
if (drag_info.item) {
if (end_grab (item, event)) {
/* grab dragged, so do nothing else */
cerr << "return from drag+grab\n";
return true;
}
}
@ -1423,6 +1427,13 @@ 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)
{
@ -1457,23 +1468,26 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
switch (mouse_mode) {
case MouseAudition:
if (_scrubbing) {
struct timeval tmnow;
if (last_scrub_frame == 0) {
/* first motion, just set up the variables */
last_scrub_frame = (nframes64_t) drag_info.current_pointer_frame;
last_scrub_time = ((GdkEventMotion*)event)->time;
session->request_locate (last_scrub_frame);
gettimeofday (&tmnow, 0);
last_scrub_time = tmnow.tv_sec * 1000000 + tmnow.tv_usec;
session->request_locate (last_scrub_frame, true);
} else {
/* how fast is the mouse moving ? */
double speed;
nframes_t distance;
uint32_t time;
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;
@ -1481,14 +1495,29 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
distance = last_scrub_frame - (nframes64_t) drag_info.current_pointer_frame;
dir = -1.0;
}
time = ((GdkEventMotion*) event)->time - last_scrub_time;
#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 + tmnow.tv_usec) - last_scrub_time;
last_scrub_frame = drag_info.current_pointer_frame;
last_scrub_time = ((GdkEventMotion*)event)->time;
speed = (distance * 1000.0) / time; // frames/sec
last_scrub_time = (tmnow.tv_sec * 1000000) + tmnow.tv_usec;
speed = (distance * 1000000.0) / time; // frames/sec
speed /= session->frame_rate();
speed *= dir;
session->request_transport_speed (speed);
add_mouse_speed (speed, dir);
if (mouse_speed_update < 0) {
mouse_speed_update = g_timeout_add (10, _update_mouse_speed, this);
update_mouse_speed ();
}
}
}
@ -4960,3 +4989,95 @@ 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;
double rev;
double dir;
static size_t update_cnt = 0;
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;
}