Audacity 3.2.0
SampleTrackCache.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5SampleTrackCache.cpp
6
7Paul Licameli split from WaveTrack.cpp
8
9**********************************************************************/
10
11#include "SampleTrackCache.h"
12#include "SampleTrack.h"
13
15{
16}
17
18void SampleTrackCache::SetTrack(const std::shared_ptr<const SampleTrack> &pTrack)
19{
20 if (mPTrack != pTrack) {
21 if (pTrack) {
22 mBufferSize = pTrack->GetMaxBlockSize();
23 if (!mPTrack ||
24 mPTrack->GetMaxBlockSize() != mBufferSize) {
25 Free();
28 }
29 }
30 else
31 Free();
32 mPTrack = pTrack;
34 }
35}
36
38 sampleCount start, size_t len, bool mayThrow)
39{
40 constexpr auto format = floatSample;
41 if (format == floatSample && len > 0) {
42 const auto end = start + len;
43
44 bool fillFirst = (mNValidBuffers < 1);
45 bool fillSecond = (mNValidBuffers < 2);
46
47 // Discard cached results that we no longer need
48 if (mNValidBuffers > 0 &&
49 (end <= mBuffers[0].start ||
50 start >= mBuffers[mNValidBuffers - 1].end())) {
51 // Complete miss
52 fillFirst = true;
53 fillSecond = true;
54 }
55 else if (mNValidBuffers == 2 &&
56 start >= mBuffers[1].start &&
57 end > mBuffers[1].end()) {
58 // Request starts in the second buffer and extends past it.
59 // Discard the first buffer.
60 // (But don't deallocate the buffer space.)
61 mBuffers[0] .swap ( mBuffers[1] );
62 fillSecond = true;
64 }
65 else if (mNValidBuffers > 0 &&
66 start < mBuffers[0].start &&
67 0 <= mPTrack->GetBlockStart(start)) {
68 // Request is not a total miss but starts before the cache,
69 // and there is a clip to fetch from.
70 // Not the access pattern for drawing spectrogram or playback,
71 // but maybe scrubbing causes this.
72 // Move the first buffer into second place, and later
73 // refill the first.
74 // (This case might be useful when marching backwards through
75 // the track, as with scrubbing.)
76 mBuffers[0] .swap ( mBuffers[1] );
77 fillFirst = true;
78 fillSecond = false;
79 // Cache is not in a consistent state yet
81 }
82
83 // Refill buffers as needed
84 if (fillFirst) {
85 const auto start0 = mPTrack->GetBlockStart(start);
86 if (start0 >= 0) {
87 const auto len0 = mPTrack->GetBestBlockSize(start0);
88 wxASSERT(len0 <= mBufferSize);
89 if (!mPTrack->GetFloats(
90 mBuffers[0].data.get(), start0, len0,
91 fillZero, mayThrow))
92 return nullptr;
93 mBuffers[0].start = start0;
94 mBuffers[0].len = len0;
95 if (!fillSecond &&
96 mBuffers[0].end() != mBuffers[1].start)
97 fillSecond = true;
98 // Keep the partially updated state consistent:
99 mNValidBuffers = fillSecond ? 1 : 2;
100 }
101 else {
102 // Request may fall between the clips of a track.
103 // Invalidate all. WaveTrack::Get() will return zeroes.
104 mNValidBuffers = 0;
105 fillSecond = false;
106 }
107 }
108 wxASSERT(!fillSecond || mNValidBuffers > 0);
109 if (fillSecond) {
110 mNValidBuffers = 1;
111 const auto end0 = mBuffers[0].end();
112 if (end > end0) {
113 const auto start1 = mPTrack->GetBlockStart(end0);
114 if (start1 == end0) {
115 const auto len1 = mPTrack->GetBestBlockSize(start1);
116 wxASSERT(len1 <= mBufferSize);
117 if (!mPTrack->GetFloats(mBuffers[1].data.get(), start1, len1, fillZero, mayThrow))
118 return nullptr;
119 mBuffers[1].start = start1;
120 mBuffers[1].len = len1;
121 mNValidBuffers = 2;
122 }
123 }
124 }
125 wxASSERT(mNValidBuffers < 2 || mBuffers[0].end() == mBuffers[1].start);
126
127 samplePtr buffer = nullptr; // will point into mOverlapBuffer
128 auto remaining = len;
129
130 // Possibly get an initial portion that is uncached
131
132 // This may be negative
133 const auto initLen =
134 mNValidBuffers < 1 ? sampleCount( len )
135 : std::min(sampleCount( len ), mBuffers[0].start - start);
136
137 if (initLen > 0) {
138 // This might be fetching zeroes between clips
140 // initLen is not more than len:
141 auto sinitLen = initLen.as_size_t();
142 if (!mPTrack->GetFloats(
143 // See comment below about casting
144 reinterpret_cast<float *>(mOverlapBuffer.ptr()),
145 start, sinitLen, fillZero, mayThrow))
146 return nullptr;
147 wxASSERT( sinitLen <= remaining );
148 remaining -= sinitLen;
149 start += initLen;
150 buffer = mOverlapBuffer.ptr() + sinitLen * SAMPLE_SIZE(format);
151 }
152
153 // Now satisfy the request from the buffers
154 for (int ii = 0; ii < mNValidBuffers && remaining > 0; ++ii) {
155 const auto starti = start - mBuffers[ii].start;
156 // Treatment of initLen above establishes this loop invariant,
157 // and statements below preserve it:
158 wxASSERT(starti >= 0);
159
160 // This may be negative
161 const auto leni =
162 std::min( sampleCount( remaining ), mBuffers[ii].len - starti );
163 if (initLen <= 0 && leni == len) {
164 // All is contiguous already. We can completely avoid copying
165 // leni is nonnegative, therefore start falls within mBuffers[ii],
166 // so starti is bounded between 0 and buffer length
167 return mBuffers[ii].data.get() + starti.as_size_t() ;
168 }
169 else if (leni > 0) {
170 // leni is nonnegative, therefore start falls within mBuffers[ii]
171 // But we can't satisfy all from one buffer, so copy
172 if (!buffer) {
174 buffer = mOverlapBuffer.ptr();
175 }
176 // leni is positive and not more than remaining
177 const size_t size = sizeof(float) * leni.as_size_t();
178 // starti is less than mBuffers[ii].len and nonnegative
179 memcpy(buffer, mBuffers[ii].data.get() + starti.as_size_t(), size);
180 wxASSERT( leni <= remaining );
181 remaining -= leni.as_size_t();
182 start += leni;
183 buffer += size;
184 }
185 }
186
187 if (remaining > 0) {
188 // Very big request!
189 // Fall back to direct fetch
190 if (!buffer) {
192 buffer = mOverlapBuffer.ptr();
193 }
194 // See comment below about casting
195 if (!mPTrack->GetFloats( reinterpret_cast<float*>(buffer),
196 start, remaining, fillZero, mayThrow))
197 return 0;
198 }
199
200 // Overlap buffer was meant for the more general support of sample formats
201 // besides float, which explains the cast
202 return reinterpret_cast<const float*>(mOverlapBuffer.ptr());
203 }
204 else {
205#if 0
206 // Cache works only for float format.
208 if (mPTrack->Get(mOverlapBuffer.ptr(), format, start, len, fillZero, mayThrow))
209 return mOverlapBuffer.ptr();
210#else
211 // No longer handling other than float format. Therefore len is 0.
212#endif
213 return nullptr;
214 }
215}
216
218{
219 mBuffers[0].Free();
220 mBuffers[1].Free();
222 mNValidBuffers = 0;
223}
int min(int a, int b)
int format
Definition: ExportPCM.cpp:56
@ floatSample
Definition: SampleFormat.h:34
char * samplePtr
Definition: SampleFormat.h:49
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:44
@ fillZero
Definition: SampleFormat.h:54
GrowableSampleBuffer & Resize(size_t count, sampleFormat format)
Definition: SampleFormat.h:130
samplePtr ptr() const
Definition: SampleFormat.h:110
GrowableSampleBuffer mOverlapBuffer
const float * GetFloats(sampleCount start, size_t len, bool mayThrow)
Retrieve samples as floats from the track or from the memory cache.
void SetTrack(const std::shared_ptr< const SampleTrack > &pTrack)
std::shared_ptr< const SampleTrack > mPTrack
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
void swap(Buffer &other)
sampleCount end() const