![]() |
Mixxx
|
00001 // cachingreader.h 00002 // Created 7/9/2009 by RJ Ryan (rryan@mit.edu) 00003 00004 #ifndef CACHINGREADER_H 00005 #define CACHINGREADER_H 00006 00007 #include <QDebug> 00008 #include <QMutex> 00009 #include <QQueue> 00010 #include <QWaitCondition> 00011 #include <QList> 00012 #include <QVector> 00013 #include <QLinkedList> 00014 #include <QHash> 00015 #include <QThread> 00016 00017 #include "defs.h" 00018 #include "configobject.h" 00019 #include "trackinfoobject.h" 00020 #include "engine/engineworker.h" 00021 #include "util/fifo.h" 00022 00023 class ControlObjectThread; 00024 namespace Mixxx { 00025 class SoundSource; 00026 } 00027 00028 // A Hint is an indication to the CachingReader that a certain section of a 00029 // SoundSource will be used 'soon' and so it should be brought into memory by 00030 // the reader work thread. 00031 typedef struct Hint { 00032 // The sample to ensure is present in memory. 00033 int sample; 00034 // If a range of samples should be present, use length to indicate that the 00035 // range (sample, sample+length) should be present in memory. 00036 int length; 00037 // Currently unused -- but in the future could be used to prioritize certain 00038 // hints over others. A priority of 1 is the highest priority and should be 00039 // used for samples that will be read imminently. Hints for samples that 00040 // have the potential to be read (i.e. a cue point) should be issued with 00041 // priority >10. 00042 int priority; 00043 } Hint; 00044 00045 // A Chunk is a section of audio that is being cached. The chunk_number can be 00046 // used to figure out the sample number of the first sample in data by using 00047 // sampleForChunk() 00048 typedef struct Chunk { 00049 int chunk_number; 00050 int length; 00051 CSAMPLE* data; 00052 Chunk* prev_lru; 00053 Chunk* next_lru; 00054 } Chunk; 00055 00056 typedef struct ChunkReadRequest { 00057 Chunk* chunk; 00058 00059 ChunkReadRequest() { chunk = NULL; } 00060 } ChunkReadRequest; 00061 00062 enum ReaderStatus { 00063 INVALID, 00064 TRACK_NOT_LOADED, 00065 TRACK_LOADED, 00066 CHUNK_READ_SUCCESS, 00067 CHUNK_READ_EOF, 00068 CHUNK_READ_INVALID 00069 }; 00070 00071 typedef struct ReaderStatusUpdate { 00072 ReaderStatus status; 00073 Chunk* chunk; 00074 int trackNumSamples; 00075 ReaderStatusUpdate() { 00076 status = INVALID; 00077 chunk = NULL; 00078 } 00079 } ReaderStatusUpdate; 00080 00081 // CachingReader provides a layer on top of a SoundSource for reading samples 00082 // from a file. A cache is provided so that repeated reads to a certain section 00083 // of a song do not cause disk seeks or unnecessary SoundSource 00084 // calls. CachingReader provides a worker thread that can be used to prepare the 00085 // cache so that areas of a file that will soon be read are present in memory 00086 // once they are needed. This can be accomplished by issueing 'hints' to the 00087 // reader of areas of a SoundSource that will be read soon. 00088 class CachingReader : public EngineWorker { 00089 Q_OBJECT 00090 00091 public: 00092 // Construct a CachingReader with the given group. 00093 CachingReader(const char* _group, 00094 ConfigObject<ConfigValue>* _config); 00095 virtual ~CachingReader(); 00096 00097 void process(); 00098 00099 // Read num_samples from the SoundSource starting with sample into 00100 // buffer. Returns the total number of samples actually written to buffer. 00101 int read(int sample, int num_samples, CSAMPLE* buffer); 00102 00103 // Issue a list of hints, but check whether any of the hints request a chunk 00104 // that is not in the cache. If any hints do request a chunk not in cache, 00105 // then wake the reader so that it can process them. Must only be called 00106 // from the engine callback. 00107 void hintAndMaybeWake(QList<Hint>& hintList); 00108 00109 // Request that the CachingReader load a new track. These requests are 00110 // processed in the work thread, so the reader must be woken up via wake() 00111 // for this to take effect. 00112 void newTrack(TrackPointer pTrack); 00113 00114 // Wake the reader up so that it will process newTrack requests and hints. 00115 void wake(); 00116 00117 // Run upkeep operations like loading tracks and reading from file. Run by a 00118 // thread pool via the EngineWorkerScheduler. 00119 void run(); 00120 00121 // A Chunk is a memory-resident section of audio that has been cached. Each 00122 // chunk holds a fixed number of samples given by kSamplesPerChunk. 00123 const static int kChunkLength, kSamplesPerChunk; 00124 00125 signals: 00126 // Emitted once a new track is loaded and ready to be read from. 00127 void trackLoaded(TrackPointer pTrack, int iSampleRate, int iNumSamples); 00128 void trackLoadFailed(TrackPointer pTrack, QString reason); 00129 00130 private: 00131 // Removes a chunk from the LRU list 00132 static Chunk* removeFromLRUList(Chunk* chunk, Chunk* head); 00133 // Inserts a chunk into the LRU list 00134 static Chunk* insertIntoLRUList(Chunk* chunk, Chunk* head); 00135 00136 // Given a sample number, return the chunk number corresponding to it. 00137 inline static int chunkForSample(int sample_number) { 00138 return sample_number / kSamplesPerChunk; 00139 } 00140 00141 // Given a chunk number, return the start sample number for the chunk. 00142 inline static int sampleForChunk(int chunk_number) { 00143 return chunk_number * kSamplesPerChunk; 00144 } 00145 00146 // Initialize the reader by creating all the chunks from the RAM provided to 00147 // the CachingReader. 00148 void initialize(); 00149 00150 const char* m_pGroup; 00151 const ConfigObject<ConfigValue>* m_pConfig; 00152 00153 // Thread-safe FIFOs for communication between the engine callback and 00154 // reader thread. 00155 FIFO<ChunkReadRequest> m_chunkReadRequestFIFO; 00156 FIFO<ReaderStatusUpdate> m_readerStatusFIFO; 00157 00158 // Queue of Tracks to load, and the corresponding lock. Must acquire the 00159 // lock to touch. 00160 QMutex m_trackQueueMutex; 00161 QQueue<TrackWeakPointer> m_trackQueue; 00162 00164 // The following may /only/ be called within the engine callback 00166 00167 // Looks for the provided chunk number in the index of in-memory chunks and 00168 // returns it if it is present. If not, returns NULL. 00169 Chunk* lookupChunk(int chunk_number); 00170 00171 // Returns a Chunk to the free list 00172 void freeChunk(Chunk* pChunk); 00173 00174 // Returns all allocated chunks to the free list 00175 void freeAllChunks(); 00176 00177 // Gets a chunk from the free list. Returns NULL if none available. 00178 Chunk* allocateChunk(); 00179 00180 // Gets a chunk from the free list, frees the LRU Chunk if none available. 00181 Chunk* allocateChunkExpireLRU(); 00182 00183 ReaderStatus m_readerStatus; 00184 00185 // Keeps track of free Chunks we've allocated 00186 QVector<Chunk*> m_chunks; 00187 // List of free chunks available for use. 00188 QList<Chunk*> m_freeChunks; 00189 // List of reserved chunks with reads in progress 00190 QHash<int, Chunk*> m_chunksBeingRead; 00191 00192 // Keeps track of what Chunks we've allocated and indexes them based on what 00193 // chunk number they are allocated to. 00194 QHash<int, Chunk*> m_allocatedChunks; 00195 00196 // The linked list of recently-used chunks. 00197 Chunk* m_mruChunk; 00198 Chunk* m_lruChunk; 00199 00200 // The raw memory buffer which is divided up into chunks. 00201 CSAMPLE* m_pRawMemoryBuffer; 00202 00204 // The following may /only/ be called within the reader thread 00206 00207 // Internal method to load a track. Emits trackLoaded when finished. 00208 void loadTrack(TrackPointer pTrack); 00209 00210 // Read the given chunk_number from the file into pChunk's data 00211 // buffer. Fills length/sample information about Chunk* as well. 00212 void processChunkReadRequest(ChunkReadRequest* request, 00213 ReaderStatusUpdate* update); 00214 00215 // The current sound source of the track loaded 00216 Mixxx::SoundSource* m_pCurrentSoundSource; 00217 int m_iTrackSampleRate; 00218 int m_iTrackNumSamples; 00219 int m_iTrackNumSamplesCallbackSafe; 00220 00221 // Temporary buffer for reading from SoundSources 00222 SAMPLE* m_pSample; 00223 }; 00224 00225 00226 #endif /* CACHINGREADER_H */