Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/engine/ratecontrol.cpp

Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines