tagging release
[dasher.git] / Src / DasherCore / AutoSpeedControl.cpp
blob076ca806a86cd697cc9bd9e3b1cd1c324aa2c425
1 #ifndef _WIN32
2 #include "config.h"
3 #endif
5 #include "../Common/Common.h"
7 #include "AutoSpeedControl.h"
9 #include <cmath>
10 #include <cfloat>
11 #include <iostream>
13 #ifndef WITH_DARWIN
14 double round(double dVal) {
15 double dF = floor(dVal);
16 double dC = ceil(dVal);
18 if(dVal - dF < dC - dVal)
19 return dF;
20 else
21 return dC;
23 #endif
25 CAutoSpeedControl::CAutoSpeedControl(Dasher::CEventHandler * pEventHandler, CSettingsStore * pSettingsStore, double dFrameRate)
26 : CDasherComponent(pEventHandler, pSettingsStore) {
28 //scale #samples by #samples = m_dSamplesScale / (current bitrate) + m_dSampleOffset
29 m_dSampleScale = 1.5;
30 m_dSampleOffset = 1.3;
31 m_dMinRRate = 80.0;
32 m_dSensitivity = GetLongParameter(LP_AUTOSPEED_SENSITIVITY) / 100.0; //param only, no GUI!
33 //tolerance for automatic speed control
34 m_dTier1 = 0.0005; // should be arranged so that tier4 > tier3 > tier2 > tier1 !!!
35 m_dTier2 = 0.01;
36 m_dTier3 = 0.2;
37 m_dTier4 = 0.31;
38 //bitrate fractional changes for auto-speed control
39 m_dChange1 = 1.1;
40 m_dChange2 = 1.02;
41 m_dChange3 = 0.97;
42 m_dChange4 = 0.94;
43 //cap bitrate at...
44 m_dSpeedMax = 8.0;
45 m_dSpeedMin = 0.1;
46 //variance of two-centred-gaussians for adaptive radius
47 m_dSigma1 = 0.5;
48 m_dSigma2 = 0.05;
49 //Initialise auto-speed control
50 m_nSpeedCounter = 0;
51 m_dBitrate = double(round(GetLongParameter(LP_MAX_BITRATE) / 100.0));
53 UpdateMinRadius();
54 UpdateSampleSize(dFrameRate);
57 ////////////////////////////////////////////////
58 ///
59 /// Change max bitrate based on variance of angle
60 /// in dasher space.
61 ///
62 /////////////////////////////////////////////////
64 inline double CAutoSpeedControl::UpdateBitrate()
66 double var = Variance();
67 if(var < m_dTier1)
69 m_dBitrate *= m_dChange1;
71 else if(var < m_dTier2)
73 m_dBitrate *= m_dChange2;
75 else if(var > m_dTier4) //Tier 4 comes before tier 3 because tier4 > tier3 !!!
77 m_dBitrate *= m_dChange4;
79 else if(var > m_dTier3)
81 m_dBitrate *= m_dChange3;
83 //else if( in the middle )
84 // nothing happens! ;
86 //always keep bitrate values sane
87 if(m_dBitrate > m_dSpeedMax)
89 m_dBitrate = m_dSpeedMax;
91 else if(m_dBitrate < m_dSpeedMin)
93 m_dBitrate = m_dSpeedMin;
96 return m_dBitrate;
98 ///////////////////////////////////////////////
99 ///
100 /// Finds variance for automatic speed control
102 //////////////////////////////////////////////
104 inline double CAutoSpeedControl::Variance()
106 double avgcos, avgsin;
107 avgsin = avgcos = 0.0;
108 DOUBLE_DEQUE::iterator i;
109 // find average of cos(theta) and sin(theta)
110 for(i = m_dequeAngles.begin(); i != m_dequeAngles.end(); i++) {
111 avgcos += cos(*i);
112 avgsin += sin(*i);
114 avgcos /= (1.0 * m_dequeAngles.size());
115 avgsin /= (1.0 * m_dequeAngles.size());
116 //return variance (see dasher/Doc/speedcontrol.tex)
117 return -log(avgcos * avgcos + avgsin * avgsin);
120 //////////////////////////////////////////////////////////////////////
122 /// The number of samples depends on the clock rate of the
123 /// machine (framerate) and the user's speed (bitrate). See
124 /// speedcontrol.tex in dasher/Doc/ dir.
126 /////////////////////////////////////////////////////////////////////
129 inline int CAutoSpeedControl::UpdateSampleSize(double dFrameRate)
131 double dFramerate = dFrameRate;
132 double dSpeedSamples = 0.0;
133 double dBitrate = m_dBitrate;
134 if(dBitrate < 1.0)// for the purposes of this function
135 dBitrate = 1.0; // we don't care exactly how slow we're going
136 // *really* low speeds are ~ equivalent?
137 dSpeedSamples = dFramerate * (m_dSampleScale / dBitrate + m_dSampleOffset);
139 m_nSpeedSamples = int(round(dSpeedSamples));
141 return m_nSpeedSamples;
143 /////////////////////////////////////////////////////////////
145 /// double UpdateMinRadius() - find adaptive min radius for
146 /// auto-speed control. Calculated by DJCM's
147 /// mixture-of-2-centred-gaussians model.
149 ///////////////////////////////////////////////////////////
151 inline double CAutoSpeedControl::UpdateMinRadius()
153 m_dMinRadius = sqrt( log( (m_dSigma2 * m_dSigma2) / (m_dSigma1 * m_dSigma1) ) /
154 ( 1 / (m_dSigma1 * m_dSigma1) - 1 / (m_dSigma2 * m_dSigma2)) );
155 return m_dMinRadius;
158 //////////////////////////////////////////////////////////////
160 /// NB: updates VARIANCES of two populations of
161 /// mixture-of-2-centred-Gaussians model!
163 //////////////////////////////////////////////////////////////
165 inline void CAutoSpeedControl::UpdateSigmas(double r, double dFrameRate)
167 double dSamples = m_dMinRRate* dFrameRate / m_dBitrate;
168 if(r > m_dMinRadius)
169 m_dSigma1 = m_dSigma1 - (m_dSigma1 - r * r) / dSamples;
170 else
171 m_dSigma2 = m_dSigma2 - (m_dSigma2 - r * r) / dSamples;
174 /////////////////////////////////////////////////////////////////
176 /// AUTOMATIC SPEED CONTROL, CEH 7/05: Analyse variance of angle
177 /// mouse position makes with +ve x-axis in Dasher-space
179 ////////////////////////////////////////////////////////////////
182 void CAutoSpeedControl::SpeedControl(myint iDasherX, myint iDasherY, double dFrameRate, CDasherView *pView) {
183 if(GetBoolParameter(BP_AUTO_SPEEDCONTROL) && !GetBoolParameter(BP_DASHER_PAUSED)) {
185 // Coordinate transforms:
186 iDasherX = myint(pView->xmap(iDasherX / static_cast < double >(GetLongParameter(LP_MAX_Y))) * GetLongParameter(LP_MAX_Y));
187 iDasherY = myint(pView->ymap(iDasherY));
189 myint iDasherOX = myint(pView->xmap(GetLongParameter(LP_OX) / static_cast < double >(GetLongParameter(LP_MAX_Y))) * GetLongParameter(LP_MAX_Y));
190 myint iDasherOY = myint(pView->ymap(GetLongParameter(LP_OY)));
192 double x = -(iDasherX - iDasherOX) / double(iDasherOX); //Use normalised coords so min r works
193 double y = -(iDasherY - iDasherOY) / double(iDasherOY);
194 double theta = atan2(y, x);
195 double r = sqrt(x * x + y * y);
197 m_dBitrate = GetLongParameter(LP_MAX_BITRATE) / 100.0; // stored as long(round(true bitrate * 100))
199 UpdateSigmas(r, dFrameRate);
201 // Data collection:
203 if(r > m_dMinRadius && fabs(theta) < 1.25) {
204 m_nSpeedCounter++;
205 m_dequeAngles.push_back(theta);
206 while(m_dequeAngles.size() > m_nSpeedSamples) {
207 m_dequeAngles.pop_front();
211 m_dSensitivity = GetLongParameter(LP_AUTOSPEED_SENSITIVITY) / 100.0;
212 if(m_nSpeedCounter > round(m_nSpeedSamples / m_dSensitivity)) {
213 //do speed control every so often!
215 UpdateSampleSize(dFrameRate);
216 UpdateMinRadius();
217 UpdateBitrate();
218 long lBitrateTimes100 = long(round(m_dBitrate * 100)); //Dasher settings want long numerical parameters
219 SetLongParameter(LP_MAX_BITRATE, lBitrateTimes100);
220 m_nSpeedCounter = 0;