#include #include #include "mackie_jog_wheel.h" #include "mackie_control_protocol.h" #include "surface_port.h" #include "controls.h" #include "surface.h" #include using namespace Mackie; using std::isnan; JogWheel::JogWheel( MackieControlProtocol & mcp ) : _mcp( mcp ) , _transport_speed( 4.0 ) , _transport_direction( 0 ) , _shuttle_speed( 0.0 ) { } JogWheel::State JogWheel::jog_wheel_state() const { if ( !_jog_wheel_states.empty() ) return _jog_wheel_states.top(); else return scroll; } void JogWheel::zoom_event (SurfacePort &, Control &, const ControlState &) { } void JogWheel::scrub_event (SurfacePort &, Control &, const ControlState &) { } void JogWheel::speed_event (SurfacePort &, Control &, const ControlState &) { } void JogWheel::scroll_event (SurfacePort &, Control &, const ControlState &) { } void JogWheel::jog_event (SurfacePort &, Control &, const ControlState & state) { // TODO use current snap-to setting? switch ( jog_wheel_state() ) { case scroll: _mcp.ScrollTimeline( state.delta * state.sign ); break; case zoom: // Chunky Zoom. // TODO implement something similar to ScrollTimeline which // ends up in Editor::control_scroll for smoother zooming. if ( state.sign > 0 ) for ( unsigned int i = 0; i < state.ticks; ++i ) _mcp.ZoomIn(); else for ( unsigned int i = 0; i < state.ticks; ++i ) _mcp.ZoomOut(); break; case speed: // locally, _transport_speed is an positive value _transport_speed += _mcp.surface().scaled_delta( state, _mcp.get_session().transport_speed() ); // make sure no weirdness gets to the session if ( _transport_speed < 0 || isnan( _transport_speed ) ) { _transport_speed = 0.0; } // translate _transport_speed speed to a signed transport velocity _mcp.get_session().request_transport_speed( transport_speed() * transport_direction() ); break; case scrub: { if ( state.sign != 0 ) { add_scrub_interval( _scrub_timer.restart() ); // x clicks per second => speed == 1.0 float speed = _mcp.surface().scrub_scaling_factor() / average_scrub_interval() * state.ticks; _mcp.get_session().request_transport_speed( speed * state.sign ); } else { // we have a stop event check_scrubbing(); } break; } case shuttle: _shuttle_speed = _mcp.get_session().transport_speed(); _shuttle_speed += _mcp.surface().scaled_delta( state, _mcp.get_session().transport_speed() ); _mcp.get_session().request_transport_speed( _shuttle_speed ); break; case select: std::cout << "JogWheel select not implemented" << std::endl; break; } } void JogWheel::check_scrubbing() { // if the last elapsed is greater than the average + std deviation, then stop if ( !_scrub_intervals.empty() && _scrub_timer.elapsed() > average_scrub_interval() + std_dev_scrub_interval() ) { _mcp.get_session().request_transport_speed( 0.0 ); _scrub_intervals.clear(); } } void JogWheel::push( State state ) { _jog_wheel_states.push( state ); } void JogWheel::pop() { if ( _jog_wheel_states.size() > 0 ) { _jog_wheel_states.pop(); } } void JogWheel::zoom_state_toggle() { if ( jog_wheel_state() == zoom ) pop(); else push( zoom ); } JogWheel::State JogWheel::scrub_state_cycle() { State top = jog_wheel_state(); if ( top == scrub ) { // stop scrubbing and go to shuttle pop(); push( shuttle ); _shuttle_speed = 0.0; } else if ( top == shuttle ) { // default to scroll, or the last selected pop(); } else { // start with scrub push( scrub ); } return jog_wheel_state(); } void JogWheel::add_scrub_interval( unsigned long elapsed ) { if ( _scrub_intervals.size() > 5 ) { _scrub_intervals.pop_front(); } _scrub_intervals.push_back( elapsed ); } float JogWheel::average_scrub_interval() { float sum = 0.0; for ( std::deque::iterator it = _scrub_intervals.begin(); it != _scrub_intervals.end(); ++it ) { sum += *it; } return sum / _scrub_intervals.size(); } float JogWheel::std_dev_scrub_interval() { float average = average_scrub_interval(); // calculate standard deviation float sum = 0.0; for ( std::deque::iterator it = _scrub_intervals.begin(); it != _scrub_intervals.end(); ++it ) { sum += pow( *it - average, 2 ); } return sqrt( sum / _scrub_intervals.size() -1 ); }