From 35aca6811169431df5cc728b50beba169436ac84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Fri, 23 Nov 2018 21:44:42 +0100 Subject: [PATCH 01/27] Replace connections to EngineControls by direct calls for better tracability. --- src/engine/enginebuffer.cpp | 19 +++++++++++++------ src/engine/enginebuffer.h | 1 + src/engine/enginecontrol.h | 2 -- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index 25819d9dac3..c829177f766 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -530,8 +530,7 @@ void EngineBuffer::slotTrackLoaded(TrackPointer pTrack, // Reset the pitch value for the new track. m_pause.unlock(); - // All EngineControls are connected directly - emit(trackLoaded(pTrack, pOldTrack)); + notifyTrackLoaded(pTrack, pOldTrack); // Start buffer processing after all EngineContols are up to date // with the current track e.g track is seeked to Cue m_iTrackLoading = 0; @@ -577,7 +576,7 @@ void EngineBuffer::ejectTrack() { m_pReader->newTrack(TrackPointer()); if (pTrack) { - emit(trackLoaded(TrackPointer(), pTrack)); + notifyTrackLoaded(TrackPointer(), pTrack); } } @@ -1302,9 +1301,6 @@ void EngineBuffer::addControl(EngineControl* pControl) { // Connect to signals from EngineControl here... m_engineControls.push_back(pControl); pControl->setEngineBuffer(this); - connect(this, SIGNAL(trackLoaded(TrackPointer, TrackPointer)), - pControl, SLOT(trackLoaded(TrackPointer, TrackPointer)), - Qt::DirectConnection); } void EngineBuffer::bindWorkers(EngineWorkerScheduler* pWorkerScheduler) { @@ -1371,3 +1367,14 @@ void EngineBuffer::collectFeatures(GroupFeatureState* pGroupFeatures) const { m_pBpmControl->collectFeatures(pGroupFeatures); } } + +void EngineBuffer::notifyTrackLoaded( + TrackPointer pNewTrack, TrackPointer pOldTrack) { + // First inform engineControls directly + // Note: we are still in a worker thread. + for (const auto& pControl: m_engineControls) { + pControl->trackLoaded(pNewTrack, pOldTrack); + } + // Inform BaseTrackPlayer via a queued connection + emit(trackLoaded(pNewTrack, pOldTrack)); +} diff --git a/src/engine/enginebuffer.h b/src/engine/enginebuffer.h index 6adf9ab24dc..15e9d8b57d5 100644 --- a/src/engine/enginebuffer.h +++ b/src/engine/enginebuffer.h @@ -235,6 +235,7 @@ class EngineBuffer : public EngineObject { bool updateIndicatorsAndModifyPlay(bool newPlay); void verifyPlay(); + void notifyTrackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack); // Holds the name of the control group QString m_group; diff --git a/src/engine/enginecontrol.h b/src/engine/enginecontrol.h index 1661a0b6821..fec4160fb8e 100644 --- a/src/engine/enginecontrol.h +++ b/src/engine/enginecontrol.h @@ -67,8 +67,6 @@ class EngineControl : public QObject { // Called whenever a seek occurs to allow the EngineControl to respond. virtual void notifySeek(double dNewPlaypo, bool adjustingPhase); - - public slots: virtual void trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack); protected: From 82afb2a789adee52a89c1fe9037c63bce840b049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Fri, 23 Nov 2018 22:02:08 +0100 Subject: [PATCH 02/27] use range based loops --- src/engine/enginebuffer.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index c829177f766..d603a143695 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -372,7 +372,7 @@ double EngineBuffer::getLocalBpm() { } void EngineBuffer::setEngineMaster(EngineMaster* pEngineMaster) { - foreach (EngineControl* pControl, m_engineControls) { + for (const auto& pControl: m_engineControls) { pControl->setEngineMaster(pEngineMaster); } } @@ -461,9 +461,7 @@ void EngineBuffer::setNewPlaypos(double newpos, bool adjustingPhase) { m_iSamplesCalculated = 1000000; // Must hold the engineLock while using m_engineControls - for (QList::iterator it = m_engineControls.begin(); - it != m_engineControls.end(); ++it) { - EngineControl *pControl = *it; + for (const auto& pControl: m_engineControls) { pControl->notifySeek(m_filepos_play, adjustingPhase); } @@ -1020,9 +1018,7 @@ void EngineBuffer::process(CSAMPLE* pOutput, const int iBufferSize) { } } - QListIterator it(m_engineControls); - while (it.hasNext()) { - EngineControl* pControl = it.next(); + for (const auto& pControl: m_engineControls) { pControl->setCurrentSample(m_filepos_play, m_trackSamplesOld); pControl->process(rate, m_filepos_play, m_trackSamplesOld, iBufferSize); } From 3f6865b309197a27314034850d6b7501bb6a43c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Fri, 23 Nov 2018 23:36:29 +0100 Subject: [PATCH 03/27] Axtracted processTrackLocked() for a save Track access. --- src/engine/enginebuffer.cpp | 591 ++++++++++++++++++----------------- src/engine/enginebuffer.h | 3 +- src/engine/enginecontrol.cpp | 4 +- 3 files changed, 301 insertions(+), 297 deletions(-) diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index d603a143695..21cb2eb0f64 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -53,14 +53,14 @@ EngineBuffer::EngineBuffer(QString group, UserSettingsPointer pConfig, EngineChannel* pChannel, EngineMaster* pMixingEngine) : m_group(group), m_pConfig(pConfig), - m_pLoopingControl(NULL), - m_pSyncControl(NULL), - m_pVinylControlControl(NULL), - m_pRateControl(NULL), - m_pBpmControl(NULL), - m_pKeyControl(NULL), - m_pReadAheadManager(NULL), - m_pReader(NULL), + m_pLoopingControl(nullptr), + m_pSyncControl(nullptr), + m_pVinylControlControl(nullptr), + m_pRateControl(nullptr), + m_pBpmControl(nullptr), + m_pKeyControl(nullptr), + m_pReadAheadManager(nullptr), + m_pReader(nullptr), m_filepos_play(0.), m_speed_old(0), m_tempo_ratio_old(1.), @@ -77,9 +77,9 @@ EngineBuffer::EngineBuffer(QString group, UserSettingsPointer pConfig, m_dSlipRate(1.0), m_slipEnabled(0), m_bSlipEnabledProcessing(false), - m_pRepeat(NULL), - m_startButton(NULL), - m_endButton(NULL), + m_pRepeat(nullptr), + m_startButton(nullptr), + m_endButton(nullptr), m_bScalerOverride(false), m_iSeekQueued(SEEK_NONE), m_iSeekPhaseQueued(0), @@ -724,336 +724,339 @@ void EngineBuffer::slotKeylockEngineChanged(double dIndex) { } } -void EngineBuffer::process(CSAMPLE* pOutput, const int iBufferSize) { - // Bail if we receive a buffer size with incomplete sample frames. Assert in debug builds. - VERIFY_OR_DEBUG_ASSERT((iBufferSize % kSamplesPerFrame) == 0) { - return; - } - m_pReader->process(); - // Steps: - // - Lookup new reader information - // - Calculate current rate - // - Scale the audio with m_pScale, copy the resulting samples into the - // output buffer - // - Give EngineControl's a chance to do work / request seeks, etc - // - Process repeat mode if we're at the end or beginning of a track - // - Set last sample value (m_fLastSampleValue) so that rampOut works? Other - // miscellaneous upkeep issues. +void EngineBuffer::processTrackLocked( + CSAMPLE* pOutput, const int iBufferSize, int sample_rate) { + ScopedTimer t("EngineBuffer::process_pauselock"); - bool bCurBufferPaused = false; - double rate = 0; - int sample_rate = static_cast(m_pSampleRate->get()); - - // If the sample rate has changed, force Rubberband to reset so that - // it doesn't reallocate when the user engages keylock during playback. - // We do this even if rubberband is not active. - if (sample_rate != m_iSampleRate) { - m_pScaleLinear->setSampleRate(sample_rate); - m_pScaleST->setSampleRate(sample_rate); - m_pScaleRB->setSampleRate(sample_rate); - m_iSampleRate = sample_rate; + double baserate = 0.0; + if (sample_rate > 0) { + baserate = ((double)m_trackSampleRateOld / sample_rate); } - bool bTrackLoading = load_atomic(m_iTrackLoading) != 0; - if (!bTrackLoading && m_pause.tryLock()) { - ScopedTimer t("EngineBuffer::process_pauselock"); - - double baserate = 0.0; - if (sample_rate > 0) { - baserate = ((double)m_trackSampleRateOld / sample_rate); - } - - // Note: play is also active during cue preview - bool paused = !m_playButton->toBool(); + // Note: play is also active during cue preview + bool paused = !m_playButton->toBool(); KeyControl::PitchTempoRatio pitchTempoRatio = m_pKeyControl->getPitchTempoRatio(); - // The pitch adjustment in Ratio (1.0 being normal - // pitch. 2.0 is a full octave shift up). - double pitchRatio = pitchTempoRatio.pitchRatio; - double tempoRatio = pitchTempoRatio.tempoRatio; - const bool keylock_enabled = pitchTempoRatio.keylock; + // The pitch adjustment in Ratio (1.0 being normal + // pitch. 2.0 is a full octave shift up). + double pitchRatio = pitchTempoRatio.pitchRatio; + double tempoRatio = pitchTempoRatio.tempoRatio; + const bool keylock_enabled = pitchTempoRatio.keylock; - bool is_scratching = false; - bool is_reverse = false; + bool is_scratching = false; + bool is_reverse = false; - // Update the slipped position and seek if it was disabled. - processSlip(iBufferSize); - processSyncRequests(); + // Update the slipped position and seek if it was disabled. + processSlip(iBufferSize); + processSyncRequests(); - // Note: This may effects the m_filepos_play, play, scaler and crossfade buffer - processSeek(paused); + // Note: This may effects the m_filepos_play, play, scaler and crossfade buffer + processSeek(paused); - // speed is the ratio between track-time and real-time - // (1.0 being normal rate. 2.0 plays at 2x speed -- 2 track seconds - // pass for every 1 real second). Depending on whether - // keylock is enabled, this is applied to either the rate or the tempo. - double speed = m_pRateControl->calculateSpeed( - baserate, tempoRatio, paused, iBufferSize, &is_scratching, &is_reverse); + // speed is the ratio between track-time and real-time + // (1.0 being normal rate. 2.0 plays at 2x speed -- 2 track seconds + // pass for every 1 real second). Depending on whether + // keylock is enabled, this is applied to either the rate or the tempo. + double speed = m_pRateControl->calculateSpeed( + baserate, tempoRatio, paused, iBufferSize, &is_scratching, &is_reverse); - // The cue indicator may change when scratch state is changed - if (is_scratching != m_scratching_old) { - m_pCueControl->updateIndicators(); - } + // The cue indicator may change when scratch state is changed + if (is_scratching != m_scratching_old) { + m_pCueControl->updateIndicators(); + } - bool useIndependentPitchAndTempoScaling = false; - - // TODO(owen): Maybe change this so that rubberband doesn't disable - // keylock on scratch. (just check m_pScaleKeylock == m_pScaleST) - if (is_scratching || fabs(speed) > 1.9) { - // Scratching and high speeds with always disables keylock - // because Soundtouch sounds terrible in these conditions. Rubberband - // sounds better, but still has some problems (it may reallocate in - // a party-crashing manner at extremely slow speeds). - // High seek speeds also disables keylock. Our pitch slider could go - // to 90%, so that's the cutoff point. - - // Force pitchRatio to the linear pitch set by speed - pitchRatio = speed; - // This is for the natural speed pitch found on turn tables - } else if (fabs(speed) < 0.1) { - // We have pre-allocated big buffers in Rubberband and Soundtouch for - // a minimum speed of 0.1. Slower speeds will re-allocate much bigger - // buffers which may cause underruns. - // Disable keylock under these conditions. - - // Force pitchRatio to the linear pitch set by speed - pitchRatio = speed; - } else if (keylock_enabled) { - // always use IndependentPitchAndTempoScaling - // to avoid clicks when crossing the linear pitch - // in this case it is most likely that the user - // will have an non linear pitch - // Note: We have undesired noise when cossfading between scalers - useIndependentPitchAndTempoScaling = true; - } else { - // We might have have temporary speed change, so adjust pitch if not locked - // Note: This will not update key and tempo widgets - if (tempoRatio) { - pitchRatio *= (speed / tempoRatio); - } + bool useIndependentPitchAndTempoScaling = false; + + // TODO(owen): Maybe change this so that rubberband doesn't disable + // keylock on scratch. (just check m_pScaleKeylock == m_pScaleST) + if (is_scratching || fabs(speed) > 1.9) { + // Scratching and high speeds with always disables keylock + // because Soundtouch sounds terrible in these conditions. Rubberband + // sounds better, but still has some problems (it may reallocate in + // a party-crashing manner at extremely slow speeds). + // High seek speeds also disables keylock. Our pitch slider could go + // to 90%, so that's the cutoff point. + + // Force pitchRatio to the linear pitch set by speed + pitchRatio = speed; + // This is for the natural speed pitch found on turn tables + } else if (fabs(speed) < 0.1) { + // We have pre-allocated big buffers in Rubberband and Soundtouch for + // a minimum speed of 0.1. Slower speeds will re-allocate much bigger + // buffers which may cause underruns. + // Disable keylock under these conditions. + + // Force pitchRatio to the linear pitch set by speed + pitchRatio = speed; + } else if (keylock_enabled) { + // always use IndependentPitchAndTempoScaling + // to avoid clicks when crossing the linear pitch + // in this case it is most likely that the user + // will have an non linear pitch + // Note: We have undesired noise when cossfading between scalers + useIndependentPitchAndTempoScaling = true; + } else { + // We might have have temporary speed change, so adjust pitch if not locked + // Note: This will not update key and tempo widgets + if (tempoRatio) { + pitchRatio *= (speed / tempoRatio); + } - // Check if we are off-linear (musical key has been adjusted - // independent from speed) to determine if the keylock scaler - // should be used even though keylock is disabled. - if (speed != 0.0) { - double offlinear = pitchRatio / speed; - if (offlinear > kLinearScalerElipsis || - offlinear < 1 / kLinearScalerElipsis) { - // only enable keylock scaler if pitch adjustment is at - // least 1 cent. Everything below is not hear-able. - useIndependentPitchAndTempoScaling = true; - } + // Check if we are off-linear (musical key has been adjusted + // independent from speed) to determine if the keylock scaler + // should be used even though keylock is disabled. + if (speed != 0.0) { + double offlinear = pitchRatio / speed; + if (offlinear > kLinearScalerElipsis || + offlinear < 1 / kLinearScalerElipsis) { + // only enable keylock scaler if pitch adjustment is at + // least 1 cent. Everything below is not hear-able. + useIndependentPitchAndTempoScaling = true; } } + } - if (speed != 0.0) { - // Do not switch scaler when we have no transport - enableIndependentPitchTempoScaling(useIndependentPitchAndTempoScaling, - iBufferSize); - } else if (m_speed_old && !is_scratching) { - // we are stopping, collect samples for fade out + if (speed != 0.0) { + // Do not switch scaler when we have no transport + enableIndependentPitchTempoScaling(useIndependentPitchAndTempoScaling, + iBufferSize); + } else if (m_speed_old && !is_scratching) { + // we are stopping, collect samples for fade out + readToCrossfadeBuffer(iBufferSize); + // Clear the scaler information + m_pScale->clear(); + } + + // How speed/tempo/pitch are related: + // Processing is done in two parts, the first part is calculated inside + // the KeyKontrol class and effects the visual key/pitch widgets. + // The Speed slider controls the tempoRatio and a speedSliderPitchRatio, + // the pitch amount caused by it. + // By default the speed slider controls pitch and tempo with the same + // value. + // If key lock is enabled, the speedSliderPitchRatio is decoupled from + // the speed slider (const). + // + // With preference mode KeylockMode = kLockOriginalKey + // the speedSliderPitchRatio is reset to 1 and back to the tempoRatio + // (natural vinyl Pitch) when keylock is disabled and enabled. + // + // With preference mode KeylockMode = kCurrentKey + // the speedSliderPitchRatio is not reseted when keylock is enabled. + // This mode allows to enable keylock + // while the track is already played. You can reset to the tracks + // original pitch by resetting the pitch knob to center. When disabling + // keylock the pitch is reset to the linear vinyl pitch. + + // The Pitch knob turns if the speed slider is moved without keylock. + // This is useful to get always an analog impression of current pitch, + // and its distance to the original track pitch + // + // The Pitch_Adjust knob does not reflect the speedSliderPitchRatio. + // So it is is useful for controller mappings, because it is not + // changed by the speed slider or keylock. + + // In the second part all other speed changing controls are processed. + // They may produce an additional pitch if keylock is disabled or + // override the pitch in scratching case. + // If pitch ratio and tempo ratio are equal, a linear scaler is used, + // otherwise tempo and pitch are processed individual + + // If we were scratching, and scratching is over, and we're a follower, + // and we're quantized, and not paused, + // we need to sync phase or we'll be totally out of whack and the sync + // adjuster will kick in and push the track back in to sync with the + // master. + if (m_scratching_old && !is_scratching && m_pQuantize->toBool() + && m_pSyncControl->getSyncMode() == SYNC_FOLLOWER && !paused) { + // TODO() The resulting seek is processed in the following callback + // That is to late + requestSyncPhase(); + } + + double rate = 0; + // If the baserate, speed, or pitch has changed, we need to update the + // scaler. Also, if we have changed scalers then we need to update the + // scaler. + if (baserate != m_baserate_old || speed != m_speed_old || + pitchRatio != m_pitch_old || tempoRatio != m_tempo_ratio_old || + m_bScalerChanged) { + // The rate returned by the scale object can be different from the + // wanted rate! Make sure new scaler has proper position. This also + // crossfades between the old scaler and new scaler to prevent + // clicks. + + // Handle direction change. + // The linear scaler supports ramping though zero. + // This is used for scratching, but not for reverse + // For the other, crossfade forward and backward samples + if ((m_speed_old * speed < 0) && // Direction has changed! + (m_pScale != m_pScaleVinyl || // only m_pScaleLinear supports going though 0 + m_reverse_old != is_reverse)) { // no pitch change when reversing + //XXX: Trying to force RAMAN to read from correct + // playpos when rate changes direction - Albert readToCrossfadeBuffer(iBufferSize); // Clear the scaler information m_pScale->clear(); } - // How speed/tempo/pitch are related: - // Processing is done in two parts, the first part is calculated inside - // the KeyKontrol class and effects the visual key/pitch widgets. - // The Speed slider controls the tempoRatio and a speedSliderPitchRatio, - // the pitch amount caused by it. - // By default the speed slider controls pitch and tempo with the same - // value. - // If key lock is enabled, the speedSliderPitchRatio is decoupled from - // the speed slider (const). - // - // With preference mode KeylockMode = kLockOriginalKey - // the speedSliderPitchRatio is reset to 1 and back to the tempoRatio - // (natural vinyl Pitch) when keylock is disabled and enabled. - // - // With preference mode KeylockMode = kCurrentKey - // the speedSliderPitchRatio is not reseted when keylock is enabled. - // This mode allows to enable keylock - // while the track is already played. You can reset to the tracks - // original pitch by resetting the pitch knob to center. When disabling - // keylock the pitch is reset to the linear vinyl pitch. - - // The Pitch knob turns if the speed slider is moved without keylock. - // This is useful to get always an analog impression of current pitch, - // and its distance to the original track pitch - // - // The Pitch_Adjust knob does not reflect the speedSliderPitchRatio. - // So it is is useful for controller mappings, because it is not - // changed by the speed slider or keylock. - - // In the second part all other speed changing controls are processed. - // They may produce an additional pitch if keylock is disabled or - // override the pitch in scratching case. - // If pitch ratio and tempo ratio are equal, a linear scaler is used, - // otherwise tempo and pitch are processed individual - - // If we were scratching, and scratching is over, and we're a follower, - // and we're quantized, and not paused, - // we need to sync phase or we'll be totally out of whack and the sync - // adjuster will kick in and push the track back in to sync with the - // master. - if (m_scratching_old && !is_scratching && m_pQuantize->toBool() - && m_pSyncControl->getSyncMode() == SYNC_FOLLOWER && !paused) { - // TODO() The resulting seek is processed in the following callback - // That is to late - requestSyncPhase(); - } - - // If the baserate, speed, or pitch has changed, we need to update the - // scaler. Also, if we have changed scalers then we need to update the - // scaler. - if (baserate != m_baserate_old || speed != m_speed_old || - pitchRatio != m_pitch_old || tempoRatio != m_tempo_ratio_old || - m_bScalerChanged) { - // The rate returned by the scale object can be different from the - // wanted rate! Make sure new scaler has proper position. This also - // crossfades between the old scaler and new scaler to prevent - // clicks. - - // Handle direction change. - // The linear scaler supports ramping though zero. - // This is used for scratching, but not for reverse - // For the other, crossfade forward and backward samples - if ((m_speed_old * speed < 0) && // Direction has changed! - (m_pScale != m_pScaleVinyl || // only m_pScaleLinear supports going though 0 - m_reverse_old != is_reverse)) { // no pitch change when reversing - //XXX: Trying to force RAMAN to read from correct - // playpos when rate changes direction - Albert - readToCrossfadeBuffer(iBufferSize); - // Clear the scaler information - m_pScale->clear(); - } - - m_baserate_old = baserate; - m_speed_old = speed; - m_pitch_old = pitchRatio; - m_tempo_ratio_old = tempoRatio; - m_reverse_old = is_reverse; + m_baserate_old = baserate; + m_speed_old = speed; + m_pitch_old = pitchRatio; + m_tempo_ratio_old = tempoRatio; + m_reverse_old = is_reverse; - // Now we need to update the scaler with the master sample rate, the - // base rate (ratio between sample rate of the source audio and the - // master samplerate), the deck speed, the pitch shift, and whether - // the deck speed should affect the pitch. + // Now we need to update the scaler with the master sample rate, the + // base rate (ratio between sample rate of the source audio and the + // master samplerate), the deck speed, the pitch shift, and whether + // the deck speed should affect the pitch. m_pScale->setScaleParameters(baserate, &speed, &pitchRatio); - // The way we treat rate inside of EngineBuffer is actually a - // description of "sample consumption rate" or percentage of samples - // consumed relative to playing back the track at its native sample - // rate and normal speed. pitch_adjust does not change the playback - // rate. - rate = baserate * speed; + // The way we treat rate inside of EngineBuffer is actually a + // description of "sample consumption rate" or percentage of samples + // consumed relative to playing back the track at its native sample + // rate and normal speed. pitch_adjust does not change the playback + // rate. + rate = baserate * speed; + + // Scaler is up to date now. + m_bScalerChanged = false; + } else { + // Scaler did not need updating. By definition this means we are at + // our old rate. + rate = m_rate_old; + } - // Scaler is up to date now. - m_bScalerChanged = false; + bool at_start = m_filepos_play <= 0; + bool at_end = m_filepos_play >= m_trackSamplesOld; + bool backwards = rate < 0; + + bool bCurBufferPaused = false; + if (at_end && !backwards) { + // do not play past end + bCurBufferPaused = true; + } else if (rate == 0 && !is_scratching) { + // do not process samples if have no transport + // the linear scaler supports ramping down to 0 + // this is used for pause by scratching only + bCurBufferPaused = true; + } + + m_rate_old = rate; + + // If the buffer is not paused, then scale the audio. + if (!bCurBufferPaused) { + // Perform scaling of Reader buffer into buffer. + double framesRead = + m_pScale->scaleBuffer(pOutput, iBufferSize); + + // TODO(XXX): The result framesRead might not be an integer value. + // Converting to samples here does not make sense. All positional + // calculations should be done in frames instead of samples! Otherwise + // rounding errors might occur when converting from samples back to + // frames later. + double samplesRead = framesRead * kSamplesPerFrame; + + if (m_bScalerOverride) { + // If testing, we don't have a real log so we fake the position. + m_filepos_play += samplesRead; } else { - // Scaler did not need updating. By definition this means we are at - // our old rate. - rate = m_rate_old; + // Adjust filepos_play by the amount we processed. + m_filepos_play = + m_pReadAheadManager->getFilePlaypositionFromLog( + m_filepos_play, samplesRead); } - - bool at_start = m_filepos_play <= 0; - bool at_end = m_filepos_play >= m_trackSamplesOld; - bool backwards = rate < 0; - - if (at_end && !backwards) { - // do not play past end - bCurBufferPaused = true; - } else if (rate == 0 && !is_scratching) { - // do not process samples if have no transport - // the linear scaler supports ramping down to 0 - // this is used for pause by scratching only - bCurBufferPaused = true; + if (m_bCrossfadeReady) { + SampleUtil::linearCrossfadeBuffers( + pOutput, m_pCrossfadeBuffer, pOutput, iBufferSize); } - - m_rate_old = rate; - - // If the buffer is not paused, then scale the audio. - if (!bCurBufferPaused) { - // Perform scaling of Reader buffer into buffer. - double framesRead = - m_pScale->scaleBuffer(pOutput, iBufferSize); - - // TODO(XXX): The result framesRead might not be an integer value. - // Converting to samples here does not make sense. All positional - // calculations should be done in frames instead of samples! Otherwise - // rounding errors might occur when converting from samples back to - // frames later. - double samplesRead = framesRead * kSamplesPerFrame; - - if (m_bScalerOverride) { - // If testing, we don't have a real log so we fake the position. - m_filepos_play += samplesRead; - } else { - // Adjust filepos_play by the amount we processed. - m_filepos_play = - m_pReadAheadManager->getFilePlaypositionFromLog( - m_filepos_play, samplesRead); - } - if (m_bCrossfadeReady) { - SampleUtil::linearCrossfadeBuffers( - pOutput, m_pCrossfadeBuffer, pOutput, iBufferSize); - } - // Note: we do not fade here if we pass the end or the start of - // the track in reverse direction - // because we assume that the track samples itself start and stop - // towards zero. - // If it turns out that ramping is required be aware that the end - // or start may pass in the middle of the buffer. + // Note: we do not fade here if we pass the end or the start of + // the track in reverse direction + // because we assume that the track samples itself start and stop + // towards zero. + // If it turns out that ramping is required be aware that the end + // or start may pass in the middle of the buffer. + } else { + // Pause + if (m_bCrossfadeReady) { + // We don't ramp here, since EnginePregain handles fades + // from and to speed == 0 + SampleUtil::copy(pOutput, m_pCrossfadeBuffer, iBufferSize); } else { - // Pause - if (m_bCrossfadeReady) { - // We don't ramp here, since EnginePregain handles fades - // from and to speed == 0 - SampleUtil::copy(pOutput, m_pCrossfadeBuffer, iBufferSize); - } else { - SampleUtil::clear(pOutput, iBufferSize); - } + SampleUtil::clear(pOutput, iBufferSize); } + } - for (const auto& pControl: m_engineControls) { - pControl->setCurrentSample(m_filepos_play, m_trackSamplesOld); - pControl->process(rate, m_filepos_play, m_trackSamplesOld, iBufferSize); - } + for (const auto& pControl: m_engineControls) { + pControl->setCurrentSample(m_filepos_play, m_trackSamplesOld); + pControl->process(rate, m_filepos_play, m_trackSamplesOld, iBufferSize); + } - m_scratching_old = is_scratching; + m_scratching_old = is_scratching; - // Handle repeat mode - at_start = m_filepos_play <= 0; - at_end = m_filepos_play >= m_trackSamplesOld; + // Handle repeat mode + at_start = m_filepos_play <= 0; + at_end = m_filepos_play >= m_trackSamplesOld; - bool repeat_enabled = m_pRepeat->get() != 0.0; + bool repeat_enabled = m_pRepeat->get() != 0.0; - bool end_of_track = //(at_start && backwards) || + bool end_of_track = //(at_start && backwards) || (at_end && !backwards); - // If playbutton is pressed, check if we are at start or end of track + // If playbutton is pressed, check if we are at start or end of track if ((m_playButton->get() || (m_fwdButton->get() || m_backButton->get())) && end_of_track) { - if (repeat_enabled) { - double fractionalPos = at_start ? 1.0 : 0; - doSeekFractional(fractionalPos, SEEK_STANDARD); - } else { - m_playButton->set(0.); - } + if (repeat_enabled) { + double fractionalPos = at_start ? 1.0 : 0; + doSeekFractional(fractionalPos, SEEK_STANDARD); + } else { + m_playButton->set(0.); } + } - // Give the Reader hints as to which chunks of the current song we - // really care about. It will try very hard to keep these in memory - hintReader(rate); + // Give the Reader hints as to which chunks of the current song we + // really care about. It will try very hard to keep these in memory + hintReader(rate); +} +void EngineBuffer::process(CSAMPLE* pOutput, const int iBufferSize) { + // Bail if we receive a buffer size with incomplete sample frames. Assert in debug builds. + VERIFY_OR_DEBUG_ASSERT((iBufferSize % kSamplesPerFrame) == 0) { + return; + } + m_pReader->process(); + // Steps: + // - Lookup new reader information + // - Calculate current rate + // - Scale the audio with m_pScale, copy the resulting samples into the + // output buffer + // - Give EngineControl's a chance to do work / request seeks, etc + // - Process repeat mode if we're at the end or beginning of a track + // - Set last sample value (m_fLastSampleValue) so that rampOut works? Other + // miscellaneous upkeep issues. + + int sample_rate = static_cast(m_pSampleRate->get()); + + // If the sample rate has changed, force Rubberband to reset so that + // it doesn't reallocate when the user engages keylock during playback. + // We do this even if rubberband is not active. + if (sample_rate != m_iSampleRate) { + m_pScaleLinear->setSampleRate(sample_rate); + m_pScaleST->setSampleRate(sample_rate); + m_pScaleRB->setSampleRate(sample_rate); + m_iSampleRate = sample_rate; + } + + bool bTrackLoading = load_atomic(m_iTrackLoading) != 0; + if (!bTrackLoading && m_pause.tryLock()) { + processTrackLocked(pOutput, iBufferSize, sample_rate); // release the pauselock m_pause.unlock(); - } else { // if (!bTrackLoading && m_pause.tryLock()) { + } else { // We are loading a new Track - bCurBufferPaused = true; // Here the old track was playing and loading the new track is in // progress. We can't predict when it happens, so we are not able diff --git a/src/engine/enginebuffer.h b/src/engine/enginebuffer.h index 15e9d8b57d5..792bc82138f 100644 --- a/src/engine/enginebuffer.h +++ b/src/engine/enginebuffer.h @@ -236,12 +236,13 @@ class EngineBuffer : public EngineObject { bool updateIndicatorsAndModifyPlay(bool newPlay); void verifyPlay(); void notifyTrackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack); + void processTrackLocked(CSAMPLE* pOutput, const int iBufferSize, int sample_rate); // Holds the name of the control group QString m_group; UserSettingsPointer m_pConfig; - LoopingControl* m_pLoopingControl; + LoopingControl* m_pLoopingControl; // used for testes FRIEND_TEST(LoopingControlTest, LoopScale_HalvesLoop); FRIEND_TEST(LoopingControlTest, LoopMoveTest); FRIEND_TEST(LoopingControlTest, LoopResizeSeek); diff --git a/src/engine/enginecontrol.cpp b/src/engine/enginecontrol.cpp index 632553ce5c0..343da55b025 100644 --- a/src/engine/enginecontrol.cpp +++ b/src/engine/enginecontrol.cpp @@ -11,8 +11,8 @@ EngineControl::EngineControl(QString group, UserSettingsPointer pConfig) : m_group(group), m_pConfig(pConfig), - m_pEngineMaster(NULL), - m_pEngineBuffer(NULL) { + m_pEngineMaster(nullptr), + m_pEngineBuffer(nullptr) { setCurrentSample(0.0, 0.0); } From 7c87fc9e0159a8ed7e22d3ff8c84f8643f9fb15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 24 Nov 2018 00:33:38 +0100 Subject: [PATCH 04/27] removed unused pOldTrack from trackLoaded function --- src/engine/bpmcontrol.cpp | 3 +-- src/engine/bpmcontrol.h | 6 ++---- src/engine/clockcontrol.cpp | 4 +--- src/engine/clockcontrol.h | 4 ++-- src/engine/cuecontrol.cpp | 4 +--- src/engine/cuecontrol.h | 5 ++--- src/engine/enginebuffer.cpp | 4 ++-- src/engine/enginecontrol.cpp | 3 +-- src/engine/enginecontrol.h | 2 +- src/engine/loopingcontrol.cpp | 4 +--- src/engine/loopingcontrol.h | 4 ++-- src/engine/quantizecontrol.cpp | 3 +-- src/engine/quantizecontrol.h | 6 ++---- src/engine/ratecontrol.cpp | 3 +-- src/engine/ratecontrol.h | 4 ++-- src/engine/sync/synccontrol.cpp | 3 +-- src/engine/sync/synccontrol.h | 4 +--- src/engine/vinylcontrolcontrol.cpp | 3 +-- src/engine/vinylcontrolcontrol.h | 4 ++-- src/test/readaheadmanager_test.cpp | 4 +--- 20 files changed, 28 insertions(+), 49 deletions(-) diff --git a/src/engine/bpmcontrol.cpp b/src/engine/bpmcontrol.cpp index 123ab756fdd..293d67e0d56 100644 --- a/src/engine/bpmcontrol.cpp +++ b/src/engine/bpmcontrol.cpp @@ -745,8 +745,7 @@ void BpmControl::slotUpdateRateSlider() { m_pRateSlider->set(dRateSlider); } -void BpmControl::trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) { - Q_UNUSED(pOldTrack); +void BpmControl::trackLoaded(TrackPointer pNewTrack) { if (m_pTrack) { disconnect(m_pTrack.get(), SIGNAL(beatsUpdated()), this, SLOT(slotUpdatedTrackBeats())); diff --git a/src/engine/bpmcontrol.h b/src/engine/bpmcontrol.h index 12e28297aa0..0e3862095e9 100644 --- a/src/engine/bpmcontrol.h +++ b/src/engine/bpmcontrol.h @@ -70,9 +70,7 @@ class BpmControl : public EngineControl { // Example: shortestPercentageChange(0.99, 0.01) == 0.02 static double shortestPercentageChange(const double& current_percentage, const double& target_percentage); - - public slots: - void trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) override; + void trackLoaded(TrackPointer pNewTrack) override; private slots: void slotFileBpmChanged(double); @@ -158,7 +156,7 @@ class BpmControl : public EngineControl { TapFilter m_tapFilter; - TrackPointer m_pTrack; + TrackPointer m_pTrack; // is witten from an engine worker thread BeatsPointer m_pBeats; QString m_sGroup; diff --git a/src/engine/clockcontrol.cpp b/src/engine/clockcontrol.cpp index 841b4662159..f25ee2831f5 100644 --- a/src/engine/clockcontrol.cpp +++ b/src/engine/clockcontrol.cpp @@ -17,9 +17,7 @@ ClockControl::~ClockControl() { delete m_pCOSampleRate; } -void ClockControl::trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) { - Q_UNUSED(pOldTrack); - +void ClockControl::trackLoaded(TrackPointer pNewTrack) { // Clear on-beat control m_pCOBeatActive->set(0.0); diff --git a/src/engine/clockcontrol.h b/src/engine/clockcontrol.h index 3aa83a87d66..5b51eb95fdf 100644 --- a/src/engine/clockcontrol.h +++ b/src/engine/clockcontrol.h @@ -22,13 +22,13 @@ class ClockControl: public EngineControl { const double totalSamples, const int iBufferSize) override; public slots: - void trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) override; + void trackLoaded(TrackPointer pNewTrack) override; void slotBeatsUpdated(); private: ControlObject* m_pCOBeatActive; ControlProxy* m_pCOSampleRate; - TrackPointer m_pTrack; + TrackPointer m_pTrack; // is witten from an engine worker thread BeatsPointer m_pBeats; }; diff --git a/src/engine/cuecontrol.cpp b/src/engine/cuecontrol.cpp index 76edcfd5c6c..86b5f3528a0 100644 --- a/src/engine/cuecontrol.cpp +++ b/src/engine/cuecontrol.cpp @@ -187,10 +187,8 @@ void CueControl::detachCue(int hotCue) { pControl->resetCue(); } -void CueControl::trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) { +void CueControl::trackLoaded(TrackPointer pNewTrack) { QMutexLocker lock(&m_mutex); - - DEBUG_ASSERT(m_pLoadedTrack == pOldTrack); if (m_pLoadedTrack) { disconnect(m_pLoadedTrack.get(), 0, this, 0); for (int i = 0; i < m_iNumHotCues; ++i) { diff --git a/src/engine/cuecontrol.h b/src/engine/cuecontrol.h index 7b8faddb0cd..788f4114733 100644 --- a/src/engine/cuecontrol.h +++ b/src/engine/cuecontrol.h @@ -103,8 +103,7 @@ class CueControl : public EngineControl { bool isPlayingByPlayButton(); bool getPlayFlashingAtPause(); - public slots: - void trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) override; + void trackLoaded(TrackPointer pNewTrack) override; private slots: void cueUpdated(); @@ -165,7 +164,7 @@ class CueControl : public EngineControl { ControlProxy* m_pVinylControlEnabled; ControlProxy* m_pVinylControlMode; - TrackPointer m_pLoadedTrack; + TrackPointer m_pLoadedTrack; // is witten from an engine worker thread // Tells us which controls map to which hotcue QMap m_controlMap; diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index 21cb2eb0f64..aeba3b13516 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -501,7 +501,7 @@ void EngineBuffer::loadFakeTrack(TrackPointer pTrack, bool bPlay) { slotTrackLoaded(pTrack, pTrack->getSampleRate(), pTrack->getSampleRate() * pTrack->getDurationInt()); m_pSyncControl->setLocalBpm(pTrack->getBpm()); - m_pSyncControl->trackLoaded(pTrack, TrackPointer()); + m_pSyncControl->trackLoaded(pTrack); } // WARNING: Always called from the EngineWorker thread pool @@ -1372,7 +1372,7 @@ void EngineBuffer::notifyTrackLoaded( // First inform engineControls directly // Note: we are still in a worker thread. for (const auto& pControl: m_engineControls) { - pControl->trackLoaded(pNewTrack, pOldTrack); + pControl->trackLoaded(pNewTrack); } // Inform BaseTrackPlayer via a queued connection emit(trackLoaded(pNewTrack, pOldTrack)); diff --git a/src/engine/enginecontrol.cpp b/src/engine/enginecontrol.cpp index 343da55b025..7a232e20475 100644 --- a/src/engine/enginecontrol.cpp +++ b/src/engine/enginecontrol.cpp @@ -29,9 +29,8 @@ void EngineControl::process(const double dRate, Q_UNUSED(iBufferSize); } -void EngineControl::trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) { +void EngineControl::trackLoaded(TrackPointer pNewTrack) { Q_UNUSED(pNewTrack); - Q_UNUSED(pOldTrack); } void EngineControl::hintReader(HintVector*) { diff --git a/src/engine/enginecontrol.h b/src/engine/enginecontrol.h index fec4160fb8e..fe5bc686e9c 100644 --- a/src/engine/enginecontrol.h +++ b/src/engine/enginecontrol.h @@ -67,7 +67,7 @@ class EngineControl : public QObject { // Called whenever a seek occurs to allow the EngineControl to respond. virtual void notifySeek(double dNewPlaypo, bool adjustingPhase); - virtual void trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack); + virtual void trackLoaded(TrackPointer pNewTrack); protected: void seek(double fractionalPosition); diff --git a/src/engine/loopingcontrol.cpp b/src/engine/loopingcontrol.cpp index d8a92fc536e..8c7b00a4adb 100644 --- a/src/engine/loopingcontrol.cpp +++ b/src/engine/loopingcontrol.cpp @@ -801,9 +801,7 @@ void LoopingControl::setLoopingEnabled(bool enabled) { } } -void LoopingControl::trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) { - Q_UNUSED(pOldTrack); - +void LoopingControl::trackLoaded(TrackPointer pNewTrack) { if (m_pTrack) { disconnect(m_pTrack.get(), SIGNAL(beatsUpdated()), this, SLOT(slotUpdatedTrackBeats())); diff --git a/src/engine/loopingcontrol.h b/src/engine/loopingcontrol.h index 01d3644b121..4795571d5f0 100644 --- a/src/engine/loopingcontrol.h +++ b/src/engine/loopingcontrol.h @@ -60,7 +60,7 @@ class LoopingControl : public EngineControl { void slotReloopAndStop(double); void slotLoopStartPos(double); void slotLoopEndPos(double); - virtual void trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) override; + void trackLoaded(TrackPointer pNewTrack) override; void slotUpdatedTrackBeats(); // Generate a loop of 'beats' length. It can also do fractions for a @@ -158,7 +158,7 @@ class LoopingControl : public EngineControl { ControlObject* m_pCOLoopMove; QList m_loopMoves; - TrackPointer m_pTrack; + TrackPointer m_pTrack; // is witten from an engine worker thread BeatsPointer m_pBeats; }; diff --git a/src/engine/quantizecontrol.cpp b/src/engine/quantizecontrol.cpp index faebf241e17..0f78bab377b 100644 --- a/src/engine/quantizecontrol.cpp +++ b/src/engine/quantizecontrol.cpp @@ -32,8 +32,7 @@ QuantizeControl::~QuantizeControl() { delete m_pCOClosestBeat; } -void QuantizeControl::trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) { - Q_UNUSED(pOldTrack); +void QuantizeControl::trackLoaded(TrackPointer pNewTrack) { if (m_pTrack) { disconnect(m_pTrack.get(), SIGNAL(beatsUpdated()), this, SLOT(slotBeatsUpdated())); diff --git a/src/engine/quantizecontrol.h b/src/engine/quantizecontrol.h index 76baef63ebf..06ab912c15d 100644 --- a/src/engine/quantizecontrol.h +++ b/src/engine/quantizecontrol.h @@ -20,9 +20,7 @@ class QuantizeControl : public EngineControl { void setCurrentSample(const double dCurrentSample, const double dTotalSamples) override; - - public slots: - void trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) override; + void trackLoaded(TrackPointer pNewTrack) override; private slots: void slotBeatsUpdated(); @@ -39,7 +37,7 @@ class QuantizeControl : public EngineControl { ControlObject* m_pCOPrevBeat; ControlObject* m_pCOClosestBeat; - TrackPointer m_pTrack; + TrackPointer m_pTrack; // is witten from an engine worker thread BeatsPointer m_pBeats; }; diff --git a/src/engine/ratecontrol.cpp b/src/engine/ratecontrol.cpp index 644b5e385ff..05bd3e31cc3 100644 --- a/src/engine/ratecontrol.cpp +++ b/src/engine/ratecontrol.cpp @@ -393,8 +393,7 @@ void RateControl::slotControlRateTempUpSmall(double) } } -void RateControl::trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) { - Q_UNUSED(pOldTrack); +void RateControl::trackLoaded(TrackPointer pNewTrack) { m_pTrack = pNewTrack; } diff --git a/src/engine/ratecontrol.h b/src/engine/ratecontrol.h index bd8b407fe56..9a98d035ec0 100644 --- a/src/engine/ratecontrol.h +++ b/src/engine/ratecontrol.h @@ -106,7 +106,7 @@ class RateControl : public EngineControl { void slotControlRateTempUpSmall(double); void slotControlFastForward(double); void slotControlFastBack(double); - void trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) override; + void trackLoaded(TrackPointer pNewTrack) override; private: double getJogFactor() const; @@ -163,7 +163,7 @@ class RateControl : public EngineControl { ControlObject* m_pSampleRate; - TrackPointer m_pTrack; + TrackPointer m_pTrack; // is witten from an engine worker thread // For Master Sync BpmControl* m_pBpmControl; diff --git a/src/engine/sync/synccontrol.cpp b/src/engine/sync/synccontrol.cpp index fdab298be46..42c535a1095 100644 --- a/src/engine/sync/synccontrol.cpp +++ b/src/engine/sync/synccontrol.cpp @@ -317,8 +317,7 @@ void SyncControl::reportTrackPosition(double fractionalPlaypos) { } } -void SyncControl::trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) { - Q_UNUSED(pOldTrack); +void SyncControl::trackLoaded(TrackPointer pNewTrack) { //qDebug() << getGroup() << "SyncControl::trackLoaded"; if (getSyncMode() == SYNC_MASTER) { // If we change or remove a new track while master, hand off. diff --git a/src/engine/sync/synccontrol.h b/src/engine/sync/synccontrol.h index d5020cde648..d2d127bd0d9 100644 --- a/src/engine/sync/synccontrol.h +++ b/src/engine/sync/synccontrol.h @@ -57,9 +57,7 @@ class SyncControl : public EngineControl, public Syncable { void reportTrackPosition(double fractionalPlaypos); void reportPlayerSpeed(double speed, bool scratching); - - public slots: - void trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) override; + void trackLoaded(TrackPointer pNewTrack) override; private slots: // Fired by changes in play. diff --git a/src/engine/vinylcontrolcontrol.cpp b/src/engine/vinylcontrolcontrol.cpp index 9ed43b6e54b..53b2bb7f2ee 100644 --- a/src/engine/vinylcontrolcontrol.cpp +++ b/src/engine/vinylcontrolcontrol.cpp @@ -62,8 +62,7 @@ VinylControlControl::~VinylControlControl() { delete m_pControlVinylStatus; } -void VinylControlControl::trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) { - Q_UNUSED(pOldTrack); +void VinylControlControl::trackLoaded(TrackPointer pNewTrack) { m_pCurrentTrack = pNewTrack; } diff --git a/src/engine/vinylcontrolcontrol.h b/src/engine/vinylcontrolcontrol.h index cb7e033c435..f3bcae260fe 100644 --- a/src/engine/vinylcontrolcontrol.h +++ b/src/engine/vinylcontrolcontrol.h @@ -18,10 +18,10 @@ class VinylControlControl : public EngineControl { void notifySeekQueued(); bool isEnabled(); bool isScratching(); + void trackLoaded(TrackPointer pNewTrack) override; private slots: void slotControlVinylSeek(double fractionalPos); - void trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) override; private: ControlObject* m_pControlVinylRate; @@ -35,7 +35,7 @@ class VinylControlControl : public EngineControl { ControlPushButton* m_pControlVinylCueing; ControlPushButton* m_pControlVinylSignalEnabled; ControlProxy* m_pPlayEnabled; - TrackPointer m_pCurrentTrack; + TrackPointer m_pCurrentTrack; // is witten from an engine worker thread bool m_bSeekRequested; }; diff --git a/src/test/readaheadmanager_test.cpp b/src/test/readaheadmanager_test.cpp index 1573663dbc7..69281f402c3 100644 --- a/src/test/readaheadmanager_test.cpp +++ b/src/test/readaheadmanager_test.cpp @@ -61,10 +61,8 @@ class StubLoopControl : public LoopingControl { Q_UNUSED(adjustingPhase); } - public slots: - void trackLoaded(TrackPointer pTrack, TrackPointer pOldTrack) override { + void trackLoaded(TrackPointer pTrack) override { Q_UNUSED(pTrack); - Q_UNUSED(pOldTrack); } protected: From 8345b0d1a7819d9a9571bb3e040a0997488689dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 24 Nov 2018 21:54:37 +0100 Subject: [PATCH 05/27] rename m_pSampleRate to m_pMasterSampleRate to distinguish from track sample rate --- src/engine/enginebuffer.cpp | 9 ++++----- src/engine/enginebuffer.h | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index aeba3b13516..897eb2b40d9 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -171,8 +171,7 @@ EngineBuffer::EngineBuffer(QString group, UserSettingsPointer pConfig, m_pRepeat = new ControlPushButton(ConfigKey(m_group, "repeat")); m_pRepeat->setButtonMode(ControlPushButton::TOGGLE); - // Sample rate - m_pSampleRate = new ControlProxy("[Master]", "samplerate", this); + m_pMasterSampleRate = new ControlProxy("[Master]", "samplerate", this); m_pKeylockEngine = new ControlProxy("[Master]", "keylock_engine", this); m_pKeylockEngine->connectValueChanged(SLOT(slotKeylockEngineChanged(double)), @@ -300,7 +299,7 @@ EngineBuffer::~EngineBuffer() { delete m_pSlipButton; delete m_pRepeat; - delete m_pSampleRate; + delete m_pMasterSampleRate; delete m_pTrackLoaded; delete m_pTrackSamples; @@ -1038,7 +1037,7 @@ void EngineBuffer::process(CSAMPLE* pOutput, const int iBufferSize) { // - Set last sample value (m_fLastSampleValue) so that rampOut works? Other // miscellaneous upkeep issues. - int sample_rate = static_cast(m_pSampleRate->get()); + int sample_rate = static_cast(m_pMasterSampleRate->get()); // If the sample rate has changed, force Rubberband to reset so that // it doesn't reallocate when the user engages keylock during playback. @@ -1233,7 +1232,7 @@ void EngineBuffer::updateIndicators(double speed, int iBufferSize) { // Update indicators that are only updated after every // sampleRate/kiUpdateRate samples processed. (e.g. playposSlider) - if (m_iSamplesCalculated > (m_pSampleRate->get() / kiPlaypositionUpdateRate)) { + if (m_iSamplesCalculated > (kSamplesPerFrame * m_pMasterSampleRate->get() / kiPlaypositionUpdateRate)) { const double samplePositionToSeconds = 1.0 / m_trackSampleRateOld / kSamplesPerFrame / m_tempo_ratio_old; m_timeElapsed->set(m_filepos_play * samplePositionToSeconds); diff --git a/src/engine/enginebuffer.h b/src/engine/enginebuffer.h index 792bc82138f..e1e42bf83df 100644 --- a/src/engine/enginebuffer.h +++ b/src/engine/enginebuffer.h @@ -342,7 +342,7 @@ class EngineBuffer : public EngineObject { ControlObject* m_timeElapsed; ControlObject* m_timeRemaining; ControlPotmeter* m_playposSlider; - ControlProxy* m_pSampleRate; + ControlProxy* m_pMasterSampleRate; ControlProxy* m_pKeylockEngine; ControlPushButton* m_pKeylock; From fb25a897c067f5d08e953a4bb36e6f0420d88797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 24 Nov 2018 23:05:18 +0100 Subject: [PATCH 06/27] fix m_trackSampleRateOld handling --- src/engine/enginebuffer.cpp | 10 +++++++--- src/engine/enginebuffer.h | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index 897eb2b40d9..5345569aacc 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -513,7 +513,6 @@ void EngineBuffer::slotTrackLoaded(TrackPointer pTrack, m_pause.lock(); m_visualPlayPos->setInvalid(); m_pCurrentTrack = pTrack; - m_trackSampleRateOld = iTrackSampleRate; m_trackSamplesOld = iTrackNumSamples; m_pTrackSamples->set(iTrackNumSamples); m_pTrackSampleRate->set(iTrackSampleRate); @@ -557,7 +556,6 @@ void EngineBuffer::ejectTrack() { m_pTrackSampleRate->set(0); TrackPointer pTrack = m_pCurrentTrack; m_pCurrentTrack.reset(); - m_trackSampleRateOld = 0; m_trackSamplesOld = 0; m_playButton->set(0.0); m_visualBpm->set(0.0); @@ -727,9 +725,11 @@ void EngineBuffer::processTrackLocked( CSAMPLE* pOutput, const int iBufferSize, int sample_rate) { ScopedTimer t("EngineBuffer::process_pauselock"); + m_trackSampleRateOld = m_pTrackSampleRate->get(); + double baserate = 0.0; if (sample_rate > 0) { - baserate = ((double)m_trackSampleRateOld / sample_rate); + baserate = m_trackSampleRateOld / sample_rate; } // Note: play is also active during cue preview @@ -1215,6 +1215,10 @@ void EngineBuffer::postProcess(const int iBufferSize) { } void EngineBuffer::updateIndicators(double speed, int iBufferSize) { + if (!m_trackSampleRateOld) { + // no track loaded + return; + } // Increase samplesCalculated by the buffer size m_iSamplesCalculated += iBufferSize; diff --git a/src/engine/enginebuffer.h b/src/engine/enginebuffer.h index e1e42bf83df..d914b51ec8b 100644 --- a/src/engine/enginebuffer.h +++ b/src/engine/enginebuffer.h @@ -305,7 +305,7 @@ class EngineBuffer : public EngineObject { int m_trackSamplesOld; // Copy of file sample rate - int m_trackSampleRateOld; + double m_trackSampleRateOld; // Mutex controlling weather the process function is in pause mode. This happens // during seek and loading of a new track From 51bd6b5a6bab994575690ebb485b64a16592f020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 24 Nov 2018 23:44:19 +0100 Subject: [PATCH 07/27] fix m_trackSamplesOld handling --- src/engine/enginebuffer.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index 5345569aacc..403e2275698 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -69,7 +69,7 @@ EngineBuffer::EngineBuffer(QString group, UserSettingsPointer pConfig, m_pitch_old(0), m_baserate_old(0), m_rate_old(0.), - m_trackSamplesOld(-1), + m_trackSamplesOld(0), m_trackSampleRateOld(0), m_iSamplesCalculated(0), m_iUiSlowTick(0), @@ -319,7 +319,7 @@ EngineBuffer::~EngineBuffer() { double EngineBuffer::fractionalPlayposFromAbsolute(double absolutePlaypos) { double fFractionalPlaypos = 0.0; - if (m_trackSamplesOld != 0.) { + if (m_trackSamplesOld) { fFractionalPlaypos = math_min(absolutePlaypos, m_trackSamplesOld); fFractionalPlaypos /= m_trackSamplesOld; } @@ -513,7 +513,6 @@ void EngineBuffer::slotTrackLoaded(TrackPointer pTrack, m_pause.lock(); m_visualPlayPos->setInvalid(); m_pCurrentTrack = pTrack; - m_trackSamplesOld = iTrackNumSamples; m_pTrackSamples->set(iTrackNumSamples); m_pTrackSampleRate->set(iTrackSampleRate); // Reset slip mode @@ -556,7 +555,6 @@ void EngineBuffer::ejectTrack() { m_pTrackSampleRate->set(0); TrackPointer pTrack = m_pCurrentTrack; m_pCurrentTrack.reset(); - m_trackSamplesOld = 0; m_playButton->set(0.0); m_visualBpm->set(0.0); m_visualKey->set(0.0); @@ -602,16 +600,11 @@ void EngineBuffer::doSeekFractional(double fractionalPos, enum SeekRequest seekT if (isnan(fractionalPos)) { return; } - double newSamplePosition = fractionalPos * m_trackSamplesOld; + double newSamplePosition = fractionalPos * m_pTrackSamples->get(); doSeekPlayPos(newSamplePosition, seekType); } void EngineBuffer::doSeekPlayPos(double new_playpos, enum SeekRequest seekType) { - // Don't allow the playposition to go past the end. - if (new_playpos > m_trackSamplesOld) { - new_playpos = m_trackSamplesOld; - } - #ifdef __VINYLCONTROL__ // Notify the vinyl control that a seek has taken place in case it is in // absolute mode and needs be switched to relative. @@ -630,7 +623,7 @@ bool EngineBuffer::updateIndicatorsAndModifyPlay(bool newPlay) { bool playPossible = true; if ((!m_pCurrentTrack && load_atomic(m_iTrackLoading) == 0) || (m_pCurrentTrack && load_atomic(m_iTrackLoading) == 0 && - m_filepos_play >= m_trackSamplesOld && + m_filepos_play >= m_pTrackSamples->get() && !load_atomic(m_iSeekQueued))) { // play not possible playPossible = false; @@ -726,6 +719,7 @@ void EngineBuffer::processTrackLocked( ScopedTimer t("EngineBuffer::process_pauselock"); m_trackSampleRateOld = m_pTrackSampleRate->get(); + m_trackSamplesOld = m_pTrackSamples->get(); double baserate = 0.0; if (sample_rate > 0) { @@ -1154,6 +1148,11 @@ void EngineBuffer::processSeek(bool paused) { m_iSeekQueued.fetchAndStoreRelease(SEEK_NONE)); double position = m_queuedSeekPosition.getValue(); + // Don't allow the playposition to go past the end. + if (position > m_trackSamplesOld) { + position = m_trackSamplesOld; + } + // Add SEEK_PHASE bit, if any if (m_iSeekPhaseQueued.fetchAndStoreRelease(0)) { seekType |= SEEK_PHASE; @@ -1215,7 +1214,7 @@ void EngineBuffer::postProcess(const int iBufferSize) { } void EngineBuffer::updateIndicators(double speed, int iBufferSize) { - if (!m_trackSampleRateOld) { + if (!m_trackSampleRateOld || !m_trackSamplesOld) { // no track loaded return; } From e67516081d49717f69076349dc3ac0d6b9492ffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 25 Nov 2018 08:25:22 +0100 Subject: [PATCH 08/27] removed unused totalSamples from process call --- src/engine/clockcontrol.cpp | 6 ++---- src/engine/clockcontrol.h | 2 +- src/engine/enginebuffer.cpp | 2 +- src/engine/enginecontrol.cpp | 2 -- src/engine/enginecontrol.h | 5 ++--- src/engine/loopingcontrol.cpp | 6 ++---- src/engine/loopingcontrol.h | 1 - src/engine/ratecontrol.cpp | 6 ++---- src/engine/ratecontrol.h | 5 ++--- 9 files changed, 12 insertions(+), 23 deletions(-) diff --git a/src/engine/clockcontrol.cpp b/src/engine/clockcontrol.cpp index f25ee2831f5..0482e18c3df 100644 --- a/src/engine/clockcontrol.cpp +++ b/src/engine/clockcontrol.cpp @@ -45,10 +45,8 @@ void ClockControl::slotBeatsUpdated() { } void ClockControl::process(const double dRate, - const double currentSample, - const double totalSamples, - const int iBuffersize) { - Q_UNUSED(totalSamples); + const double currentSample, + const int iBuffersize) { Q_UNUSED(iBuffersize); double samplerate = m_pCOSampleRate->get(); diff --git a/src/engine/clockcontrol.h b/src/engine/clockcontrol.h index 5b51eb95fdf..82b0d08d777 100644 --- a/src/engine/clockcontrol.h +++ b/src/engine/clockcontrol.h @@ -19,7 +19,7 @@ class ClockControl: public EngineControl { ~ClockControl() override; void process(const double dRate, const double currentSample, - const double totalSamples, const int iBufferSize) override; + const int iBufferSize) override; public slots: void trackLoaded(TrackPointer pNewTrack) override; diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index 403e2275698..1053f574f35 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -985,7 +985,7 @@ void EngineBuffer::processTrackLocked( for (const auto& pControl: m_engineControls) { pControl->setCurrentSample(m_filepos_play, m_trackSamplesOld); - pControl->process(rate, m_filepos_play, m_trackSamplesOld, iBufferSize); + pControl->process(rate, m_filepos_play, iBufferSize); } m_scratching_old = is_scratching; diff --git a/src/engine/enginecontrol.cpp b/src/engine/enginecontrol.cpp index 7a232e20475..d623fd4fafe 100644 --- a/src/engine/enginecontrol.cpp +++ b/src/engine/enginecontrol.cpp @@ -21,11 +21,9 @@ EngineControl::~EngineControl() { void EngineControl::process(const double dRate, const double dCurrentSample, - const double dTotalSamples, const int iBufferSize) { Q_UNUSED(dRate); Q_UNUSED(dCurrentSample); - Q_UNUSED(dTotalSamples); Q_UNUSED(iBufferSize); } diff --git a/src/engine/enginecontrol.h b/src/engine/enginecontrol.h index fe5bc686e9c..627e4c2a74a 100644 --- a/src/engine/enginecontrol.h +++ b/src/engine/enginecontrol.h @@ -43,9 +43,8 @@ class EngineControl : public QObject { // EngineControl can perform any upkeep operations that are necessary during // this call. virtual void process(const double dRate, - const double dCurrentSample, - const double dTotalSamples, - const int iBufferSize); + const double dCurrentSample, + const int iBufferSize); // hintReader allows the EngineControl to provide hints to the reader to // indicate that the given portion of a song is a potential imminent seek diff --git a/src/engine/loopingcontrol.cpp b/src/engine/loopingcontrol.cpp index 8c7b00a4adb..1474f64cbee 100644 --- a/src/engine/loopingcontrol.cpp +++ b/src/engine/loopingcontrol.cpp @@ -304,10 +304,8 @@ void LoopingControl::slotLoopDouble(double pressed) { } void LoopingControl::process(const double dRate, - const double currentSample, - const double totalSamples, - const int iBufferSize) { - Q_UNUSED(totalSamples); + const double currentSample, + const int iBufferSize) { Q_UNUSED(iBufferSize); Q_UNUSED(dRate); diff --git a/src/engine/loopingcontrol.h b/src/engine/loopingcontrol.h index 4795571d5f0..791910c60e1 100644 --- a/src/engine/loopingcontrol.h +++ b/src/engine/loopingcontrol.h @@ -35,7 +35,6 @@ class LoopingControl : public EngineControl { // the sample that should be seeked to. Otherwise it returns currentSample. void process(const double dRate, const double currentSample, - const double totalSamples, const int iBufferSize) override; // nextTrigger returns the sample at which the engine will be triggered to diff --git a/src/engine/ratecontrol.cpp b/src/engine/ratecontrol.cpp index 05bd3e31cc3..e9ffe82b48f 100644 --- a/src/engine/ratecontrol.cpp +++ b/src/engine/ratecontrol.cpp @@ -533,13 +533,11 @@ double RateControl::calculateSpeed(double baserate, double speed, bool paused, } void RateControl::process(const double rate, - const double currentSample, - const double totalSamples, - const int bufferSamples) + const double currentSample, + const int bufferSamples) { Q_UNUSED(rate); Q_UNUSED(currentSample); - Q_UNUSED(totalSamples); /* * Code to handle temporary rate change buttons. * diff --git a/src/engine/ratecontrol.h b/src/engine/ratecontrol.h index 9a98d035ec0..50fd9176571 100644 --- a/src/engine/ratecontrol.h +++ b/src/engine/ratecontrol.h @@ -63,9 +63,8 @@ class RateControl : public EngineControl { // Must be called during each callback of the audio thread so that // RateControl has a chance to update itself. void process(const double dRate, - const double currentSample, - const double totalSamples, - const int bufferSamples) override; + const double currentSample, + const int bufferSamples) override; // Returns the current engine rate. "reportScratching" is used to tell // the caller that the user is currently scratching, and this is used to // disable keylock. From b3fd646e96b0cbfd67293feeb17ea7ea66262b09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 25 Nov 2018 08:47:32 +0100 Subject: [PATCH 09/27] store track sample rate with m_sampleOfTrack --- src/engine/bpmcontrol.h | 2 +- src/engine/enginebuffer.cpp | 2 +- src/engine/enginecontrol.cpp | 6 ++++-- src/engine/enginecontrol.h | 4 +++- src/engine/quantizecontrol.cpp | 5 +++-- src/engine/quantizecontrol.h | 2 +- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/engine/bpmcontrol.h b/src/engine/bpmcontrol.h index 0e3862095e9..99222fb239f 100644 --- a/src/engine/bpmcontrol.h +++ b/src/engine/bpmcontrol.h @@ -157,7 +157,7 @@ class BpmControl : public EngineControl { TapFilter m_tapFilter; TrackPointer m_pTrack; // is witten from an engine worker thread - BeatsPointer m_pBeats; + BeatsPointer m_pBeats; // is witten from an engine worker thread QString m_sGroup; }; diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index 1053f574f35..f03eddc7451 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -984,7 +984,7 @@ void EngineBuffer::processTrackLocked( } for (const auto& pControl: m_engineControls) { - pControl->setCurrentSample(m_filepos_play, m_trackSamplesOld); + pControl->setCurrentSample(m_filepos_play, m_trackSamplesOld, m_trackSampleRateOld); pControl->process(rate, m_filepos_play, iBufferSize); } diff --git a/src/engine/enginecontrol.cpp b/src/engine/enginecontrol.cpp index d623fd4fafe..463257167a1 100644 --- a/src/engine/enginecontrol.cpp +++ b/src/engine/enginecontrol.cpp @@ -13,7 +13,7 @@ EngineControl::EngineControl(QString group, m_pConfig(pConfig), m_pEngineMaster(nullptr), m_pEngineBuffer(nullptr) { - setCurrentSample(0.0, 0.0); + setCurrentSample(0.0, 0.0, 0.0); } EngineControl::~EngineControl() { @@ -42,10 +42,12 @@ void EngineControl::setEngineBuffer(EngineBuffer* pEngineBuffer) { m_pEngineBuffer = pEngineBuffer; } -void EngineControl::setCurrentSample(const double dCurrentSample, const double dTotalSamples) { +void EngineControl::setCurrentSample( + const double dCurrentSample, const double dTotalSamples, const double dTrackSampleRate) { SampleOfTrack sot; sot.current = dCurrentSample; sot.total = dTotalSamples; + sot.rate = dTrackSampleRate; m_sampleOfTrack.setValue(sot); } diff --git a/src/engine/enginecontrol.h b/src/engine/enginecontrol.h index 627e4c2a74a..19ff156dc52 100644 --- a/src/engine/enginecontrol.h +++ b/src/engine/enginecontrol.h @@ -53,7 +53,8 @@ class EngineControl : public QObject { virtual void setEngineMaster(EngineMaster* pEngineMaster); void setEngineBuffer(EngineBuffer* pEngineBuffer); - virtual void setCurrentSample(const double dCurrentSample, const double dTotalSamples); + virtual void setCurrentSample(const double dCurrentSample, + const double dTotalSamples, const double dTrackSampleRate); double getCurrentSample() const; double getTotalSamples() const; bool atEndPosition() const; @@ -86,6 +87,7 @@ class EngineControl : public QObject { struct SampleOfTrack { double current; double total; + double rate; }; ControlValueAtomic m_sampleOfTrack; diff --git a/src/engine/quantizecontrol.cpp b/src/engine/quantizecontrol.cpp index 0f78bab377b..e763c0db7de 100644 --- a/src/engine/quantizecontrol.cpp +++ b/src/engine/quantizecontrol.cpp @@ -65,13 +65,14 @@ void QuantizeControl::slotBeatsUpdated() { } void QuantizeControl::setCurrentSample(const double dCurrentSample, - const double dTotalSamples) { + const double dTotalSamples, + const double dTrackSampleRate) { if (dCurrentSample == getCurrentSample()) { // No need to recalculate. return; } - EngineControl::setCurrentSample(dCurrentSample, dTotalSamples); + EngineControl::setCurrentSample(dCurrentSample, dTotalSamples, dTrackSampleRate); // We only need to update the prev or next if the current sample is // out of range of the existing beat positions or if we've been forced to // do so. diff --git a/src/engine/quantizecontrol.h b/src/engine/quantizecontrol.h index 06ab912c15d..71ffd3cc829 100644 --- a/src/engine/quantizecontrol.h +++ b/src/engine/quantizecontrol.h @@ -19,7 +19,7 @@ class QuantizeControl : public EngineControl { ~QuantizeControl() override; void setCurrentSample(const double dCurrentSample, - const double dTotalSamples) override; + const double dTotalSamples, const double dTrackSampleRate) override; void trackLoaded(TrackPointer pNewTrack) override; private slots: From 3681a9922b633f3bebfc43f185a550cdd9494fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 25 Nov 2018 09:14:51 +0100 Subject: [PATCH 10/27] use m_pTrack thread save --- src/engine/bpmcontrol.cpp | 19 +++++++++++-------- src/engine/enginecontrol.cpp | 4 ++++ src/engine/enginecontrol.h | 1 + 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/engine/bpmcontrol.cpp b/src/engine/bpmcontrol.cpp index 293d67e0d56..8cc06387619 100644 --- a/src/engine/bpmcontrol.cpp +++ b/src/engine/bpmcontrol.cpp @@ -186,19 +186,18 @@ void BpmControl::slotAdjustBeatsSlower(double v) { } void BpmControl::slotTranslateBeatsEarlier(double v) { - if (v > 0 && m_pTrack && m_pBeats && + if (v > 0 && m_pBeats && (m_pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { - // TODO(rryan): Track::getSampleRate is possibly inaccurate! - const int translate_dist = m_pTrack->getSampleRate() * -.01; + const int translate_dist = getTrackSampleRate() * -.01; m_pBeats->translate(translate_dist); } } void BpmControl::slotTranslateBeatsLater(double v) { - if (v > 0 && m_pTrack && m_pBeats && + if (v > 0 && m_pBeats && (m_pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { // TODO(rryan): Track::getSampleRate is possibly inaccurate! - const int translate_dist = m_pTrack->getSampleRate() * .01; + const int translate_dist = getTrackSampleRate() * .01; m_pBeats->translate(translate_dist); } } @@ -745,6 +744,7 @@ void BpmControl::slotUpdateRateSlider() { m_pRateSlider->set(dRateSlider); } +// called from an engine worker thread void BpmControl::trackLoaded(TrackPointer pNewTrack) { if (m_pTrack) { disconnect(m_pTrack.get(), SIGNAL(beatsUpdated()), @@ -766,9 +766,10 @@ void BpmControl::trackLoaded(TrackPointer pNewTrack) { } void BpmControl::slotUpdatedTrackBeats() { - if (m_pTrack) { + TrackPointer pTrack = m_pTrack; + if (pTrack) { resetSyncAdjustment(); - m_pBeats = m_pTrack->getBeats(); + m_pBeats = pTrack->getBeats(); } } @@ -831,6 +832,7 @@ void BpmControl::setInstantaneousBpm(double instantaneousBpm) { m_dSyncInstantaneousBpm = instantaneousBpm; } +// TODO(XXX) make this function thread save void BpmControl::resetSyncAdjustment() { // Immediately edit the beat distance to reflect the new reality. double new_distance = m_pThisBeatDistance->get() + m_dUserOffset; @@ -855,9 +857,10 @@ void BpmControl::collectFeatures(GroupFeatureState* pGroupFeatures) const { 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 - / m_pTrack->getSampleRate() / calcRateRatio(); + / getTrackSampleRate() / calcRateRatio(); pGroupFeatures->has_beat_fraction = true; pGroupFeatures->beat_fraction = dThisBeatFraction; diff --git a/src/engine/enginecontrol.cpp b/src/engine/enginecontrol.cpp index 463257167a1..ef2b040fcf6 100644 --- a/src/engine/enginecontrol.cpp +++ b/src/engine/enginecontrol.cpp @@ -59,6 +59,10 @@ double EngineControl::getTotalSamples() const { return m_sampleOfTrack.getValue().total; } +double EngineControl::getTrackSampleRate() const { + return m_sampleOfTrack.getValue().rate; +} + bool EngineControl::atEndPosition() const { SampleOfTrack sot = m_sampleOfTrack.getValue(); return (sot.current >= sot.total); diff --git a/src/engine/enginecontrol.h b/src/engine/enginecontrol.h index 19ff156dc52..11464c06951 100644 --- a/src/engine/enginecontrol.h +++ b/src/engine/enginecontrol.h @@ -57,6 +57,7 @@ class EngineControl : public QObject { const double dTotalSamples, const double dTrackSampleRate); double getCurrentSample() const; double getTotalSamples() const; + double getTrackSampleRate() const; bool atEndPosition() const; QString getGroup() const; From de532d3fcfcb27f4e29d272a0de41b2359a4c149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 25 Nov 2018 09:49:14 +0100 Subject: [PATCH 11/27] use m_pBeats in a threadsave way --- src/engine/bpmcontrol.cpp | 61 ++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/src/engine/bpmcontrol.cpp b/src/engine/bpmcontrol.cpp index 8cc06387619..894f42e1849 100644 --- a/src/engine/bpmcontrol.cpp +++ b/src/engine/bpmcontrol.cpp @@ -153,9 +153,10 @@ void BpmControl::slotFileBpmChanged(double 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(). - if (m_pBeats) { + BeatsPointer pBeats = m_pBeats; + if (pBeats) { const double beats_bpm = - m_pBeats->getBpmAroundPosition(getCurrentSample(), + pBeats->getBpmAroundPosition(getCurrentSample(), kLocalBpmSpan); if (beats_bpm != -1) { m_pLocalBpm->set(beats_bpm); @@ -172,33 +173,37 @@ void BpmControl::slotFileBpmChanged(double bpm) { } void BpmControl::slotAdjustBeatsFaster(double v) { - if (v > 0 && m_pBeats && (m_pBeats->getCapabilities() & Beats::BEATSCAP_SETBPM)) { - double new_bpm = math_min(200.0, m_pBeats->getBpm() + .01); - m_pBeats->setBpm(new_bpm); + BeatsPointer pBeats = m_pBeats; + if (v > 0 && pBeats && (pBeats->getCapabilities() & Beats::BEATSCAP_SETBPM)) { + double new_bpm = math_min(200.0, pBeats->getBpm() + .01); + pBeats->setBpm(new_bpm); } } void BpmControl::slotAdjustBeatsSlower(double v) { - if (v > 0 && m_pBeats && (m_pBeats->getCapabilities() & Beats::BEATSCAP_SETBPM)) { - double new_bpm = math_max(10.0, m_pBeats->getBpm() - .01); - m_pBeats->setBpm(new_bpm); + BeatsPointer pBeats = m_pBeats; + if (v > 0 && pBeats && (pBeats->getCapabilities() & Beats::BEATSCAP_SETBPM)) { + double new_bpm = math_max(10.0, pBeats->getBpm() - .01); + pBeats->setBpm(new_bpm); } } void BpmControl::slotTranslateBeatsEarlier(double v) { - if (v > 0 && m_pBeats && - (m_pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { + BeatsPointer pBeats = m_pBeats; + if (v > 0 && pBeats && + (pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { const int translate_dist = getTrackSampleRate() * -.01; - m_pBeats->translate(translate_dist); + pBeats->translate(translate_dist); } } void BpmControl::slotTranslateBeatsLater(double v) { - if (v > 0 && m_pBeats && - (m_pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { + BeatsPointer pBeats = m_pBeats; + if (v > 0 && pBeats && + (pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { // TODO(rryan): Track::getSampleRate is possibly inaccurate! const int translate_dist = getTrackSampleRate() * .01; - m_pBeats->translate(translate_dist); + pBeats->translate(translate_dist); } } @@ -218,7 +223,7 @@ void BpmControl::slotTapFilter(double averageLength, int numSamples) { if (numSamples < 4) return; - auto pBeats = m_pBeats; + BeatsPointer pBeats = m_pBeats; if (!pBeats) return; @@ -389,7 +394,7 @@ double BpmControl::calcSyncedRate(double userTweak) { // 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 == NULL || m_pReverseButton->get()) { + !m_pBeats || m_pReverseButton->get()) { m_resetSyncAdjustment = true; return rate + userTweak; } @@ -577,7 +582,8 @@ bool BpmControl::getBeatContextNoLookup( double BpmControl::getNearestPositionInPhase(double dThisPosition, bool respectLoops, bool playing) { // Without a beatgrid, we don't know the phase offset. - if (!m_pBeats) { + BeatsPointer pBeats = m_pBeats; + if (!pBeats) { return dThisPosition; } // Master buffer is always in sync! @@ -593,7 +599,7 @@ double BpmControl::getNearestPositionInPhase(double dThisPosition, bool respectL // 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(m_pBeats, dThisPosition, + if (!getBeatContext(pBeats, dThisPosition, &dThisPrevBeat, &dThisNextBeat, &dThisBeatLength, NULL)) { return dThisPosition; @@ -670,7 +676,7 @@ double BpmControl::getNearestPositionInPhase(double dThisPosition, bool respectL } else if (this_near_next && !other_near_next) { dNewPlaypos += dThisNextBeat; } else { //!this_near_next && other_near_next - dThisPrevBeat = m_pBeats->findNthBeat(dThisPosition, -2); + dThisPrevBeat = pBeats->findNthBeat(dThisPosition, -2); dNewPlaypos += dThisPrevBeat; } @@ -774,33 +780,36 @@ void BpmControl::slotUpdatedTrackBeats() { } void BpmControl::slotBeatsTranslate(double v) { - if (v > 0 && m_pBeats && (m_pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { + BeatsPointer pBeats = m_pBeats; + if (v > 0 && pBeats && (pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { double currentSample = getCurrentSample(); - double closestBeat = m_pBeats->findClosestBeat(currentSample); + double closestBeat = pBeats->findClosestBeat(currentSample); int delta = currentSample - closestBeat; if (delta % 2 != 0) { delta--; } - m_pBeats->translate(delta); + pBeats->translate(delta); } } void BpmControl::slotBeatsTranslateMatchAlignment(double v) { - if (v > 0 && m_pBeats && (m_pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { + 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 = 0.0; double offset = getPhaseOffset(getCurrentSample()); - m_pBeats->translate(-offset); + pBeats->translate(-offset); } } double BpmControl::updateLocalBpm() { double prev_local_bpm = m_pLocalBpm->get(); double local_bpm = 0; - if (m_pBeats) { - local_bpm = m_pBeats->getBpmAroundPosition(getCurrentSample(), + BeatsPointer pBeats = m_pBeats; + if (pBeats) { + local_bpm = pBeats->getBpmAroundPosition(getCurrentSample(), kLocalBpmSpan); if (local_bpm == -1) { local_bpm = m_pFileBpm->get(); From 44e80f10e421d1acef9d4d0c8489f9c216c3544b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 25 Nov 2018 10:36:19 +0100 Subject: [PATCH 12/27] LoopingControl: use m_pBeats and m_pTrack in a thread save way --- src/engine/clockcontrol.cpp | 11 +++--- src/engine/clockcontrol.h | 2 +- src/engine/keycontrol.h | 5 ++- src/engine/loopingcontrol.cpp | 63 +++++++++++++++++++---------------- src/engine/loopingcontrol.h | 2 +- src/engine/quantizecontrol.h | 2 +- 6 files changed, 46 insertions(+), 39 deletions(-) diff --git a/src/engine/clockcontrol.cpp b/src/engine/clockcontrol.cpp index 0482e18c3df..0a38d566006 100644 --- a/src/engine/clockcontrol.cpp +++ b/src/engine/clockcontrol.cpp @@ -17,6 +17,7 @@ ClockControl::~ClockControl() { delete m_pCOSampleRate; } +// called from an engine worker thread void ClockControl::trackLoaded(TrackPointer pNewTrack) { // Clear on-beat control m_pCOBeatActive->set(0.0); @@ -39,8 +40,9 @@ void ClockControl::trackLoaded(TrackPointer pNewTrack) { } void ClockControl::slotBeatsUpdated() { - if(m_pTrack) { - m_pBeats = m_pTrack->getBeats(); + TrackPointer pTrack = m_pTrack; + if(pTrack) { + m_pBeats = pTrack->getBeats(); } } @@ -57,8 +59,9 @@ void ClockControl::process(const double dRate, // by the rate. const double blinkIntervalSamples = 2.0 * samplerate * (1.0 * dRate) * blinkSeconds; - if (m_pBeats) { - double closestBeat = m_pBeats->findClosestBeat(currentSample); + BeatsPointer pBeats = m_pBeats; + if (pBeats) { + double closestBeat = pBeats->findClosestBeat(currentSample); double distanceToClosestBeat = fabs(currentSample - closestBeat); m_pCOBeatActive->set(distanceToClosestBeat < blinkIntervalSamples / 2.0); } diff --git a/src/engine/clockcontrol.h b/src/engine/clockcontrol.h index 82b0d08d777..9998213023e 100644 --- a/src/engine/clockcontrol.h +++ b/src/engine/clockcontrol.h @@ -29,7 +29,7 @@ class ClockControl: public EngineControl { ControlObject* m_pCOBeatActive; ControlProxy* m_pCOSampleRate; TrackPointer m_pTrack; // is witten from an engine worker thread - BeatsPointer m_pBeats; + BeatsPointer m_pBeats; // is witten from an engine worker thread }; #endif /* CLOCKCONTROL_H */ diff --git a/src/engine/keycontrol.h b/src/engine/keycontrol.h index b97f3567c24..7572e950e27 100644 --- a/src/engine/keycontrol.h +++ b/src/engine/keycontrol.h @@ -66,14 +66,13 @@ class KeyControl : public EngineControl { ControlPushButton* m_keylockMode; ControlPushButton* m_keyunlockMode; - /** The current loaded file's detected key */ + // The current loaded file's detected key ControlObject* m_pFileKey; - /** The current effective key of the engine */ + // The current effective key of the engine ControlObject* m_pEngineKey; ControlPotmeter* m_pEngineKeyDistance; - TrackPointer m_pTrack; struct PitchTempoRatio m_pitchRateInfo; QAtomicInt m_updatePitchRequest; QAtomicInt m_updatePitchAdjustRequest; diff --git a/src/engine/loopingcontrol.cpp b/src/engine/loopingcontrol.cpp index 1474f64cbee..d094acb1b62 100644 --- a/src/engine/loopingcontrol.cpp +++ b/src/engine/loopingcontrol.cpp @@ -454,10 +454,11 @@ void LoopingControl::hintReader(HintVector* pHintList) { void LoopingControl::setLoopInToCurrentPosition() { // set loop-in position + BeatsPointer pBeats = m_pBeats; LoopSamples loopSamples = m_loopSamples.getValue(); double quantizedBeat = -1; double pos = m_currentSample.getValue(); - if (m_pQuantizeEnabled->toBool() && m_pBeats != nullptr) { + if (m_pQuantizeEnabled->toBool() && pBeats) { if (m_bAdjustingLoopIn) { double closestBeat = m_pClosestBeat->get(); if (closestBeat == getCurrentSample()) { @@ -486,8 +487,8 @@ void LoopingControl::setLoopInToCurrentPosition() { // pre-defined beatloop size instead (when possible) if (loopSamples.end != kNoTrigger && (loopSamples.end - pos) < MINIMUM_AUDIBLE_LOOP_SIZE) { - if (quantizedBeat != -1 && m_pBeats) { - pos = m_pBeats->findNthBeat(quantizedBeat, -2); + if (quantizedBeat != -1 && pBeats) { + pos = pBeats->findNthBeat(quantizedBeat, -2); if (pos == -1 || (loopSamples.end - pos) < MINIMUM_AUDIBLE_LOOP_SIZE) { pos = loopSamples.end - MINIMUM_AUDIBLE_LOOP_SIZE; } @@ -511,9 +512,9 @@ void LoopingControl::setLoopInToCurrentPosition() { if (m_pQuantizeEnabled->toBool() && loopSamples.start < loopSamples.end - && m_pBeats != nullptr) { + && pBeats) { m_pCOBeatLoopSize->setAndConfirm( - m_pBeats->numBeatsInRange(loopSamples.start, loopSamples.end)); + pBeats->numBeatsInRange(loopSamples.start, loopSamples.end)); updateBeatLoopingControls(); } else { clearActiveBeatLoop(); @@ -524,7 +525,7 @@ void LoopingControl::setLoopInToCurrentPosition() { } void LoopingControl::slotLoopIn(double pressed) { - if (m_pTrack == nullptr) { + if (!m_pTrack) { return; } @@ -555,10 +556,11 @@ void LoopingControl::slotLoopInGoto(double pressed) { } void LoopingControl::setLoopOutToCurrentPosition() { + BeatsPointer pBeats = m_pBeats; LoopSamples loopSamples = m_loopSamples.getValue(); double quantizedBeat = -1; int pos = m_currentSample.getValue(); - if (m_pQuantizeEnabled->toBool() && m_pBeats != nullptr) { + if (m_pQuantizeEnabled->toBool() && pBeats) { if (m_bAdjustingLoopOut) { double closestBeat = m_pClosestBeat->get(); if (closestBeat == getCurrentSample()) { @@ -584,8 +586,8 @@ void LoopingControl::setLoopOutToCurrentPosition() { // inaudible (which can happen easily with quantize-to-beat enabled,) // use the smallest pre-defined beatloop instead (when possible) if ((pos - loopSamples.start) < MINIMUM_AUDIBLE_LOOP_SIZE) { - if (quantizedBeat != -1 && m_pBeats) { - pos = static_cast(floor(m_pBeats->findNthBeat(quantizedBeat, 2))); + if (quantizedBeat != -1 && pBeats) { + pos = static_cast(floor(pBeats->findNthBeat(quantizedBeat, 2))); if (pos == -1 || (pos - loopSamples.start) < MINIMUM_AUDIBLE_LOOP_SIZE) { pos = loopSamples.start + MINIMUM_AUDIBLE_LOOP_SIZE; } @@ -608,9 +610,9 @@ void LoopingControl::setLoopOutToCurrentPosition() { loopSamples.seek = false; } - if (m_pQuantizeEnabled->toBool() && m_pBeats != nullptr) { + if (m_pQuantizeEnabled->toBool() && pBeats) { m_pCOBeatLoopSize->setAndConfirm( - m_pBeats->numBeatsInRange(loopSamples.start, loopSamples.end)); + pBeats->numBeatsInRange(loopSamples.start, loopSamples.end)); updateBeatLoopingControls(); } else { clearActiveBeatLoop(); @@ -818,10 +820,10 @@ void LoopingControl::trackLoaded(TrackPointer pNewTrack) { } } -void LoopingControl::slotUpdatedTrackBeats() -{ - if (m_pTrack) { - m_pBeats = m_pTrack->getBeats(); +void LoopingControl::slotUpdatedTrackBeats() { + TrackPointer pTrack = m_pTrack; + if (pTrack) { + m_pBeats = pTrack->getBeats(); } } @@ -872,7 +874,8 @@ void LoopingControl::clearActiveBeatLoop() { } bool LoopingControl::currentLoopMatchesBeatloopSize() { - if (m_pBeats == nullptr) { + BeatsPointer pBeats = m_pBeats; + if (!pBeats) { return false; } @@ -880,7 +883,7 @@ bool LoopingControl::currentLoopMatchesBeatloopSize() { // Calculate where the loop out point would be if it is a beatloop double beatLoopOutPoint = - m_pBeats->findNBeatsFromSample(loopSamples.start, m_pCOBeatLoopSize->get()); + pBeats->findNBeatsFromSample(loopSamples.start, m_pCOBeatLoopSize->get()); return loopSamples.end > beatLoopOutPoint - 2 && loopSamples.end < beatLoopOutPoint + 2; @@ -922,8 +925,8 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable } int samples = m_pTrackSamples->get(); - if (!m_pTrack || samples == 0 - || !m_pBeats) { + BeatsPointer pBeats = m_pBeats; + if (samples == 0 || !pBeats) { clearActiveBeatLoop(); m_pCOBeatLoopSize->setAndConfirm(beats); return; @@ -949,7 +952,7 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable double cur_pos = getCurrentSample(); double prevBeat; double nextBeat; - m_pBeats->findPrevNextBeats(cur_pos, &prevBeat, &nextBeat); + pBeats->findPrevNextBeats(cur_pos, &prevBeat, &nextBeat); if (m_pQuantizeEnabled->toBool() && prevBeat != -1) { if (beats >= 1.0) { @@ -976,7 +979,7 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable } } - newloopSamples.end = m_pBeats->findNBeatsFromSample(newloopSamples.start, beats); + newloopSamples.end = pBeats->findNBeatsFromSample(newloopSamples.start, beats); if (newloopSamples.start >= newloopSamples.end // happens when the call above fails || newloopSamples.end > samples) { // Do not allow beat loops to go beyond the end of the track // If a track is loaded with beatloop_size larger than @@ -984,7 +987,7 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable // the end of the track, let beatloop_size be set to // a smaller size, but not get larger. double previousBeatloopSize = m_pCOBeatLoopSize->get(); - double previousBeatloopOutPoint = m_pBeats->findNBeatsFromSample( + double previousBeatloopOutPoint = pBeats->findNBeatsFromSample( newloopSamples.start, previousBeatloopSize); if (previousBeatloopOutPoint < newloopSamples.start && beats < previousBeatloopSize) { @@ -1069,7 +1072,8 @@ void LoopingControl::slotBeatLoopRollActivate(double pressed) { } void LoopingControl::slotBeatJump(double beats) { - if (!m_pTrack || !m_pBeats) { + BeatsPointer pBeats = m_pBeats; + if (!pBeats) { return; } @@ -1082,7 +1086,7 @@ void LoopingControl::slotBeatJump(double beats) { // If inside an active loop, move loop slotLoopMove(beats); } else { - seekAbs(m_pBeats->findNBeatsFromSample(getCurrentSample(), beats)); + seekAbs(pBeats->findNBeatsFromSample(getCurrentSample(), beats)); } } @@ -1099,7 +1103,8 @@ void LoopingControl::slotBeatJumpBackward(double pressed) { } void LoopingControl::slotLoopMove(double beats) { - if (m_pTrack == nullptr || m_pBeats == nullptr || beats == 0) { + BeatsPointer pBeats = m_pBeats; + if (!pBeats || beats == 0) { return; } LoopSamples loopSamples = m_loopSamples.getValue(); @@ -1107,12 +1112,12 @@ void LoopingControl::slotLoopMove(double beats) { return; } - if (BpmControl::getBeatContext(m_pBeats, getCurrentSample(), + if (BpmControl::getBeatContext(pBeats, getCurrentSample(), nullptr, nullptr, nullptr, nullptr)) { - double new_loop_in = m_pBeats->findNBeatsFromSample(loopSamples.start, beats); + double new_loop_in = pBeats->findNBeatsFromSample(loopSamples.start, beats); double new_loop_out = currentLoopMatchesBeatloopSize() ? - m_pBeats->findNBeatsFromSample(new_loop_in, m_pCOBeatLoopSize->get()) : - m_pBeats->findNBeatsFromSample(loopSamples.end, beats); + pBeats->findNBeatsFromSample(new_loop_in, m_pCOBeatLoopSize->get()) : + pBeats->findNBeatsFromSample(loopSamples.end, beats); // If we are looping make sure that the play head does not leave the // loop as a result of our adjustment. diff --git a/src/engine/loopingcontrol.h b/src/engine/loopingcontrol.h index 791910c60e1..1dc615d5760 100644 --- a/src/engine/loopingcontrol.h +++ b/src/engine/loopingcontrol.h @@ -158,7 +158,7 @@ class LoopingControl : public EngineControl { QList m_loopMoves; TrackPointer m_pTrack; // is witten from an engine worker thread - BeatsPointer m_pBeats; + BeatsPointer m_pBeats; // is witten from an engine worker thread }; // Class for handling loop moves of a set size. This allows easy access from diff --git a/src/engine/quantizecontrol.h b/src/engine/quantizecontrol.h index 71ffd3cc829..ab5dd0323dd 100644 --- a/src/engine/quantizecontrol.h +++ b/src/engine/quantizecontrol.h @@ -38,7 +38,7 @@ class QuantizeControl : public EngineControl { ControlObject* m_pCOClosestBeat; TrackPointer m_pTrack; // is witten from an engine worker thread - BeatsPointer m_pBeats; + BeatsPointer m_pBeats; // is witten from an engine worker thread }; #endif // QUANTIZECONTROL_H From eb79a3e7d58d13ba496e2bdb02ad7bea7250bcae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 25 Nov 2018 22:20:47 +0100 Subject: [PATCH 13/27] QuantizeControl: use m_pTrack and m_pBeats thread save --- src/engine/quantizecontrol.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/engine/quantizecontrol.cpp b/src/engine/quantizecontrol.cpp index e763c0db7de..e16c526c084 100644 --- a/src/engine/quantizecontrol.cpp +++ b/src/engine/quantizecontrol.cpp @@ -57,8 +57,9 @@ void QuantizeControl::trackLoaded(TrackPointer pNewTrack) { } void QuantizeControl::slotBeatsUpdated() { - if (m_pTrack) { - m_pBeats = m_pTrack->getBeats(); + TrackPointer pTrack = m_pTrack; + if (pTrack) { + m_pBeats = pTrack->getBeats(); lookupBeatPositions(getCurrentSample()); updateClosestBeat(getCurrentSample()); } @@ -85,9 +86,10 @@ void QuantizeControl::setCurrentSample(const double dCurrentSample, } void QuantizeControl::lookupBeatPositions(double dCurrentSample) { - if (m_pBeats) { + BeatsPointer pBeats = m_pBeats; + if (pBeats) { double prevBeat, nextBeat; - m_pBeats->findPrevNextBeats(dCurrentSample, &prevBeat, &nextBeat); + pBeats->findPrevNextBeats(dCurrentSample, &prevBeat, &nextBeat); m_pCOPrevBeat->set(prevBeat); m_pCONextBeat->set(nextBeat); } From 5f2105fe33ab36a78b1e0e71036f0c62a75f92e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 25 Nov 2018 22:23:57 +0100 Subject: [PATCH 14/27] Removed unused m_pTrack from RateControl --- src/engine/ratecontrol.cpp | 4 ---- src/engine/ratecontrol.h | 3 --- 2 files changed, 7 deletions(-) diff --git a/src/engine/ratecontrol.cpp b/src/engine/ratecontrol.cpp index e9ffe82b48f..3cba657208a 100644 --- a/src/engine/ratecontrol.cpp +++ b/src/engine/ratecontrol.cpp @@ -393,10 +393,6 @@ void RateControl::slotControlRateTempUpSmall(double) } } -void RateControl::trackLoaded(TrackPointer pNewTrack) { - m_pTrack = pNewTrack; -} - double RateControl::calcRateRatio() const { double rateRatio = 1.0 + m_pRateDir->get() * m_pRateRange->get() * m_pRateSlider->get(); diff --git a/src/engine/ratecontrol.h b/src/engine/ratecontrol.h index 50fd9176571..65017c8ef62 100644 --- a/src/engine/ratecontrol.h +++ b/src/engine/ratecontrol.h @@ -105,7 +105,6 @@ class RateControl : public EngineControl { void slotControlRateTempUpSmall(double); void slotControlFastForward(double); void slotControlFastBack(double); - void trackLoaded(TrackPointer pNewTrack) override; private: double getJogFactor() const; @@ -162,8 +161,6 @@ class RateControl : public EngineControl { ControlObject* m_pSampleRate; - TrackPointer m_pTrack; // is witten from an engine worker thread - // For Master Sync BpmControl* m_pBpmControl; From 3382d2a3b9e6f647e7bf4a1ddeee7b96946bdae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 25 Nov 2018 22:36:12 +0100 Subject: [PATCH 15/27] VinylControlControl: use m_pTrack thread save --- src/engine/vinylcontrolcontrol.cpp | 9 +++++---- src/engine/vinylcontrolcontrol.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/engine/vinylcontrolcontrol.cpp b/src/engine/vinylcontrolcontrol.cpp index 53b2bb7f2ee..c8bfcc7a956 100644 --- a/src/engine/vinylcontrolcontrol.cpp +++ b/src/engine/vinylcontrolcontrol.cpp @@ -63,7 +63,7 @@ VinylControlControl::~VinylControlControl() { } void VinylControlControl::trackLoaded(TrackPointer pNewTrack) { - m_pCurrentTrack = pNewTrack; + m_pTrack = pNewTrack; } void VinylControlControl::notifySeekQueued() { @@ -84,7 +84,8 @@ void VinylControlControl::slotControlVinylSeek(double fractionalPos) { } // Do nothing if no track is loaded. - if (!m_pCurrentTrack) { + TrackPointer pTrack = m_pTrack; + if (!pTrack) { return; } @@ -106,7 +107,7 @@ void VinylControlControl::slotControlVinylSeek(double fractionalPos) { return; // If off, do nothing. case MIXXX_RELATIVE_CUE_ONECUE: //if onecue, just seek to the regular cue - seekExact(m_pCurrentTrack->getCuePoint()); + seekExact(pTrack->getCuePoint()); return; case MIXXX_RELATIVE_CUE_HOTCUE: // Continue processing in this function. @@ -119,7 +120,7 @@ void VinylControlControl::slotControlVinylSeek(double fractionalPos) { double shortest_distance = 0; int nearest_playpos = -1; - const QList cuePoints(m_pCurrentTrack->getCuePoints()); + const QList cuePoints(pTrack->getCuePoints()); QListIterator it(cuePoints); while (it.hasNext()) { CuePointer pCue(it.next()); diff --git a/src/engine/vinylcontrolcontrol.h b/src/engine/vinylcontrolcontrol.h index f3bcae260fe..d2edb7e562e 100644 --- a/src/engine/vinylcontrolcontrol.h +++ b/src/engine/vinylcontrolcontrol.h @@ -35,7 +35,7 @@ class VinylControlControl : public EngineControl { ControlPushButton* m_pControlVinylCueing; ControlPushButton* m_pControlVinylSignalEnabled; ControlProxy* m_pPlayEnabled; - TrackPointer m_pCurrentTrack; // is witten from an engine worker thread + TrackPointer m_pTrack; // is witten from an engine worker thread bool m_bSeekRequested; }; From 9ea73ae6064ed301182d4b38d3c360b1b5022201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 25 Nov 2018 22:59:25 +0100 Subject: [PATCH 16/27] make BpmControl::resetSyncAdjustment thread save --- src/engine/bpmcontrol.cpp | 25 +++++++++++-------------- src/engine/bpmcontrol.h | 4 ++-- src/test/enginesynctest.cpp | 2 +- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/engine/bpmcontrol.cpp b/src/engine/bpmcontrol.cpp index 894f42e1849..fc56bff2aa0 100644 --- a/src/engine/bpmcontrol.cpp +++ b/src/engine/bpmcontrol.cpp @@ -31,8 +31,6 @@ BpmControl::BpmControl(QString group, m_dSyncTargetBeatDistance(0.0), m_dSyncInstantaneousBpm(0.0), m_dLastSyncAdjustment(1.0), - m_resetSyncAdjustment(false), - m_dUserOffset(0.0), m_tapFilter(this, kFilterLength, kMaxInterval), m_sGroup(group) { m_pPlayButton = new ControlProxy(group, "play", this); @@ -428,8 +426,8 @@ double BpmControl::calcSyncedRate(double userTweak) { } double BpmControl::calcSyncAdjustment(double my_percentage, bool userTweakingSync) { - if (m_resetSyncAdjustment) { - m_resetSyncAdjustment = false; + int resetSyncAdjustment = m_resetSyncAdjustment.fetchAndStoreRelaxed(0); + if (resetSyncAdjustment) { m_dLastSyncAdjustment = 1.0; } @@ -461,9 +459,9 @@ double BpmControl::calcSyncAdjustment(double my_percentage, bool userTweakingSyn if (userTweakingSync) { // Don't do anything else, leave it adjustment = 1.0; - m_dUserOffset = shortest_distance; + m_dUserOffset.setValue(shortest_distance); } else { - double error = shortest_distance - m_dUserOffset; + double error = shortest_distance - m_dUserOffset.getValue(); // Threshold above which we do sync adjustment. const double kErrorThreshold = 0.01; // Threshold above which sync is really, really bad, so much so that we @@ -509,7 +507,7 @@ double BpmControl::getBeatDistance(double dThisPosition) const { double dNextBeat = m_pNextBeat->get(); if (dPrevBeat == -1 || dNextBeat == -1) { - return 0.0 - m_dUserOffset; + return 0.0 - m_dUserOffset.getValue(); } double dBeatLength = dNextBeat - dPrevBeat; @@ -520,7 +518,7 @@ double BpmControl::getBeatDistance(double dThisPosition) const { if (dBeatPercentage < 0) ++dBeatPercentage; if (dBeatPercentage > 1) --dBeatPercentage; - return dBeatPercentage - m_dUserOffset; + return dBeatPercentage - m_dUserOffset.getValue(); } // static @@ -670,7 +668,7 @@ double BpmControl::getNearestPositionInPhase(double dThisPosition, bool respectL // infinite beatgrids because the assumption that findNthBeat(-2) always // works will be wrong then. - double dNewPlaypos = (dOtherBeatFraction + m_dUserOffset) * dThisBeatLength; + double dNewPlaypos = (dOtherBeatFraction + m_dUserOffset.getValue()) * dThisBeatLength; if (this_near_next == other_near_next) { dNewPlaypos += dThisPrevBeat; } else if (this_near_next && !other_near_next) { @@ -797,7 +795,7 @@ void BpmControl::slotBeatsTranslateMatchAlignment(double v) { 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 = 0.0; + m_dUserOffset.setValue(0.0); double offset = getPhaseOffset(getCurrentSample()); pBeats->translate(-offset); @@ -828,7 +826,7 @@ double BpmControl::updateBeatDistance() { double beat_distance = getBeatDistance(getCurrentSample()); m_pThisBeatDistance->set(beat_distance); if (getSyncMode() == SYNC_NONE) { - m_dUserOffset = 0.0; + m_dUserOffset.setValue(0.0); } return beat_distance; } @@ -841,12 +839,11 @@ void BpmControl::setInstantaneousBpm(double instantaneousBpm) { m_dSyncInstantaneousBpm = instantaneousBpm; } -// TODO(XXX) make this function thread save void BpmControl::resetSyncAdjustment() { // Immediately edit the beat distance to reflect the new reality. - double new_distance = m_pThisBeatDistance->get() + m_dUserOffset; + double new_distance = m_pThisBeatDistance->get() + m_dUserOffset.getValue(); m_pThisBeatDistance->set(new_distance); - m_dUserOffset = 0.0; + m_dUserOffset.setValue(0.0); m_resetSyncAdjustment = true; } diff --git a/src/engine/bpmcontrol.h b/src/engine/bpmcontrol.h index 99222fb239f..c9d698e02fe 100644 --- a/src/engine/bpmcontrol.h +++ b/src/engine/bpmcontrol.h @@ -150,9 +150,9 @@ class BpmControl : public EngineControl { double m_dSyncTargetBeatDistance; double m_dSyncInstantaneousBpm; double m_dLastSyncAdjustment; - bool m_resetSyncAdjustment; + QAtomicInt m_resetSyncAdjustment; FRIEND_TEST(EngineSyncTest, UserTweakBeatDistance); - double m_dUserOffset; + ControlValueAtomic m_dUserOffset; TapFilter m_tapFilter; diff --git a/src/test/enginesynctest.cpp b/src/test/enginesynctest.cpp index 9a2d8923165..fd52ae1f47c 100644 --- a/src/test/enginesynctest.cpp +++ b/src/test/enginesynctest.cpp @@ -1395,7 +1395,7 @@ TEST_F(EngineSyncTest, UserTweakBeatDistance) { "beat_distance"))->get()); EXPECT_LT(difference, .00001); - EXPECT_FLOAT_EQ(0.0, m_pChannel1->getEngineBuffer()->m_pBpmControl->m_dUserOffset); + EXPECT_FLOAT_EQ(0.0, m_pChannel1->getEngineBuffer()->m_pBpmControl->m_dUserOffset.getValue()); } TEST_F(EngineSyncTest, MasterBpmNeverZero) { From fb706ca21716247049dea539b939fb5156227303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Fri, 30 Nov 2018 23:32:38 +0100 Subject: [PATCH 17/27] Fix typo --- src/engine/bpmcontrol.h | 5 +++-- src/engine/clockcontrol.h | 6 ++++-- src/engine/cuecontrol.h | 2 +- src/engine/loopingcontrol.h | 5 +++-- src/engine/quantizecontrol.h | 5 +++-- src/engine/vinylcontrolcontrol.h | 4 +++- src/util/console.cpp | 2 +- 7 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/engine/bpmcontrol.h b/src/engine/bpmcontrol.h index c9d698e02fe..ea4c772a5a4 100644 --- a/src/engine/bpmcontrol.h +++ b/src/engine/bpmcontrol.h @@ -156,8 +156,9 @@ class BpmControl : public EngineControl { TapFilter m_tapFilter; - TrackPointer m_pTrack; // is witten from an engine worker thread - BeatsPointer m_pBeats; // is witten from an engine worker thread + // objects below are written from an engine worker thread + TrackPointer m_pTrack; + BeatsPointer m_pBeats; QString m_sGroup; }; diff --git a/src/engine/clockcontrol.h b/src/engine/clockcontrol.h index 9998213023e..132edd454b7 100644 --- a/src/engine/clockcontrol.h +++ b/src/engine/clockcontrol.h @@ -28,8 +28,10 @@ class ClockControl: public EngineControl { private: ControlObject* m_pCOBeatActive; ControlProxy* m_pCOSampleRate; - TrackPointer m_pTrack; // is witten from an engine worker thread - BeatsPointer m_pBeats; // is witten from an engine worker thread + + // objects below are written from an engine worker thread + TrackPointer m_pTrack; + BeatsPointer m_pBeats; }; #endif /* CLOCKCONTROL_H */ diff --git a/src/engine/cuecontrol.h b/src/engine/cuecontrol.h index 788f4114733..03a8413cc0a 100644 --- a/src/engine/cuecontrol.h +++ b/src/engine/cuecontrol.h @@ -164,7 +164,7 @@ class CueControl : public EngineControl { ControlProxy* m_pVinylControlEnabled; ControlProxy* m_pVinylControlMode; - TrackPointer m_pLoadedTrack; // is witten from an engine worker thread + TrackPointer m_pLoadedTrack; // is written from an engine worker thread // Tells us which controls map to which hotcue QMap m_controlMap; diff --git a/src/engine/loopingcontrol.h b/src/engine/loopingcontrol.h index 1dc615d5760..a5ee490a5ca 100644 --- a/src/engine/loopingcontrol.h +++ b/src/engine/loopingcontrol.h @@ -157,8 +157,9 @@ class LoopingControl : public EngineControl { ControlObject* m_pCOLoopMove; QList m_loopMoves; - TrackPointer m_pTrack; // is witten from an engine worker thread - BeatsPointer m_pBeats; // is witten from an engine worker thread + // objects below are written from an engine worker thread + TrackPointer m_pTrack; + BeatsPointer m_pBeats; }; // Class for handling loop moves of a set size. This allows easy access from diff --git a/src/engine/quantizecontrol.h b/src/engine/quantizecontrol.h index ab5dd0323dd..d2f828dcf67 100644 --- a/src/engine/quantizecontrol.h +++ b/src/engine/quantizecontrol.h @@ -37,8 +37,9 @@ class QuantizeControl : public EngineControl { ControlObject* m_pCOPrevBeat; ControlObject* m_pCOClosestBeat; - TrackPointer m_pTrack; // is witten from an engine worker thread - BeatsPointer m_pBeats; // is witten from an engine worker thread + // objects below are written from an engine worker thread + TrackPointer m_pTrack; + BeatsPointer m_pBeats; }; #endif // QUANTIZECONTROL_H diff --git a/src/engine/vinylcontrolcontrol.h b/src/engine/vinylcontrolcontrol.h index d2edb7e562e..0c9783475ea 100644 --- a/src/engine/vinylcontrolcontrol.h +++ b/src/engine/vinylcontrolcontrol.h @@ -35,7 +35,9 @@ class VinylControlControl : public EngineControl { ControlPushButton* m_pControlVinylCueing; ControlPushButton* m_pControlVinylSignalEnabled; ControlProxy* m_pPlayEnabled; - TrackPointer m_pTrack; // is witten from an engine worker thread + + TrackPointer m_pTrack; // is written from an engine worker thread + bool m_bSeekRequested; }; diff --git a/src/util/console.cpp b/src/util/console.cpp index e953606788b..bf05fc4b404 100644 --- a/src/util/console.cpp +++ b/src/util/console.cpp @@ -177,7 +177,7 @@ Console::~Console() { } } if (m_shouldFreeConsole) { - // Note: The console has already witten the command on top of the output + // Note: The console has already written the command on top of the output // because it was originally released due to the /subsystem:windows flag. // We may send a fake "Enter" key here, using SendInput() to get a new // command prompt, but this executes a user entry unconditionally or has other From 213ccb3b21c3d39a944e4958435e832b7ae49dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 1 Dec 2018 00:20:41 +0100 Subject: [PATCH 18/27] fix a race condition in bpmcontrol and improve documentation --- src/engine/bpmcontrol.cpp | 24 ++++++++++++------------ src/engine/bpmcontrol.h | 20 +++++++++++--------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/engine/bpmcontrol.cpp b/src/engine/bpmcontrol.cpp index fc56bff2aa0..65a0d180141 100644 --- a/src/engine/bpmcontrol.cpp +++ b/src/engine/bpmcontrol.cpp @@ -26,13 +26,12 @@ const SINT kSamplesPerFrame = 2; } BpmControl::BpmControl(QString group, - UserSettingsPointer pConfig) : - EngineControl(group, pConfig), - m_dSyncTargetBeatDistance(0.0), - m_dSyncInstantaneousBpm(0.0), - m_dLastSyncAdjustment(1.0), - m_tapFilter(this, kFilterLength, kMaxInterval), - m_sGroup(group) { + UserSettingsPointer pConfig) + : EngineControl(group, pConfig), + m_tapFilter(this, kFilterLength, kMaxInterval), + m_dSyncInstantaneousBpm(0.0), + m_dLastSyncAdjustment(1.0), + m_sGroup(group) { m_pPlayButton = new ControlProxy(group, "play", this); m_pReverseButton = new ControlProxy(group, "reverse", this); m_pRateSlider = new ControlProxy(group, "rate", this); @@ -123,7 +122,7 @@ BpmControl::BpmControl(QString group, // Measures distance from last beat in percentage: 0.5 = half-beat away. m_pThisBeatDistance = new ControlProxy(group, "beat_distance", this); - m_pSyncMode = ControlObject::getControl(ConfigKey(group, "sync_mode")); + m_pSyncMode = new ControlProxy(group, "sync_mode", this); } BpmControl::~BpmControl() { @@ -444,7 +443,7 @@ double BpmControl::calcSyncAdjustment(double my_percentage, bool userTweakingSyn // than modular 1.0 beat fractions. This will allow sync to work across loop // boundaries too. - double master_percentage = m_dSyncTargetBeatDistance; + double master_percentage = m_dSyncTargetBeatDistance.getValue(); double shortest_distance = shortestPercentageChange( master_percentage, my_percentage); @@ -578,7 +577,8 @@ bool BpmControl::getBeatContextNoLookup( return true; } -double BpmControl::getNearestPositionInPhase(double dThisPosition, bool respectLoops, bool playing) { +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) { @@ -613,7 +613,7 @@ double BpmControl::getNearestPositionInPhase(double dThisPosition, bool respectL double dOtherBeatFraction; if (getSyncMode() == SYNC_FOLLOWER) { // If we're a follower, it's easy to get the other beat fraction - dOtherBeatFraction = m_dSyncTargetBeatDistance; + dOtherBeatFraction = m_dSyncTargetBeatDistance.getValue(); } else { // If not, we have to figure it out EngineBuffer* pOtherEngineBuffer = pickSyncTarget(); @@ -832,7 +832,7 @@ double BpmControl::updateBeatDistance() { } void BpmControl::setTargetBeatDistance(double beatDistance) { - m_dSyncTargetBeatDistance = beatDistance; + m_dSyncTargetBeatDistance.setValue(beatDistance); } void BpmControl::setInstantaneousBpm(double instantaneousBpm) { diff --git a/src/engine/bpmcontrol.h b/src/engine/bpmcontrol.h index ea4c772a5a4..0b947d506d1 100644 --- a/src/engine/bpmcontrol.h +++ b/src/engine/bpmcontrol.h @@ -144,23 +144,25 @@ class BpmControl : public EngineControl { // Button that translates beats to match another playing deck ControlPushButton* m_pBeatsTranslateMatchAlignment; - // Master Sync objects and values. - ControlObject* m_pSyncMode; ControlProxy* m_pThisBeatDistance; - double m_dSyncTargetBeatDistance; - double m_dSyncInstantaneousBpm; - double m_dLastSyncAdjustment; - QAtomicInt m_resetSyncAdjustment; - FRIEND_TEST(EngineSyncTest, UserTweakBeatDistance); + ControlValueAtomic m_dSyncTargetBeatDistance; ControlValueAtomic m_dUserOffset; + QAtomicInt m_resetSyncAdjustment; + ControlProxy* m_pSyncMode; - TapFilter m_tapFilter; + TapFilter m_tapFilter; // TODO: not threadsave + + // used in the engine thread only + double m_dSyncInstantaneousBpm; + double m_dLastSyncAdjustment; // objects below are written from an engine worker thread TrackPointer m_pTrack; BeatsPointer m_pBeats; - QString m_sGroup; + const QString m_sGroup; + + FRIEND_TEST(EngineSyncTest, UserTweakBeatDistance); }; From c6a001a1f072c7531a3e41d0d990f6a26d9f6dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 1 Dec 2018 00:59:06 +0100 Subject: [PATCH 19/27] RateControl: Make config options atomic --- src/engine/ratecontrol.cpp | 53 ++++++++++++++++++-------------------- src/engine/ratecontrol.h | 8 +++--- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/src/engine/ratecontrol.cpp b/src/engine/ratecontrol.cpp index 3cba657208a..4f3a234906d 100644 --- a/src/engine/ratecontrol.cpp +++ b/src/engine/ratecontrol.cpp @@ -18,10 +18,10 @@ #include // Static default values for rate buttons (percents) -double RateControl::m_dTemporaryRateChangeCoarse; -double RateControl::m_dTemporaryRateChangeFine; -double RateControl::m_dPermanentRateChangeCoarse; -double RateControl::m_dPermanentRateChangeFine; +ControlValueAtomic RateControl::m_dTemporaryRateChangeCoarse; +ControlValueAtomic RateControl::m_dTemporaryRateChangeFine; +ControlValueAtomic RateControl::m_dPermanentRateChangeCoarse; +ControlValueAtomic RateControl::m_dPermanentRateChangeFine; int RateControl::m_iRateRampSensitivity; RateControl::RampMode RateControl::m_eRateRampMode; @@ -233,42 +233,42 @@ void RateControl::setRateRampSensitivity(int sense) { //static void RateControl::setTemporaryRateChangeCoarseAmount(double v) { - m_dTemporaryRateChangeCoarse = v; + m_dTemporaryRateChangeCoarse.setValue(v); } //static void RateControl::setTemporaryRateChangeFineAmount(double v) { - m_dTemporaryRateChangeFine = v; + m_dTemporaryRateChangeFine.setValue(v); } //static void RateControl::setPermanentRateChangeCoarseAmount(double v) { - m_dPermanentRateChangeCoarse = v; + m_dPermanentRateChangeCoarse.setValue(v); } //static void RateControl::setPermanentRateChangeFineAmount(double v) { - m_dPermanentRateChangeFine = v; + m_dPermanentRateChangeFine.setValue(v); } //static double RateControl::getTemporaryRateChangeCoarseAmount() { - return m_dTemporaryRateChangeCoarse; + return m_dTemporaryRateChangeCoarse.getValue(); } //static double RateControl::getTemporaryRateChangeFineAmount() { - return m_dTemporaryRateChangeFine; + return m_dTemporaryRateChangeFine.getValue(); } //static double RateControl::getPermanentRateChangeCoarseAmount() { - return m_dPermanentRateChangeCoarse; + return m_dPermanentRateChangeCoarse.getValue(); } //static double RateControl::getPermanentRateChangeFineAmount() { - return m_dPermanentRateChangeFine; + return m_dPermanentRateChangeFine.getValue(); } void RateControl::slotReverseRollActivate(double v) { @@ -304,7 +304,7 @@ void RateControl::slotControlRatePermDown(double) // Adjusts temp rate down if button pressed if (buttonRatePermDown->get()) { m_pRateSlider->set(m_pRateSlider->get() - - m_pRateDir->get() * m_dPermanentRateChangeCoarse / (100 * m_pRateRange->get())); + m_pRateDir->get() * m_dPermanentRateChangeCoarse.getValue() / (100 * m_pRateRange->get())); } } @@ -313,7 +313,7 @@ void RateControl::slotControlRatePermDownSmall(double) // Adjusts temp rate down if button pressed if (buttonRatePermDownSmall->get()) m_pRateSlider->set(m_pRateSlider->get() - - m_pRateDir->get() * m_dPermanentRateChangeFine / (100. * m_pRateRange->get())); + m_pRateDir->get() * m_dPermanentRateChangeFine.getValue() / (100. * m_pRateRange->get())); } void RateControl::slotControlRatePermUp(double) @@ -321,7 +321,7 @@ void RateControl::slotControlRatePermUp(double) // Adjusts temp rate up if button pressed if (buttonRatePermUp->get()) { m_pRateSlider->set(m_pRateSlider->get() + - m_pRateDir->get() * m_dPermanentRateChangeCoarse / (100. * m_pRateRange->get())); + m_pRateDir->get() * m_dPermanentRateChangeCoarse.getValue() / (100. * m_pRateRange->get())); } } @@ -330,7 +330,7 @@ void RateControl::slotControlRatePermUpSmall(double) // Adjusts temp rate up if button pressed if (buttonRatePermUpSmall->get()) m_pRateSlider->set(m_pRateSlider->get() + - m_pRateDir->get() * m_dPermanentRateChangeFine / (100. * m_pRateRange->get())); + m_pRateDir->get() * m_dPermanentRateChangeFine.getValue() / (100. * m_pRateRange->get())); } void RateControl::slotControlRateTempDown(double) @@ -378,16 +378,12 @@ void RateControl::slotControlRateTempUp(double) } } -void RateControl::slotControlRateTempUpSmall(double) -{ +void RateControl::slotControlRateTempUpSmall(double) { // Set the state of the Temporary button. Logic is handled in ::process() - if (buttonRateTempUpSmall->get() && !(m_ePbPressed & RateControl::RATERAMP_UP)) - { + if (buttonRateTempUpSmall->get() && !(m_ePbPressed & RateControl::RATERAMP_UP)){ m_ePbPressed |= RateControl::RATERAMP_UP; m_ePbCurrent = RateControl::RATERAMP_UP; - } - else if (!buttonRateTempUpSmall->get()) - { + } else if (!buttonRateTempUpSmall->get()) { m_ePbPressed &= ~RateControl::RATERAMP_UP; m_ePbCurrent = m_ePbPressed; } @@ -564,9 +560,9 @@ void RateControl::process(const double rate, return; } - double change = m_pRateDir->get() * m_dTemporaryRateChangeCoarse / + double change = m_pRateDir->get() * m_dTemporaryRateChangeCoarse.getValue() / (100. * range); - double csmall = m_pRateDir->get() * m_dTemporaryRateChangeFine / + double csmall = m_pRateDir->get() * m_dTemporaryRateChangeFine.getValue() / (100. * range); if (buttonRateTempUp->get()) @@ -578,7 +574,8 @@ void RateControl::process(const double rate, else if (buttonRateTempDownSmall->get()) subRateTemp(csmall); } else if (m_eRateRampMode == RampMode::Linear) { - m_dTemporaryRateChangeCoarse = ((double)latrate / ((double)m_iRateRampSensitivity / 100.)); + m_dTemporaryRateChangeCoarse.setValue( + ((double)latrate / ((double)m_iRateRampSensitivity / 100.))); if (m_eRampBackMode == RATERAMP_RAMPBACK_PERIOD) m_dRateTempRampbackChange = 0.0; @@ -590,9 +587,9 @@ void RateControl::process(const double rate, if (m_ePbCurrent) { // apply ramped pitchbending if (m_ePbCurrent == RateControl::RATERAMP_UP) { - addRateTemp(m_dTemporaryRateChangeCoarse); + addRateTemp(m_dTemporaryRateChangeCoarse.getValue()); } else if (m_ePbCurrent == RateControl::RATERAMP_DOWN) { - subRateTemp(m_dTemporaryRateChangeCoarse); + subRateTemp(m_dTemporaryRateChangeCoarse.getValue()); } } else if ((m_bTempStarted) || ((m_eRampBackMode != RATERAMP_RAMPBACK_NONE) diff --git a/src/engine/ratecontrol.h b/src/engine/ratecontrol.h index 65017c8ef62..4c5315bd53a 100644 --- a/src/engine/ratecontrol.h +++ b/src/engine/ratecontrol.h @@ -123,10 +123,10 @@ class RateControl : public EngineControl { double getTempRate(void); // Values used when temp and perm rate buttons are pressed - static double m_dTemporaryRateChangeCoarse; - static double m_dTemporaryRateChangeFine; - static double m_dPermanentRateChangeCoarse; - static double m_dPermanentRateChangeFine; + static ControlValueAtomic m_dTemporaryRateChangeCoarse; + static ControlValueAtomic m_dTemporaryRateChangeFine; + static ControlValueAtomic m_dPermanentRateChangeCoarse; + static ControlValueAtomic m_dPermanentRateChangeFine; ControlPushButton *buttonRateTempDown; ControlPushButton *buttonRateTempDownSmall; From 3a2fdc46e2d24945607618fadcf4c0cb0ed79a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 1 Dec 2018 01:05:40 +0100 Subject: [PATCH 20/27] remove unused variable --- src/engine/ratecontrol.cpp | 1 - src/engine/ratecontrol.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/engine/ratecontrol.cpp b/src/engine/ratecontrol.cpp index 4f3a234906d..ef14fca0d77 100644 --- a/src/engine/ratecontrol.cpp +++ b/src/engine/ratecontrol.cpp @@ -35,7 +35,6 @@ RateControl::RateControl(QString group, m_ePbCurrent(0), m_ePbPressed(0), m_bTempStarted(false), - m_dTempRateChange(0.0), m_dRateTemp(0.0), m_eRampBackMode(RATERAMP_RAMPBACK_NONE), m_dRateTempRampbackChange(0.0) { diff --git a/src/engine/ratecontrol.h b/src/engine/ratecontrol.h index 4c5315bd53a..309e13a8c42 100644 --- a/src/engine/ratecontrol.h +++ b/src/engine/ratecontrol.h @@ -174,8 +174,6 @@ class RateControl : public EngineControl { // This is true if we've already started to ramp the rate bool m_bTempStarted; - // Set to the rate change used for rate temp - double m_dTempRateChange; // Set the Temporary Rate Change Mode static RampMode m_eRateRampMode; // The Rate Temp Sensitivity, the higher it is the slower it gets From c8c5883d5e0635b68215e40c1eee4f09b4754748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 2 Dec 2018 16:29:37 +0100 Subject: [PATCH 21/27] make TabFilter thread save --- src/engine/bpmcontrol.h | 2 +- src/util/tapfilter.cpp | 1 + src/util/tapfilter.h | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/engine/bpmcontrol.h b/src/engine/bpmcontrol.h index 0b947d506d1..5d53225aed1 100644 --- a/src/engine/bpmcontrol.h +++ b/src/engine/bpmcontrol.h @@ -150,7 +150,7 @@ class BpmControl : public EngineControl { QAtomicInt m_resetSyncAdjustment; ControlProxy* m_pSyncMode; - TapFilter m_tapFilter; // TODO: not threadsave + TapFilter m_tapFilter; // threadsave // used in the engine thread only double m_dSyncInstantaneousBpm; diff --git a/src/util/tapfilter.cpp b/src/util/tapfilter.cpp index d91b92e791f..240b0a4018d 100644 --- a/src/util/tapfilter.cpp +++ b/src/util/tapfilter.cpp @@ -11,6 +11,7 @@ TapFilter::~TapFilter() { } void TapFilter::tap() { + QMutexLocker locker(&m_mutex); mixxx::Duration elapsed = m_timer.restart(); if (elapsed <= m_maxInterval) { emit(tapped(m_mean.insert(elapsed.toDoubleMillis()), m_mean.size())); diff --git a/src/util/tapfilter.h b/src/util/tapfilter.h index d5c95707d0c..d6805dcd315 100644 --- a/src/util/tapfilter.h +++ b/src/util/tapfilter.h @@ -2,6 +2,7 @@ #define TAPFILTER_H #include +#include #include "util/duration.h" #include "util/movinginterquartilemean.h" @@ -24,6 +25,7 @@ class TapFilter : public QObject { PerformanceTimer m_timer; MovingInterquartileMean m_mean; mixxx::Duration m_maxInterval; + QMutex m_mutex; }; #endif /* TAPFILTER_H */ From 0652d02c2d7cfbe89e1f93c575908d6db4cbdf10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Wed, 12 Dec 2018 07:26:15 +0100 Subject: [PATCH 22/27] Initalize atomic values and check trackSamples for 0 --- src/engine/bpmcontrol.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/engine/bpmcontrol.cpp b/src/engine/bpmcontrol.cpp index 65a0d180141..f7b0af61d95 100644 --- a/src/engine/bpmcontrol.cpp +++ b/src/engine/bpmcontrol.cpp @@ -32,6 +32,9 @@ BpmControl::BpmControl(QString group, m_dSyncInstantaneousBpm(0.0), m_dLastSyncAdjustment(1.0), m_sGroup(group) { + m_dSyncTargetBeatDistance.setValue(0.0); + m_dUserOffset.setValue(0.0); + m_pPlayButton = new ControlProxy(group, "play", this); m_pReverseButton = new ControlProxy(group, "reverse", this); m_pRateSlider = new ControlProxy(group, "rate", this); @@ -849,7 +852,8 @@ void BpmControl::resetSyncAdjustment() { void BpmControl::collectFeatures(GroupFeatureState* pGroupFeatures) const { // Without a beatgrid we don't know any beat details. - if (!m_pBeats) { + double dTrackSampleRate = getTrackSampleRate(); + if (!dTrackSampleRate || !m_pBeats) { return; } @@ -866,7 +870,7 @@ void BpmControl::collectFeatures(GroupFeatureState* pGroupFeatures) const { // Note: dThisBeatLength is fractional frames count * 2 (stereo samples) pGroupFeatures->beat_length_sec = dThisBeatLength / kSamplesPerFrame - / getTrackSampleRate() / calcRateRatio(); + / dTrackSampleRate / calcRateRatio(); pGroupFeatures->has_beat_fraction = true; pGroupFeatures->beat_fraction = dThisBeatFraction; From df5653c526e49f1ad6df3ae0c712ea7c1206b6c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Wed, 12 Dec 2018 20:05:57 +0100 Subject: [PATCH 23/27] Revert rename of m_pSampleRate --- src/engine/enginebuffer.cpp | 8 ++++---- src/engine/enginebuffer.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index f03eddc7451..b73d21f5df8 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -171,7 +171,7 @@ EngineBuffer::EngineBuffer(QString group, UserSettingsPointer pConfig, m_pRepeat = new ControlPushButton(ConfigKey(m_group, "repeat")); m_pRepeat->setButtonMode(ControlPushButton::TOGGLE); - m_pMasterSampleRate = new ControlProxy("[Master]", "samplerate", this); + m_pSampleRate = new ControlProxy("[Master]", "samplerate", this); m_pKeylockEngine = new ControlProxy("[Master]", "keylock_engine", this); m_pKeylockEngine->connectValueChanged(SLOT(slotKeylockEngineChanged(double)), @@ -299,7 +299,7 @@ EngineBuffer::~EngineBuffer() { delete m_pSlipButton; delete m_pRepeat; - delete m_pMasterSampleRate; + delete m_pSampleRate; delete m_pTrackLoaded; delete m_pTrackSamples; @@ -1031,7 +1031,7 @@ void EngineBuffer::process(CSAMPLE* pOutput, const int iBufferSize) { // - Set last sample value (m_fLastSampleValue) so that rampOut works? Other // miscellaneous upkeep issues. - int sample_rate = static_cast(m_pMasterSampleRate->get()); + int sample_rate = static_cast(m_pSampleRate->get()); // If the sample rate has changed, force Rubberband to reset so that // it doesn't reallocate when the user engages keylock during playback. @@ -1235,7 +1235,7 @@ void EngineBuffer::updateIndicators(double speed, int iBufferSize) { // Update indicators that are only updated after every // sampleRate/kiUpdateRate samples processed. (e.g. playposSlider) - if (m_iSamplesCalculated > (kSamplesPerFrame * m_pMasterSampleRate->get() / kiPlaypositionUpdateRate)) { + if (m_iSamplesCalculated > (kSamplesPerFrame * m_pSampleRate->get() / kiPlaypositionUpdateRate)) { const double samplePositionToSeconds = 1.0 / m_trackSampleRateOld / kSamplesPerFrame / m_tempo_ratio_old; m_timeElapsed->set(m_filepos_play * samplePositionToSeconds); diff --git a/src/engine/enginebuffer.h b/src/engine/enginebuffer.h index d914b51ec8b..cdb01276b4d 100644 --- a/src/engine/enginebuffer.h +++ b/src/engine/enginebuffer.h @@ -342,7 +342,7 @@ class EngineBuffer : public EngineObject { ControlObject* m_timeElapsed; ControlObject* m_timeRemaining; ControlPotmeter* m_playposSlider; - ControlProxy* m_pMasterSampleRate; + ControlProxy* m_pSampleRate; ControlProxy* m_pKeylockEngine; ControlPushButton* m_pKeylock; From 3e870698cf474c9d0275bc1f45a522e093523050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Wed, 12 Dec 2018 20:11:03 +0100 Subject: [PATCH 24/27] use qAsConst() --- src/engine/enginebuffer.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index b73d21f5df8..f9d1f23403d 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -371,7 +371,7 @@ double EngineBuffer::getLocalBpm() { } void EngineBuffer::setEngineMaster(EngineMaster* pEngineMaster) { - for (const auto& pControl: m_engineControls) { + for (const auto& pControl: qAsConst(m_engineControls)) { pControl->setEngineMaster(pEngineMaster); } } @@ -460,7 +460,7 @@ void EngineBuffer::setNewPlaypos(double newpos, bool adjustingPhase) { m_iSamplesCalculated = 1000000; // Must hold the engineLock while using m_engineControls - for (const auto& pControl: m_engineControls) { + for (const auto& pControl: qAsConst(m_engineControls)) { pControl->notifySeek(m_filepos_play, adjustingPhase); } @@ -983,7 +983,7 @@ void EngineBuffer::processTrackLocked( } } - for (const auto& pControl: m_engineControls) { + for (const auto& pControl: qAsConst(m_engineControls)) { pControl->setCurrentSample(m_filepos_play, m_trackSamplesOld, m_trackSampleRateOld); pControl->process(rate, m_filepos_play, iBufferSize); } @@ -1279,7 +1279,7 @@ void EngineBuffer::hintReader(const double dRate) { m_hintList.append(hint); } - for (const auto& pControl: m_engineControls) { + for (const auto& pControl: qAsConst(m_engineControls)) { pControl->hintReader(&m_hintList); } m_pReader->hintAndMaybeWake(m_hintList); @@ -1373,7 +1373,7 @@ void EngineBuffer::notifyTrackLoaded( TrackPointer pNewTrack, TrackPointer pOldTrack) { // First inform engineControls directly // Note: we are still in a worker thread. - for (const auto& pControl: m_engineControls) { + for (const auto& pControl: qAsConst(m_engineControls)) { pControl->trackLoaded(pNewTrack); } // Inform BaseTrackPlayer via a queued connection From 562387cc164dedd497e3661e7a195b3c13fe9af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Wed, 12 Dec 2018 21:58:21 +0100 Subject: [PATCH 25/27] reset play and cue indicator unconditionally after eject, fixing lp1808222 --- src/engine/cuecontrol.cpp | 5 +++++ src/engine/cuecontrol.h | 1 + src/engine/enginebuffer.cpp | 13 ++++--------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/engine/cuecontrol.cpp b/src/engine/cuecontrol.cpp index 86b5f3528a0..ab199a30ff4 100644 --- a/src/engine/cuecontrol.cpp +++ b/src/engine/cuecontrol.cpp @@ -983,6 +983,11 @@ void CueControl::updateIndicators() { } } +void CueControl::resetIndicators() { + m_pCueIndicator->setBlinkValue(ControlIndicator::OFF); + m_pPlayIndicator->setBlinkValue(ControlIndicator::OFF); +} + bool CueControl::isTrackAtCue() { return (fabs(getCurrentSample() - m_pCuePoint->get()) < 1.0f); } diff --git a/src/engine/cuecontrol.h b/src/engine/cuecontrol.h index 03a8413cc0a..f58845ea06e 100644 --- a/src/engine/cuecontrol.h +++ b/src/engine/cuecontrol.h @@ -99,6 +99,7 @@ class CueControl : public EngineControl { virtual void hintReader(HintVector* pHintList) override; bool updateIndicatorsAndModifyPlay(bool newPlay, bool playPossible); void updateIndicators(); + void resetIndicators(); bool isTrackAtCue(); bool isPlayingByPlayButton(); bool getPlayFlashingAtPause(); diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index f9d1f23403d..aab9c94ad4b 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -561,7 +561,7 @@ void EngineBuffer::ejectTrack() { m_timeElapsed->set(0); m_timeRemaining->set(0); m_playposSlider->set(0); - m_pCueControl->updateIndicators(); + m_pCueControl->resetIndicators(); doSeekFractional(0.0, SEEK_EXACT); m_pause.unlock(); @@ -753,11 +753,6 @@ void EngineBuffer::processTrackLocked( double speed = m_pRateControl->calculateSpeed( baserate, tempoRatio, paused, iBufferSize, &is_scratching, &is_reverse); - // The cue indicator may change when scratch state is changed - if (is_scratching != m_scratching_old) { - m_pCueControl->updateIndicators(); - } - bool useIndependentPitchAndTempoScaling = false; // TODO(owen): Maybe change this so that rubberband doesn't disable @@ -904,9 +899,9 @@ void EngineBuffer::processTrackLocked( // master samplerate), the deck speed, the pitch shift, and whether // the deck speed should affect the pitch. - m_pScale->setScaleParameters(baserate, - &speed, - &pitchRatio); + m_pScale->setScaleParameters(baserate, + &speed, + &pitchRatio); // The way we treat rate inside of EngineBuffer is actually a // description of "sample consumption rate" or percentage of samples From 94756f382f5ae7eb540d402b1614a15001b67e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Thu, 13 Dec 2018 21:45:45 +0100 Subject: [PATCH 26/27] replace single getter with one for the whol atomic struct. --- src/engine/bpmcontrol.cpp | 29 ++++++------ src/engine/cuecontrol.cpp | 73 ++++++++++++++++++------------ src/engine/cuecontrol.h | 9 +++- src/engine/enginecontrol.cpp | 17 ------- src/engine/enginecontrol.h | 29 ++++++++---- src/engine/loopingcontrol.cpp | 20 ++++---- src/engine/quantizecontrol.cpp | 7 +-- src/engine/ratecontrol.cpp | 2 +- src/engine/vinylcontrolcontrol.cpp | 3 +- src/test/looping_control_test.cpp | 30 ++++++------ 10 files changed, 115 insertions(+), 104 deletions(-) diff --git a/src/engine/bpmcontrol.cpp b/src/engine/bpmcontrol.cpp index f7b0af61d95..3978e2d5efb 100644 --- a/src/engine/bpmcontrol.cpp +++ b/src/engine/bpmcontrol.cpp @@ -156,8 +156,8 @@ void BpmControl::slotFileBpmChanged(double bpm) { BeatsPointer pBeats = m_pBeats; if (pBeats) { const double beats_bpm = - pBeats->getBpmAroundPosition(getCurrentSample(), - kLocalBpmSpan); + pBeats->getBpmAroundPosition( + getSampleOfTrack().current, kLocalBpmSpan); if (beats_bpm != -1) { m_pLocalBpm->set(beats_bpm); } else { @@ -192,7 +192,7 @@ void BpmControl::slotTranslateBeatsEarlier(double v) { BeatsPointer pBeats = m_pBeats; if (v > 0 && pBeats && (pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { - const int translate_dist = getTrackSampleRate() * -.01; + const int translate_dist = getSampleOfTrack().rate * -.01; pBeats->translate(translate_dist); } } @@ -202,7 +202,7 @@ void BpmControl::slotTranslateBeatsLater(double v) { if (v > 0 && pBeats && (pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { // TODO(rryan): Track::getSampleRate is possibly inaccurate! - const int translate_dist = getTrackSampleRate() * .01; + const int translate_dist = getSampleOfTrack().rate * .01; pBeats->translate(translate_dist); } } @@ -401,7 +401,7 @@ double BpmControl::calcSyncedRate(double userTweak) { // Now we need to get our beat distance so we can figure out how // out of phase we are. - double dThisPosition = getCurrentSample(); + double dThisPosition = getSampleOfTrack().current; double dBeatLength; double my_percentage; if (!BpmControl::getBeatContextNoLookup(dThisPosition, @@ -783,7 +783,7 @@ void BpmControl::slotUpdatedTrackBeats() { void BpmControl::slotBeatsTranslate(double v) { BeatsPointer pBeats = m_pBeats; if (v > 0 && pBeats && (pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) { - double currentSample = getCurrentSample(); + double currentSample = getSampleOfTrack().current; double closestBeat = pBeats->findClosestBeat(currentSample); int delta = currentSample - closestBeat; if (delta % 2 != 0) { @@ -800,7 +800,7 @@ void BpmControl::slotBeatsTranslateMatchAlignment(double v) { // otherwise it will always return 0 if master sync is active. m_dUserOffset.setValue(0.0); - double offset = getPhaseOffset(getCurrentSample()); + double offset = getPhaseOffset(getSampleOfTrack().current); pBeats->translate(-offset); } } @@ -810,8 +810,8 @@ double BpmControl::updateLocalBpm() { double local_bpm = 0; BeatsPointer pBeats = m_pBeats; if (pBeats) { - local_bpm = pBeats->getBpmAroundPosition(getCurrentSample(), - kLocalBpmSpan); + local_bpm = pBeats->getBpmAroundPosition( + getSampleOfTrack().current, kLocalBpmSpan); if (local_bpm == -1) { local_bpm = m_pFileBpm->get(); } @@ -826,7 +826,7 @@ double BpmControl::updateLocalBpm() { } double BpmControl::updateBeatDistance() { - double beat_distance = getBeatDistance(getCurrentSample()); + double beat_distance = getBeatDistance(getSampleOfTrack().current); m_pThisBeatDistance->set(beat_distance); if (getSyncMode() == SYNC_NONE) { m_dUserOffset.setValue(0.0); @@ -852,25 +852,24 @@ void BpmControl::resetSyncAdjustment() { void BpmControl::collectFeatures(GroupFeatureState* pGroupFeatures) const { // Without a beatgrid we don't know any beat details. - double dTrackSampleRate = getTrackSampleRate(); - if (!dTrackSampleRate || !m_pBeats) { + SampleOfTrack sot = getSampleOfTrack(); + if (!sot.rate || !m_pBeats) { return; } // Get the current position of this deck. - double dThisPosition = getCurrentSample(); double dThisPrevBeat = m_pPrevBeat->get(); double dThisNextBeat = m_pNextBeat->get(); double dThisBeatLength; double dThisBeatFraction; - if (getBeatContextNoLookup(dThisPosition, + 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 - / dTrackSampleRate / calcRateRatio(); + / sot.rate / calcRateRatio(); pGroupFeatures->has_beat_fraction = true; pGroupFeatures->beat_fraction = dThisBeatFraction; diff --git a/src/engine/cuecontrol.cpp b/src/engine/cuecontrol.cpp index ab199a30ff4..13fda8aebb9 100644 --- a/src/engine/cuecontrol.cpp +++ b/src/engine/cuecontrol.cpp @@ -332,7 +332,7 @@ void CueControl::hotcueSet(HotcueControl* pControl, double v) { double closestBeat = m_pClosestBeat->get(); double cuePosition = (m_pQuantizeEnabled->toBool() && closestBeat != -1) ? - closestBeat : getCurrentSample(); + closestBeat : getSampleOfTrack().current; pCue->setPosition(cuePosition); pCue->setHotCue(hotcue); pCue->setLabel(""); @@ -574,7 +574,7 @@ void CueControl::cueSet(double v) { QMutexLocker lock(&m_mutex); double closestBeat = m_pClosestBeat->get(); double cue = (m_pQuantizeEnabled->toBool() && closestBeat != -1) ? - closestBeat : getCurrentSample(); + closestBeat : getSampleOfTrack().current; m_pCuePoint->set(cue); TrackPointer pLoadedTrack = m_pLoadedTrack; lock.unlock(); @@ -665,6 +665,7 @@ void CueControl::cueCDJ(double v) { QMutexLocker lock(&m_mutex); const auto freely_playing = m_pPlay->toBool() && !getEngineBuffer()->getScratching(); + TrackAt trackAt = getTrackAt(); if (v) { if (m_iCurrentlyPreviewingHotcues) { @@ -673,7 +674,7 @@ void CueControl::cueCDJ(double v) { m_bPreviewing = true; lock.unlock(); seekAbs(m_pCuePoint->get()); - } else if (freely_playing || atEndPosition()) { + } else if (freely_playing || trackAt == TrackAt::End) { // Jump to cue when playing or when at end position // Just in case. @@ -684,7 +685,7 @@ void CueControl::cueCDJ(double v) { lock.unlock(); seekAbs(m_pCuePoint->get()); - } else if (isTrackAtCue()) { + } else if (trackAt == TrackAt::Cue) { // pause at cue point m_bPreviewing = true; m_pPlay->set(1.0); @@ -731,6 +732,7 @@ void CueControl::cueDenon(double v) { QMutexLocker lock(&m_mutex); bool playing = (m_pPlay->toBool()); + TrackAt trackAt = getTrackAt(); if (v) { if (m_iCurrentlyPreviewingHotcues) { @@ -739,7 +741,7 @@ void CueControl::cueDenon(double v) { m_bPreviewing = true; lock.unlock(); seekAbs(m_pCuePoint->get()); - } else if (!playing && isTrackAtCue()) { + } else if (!playing && trackAt == TrackAt::Cue) { // pause at cue point m_bPreviewing = true; m_pPlay->set(1.0); @@ -775,6 +777,7 @@ void CueControl::cuePlay(double v) { QMutexLocker lock(&m_mutex); const auto freely_playing = m_pPlay->toBool() && !getEngineBuffer()->getScratching(); + TrackAt trackAt = getTrackAt(); // pressed if (v) { @@ -786,7 +789,7 @@ void CueControl::cuePlay(double v) { lock.unlock(); seekAbs(m_pCuePoint->get()); - } else if (!isTrackAtCue() && getCurrentSample() <= getTotalSamples()) { + } else if (trackAt == TrackAt::ElseWhere) { // Pause not at cue point and not at end position cueSet(v); // Just in case. @@ -800,11 +803,10 @@ void CueControl::cuePlay(double v) { seekAbs(m_pCuePoint->get()); } } - } else if (isTrackAtCue()){ + } else if (trackAt == TrackAt::Cue){ m_bPreviewing = false; m_pPlay->set(1.0); lock.unlock(); - } } @@ -872,6 +874,8 @@ bool CueControl::updateIndicatorsAndModifyPlay(bool newPlay, bool playPossible) } } + TrackAt trackAt = getTrackAt(); + if (!playPossible) { // play not possible newPlay = false; @@ -885,7 +889,7 @@ bool CueControl::updateIndicatorsAndModifyPlay(bool newPlay, bool playPossible) // Pause: m_pStopButton->set(1.0); if (cueMode == CUE_MODE_DENON) { - if (isTrackAtCue() || previewing) { + if (trackAt == TrackAt::Cue || previewing) { m_pPlayIndicator->setBlinkValue(ControlIndicator::OFF); } else { // Flashing indicates that a following play would move cue point @@ -902,8 +906,7 @@ bool CueControl::updateIndicatorsAndModifyPlay(bool newPlay, bool playPossible) if (cueMode != CUE_MODE_DENON && cueMode != CUE_MODE_NUMARK) { if (m_pCuePoint->get() != -1) { - if (newPlay == 0.0 && !isTrackAtCue() && - !atEndPosition()) { + if (newPlay == 0.0 && trackAt == TrackAt::ElseWhere) { 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); @@ -925,14 +928,16 @@ bool CueControl::updateIndicatorsAndModifyPlay(bool newPlay, bool playPossible) return newPlay; } +// called from the engine thread void CueControl::updateIndicators() { // No need for mutex lock because we are only touching COs. double cueMode = m_pCueMode->get(); + TrackAt trackAt = getTrackAt(); if (cueMode == CUE_MODE_DENON || cueMode == CUE_MODE_NUMARK) { // Cue button is only lit at cue point bool playing = m_pPlay->toBool(); - if (isTrackAtCue()) { + if (trackAt == TrackAt::Cue) { // at cue point if (!playing) { m_pCueIndicator->setBlinkValue(ControlIndicator::ON); @@ -941,7 +946,7 @@ void CueControl::updateIndicators() { } else { m_pCueIndicator->setBlinkValue(ControlIndicator::OFF); if (!playing) { - if (!atEndPosition() && cueMode != CUE_MODE_NUMARK) { + if (trackAt != TrackAt::End && cueMode != CUE_MODE_NUMARK) { // Play will move cue point m_pPlayIndicator->setBlinkValue(ControlIndicator::RATIO1TO1_500MS); } else { @@ -956,24 +961,26 @@ void CueControl::updateIndicators() { 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 + switch (trackAt) { + case TrackAt::ElseWhere: + 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 if (m_pCuePoint->get() != -1) { + break; + case TrackAt::End: + // At track end + m_pCueIndicator->setBlinkValue(ControlIndicator::OFF); + break; + case TrackAt::Cue: // Next Press is preview m_pCueIndicator->setBlinkValue(ControlIndicator::ON); + break; } } else { // Cue indicator should be off when freely playing @@ -988,8 +995,16 @@ void CueControl::resetIndicators() { m_pPlayIndicator->setBlinkValue(ControlIndicator::OFF); } -bool CueControl::isTrackAtCue() { - return (fabs(getCurrentSample() - m_pCuePoint->get()) < 1.0f); +CueControl::TrackAt CueControl::getTrackAt() const { + SampleOfTrack sot = getSampleOfTrack(); + if (sot.current >= sot.total) { + return TrackAt::End; + } + double cue = m_pCuePoint->get(); + if (cue != -1 && fabs(sot.current - cue) < 1.0f) { + return TrackAt::Cue; + } + return TrackAt::ElseWhere; } bool CueControl::isPlayingByPlayButton() { diff --git a/src/engine/cuecontrol.h b/src/engine/cuecontrol.h index f58845ea06e..c9d82c06f84 100644 --- a/src/engine/cuecontrol.h +++ b/src/engine/cuecontrol.h @@ -100,10 +100,8 @@ class CueControl : public EngineControl { bool updateIndicatorsAndModifyPlay(bool newPlay, bool playPossible); void updateIndicators(); void resetIndicators(); - bool isTrackAtCue(); bool isPlayingByPlayButton(); bool getPlayFlashingAtPause(); - void trackLoaded(TrackPointer pNewTrack) override; private slots: @@ -131,10 +129,17 @@ class CueControl : public EngineControl { void playStutter(double v); private: + enum class TrackAt { + Cue, + End, + ElseWhere + }; + // These methods are not thread safe, only call them when the lock is held. void createControls(); void attachCue(CuePointer pCue, int hotcueNumber); void detachCue(int hotcueNumber); + TrackAt getTrackAt() const; bool m_bPreviewing; ControlObject* m_pPlay; diff --git a/src/engine/enginecontrol.cpp b/src/engine/enginecontrol.cpp index ef2b040fcf6..14fe7dc659f 100644 --- a/src/engine/enginecontrol.cpp +++ b/src/engine/enginecontrol.cpp @@ -51,23 +51,6 @@ void EngineControl::setCurrentSample( m_sampleOfTrack.setValue(sot); } -double EngineControl::getCurrentSample() const { - return m_sampleOfTrack.getValue().current; -} - -double EngineControl::getTotalSamples() const { - return m_sampleOfTrack.getValue().total; -} - -double EngineControl::getTrackSampleRate() const { - return m_sampleOfTrack.getValue().rate; -} - -bool EngineControl::atEndPosition() const { - SampleOfTrack sot = m_sampleOfTrack.getValue(); - return (sot.current >= sot.total); -} - QString EngineControl::getGroup() const { return m_group; } diff --git a/src/engine/enginecontrol.h b/src/engine/enginecontrol.h index 11464c06951..3d85173a1db 100644 --- a/src/engine/enginecontrol.h +++ b/src/engine/enginecontrol.h @@ -7,6 +7,8 @@ #include #include +#include + #include "preferences/usersettings.h" #include "track/track.h" #include "control/controlvalue.h" @@ -55,10 +57,6 @@ class EngineControl : public QObject { void setEngineBuffer(EngineBuffer* pEngineBuffer); virtual void setCurrentSample(const double dCurrentSample, const double dTotalSamples, const double dTrackSampleRate); - double getCurrentSample() const; - double getTotalSamples() const; - double getTrackSampleRate() const; - bool atEndPosition() const; QString getGroup() const; // Called to collect player features for effects processing. @@ -71,6 +69,15 @@ class EngineControl : public QObject { virtual void trackLoaded(TrackPointer pNewTrack); protected: + struct SampleOfTrack { + double current; + double total; + double rate; + }; + + SampleOfTrack getSampleOfTrack() const { + return m_sampleOfTrack.getValue(); + } void seek(double fractionalPosition); void seekAbs(double sample); // Seek to an exact sample and don't allow quantizing adjustment. @@ -85,15 +92,17 @@ class EngineControl : public QObject { UserSettingsPointer m_pConfig; private: - struct SampleOfTrack { - double current; - double total; - double rate; - }; - ControlValueAtomic m_sampleOfTrack; EngineMaster* m_pEngineMaster; EngineBuffer* m_pEngineBuffer; + + + FRIEND_TEST(LoopingControlTest, ReloopToggleButton_DoesNotJumpAhead); + FRIEND_TEST(LoopingControlTest, ReloopAndStopButton); + FRIEND_TEST(LoopingControlTest, LoopScale_HalvesLoop); + FRIEND_TEST(LoopingControlTest, LoopMoveTest); + FRIEND_TEST(LoopingControlTest, LoopResizeSeek); + FRIEND_TEST(LoopingControlTest, Beatjump_JumpsByBeats); }; #endif /* ENGINECONTROL_H */ diff --git a/src/engine/loopingcontrol.cpp b/src/engine/loopingcontrol.cpp index d094acb1b62..6b5e06cd56f 100644 --- a/src/engine/loopingcontrol.cpp +++ b/src/engine/loopingcontrol.cpp @@ -461,7 +461,7 @@ void LoopingControl::setLoopInToCurrentPosition() { if (m_pQuantizeEnabled->toBool() && pBeats) { if (m_bAdjustingLoopIn) { double closestBeat = m_pClosestBeat->get(); - if (closestBeat == getCurrentSample()) { + if (closestBeat == m_currentSample.getValue()) { quantizedBeat = closestBeat; } else { quantizedBeat = m_pPreviousBeat->get(); @@ -563,7 +563,7 @@ void LoopingControl::setLoopOutToCurrentPosition() { if (m_pQuantizeEnabled->toBool() && pBeats) { if (m_bAdjustingLoopOut) { double closestBeat = m_pClosestBeat->get(); - if (closestBeat == getCurrentSample()) { + if (closestBeat == m_currentSample.getValue()) { quantizedBeat = closestBeat; } else { quantizedBeat = m_pNextBeat->get(); @@ -693,7 +693,7 @@ void LoopingControl::slotReloopToggle(double val) { if (loopSamples.start != kNoTrigger && loopSamples.end != kNoTrigger && loopSamples.start <= loopSamples.end) { setLoopingEnabled(true); - if (getCurrentSample() > loopSamples.end) { + if (m_currentSample.getValue() > loopSamples.end) { slotLoopInGoto(1); } } @@ -936,6 +936,7 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable // give start and end defaults so we can detect problems LoopSamples newloopSamples = {kNoTrigger, kNoTrigger, false}; LoopSamples loopSamples = m_loopSamples.getValue(); + double currentSample = m_currentSample.getValue(); // Start from the current position/closest beat and // create the loop around X beats from there. @@ -943,16 +944,15 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable if (loopSamples.start != kNoTrigger) { newloopSamples.start = loopSamples.start; } else { - newloopSamples.start = getCurrentSample(); + newloopSamples.start = currentSample; } } else { // loop_in is set to the previous beat if quantize is on. The // closest beat might be ahead of play position which would cause a seek. // TODO: If in reverse, should probably choose nextBeat. - double cur_pos = getCurrentSample(); double prevBeat; double nextBeat; - pBeats->findPrevNextBeats(cur_pos, &prevBeat, &nextBeat); + pBeats->findPrevNextBeats(currentSample, &prevBeat, &nextBeat); if (m_pQuantizeEnabled->toBool() && prevBeat != -1) { if (beats >= 1.0) { @@ -967,7 +967,7 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable // If I press 1/4 beatloop, we want loop from 50% to 75% etc double beat_len = nextBeat - prevBeat; double loops_per_beat = 1.0 / beats; - double beat_pos = cur_pos - prevBeat; + double beat_pos = currentSample - prevBeat; int beat_frac = static_cast(floor((beat_pos / beat_len) * loops_per_beat)); @@ -975,7 +975,7 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable } } else { - newloopSamples.start = cur_pos; + newloopSamples.start = currentSample; } } @@ -1086,7 +1086,7 @@ void LoopingControl::slotBeatJump(double beats) { // If inside an active loop, move loop slotLoopMove(beats); } else { - seekAbs(pBeats->findNBeatsFromSample(getCurrentSample(), beats)); + seekAbs(pBeats->findNBeatsFromSample(currentSample, beats)); } } @@ -1112,7 +1112,7 @@ void LoopingControl::slotLoopMove(double beats) { return; } - if (BpmControl::getBeatContext(pBeats, getCurrentSample(), + if (BpmControl::getBeatContext(pBeats, m_currentSample.getValue(), nullptr, nullptr, nullptr, nullptr)) { double new_loop_in = pBeats->findNBeatsFromSample(loopSamples.start, beats); double new_loop_out = currentLoopMatchesBeatloopSize() ? diff --git a/src/engine/quantizecontrol.cpp b/src/engine/quantizecontrol.cpp index e16c526c084..cd638e9e711 100644 --- a/src/engine/quantizecontrol.cpp +++ b/src/engine/quantizecontrol.cpp @@ -60,15 +60,16 @@ void QuantizeControl::slotBeatsUpdated() { TrackPointer pTrack = m_pTrack; if (pTrack) { m_pBeats = pTrack->getBeats(); - lookupBeatPositions(getCurrentSample()); - updateClosestBeat(getCurrentSample()); + double current = getSampleOfTrack().current; + lookupBeatPositions(current); + updateClosestBeat(current); } } void QuantizeControl::setCurrentSample(const double dCurrentSample, const double dTotalSamples, const double dTrackSampleRate) { - if (dCurrentSample == getCurrentSample()) { + if (dCurrentSample == getSampleOfTrack().current) { // No need to recalculate. return; } diff --git a/src/engine/ratecontrol.cpp b/src/engine/ratecontrol.cpp index ef14fca0d77..109ecce8c9b 100644 --- a/src/engine/ratecontrol.cpp +++ b/src/engine/ratecontrol.cpp @@ -484,7 +484,7 @@ double RateControl::calculateSpeed(double baserate, double speed, bool paused, } } - double currentSample = getCurrentSample(); + double currentSample = getSampleOfTrack().current; m_pScratchController->process(currentSample, rate, iSamplesPerBuffer, baserate); // If waveform scratch is enabled, override all other controls diff --git a/src/engine/vinylcontrolcontrol.cpp b/src/engine/vinylcontrolcontrol.cpp index c8bfcc7a956..31248dd494a 100644 --- a/src/engine/vinylcontrolcontrol.cpp +++ b/src/engine/vinylcontrolcontrol.cpp @@ -89,8 +89,7 @@ void VinylControlControl::slotControlVinylSeek(double fractionalPos) { return; } - - double total_samples = getTotalSamples(); + double total_samples = getSampleOfTrack().total; double new_playpos = round(fractionalPos * total_samples); if (m_pControlVinylEnabled->get() > 0.0 && m_pControlVinylMode->get() == MIXXX_VCMODE_RELATIVE) { diff --git a/src/test/looping_control_test.cpp b/src/test/looping_control_test.cpp index 42ee3ad5dc0..cd6ba998b67 100644 --- a/src/test/looping_control_test.cpp +++ b/src/test/looping_control_test.cpp @@ -336,7 +336,7 @@ TEST_F(LoopingControlTest, ReloopToggleButton_DoesNotJumpAhead) { m_pButtonReloopToggle->slotSet(1); m_pButtonReloopToggle->slotSet(0); seekToSampleAndProcess(50); - EXPECT_LE(m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample(), m_pLoopStartPoint->get()); + EXPECT_LE(m_pChannel1->getEngineBuffer()->m_pLoopingControl->getSampleOfTrack().current, m_pLoopStartPoint->get()); } TEST_F(LoopingControlTest, ReloopAndStopButton) { @@ -348,7 +348,7 @@ TEST_F(LoopingControlTest, ReloopAndStopButton) { m_pButtonReloopAndStop->slotSet(1); m_pButtonReloopAndStop->slotSet(0); ProcessBuffer(); - EXPECT_EQ(m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample(), m_pLoopStartPoint->get()); + EXPECT_EQ(m_pChannel1->getEngineBuffer()->m_pLoopingControl->getSampleOfTrack().current, m_pLoopStartPoint->get()); EXPECT_TRUE(m_pLoopEnabled->toBool()); } @@ -375,7 +375,7 @@ TEST_F(LoopingControlTest, LoopScale_HalvesLoop) { seekToSampleAndProcess(1800); EXPECT_EQ(0, m_pLoopStartPoint->get()); EXPECT_EQ(2000, m_pLoopEndPoint->get()); - EXPECT_EQ(1800, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample()); + EXPECT_EQ(1800, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getSampleOfTrack().current); EXPECT_FALSE(isLoopEnabled()); m_pLoopScale->set(0.5); ProcessBuffer(); @@ -384,7 +384,7 @@ TEST_F(LoopingControlTest, LoopScale_HalvesLoop) { // The loop was not enabled so halving the loop should not move the playhead // even though it is outside the loop. - EXPECT_EQ(1800, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample()); + EXPECT_EQ(1800, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getSampleOfTrack().current); m_pButtonReloopToggle->slotSet(1); EXPECT_TRUE(isLoopEnabled()); @@ -512,7 +512,7 @@ TEST_F(LoopingControlTest, LoopMoveTest) { EXPECT_TRUE(isLoopEnabled()); EXPECT_EQ(0, m_pLoopStartPoint->get()); EXPECT_EQ(300, m_pLoopEndPoint->get()); - EXPECT_EQ(10, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample()); + EXPECT_EQ(10, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getSampleOfTrack().current); // Move the loop out from under the playposition. m_pButtonBeatMoveForward->set(1.0); @@ -522,7 +522,7 @@ TEST_F(LoopingControlTest, LoopMoveTest) { EXPECT_EQ(44400, m_pLoopEndPoint->get()); ProcessBuffer(); // Should seek to the corresponding offset within the moved loop - EXPECT_EQ(44110, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample()); + EXPECT_EQ(44110, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getSampleOfTrack().current); // Move backward so that the current position is outside the new location of the loop m_pChannel1->getEngineBuffer()->queueNewPlaypos(44300, EngineBuffer::SEEK_STANDARD); @@ -534,7 +534,7 @@ TEST_F(LoopingControlTest, LoopMoveTest) { EXPECT_NEAR(300, m_pLoopEndPoint->get(), kLoopPositionMaxAbsError); ProcessBuffer(); EXPECT_NEAR(200, - m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample(), + m_pChannel1->getEngineBuffer()->m_pLoopingControl->getSampleOfTrack().current, kLoopPositionMaxAbsError); // Now repeat the test with looping disabled (should not affect the @@ -550,7 +550,7 @@ TEST_F(LoopingControlTest, LoopMoveTest) { EXPECT_EQ(44400, m_pLoopEndPoint->get()); // Should not seek inside the moved loop when the loop is disabled EXPECT_NEAR(200, - m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample(), + m_pChannel1->getEngineBuffer()->m_pLoopingControl->getSampleOfTrack().current, kLoopPositionMaxAbsError); // Move backward so that the current position is outside the new location of the loop @@ -562,7 +562,7 @@ TEST_F(LoopingControlTest, LoopMoveTest) { EXPECT_EQ(0, m_pLoopStartPoint->get()); EXPECT_NEAR(300, m_pLoopEndPoint->get(), kLoopPositionMaxAbsError); EXPECT_NEAR(500, - m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample(), + m_pChannel1->getEngineBuffer()->m_pLoopingControl->getSampleOfTrack().current, kLoopPositionMaxAbsError); } @@ -582,7 +582,7 @@ TEST_F(LoopingControlTest, LoopResizeSeek) { EXPECT_TRUE(isLoopEnabled()); EXPECT_EQ(0, m_pLoopStartPoint->get()); EXPECT_EQ(600, m_pLoopEndPoint->get()); - EXPECT_EQ(500, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample()); + EXPECT_EQ(500, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getSampleOfTrack().current); // Activate a shorter loop m_pButtonBeatLoop2Activate->set(1.0); @@ -594,7 +594,7 @@ TEST_F(LoopingControlTest, LoopResizeSeek) { EXPECT_EQ(0, m_pLoopStartPoint->get()); EXPECT_EQ(450, m_pLoopEndPoint->get()); ProcessBuffer(); - EXPECT_EQ(50, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample()); + EXPECT_EQ(50, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getSampleOfTrack().current); // But if looping is not enabled, no warping occurs. m_pLoopStartPoint->slotSet(0); @@ -604,14 +604,14 @@ TEST_F(LoopingControlTest, LoopResizeSeek) { EXPECT_FALSE(isLoopEnabled()); EXPECT_EQ(0, m_pLoopStartPoint->get()); EXPECT_EQ(600, m_pLoopEndPoint->get()); - EXPECT_EQ(500, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample()); + EXPECT_EQ(500, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getSampleOfTrack().current); m_pButtonBeatLoop2Activate->set(1.0); ProcessBuffer(); EXPECT_EQ(500, m_pLoopStartPoint->get()); EXPECT_EQ(950, m_pLoopEndPoint->get()); - EXPECT_EQ(500, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample()); + EXPECT_EQ(500, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getSampleOfTrack().current); } TEST_F(LoopingControlTest, BeatLoopSize_SetAndToggle) { @@ -789,11 +789,11 @@ TEST_F(LoopingControlTest, Beatjump_JumpsByBeats) { m_pButtonBeatJumpForward->set(1.0); m_pButtonBeatJumpForward->set(0.0); ProcessBuffer(); - EXPECT_EQ(beatLength * 4, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample()); + EXPECT_EQ(beatLength * 4, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getSampleOfTrack().current); m_pButtonBeatJumpBackward->set(1.0); m_pButtonBeatJumpBackward->set(0.0); ProcessBuffer(); - EXPECT_EQ(0, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample()); + EXPECT_EQ(0, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getSampleOfTrack().current); } TEST_F(LoopingControlTest, Beatjump_MovesActiveLoop) { From d6b9f8b22e4f7784820a97c6fc8642269a61c7db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Thu, 13 Dec 2018 21:56:38 +0100 Subject: [PATCH 27/27] unlock mutex before emit a signal --- src/util/tapfilter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/util/tapfilter.cpp b/src/util/tapfilter.cpp index 240b0a4018d..299ddeb2b56 100644 --- a/src/util/tapfilter.cpp +++ b/src/util/tapfilter.cpp @@ -14,7 +14,10 @@ void TapFilter::tap() { QMutexLocker locker(&m_mutex); mixxx::Duration elapsed = m_timer.restart(); if (elapsed <= m_maxInterval) { - emit(tapped(m_mean.insert(elapsed.toDoubleMillis()), m_mean.size())); + double averageLength = m_mean.insert(elapsed.toDoubleMillis()); + int numSamples = m_mean.size(); + locker.unlock(); + emit(tapped(averageLength, numSamples)); } else { // Reset the filter m_mean.clear();