void CueControl::updateIndicators() { // No need for mutex lock because we are only touching COs. double cueMode = m_pCueMode->get(); if (cueMode == CUE_MODE_DENON || cueMode == CUE_MODE_NUMARK) { // Cue button is only lit at cue point bool playing = m_pPlay->toBool(); if (isTrackAtCue()) { // at cue point if (!playing) { m_pCueIndicator->setBlinkValue(ControlIndicator::ON); m_pPlayIndicator->setBlinkValue(ControlIndicator::OFF); } } else { m_pCueIndicator->setBlinkValue(ControlIndicator::OFF); if (!playing) { if (!atEndPosition() && cueMode != CUE_MODE_NUMARK) { // Play will move cue point m_pPlayIndicator->setBlinkValue(ControlIndicator::RATIO1TO1_500MS); } else { // At track end m_pPlayIndicator->setBlinkValue(ControlIndicator::OFF); } } } } else { // Here we have CUE_MODE_PIONEER or CUE_MODE_MIXXX // default to Pioneer mode if (!m_bPreviewing) { const auto freely_playing = m_pPlay->toBool() && !getEngineBuffer()->getScratching(); if (!freely_playing) { if (!isTrackAtCue()) { if (!atEndPosition()) { if (cueMode == CUE_MODE_MIXXX) { // in Mixxx mode Cue Button is flashing slow if CUE will move Cue point m_pCueIndicator->setBlinkValue(ControlIndicator::RATIO1TO1_500MS); } else if (cueMode == CUE_MODE_MIXXX_NO_BLINK) { m_pCueIndicator->setBlinkValue(ControlIndicator::OFF); } else { // in Pioneer mode Cue Button is flashing fast if CUE will move Cue point m_pCueIndicator->setBlinkValue(ControlIndicator::RATIO1TO1_250MS); } } else { // At track end m_pCueIndicator->setBlinkValue(ControlIndicator::OFF); } } else if (m_pCuePoint->get() != -1) { // Next Press is preview m_pCueIndicator->setBlinkValue(ControlIndicator::ON); } } else { // Cue indicator should be off when freely playing m_pCueIndicator->setBlinkValue(ControlIndicator::OFF); } } } }
void BpmControl::slotControlBeatSync(double v) { if (!v) return; // If the player is playing, and adjusting its tempo succeeded, adjust its // phase so that it plays in sync. if (syncTempo() && m_pPlayButton->get() > 0) { getEngineBuffer()->requestSyncPhase(); } }
void BpmControl::slotControlBeatSync(double v) { if (!v) return; if (!syncTempo()) { // syncTempo failed, nothing else to do return; } // Also sync phase if quantize is enabled. // this is used from controller scripts, where the latching behaviour of // the sync_enable CO cannot be used if (m_pPlayButton->toBool() && m_pQuantize->toBool()) { getEngineBuffer()->requestSyncPhase(); } }
void CueControl::cuePlay(double v) { // This is how CUP button works: // If freely playing (i.e. playing and platter NOT being touched), press to go to cue and stop. // If not freely playing (i.e. stopped or platter IS being touched), press to go to cue and stop. // On release, start playing from cue point. QMutexLocker lock(&m_mutex); const auto freely_playing = m_pPlay->toBool() && !getEngineBuffer()->getScratching(); // pressed if (v) { if (freely_playing) { m_bPreviewing = false; m_pPlay->set(0.0); // Need to unlock before emitting any signals to prevent deadlock. lock.unlock(); seekAbs(m_pCuePoint->get()); } else if (!isTrackAtCue() && getCurrentSample() <= getTotalSamples()) { // Pause not at cue point and not at end position cueSet(v); // Just in case. m_bPreviewing = false; m_pPlay->set(0.0); // If quantize is enabled, jump to the cue point since it's not // necessarily where we currently are if (m_pQuantizeEnabled->get() > 0.0) { lock.unlock(); // prevent deadlock. // Enginebuffer will quantize more exactly than we can. seekAbs(m_pCuePoint->get()); } } } else if (isTrackAtCue()){ m_bPreviewing = false; m_pPlay->set(1.0); lock.unlock(); } }
void BpmControl::slotControlBeatSyncPhase(double v) { if (!v) return; getEngineBuffer()->requestSyncPhase(); }
double BpmControl::getNearestPositionInPhase( double dThisPosition, bool respectLoops, bool playing) { // Without a beatgrid, we don't know the phase offset. BeatsPointer pBeats = m_pBeats; if (!pBeats) { return dThisPosition; } SyncMode syncMode = getSyncMode(); // Master buffer is always in sync! if (syncMode == SYNC_MASTER) { return dThisPosition; } // Get the current position of this deck. double dThisPrevBeat = m_pPrevBeat->get(); double dThisNextBeat = m_pNextBeat->get(); double dThisBeatLength; if (dThisPosition > dThisNextBeat || dThisPosition < dThisPrevBeat) { // There's a chance the COs might be out of date, so do a lookup. // TODO: figure out a way so that quantized control can take care of // this so this call isn't necessary. if (!getBeatContext(pBeats, dThisPosition, &dThisPrevBeat, &dThisNextBeat, &dThisBeatLength, NULL)) { return dThisPosition; } } else { if (!getBeatContextNoLookup(dThisPosition, dThisPrevBeat, dThisNextBeat, &dThisBeatLength, NULL)) { return dThisPosition; } } double dOtherBeatFraction; if (syncMode == SYNC_FOLLOWER) { // If we're a follower, it's easy to get the other beat fraction dOtherBeatFraction = m_dSyncTargetBeatDistance.getValue(); } else { // If not, we have to figure it out EngineBuffer* pOtherEngineBuffer = pickSyncTarget(); if (playing) { if (!pOtherEngineBuffer || pOtherEngineBuffer->getSpeed() == 0.0) { // "this" track is playing, or just starting // only match phase if the sync target is playing as well // else use the previouse phase of "this" track before the seek pOtherEngineBuffer = getEngineBuffer(); } } if (!pOtherEngineBuffer) { // no suitable sync buffer found return dThisPosition; } TrackPointer otherTrack = pOtherEngineBuffer->getLoadedTrack(); BeatsPointer otherBeats = otherTrack ? otherTrack->getBeats() : BeatsPointer(); // If either track does not have beats, then we can't adjust the phase. if (!otherBeats) { return dThisPosition; } double dOtherLength = ControlObject::getControl( ConfigKey(pOtherEngineBuffer->getGroup(), "track_samples"))->get(); double dOtherEnginePlayPos = pOtherEngineBuffer->getVisualPlayPos(); double dOtherPosition = dOtherLength * dOtherEnginePlayPos; if (!BpmControl::getBeatContext(otherBeats, dOtherPosition, NULL, NULL, NULL, &dOtherBeatFraction)) { return dThisPosition; } } bool this_near_next = dThisNextBeat - dThisPosition <= dThisPosition - dThisPrevBeat; bool other_near_next = dOtherBeatFraction >= 0.5; // We want our beat fraction to be identical to theirs. // If the two tracks have similar alignment, adjust phase is straight- // forward. Use the same fraction for both beats, starting from the previous // beat. But if This track is nearer to the next beat and the Other track // is nearer to the previous beat, use This Next beat as the starting point // for the phase. (ie, we pushed the sync button late). If This track // is nearer to the previous beat, but the Other track is nearer to the // next beat, we pushed the sync button early so use the double-previous // beat as the basis for the adjustment. // // This makes way more sense when you're actually mixing. // // TODO(XXX) Revisit this logic once we move away from tempo-locked, // infinite beatgrids because the assumption that findNthBeat(-2) always // works will be wrong then. double dNewPlaypos = (dOtherBeatFraction + m_dUserOffset.getValue()) * dThisBeatLength; if (this_near_next == other_near_next) { dNewPlaypos += dThisPrevBeat; } else if (this_near_next && !other_near_next) { dNewPlaypos += dThisNextBeat; } else { //!this_near_next && other_near_next dThisPrevBeat = pBeats->findNthBeat(dThisPosition, -2); dNewPlaypos += dThisPrevBeat; } if (respectLoops) { // We might be seeking outside the loop. const bool loop_enabled = m_pLoopEnabled->toBool(); const double loop_start_position = m_pLoopStartPosition->get(); const double loop_end_position = m_pLoopEndPosition->get(); // Cases for sanity: // // CASE 1 // Two identical 1-beat loops, out of phase by X samples. // Other deck is at its loop start. // This deck is half way through. We want to jump forward X samples to the loop end point. // // Two identical 1-beat loop, out of phase by X samples. // Other deck is // If sync target is 50% through the beat, // If we are at the loop end point and hit sync, jump forward X samples. // TODO(rryan): Revise this with something that keeps a broader number of // cases in sync. This at least prevents breaking out of the loop. if (loop_enabled && dThisPosition <= loop_end_position) { const double loop_length = loop_end_position - loop_start_position; const double end_delta = dNewPlaypos - loop_end_position; // Syncing to after the loop end. if (end_delta > 0 && loop_length > 0.0) { int i = end_delta / loop_length; dNewPlaypos = loop_start_position + end_delta - i * loop_length; // Move new position after loop jump into phase as well. // This is a recursive call, called only twice because of // respectLoops = false dNewPlaypos = getNearestPositionInPhase(dNewPlaypos, false, playing); } // Note: Syncing to before the loop beginning is allowed, because // loops are catching } } return dNewPlaypos; }
void CueControl::cueCDJ(double v) { // This is how Pioneer cue buttons work: // If pressed while freely playing (i.e. playing and platter NOT being touched), stop playback and go to cue. // If pressed while NOT freely playing (i.e. stopped or playing but platter IS being touched), set new cue point. // If pressed while stopped and at cue, play while pressed. // If play is pressed while holding cue, the deck is now playing. (Handled in playFromCuePreview().) QMutexLocker lock(&m_mutex); const auto freely_playing = m_pPlay->toBool() && !getEngineBuffer()->getScratching(); if (v) { if (m_iCurrentlyPreviewingHotcues) { // we are already previewing by hotcues // just jump to cue point and continue previewing m_bPreviewing = true; lock.unlock(); seekAbs(m_pCuePoint->get()); } else if (freely_playing || atEndPosition()) { // Jump to cue when playing or when at end position // Just in case. m_bPreviewing = false; m_pPlay->set(0.0); // Need to unlock before emitting any signals to prevent deadlock. lock.unlock(); seekAbs(m_pCuePoint->get()); } else if (isTrackAtCue()) { // pause at cue point m_bPreviewing = true; m_pPlay->set(1.0); } else { // Pause not at cue point and not at end position cueSet(v); // Just in case. m_bPreviewing = false; m_pPlay->set(0.0); // If quantize is enabled, jump to the cue point since it's not // necessarily where we currently are if (m_pQuantizeEnabled->get() > 0.0) { lock.unlock(); // prevent deadlock. // Enginebuffer will quantize more exactly than we can. seekAbs(m_pCuePoint->get()); } } } else if (m_bPreviewing) { m_bPreviewing = false; if (!m_iCurrentlyPreviewingHotcues) { m_pPlay->set(0.0); // Need to unlock before emitting any signals to prevent deadlock. lock.unlock(); seekAbs(m_pCuePoint->get()); } } // indicator may flash because the delayed adoption of seekAbs // Correct the Indicator set via play if (m_pLoadedTrack && !freely_playing) { m_pCueIndicator->setBlinkValue(ControlIndicator::ON); } else { m_pCueIndicator->setBlinkValue(ControlIndicator::OFF); } }