summaryrefslogtreecommitdiffstats
path: root/src/sources/readaheadframebuffer.h
blob: 0f5d3d6077f5c1f19fac574d1e97b001cf52a65f (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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#pragma once

#include <limits>

#include "sources/audiosource.h"
#include "util/readaheadsamplebuffer.h"

namespace mixxx {

typedef SINT FrameIndex;
typedef SINT FrameCount;

/// Keeps track of the current frame position (index) while decoding
/// an audio stream. Buffers samples (FIFO) ahead of this position
/// that have been decoded but not yet consumed.
class ReadAheadFrameBuffer final {
  public:
    static constexpr FrameIndex kInvalidFrameIndex = std::numeric_limits<FrameIndex>::min();
    static constexpr FrameIndex kUnknownFrameIndex = std::numeric_limits<FrameIndex>::max();

    static constexpr FrameCount kEmptyCapacity = 0;

    ReadAheadFrameBuffer();
    explicit ReadAheadFrameBuffer(
            const audio::SignalInfo& signalInfo,
            FrameCount initialCapacity = kEmptyCapacity);
    ReadAheadFrameBuffer(ReadAheadFrameBuffer&&) = default;
    ReadAheadFrameBuffer(const ReadAheadFrameBuffer&) = delete;
    ReadAheadFrameBuffer& operator=(ReadAheadFrameBuffer&&) = default;
    ReadAheadFrameBuffer& operator=(const ReadAheadFrameBuffer&) = delete;

    const audio::SignalInfo& signalInfo() const {
        return m_signalInfo;
    }

    FrameCount capacity() const {
        return m_signalInfo.samples2frames(
                m_sampleBuffer.capacity());
    }

    /// Invalidate the whole buffer and its position, e.g. to
    /// indicate decoding errors.
    void invalidate() {
        m_readIndex = kInvalidFrameIndex;
    }

    bool isValid() const {
        return m_readIndex != kInvalidFrameIndex;
    }

    /// Check that the first readable frame is at a valid position.
    bool isReady() const {
        return isValid() &&
                m_readIndex != kUnknownFrameIndex;
    }

    bool isEmpty() const {
        return m_sampleBuffer.empty();
    }

    FrameCount bufferedLength() const {
        DEBUG_ASSERT(isReady() || isEmpty());
        return m_signalInfo.samples2frames(
                m_sampleBuffer.readableLength());
    }

    IndexRange bufferedRange() const {
        return IndexRange::forward(
                readIndex(),
                bufferedLength());
    }

    /// Return the current position from where data
    /// could be read.
    FrameIndex readIndex() const {
        DEBUG_ASSERT(isValid());
        return m_readIndex;
    }

    /// Return the current position from where data
    /// will be written.
    FrameIndex writeIndex() const {
        return readIndex() + bufferedLength();
    }

    void reset(
            FrameIndex currentIndex = kUnknownFrameIndex);

    /// Try to reposition the buffer to a new read position
    /// within the buffered range, keeping all remaining data
    /// ahead of the read position.
    bool tryContinueReadingFrom(
            FrameIndex readIndex);

    enum class DiscontinuityOverlapMode {
        Ignore,
        Rewind,
        Default = Rewind, // recommended default
    };
    enum class DiscontinuityGapMode {
        Skip,
        FillWithSilence,
        Default = FillWithSilence, // recommended default
    };

    /// Drain as many buffered sample frames as possible and copy them
    /// into the output buffer.
    ///
    /// Sample data can only be consumed from the current read index
    /// or beyond.
    ///
    /// The output sample buffer may be null. In this case the consumed
    /// samples are dropped instead of copied.
    ///
    /// Returns the remaining portion that could not be filled from
    /// the buffer.
    WritableSampleFrames drainBuffer(
            const WritableSampleFrames& outputBuffer);

    /// Fills first the output buffer and then the internal buffer with
    /// the data from the input buffer. The whole input buffer is consumed.
    ///
    /// If the inputBuffer starts before the current position write
    /// position then the position is rewind back as far as possible.
    /// The parameter minOutputIndex indicates how far back the
    /// output buffer could be rewind, i.e. indicates the very first
    /// position before the current start position that would still
    /// be valid.
    ///
    /// All discontinuities in the output stream, i.e. both gaps and overlapping
    /// regions are unexpected and will trigger a debug assertion.
    ///
    /// The output sample buffer may be null. In this case the consumed
    /// samples are dropped instead of copied.
    ///
    /// Returns the remaining portion that could not be filled from
    /// the buffer.
    WritableSampleFrames consumeAndFillBuffer(ReadableSampleFrames inputBuffer,
            const WritableSampleFrames& outputBuffer,
            FrameIndex minOutputIndex,
            std::pair<DiscontinuityOverlapMode, DiscontinuityGapMode>
                    discontinuityModes = std::make_pair(
                            DiscontinuityOverlapMode::Default,
                            DiscontinuityGapMode::Default));

  private:
    void adjustCapacityBeforeBuffering(
            FrameCount frameCount);

    /// Buffer the given readable samples frames.
    ///
    /// The given sample data must start at after the last buffered
    /// frame. The buffering mode controls how a gap between the
    /// last buffered frame and the next input frame is handled.
    ///
    /// Returns the unread portion of the readable sample frames,
    /// which should typically be empty.
    ReadableSampleFrames fillBuffer(
            const ReadableSampleFrames& inputBuffer,
            DiscontinuityGapMode discontinuityGapMode);

    /// Advance the read position thereby discarding samples
    /// from the front of the FIFO buffer.
    FrameCount discardFirstBufferedFrames(
            FrameCount frameCount);

    /// Rewind the write position thereby discarding samples
    /// from the back of the FIFO buffer.
    FrameCount discardLastBufferedFrames(
            FrameCount frameCount);

    audio::SignalInfo m_signalInfo;
    ReadAheadSampleBuffer m_sampleBuffer;
    FrameIndex m_readIndex;
};

} // namespace mixxx