void CueControl::hotcueGotoAndPlay(HotcueControl* pControl, double v) { if (!v) return; QMutexLocker lock(&m_mutex); if (!m_pLoadedTrack) { return; } CuePointer pCue(pControl->getCue()); // Need to unlock before emitting any signals to prevent deadlock. lock.unlock(); if (pCue) { int position = pCue->getPosition(); if (position != -1) { seekAbs(position); if (!isPlayingByPlayButton()) { // cueGoto is processed asynchrony. // avoid a wrong cue set if seek by cueGoto is still pending m_bPreviewing = false; m_iCurrentlyPreviewingHotcues = 0; // don't move the cue point to the hot cue point in DENON mode m_bypassCueSetByPlay = true; m_pPlay->set(1.0); } } } }
void CueControl::hotcueSet(HotcueControl* pControl, double v) { //qDebug() << "CueControl::hotcueSet" << v; if (!v) return; QMutexLocker lock(&m_mutex); if (!m_pLoadedTrack) return; int hotcue = pControl->getHotcueNumber(); hotcueClear(pControl, v); CuePointer pCue(m_pLoadedTrack->addCue()); double cuePosition = (m_pQuantizeEnabled->get() > 0.0 && m_pClosestBeat->get() != -1) ? floor(m_pClosestBeat->get()) : floor(getCurrentSample()); if (!even(static_cast<int>(cuePosition))) cuePosition--; pCue->setPosition(cuePosition); pCue->setHotCue(hotcue); pCue->setLabel(""); pCue->setType(Cue::CUE); // TODO(XXX) deal with spurious signals attachCue(pCue, hotcue); // If quantize is enabled and we are not playing, jump to the cue point // since it's not necessarily where we currently are. TODO(XXX) is this // potentially invalid for vinyl control? bool playing = m_pPlayButton->toBool(); if (!playing && m_pQuantizeEnabled->get() > 0.0) { lock.unlock(); // prevent deadlock. // Enginebuffer will quantize more exactly than we can. seekAbs(cuePosition); } }
void BaseTrackPlayerImpl::loadTrack(TrackPointer pTrack) { DEBUG_ASSERT(!m_pLoadedTrack); m_pLoadedTrack = std::move(pTrack); if (!m_pLoadedTrack) { // nothing to return; } // Clear loop // It seems that the trick is to first clear the loop out point, and then // the loop in point. If we first clear the loop in point, the loop out point // does not get cleared. m_pLoopOutPoint->set(kNoTrigger); m_pLoopInPoint->set(kNoTrigger); // The loop in and out points must be set here and not in slotTrackLoaded // so LoopingControl::trackLoaded can access them. const QList<CuePointer> trackCues(m_pLoadedTrack->getCuePoints()); QListIterator<CuePointer> it(trackCues); while (it.hasNext()) { CuePointer pCue(it.next()); if (pCue->getType() == Cue::LOOP) { double loopStart = pCue->getPosition(); double loopEnd = loopStart + pCue->getLength(); if (loopStart != kNoTrigger && loopEnd != kNoTrigger && loopStart <= loopEnd) { m_pLoopInPoint->set(loopStart); m_pLoopOutPoint->set(loopEnd); break; } } } connectLoadedTrack(); }
void CueControl::trackCuesUpdated() { QMutexLocker lock(&m_mutex); QSet<int> active_hotcues; if (!m_pLoadedTrack) return; const QList<CuePointer> cuePoints(m_pLoadedTrack->getCuePoints()); QListIterator<CuePointer> it(cuePoints); while (it.hasNext()) { CuePointer pCue(it.next()); if (pCue->getType() != Cue::CUE && pCue->getType() != Cue::LOAD) continue; int hotcue = pCue->getHotCue(); if (hotcue != -1) { HotcueControl* pControl = m_hotcueControl.value(hotcue, NULL); // Cue's hotcue doesn't have a hotcue control. if (pControl == NULL) { continue; } CuePointer pOldCue(pControl->getCue()); // If the old hotcue is different than this one. if (pOldCue != pCue) { // If the old hotcue exists, detach it if (pOldCue) { detachCue(hotcue); } attachCue(pCue, hotcue); } else { // If the old hotcue is the same, then we only need to update double dOldPosition = pControl->getPosition()->get(); double dOldEnabled = pControl->getEnabled()->get(); double dPosition = pCue->getPosition(); double dEnabled = dPosition == -1 ? 0.0 : 1.0; if (dEnabled != dOldEnabled) { pControl->getEnabled()->set(dEnabled); } if (dPosition != dOldPosition) { pControl->getPosition()->set(dPosition); } } // Add the hotcue to the list of active hotcues active_hotcues.insert(hotcue); } } // Detach all hotcues that are no longer present for (int i = 0; i < m_iNumHotCues; ++i) { if (!active_hotcues.contains(i)) { detachCue(i); } } }
void CueControl::detachCue(int hotCue) { HotcueControl* pControl = m_hotcueControls.value(hotCue, NULL); if (pControl == NULL) { return; } CuePointer pCue(pControl->getCue()); if (!pCue) return; disconnect(pCue.get(), 0, this, 0); pControl->resetCue(); }
void CueControl::hotcueClear(HotcueControl* pControl, double v) { if (!v) return; QMutexLocker lock(&m_mutex); if (!m_pLoadedTrack) { return; } CuePointer pCue(pControl->getCue()); detachCue(pControl->getHotcueNumber()); m_pLoadedTrack->removeCue(pCue); }
void CueControl::detachCue(int hotCue) { HotcueControl* pControl = m_hotcueControl.value(hotCue, NULL); if (pControl == NULL) { return; } CuePointer pCue(pControl->getCue()); if (!pCue) return; disconnect(pCue.data(), 0, this, 0); // clear pCue first because we have a null check for valid data else where // in the code pControl->setCue(CuePointer()); pControl->getPosition()->set(-1); // invalidate position for hintReader() pControl->getEnabled()->set(0); }
void CueControl::hotcuePositionChanged(HotcueControl* pControl, double newPosition) { QMutexLocker lock(&m_mutex); if (!m_pLoadedTrack) return; CuePointer pCue(pControl->getCue()); if (pCue) { // Setting the position to -1 is the same as calling hotcue_x_clear if (newPosition == -1) { pCue->setHotCue(-1); detachCue(pControl->getHotcueNumber()); } else if (newPosition > 0 && newPosition < m_pTrackSamples->get()) { pCue->setPosition(newPosition); } } }
void CueControl::hotcueActivate(HotcueControl* pControl, double v) { //qDebug() << "CueControl::hotcueActivate" << v; QMutexLocker lock(&m_mutex); if (!m_pLoadedTrack) { return; } CuePointer pCue(pControl->getCue()); lock.unlock(); if (pCue) { if (v) { if (pCue->getPosition() == -1) { hotcueSet(pControl, v); } else { if (!m_iCurrentlyPreviewingHotcues && !m_bPreviewing && m_pPlayButton->toBool()) { hotcueGoto(pControl, v); } else { hotcueActivatePreview(pControl, v); } } } else { if (pCue->getPosition() != -1) { hotcueActivatePreview(pControl, v); } } } else { if (v) { // just in case hotcueSet(pControl, v); } else if (m_iCurrentlyPreviewingHotcues) { // The cue is non-existent, yet we got a release for it and are // currently previewing a hotcue. This is indicative of a corner // case where the cue was detached while we were pressing it. Let // hotcueActivatePreview handle it. hotcueActivatePreview(pControl, v); } } }
void CueControl::hotcuePositionChanged(HotcueControl* pControl, double newPosition) { QMutexLocker lock(&m_mutex); if (!m_pLoadedTrack) return; CuePointer pCue(pControl->getCue()); if (pCue) { // Setting the position to -1 is the same as calling hotcue_x_clear if (newPosition == -1) { pCue->setHotCue(-1); detachCue(pControl->getHotcueNumber()); } else if (newPosition > 0 && newPosition < m_pTrackSamples->get()) { int position = newPosition; // People writing from MIDI land, elsewhere might be careless. if (position % 2 != 0) { position--; } pCue->setPosition(position); } } }
void CueControl::hotcueGotoAndStop(HotcueControl* pControl, double v) { if (!v) return; QMutexLocker lock(&m_mutex); if (!m_pLoadedTrack) return; CuePointer pCue(pControl->getCue()); // Need to unlock before emitting any signals to prevent deadlock. lock.unlock(); if (pCue) { int position = pCue->getPosition(); if (position != -1) { m_pPlayButton->set(0.0); seekExact(position); } } }
void CueControl::hotcueGoto(HotcueControl* pControl, double v) { if (!v) return; QMutexLocker lock(&m_mutex); if (!m_pLoadedTrack) { return; } CuePointer pCue(pControl->getCue()); // Need to unlock before emitting any signals to prevent deadlock. lock.unlock(); if (pCue) { int position = pCue->getPosition(); if (position != -1) { seekAbs(position); } } }
void CueControl::hotcueActivatePreview(HotcueControl* pControl, double v) { QMutexLocker lock(&m_mutex); if (!m_pLoadedTrack) { return; } CuePointer pCue(pControl->getCue()); if (v) { if (pCue && pCue->getPosition() != -1) { m_iCurrentlyPreviewingHotcues++; int iPosition = pCue->getPosition(); m_bypassCueSetByPlay = true; m_pPlayButton->set(1.0); pControl->setPreviewing(true); pControl->setPreviewingPosition(iPosition); // Need to unlock before emitting any signals to prevent deadlock. lock.unlock(); seekAbs(iPosition); } } else if (m_iCurrentlyPreviewingHotcues) { // This is a activate release and we are previewing at least one // hotcue. If this hotcue is previewing: if (pControl->isPreviewing()) { // Mark this hotcue as not previewing. int iPosition = pControl->getPreviewingPosition(); pControl->setPreviewing(false); pControl->setPreviewingPosition(-1); // If this is the last hotcue to leave preview. if (--m_iCurrentlyPreviewingHotcues == 0 && !m_bPreviewing) { m_pPlayButton->set(0.0); // Need to unlock before emitting any signals to prevent deadlock. lock.unlock(); seekExact(iPosition); } } } }
void CueControl::hotcueSet(HotcueControl* pControl, double v) { //qDebug() << "CueControl::hotcueSet" << v; if (!v) return; QMutexLocker lock(&m_mutex); if (!m_pLoadedTrack) return; int hotcue = pControl->getHotcueNumber(); // Note: the cue is just detached from the hotcue control // It remains in the database for later use // TODO: find a rule, that allows us to delete the cue as well // https://bugs.launchpad.net/mixxx/+bug/1653276 hotcueClear(pControl, v); CuePointer pCue(m_pLoadedTrack->createAndAddCue()); double closestBeat = m_pClosestBeat->get(); double cuePosition = (m_pQuantizeEnabled->toBool() && closestBeat != -1) ? closestBeat : getCurrentSample(); pCue->setPosition(cuePosition); pCue->setHotCue(hotcue); pCue->setLabel(""); pCue->setType(Cue::CUE); // TODO(XXX) deal with spurious signals attachCue(pCue, hotcue); // If quantize is enabled and we are not playing, jump to the cue point // since it's not necessarily where we currently are. TODO(XXX) is this // potentially invalid for vinyl control? bool playing = m_pPlay->toBool(); if (!playing && m_pQuantizeEnabled->get() > 0.0) { lock.unlock(); // prevent deadlock. // Enginebuffer will quantize more exactly than we can. seekAbs(cuePosition); } }
TrackPointer BaseTrackPlayerImpl::unloadTrack() { if (!m_pLoadedTrack) { // nothing to do return TrackPointer(); } // Save the loops that are currently set in a loop cue. If no loop cue is // currently on the track, then create a new one. double loopStart = m_pLoopInPoint->get(); double loopEnd = m_pLoopOutPoint->get(); if (loopStart != kNoTrigger && loopEnd != kNoTrigger && loopStart <= loopEnd) { CuePointer pLoopCue; QList<CuePointer> cuePoints(m_pLoadedTrack->getCuePoints()); QListIterator<CuePointer> it(cuePoints); while (it.hasNext()) { CuePointer pCue(it.next()); if (pCue->getType() == Cue::LOOP) { pLoopCue = pCue; } } if (!pLoopCue) { pLoopCue = m_pLoadedTrack->createAndAddCue(); pLoopCue->setType(Cue::LOOP); } pLoopCue->setPosition(loopStart); pLoopCue->setLength(loopEnd - loopStart); } disconnectLoadedTrack(); // Do not reset m_pReplayGain here, because the track might be still // playing and the last buffer will be processed. m_pPlay->set(0.0); TrackPointer pUnloadedTrack(std::move(m_pLoadedTrack)); DEBUG_ASSERT(!m_pLoadedTrack); return pUnloadedTrack; }
void CueControl::hotcueGotoAndPlay(HotcueControl* pControl, double v) { if (!v) return; QMutexLocker lock(&m_mutex); if (!m_pLoadedTrack) { return; } CuePointer pCue(pControl->getCue()); // Need to unlock before emitting any signals to prevent deadlock. lock.unlock(); if (pCue) { int position = pCue->getPosition(); if (position != -1) { seekAbs(position); // don't move the cue point to the hot cue point in DENON mode m_bypassCueSetByPlay = true; m_pPlayButton->set(1.0); } } }
void CueControl::trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) { Q_UNUSED(pOldTrack); QMutexLocker lock(&m_mutex); if (m_pLoadedTrack) { disconnect(m_pLoadedTrack.data(), 0, this, 0); for (int i = 0; i < m_iNumHotCues; ++i) { detachCue(i); } // Store the cue point in a load cue. double cuePoint = m_pCuePoint->get(); if (cuePoint != -1 && cuePoint != 0.0) { CuePointer loadCue; const QList<CuePointer> cuePoints(m_pLoadedTrack->getCuePoints()); QListIterator<CuePointer> it(cuePoints); while (it.hasNext()) { CuePointer pCue(it.next()); if (pCue->getType() == Cue::LOAD) { loadCue = pCue; break; } } if (!loadCue) { loadCue = m_pLoadedTrack->addCue(); loadCue->setType(Cue::LOAD); loadCue->setLength(0); } loadCue->setPosition(cuePoint); } m_pCueIndicator->setBlinkValue(ControlIndicator::OFF); m_pCuePoint->set(-1.0); m_pLoadedTrack.clear(); } if (pNewTrack.isNull()) { return; } m_pLoadedTrack = pNewTrack; connect(pNewTrack.data(), SIGNAL(cuesUpdated()), this, SLOT(trackCuesUpdated()), Qt::DirectConnection); CuePointer loadCue; const QList<CuePointer> cuePoints(pNewTrack->getCuePoints()); QListIterator<CuePointer> it(cuePoints); while (it.hasNext()) { CuePointer pCue(it.next()); if (pCue->getType() == Cue::LOAD) { loadCue = pCue; } else if (pCue->getType() != Cue::CUE) { continue; } int hotcue = pCue->getHotCue(); if (hotcue != -1) attachCue(pCue, hotcue); } double loadCuePoint = 0.0; // If cue recall is ON in the prefs, then we're supposed to seek to the cue // point on song load. Note that [Controls],cueRecall == 0 corresponds to "ON", not OFF. bool cueRecall = (getConfig()->getValueString( ConfigKey("[Controls]","CueRecall"), "0").toInt() == 0); if (loadCue != NULL) { m_pCuePoint->set(loadCue->getPosition()); if (cueRecall) { loadCuePoint = loadCue->getPosition(); } } else { // If no cue point is stored, set one at track start m_pCuePoint->set(0.0); } // Need to unlock before emitting any signals to prevent deadlock. lock.unlock(); // If cueRecall is on, seek to it even if we didn't find a cue value (we'll // seek to 0. if (cueRecall) { seekExact(loadCuePoint); } else if (!(m_pVinylControlEnabled->get() && m_pVinylControlMode->get() == MIXXX_VCMODE_ABSOLUTE)) { // If cuerecall is off, seek to zero unless // vinylcontrol is on and set to absolute. This allows users to // load tracks and have the needle-drop be maintained. seekExact(0.0); } }
void VinylControlControl::slotControlVinylSeek(double fractionalPos) { // Prevent NaN's from sneaking into the engine. if (isnan(fractionalPos)) { return; } // Do nothing if no track is loaded. if (!m_pCurrentTrack) { return; } double total_samples = getTotalSamples(); double new_playpos = round(fractionalPos * total_samples); if (m_pControlVinylEnabled->get() > 0.0 && m_pControlVinylMode->get() == MIXXX_VCMODE_RELATIVE) { int cuemode = (int)m_pControlVinylCueing->get(); //if in preroll, always seek if (new_playpos < 0) { seekExact(new_playpos); return; } switch (cuemode) { case MIXXX_RELATIVE_CUE_OFF: return; // If off, do nothing. case MIXXX_RELATIVE_CUE_ONECUE: //if onecue, just seek to the regular cue seekExact(m_pCurrentTrack->getCuePoint()); return; case MIXXX_RELATIVE_CUE_HOTCUE: // Continue processing in this function. break; default: qWarning() << "Invalid vinyl cue setting"; return; } double shortest_distance = 0; int nearest_playpos = -1; const QList<CuePointer> cuePoints(m_pCurrentTrack->getCuePoints()); QListIterator<CuePointer> it(cuePoints); while (it.hasNext()) { CuePointer pCue(it.next()); if (pCue->getType() != Cue::CUE || pCue->getHotCue() == -1) { continue; } int cue_position = pCue->getPosition(); //pick cues closest to new_playpos if ((nearest_playpos == -1) || (fabs(new_playpos - cue_position) < shortest_distance)) { nearest_playpos = cue_position; shortest_distance = fabs(new_playpos - cue_position); } } if (nearest_playpos == -1) { if (new_playpos >= 0) { //never found an appropriate cue, so don't seek? return; } //if negative, allow a seek by falling down to the bottom } else { m_bSeekRequested = true; seekExact(nearest_playpos); m_bSeekRequested = false; return; } } // Just seek where it wanted to originally. m_bSeekRequested = true; seekExact(new_playpos); m_bSeekRequested = false; }