diff options
Diffstat (limited to 'src')
26 files changed, 377 insertions, 221 deletions
diff --git a/src/controllers/bulk/bulkcontroller.cpp b/src/controllers/bulk/bulkcontroller.cpp index 3bfe3f0db8..4dd814b7e5 100644 --- a/src/controllers/bulk/bulkcontroller.cpp +++ b/src/controllers/bulk/bulkcontroller.cpp @@ -52,7 +52,7 @@ void BulkReader::run() { qDebug() << "Stopped Reader"; } -static QString get_string(libusb_device_handle *handle, u_int8_t id) { +static QString get_string(libusb_device_handle* handle, uint8_t id) { unsigned char buf[128] = { 0 }; if (id) { diff --git a/src/engine/bufferscalers/enginebufferscalelinear.cpp b/src/engine/bufferscalers/enginebufferscalelinear.cpp index 4f91e3a93e..33fda453e0 100644 --- a/src/engine/bufferscalers/enginebufferscalelinear.cpp +++ b/src/engine/bufferscalers/enginebufferscalelinear.cpp @@ -220,7 +220,6 @@ SINT EngineBufferScaleLinear::do_scale(CSAMPLE* buf, SINT buf_size) { SINT unscaled_frames_needed = static_cast<SINT>(frames + m_dNextFrame - floor(m_dNextFrame)); - int read_failed_count = 0; CSAMPLE floor_sample[2]; CSAMPLE ceil_sample[2]; @@ -289,14 +288,9 @@ SINT EngineBufferScaleLinear::do_scale(CSAMPLE* buf, SINT buf_size) { m_bufferIntSize = m_pReadAheadManager->getNextSamples( rate_new == 0 ? rate_old : rate_new, m_bufferInt, samples_to_read); - - if (m_bufferIntSize == 0) { - if (++read_failed_count > 1) { - break; - } else { - continue; - } - } + // Note we may get 0 samples once if we just hit a loop trigger, + // e.g. when reloop_toggle jumps back to loop_in, or when + // moving a loop causes the play position to be moved along. frames_read += getOutputSignal().samples2frames(m_bufferIntSize); unscaled_frames_needed -= getOutputSignal().samples2frames(m_bufferIntSize); @@ -306,11 +300,6 @@ SINT EngineBufferScaleLinear::do_scale(CSAMPLE* buf, SINT buf_size) { currentFrameFloor = static_cast<SINT>(floor(m_dCurrentFrame)); } while (getOutputSignal().frames2samples(currentFrameFloor) + 3 >= m_bufferIntSize); - // I guess? - if (read_failed_count > 1) { - break; - } - // Now that the buffer is up to date, we can get the value of the sample // at the floor of our position. if (currentFrameFloor >= 0) { diff --git a/src/engine/bufferscalers/enginebufferscalerubberband.cpp b/src/engine/bufferscalers/enginebufferscalerubberband.cpp index 66f75b0e59..b502bad384 100644 --- a/src/engine/bufferscalers/enginebufferscalerubberband.cpp +++ b/src/engine/bufferscalers/enginebufferscalerubberband.cpp @@ -163,15 +163,19 @@ SINT EngineBufferScaleRubberBand::retrieveAndDeinterleave( } void EngineBufferScaleRubberBand::deinterleaveAndProcess( - const CSAMPLE* pBuffer, SINT frames, bool flush) { + const CSAMPLE* pBuffer, + SINT frames) { DEBUG_ASSERT(frames <= static_cast<SINT>(m_buffers[0].size())); SampleUtil::deinterleaveBuffer( - m_buffers[0].data(), m_buffers[1].data(), pBuffer, frames); + m_buffers[0].data(), + m_buffers[1].data(), + pBuffer, + frames); m_pRubberBand->process(m_bufferPtrs.data(), frames, - flush); + false); } double EngineBufferScaleRubberBand::scaleBuffer( @@ -189,7 +193,6 @@ double EngineBufferScaleRubberBand::scaleBuffer( SINT remaining_frames = getOutputSignal().samples2frames(iOutputBufferSize); CSAMPLE* read = pOutputBuffer; bool last_read_failed = false; - bool break_out_after_retrieve_and_reset_rubberband = false; while (remaining_frames > 0) { // ReadAheadManager will eventually read the requested frames with // enough calls to retrieveAndDeinterleave because CachingReader returns @@ -204,14 +207,6 @@ double EngineBufferScaleRubberBand::scaleBuffer( total_received_frames += received_frames; read += getOutputSignal().frames2samples(received_frames); - if (break_out_after_retrieve_and_reset_rubberband) { - // qDebug() << "break_out_after_retrieve_and_reset_rubberband"; - // If we break out early then we have flushed RubberBand and need to - // reset it. - reset(); - break; - } - const SINT next_block_frames_required = static_cast<SINT>(m_pRubberBand->getSamplesRequired()); if (remaining_frames > 0 && next_block_frames_required > 0) { @@ -225,14 +220,22 @@ double EngineBufferScaleRubberBand::scaleBuffer( if (available_frames > 0) { last_read_failed = false; - deinterleaveAndProcess(m_interleavedReadBuffer.data(), available_frames, false); + deinterleaveAndProcess(m_interleavedReadBuffer.data(), available_frames); } else { + // We may get 0 samples once if we just hit a loop trigger, e.g. + // when reloop_toggle jumps back to loop_in, or when moving a + // loop causes the play position to be moved along. if (last_read_failed) { - // Flush and break out after the next retrieval. If we are - // at EOF this serves to get the last samples out of - // RubberBand. - deinterleaveAndProcess(m_interleavedReadBuffer.data(), 0, true); - break_out_after_retrieve_and_reset_rubberband = true; + // If we get 0 samples repeatedly, flush and break out after + // the next retrieval. If we are at EOF this serves to get + // the last samples out of RubberBand. + qDebug() << "ReadAheadManager::getNextSamples() returned " + "zero samples repeatedly. Padding with silence."; + SampleUtil::clear( + m_interleavedReadBuffer.data(), + getOutputSignal().frames2samples(next_block_frames_required)); + deinterleaveAndProcess(m_interleavedReadBuffer.data(), + next_block_frames_required); } last_read_failed = true; } diff --git a/src/engine/bufferscalers/enginebufferscalerubberband.h b/src/engine/bufferscalers/enginebufferscalerubberband.h index 4837410937..bfd42bc58a 100644 --- a/src/engine/bufferscalers/enginebufferscalerubberband.h +++ b/src/engine/bufferscalers/enginebufferscalerubberband.h @@ -56,7 +56,7 @@ class EngineBufferScaleRubberBand final : public EngineBufferScale { /// `m_pRubberBand->reset()` directly. void reset(); - void deinterleaveAndProcess(const CSAMPLE* pBuffer, SINT frames, bool flush); + void deinterleaveAndProcess(const CSAMPLE* pBuffer, SINT frames); SINT retrieveAndDeinterleave(CSAMPLE* pBuffer, SINT frames); // The read-ahead manager that we use to fetch samples diff --git a/src/engine/bufferscalers/enginebufferscalest.cpp b/src/engine/bufferscalers/enginebufferscalest.cpp index 5c8b5f8c70..8a09309ed4 100644 --- a/src/engine/bufferscalers/enginebufferscalest.cpp +++ b/src/engine/bufferscalers/enginebufferscalest.cpp @@ -153,9 +153,18 @@ double EngineBufferScaleST::scaleBuffer( last_read_failed = false; m_pSoundTouch->putSamples(buffer_back.data(), iAvailFrames); } else { + // We may get 0 samples once if we just hit a loop trigger, e.g. + // when reloop_toggle jumps back to loop_in, or when moving a + // loop causes the play position to be moved along. if (last_read_failed) { - m_pSoundTouch->flush(); - break; // exit loop after failure + // If we get 0 samples repeatedly, add silence that allows + // to flush the last samples out of Soundtouch. + // m_pSoundTouch->flush() must not be used, because it allocates + // a temporary buffer in the heap which maybe locking + qDebug() << "ReadAheadManager::getNextSamples() returned " + "zero samples repeatedly. Padding with silence."; + SampleUtil::clear(buffer_back.data(), buffer_back.size()); + m_pSoundTouch->putSamples(buffer_back.data(), buffer_back.size()); } last_read_failed = true; } diff --git a/src/engine/controls/loopingcontrol.cpp b/src/engine/controls/loopingcontrol.cpp index 4015076143..03e9a4c281 100644 --- a/src/engine/controls/loopingcontrol.cpp +++ b/src/engine/controls/loopingcontrol.cpp @@ -124,12 +124,6 @@ LoopingControl::LoopingControl(const QString& group, Qt::DirectConnection); m_pQuantizeEnabled = ControlObject::getControl(ConfigKey(group, "quantize")); - m_pNextBeat = ControlObject::getControl(ConfigKey(group, "beat_next")); - m_pNextBeat->setKbdRepeatable(true); - m_pPreviousBeat = ControlObject::getControl(ConfigKey(group, "beat_prev")); - m_pPreviousBeat->setKbdRepeatable(true); - m_pClosestBeat = ControlObject::getControl(ConfigKey(group, "beat_closest")); - m_pTrackSamples = ControlObject::getControl(ConfigKey(group, "track_samples")); m_pSlipEnabled = ControlObject::getControl(ConfigKey(group, "slip_enabled")); // DEPRECATED: Use beatloop_size and beatloop_set instead. @@ -301,9 +295,8 @@ void LoopingControl::slotLoopScale(double scaleFactor) { const mixxx::audio::FrameDiff_t loopLength = (loopInfo.endPosition - loopInfo.startPosition) * scaleFactor; - const auto trackEndPosition = - mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid( - m_pTrackSamples->get()); + const FrameInfo info = frameInfo(); + const auto trackEndPosition = info.trackEndPosition; if (!trackEndPosition.isValid()) { return; } @@ -444,64 +437,84 @@ mixxx::audio::FramePos LoopingControl::nextTrigger(bool reverse, } if (m_bLoopingEnabled && - !m_bAdjustingLoopIn && !m_bAdjustingLoopOut && loopInfo.startPosition.isValid() && loopInfo.endPosition.isValid()) { - if (loopInfo.startPosition != m_oldLoopInfo.startPosition || - loopInfo.endPosition != m_oldLoopInfo.endPosition) { - // bool seek is only valid after the loop has changed - switch (loopInfo.seekMode) { - case LoopSeekMode::Changed: - // here the loop has changed and the play position - // should be moved with it - *pTargetPosition = adjustedPositionInsideAdjustedLoop(currentPosition, - reverse, - m_oldLoopInfo.startPosition, - m_oldLoopInfo.endPosition, - loopInfo.startPosition, - loopInfo.endPosition); - break; - case LoopSeekMode::MovedOut: { - bool movedOut = false; - // Check if we have moved out of the loop, before we could enable it - if (reverse) { - if (loopInfo.startPosition > currentPosition) { - movedOut = true; - } - } else { - if (loopInfo.endPosition < currentPosition) { - movedOut = true; - } - } - if (movedOut) { + if (!m_bAdjustingLoopIn && !m_bAdjustingLoopOut) { + if (loopInfo.startPosition != m_oldLoopInfo.startPosition || + loopInfo.endPosition != m_oldLoopInfo.endPosition) { + // bool seek is only valid after the loop has changed + switch (loopInfo.seekMode) { + case LoopSeekMode::Changed: + // here the loop has changed and the play position + // should be moved with it *pTargetPosition = adjustedPositionInsideAdjustedLoop(currentPosition, reverse, - loopInfo.startPosition, - loopInfo.endPosition, + m_oldLoopInfo.startPosition, + m_oldLoopInfo.endPosition, loopInfo.startPosition, loopInfo.endPosition); + break; + case LoopSeekMode::MovedOut: { + bool movedOut = false; + // Check if we have moved out of the loop, before we could enable it + if (reverse) { + if (loopInfo.startPosition > currentPosition) { + movedOut = true; + } + } else { + if (loopInfo.endPosition < currentPosition) { + movedOut = true; + } + } + if (movedOut) { + *pTargetPosition = adjustedPositionInsideAdjustedLoop(currentPosition, + reverse, + loopInfo.startPosition, + loopInfo.endPosition, + loopInfo.startPosition, + loopInfo.endPosition); + } + break; + } + case LoopSeekMode::None: + // Nothing to do here. This is used for enabling saved loops + // which we want to do without jumping to the loop start + // position. + break; + } + m_oldLoopInfo = loopInfo; + if (pTargetPosition->isValid()) { + // jump immediately + return currentPosition; } - break; - } - case LoopSeekMode::None: - // Nothing to do here. This is used for enabling saved loops - // which we want to do without jumping to the loop start - // position. - break; } - m_oldLoopInfo = loopInfo; - if (pTargetPosition->isValid()) { - // jump immediately - return currentPosition; + if (reverse) { + *pTargetPosition = loopInfo.endPosition; + return loopInfo.startPosition; + } else { + *pTargetPosition = loopInfo.startPosition; + return loopInfo.endPosition; } - } - - if (reverse) { - *pTargetPosition = loopInfo.endPosition; - return loopInfo.startPosition; } else { - *pTargetPosition = loopInfo.startPosition; - return loopInfo.endPosition; + // LOOP in or out button is pressed for adjusting. + // Jump back to loop start, when reaching the track end this + // prevents that the track stops outside the adjusted loop. + if (!reverse) { + if (m_bAdjustingLoopIn) { + // Just in case the user does not release loop-in in time. + *pTargetPosition = m_oldLoopInfo.startPosition; + return loopInfo.endPosition; + } + const FrameInfo info = frameInfo(); + *pTargetPosition = loopInfo.startPosition; + return info.trackEndPosition; + } else { + if (m_bAdjustingLoopOut) { + // Just in case the user does not release loop-out in time. + *pTargetPosition = m_oldLoopInfo.endPosition; + return loopInfo.startPosition; + } + } } } return mixxx::audio::kInvalidFramePos; @@ -658,23 +671,33 @@ void LoopingControl::setLoopInToCurrentPosition() { const mixxx::BeatsPointer pBeats = m_pBeats; LoopInfo loopInfo = m_loopInfo.getValue(); mixxx::audio::FramePos quantizedBeatPosition; - mixxx::audio::FramePos position = m_currentPosition.getValue(); + const FrameInfo info = frameInfo(); + // Note: currentPos can be past the end of the track, in the padded + // silence of the last buffer. This position might be not reachable in + // a future runs, depending on the buffering. + mixxx::audio::FramePos position = math_min(info.currentPosition, info.trackEndPosition); if (m_pQuantizeEnabled->toBool() && pBeats) { - const auto closestBeatPosition = - mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid( - m_pClosestBeat->get()); - if (m_bAdjustingLoopIn) { - if (closestBeatPosition == m_currentPosition.getValue()) { - quantizedBeatPosition = closestBeatPosition; + mixxx::audio::FramePos prevBeatPosition; + mixxx::audio::FramePos nextBeatPosition; + if (pBeats->findPrevNextBeats(position, &prevBeatPosition, &nextBeatPosition, false)) { + // both beat positions are valid + mixxx::audio::FramePos closestBeatPosition = + (nextBeatPosition - position > position - prevBeatPosition) + ? prevBeatPosition + : nextBeatPosition; + if (m_bAdjustingLoopIn) { + if (closestBeatPosition == position) { + quantizedBeatPosition = closestBeatPosition; + } else { + quantizedBeatPosition = prevBeatPosition; + } } else { - quantizedBeatPosition = - mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid( - m_pPreviousBeat->get()); + if (closestBeatPosition > info.trackEndPosition) { + quantizedBeatPosition = prevBeatPosition; + } else { + quantizedBeatPosition = closestBeatPosition; + } } - } else { - quantizedBeatPosition = closestBeatPosition; - } - if (quantizedBeatPosition.isValid()) { position = quantizedBeatPosition; } } @@ -684,6 +707,9 @@ void LoopingControl::setLoopInToCurrentPosition() { if (loopInfo.endPosition.isValid() && loopInfo.endPosition < position) { loopInfo.endPosition = mixxx::audio::kInvalidFramePos; m_pCOLoopEndPosition->set(loopInfo.endPosition.toEngineSamplePosMaybeInvalid()); + if (m_bLoopingEnabled) { + setLoopingEnabled(false); + } } // If we're looping and the loop-in and out points are now so close @@ -787,23 +813,37 @@ void LoopingControl::setLoopOutToCurrentPosition() { mixxx::BeatsPointer pBeats = m_pBeats; LoopInfo loopInfo = m_loopInfo.getValue(); mixxx::audio::FramePos quantizedBeatPosition; - mixxx::audio::FramePos position = m_currentPosition.getValue(); + FrameInfo info = frameInfo(); + // Note: currentPos can be past the end of the track, in the padded + // silence of the last buffer. This position might be not reachable in + // a future runs, depending on the buffering. + mixxx::audio::FramePos position = math_min(info.currentPosition, info.trackEndPosition); if (m_pQuantizeEnabled->toBool() && pBeats) { - const auto closestBeatPosition = - mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid( - m_pClosestBeat->get()); - if (m_bAdjustingLoopOut) { - if (closestBeatPosition == m_currentPosition.getValue()) { - quantizedBeatPosition = closestBeatPosition; + mixxx::audio::FramePos prevBeatPosition; + mixxx::audio::FramePos nextBeatPosition; + if (pBeats->findPrevNextBeats(position, &prevBeatPosition, &nextBeatPosition, false)) { + // both beat positions are valid + const mixxx::audio::FramePos closestBeatPosition = + (nextBeatPosition - position > position - prevBeatPosition) + ? prevBeatPosition + : nextBeatPosition; + if (m_bAdjustingLoopOut) { + if (closestBeatPosition == position) { + quantizedBeatPosition = closestBeatPosition; + } else { + if (nextBeatPosition > info.trackEndPosition) { + quantizedBeatPosition = prevBeatPosition; + } else { + quantizedBeatPosition = nextBeatPosition; + } + } } else { - quantizedBeatPosition = - mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid( - m_pNextBeat->get()); + if (closestBeatPosition > info.trackEndPosition) { + quantizedBeatPosition = prevBeatPosition; + } else { + quantizedBeatPosition = closestBeatPosition; + } } - } else { - quantizedBeatPosition = closestBeatPosition; - } - if (quantizedBeatPosition.isValid()) { position = quantizedBeatPosition; } } @@ -1351,9 +1391,8 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable beats = minBeatSize; } - const auto trackEndPosition = - mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid( - m_pTrackSamples->get()); + FrameInfo info = frameInfo(); + const auto trackEndPosition = info.trackEndPosition; const mixxx::BeatsPointer pBeats = m_pBeats; if (!trackEndPosition.isValid() || !pBeats) { clearActiveBeatLoop(); @@ -1367,15 +1406,15 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable mixxx::audio::kInvalidFramePos, LoopSeekMode::MovedOut}; LoopInfo loopInfo = m_loopInfo.getValue(); - mixxx::audio::FramePos currentPosition = m_currentPosition.getValue(); - + mixxx::audio::FramePos currentPosition = info.currentPosition; // Start from the current position/closest beat and // create the loop around X beats from there. if (keepStartPoint) { if (loopInfo.startPosition.isValid()) { newloopInfo.startPosition = loopInfo.startPosition; } else { - newloopInfo.startPosition = currentPosition; + newloopInfo.startPosition = + math_min(info.currentPosition, info.trackEndPosition); } } else { // If running reverse, move the loop one loop size to the left. @@ -1587,8 +1626,9 @@ void LoopingControl::slotLoopMove(double beats) { return; } + FrameInfo info = frameInfo(); if (BpmControl::getBeatContext(pBeats, - m_currentPosition.getValue(), + info.currentPosition, nullptr, nullptr, nullptr, @@ -1602,9 +1642,7 @@ void LoopingControl::slotLoopMove(double beats) { // The track would stop as soon as the playhead crosses track end, // so we don't allow moving a loop beyond end. // https://github.com/mixxxdj/mixxx/issues/9478 - const auto trackEndPosition = - mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid( - m_pTrackSamples->get()); + const auto trackEndPosition = info.trackEndPosition; if (!trackEndPosition.isValid() || newLoopEndPosition > trackEndPosition) { return; } diff --git a/src/engine/controls/loopingcontrol.h b/src/engine/controls/loopingcontrol.h index 3f239e70aa..3a3c0ab603 100644 --- a/src/engine/controls/loopingcontrol.h +++ b/src/engine/controls/loopingcontrol.h @@ -182,10 +182,6 @@ class LoopingControl : public EngineControl { LoopInfo m_oldLoopInfo; ControlValueAtomic<mixxx::audio::FramePos> m_currentPosition; ControlObject* m_pQuantizeEnabled; - ControlObject* m_pNextBeat; - ControlObject* m_pPreviousBeat; - ControlObject* m_pClosestBeat; - ControlObject* m_pTrackSamples; QAtomicPointer<BeatLoopingControl> m_pActiveBeatLoop; // Base BeatLoop Control Object. diff --git a/src/engine/controls/quantizecontrol.cpp b/src/engine/controls/quantizecontrol.cpp index f36f6159ef..81c40d2f13 100644 --- a/src/engine/controls/quantizecontrol.cpp +++ b/src/engine/controls/quantizecontrol.cpp @@ -16,8 +16,10 @@ QuantizeControl::QuantizeControl(const QString& group, m_pCOQuantizeEnabled = new ControlPushButton(ConfigKey(group, "quantize"), true); m_pCOQuantizeEnabled->setButtonMode(ControlPushButton::TOGGLE); m_pCONextBeat = new ControlObject(ConfigKey(group, "beat_next")); + m_pCONextBeat->setKbdRepeatable(true); m_pCONextBeat->set(mixxx::audio::kInvalidFramePos.toEngineSamplePosMaybeInvalid()); m_pCOPrevBeat = new ControlObject(ConfigKey(group, "beat_prev")); + m_pCOPrevBeat->setKbdRepeatable(true); m_pCOPrevBeat->set(mixxx::audio::kInvalidFramePos.toEngineSamplePosMaybeInvalid()); m_pCOClosestBeat = new ControlObject(ConfigKey(group, "beat_closest")); m_pCOClosestBeat->set(mixxx::audio::kInvalidFramePos.toEngineSamplePosMaybeInvalid()); diff --git a/src/engine/readaheadmanager.cpp b/src/engine/readaheadmanager.cpp index fb1908c66d..264196a34d 100644 --- a/src/engine/readaheadmanager.cpp +++ b/src/engine/readaheadmanager.cpp @@ -80,7 +80,7 @@ SINT ReadAheadManager::getNextSamples(double dRate, CSAMPLE* pOutput, } // Sanity checks. - if (samples_from_reader < 0) { + VERIFY_OR_DEBUG_ASSERT(samples_from_reader >= 0) { qDebug() << "Need negative samples in ReadAheadManager::getNextSamples. Ignoring read"; return 0; } @@ -98,8 +98,8 @@ SINT ReadAheadManager::getNextSamples(double dRate, CSAMPLE* pOutput, m_cacheMissHappened = true; } else if (m_cacheMissHappened) { // Previous read was a cache miss, but now we got something back. - // Apply ramping gain, because the last buffer has unwanted silenced - // and new without fading are causing a pop. + // Apply ramping gain, because the last buffer has unwanted silence + // and new samples without fading are causing a pop. SampleUtil::applyRampingGain(pOutput, 0.0, 1.0, samples_from_reader); // Reset the cache miss flag, because we are now back on track. m_cacheMissHappened = false; @@ -157,12 +157,10 @@ SINT ReadAheadManager::getNextSamples(double dRate, CSAMPLE* pOutput, } // do crossfade from the current buffer into the new loop beginning - if (samples_from_reader != 0) { // avoid division by zero - SampleUtil::linearCrossfadeBuffersOut( - pOutput, - m_pCrossFadeBuffer, - samples_from_reader); - } + SampleUtil::linearCrossfadeBuffersOut( + pOutput, + m_pCrossFadeBuffer, + samples_from_reader); } //qDebug() << "read" << m_currentPosition << samples_read; diff --git a/src/library/autodj/autodjprocessor.cpp b/src/library/autodj/autodjprocessor.cpp index 57014e0641..38051d0d11 100644 --- a/src/library/autodj/autodjprocessor.cpp +++ b/src/library/autodj/autodjprocessor.cpp @@ -1280,23 +1280,29 @@ void AutoDJProcessor::calculateTransition(DeckAttributes* pFromDeck, } pToDeck->fadeBeginPos = toDeckOutroStartSecond; - double introStart; + double toDeckStartSeconds = toDeckPositionSeconds; + const double introStart = getIntroStartSecond(pToDeck); + const double introEnd = getIntroEndSecond(pToDeck); if (seekToStartPoint || toDeckPositionSeconds >= pToDeck->fadeBeginPos) { // toDeckPosition >= pToDeck->fadeBeginPos happens when the // user has seeked or played the to track behind fadeBeginPos of // the fade after the next. // In this case we recue the track just before the transition. - introStart = getIntroStartSecond(pToDeck); - } else { - introStart = toDeckPositionSeconds; + toDeckStartSeconds = introStart; } double introLength = 0; - // This returns introStart in case the user has not yet set an intro end - const double introEnd = getIntroEndSecond(pToDeck); - if (introStart < introEnd) { - introLength = introEnd - introStart; + // introEnd is equal introStart in case it has not yet been set + if (toDeckStartSeconds < introEnd && introStart < introEnd) { + // Limit the intro length that results from a revers seek + // to a reasonable values. If the seek was too big, ignore it. + introLength = introEnd - toDeckStartSeconds; + if (introLength > (introEnd - introStart) * 2 && + introLength > (introEnd - introStart) + m_transitionTime && + introLength > outroLength) { + introLength = 0; + } } if constexpr (sDebug) { @@ -1337,10 +1343,10 @@ void AutoDJProcessor::calculateTransition(DeckAttributes* pFromDeck, } } if (transitionLength > 0) { - const double transitionEnd = introStart + transitionLength; + const double transitionEnd = toDeckStartSeconds + transitionLength; if (transitionEnd > pToDeck->fadeBeginPos) { // End intro before next outro starts - transitionLength = pToDeck->fadeBeginPos - introStart; + transitionLength = pToDeck->fadeBeginPos - toDeckStartSeconds; VERIFY_OR_DEBUG_ASSERT(transitionLength > 0) { // We seek to intro start above in this case so this never happens transitionLength = 1; @@ -1348,9 +1354,9 @@ void AutoDJProcessor::calculateTransition(DeckAttributes* pFromDeck, } pFromDeck->fadeBeginPos = outroEnd - transitionLength; pFromDeck->fadeEndPos = outroEnd; - pToDeck->startPos = introStart; + pToDeck->startPos = toDeckStartSeconds; } else { - useFixedFadeTime(pFromDeck, pToDeck, fromDeckPosition, outroEnd, introStart); + useFixedFadeTime(pFromDeck, pToDeck, fromDeckPosition, outroEnd, toDeckStartSeconds); } } break; case TransitionMode::FadeAtOutroStart: { @@ -1383,10 +1389,10 @@ void AutoDJProcessor::calculateTransition(DeckAttributes* pFromDeck, transitionLength = introLength; } } - const double transitionEnd = introStart + transitionLength; + const double transitionEnd = toDeckStartSeconds + transitionLength; if (transitionEnd > pToDeck->fadeBeginPos) { // End intro before next outro starts - transitionLength = pToDeck->fadeBeginPos - introStart; + transitionLength = pToDeck->fadeBeginPos - toDeckStartSeconds; VERIFY_OR_DEBUG_ASSERT(transitionLength > 0) { // We seek to intro start above in this case so this never happens transitionLength = 1; @@ -1394,14 +1400,14 @@ void AutoDJProcessor::calculateTransition(DeckAttributes* pFromDeck, } pFromDeck->fadeBeginPos = outroStart; pFromDeck->fadeEndPos = outroStart + transitionLength; - pToDeck->startPos = introStart; + pToDeck->startPos = toDeckStartSeconds; } else if (introLength > 0) { transitionLength = introLength; pFromDeck->fadeBeginPos = outroEnd - transitionLength; pFromDeck->fadeEndPos = outroEnd; - pToDeck->startPos = introStart; + pToDeck->startPos = toDeckStartSeconds; } else { - useFixedFadeTime(pFromDeck, pToDeck, fromDeckPosition, outroEnd, introStart); + useFixedFadeTime(pFromDeck, pToDeck, fromDeckPosition, outroEnd, toDeckStartSeconds); } } break; case TransitionMode::FixedSkipSilence: { diff --git a/src/library/banshee/bansheefeature.h b/src/library/banshee/bansheefeature.h index 0b4684db23..57b6357b37 100644 --- a/src/library/banshee/bansheefeature.h +++ b/src/library/banshee/bansheefeature.h @@ -19,20 +19,20 @@ class BansheeFeature : public BaseExternalLibraryFeature { Q_OBJECT publi |