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