void BpmControl::collectFeatures(GroupFeatureState* pGroupFeatures) const { // Without a beatgrid we don't know any beat details. SampleOfTrack sot = getSampleOfTrack(); if (!sot.rate || !m_pBeats) { return; } // Get the current position of this deck. double dThisPrevBeat = m_pPrevBeat->get(); double dThisNextBeat = m_pNextBeat->get(); double dThisBeatLength; double dThisBeatFraction; if (getBeatContextNoLookup(sot.current, dThisPrevBeat, dThisNextBeat, &dThisBeatLength, &dThisBeatFraction)) { pGroupFeatures->has_beat_length_sec = true; // Note: dThisBeatLength is fractional frames count * 2 (stereo samples) pGroupFeatures->beat_length_sec = dThisBeatLength / kSamplesPerFrame / sot.rate / calcRateRatio(); pGroupFeatures->has_beat_fraction = true; pGroupFeatures->beat_fraction = dThisBeatFraction; } }
void BpmControl::slotTranslateBeatsEarlier(double v) { BeatsPointer pBeats = m_pBeats; if (v > 0 && pBeats && (pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { const int translate_dist = getSampleOfTrack().rate * -.01; pBeats->translate(translate_dist); } }
double BpmControl::updateBeatDistance() { double beat_distance = getBeatDistance(getSampleOfTrack().current); m_pThisBeatDistance->set(beat_distance); if (!isSynchronized()) { m_dUserOffset.setValue(0.0); } return beat_distance; }
void BpmControl::slotTranslateBeatsLater(double v) { BeatsPointer pBeats = m_pBeats; if (v > 0 && pBeats && (pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { // TODO(rryan): Track::getSampleRate is possibly inaccurate! const int translate_dist = getSampleOfTrack().rate * .01; pBeats->translate(translate_dist); } }
void BpmControl::slotBeatsTranslateMatchAlignment(double v) { BeatsPointer pBeats = m_pBeats; if (v > 0 && pBeats && (pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { // Must reset the user offset *before* calling getPhaseOffset(), // otherwise it will always return 0 if master sync is active. m_dUserOffset.setValue(0.0); double offset = getPhaseOffset(getSampleOfTrack().current); pBeats->translate(-offset); } }
void BpmControl::slotBeatsTranslate(double v) { BeatsPointer pBeats = m_pBeats; if (v > 0 && pBeats && (pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { double currentSample = getSampleOfTrack().current; double closestBeat = pBeats->findClosestBeat(currentSample); int delta = currentSample - closestBeat; if (delta % 2 != 0) { delta--; } pBeats->translate(delta); } }
double BpmControl::updateLocalBpm() { double prev_local_bpm = m_pLocalBpm->get(); double local_bpm = 0; BeatsPointer pBeats = m_pBeats; if (pBeats) { local_bpm = pBeats->getBpmAroundPosition( getSampleOfTrack().current, kLocalBpmSpan); if (local_bpm == -1) { local_bpm = m_pFileBpm->get(); } } else { local_bpm = m_pFileBpm->get(); } if (local_bpm != prev_local_bpm) { m_pLocalBpm->set(local_bpm); slotUpdateEngineBpm(); } return local_bpm; }
double BpmControl::calcSyncedRate(double userTweak) { double rate = 1.0; // Don't know what to do if there's no bpm. if (m_pLocalBpm->get() != 0.0) { rate = m_dSyncInstantaneousBpm / m_pLocalBpm->get(); } // If we are not quantized, or there are no beats, or we're master, // or we're in reverse, just return the rate as-is. if (!m_pQuantize->get() || getSyncMode() == SYNC_MASTER || !m_pBeats || m_pReverseButton->get()) { m_resetSyncAdjustment = true; return rate + userTweak; } // Now we need to get our beat distance so we can figure out how // out of phase we are. double dThisPosition = getSampleOfTrack().current; double dBeatLength; double my_percentage; if (!BpmControl::getBeatContextNoLookup(dThisPosition, m_pPrevBeat->get(), m_pNextBeat->get(), &dBeatLength, &my_percentage)) { m_resetSyncAdjustment = true; return rate + userTweak; } // Now that we have our beat distance we can also check how large the // current loop is. If we are in a <1 beat loop, don't worry about offset. const bool loop_enabled = m_pLoopEnabled->toBool(); const double loop_size = (m_pLoopEndPosition->get() - m_pLoopStartPosition->get()) / dBeatLength; if (loop_enabled && loop_size < 1.0 && loop_size > 0) { m_resetSyncAdjustment = true; return rate + userTweak; } // Now we have all we need to calculate the sync adjustment if any. double adjustment = calcSyncAdjustment(my_percentage, userTweak != 0.0); return (rate + userTweak) * adjustment; }
void BpmControl::slotFileBpmChanged(double file_bpm) { // Adjust the file-bpm with the current setting of the rate to get the // engine BPM. We only do this for SYNC_NONE decks because EngineSync will // set our BPM if the file BPM changes. See SyncControl::fileBpmChanged(). //qDebug() << "BpmControl::slotFileBpmChanged" << file_bpm; BeatsPointer pBeats = m_pBeats; if (pBeats) { const double beats_bpm = pBeats->getBpmAroundPosition( getSampleOfTrack().current, kLocalBpmSpan); if (beats_bpm != -1) { m_pLocalBpm->set(beats_bpm); } else { m_pLocalBpm->set(file_bpm); } } else { m_pLocalBpm->set(file_bpm); } resetSyncAdjustment(); }
void VinylControlControl::slotControlVinylSeek(double fractionalPos) { // Prevent NaN's from sneaking into the engine. if (isnan(fractionalPos)) { return; } // Do nothing if no track is loaded. TrackPointer pTrack = m_pTrack; if (!pTrack) { return; } double total_samples = getSampleOfTrack().total; 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(pTrack->getCuePoint().getPosition()); 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(pTrack->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; }