![]() |
Mixxx
|
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