![]() |
Mixxx
|
00001 // ratecontrol.cpp 00002 // Created 7/4/2009 by RJ Ryan (rryan@mit.edu) 00003 00004 #include "controlobject.h" 00005 #include "controlpushbutton.h" 00006 #include "controlpotmeter.h" 00007 #include "controlttrotary.h" 00008 #include "rotary.h" 00009 00010 #include "engine/enginecontrol.h" 00011 #include "engine/ratecontrol.h" 00012 #include "engine/positionscratchcontroller.h" 00013 00014 #ifdef _MSC_VER 00015 #include <float.h> // for _isnan() on VC++ 00016 #define isnan(x) _isnan(x) // VC++ uses _isnan() instead of isnan() 00017 #else 00018 #include <math.h> // for isnan() everywhere else 00019 #endif 00020 00021 // Static default values for rate buttons (percents) 00022 double RateControl::m_dTemp = 4.00; //(eg. 4.00%) 00023 double RateControl::m_dTempSmall = 1.00; 00024 double RateControl::m_dPerm = 0.50; 00025 double RateControl::m_dPermSmall = 0.05; 00026 00027 int RateControl::m_iRateRampSensitivity = 250; 00028 enum RateControl::RATERAMP_MODE RateControl::m_eRateRampMode = RateControl::RATERAMP_STEP; 00029 00030 RateControl::RateControl(const char* _group, 00031 ConfigObject<ConfigValue>* _config) : 00032 EngineControl(_group, _config), 00033 m_ePbCurrent(0), 00034 m_ePbPressed(0), 00035 m_bTempStarted(false), 00036 m_dTempRateChange(0.0), 00037 m_dRateTemp(0.0), 00038 m_eRampBackMode(RATERAMP_RAMPBACK_NONE), 00039 m_dRateTempRampbackChange(0.0), 00040 m_dOldRate(0.0f), 00041 m_pConfig(_config) { 00042 m_pScratchController = new PositionScratchController(_group); 00043 00044 m_pRateDir = new ControlObject(ConfigKey(_group, "rate_dir")); 00045 m_pRateRange = new ControlObject(ConfigKey(_group, "rateRange")); 00046 m_pRateSlider = new ControlPotmeter(ConfigKey(_group, "rate"), -1.f, 1.f); 00047 00048 // Search rate. Rate used when searching in sound. This overrules the 00049 // playback rate 00050 m_pRateSearch = new ControlPotmeter(ConfigKey(_group, "rateSearch"), -300., 300.); 00051 00052 // Reverse button 00053 m_pReverseButton = new ControlPushButton(ConfigKey(_group, "reverse")); 00054 m_pReverseButton->set(0); 00055 00056 // Forward button 00057 m_pForwardButton = new ControlPushButton(ConfigKey(_group, "fwd")); 00058 connect(m_pForwardButton, SIGNAL(valueChanged(double)), 00059 this, SLOT(slotControlFastForward(double)), 00060 Qt::DirectConnection); 00061 m_pForwardButton->set(0); 00062 00063 // Back button 00064 m_pBackButton = new ControlPushButton(ConfigKey(_group, "back")); 00065 connect(m_pBackButton, SIGNAL(valueChanged(double)), 00066 this, SLOT(slotControlFastBack(double)), 00067 Qt::DirectConnection); 00068 m_pBackButton->set(0); 00069 00070 // Permanent rate-change buttons 00071 buttonRatePermDown = 00072 new ControlPushButton(ConfigKey(_group,"rate_perm_down")); 00073 connect(buttonRatePermDown, SIGNAL(valueChanged(double)), 00074 this, SLOT(slotControlRatePermDown(double)), 00075 Qt::DirectConnection); 00076 00077 buttonRatePermDownSmall = 00078 new ControlPushButton(ConfigKey(_group,"rate_perm_down_small")); 00079 connect(buttonRatePermDownSmall, SIGNAL(valueChanged(double)), 00080 this, SLOT(slotControlRatePermDownSmall(double)), 00081 Qt::DirectConnection); 00082 00083 buttonRatePermUp = 00084 new ControlPushButton(ConfigKey(_group,"rate_perm_up")); 00085 connect(buttonRatePermUp, SIGNAL(valueChanged(double)), 00086 this, SLOT(slotControlRatePermUp(double)), 00087 Qt::DirectConnection); 00088 00089 buttonRatePermUpSmall = 00090 new ControlPushButton(ConfigKey(_group,"rate_perm_up_small")); 00091 connect(buttonRatePermUpSmall, SIGNAL(valueChanged(double)), 00092 this, SLOT(slotControlRatePermUpSmall(double)), 00093 Qt::DirectConnection); 00094 00095 // Temporary rate-change buttons 00096 buttonRateTempDown = 00097 new ControlPushButton(ConfigKey(_group,"rate_temp_down")); 00098 connect(buttonRateTempDown, SIGNAL(valueChanged(double)), 00099 this, SLOT(slotControlRateTempDown(double)), 00100 Qt::DirectConnection); 00101 00102 buttonRateTempDownSmall = 00103 new ControlPushButton(ConfigKey(_group,"rate_temp_down_small")); 00104 connect(buttonRateTempDownSmall, SIGNAL(valueChanged(double)), 00105 this, SLOT(slotControlRateTempDownSmall(double)), 00106 Qt::DirectConnection); 00107 00108 buttonRateTempUp = 00109 new ControlPushButton(ConfigKey(_group,"rate_temp_up")); 00110 connect(buttonRateTempUp, SIGNAL(valueChanged(double)), 00111 this, SLOT(slotControlRateTempUp(double)), 00112 Qt::DirectConnection); 00113 00114 buttonRateTempUpSmall = 00115 new ControlPushButton(ConfigKey(_group,"rate_temp_up_small")); 00116 connect(buttonRateTempUpSmall, SIGNAL(valueChanged(double)), 00117 this, SLOT(slotControlRateTempUpSmall(double)), 00118 Qt::DirectConnection); 00119 00120 // We need the sample rate so we can guesstimate something close 00121 // what latency is. 00122 m_pSampleRate = ControlObject::getControl(ConfigKey("[Master]","samplerate")); 00123 00124 // Wheel to control playback position/speed 00125 m_pWheel = new ControlTTRotary(ConfigKey(_group, "wheel")); 00126 00127 // Scratch controller, this is an accumulator which is useful for 00128 // controllers that return individiual +1 or -1s, these get added up and 00129 // cleared when we read 00130 m_pScratch = new ControlTTRotary(ConfigKey(_group, "scratch2")); 00131 m_pOldScratch = new ControlTTRotary(ConfigKey(_group, "scratch")); // Deprecated 00132 00133 // Scratch enable toggle 00134 m_pScratchToggle = new ControlPushButton(ConfigKey(_group, "scratch2_enable")); 00135 m_pScratchToggle->set(0); 00136 00137 m_pJog = new ControlObject(ConfigKey(_group, "jog")); 00138 m_pJogFilter = new Rotary(); 00139 // FIXME: This should be dependent on sample rate/block size or something 00140 m_pJogFilter->setFilterLength(25); 00141 00142 // Update Internal Settings 00143 // Set Pitchbend Mode 00144 m_eRateRampMode = (RateControl::RATERAMP_MODE) 00145 m_pConfig->getValueString(ConfigKey("[Controls]","RateRamp")).toInt(); 00146 00147 // Set the Sensitivity 00148 m_iRateRampSensitivity = 00149 m_pConfig->getValueString(ConfigKey("[Controls]","RateRampSensitivity")).toInt(); 00150 00151 #ifdef __VINYLCONTROL__ 00152 ControlObject* pVCEnabled = ControlObject::getControl(ConfigKey(_group, "vinylcontrol_enabled")); 00153 // Throw a hissy fit if somebody moved us such that the vinylcontrol_enabled 00154 // control doesn't exist yet. This will blow up immediately, won't go unnoticed. 00155 Q_ASSERT(pVCEnabled); 00156 connect(pVCEnabled, SIGNAL(valueChanged(double)), 00157 this, SLOT(slotControlVinyl(double)), 00158 Qt::DirectConnection); 00159 connect(pVCEnabled, SIGNAL(valueChangedFromEngine(double)), 00160 this, SLOT(slotControlVinyl(double)), 00161 Qt::DirectConnection); 00162 #endif 00163 } 00164 00165 RateControl::~RateControl() { 00166 delete m_pRateSlider; 00167 delete m_pRateRange; 00168 delete m_pRateDir; 00169 00170 delete m_pRateSearch; 00171 00172 delete m_pReverseButton; 00173 delete m_pForwardButton; 00174 delete m_pBackButton; 00175 00176 delete buttonRateTempDown; 00177 delete buttonRateTempDownSmall; 00178 delete buttonRateTempUp; 00179 delete buttonRateTempUpSmall; 00180 delete buttonRatePermDown; 00181 delete buttonRatePermDownSmall; 00182 delete buttonRatePermUp; 00183 delete buttonRatePermUpSmall; 00184 00185 delete m_pWheel; 00186 delete m_pScratch; 00187 delete m_pOldScratch; 00188 delete m_pScratchToggle; 00189 delete m_pJog; 00190 delete m_pJogFilter; 00191 delete m_pScratchController; 00192 } 00193 00194 void RateControl::setRateRamp(bool linearMode) 00195 { 00196 if ( linearMode ) 00197 m_eRateRampMode = RateControl::RATERAMP_LINEAR; 00198 else 00199 m_eRateRampMode = RateControl::RATERAMP_STEP; 00200 } 00201 00202 void RateControl::setRateRampSensitivity(int sense) 00203 { 00204 // Reverse the actual sensitivity value passed. 00205 // That way the gui works in an intuitive manner. 00206 sense = RATE_SENSITIVITY_MAX - sense + RATE_SENSITIVITY_MIN; 00207 if ( sense < RATE_SENSITIVITY_MIN ) 00208 m_iRateRampSensitivity = RATE_SENSITIVITY_MIN; 00209 else if ( sense > RATE_SENSITIVITY_MAX ) 00210 m_iRateRampSensitivity = RATE_SENSITIVITY_MAX; 00211 else 00212 m_iRateRampSensitivity = sense; 00213 } 00214 00215 void RateControl::setTemp(double v) { 00216 m_dTemp = v; 00217 } 00218 00219 void RateControl::setTempSmall(double v) { 00220 m_dTempSmall = v; 00221 } 00222 00223 void RateControl::setPerm(double v) { 00224 m_dPerm = v; 00225 } 00226 00227 void RateControl::setPermSmall(double v) { 00228 m_dPermSmall = v; 00229 } 00230 00231 void RateControl::slotControlFastForward(double v) 00232 { 00233 //qDebug() << "slotControlFastForward(" << v << ")"; 00234 if (v==0.) 00235 m_pRateSearch->set(0.); 00236 else 00237 m_pRateSearch->set(4.); 00238 } 00239 00240 void RateControl::slotControlFastBack(double v) 00241 { 00242 //qDebug() << "slotControlFastBack(" << v << ")"; 00243 if (v==0.) 00244 m_pRateSearch->set(0.); 00245 else 00246 m_pRateSearch->set(-4.); 00247 } 00248 00249 void RateControl::slotControlRatePermDown(double) 00250 { 00251 // Adjusts temp rate down if button pressed 00252 if (buttonRatePermDown->get()) 00253 m_pRateSlider->sub(m_pRateDir->get() * m_dPerm / (100. * m_pRateRange->get())); 00254 } 00255 00256 void RateControl::slotControlRatePermDownSmall(double) 00257 { 00258 // Adjusts temp rate down if button pressed 00259 if (buttonRatePermDownSmall->get()) 00260 m_pRateSlider->sub(m_pRateDir->get() * m_dPermSmall / (100. * m_pRateRange->get())); 00261 } 00262 00263 void RateControl::slotControlRatePermUp(double) 00264 { 00265 // Adjusts temp rate up if button pressed 00266 if (buttonRatePermUp->get()) 00267 m_pRateSlider->add(m_pRateDir->get() * m_dPerm / (100. * m_pRateRange->get())); 00268 } 00269 00270 void RateControl::slotControlRatePermUpSmall(double) 00271 { 00272 // Adjusts temp rate up if button pressed 00273 if (buttonRatePermUpSmall->get()) 00274 m_pRateSlider->add(m_pRateDir->get() * m_dPermSmall / (100. * m_pRateRange->get())); 00275 } 00276 00277 void RateControl::slotControlRateTempDown(double) 00278 { 00279 // Set the state of the Temporary button. Logic is handled in ::process() 00280 if (buttonRateTempDown->get() && !(m_ePbPressed & RateControl::RATERAMP_DOWN)) 00281 { 00282 m_ePbPressed |= RateControl::RATERAMP_DOWN; 00283 m_ePbCurrent = RateControl::RATERAMP_DOWN; 00284 } 00285 else if (!buttonRateTempDown->get()) 00286 { 00287 m_ePbPressed &= ~RateControl::RATERAMP_DOWN; 00288 m_ePbCurrent = m_ePbPressed; 00289 } 00290 } 00291 00292 void RateControl::slotControlRateTempDownSmall(double) 00293 { 00294 // Set the state of the Temporary button. Logic is handled in ::process() 00295 if (buttonRateTempDownSmall->get() && !(m_ePbPressed & RateControl::RATERAMP_DOWN)) 00296 { 00297 m_ePbPressed |= RateControl::RATERAMP_DOWN; 00298 m_ePbCurrent = RateControl::RATERAMP_DOWN; 00299 } 00300 else if (!buttonRateTempDownSmall->get()) 00301 { 00302 m_ePbPressed &= ~RateControl::RATERAMP_DOWN; 00303 m_ePbCurrent = m_ePbPressed; 00304 } 00305 } 00306 00307 void RateControl::slotControlRateTempUp(double) 00308 { 00309 // Set the state of the Temporary button. Logic is handled in ::process() 00310 if (buttonRateTempUp->get() && !(m_ePbPressed & RateControl::RATERAMP_UP)) 00311 { 00312 m_ePbPressed |= RateControl::RATERAMP_UP; 00313 m_ePbCurrent = RateControl::RATERAMP_UP; 00314 } 00315 else if (!buttonRateTempUp->get()) 00316 { 00317 m_ePbPressed &= ~RateControl::RATERAMP_UP; 00318 m_ePbCurrent = m_ePbPressed; 00319 } 00320 } 00321 00322 void RateControl::slotControlRateTempUpSmall(double) 00323 { 00324 // Set the state of the Temporary button. Logic is handled in ::process() 00325 if (buttonRateTempUpSmall->get() && !(m_ePbPressed & RateControl::RATERAMP_UP)) 00326 { 00327 m_ePbPressed |= RateControl::RATERAMP_UP; 00328 m_ePbCurrent = RateControl::RATERAMP_UP; 00329 } 00330 else if (!buttonRateTempUpSmall->get()) 00331 { 00332 m_ePbPressed &= ~RateControl::RATERAMP_UP; 00333 m_ePbCurrent = m_ePbPressed; 00334 } 00335 } 00336 00337 double RateControl::getRawRate() { 00338 return m_pRateSlider->get() * 00339 m_pRateRange->get() * 00340 m_pRateDir->get(); 00341 } 00342 00343 double RateControl::getWheelFactor() { 00344 return m_pWheel->get(); 00345 } 00346 00347 double RateControl::getJogFactor() { 00348 // FIXME: Sensitivity should be configurable separately? 00349 const double jogSensitivity = 0.1; // Nudges during playback 00350 double jogValue = m_pJog->get(); 00351 00352 // Since m_pJog is an accumulator, reset it since we've used its value. 00353 if(jogValue != 0.) 00354 m_pJog->set(0.); 00355 00356 double jogValueFiltered = m_pJogFilter->filter(jogValue); 00357 double jogFactor = jogValueFiltered * jogSensitivity; 00358 00359 if (isnan(jogValue) || isnan(jogFactor)) { 00360 jogFactor = 0.0f; 00361 } 00362 00363 return jogFactor; 00364 } 00365 00366 double RateControl::calculateRate(double baserate, bool paused, int iSamplesPerBuffer, 00367 bool* isScratching) { 00368 double rate = 0.0; 00369 double wheelFactor = getWheelFactor(); 00370 double jogFactor = getJogFactor(); 00371 bool searching = m_pRateSearch->get() != 0.; 00372 bool scratchEnable = m_pScratchToggle->get() != 0 || m_bVinylControlEnabled; 00373 double scratchFactor = m_pScratch->get(); 00374 double oldScratchFactor = m_pOldScratch->get(); // Deprecated 00375 00376 // Don't trust values from m_pScratch 00377 if(isnan(scratchFactor)) { 00378 scratchFactor = 0.0; 00379 } 00380 if(isnan(oldScratchFactor)) { 00381 oldScratchFactor = 0.0; 00382 } 00383 00384 double currentSample = getCurrentSample(); 00385 m_pScratchController->process(currentSample, paused, iSamplesPerBuffer); 00386 00387 // If position control is enabled, override scratchFactor 00388 if (m_pScratchController->isEnabled()) { 00389 scratchEnable = true; 00390 scratchFactor = m_pScratchController->getRate(); 00391 *isScratching = true; 00392 } 00393 00394 if (searching) { 00395 // If searching is in progress, it overrides the playback rate. 00396 rate = m_pRateSearch->get(); 00397 } else if (paused) { 00398 // Stopped. Wheel, jog and scratch controller all scrub through audio. 00399 // New scratch behavior overrides old 00400 if (scratchEnable) rate = scratchFactor + jogFactor + wheelFactor*40.0; 00401 else rate = oldScratchFactor + jogFactor*18 + wheelFactor; // Just remove oldScratchFactor in future 00402 } else { 00403 // The buffer is playing, so calculate the buffer rate. 00404 00405 // There are four rate effects we apply: wheel, scratch, jog and temp. 00406 // Wheel: a linear additive effect (no spring-back) 00407 // Scratch: a rate multiplier 00408 // Jog: a linear additive effect whose value is filtered (springs back) 00409 // Temp: pitch bend 00410 00411 rate = 1. + getRawRate() + getTempRate(); 00412 rate += wheelFactor; 00413 00414 // New scratch behavior - overrides playback speed (and old behavior) 00415 if (scratchEnable) rate = scratchFactor; 00416 else { 00417 // Deprecated old scratch behavior 00418 if (oldScratchFactor < 0.) { 00419 rate *= (oldScratchFactor-1.); 00420 } else if (oldScratchFactor > 0.) { 00421 rate *= (oldScratchFactor+1.); 00422 } 00423 } 00424 00425 rate += jogFactor; 00426 00427 // If we are reversing (and not scratching,) flip the rate. 00428 if (!scratchEnable && m_pReverseButton->get()) { 00429 rate = -rate; 00430 } 00431 } 00432 00433 // Scale the rate by the engine samplerate 00434 rate *= baserate; 00435 00436 return rate; 00437 } 00438 00439 double RateControl::process(const double rate, 00440 const double currentSample, 00441 const double totalSamples, 00442 const int bufferSamples) 00443 { 00444 /* 00445 * Code to handle temporary rate change buttons. 00446 * 00447 * We support two behaviours, the standard ramped pitch bending 00448 * and pitch shift stepping, which is the old behaviour. 00449 */ 00450 00451 /* 00452 * Initialize certain values necessary for pitchbending. Most of this 00453 * code should be handled inside a slot, but we'd need to connect to 00454 * the troublesome Latency ControlObject... Either the Master or Soundcard 00455 * one. 00456 */ 00457 00458 double latrate = ((double)bufferSamples / (double)m_pSampleRate->get()); 00459 00460 00461 if ((m_ePbPressed) && (!m_bTempStarted)) 00462 { 00463 m_bTempStarted = true; 00464 00465 00466 if ( m_eRateRampMode == RATERAMP_STEP ) 00467 { 00468 // old temporary pitch shift behaviour 00469 double range = m_pRateRange->get(); 00470 00471 // Avoid Division by Zero 00472 if (range == 0) { 00473 qDebug() << "Avoiding a Division by Zero in RATERAMP_STEP code"; 00474 return kNoTrigger; 00475 } 00476 00477 double change = m_pRateDir->get() * m_dTemp / 00478 (100. * range); 00479 double csmall = m_pRateDir->get() * m_dTempSmall / 00480 (100. * range); 00481 00482 if (buttonRateTempUp->get()) 00483 addRateTemp(change); 00484 else if (buttonRateTempDown->get()) 00485 subRateTemp(change); 00486 else if (buttonRateTempUpSmall->get()) 00487 addRateTemp(csmall); 00488 else if (buttonRateTempDownSmall->get()) 00489 subRateTemp(csmall); 00490 } 00491 else 00492 { 00493 m_dTempRateChange = ((double)latrate / ((double)m_iRateRampSensitivity / 100.)); 00494 00495 if (m_eRampBackMode == RATERAMP_RAMPBACK_PERIOD) 00496 m_dRateTempRampbackChange = 0.0; 00497 } 00498 00499 } 00500 00501 if (m_eRateRampMode == RATERAMP_LINEAR) { 00502 00503 if (m_ePbCurrent) 00504 { 00505 // apply ramped pitchbending 00506 if ( m_ePbCurrent == RateControl::RATERAMP_UP ) 00507 addRateTemp(m_dTempRateChange); 00508 else if ( m_ePbCurrent == RateControl::RATERAMP_DOWN ) 00509 subRateTemp(m_dTempRateChange); 00510 } 00511 else if ((m_bTempStarted) || ((m_eRampBackMode != RATERAMP_RAMPBACK_NONE) && (m_dRateTemp != 0.0))) 00512 { 00513 // No buttons pressed, so time to deinitialize 00514 m_bTempStarted = false; 00515 00516 00517 if ((m_eRampBackMode == RATERAMP_RAMPBACK_PERIOD) && (m_dRateTempRampbackChange == 0.0)) 00518 { 00519 int period = 2; 00520 if (period) 00521 m_dRateTempRampbackChange = fabs(m_dRateTemp / (double)period); 00522 else { 00523 resetRateTemp(); 00524 return kNoTrigger; 00525 } 00526 00527 } 00528 else if ((m_eRampBackMode != RATERAMP_RAMPBACK_NONE) && (m_dRateTempRampbackChange == 0.0)) 00529 { 00530 00531 if ( fabs(m_dRateTemp) < m_dRateTempRampbackChange) 00532 resetRateTemp(); 00533 else if ( m_dRateTemp > 0 ) 00534 subRateTemp(m_dRateTempRampbackChange); 00535 else 00536 addRateTemp(m_dRateTempRampbackChange); 00537 } 00538 else 00539 resetRateTemp(); 00540 } 00541 } 00542 else if ((m_eRateRampMode == RATERAMP_STEP) && (m_bTempStarted)) 00543 { 00544 if (!m_ePbCurrent) { 00545 m_bTempStarted = false; 00546 resetRateTemp(); 00547 } 00548 } 00549 00550 return kNoTrigger; 00551 } 00552 00553 double RateControl::getTempRate() { 00554 return (m_pRateDir->get() * (m_dRateTemp * m_pRateRange->get())); 00555 } 00556 00557 void RateControl::setRateTemp(double v) 00558 { 00559 // Do not go backwards 00560 if (( 1. + getRawRate() + v ) < 0) 00561 return; 00562 00563 m_dRateTemp = v; 00564 if ( m_dRateTemp < -1.0 ) 00565 m_dRateTemp = -1.0; 00566 else if ( m_dRateTemp > 1.0 ) 00567 m_dRateTemp = 1.0; 00568 else if ( isnan(m_dRateTemp)) 00569 m_dRateTemp = 0; 00570 } 00571 00572 void RateControl::addRateTemp(double v) 00573 { 00574 setRateTemp(m_dRateTemp + v); 00575 } 00576 00577 void RateControl::subRateTemp(double v) 00578 { 00579 setRateTemp(m_dRateTemp - v); 00580 } 00581 00582 void RateControl::resetRateTemp(void) 00583 { 00584 setRateTemp(0.0); 00585 } 00586 00587 void RateControl::slotControlVinyl(double toggle) 00588 { 00589 m_bVinylControlEnabled = (bool)toggle; 00590 } 00591 00592 void RateControl::notifySeek(double playPos) { 00593 m_pScratchController->notifySeek(playPos); 00594 }