dbf18de931490bfb1ddf5a0e758d32d0a2f3e5d9
[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                 if ( state.delta != 0.0 )
80                 {
81                         add_scrub_interval( _scrub_timer.restart() );
82                         // x clicks per second => speed == 1.0
83                         float speed = _mcp.surface().scrub_scaling_factor() / average_scrub_interval() * state.ticks;
84                         _mcp.get_session().request_transport_speed( speed * state.sign );
85                 }
86                 else
87                 {
88                         // we have a stop event
89                         check_scrubbing();
90                 }
91                 break;
92         }
93         
94         case shuttle:
95                 _shuttle_speed = _mcp.get_session().transport_speed();
96                 _shuttle_speed += _mcp.surface().scaled_delta( state, _mcp.get_session().transport_speed() );
97                 _mcp.get_session().request_transport_speed( _shuttle_speed );
98                 break;
99         
100         case select:
101                 cout << "JogWheel select not implemented" << endl;
102                 break;
103         }
104 }
105
106 void JogWheel::check_scrubbing()
107 {
108         // if the last elapsed is greater than the average + std deviation, then stop
109         if ( !_scrub_intervals.empty() && _scrub_timer.elapsed() > average_scrub_interval() + std_dev_scrub_interval() )
110         {
111                 _mcp.get_session().request_transport_speed( 0.0 );
112                 _scrub_intervals.clear();
113         }
114 }
115
116 void JogWheel::push( State state )
117 {
118         _jog_wheel_states.push( state );
119 }
120
121 void JogWheel::pop()
122 {
123         if ( _jog_wheel_states.size() > 0 )
124         {
125                 _jog_wheel_states.pop();
126         }
127 }
128
129 void JogWheel::zoom_state_toggle()
130 {
131         if ( jog_wheel_state() == zoom )
132                 pop();
133         else
134                 push( zoom );
135 }
136
137 JogWheel::State JogWheel::scrub_state_cycle()
138 {
139         State top = jog_wheel_state();
140         if ( top == scrub )
141         {
142                 // stop scrubbing and go to shuttle
143                 pop();
144                 push( shuttle );
145                 _shuttle_speed = 0.0;
146         }
147         else if ( top == shuttle )
148         {
149                 // default to scroll, or the last selected
150                 pop();
151         }
152         else
153         {
154                 // start with scrub
155                 push( scrub );
156         }
157         
158         return jog_wheel_state();
159 }
160
161 void JogWheel::add_scrub_interval( unsigned long elapsed )
162 {
163         if ( _scrub_intervals.size() > 5 )
164         {
165                 _scrub_intervals.pop_front();
166         }
167         _scrub_intervals.push_back( elapsed );
168 }
169
170 float JogWheel::average_scrub_interval()
171 {
172         float sum = 0.0;
173         for ( std::deque<unsigned long>::iterator it = _scrub_intervals.begin(); it != _scrub_intervals.end(); ++it )
174         {
175                 sum += *it;
176         }
177         return sum / _scrub_intervals.size(); 
178 }
179
180 float JogWheel::std_dev_scrub_interval()
181 {
182         float average = average_scrub_interval();
183         
184         // calculate standard deviation
185         float sum = 0.0;
186         for ( std::deque<unsigned long>::iterator it = _scrub_intervals.begin(); it != _scrub_intervals.end(); ++it )
187         {
188                 sum += pow( *it - average, 2 );
189         }
190         return sqrt( sum / _scrub_intervals.size() -1 );
191 }