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