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 "WaveClipUIUtilities.h"
17#include "WaveTrack.h"
18
19class WaveCache {
20public:
22 : dirty(-1)
23 , start(-1)
25 , rate(-1)
26 , where(0)
27 , min(0)
28 , max(0)
29 , rms(0)
30 {
31 }
32
33 WaveCache(size_t len_, double samplesPerPixel, double rate_, double t0, int dirty_)
34 : dirty(dirty_)
35 , len(len_)
36 , start(t0)
38 , rate(rate_)
39 , where(1 + len)
40 , min(len)
41 , max(len)
42 , rms(len)
43 {
44 }
45
47 {
48 }
49
50 int dirty;
51 const size_t len { 0 }; // counts pixels, not samples
52 const double start;
53 const double samplesPerPixel;
54 const int rate;
55 std::vector<sampleCount> where;
56 std::vector<float> min;
57 std::vector<float> max;
58 std::vector<float> rms;
59};
60
61//
62// Getting high-level data from the track for screen display and
63// clipping calculations
64//
65
67 const WaveChannelInterval &clip, WaveDisplay &display,
68 double t0, double pixelsPerSecond)
69{
70 auto &waveCache = mWaveCaches[clip.GetChannelIndex()];
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 std::vector<sampleCount> *pWhere;
85
86 if (allocated) {
87 // assume ownWhere is filled.
88 min = &display.min[0];
89 max = &display.max[0];
90 rms = &display.rms[0];
91 pWhere = &display.ownWhere;
92 }
93 else {
94 const auto sampleRate = clip.GetRate();
95 const auto stretchRatio = clip.GetStretchRatio();
96 const double samplesPerPixel =
97 sampleRate / pixelsPerSecond / stretchRatio;
98
99 // Make a tolerant comparison of the samples-per-pixel values in this wise:
100 // accumulated difference of times over the number of pixels is less than
101 // a sample period.
102 const bool samplesPerPixelMatch =
103 waveCache &&
104 (fabs(samplesPerPixel - waveCache->samplesPerPixel) * numPixels < 1.0);
105
106 const bool match = waveCache && samplesPerPixelMatch &&
107 waveCache->len > 0 && waveCache->dirty == mDirty;
108
109 if (match &&
110 waveCache->start == t0 &&
111 waveCache->len >= numPixels) {
112
113 // Satisfy the request completely from the cache
114 display.min = &waveCache->min[0];
115 display.max = &waveCache->max[0];
116 display.rms = &waveCache->rms[0];
117 display.where = &waveCache->where[0];
118 return true;
119 }
120
121 std::unique_ptr<WaveCache> oldCache(std::move(waveCache));
122
123 int oldX0 = 0;
124 double correction = 0.0;
125 size_t copyBegin = 0, copyEnd = 0;
126 if (match) {
128 oldCache->where, oldCache->len, numPixels, t0, sampleRate,
129 stretchRatio, samplesPerPixel, oldX0, correction);
130 // Remember our first pixel maps to oldX0 in the old cache,
131 // possibly out of bounds.
132 // For what range of pixels can data be copied?
133 copyBegin = std::min<size_t>(numPixels, std::max(0, -oldX0));
134 copyEnd = std::min<size_t>(numPixels, std::max(0,
135 (int)oldCache->len - oldX0
136 ));
137 }
138 if (!(copyEnd > copyBegin))
139 oldCache.reset(0);
140
141 waveCache = std::make_unique<WaveCache>(
142 numPixels, samplesPerPixel, sampleRate, t0, mDirty);
143 min = &waveCache->min[0];
144 max = &waveCache->max[0];
145 rms = &waveCache->rms[0];
146 pWhere = &waveCache->where;
147
148 constexpr auto addBias = false;
150 *pWhere, numPixels, addBias, correction, t0, sampleRate, stretchRatio,
151 samplesPerPixel);
152
153 // The range of pixels we must fetch from the Sequence:
154 p0 = (copyBegin > 0) ? 0 : copyEnd;
155 p1 = (copyEnd >= numPixels) ? copyBegin : numPixels;
156
157 // Optimization: if the old cache is good and overlaps
158 // with the current one, re-use as much of the cache as
159 // possible
160
161 if (oldCache) {
162
163 // Copy what we can from the old cache.
164 const int length = copyEnd - copyBegin;
165 const size_t sizeFloats = length * sizeof(float);
166 const int srcIdx = (int)copyBegin + oldX0;
167 memcpy(&min[copyBegin], &oldCache->min[srcIdx], sizeFloats);
168 memcpy(&max[copyBegin], &oldCache->max[srcIdx], sizeFloats);
169 memcpy(&rms[copyBegin], &oldCache->rms[srcIdx], sizeFloats);
170 }
171 }
172
173 if (p1 > p0) {
174 // Cache was not used or did not satisfy the whole request
175 std::vector<sampleCount> &where = *pWhere;
176
177 /* handle values in the append buffer */
178
179 const auto &sequence = clip.GetSequence();
180 auto numSamples = sequence.GetNumSamples();
181 auto a = p0;
182
183 // Not all of the required columns might be in the sequence.
184 // Some might be in the append buffer.
185 for (; a < p1; ++a) {
186 if (where[a + 1] > numSamples)
187 break;
188 }
189
190 // Handle the columns that land in the append buffer.
191 //compute the values that are outside the overlap from scratch.
192 if (a < p1) {
193 const auto appendBufferLen = clip.GetAppendBufferLen();
194 const auto &appendBuffer = clip.GetAppendBuffer();
195 sampleFormat seqFormat = sequence.GetSampleFormats().Stored();
196 bool didUpdate = false;
197 for(auto i = a; i < p1; i++) {
198 auto left = std::max(sampleCount{ 0 },
199 where[i] - numSamples);
200 auto right = std::min(sampleCount{ appendBufferLen },
201 where[i + 1] - numSamples);
202
203 //wxCriticalSectionLocker locker(mAppendCriticalSection);
204
205 if (right > left) {
206 Floats b;
207 const float *pb{};
208 // left is nonnegative and at most mAppendBufferLen:
209 auto sLeft = left.as_size_t();
210 // The difference is at most mAppendBufferLen:
211 size_t len = ( right - left ).as_size_t();
212
213 if (seqFormat == floatSample)
214 pb = &((const float *)appendBuffer)[sLeft];
215 else {
216 b.reinit(len);
217 pb = b.get();
219 appendBuffer + sLeft * SAMPLE_SIZE(seqFormat),
220 seqFormat, b.get(), len);
221 }
222
223 float theMax, theMin, sumsq;
224 {
225 const float val = pb[0];
226 theMax = theMin = val;
227 sumsq = val * val;
228 }
229 for(decltype(len) j = 1; j < len; j++) {
230 const float val = pb[j];
231 theMax = std::max(theMax, val);
232 theMin = std::min(theMin, val);
233 sumsq += val * val;
234 }
235
236 min[i] = theMin;
237 max[i] = theMax;
238 rms[i] = (float)sqrt(sumsq / len);
239
240 didUpdate=true;
241 }
242 }
243
244 // Shrink the right end of the range to fetch from Sequence
245 if(didUpdate)
246 p1 = a;
247 }
248
249 // Done with append buffer, now fetch the rest of the cache miss
250 // from the sequence
251 if (p1 > p0) {
252 if (!::GetWaveDisplay(sequence, &min[p0], &max[p0], &rms[p0], p1 - p0,
253 &where[p0]))
254 {
255 return false;
256 }
257 }
258 }
259
260 if (!allocated) {
261 // Now report the results
262 display.min = min;
263 display.max = max;
264 display.rms = rms;
265 display.where = &(*pWhere)[0];
266 }
267
268 return true;
269}
270
272 : mWaveCaches(nChannels)
273{
274 for (auto &pCache : mWaveCaches)
275 pCache = std::make_unique<WaveCache>();
276}
277
279{
280}
281
282std::unique_ptr<WaveClipListener> WaveClipWaveformCache::Clone() const
283{
284 // Don't need to copy contents
285 return std::make_unique<WaveClipWaveformCache>(mWaveCaches.size());
286}
287
288static WaveClip::Attachments::RegisteredFactory sKeyW{ [](WaveClip &clip) {
289 return std::make_unique<WaveClipWaveformCache>(clip.NChannels());
290} };
291
294{
295 return const_cast<WaveClip&>(clip.GetClip()) // Consider it mutable data
296 .Attachments::Get< WaveClipWaveformCache >( sKeyW );
297}
298
300{
301 ++mDirty;
302}
303
305{
306 // Invalidate wave display caches
307 for (auto &pCache : mWaveCaches)
308 pCache = std::make_unique<WaveCache>();
309}
310
312{
313 auto pOther = dynamic_cast<WaveClipWaveformCache *>(&other);
314 assert(pOther); // precondition
315 mWaveCaches.push_back(move(pOther->mWaveCaches[0]));
316}
317
319{
320 mWaveCaches.resize(2);
322}
323
325{
326 if (index < mWaveCaches.size())
327 mWaveCaches.erase(mWaveCaches.begin() + index);
328}
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:52
static WaveClip::Attachments::RegisteredFactory sKeyW
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:58
sampleCount GetNumSamples() const
Definition: Sequence.h:87
const double start
std::vector< float > max
const double samplesPerPixel
WaveCache(size_t len_, double samplesPerPixel, 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
WaveClip & GetClip()
Definition: WaveClip.h:96
size_t GetChannelIndex() const
Definition: WaveClip.h:99
constSamplePtr GetAppendBuffer() const
Definition: WaveClip.cpp:135
int GetRate() const override
Definition: WaveClip.cpp:193
double GetTrimLeft() const
Definition: WaveClip.cpp:110
const Sequence & GetSequence() const
Definition: WaveClip.cpp:127
double GetStretchRatio() const override
Definition: WaveClip.cpp:218
size_t GetAppendBufferLen() const
Definition: WaveClip.cpp:140
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:238
float * rms
Definition: WaveformCache.h:26
sampleCount * where
Definition: WaveformCache.h:25
std::vector< sampleCount > ownWhere
Definition: WaveformCache.h:28
float * min
Definition: WaveformCache.h:26
float * max
Definition: WaveformCache.h:26
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
AUDACITY_DLL_API void fillWhere(std::vector< sampleCount > &where, size_t len, bool addBias, double correction, double t0, double sampleRate, double stretchRatio, double samplesPerPixel)
AUDACITY_DLL_API void findCorrection(const std::vector< sampleCount > &oldWhere, size_t oldLen, size_t newLen, double t0, double sampleRate, double stretchRatio, double samplesPerPixel, int &oldX0, double &correction)
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:628
__finl float_x4 __vecc sqrt(const float_x4 &a)
bool GetWaveDisplay(const WaveChannelInterval &clip, WaveDisplay &display, double t0, double pixelsPerSecond)
void Erase(size_t index) override
void MarkChanged() noexcept override
~WaveClipWaveformCache() override
void MakeStereo(WaveClipListener &&other, bool aligned) override
void SwapChannels() override
Default implementation does nothing.
static WaveClipWaveformCache & Get(const WaveChannelInterval &clip)
std::unique_ptr< WaveClipListener > Clone() const override
void Invalidate() override
std::vector< std::unique_ptr< WaveCache > > mWaveCaches
Definition: WaveformCache.h:69
WaveClipWaveformCache(size_t nChannels)