1 #pragma once 2 3 #include <gtest/gtest_prod.h> 4 5 #include <QAtomicInt> 6 #include <QMutex> 7 #include <cfloat> 8 9 #include "control/controlvalue.h" 10 #include "engine/cachingreader/cachingreader.h" 11 #include "engine/engineobject.h" 12 #include "engine/sync/syncable.h" 13 #include "preferences/usersettings.h" 14 #include "track/track_decl.h" 15 #include "util/rotary.h" 16 #include "util/types.h" 17 18 //for the writer 19 #ifdef __SCALER_DEBUG__ 20 #include <QFile> 21 #include <QTextStream> 22 #endif 23 24 class EngineChannel; 25 class EngineControl; 26 class BpmControl; 27 class KeyControl; 28 class RateControl; 29 class SyncControl; 30 class VinylControlControl; 31 class LoopingControl; 32 class ClockControl; 33 class CueControl; 34 class ReadAheadManager; 35 class ControlObject; 36 class ControlProxy; 37 class ControlPushButton; 38 class ControlIndicator; 39 class ControlBeat; 40 class ControlTTRotary; 41 class ControlPotmeter; 42 class EngineBufferScale; 43 class EngineBufferScaleLinear; 44 class EngineBufferScaleST; 45 class EngineBufferScaleRubberBand; 46 class EngineSync; 47 class EngineWorkerScheduler; 48 class VisualPlayPosition; 49 class EngineMaster; 50 51 class EngineBuffer : public EngineObject { 52 Q_OBJECT 53 private: 54 enum SyncRequestQueued { 55 SYNC_REQUEST_NONE, 56 SYNC_REQUEST_ENABLE, 57 SYNC_REQUEST_DISABLE, 58 SYNC_REQUEST_ENABLEDISABLE, 59 }; 60 public: 61 enum SeekRequest { 62 SEEK_NONE = 0x00, 63 SEEK_PHASE = 0x01, // This is set to force an in-phase seek. 64 SEEK_EXACT = 0x02, // This is used to seek to position regardless of 65 // if Quantize is enabled. 66 SEEK_EXACT_PHASE = SEEK_PHASE | SEEK_EXACT, 67 // This is an artificial state that happens if 68 // an exact seek and a phase seek are scheduled 69 // at the same time. 70 SEEK_STANDARD = 0x04, // This seeks to the exact position if Quantize is 71 // disabled or performs an in-phase seek if it is enabled. 72 SEEK_STANDARD_PHASE = SEEK_STANDARD | SEEK_PHASE, 73 // This is an artificial state that happens if 74 // a standard seek and a phase seek are scheduled 75 // at the same time. 76 }; 77 Q_DECLARE_FLAGS(SeekRequests, SeekRequest); 78 79 enum KeylockEngine { 80 SOUNDTOUCH, 81 RUBBERBAND, 82 KEYLOCK_ENGINE_COUNT, 83 }; 84 85 // This value is used to make sure the initial seek after loading a track is 86 // not omitted. Therefore this value must be different for 0.0 or any likely 87 // value for the main cue 88 static constexpr double kInitalSamplePosition = -DBL_MAX; 89 90 EngineBuffer(const QString& group, UserSettingsPointer pConfig, 91 EngineChannel* pChannel, EngineMaster* pMixingEngine); 92 virtual ~EngineBuffer(); 93 94 void bindWorkers(EngineWorkerScheduler* pWorkerScheduler); 95 96 // Return the current rate (not thread-safe) 97 double getSpeed(); 98 bool getScratching(); 99 // Returns current bpm value (not thread-safe) 100 double getBpm(); 101 // Returns the BPM of the loaded track around the current position (not thread-safe) 102 double getLocalBpm(); 103 // Sets pointer to other engine buffer/channel 104 void setEngineMaster(EngineMaster*); 105 106 // Queues a new seek position. Use SEEK_EXACT or SEEK_STANDARD as seekType 107 void queueNewPlaypos(double newpos, enum SeekRequest seekType); 108 void requestSyncPhase(); 109 void requestEnableSync(bool enabled); 110 void requestSyncMode(SyncMode mode); 111 void requestClonePosition(EngineChannel* pChannel); 112 113 // The process methods all run in the audio callback. 114 void process(CSAMPLE* pOut, const int iBufferSize); 115 void processSlip(int iBufferSize); 116 void postProcess(const int iBufferSize); 117 118 QString getGroup(); 119 bool isTrackLoaded(); 120 // return true if a seek is currently cueued but not yet processed, false otherwise 121 // if no seek was queued, the seek position is set to -1 122 bool getQueuedSeekPosition(double* pSeekPosition); 123 TrackPointer getLoadedTrack() const; 124 125 bool isReverse(); 126 127 double getExactPlayPos(); 128 double getVisualPlayPos(); 129 double getTrackSamples(); 130 131 void collectFeatures(GroupFeatureState* pGroupFeatures) const; 132 133 double getRateRatio() const; 134 135 // For dependency injection of readers. 136 //void setReader(CachingReader* pReader); 137 138 // For dependency injection of scalers. 139 void setScalerForTest(EngineBufferScale* pScaleVinyl, 140 EngineBufferScale* pScaleKeylock); 141 142 // For injection of fake tracks. 143 void loadFakeTrack(TrackPointer pTrack, bool bPlay); 144 getKeylockEngineName(KeylockEngine engine)145 static QString getKeylockEngineName(KeylockEngine engine) { 146 switch (engine) { 147 case SOUNDTOUCH: 148 return tr("Soundtouch (faster)"); 149 case RUBBERBAND: 150 return tr("Rubberband (better)"); 151 default: 152 return tr("Unknown (bad value)"); 153 } 154 } 155 156 // Request that the EngineBuffer load a track. Since the process is 157 // asynchronous, EngineBuffer will emit a trackLoaded signal when the load 158 // has completed. 159 void loadTrack(TrackPointer pTrack, bool play); 160 setChannelIndex(int channelIndex)161 void setChannelIndex(int channelIndex) { 162 m_channelIndex = channelIndex; 163 } 164 165 public slots: 166 void slotControlPlayRequest(double); 167 void slotControlPlayFromStart(double); 168 void slotControlJumpToStartAndStop(double); 169 void slotControlStop(double); 170 void slotControlStart(double); 171 void slotControlEnd(double); 172 void slotControlSeek(double); 173 void slotControlSeekAbs(double); 174 void slotControlSeekExact(double); 175 void slotKeylockEngineChanged(double); 176 177 void slotEjectTrack(double); 178 179 signals: 180 void trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack); 181 void trackLoadFailed(TrackPointer pTrack, const QString& reason); 182 183 private slots: 184 void slotTrackLoading(); 185 void slotTrackLoaded(TrackPointer pTrack, 186 int iSampleRate, int iNumSamples); 187 void slotTrackLoadFailed(TrackPointer pTrack, 188 const QString& reason); 189 // Fired when passthrough mode is enabled or disabled. 190 void slotPassthroughChanged(double v); 191 void slotUpdatedTrackBeats(); 192 193 private: 194 struct QueuedSeek { 195 double position; 196 enum SeekRequest seekType; 197 }; 198 199 // Add an engine control to the EngineBuffer 200 // must not be called outside the Constructor 201 void addControl(EngineControl* pControl); 202 203 void enableIndependentPitchTempoScaling(bool bEnable, 204 const int iBufferSize); 205 206 void updateIndicators(double rate, int iBufferSize); 207 208 void hintReader(const double rate); 209 210 void ejectTrack(); 211 212 double fractionalPlayposFromAbsolute(double absolutePlaypos); 213 214 void doSeekFractional(double fractionalPos, enum SeekRequest seekType); 215 void doSeekPlayPos(double playpos, enum SeekRequest seekType); 216 217 // Read one buffer from the current scaler into the crossfade buffer. Used 218 // for transitioning from one scaler to another, or reseeking a scaler 219 // to prevent pops. 220 void readToCrossfadeBuffer(const int iBufferSize); 221 222 // Copy the play position from the given buffer 223 void seekCloneBuffer(EngineBuffer* pOtherBuffer); 224 225 // Reset buffer playpos and set file playpos. 226 void setNewPlaypos(double playpos); 227 228 void processSyncRequests(); 229 void processSeek(bool paused); 230 231 bool updateIndicatorsAndModifyPlay(bool newPlay, bool oldPlay); 232 void verifyPlay(); 233 void notifyTrackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack); 234 void processTrackLocked(CSAMPLE* pOutput, const int iBufferSize, int sample_rate); 235 236 // Holds the name of the control group 237 const QString m_group; 238 int m_channelIndex; 239 240 UserSettingsPointer m_pConfig; 241 242 friend class CueControlTest; 243 244 LoopingControl* m_pLoopingControl; // used for testes 245 FRIEND_TEST(LoopingControlTest, LoopScale_HalvesLoop); 246 FRIEND_TEST(LoopingControlTest, LoopMoveTest); 247 FRIEND_TEST(LoopingControlTest, LoopResizeSeek); 248 FRIEND_TEST(LoopingControlTest, ReloopToggleButton_DoesNotJumpAhead); 249 FRIEND_TEST(LoopingControlTest, ReloopAndStopButton); 250 FRIEND_TEST(LoopingControlTest, Beatjump_JumpsByBeats); 251 FRIEND_TEST(SyncControlTest, TestDetermineBpmMultiplier); 252 FRIEND_TEST(EngineSyncTest, HalfDoubleBpmTest); 253 FRIEND_TEST(EngineSyncTest, HalfDoubleThenPlay); 254 FRIEND_TEST(EngineSyncTest, UserTweakBeatDistance); 255 FRIEND_TEST(EngineSyncTest, UserTweakPreservedInSeek); 256 FRIEND_TEST(EngineSyncTest, BeatMapQantizePlay); 257 FRIEND_TEST(EngineBufferTest, ScalerNoTransport); 258 EngineSync* m_pEngineSync; 259 SyncControl* m_pSyncControl; 260 VinylControlControl* m_pVinylControlControl; 261 RateControl* m_pRateControl; 262 BpmControl* m_pBpmControl; 263 KeyControl* m_pKeyControl; 264 ClockControl* m_pClockControl; 265 CueControl* m_pCueControl; 266 267 QList<EngineControl*> m_engineControls; 268 269 // The read ahead manager for EngineBufferScale's that need to read ahead 270 ReadAheadManager* m_pReadAheadManager; 271 272 // The reader used to read audio files 273 CachingReader* m_pReader; 274 275 // List of hints to provide to the CachingReader 276 HintVector m_hintList; 277 278 // The current sample to play in the file. 279 double m_filepos_play; 280 281 // The previous callback's speed. Used to check if the scaler parameters 282 // need updating. 283 double m_speed_old; 284 285 // The previous callback's tempo ratio. 286 double m_tempo_ratio_old; 287 288 // True if the previous callback was scratching. 289 bool m_scratching_old; 290 291 // True if the previous callback was reverse. 292 bool m_reverse_old; 293 294 // The previous callback's pitch. Used to check if the scaler parameters 295 // need updating. 296 double m_pitch_old; 297 298 // The previous callback's baserate. Used to check if the scaler parameters 299 // need updating. 300 double m_baserate_old; 301 302 // Copy of rate_exchange, used to check if rate needs to be updated 303 double m_rate_old; 304 305 // Copy of length of file 306 double m_trackSamplesOld; 307 308 // Copy of file sample rate 309 double m_trackSampleRateOld; 310 311 // Mutex controlling whether the process function is in pause mode. This happens 312 // during seek and loading of a new track 313 QMutex m_pause; 314 // Used in update of playpos slider 315 int m_iSamplesSinceLastIndicatorUpdate; 316 317 // The location where the track would have been had slip not been engaged 318 double m_dSlipPosition; 319 // Saved value of rate for slip mode 320 double m_dSlipRate; 321 // m_bSlipEnabledProcessing is only used by the engine processing thread. 322 bool m_bSlipEnabledProcessing; 323 324 ControlObject* m_pTrackSamples; 325 ControlObject* m_pTrackSampleRate; 326 327 ControlPushButton* m_playButton; 328 ControlPushButton* m_playStartButton; 329 ControlPushButton* m_stopStartButton; 330 ControlPushButton* m_stopButton; 331 332 ControlObject* m_fwdButton; 333 ControlObject* m_backButton; 334 ControlPushButton* m_pSlipButton; 335 336 ControlObject* m_pQuantize; 337 ControlObject* m_pMasterRate; 338 ControlPotmeter* m_playposSlider; 339 ControlProxy* m_pSampleRate; 340 ControlProxy* m_pKeylockEngine; 341 ControlPushButton* m_pKeylock; 342 343 // This ControlProxys is created as parent to this and deleted by 344 // the Qt object tree. This helps that they are deleted by the creating 345 // thread, which is required to avoid segfaults. 346 ControlProxy* m_pPassthroughEnabled; 347 348 ControlPushButton* m_pEject; 349 ControlObject* m_pTrackLoaded; 350 351 // Whether or not to repeat the track when at the end 352 ControlPushButton* m_pRepeat; 353 354 // Fwd and back controls, start and end of track control 355 ControlPushButton* m_startButton; 356 ControlPushButton* m_endButton; 357 358 // Object used to perform waveform scaling (sample rate conversion). These 359 // three pointers may be reassigned depending on configuration and tests. 360 EngineBufferScale* m_pScale; 361 FRIEND_TEST(EngineBufferTest, SlowRubberBand); 362 FRIEND_TEST(EngineBufferTest, ResetPitchAdjustUsesLinear); 363 FRIEND_TEST(EngineBufferTest, VinylScalerRampZero); 364 FRIEND_TEST(EngineBufferTest, ReadFadeOut); 365 FRIEND_TEST(EngineBufferTest, RateTempTest); 366 FRIEND_TEST(EngineBufferTest, RatePermTest); 367 EngineBufferScale* m_pScaleVinyl; 368 // The keylock engine is configurable, so it could flip flop between 369 // ScaleST and ScaleRB during a single callback. 370 EngineBufferScale* volatile m_pScaleKeylock; 371 372 // Object used for vinyl-style interpolation scaling of the audio 373 EngineBufferScaleLinear* m_pScaleLinear; 374 // Objects used for pitch-indep time stretch (key lock) scaling of the audio 375 EngineBufferScaleST* m_pScaleST; 376 EngineBufferScaleRubberBand* m_pScaleRB; 377 378 // Indicates whether the scaler has changed since the last process() 379 bool m_bScalerChanged; 380 // Indicates that dependency injection has taken place. 381 bool m_bScalerOverride; 382 383 QAtomicInt m_iSeekPhaseQueued; 384 QAtomicInt m_iEnableSyncQueued; 385 QAtomicInt m_iSyncModeQueued; 386 ControlValueAtomic<QueuedSeek> m_queuedSeek; 387 static constexpr QueuedSeek kNoQueuedSeek = { 388 -1.0, SEEK_NONE}; // value used if no seek is queued 389 QAtomicPointer<EngineChannel> m_pChannelToCloneFrom; 390 391 // Is true if the previous buffer was silent due to pausing 392 QAtomicInt m_iTrackLoading; 393 bool m_bPlayAfterLoading; 394 // Records the sample rate so we can detect when it changes. Initialized to 395 // 0 to guarantee we see a change on the first callback. 396 int m_iSampleRate; 397 398 TrackPointer m_pCurrentTrack; 399 #ifdef __SCALER_DEBUG__ 400 QFile df; 401 QTextStream writer; 402 #endif 403 404 // Certain operations like seeks and engine changes need to be crossfaded 405 // to eliminate clicks and pops. 406 CSAMPLE* m_pCrossfadeBuffer; 407 bool m_bCrossfadeReady; 408 int m_iLastBufferSize; 409 410 QSharedPointer<VisualPlayPosition> m_visualPlayPos; 411 }; 412 413 Q_DECLARE_OPERATORS_FOR_FLAGS(EngineBuffer::SeekRequests) 414