1 #pragma once 2 3 #include <vector> 4 5 #include "analyzer/analyzer.h" 6 #include "analyzer/analyzerprogress.h" 7 #include "preferences/usersettings.h" 8 #include "rigtorp/SPSCQueue.h" 9 #include "sources/audiosource.h" 10 #include "track/track_decl.h" 11 #include "track/trackid.h" 12 #include "util/db/dbconnectionpool.h" 13 #include "util/memory.h" 14 #include "util/performancetimer.h" 15 #include "util/samplebuffer.h" 16 #include "util/workerthread.h" 17 18 enum AnalyzerModeFlags { 19 None = 0x00, 20 WithBeats = 0x01, 21 WithWaveform = 0x02, 22 LowPriority = 0x04, 23 All = WithBeats | WithWaveform, 24 }; 25 26 enum class AnalyzerThreadState { 27 Void, 28 Idle, 29 Busy, 30 Done, 31 Exit, 32 }; 33 34 Q_DECLARE_TYPEINFO(AnalyzerThreadState, Q_MOVABLE_TYPE); 35 Q_DECLARE_METATYPE(AnalyzerThreadState); 36 37 // Atomic control values are used for transferring data between the 38 // host and the worker thread, e.g. the next track to be analyzed or 39 // the current analyzer progress that can be read independent of any 40 // progress signal 41 // 42 // The frequency of progress signal is limited to avoid flooding the 43 // signal queued connection between the internal worker thread and 44 // the host, which might otherwise cause unresponsiveness of the host. 45 class AnalyzerThread : public WorkerThread { 46 Q_OBJECT 47 48 public: 49 typedef std::unique_ptr<AnalyzerThread, void (*)(AnalyzerThread*)> Pointer; 50 // Subclass that provides a default constructor and nothing else 51 class NullPointer : public Pointer { 52 public: 53 NullPointer(); 54 }; 55 56 static Pointer createInstance( 57 int id, 58 mixxx::DbConnectionPoolPtr dbConnectionPool, 59 UserSettingsPointer pConfig, 60 AnalyzerModeFlags modeFlags); 61 62 /*private*/ AnalyzerThread( 63 int id, 64 mixxx::DbConnectionPoolPtr dbConnectionPool, 65 UserSettingsPointer pConfig, 66 AnalyzerModeFlags modeFlags); 67 ~AnalyzerThread() override = default; 68 id()69 int id() const { 70 return m_id; 71 } 72 73 // Submits the next track to the worker thread without 74 // blocking. This is only allowed after a progress() signal 75 // with state Idle has been received to avoid overwriting 76 // a previously sent track that has not been received by the 77 // worker thread, yet. 78 bool submitNextTrack(TrackPointer nextTrack); 79 80 signals: 81 // Use a single signal for progress updates to ensure that all signals 82 // are queued and received in the same order as emitted from the internal 83 // worker thread. Different signals would otherwise end up in different 84 // queued connections which are processed in an undefined order! 85 // TODO(uklotzde): Encapsulate all signal parameters into an 86 // AnalyzerThreadProgress object and register it as a new meta type. 87 void progress(int threadId, AnalyzerThreadState threadState, TrackId trackId, AnalyzerProgress trackProgress); 88 89 protected: 90 void doRun() override; 91 92 TryFetchWorkItemsResult tryFetchWorkItems() override; 93 94 private: 95 ///////////////////////////////////////////////////////////////////////// 96 // Immutable values and pointers (objects are thread-safe) 97 const int m_id; 98 const mixxx::DbConnectionPoolPtr m_dbConnectionPool; 99 const UserSettingsPointer m_pConfig; 100 const AnalyzerModeFlags m_modeFlags; 101 102 ///////////////////////////////////////////////////////////////////////// 103 // Thread-safe atomic values 104 105 // There is only one consumer (namely the worker thread) and one producer 106 // (the host thread) for this value. A single value is written and then 107 // read so a lock-free FIFO with the minimum capacity is sufficient for 108 // safely exchanging data between two threads. 109 // NOTE(uklotzde, 2018-01-04): Ideally we would use std::atomic<TrackPointer>, 110 // for this purpose, which will become available in C++20. 111 rigtorp::SPSCQueue<TrackPointer> m_nextTrack; 112 113 ///////////////////////////////////////////////////////////////////////// 114 // Thread local: Only used in the constructor/destructor and within 115 // run() by the worker thread. 116 117 std::vector<AnalyzerWithState> m_analyzers; 118 119 mixxx::SampleBuffer m_sampleBuffer; 120 121 TrackPointer m_currentTrack; 122 123 AnalyzerThreadState m_emittedState; 124 125 PerformanceTimer m_lastBusyProgressEmittedTimer; 126 127 enum class AnalysisResult { 128 Pending, 129 Finished, 130 Cancelled, 131 }; 132 AnalysisResult analyzeAudioSource( 133 const mixxx::AudioSourcePointer& audioSource); 134 135 // Blocks the worker thread until a next track becomes available 136 TrackPointer receiveNextTrack(); 137 138 // Conditionally emit a progress() signal while busy (frequency is limited) 139 void emitBusyProgress(AnalyzerProgress busyProgress); 140 141 // Unconditionally emits a progress() signal when done 142 void emitDoneProgress(AnalyzerProgress doneProgress); 143 144 // Unconditionally emits any kind of progress() signal if not current track is present 145 void emitProgress(AnalyzerThreadState state); 146 147 // Unconditionally emits any kind of progress() signal 148 void emitProgress(AnalyzerThreadState state, TrackId trackId, AnalyzerProgress trackProgress); 149 }; 150