more notes and comments and tings
[ardour.git] / libs / surfaces / mackie / mackie_jog_wheel.cc
1 #include "mackie_jog_wheel.h"
2
3 #include "mackie_control_protocol.h"
4 #include "surface_port.h"
5 #include "controls.h"
6 #include "surface.h"
7
8 #include <algorithm>
9
10 using namespace Mackie;
11
12 JogWheel::JogWheel( MackieControlProtocol & mcp )
13 : _mcp( mcp )
14 , _transport_speed( 4.0 )
15 , _transport_direction( 0 )
16 , _shuttle_speed( 0.0 )
17 {
18 }
19
20 JogWheel::State JogWheel::jog_wheel_state() const
21 {
22         if ( !_jog_wheel_states.empty() )
23                 return _jog_wheel_states.top();
24         else 
25                 return scroll;
26 }
27
28 void JogWheel::zoom_event( SurfacePort & port, Control & control, const ControlState & state )
29 {
30 }
31
32 void JogWheel::scrub_event( SurfacePort & port, Control & control, const ControlState & state )
33 {
34 }
35
36 void JogWheel::speed_event( SurfacePort & port, Control & control, const ControlState & state )
37 {
38 }
39
40 void JogWheel::scroll_event( SurfacePort & port, Control & control, const ControlState & state )
41 {
42 }
43
44 void JogWheel::jog_event( SurfacePort & port, Control & control, const ControlState & state )
45 {
46         // TODO use current snap-to setting?
47         switch ( jog_wheel_state() )
48         {
49         case scroll:
50                 _mcp.ScrollTimeline( state.delta * state.sign );
51                 break;
52         
53         case zoom:
54                 // Chunky Zoom.
55                 // TODO implement something similar to ScrollTimeline which
56                 // ends up in Editor::control_scroll for smoother zooming.
57                 if ( state.sign > 0 )
58                         for ( unsigned int i = 0; i < state.ticks; ++i ) _mcp.ZoomIn();
59                 else
60                         for ( unsigned int i = 0; i < state.ticks; ++i ) _mcp.ZoomOut();
61                 break;
62                 
63         case speed:
64                 // locally, _transport_speed is an positive value
65                 _transport_speed += _mcp.surface().scaled_delta( state, _mcp.get_session().transport_speed() );
66
67                 // make sure no weirdness gets to the session
68                 if ( _transport_speed < 0 || isnan( _transport_speed ) )
69                 {
70                         _transport_speed = 0.0;
71                 }
72                 
73                 // translate _transport_speed speed to a signed transport velocity
74                 _mcp.get_session().request_transport_speed( transport_speed() * transport_direction() );
75                 break;
76         
77         case scrub:
78         {
79                 add_scrub_interval( _scrub_timer.restart() );
80                 // x clicks per second => speed == 1.0
81                 float speed = _mcp.surface().scrub_scaling_factor() / average_scrub_interval() * state.ticks;
82                 _mcp.get_session().request_transport_speed( speed * state.sign );
83                 break;
84         }
85         
86         case shuttle:
87                 _shuttle_speed = _mcp.get_session().transport_speed();
88                 _shuttle_speed += _mcp.surface().scaled_delta( state, _mcp.get_session().transport_speed() );
89                 _mcp.get_session().request_transport_speed( _shuttle_speed );
90                 break;
91         
92         case select:
93                 cout << "JogWheel select not implemented" << endl;
94                 break;
95         }
96 }
97
98 void JogWheel::check_scrubbing()
99 {
100         // if the last elapsed is greater than the average + std deviation, then stop
101         if ( !_scrub_intervals.empty() && _scrub_timer.elapsed() > average_scrub_interval() + std_dev_scrub_interval() )
102         {
103                 _mcp.get_session().request_transport_speed( 0.0 );
104                 _scrub_intervals.clear();
105         }
106 }
107
108 void JogWheel::push( State state )
109 {
110         _jog_wheel_states.push( state );
111 }
112
113 void JogWheel::pop()
114 {
115         if ( _jog_wheel_states.size() > 0 )
116         {
117                 _jog_wheel_states.pop();
118         }
119 }
120
121 void JogWheel::zoom_state_toggle()
122 {
123         if ( jog_wheel_state() == zoom )
124                 pop();
125         else
126                 push( zoom );
127 }
128
129 JogWheel::State JogWheel::scrub_state_cycle()
130 {
131         State top = jog_wheel_state();
132         if ( top == scrub )
133         {
134                 // stop scrubbing and go to shuttle
135                 pop();
136                 push( shuttle );
137                 _shuttle_speed = 0.0;
138         }
139         else if ( top == shuttle )
140         {
141                 // default to scroll, or the last selected
142                 pop();
143         }
144         else
145         {
146                 // start with scrub
147                 push( scrub );
148         }
149         
150         return jog_wheel_state();
151 }
152
153 void JogWheel::add_scrub_interval( unsigned long elapsed )
154 {
155         if ( _scrub_intervals.size() > 5 )
156         {
157                 _scrub_intervals.pop_front();
158         }
159         _scrub_intervals.push_back( elapsed );
160 }
161
162 float JogWheel::average_scrub_interval()
163 {
164         float sum = 0.0;
165         for ( std::deque<unsigned long>::iterator it = _scrub_intervals.begin(); it != _scrub_intervals.end(); ++it )
166         {
167                 sum += *it;
168         }
169         return sum / _scrub_intervals.size(); 
170 }
171
172 float JogWheel::std_dev_scrub_interval()
173 {
174         float average = average_scrub_interval();
175         
176         // calculate standard deviation
177         float sum = 0.0;
178         for ( std::deque<unsigned long>::iterator it = _scrub_intervals.begin(); it != _scrub_intervals.end(); ++it )
179         {
180                 sum += pow( *it - average, 2 );
181         }
182         return sqrt( sum / _scrub_intervals.size() -1 );
183 }