diff options
author | RJ Ryan <rryan@mixxx.org> | 2016-04-22 20:27:07 -0700 |
---|---|---|
committer | RJ Ryan <rryan@mixxx.org> | 2016-04-22 20:27:07 -0700 |
commit | 3324fb27d4f7bc956edb50a173472072b3c70968 (patch) | |
tree | 8bd53f1e4605ac67aac25ed95fbe14e2f3f4c600 /src/engine/cachingreaderchunk.h | |
parent | 2e8e464a97df1cf2eea4a5fefa9d0e0668b67468 (diff) |
Move CachingReader and friends into src/engine.
Diffstat (limited to 'src/engine/cachingreaderchunk.h')
-rw-r--r-- | src/engine/cachingreaderchunk.h | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/src/engine/cachingreaderchunk.h b/src/engine/cachingreaderchunk.h new file mode 100644 index 0000000000..2eec40b736 --- /dev/null +++ b/src/engine/cachingreaderchunk.h @@ -0,0 +1,158 @@ +#ifndef ENGINE_CACHINGREADERCHUNK_H +#define ENGINE_CACHINGREADERCHUNK_H + +#include "sources/audiosource.h" + +// A Chunk is a memory-resident section of audio that has been cached. +// Each chunk holds a fixed number kFrames of frames with samples for +// kChannels. +// +// The class is not thread-safe although it is shared between CachingReader +// and CachingReaderWorker! A lock-free FIFO ensures that only a single +// thread has exclusive access on each chunk. This abstract base class +// is available for both the worker thread and the cache. +// +// This is the common (abstract) base class for both the cache (as the owner) +// and the worker. +class CachingReaderChunk { +public: + static const SINT kInvalidIndex; + static const SINT kChannels; + static const SINT kFrames; + static const SINT kSamples; + + // Returns the corresponding chunk index for a frame index + inline static SINT indexForFrame(SINT frameIndex) { + DEBUG_ASSERT(Mixxx::AudioSource::getMinFrameIndex() <= frameIndex); + const SINT chunkIndex = frameIndex / kFrames; + return chunkIndex; + } + + // Returns the corresponding chunk index for a frame index + inline static SINT frameForIndex(SINT chunkIndex) { + DEBUG_ASSERT(0 <= chunkIndex); + return chunkIndex * kFrames; + } + + // Converts frames to samples + inline static SINT frames2samples(SINT frames) { + return frames * kChannels; + } + // Converts samples to frames + inline static SINT samples2frames(SINT samples) { + DEBUG_ASSERT(0 == (samples % kChannels)); + return samples / kChannels; + } + + // Disable copy and move constructors + CachingReaderChunk(const CachingReaderChunk&) = delete; + CachingReaderChunk(CachingReaderChunk&&) = delete; + + SINT getIndex() const { + return m_index; + } + + bool isValid() const { + return 0 <= getIndex(); + } + + SINT getFrameCount() const { + return m_frameCount; + } + + // Check if the audio source has sample data available + // for this chunk. + bool isReadable( + const Mixxx::AudioSourcePointer& pAudioSource, + SINT maxReadableFrameIndex) const; + + // Read sample frames from the audio source and return the + // number of frames that have been read. The in/out parameter + // pMaxReadableFrameIndex is adjusted if reading fails. + SINT readSampleFrames( + const Mixxx::AudioSourcePointer& pAudioSource, + SINT* pMaxReadableFrameIndex); + + // Copy sampleCount samples starting at sampleOffset from + // the chunk's internal buffer into sampleBuffer. + void copySamples( + CSAMPLE* sampleBuffer, + SINT sampleOffset, + SINT sampleCount) const; + + // Copy sampleCount samples in reverse order starting at sampleOffset from + // the chunk's internal buffer into sampleBuffer. + void copySamplesReverse( + CSAMPLE* sampleBuffer, + SINT sampleOffset, + SINT sampleCount) const; + +protected: + explicit CachingReaderChunk(CSAMPLE* sampleBuffer); + virtual ~CachingReaderChunk(); + + void init(SINT index); + +private: + volatile SINT m_index; + + // The worker thread will fill the sample buffer and + // set the frame count. + CSAMPLE* const m_sampleBuffer; + volatile SINT m_frameCount; +}; + +// This derived class is only accessible for the cache as the owner, +// but not the worker thread. The state READ_PENDING indicates that +// the worker thread is in control. +class CachingReaderChunkForOwner: public CachingReaderChunk { +public: + explicit CachingReaderChunkForOwner(CSAMPLE* sampleBuffer); + virtual ~CachingReaderChunkForOwner(); + + void init(SINT index); + void free(); + + enum State { + FREE, + READY, + READ_PENDING + }; + + State getState() const { + return m_state; + } + + // The state is controlled by the cache as the owner of each chunk! + void giveToWorker() { + DEBUG_ASSERT(READY == m_state); + m_state = READ_PENDING; + } + void takeFromWorker() { + DEBUG_ASSERT(READ_PENDING == m_state); + m_state = READY; + } + + // Inserts a chunk into the double-linked list before the + // given chunk. If the list is currently empty simply pass + // pBefore = nullptr. Please note that if pBefore points to + // the head of the current list this chunk becomes the new + // head of the list. + void insertIntoListBefore( + CachingReaderChunkForOwner* pBefore); + // Removes a chunk from the double-linked list and optionally + // adjusts head/tail pointers. Pass ppHead/ppTail = nullptr if + // you prefer to adjust those pointers manually. + void removeFromList( + CachingReaderChunkForOwner** ppHead, + CachingReaderChunkForOwner** ppTail); + +private: + State m_state; + + CachingReaderChunkForOwner* m_pPrev; // previous item in double-linked list + CachingReaderChunkForOwner* m_pNext; // next item in double-linked list +}; + + +#endif // ENGINE_CACHINGREADERCHUNK_H |