Audacity 3.2.0
WaveformCache.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 @file WaveformCache.cpp
6
7 Paul Licameli split from WaveClip.cpp
8
9**********************************************************************/
10
11#include "WaveformCache.h"
12
13#include <cmath>
14#include "Sequence.h"
15#include "GetWaveDisplay.h"
16#include "WaveClipUtilities.h"
17
18class WaveCache {
19public:
21 : dirty(-1)
22 , start(-1)
23 , pps(0)
24 , rate(-1)
25 , where(0)
26 , min(0)
27 , max(0)
28 , rms(0)
29 , bl(0)
30 {
31 }
32
33 WaveCache(size_t len_, double pixelsPerSecond, double rate_, double t0, int dirty_)
34 : dirty(dirty_)
35 , len(len_)
36 , start(t0)
37 , pps(pixelsPerSecond)
38 , rate(rate_)
39 , where(1 + len)
40 , min(len)
41 , max(len)
42 , rms(len)
43 , bl(len)
44 {
45 }
46
48 {
49 }
50
51 int dirty;
52 const size_t len { 0 }; // counts pixels, not samples
53 const double start;
54 const double pps;
55 const int rate;
56 std::vector<sampleCount> where;
57 std::vector<float> min;
58 std::vector<float> max;
59 std::vector<float> rms;
60 std::vector<int> bl;
61};
62
63//
64// Getting high-level data from the track for screen display and
65// clipping calculations
66//
67
69 const WaveClip &clip, WaveDisplay &display, double t0,
70 double pixelsPerSecond )
71{
72 t0 += clip.GetTrimLeft();
73
74 const bool allocated = (display.where != 0);
75
76 const size_t numPixels = (int)display.width;
77
78 size_t p0 = 0; // least column requiring computation
79 size_t p1 = numPixels; // greatest column requiring computation, plus one
80
81 float *min;
82 float *max;
83 float *rms;
84 int *bl;
85 std::vector<sampleCount> *pWhere;
86
87 if (allocated) {
88 // assume ownWhere is filled.
89 min = &display.min[0];
90 max = &display.max[0];
91 rms = &display.rms[0];
92 bl = &display.bl[0];
93 pWhere = &display.ownWhere;
94 }
95 else {
96 const double tstep = 1.0 / pixelsPerSecond;
97 const auto rate = clip.GetRate();
98 const double samplesPerPixel = rate * tstep;
99
100 // Make a tolerant comparison of the pps values in this wise:
101 // accumulated difference of times over the number of pixels is less than
102 // a sample period.
103 const bool ppsMatch = mWaveCache &&
104 (fabs(tstep - 1.0 / mWaveCache->pps) * numPixels < (1.0 / rate));
105
106 const bool match =
107 mWaveCache &&
108 ppsMatch &&
109 mWaveCache->len > 0 &&
110 mWaveCache->dirty == mDirty;
111
112 if (match &&
113 mWaveCache->start == t0 &&
114 mWaveCache->len >= numPixels) {
115
116 // Satisfy the request completely from the cache
117 display.min = &mWaveCache->min[0];
118 display.max = &mWaveCache->max[0];
119 display.rms = &mWaveCache->rms[0];
120 display.bl = &mWaveCache->bl[0];
121 display.where = &mWaveCache->where[0];
122 return true;
123 }
124
125 std::unique_ptr<WaveCache> oldCache(std::move(mWaveCache));
126
127 int oldX0 = 0;
128 double correction = 0.0;
129 size_t copyBegin = 0, copyEnd = 0;
130 if (match) {
131 findCorrection(oldCache->where, oldCache->len, numPixels,
132 t0, rate, samplesPerPixel,
133 oldX0, correction);
134 // Remember our first pixel maps to oldX0 in the old cache,
135 // possibly out of bounds.
136 // For what range of pixels can data be copied?
137 copyBegin = std::min<size_t>(numPixels, std::max(0, -oldX0));
138 copyEnd = std::min<size_t>(numPixels, std::max(0,
139 (int)oldCache->len - oldX0
140 ));
141 }
142 if (!(copyEnd > copyBegin))
143 oldCache.reset(0);
144
145 mWaveCache = std::make_unique<WaveCache>(numPixels, pixelsPerSecond, rate, t0, mDirty);
146 min = &mWaveCache->min[0];
147 max = &mWaveCache->max[0];
148 rms = &mWaveCache->rms[0];
149 bl = &mWaveCache->bl[0];
150 pWhere = &mWaveCache->where;
151
152 fillWhere(*pWhere, numPixels, 0.0, correction,
153 t0, rate, samplesPerPixel);
154
155 // The range of pixels we must fetch from the Sequence:
156 p0 = (copyBegin > 0) ? 0 : copyEnd;
157 p1 = (copyEnd >= numPixels) ? copyBegin : numPixels;
158
159 // Optimization: if the old cache is good and overlaps
160 // with the current one, re-use as much of the cache as
161 // possible
162
163 if (oldCache) {
164
165 // Copy what we can from the old cache.
166 const int length = copyEnd - copyBegin;
167 const size_t sizeFloats = length * sizeof(float);
168 const int srcIdx = (int)copyBegin + oldX0;
169 memcpy(&min[copyBegin], &oldCache->min[srcIdx], sizeFloats);
170 memcpy(&max[copyBegin], &oldCache->max[srcIdx], sizeFloats);
171 memcpy(&rms[copyBegin], &oldCache->rms[srcIdx], sizeFloats);
172 memcpy(&bl[copyBegin], &oldCache->bl[srcIdx], length * sizeof(int));
173 }
174 }
175
176 if (p1 > p0) {
177 // Cache was not used or did not satisfy the whole request
178 std::vector<sampleCount> &where = *pWhere;
179
180 /* handle values in the append buffer */
181
182 const auto sequence = clip.GetSequence();
183 auto numSamples = sequence->GetNumSamples();
184 auto a = p0;
185
186 // Not all of the required columns might be in the sequence.
187 // Some might be in the append buffer.
188 for (; a < p1; ++a) {
189 if (where[a + 1] > numSamples)
190 break;
191 }
192
193 // Handle the columns that land in the append buffer.
194 //compute the values that are outside the overlap from scratch.
195 if (a < p1) {
196 const auto appendBufferLen = clip.GetAppendBufferLen();
197 const auto &appendBuffer = clip.GetAppendBuffer();
198 sampleFormat seqFormat = sequence->GetSampleFormats().Stored();
199 bool didUpdate = false;
200 for(auto i = a; i < p1; i++) {
201 auto left = std::max(sampleCount{ 0 },
202 where[i] - numSamples);
203 auto right = std::min(sampleCount{ appendBufferLen },
204 where[i + 1] - numSamples);
205
206 //wxCriticalSectionLocker locker(mAppendCriticalSection);
207
208 if (right > left) {
209 Floats b;
210 const float *pb{};
211 // left is nonnegative and at most mAppendBufferLen:
212 auto sLeft = left.as_size_t();
213 // The difference is at most mAppendBufferLen:
214 size_t len = ( right - left ).as_size_t();
215
216 if (seqFormat == floatSample)
217 pb = &((const float *)appendBuffer)[sLeft];
218 else {
219 b.reinit(len);
220 pb = b.get();
222 appendBuffer + sLeft * SAMPLE_SIZE(seqFormat),
223 seqFormat, b.get(), len);
224 }
225
226 float theMax, theMin, sumsq;
227 {
228 const float val = pb[0];
229 theMax = theMin = val;
230 sumsq = val * val;
231 }
232 for(decltype(len) j = 1; j < len; j++) {
233 const float val = pb[j];
234 theMax = std::max(theMax, val);
235 theMin = std::min(theMin, val);
236 sumsq += val * val;
237 }
238
239 min[i] = theMin;
240 max[i] = theMax;
241 rms[i] = (float)sqrt(sumsq / len);
242 bl[i] = 1; //for now just fake it.
243
244 didUpdate=true;
245 }
246 }
247
248 // Shrink the right end of the range to fetch from Sequence
249 if(didUpdate)
250 p1 = a;
251 }
252
253 // Done with append buffer, now fetch the rest of the cache miss
254 // from the sequence
255 if (p1 > p0) {
256 if (!::GetWaveDisplay(*sequence, &min[p0],
257 &max[p0],
258 &rms[p0],
259 &bl[p0],
260 p1-p0,
261 &where[p0]))
262 {
263 return false;
264 }
265 }
266 }
267
268 if (!allocated) {
269 // Now report the results
270 display.min = min;
271 display.max = max;
272 display.rms = rms;
273 display.bl = bl;
274 display.where = &(*pWhere)[0];
275 }
276
277 return true;
278}
279
281: mWaveCache{ std::make_unique<WaveCache>() }
282{
283}
284
286{
287}
288
289static WaveClip::Caches::RegisteredFactory sKeyW{ []( WaveClip& ){
290 return std::make_unique< WaveClipWaveformCache >();
291} };
292
294{
295 return const_cast< WaveClip& >( clip ) // Consider it mutable data
296 .Caches::Get< WaveClipWaveformCache >( sKeyW );
297}
298
300{
301 ++mDirty;
302}
303
305{
306 // Invalidate wave display cache
307 mWaveCache = std::make_unique<WaveCache>();
308}
int min(int a, int b)
void SamplesToFloats(constSamplePtr src, sampleFormat srcFormat, float *dst, size_t len, size_t srcStride, size_t dstStride)
Copy samples from any format into the widest format, which is 32 bit float, with no dithering.
sampleFormat
The ordering of these values with operator < agrees with the order of increasing bit width.
Definition: SampleFormat.h:30
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:50
void findCorrection(const std::vector< sampleCount > &oldWhere, size_t oldLen, size_t newLen, double t0, double rate, double samplesPerPixel, int &oldX0, double &correction)
void fillWhere(std::vector< sampleCount > &where, size_t len, double bias, double correction, double t0, double rate, double samplesPerPixel)
static WaveClip::Caches::RegisteredFactory sKeyW
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:57
sampleCount GetNumSamples() const
Definition: Sequence.h:91
std::vector< int > bl
const double start
std::vector< float > max
const double pps
WaveCache(size_t len_, double pixelsPerSecond, double rate_, double t0, int dirty_)
const int rate
std::vector< float > min
std::vector< float > rms
const size_t len
std::vector< sampleCount > where
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:101
constSamplePtr GetAppendBuffer() const
Definition: WaveClip.cpp:157
double GetTrimLeft() const noexcept
Returns the play start offset in seconds from the beginning of the underlying sequence.
Definition: WaveClip.cpp:922
Sequence * GetSequence()
Definition: WaveClip.h:227
int GetRate() const
Definition: WaveClip.h:140
size_t GetAppendBufferLen() const
Definition: WaveClip.cpp:152
int * bl
Definition: WaveClip.h:53
float * rms
Definition: WaveClip.h:52
sampleCount * where
Definition: WaveClip.h:51
std::vector< sampleCount > ownWhere
Definition: WaveClip.h:55
float * min
Definition: WaveClip.h:52
int width
Definition: WaveClip.h:50
float * max
Definition: WaveClip.h:52
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
STL namespace.
bool GetWaveDisplay(const WaveClip &clip, WaveDisplay &display, double t0, double pixelsPerSecond)
std::unique_ptr< WaveCache > mWaveCache
Definition: WaveformCache.h:24
~WaveClipWaveformCache() override
static WaveClipWaveformCache & Get(const WaveClip &clip)
void Invalidate() override
void MarkChanged() override