Mixxx

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

Go to the documentation of this file.
00001 // loopingcontrol.cpp
00002 // Created on Sep 23, 2008
00003 // Author: asantoni, rryan
00004 
00005 #include <QtDebug>
00006 #include <QObject>
00007 
00008 #include "controlobject.h"
00009 #include "configobject.h"
00010 #include "controlpushbutton.h"
00011 #include "cachingreader.h"
00012 #include "engine/loopingcontrol.h"
00013 #include "engine/enginecontrol.h"
00014 #include "mathstuff.h"
00015 
00016 #include "trackinfoobject.h"
00017 #include "track/beats.h"
00018 
00019 double LoopingControl::s_dBeatSizes[] = { 0.0625, 0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, };
00020 
00021 LoopingControl::LoopingControl(const char * _group,
00022                                ConfigObject<ConfigValue> * _config)
00023         : EngineControl(_group, _config) {
00024     m_bLoopingEnabled = false;
00025     m_iLoopStartSample = kNoTrigger;
00026     m_iLoopEndSample = kNoTrigger;
00027     m_iCurrentSample = 0.;
00028     m_pActiveBeatLoop = NULL;
00029 
00030     //Create loop-in, loop-out, and reloop/exit ControlObjects
00031     m_pLoopInButton = new ControlPushButton(ConfigKey(_group, "loop_in"));
00032     connect(m_pLoopInButton, SIGNAL(valueChanged(double)),
00033             this, SLOT(slotLoopIn(double)),
00034             Qt::DirectConnection);
00035     m_pLoopInButton->set(0);
00036 
00037     m_pLoopOutButton = new ControlPushButton(ConfigKey(_group, "loop_out"));
00038     connect(m_pLoopOutButton, SIGNAL(valueChanged(double)),
00039             this, SLOT(slotLoopOut(double)),
00040             Qt::DirectConnection);
00041     m_pLoopOutButton->set(0);
00042 
00043     m_pReloopExitButton = new ControlPushButton(ConfigKey(_group, "reloop_exit"));
00044     connect(m_pReloopExitButton, SIGNAL(valueChanged(double)),
00045             this, SLOT(slotReloopExit(double)),
00046             Qt::DirectConnection);
00047     m_pReloopExitButton->set(0);
00048 
00049 
00050     m_pCOLoopEnabled = new ControlObject(ConfigKey(_group, "loop_enabled"));
00051     m_pCOLoopEnabled->set(0.0f);
00052 
00053     m_pCOLoopStartPosition =
00054             new ControlObject(ConfigKey(_group, "loop_start_position"));
00055     m_pCOLoopStartPosition->set(kNoTrigger);
00056     connect(m_pCOLoopStartPosition, SIGNAL(valueChanged(double)),
00057             this, SLOT(slotLoopStartPos(double)),
00058             Qt::DirectConnection);
00059 
00060     m_pCOLoopEndPosition =
00061             new ControlObject(ConfigKey(_group, "loop_end_position"));
00062     m_pCOLoopEndPosition->set(kNoTrigger);
00063     connect(m_pCOLoopEndPosition, SIGNAL(valueChanged(double)),
00064             this, SLOT(slotLoopEndPos(double)),
00065             Qt::DirectConnection);
00066 
00067     m_pQuantizeEnabled = ControlObject::getControl(ConfigKey(_group, "quantize"));
00068     m_pNextBeat = ControlObject::getControl(ConfigKey(_group, "beat_next"));
00069     m_pClosestBeat = ControlObject::getControl(ConfigKey(_group, "beat_closest"));
00070     m_pTrackSamples = ControlObject::getControl(ConfigKey(_group,"track_samples"));
00071 
00072     // Connect beatloop, which can flexibly handle different values.
00073     // Using this CO directly is meant to be used internally and by scripts,
00074     // or anything else that can pass in arbitrary values.
00075     m_pCOBeatLoop = new ControlPushButton(ConfigKey(_group, "beatloop"));
00076     connect(m_pCOBeatLoop, SIGNAL(valueChanged(double)), this,
00077             SLOT(slotBeatLoop(double)), Qt::DirectConnection);
00078 
00079 
00080     // Here we create corresponding beatloop_(SIZE) CO's which all call the same
00081     // BeatControl, but with a set value.
00082     for (unsigned int i = 0; i < (sizeof(s_dBeatSizes) / sizeof(s_dBeatSizes[0])); ++i) {
00083         BeatLoopingControl* pBeatLoop = new BeatLoopingControl(_group, s_dBeatSizes[i]);
00084         connect(pBeatLoop, SIGNAL(activateBeatLoop(BeatLoopingControl*)),
00085                 this, SLOT(slotBeatLoopActivate(BeatLoopingControl*)),
00086                 Qt::DirectConnection);
00087         connect(pBeatLoop, SIGNAL(deactivateBeatLoop(BeatLoopingControl*)),
00088                 this, SLOT(slotBeatLoopDeactivate(BeatLoopingControl*)),
00089                 Qt::DirectConnection);
00090         m_beatLoops.append(pBeatLoop);
00091     }
00092 
00093     m_pCOLoopScale = new ControlObject(ConfigKey(_group, "loop_scale"));
00094     connect(m_pCOLoopScale, SIGNAL(valueChanged(double)),
00095             this, SLOT(slotLoopScale(double)));
00096     m_pLoopHalveButton = new ControlPushButton(ConfigKey(_group, "loop_halve"));
00097     connect(m_pLoopHalveButton, SIGNAL(valueChanged(double)),
00098             this, SLOT(slotLoopHalve(double)));
00099     m_pLoopDoubleButton = new ControlPushButton(ConfigKey(_group, "loop_double"));
00100     connect(m_pLoopDoubleButton, SIGNAL(valueChanged(double)),
00101             this, SLOT(slotLoopDouble(double)));
00102 }
00103 
00104 LoopingControl::~LoopingControl() {
00105     delete m_pLoopOutButton;
00106     delete m_pLoopInButton;
00107     delete m_pReloopExitButton;
00108     delete m_pCOLoopEnabled;
00109     delete m_pCOLoopStartPosition;
00110     delete m_pCOLoopEndPosition;
00111     delete m_pCOLoopScale;
00112     delete m_pLoopHalveButton;
00113     delete m_pLoopDoubleButton;
00114     delete m_pCOBeatLoop;
00115 
00116     while (m_beatLoops.size() > 0) {
00117         BeatLoopingControl* pBeatLoop = m_beatLoops.takeLast();
00118         delete pBeatLoop;
00119     }
00120 }
00121 
00122 void LoopingControl::slotLoopScale(double scale) {
00123     int loop_length = m_iLoopEndSample - m_iLoopStartSample;
00124     int samples = m_pTrackSamples->get();
00125     loop_length *= scale;
00126 
00127     // Abandon loops that are too short of extend beyond the end of the file.
00128     if (loop_length < MINIMUM_AUDIBLE_LOOP_SIZE ||
00129         m_iLoopStartSample + loop_length > m_pTrackSamples->get()) {
00130         return;
00131     }
00132 
00133     m_iLoopEndSample = m_iLoopStartSample + loop_length;
00134 
00135     if (!even(m_iLoopEndSample)) {
00136         m_iLoopEndSample--;
00137     }
00138 
00139     // TODO(XXX) we could be smarter about taking the active beatloop, scaling
00140     // it by the desired amount and trying to find another beatloop that matches
00141     // it, but for now we just clear the active beat loop if somebody scales.
00142     clearActiveBeatLoop();
00143 
00144     // Don't allow 0 samples loop, so one can still manipulate it
00145     if (m_iLoopEndSample == m_iLoopStartSample) {
00146         if ((m_iLoopEndSample+2) >= samples)
00147             m_iLoopStartSample -= 2;
00148         else
00149             m_iLoopEndSample += 2;
00150     }
00151     // Do not allow loops to go past the end of the song
00152     else if (m_iLoopEndSample > samples)
00153         m_iLoopEndSample = samples;
00154 
00155     // Update CO for loop end marker
00156     m_pCOLoopEndPosition->set(m_iLoopEndSample);
00157 }
00158 
00159 void LoopingControl::slotLoopHalve(double v) {
00160     if (v > 0.0) {
00161         // If a beatloop is active then halve should deactive the current
00162         // beatloop and activate the previous one.
00163         if (m_pActiveBeatLoop != NULL) {
00164             int active_index = m_beatLoops.indexOf(m_pActiveBeatLoop);
00165             if (active_index - 1 >= 0) {
00166                 if (m_bLoopingEnabled) {
00167                     slotBeatLoopActivate(m_beatLoops[active_index - 1]);
00168                 } else {
00169                     // Calling scale clears the active beatloop.
00170                     slotLoopScale(0.5);
00171                     m_pActiveBeatLoop = m_beatLoops[active_index - 1];
00172                 }
00173             }
00174         } else {
00175             slotLoopScale(0.5);
00176         }
00177     }
00178 }
00179 
00180 void LoopingControl::slotLoopDouble(double v) {
00181     if (v > 0.0f) {
00182         // If a beatloop is active then double should deactive the current
00183         // beatloop and activate the next one.
00184         if (m_pActiveBeatLoop != NULL) {
00185             int active_index = m_beatLoops.indexOf(m_pActiveBeatLoop);
00186             if (active_index + 1 < m_beatLoops.size()) {
00187                 if (m_bLoopingEnabled) {
00188                     slotBeatLoopActivate(m_beatLoops[active_index + 1]);
00189                 } else {
00190                     // Calling scale clears the active beatloop.
00191                     slotLoopScale(2.0);
00192                     m_pActiveBeatLoop = m_beatLoops[active_index + 1];
00193                 }
00194             }
00195         } else {
00196             slotLoopScale(2.0);
00197         }
00198     }
00199 }
00200 
00201 double LoopingControl::process(const double dRate,
00202                                const double currentSample,
00203                                const double totalSamples,
00204                                const int iBufferSize) {
00205     m_iCurrentSample = currentSample;
00206     if (!even(m_iCurrentSample))
00207         m_iCurrentSample--;
00208 
00209     bool reverse = dRate < 0;
00210 
00211     double retval = kNoTrigger;
00212     if(m_bLoopingEnabled &&
00213        m_iLoopStartSample != kNoTrigger &&
00214        m_iLoopEndSample != kNoTrigger) {
00215         bool outsideLoop = ((!reverse && currentSample > m_iLoopEndSample) ||
00216                             (reverse && currentSample < m_iLoopStartSample));
00217         if (outsideLoop) {
00218             retval = reverse ? m_iLoopEndSample : m_iLoopStartSample;
00219         }
00220     }
00221 
00222     return retval;
00223 }
00224 
00225 double LoopingControl::nextTrigger(const double dRate,
00226                                    const double currentSample,
00227                                    const double totalSamples,
00228                                    const int iBufferSize) {
00229     bool bReverse = dRate < 0;
00230 
00231     if(m_bLoopingEnabled) {
00232         if (bReverse)
00233             return m_iLoopStartSample;
00234         else
00235             return m_iLoopEndSample;
00236     }
00237     return kNoTrigger;
00238 }
00239 
00240 double LoopingControl::getTrigger(const double dRate,
00241                                   const double currentSample,
00242                                   const double totalSamples,
00243                                   const int iBufferSize) {
00244     bool bReverse = dRate < 0;
00245 
00246     if(m_bLoopingEnabled) {
00247         if (bReverse)
00248             return m_iLoopEndSample;
00249         else
00250             return m_iLoopStartSample;
00251     }
00252     return kNoTrigger;
00253 }
00254 
00255 void LoopingControl::hintReader(QList<Hint>& hintList) {
00256     Hint loop_hint;
00257     // If the loop is enabled, then this is high priority because we will loop
00258     // sometime potentially very soon! The current audio itself is priority 1,
00259     // but we will issue ourselves at priority 2.
00260     if (m_bLoopingEnabled) {
00261         // If we're looping, hint the loop in and loop out, in case we reverse
00262         // into it. We could save information from process to tell which
00263         // direction we're going in, but that this is much simpler, and hints
00264         // aren't that bad to make anyway.
00265         if (m_iLoopStartSample >= 0) {
00266             loop_hint.priority = 2;
00267             loop_hint.sample = m_iLoopStartSample;
00268             loop_hint.length = 0; // Let it issue the default length
00269             hintList.append(loop_hint);
00270         }
00271         if (m_iLoopEndSample >= 0) {
00272             loop_hint.priority = 10;
00273             loop_hint.sample = m_iLoopEndSample;
00274             loop_hint.length = -1; // Let it issue the default (backwards) length
00275             hintList.append(loop_hint);
00276         }
00277     } else {
00278         if (m_iLoopStartSample >= 0) {
00279             loop_hint.priority = 10;
00280             loop_hint.sample = m_iLoopStartSample;
00281             loop_hint.length = 0; // Let it issue the default length
00282             hintList.append(loop_hint);
00283         }
00284     }
00285 }
00286 
00287 void LoopingControl::slotLoopIn(double val) {
00288     if (!m_pTrack) {
00289         return;
00290     }
00291     if (val) {
00292         clearActiveBeatLoop();
00293 
00294         // set loop-in position
00295         int pos =
00296                 (m_pQuantizeEnabled->get() > 0.0 && m_pClosestBeat->get() != -1) ?
00297                 static_cast<int>(floorf(m_pClosestBeat->get())) : m_iCurrentSample;
00298 
00299         // If we're looping and the loop-in and out points are now so close
00300         //  that the loop would be inaudible (which can happen easily with
00301         //  quantize-to-beat enabled,) set the in point to the smallest
00302         //  pre-defined beatloop size instead (when possible)
00303         if (m_bLoopingEnabled &&
00304             (m_iLoopEndSample - pos) < MINIMUM_AUDIBLE_LOOP_SIZE) {
00305             pos = m_iLoopEndSample;
00306             if (m_pQuantizeEnabled->get() > 0.0 && m_pBeats) {
00307                 // 1 would have just returned loop_in, so give 2 to get the beat
00308                 // following loop_in
00309                 int nextbeat = m_pBeats->findNthBeat(pos, 2);
00310                 pos -= (nextbeat - pos) * s_dBeatSizes[0];
00311             }
00312             else pos -= MINIMUM_AUDIBLE_LOOP_SIZE;
00313         }
00314 
00315         if (pos != -1 && !even(pos)) {
00316             pos--;
00317         }
00318 
00319         m_iLoopStartSample = pos;
00320         m_pCOLoopStartPosition->set(m_iLoopStartSample);
00321 
00322         // Reset the loop out position if it is before the loop in so that loops
00323         // cannot be inverted.
00324         if (m_iLoopEndSample != -1 &&
00325             m_iLoopEndSample < m_iLoopStartSample) {
00326             m_iLoopEndSample = -1;
00327             m_pCOLoopEndPosition->set(kNoTrigger);
00328         }
00329 //         qDebug() << "set loop_in to " << m_iLoopStartSample;
00330     }
00331 }
00332 
00333 void LoopingControl::slotLoopOut(double val) {
00334     if (!m_pTrack) {
00335         return;
00336     }
00337     if (val) {
00338         int pos =
00339                 (m_pQuantizeEnabled->get() > 0.0 && m_pClosestBeat->get() != -1) ?
00340                 static_cast<int>(floorf(m_pClosestBeat->get())) : m_iCurrentSample;
00341 
00342         // If the user is trying to set a loop-out before the loop in or without
00343         // having a loop-in, then ignore it.
00344         if (m_iLoopStartSample == -1 || pos < m_iLoopStartSample) {
00345             return;
00346         }
00347 
00348         // If the loop-in and out points are set so close that the loop would be
00349         //  inaudible (which can happen easily with quantize-to-beat enabled,)
00350         //  use the smallest pre-defined beatloop instead (when possible)
00351         if (pos - m_iLoopStartSample < MINIMUM_AUDIBLE_LOOP_SIZE) {
00352             pos = m_iLoopStartSample;
00353             if (m_pQuantizeEnabled->get() > 0.0 && m_pBeats) {
00354                 // 1 would have just returned loop_in, so give 2 to get the beat
00355                 // following loop_in
00356                 int nextbeat = m_pBeats->findNthBeat(m_iLoopStartSample, 2);
00357                 pos += (nextbeat - pos) * s_dBeatSizes[0];
00358             } else {
00359                 pos += MINIMUM_AUDIBLE_LOOP_SIZE;
00360             }
00361         }
00362 
00363         if (pos != -1 && !even(pos)) {
00364             pos++;  // Increment to avoid shortening too-short loops
00365         }
00366 
00367         clearActiveBeatLoop();
00368 
00369         //set loop out position
00370         m_iLoopEndSample = pos;
00371         m_pCOLoopEndPosition->set(m_iLoopEndSample);
00372 
00373         // start looping
00374         if (m_iLoopStartSample != -1 &&
00375             m_iLoopEndSample != -1) {
00376             setLoopingEnabled(true);
00377         }
00378 //         qDebug() << "set loop_out to " << m_iLoopEndSample;
00379     }
00380 }
00381 
00382 void LoopingControl::slotReloopExit(double val) {
00383     if (!m_pTrack) {
00384         return;
00385     }
00386     if (val) {
00387         // If we're looping, stop looping
00388         if (m_bLoopingEnabled) {
00389             setLoopingEnabled(false);
00390             //qDebug() << "reloop_exit looping off";
00391         } else {
00392             // If we're not looping, jump to the loop-in point and start looping
00393             if (m_iLoopStartSample != -1 && m_iLoopEndSample != -1 &&
00394                 m_iLoopStartSample <= m_iLoopEndSample) {
00395                 setLoopingEnabled(true);
00396             }
00397             //qDebug() << "reloop_exit looping on";
00398         }
00399     }
00400 }
00401 
00402 void LoopingControl::slotLoopStartPos(double pos) {
00403     if (!m_pTrack) {
00404         return;
00405     }
00406 
00407     int newpos = pos;
00408     if (newpos != -1 && !even(newpos)) {
00409         newpos--;
00410     }
00411 
00412     clearActiveBeatLoop();
00413 
00414     if (pos == -1.0f) {
00415         setLoopingEnabled(false);
00416     }
00417 
00418     m_iLoopStartSample = newpos;
00419 
00420     if (m_iLoopEndSample != -1 &&
00421         m_iLoopEndSample < m_iLoopStartSample) {
00422         m_iLoopEndSample = -1;
00423         m_pCOLoopEndPosition->set(kNoTrigger);
00424         setLoopingEnabled(false);
00425     }
00426 }
00427 
00428 void LoopingControl::slotLoopEndPos(double pos) {
00429     if (!m_pTrack) {
00430         return;
00431     }
00432 
00433     int newpos = pos;
00434     if (newpos != -1 && !even(newpos)) {
00435         newpos--;
00436     }
00437 
00438     // Reject if the loop-in is not set, or if the new position is before the
00439     // start point (but not -1).
00440     if (m_iLoopStartSample == -1 ||
00441         (newpos != -1 && newpos < m_iLoopStartSample)) {
00442         return;
00443     }
00444 
00445     clearActiveBeatLoop();
00446 
00447     if (pos == -1.0f) {
00448         setLoopingEnabled(false);
00449     }
00450     m_iLoopEndSample = newpos;
00451 }
00452 
00453 void LoopingControl::notifySeek(double dNewPlaypos) {
00454     if (m_bLoopingEnabled) {
00455         Q_ASSERT(m_iLoopStartSample != -1);
00456         Q_ASSERT(m_iLoopEndSample != -1);
00457         if (dNewPlaypos < m_iLoopStartSample || dNewPlaypos > m_iLoopEndSample) {
00458             setLoopingEnabled(false);
00459         }
00460     }
00461 }
00462 
00463 void LoopingControl::setLoopingEnabled(bool enabled) {
00464     m_bLoopingEnabled = enabled;
00465     m_pCOLoopEnabled->set(enabled);
00466     if (m_pActiveBeatLoop != NULL) {
00467         if (enabled) {
00468             m_pActiveBeatLoop->activate();
00469         } else {
00470             m_pActiveBeatLoop->deactivate();
00471         }
00472     }
00473 }
00474 
00475 void LoopingControl::trackLoaded(TrackPointer pTrack) {
00476     if (m_pTrack) {
00477         trackUnloaded(m_pTrack);
00478     }
00479 
00480     clearActiveBeatLoop();
00481 
00482     if (pTrack) {
00483         m_pTrack = pTrack;
00484         m_pBeats = m_pTrack->getBeats();
00485         connect(m_pTrack.data(), SIGNAL(beatsUpdated()),
00486                 this, SLOT(slotUpdatedTrackBeats()));
00487     }
00488 }
00489 
00490 void LoopingControl::trackUnloaded(TrackPointer pTrack) {
00491     if (m_pTrack) {
00492         disconnect(m_pTrack.data(), SIGNAL(beatsUpdated()),
00493                    this, SLOT(slotUpdatedTrackBeats()));
00494     }
00495     m_pTrack.clear();
00496     m_pBeats.clear();
00497     clearActiveBeatLoop();
00498 }
00499 
00500 void LoopingControl::slotUpdatedTrackBeats()
00501 {
00502     if (m_pTrack) {
00503         m_pBeats = m_pTrack->getBeats();
00504     }
00505 }
00506 
00507 void LoopingControl::slotBeatLoopActivate(BeatLoopingControl* pBeatLoopControl) {
00508     if (!m_pTrack) {
00509         return;
00510     }
00511 
00512     // Maintain the current start point if there is an active beat loop and we
00513     // are currently looping. slotBeatLoop will update m_pActiveBeatLoop if
00514     // applicable
00515     bool beatLoopAlreadyActive = m_pActiveBeatLoop != NULL;
00516     slotBeatLoop(pBeatLoopControl->getSize(),
00517                  beatLoopAlreadyActive && m_bLoopingEnabled);
00518 }
00519 
00520 void LoopingControl::slotBeatLoopDeactivate(BeatLoopingControl* pBeatLoopControl) {
00521     slotReloopExit(1);
00522 }
00523 
00524 void LoopingControl::clearActiveBeatLoop() {
00525     if (m_pActiveBeatLoop != NULL) {
00526         m_pActiveBeatLoop->deactivate();
00527         m_pActiveBeatLoop = NULL;
00528     }
00529 }
00530 
00531 void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint) {
00532     if (!m_pTrack) {
00533         return;
00534     }
00535 
00536     // O(n) search, but there are only ~10-ish beatloop controls so this is
00537     // fine.
00538     foreach (BeatLoopingControl* pBeatLoopControl, m_beatLoops) {
00539         if (pBeatLoopControl->getSize() == beats) {
00540             if (m_pActiveBeatLoop &&
00541                 m_pActiveBeatLoop != pBeatLoopControl) {
00542                 m_pActiveBeatLoop->deactivate();
00543             }
00544             m_pActiveBeatLoop = pBeatLoopControl;
00545             pBeatLoopControl->activate();
00546         }
00547     }
00548 
00549     // give loop_in and loop_out defaults so we can detect problems
00550     int loop_in = -1;
00551     int loop_out = -1;
00552     int samples = m_pTrackSamples->get();
00553 
00554     if (!m_pBeats) {
00555         clearActiveBeatLoop();
00556         return;
00557     }
00558 
00559     // For now we do not handle negative beatloops.
00560     if (beats < 0) {
00561         clearActiveBeatLoop();
00562         return;
00563     }
00564 
00565     // For positive numbers we start from the current position/closest beat and
00566     // create the loop around X beats from there.
00567     if (beats > 0) {
00568         if (keepStartPoint) {
00569             loop_in = m_iLoopStartSample;
00570         } else {
00571             // loop_in is set to the closest beat if quantize is on
00572             double currentClosestBeat =
00573                     floorf(m_pBeats->findClosestBeat(getCurrentSample()));
00574             loop_in = (m_pQuantizeEnabled->get() > 0.0 && currentClosestBeat != -1) ?
00575                     currentClosestBeat : floorf(getCurrentSample());
00576             if (!even(loop_in)) {
00577                 loop_in--;
00578             }
00579         }
00580 
00581         int fullbeats = static_cast<int>(floorf(beats));
00582         double fracbeats = beats - static_cast<double>(fullbeats);
00583 
00584         // Now we need to calculate the length of the beatloop. We do this by
00585         // taking the current beat and the fullbeats'th beat and measuring the
00586         // distance between them.
00587         loop_out = loop_in;
00588 
00589         if (fullbeats > 0) {
00590             // Add the length between this beat and the fullbeats'th beat to the
00591             // loop_out position;
00592             double this_beat = m_pBeats->findNthBeat(loop_in, 1);
00593             double nth_beat = m_pBeats->findNthBeat(loop_in, 1 + fullbeats);
00594             loop_out += (nth_beat - this_beat);
00595         }
00596 
00597         if (fracbeats > 0) {
00598             // Add the fraction of the beat following the current loop_out
00599             // position to loop out.
00600             double loop_out_beat = m_pBeats->findNthBeat(loop_out, 1);
00601             double loop_out_next_beat = m_pBeats->findNthBeat(loop_out, 2);
00602             loop_out += (loop_out_next_beat - loop_out_beat) * fracbeats;
00603         }
00604     }
00605 
00606     if ((loop_in == -1) || ( loop_out == -1))
00607         return;
00608 
00609     if (!even(loop_in))
00610         loop_in--;
00611     if (!even(loop_out))
00612         loop_out--;
00613 
00614     if (loop_in == loop_out) {
00615         if ((loop_out+2) > samples) {
00616             loop_in -= 2;
00617         } else {
00618             loop_out += 2;
00619         }
00620     } else if (loop_out > samples) {
00621         // Do not allow beat loops to go beyond the end of the track
00622         loop_out = samples;
00623     }
00624 
00625     m_iLoopStartSample = loop_in;
00626     m_pCOLoopStartPosition->set(loop_in);
00627     m_iLoopEndSample = loop_out;
00628     m_pCOLoopEndPosition->set(loop_out);
00629     setLoopingEnabled(true);
00630 }
00631 
00632 BeatLoopingControl::BeatLoopingControl(const char* pGroup, double size)
00633         : m_dBeatLoopSize(size),
00634           m_bActive(false) {
00635     // This is the original beatloop control which is now deprecated. Its value
00636     // is the state of the beatloop control (1 for enabled, 0 for disabled).
00637     m_pLegacy = new ControlPushButton(
00638         keyForControl(pGroup, "beatloop_%1", size));
00639     m_pLegacy->setStates(2);
00640     m_pLegacy->setToggleButton(true);
00641     connect(m_pLegacy, SIGNAL(valueChanged(double)),
00642             this, SLOT(slotLegacy(double)),
00643             Qt::DirectConnection);
00644     // A push-button which activates the beatloop.
00645     m_pActivate = new ControlPushButton(
00646         keyForControl(pGroup, "beatloop_%1_activate", size));
00647     connect(m_pActivate, SIGNAL(valueChanged(double)),
00648             this, SLOT(slotActivate(double)),
00649             Qt::DirectConnection);
00650     // A push-button which toggles the beatloop as active or inactive.
00651     m_pToggle = new ControlPushButton(
00652         keyForControl(pGroup, "beatloop_%1_toggle", size));
00653     connect(m_pToggle, SIGNAL(valueChanged(double)),
00654             this, SLOT(slotToggle(double)),
00655             Qt::DirectConnection);
00656 
00657     // An indicator control which is 1 if the beatloop is enabled and 0 if not.
00658     m_pEnabled = new ControlObject(
00659         keyForControl(pGroup, "beatloop_%1_enabled", size));
00660 }
00661 
00662 BeatLoopingControl::~BeatLoopingControl() {
00663     delete m_pActivate;
00664     delete m_pToggle;
00665     delete m_pEnabled;
00666     delete m_pLegacy;
00667 }
00668 
00669 void BeatLoopingControl::deactivate() {
00670     m_bActive = false;
00671     m_pEnabled->set(0);
00672     m_pLegacy->set(0);
00673 }
00674 
00675 void BeatLoopingControl::activate() {
00676     m_bActive = true;
00677     m_pEnabled->set(1);
00678     m_pLegacy->set(1);
00679 }
00680 
00681 void BeatLoopingControl::slotLegacy(double v) {
00682     //qDebug() << "slotLegacy" << m_dBeatLoopSize << "v" << v;
00683     if (v > 0) {
00684         emit(activateBeatLoop(this));
00685     } else {
00686         emit(deactivateBeatLoop(this));
00687     }
00688 }
00689 
00690 void BeatLoopingControl::slotActivate(double v) {
00691     //qDebug() << "slotActivate" << m_dBeatLoopSize << "v" << v;
00692     if (!v) {
00693         return;
00694     }
00695     emit(activateBeatLoop(this));
00696 }
00697 
00698 void BeatLoopingControl::slotToggle(double v) {
00699     //qDebug() << "slotToggle" << m_dBeatLoopSize << "v" << v;
00700     if (!v) {
00701         return;
00702     }
00703     if (m_bActive) {
00704         emit(deactivateBeatLoop(this));
00705     } else {
00706         emit(activateBeatLoop(this));
00707     }
00708 }
00709 
00710 ConfigKey BeatLoopingControl::keyForControl(const char* pGroup,
00711                                             QString ctrlName, double num) {
00712     ConfigKey key;
00713     key.group = pGroup;
00714     key.item = ctrlName.arg(num);
00715     return key;
00716 }
00717 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines