summaryrefslogtreecommitdiffstats
path: root/src/engine/cachingreaderchunk.h
blob: 463e7366665d4dd7318b52f67cefa1a9138b654e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#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 mixxx::AudioSignal::ChannelCount kChannels;
    static const SINT kFrames;
    static const SINT kSamples;

    // 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;
    }

    // Returns the corresponding chunk index for a frame index
    inline static SINT indexForFrame(
            /*const mixxx::AudioSourcePointer& pAudioSource,*/
            SINT frameIndex) {
        //DEBUG_ASSERT(pAudioSource->frameIndexRange().contains(frameIndex));
        const SINT frameIndexOffset = frameIndex /*- pAudioSource->frameIndexMin()*/;
        return frameIndexOffset / kFrames;
    }

    // Disable copy and move constructors
    CachingReaderChunk(const CachingReaderChunk&) = delete;
    CachingReaderChunk(CachingReaderChunk&&) = delete;

    SINT getIndex() const {
        return m_index;
    }

    // Frame index range of this chunk for the given audio source.
    mixxx::IndexRange frameIndexRange(
            const mixxx::AudioSourcePointer& pAudioSource) const;

    // Read sample frames from the audio source and return the
    // range of frames that have been read.
    mixxx::IndexRange bufferSampleFrames(
            const mixxx::AudioSourcePointer& pAudioSource,
            mixxx::SampleBuffer::WritableSlice tempOutputBuffer);

    mixxx::IndexRange readBufferedSampleFrames(
            CSAMPLE* sampleBuffer,
            const mixxx::IndexRange& frameIndexRange) const;
    mixxx::IndexRange readBufferedSampleFramesReverse(
            CSAMPLE* reverseSampleBuffer,
            const mixxx::IndexRange& frameIndexRange) const;

protected:
    explicit CachingReaderChunk(
            mixxx::SampleBuffer::WritableSlice sampleBuffer);
    virtual ~CachingReaderChunk();

    void init(SINT index);

private:
    SINT frameIndexOffset() const {
        return m_index * kFrames;
    }

    SINT m_index;

    // The worker thread will fill the sample buffer and
    // set the corresponding frame index range.
    mixxx::SampleBuffer::WritableSlice m_sampleBuffer;
    mixxx::ReadableSampleFrames m_bufferedSampleFrames;
};

// 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(
            mixxx::SampleBuffer::WritableSlice sampleBuffer);
    ~CachingReaderChunkForOwner() override;

    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