Audacity 3.2.0
GetWaveDisplay.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 @file GetWaveDisplay.cpp
6
7 Paul Licameli split from Sequence.cpp
8
9**********************************************************************/
10
11#include "GetWaveDisplay.h"
12
13#include <algorithm>
14#include <cmath>
15#include <float.h>
16#include <wx/debug.h>
17#include "SampleBlock.h"
18#include "SampleCount.h"
19#include "Sequence.h"
20
21namespace {
22
24{
25 MinMaxSumsq(const float *pv, int count, int divisor)
26 {
27 min = FLT_MAX, max = -FLT_MAX, sumsq = 0.0f;
28 while (count--) {
29 float v;
30 switch (divisor) {
31 default:
32 case 1:
33 // array holds samples
34 v = *pv++;
35 if (v < min)
36 min = v;
37 if (v > max)
38 max = v;
39 sumsq += v * v;
40 break;
41 case 256:
42 case 65536:
43 // array holds triples of min, max, and rms values
44 v = *pv++;
45 if (v < min)
46 min = v;
47 v = *pv++;
48 if (v > max)
49 max = v;
50 v = *pv++;
51 sumsq += v * v;
52 break;
53 }
54 }
55 }
56
57 float min;
58 float max;
59 float sumsq;
60};
61
62}
63
64bool GetWaveDisplay(const Sequence &sequence,
65 float *min, float *max, float *rms, int* bl,
66 size_t len, const sampleCount *where)
67{
68 wxASSERT(len > 0);
69 const auto s0 = std::max(sampleCount(0), where[0]);
70 const auto numSamples = sequence.GetNumSamples();
71 if (s0 >= numSamples)
72 // None of the samples asked for are in range. Abandon.
73 return false;
74
75 // In case where[len - 1] == where[len], raise the limit by one,
76 // so we load at least one pixel for column len - 1
77 // ... unless the mNumSamples ceiling applies, and then there are other defenses
78 const auto s1 = std::clamp(where[len], 1 + where[len - 1], numSamples);
79 const auto maxSamples = sequence.GetMaxBlockSize();
80 Floats temp{ maxSamples };
81
82 decltype(len) pixel = 0;
83
84 auto srcX = s0;
85 decltype(srcX) nextSrcX = 0;
86 int lastRmsDenom = 0;
87 int lastDivisor = 0;
88 auto whereNow = std::min(s1 - 1, where[0]);
89 decltype(whereNow) whereNext = 0;
90 // Loop over block files, opening and reading and closing each
91 // not more than once
92 const auto &blocks = sequence.GetBlockArray();
93 unsigned nBlocks = blocks.size();
94 const unsigned int block0 = sequence.FindBlock(s0);
95 for (unsigned int b = block0; b < nBlocks; ++b) {
96 if (b > block0)
97 srcX = nextSrcX;
98 if (srcX >= s1)
99 break;
100
101 // Find the range of sample values for this block that
102 // are in the display.
103 const SeqBlock &seqBlock = blocks[b];
104 const auto start = seqBlock.start;
105 nextSrcX = std::min(s1, start + seqBlock.sb->GetSampleCount());
106
107 // The column for pixel p covers samples from
108 // where[p] up to but excluding where[p + 1].
109
110 // Find the range of pixels covered by the current block file
111 // (Their starting samples covered by it, to be exact)
112 decltype(len) nextPixel;
113 if (nextSrcX >= s1)
114 // last pass
115 nextPixel = len;
116 else {
117 nextPixel = pixel;
118 // Taking min with s1 - 1, here and elsewhere, is another defense
119 // to be sure the last pixel column gets at least one sample
120 while (nextPixel < len &&
121 (whereNext = std::min(s1 - 1, where[nextPixel])) < nextSrcX)
122 ++nextPixel;
123 }
124 if (nextPixel == pixel)
125 // The entire block's samples fall within one pixel column.
126 // Either it's a rare odd block at the end, or else,
127 // we must be really zoomed out!
128 // Omit the entire block's contents from min/max/rms
129 // calculation, which is not correct, but correctness might not
130 // be worth the compute time if this happens every pixel
131 // column. -- PRL
132 continue;
133 if (nextPixel == len)
134 whereNext = s1;
135
136 // Decide the summary level
137 const double samplesPerPixel =
138 (whereNext - whereNow).as_double() / (nextPixel - pixel);
139 const int divisor =
140 (samplesPerPixel >= 65536) ? 65536
141 : (samplesPerPixel >= 256) ? 256
142 : 1;
143
144 int blockStatus = b;
145
146 // How many samples or triples are needed?
147
148 const size_t startPosition =
149 // srcX and start are in the same block
150 std::max(sampleCount(0), (srcX - start) / divisor).as_size_t();
151 const size_t inclusiveEndPosition =
152 // nextSrcX - 1 and start are in the same block
153 std::min((sampleCount(maxSamples) / divisor) - 1,
154 (nextSrcX - 1 - start) / divisor).as_size_t();
155 const auto num = 1 + inclusiveEndPosition - startPosition;
156 if (num <= 0) {
157 // What? There was a zero length block file?
158 wxASSERT(false);
159 // Do some defense against this case anyway
160 while (pixel < nextPixel) {
161 min[pixel] = max[pixel] = rms[pixel] = 0;
162 bl[pixel] = blockStatus;//MC
163 ++pixel;
164 }
165 continue;
166 }
167
168 // Read from the block file or its summary
169 switch (divisor) {
170 default:
171 case 1:
172 // Read samples
173 // no-throw for display operations!
174 sequence.Read(
175 (samplePtr)temp.get(), floatSample, seqBlock, startPosition, num, false);
176 break;
177 case 256:
178 // Read triples
179 // Ignore the return value.
180 // This function fills with zeroes if read fails
181 seqBlock.sb->GetSummary256(temp.get(), startPosition, num);
182 break;
183 case 65536:
184 // Read triples
185 // Ignore the return value.
186 // This function fills with zeroes if read fails
187 seqBlock.sb->GetSummary64k(temp.get(), startPosition, num);
188 break;
189 }
190
191 auto filePosition = startPosition;
192
193 // The previous pixel column might straddle blocks.
194 // If so, impute some of the data to it.
195 if (b > block0 && pixel > 0) {
196 // whereNow and start are in the same block
197 auto midPosition = ((whereNow - start) / divisor).as_size_t();
198 int diff(midPosition - filePosition);
199 if (diff > 0) {
200 MinMaxSumsq values(temp.get(), diff, divisor);
201 const int lastPixel = pixel - 1;
202 float &lastMin = min[lastPixel];
203 lastMin = std::min(lastMin, values.min);
204 float &lastMax = max[lastPixel];
205 lastMax = std::max(lastMax, values.max);
206 float &lastRms = rms[lastPixel];
207 int lastNumSamples = lastRmsDenom * lastDivisor;
208 lastRms = sqrt(
209 (lastRms * lastRms * lastNumSamples + values.sumsq * divisor) /
210 (lastNumSamples + diff * divisor)
211 );
212
213 filePosition = midPosition;
214 }
215 }
216
217 // Loop over file positions
218 int rmsDenom = 0;
219 for (; filePosition <= inclusiveEndPosition;) {
220 // Find range of pixel columns for this file position
221 // (normally just one, but maybe more when zoomed very close)
222 // and the range of positions for those columns
223 // (normally one or more, for that one column)
224 auto pixelX = pixel + 1;
225 decltype(filePosition) positionX = 0;
226 while (pixelX < nextPixel &&
227 filePosition ==
228 (positionX = (
229 // s1 - 1 or where[pixelX] and start are in the same block
230 (std::min(s1 - 1, where[pixelX]) - start) / divisor).as_size_t() )
231 )
232 ++pixelX;
233 if (pixelX >= nextPixel)
234 positionX = 1 + inclusiveEndPosition;
235
236 // Find results to assign
237 rmsDenom = (positionX - filePosition);
238 wxASSERT(rmsDenom > 0);
239 const float *const pv =
240 temp.get() + (filePosition - startPosition) * (divisor == 1 ? 1 : 3);
241 MinMaxSumsq values(pv, std::max(0, rmsDenom), divisor);
242
243 // Assign results
244 std::fill(&min[pixel], &min[pixelX], values.min);
245 std::fill(&max[pixel], &max[pixelX], values.max);
246 std::fill(&bl[pixel], &bl[pixelX], blockStatus);
247 std::fill(&rms[pixel], &rms[pixelX], (float)sqrt(values.sumsq / rmsDenom));
248
249 pixel = pixelX;
250 filePosition = positionX;
251 }
252
253 wxASSERT(pixel == nextPixel);
254 whereNow = whereNext;
255 pixel = nextPixel;
256 lastDivisor = divisor;
257 lastRmsDenom = rmsDenom;
258 } // for each block file
259
260 wxASSERT(pixel == len);
261
262 return true;
263}
int min(int a, int b)
const wxChar * values
bool GetWaveDisplay(const Sequence &sequence, float *min, float *max, float *rms, int *bl, size_t len, const sampleCount *where)
@ floatSample
Definition: SampleFormat.h:34
char * samplePtr
Definition: SampleFormat.h:49
Data structure containing pointer to a sample block and a start time. Element of a BlockArray.
Definition: Sequence.h:28
sampleCount start
the sample in the global wavetrack that this block starts at.
Definition: Sequence.h:33
SampleBlockPtr sb
Definition: Sequence.h:31
A WaveTrack contains WaveClip(s). A WaveClip contains a Sequence. A Sequence is primarily an interfac...
Definition: Sequence.h:61
size_t GetMaxBlockSize() const
Definition: Sequence.cpp:77
sampleCount GetNumSamples() const
Definition: Sequence.h:91
static bool Read(samplePtr buffer, sampleFormat format, const SeqBlock &b, size_t blockRelativeStart, size_t len, bool mayThrow)
Definition: Sequence.cpp:1041
int FindBlock(sampleCount pos) const
Definition: Sequence.cpp:991
BlockArray & GetBlockArray()
Definition: Sequence.h:176
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
MinMaxSumsq(const float *pv, int count, int divisor)