Chris@148
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@148
|
2
|
Chris@148
|
3 /*
|
Chris@148
|
4 Sonic Visualiser
|
Chris@148
|
5 An audio file viewer and annotation editor.
|
Chris@148
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@202
|
7 This file copyright 2006 Chris Cannam and QMUL.
|
Chris@148
|
8
|
Chris@148
|
9 This program is free software; you can redistribute it and/or
|
Chris@148
|
10 modify it under the terms of the GNU General Public License as
|
Chris@148
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@148
|
12 License, or (at your option) any later version. See the file
|
Chris@148
|
13 COPYING included with this distribution for more information.
|
Chris@148
|
14 */
|
Chris@148
|
15
|
Chris@148
|
16 #include "FFTFileCache.h"
|
Chris@148
|
17
|
Chris@150
|
18 #include "fileio/MatrixFile.h"
|
Chris@148
|
19
|
Chris@148
|
20 #include "base/Profiler.h"
|
Chris@408
|
21 #include "base/Thread.h"
|
Chris@455
|
22 #include "base/Exceptions.h"
|
Chris@148
|
23
|
Chris@148
|
24 #include <iostream>
|
Chris@148
|
25
|
Chris@374
|
26
|
Chris@148
|
27 // The underlying matrix has height (m_height * 2 + 1). In each
|
Chris@148
|
28 // column we store magnitude at [0], [2] etc and phase at [1], [3]
|
Chris@148
|
29 // etc, and then store the normalization factor (maximum magnitude) at
|
Chris@266
|
30 // [m_height * 2]. In compact mode, the factor takes two cells.
|
Chris@148
|
31
|
Chris@148
|
32 FFTFileCache::FFTFileCache(QString fileBase, MatrixFile::Mode mode,
|
Chris@148
|
33 StorageType storageType) :
|
Chris@148
|
34 m_writebuf(0),
|
Chris@148
|
35 m_readbuf(0),
|
Chris@148
|
36 m_readbufCol(0),
|
Chris@148
|
37 m_readbufWidth(0),
|
Chris@148
|
38 m_mfc(new MatrixFile
|
Chris@148
|
39 (fileBase, mode,
|
Chris@148
|
40 storageType == Compact ? sizeof(uint16_t) : sizeof(float),
|
Chris@148
|
41 mode == MatrixFile::ReadOnly)),
|
Chris@266
|
42 m_storageType(storageType),
|
Chris@266
|
43 m_factorSize(storageType == Compact ? 2 : 1)
|
Chris@148
|
44 {
|
Chris@259
|
45 // std::cerr << "FFTFileCache: storage type is " << (storageType == Compact ? "Compact" : storageType == Polar ? "Polar" : "Rectangular") << std::endl;
|
Chris@148
|
46 }
|
Chris@148
|
47
|
Chris@148
|
48 FFTFileCache::~FFTFileCache()
|
Chris@148
|
49 {
|
Chris@148
|
50 if (m_readbuf) delete[] m_readbuf;
|
Chris@148
|
51 if (m_writebuf) delete[] m_writebuf;
|
Chris@148
|
52 delete m_mfc;
|
Chris@148
|
53 }
|
Chris@148
|
54
|
Chris@148
|
55 size_t
|
Chris@148
|
56 FFTFileCache::getWidth() const
|
Chris@148
|
57 {
|
Chris@148
|
58 return m_mfc->getWidth();
|
Chris@148
|
59 }
|
Chris@148
|
60
|
Chris@148
|
61 size_t
|
Chris@148
|
62 FFTFileCache::getHeight() const
|
Chris@148
|
63 {
|
Chris@148
|
64 size_t mh = m_mfc->getHeight();
|
Chris@266
|
65 if (mh > m_factorSize) return (mh - m_factorSize) / 2;
|
Chris@148
|
66 else return 0;
|
Chris@148
|
67 }
|
Chris@148
|
68
|
Chris@148
|
69 void
|
Chris@148
|
70 FFTFileCache::resize(size_t width, size_t height)
|
Chris@148
|
71 {
|
Chris@408
|
72 MutexLocker locker(&m_writeMutex, "FFTFileCache::resize::m_writeMutex");
|
Chris@148
|
73
|
Chris@266
|
74 m_mfc->resize(width, height * 2 + m_factorSize);
|
Chris@458
|
75
|
Chris@458
|
76 {
|
Chris@458
|
77 MutexLocker locker(&m_readbufMutex, "FFTFileCache::resize::m_readMutex");
|
Chris@458
|
78 if (m_readbuf) {
|
Chris@458
|
79 delete[] m_readbuf;
|
Chris@458
|
80 m_readbuf = 0;
|
Chris@458
|
81 }
|
Chris@148
|
82 }
|
Chris@458
|
83
|
Chris@148
|
84 if (m_writebuf) {
|
Chris@148
|
85 delete[] m_writebuf;
|
Chris@148
|
86 }
|
Chris@266
|
87 m_writebuf = new char[(height * 2 + m_factorSize) * m_mfc->getCellSize()];
|
Chris@148
|
88 }
|
Chris@458
|
89
|
Chris@148
|
90 void
|
Chris@148
|
91 FFTFileCache::reset()
|
Chris@148
|
92 {
|
Chris@148
|
93 m_mfc->reset();
|
Chris@148
|
94 }
|
Chris@148
|
95
|
Chris@148
|
96 float
|
Chris@148
|
97 FFTFileCache::getMagnitudeAt(size_t x, size_t y) const
|
Chris@148
|
98 {
|
Chris@183
|
99 Profiler profiler("FFTFileCache::getMagnitudeAt", false);
|
Chris@183
|
100
|
Chris@148
|
101 float value = 0.f;
|
Chris@148
|
102
|
Chris@148
|
103 switch (m_storageType) {
|
Chris@148
|
104
|
Chris@148
|
105 case Compact:
|
Chris@509
|
106 value = (getFromReadBufCompactUnsigned(x, y * 2, true) / 65535.0)
|
Chris@509
|
107 * getNormalizationFactor(x, true);
|
Chris@148
|
108 break;
|
Chris@148
|
109
|
Chris@148
|
110 case Rectangular:
|
Chris@148
|
111 {
|
Chris@148
|
112 float real, imag;
|
Chris@148
|
113 getValuesAt(x, y, real, imag);
|
Chris@148
|
114 value = sqrtf(real * real + imag * imag);
|
Chris@148
|
115 break;
|
Chris@148
|
116 }
|
Chris@148
|
117
|
Chris@148
|
118 case Polar:
|
Chris@509
|
119 value = getFromReadBufStandard(x, y * 2, true);
|
Chris@148
|
120 break;
|
Chris@148
|
121 }
|
Chris@148
|
122
|
Chris@148
|
123 return value;
|
Chris@148
|
124 }
|
Chris@148
|
125
|
Chris@148
|
126 float
|
Chris@148
|
127 FFTFileCache::getNormalizedMagnitudeAt(size_t x, size_t y) const
|
Chris@148
|
128 {
|
Chris@148
|
129 float value = 0.f;
|
Chris@148
|
130
|
Chris@148
|
131 switch (m_storageType) {
|
Chris@148
|
132
|
Chris@148
|
133 case Compact:
|
Chris@509
|
134 value = getFromReadBufCompactUnsigned(x, y * 2, true) / 65535.0;
|
Chris@148
|
135 break;
|
Chris@148
|
136
|
Chris@148
|
137 default:
|
Chris@148
|
138 {
|
Chris@148
|
139 float mag = getMagnitudeAt(x, y);
|
Chris@509
|
140 float factor = getNormalizationFactor(x, true);
|
Chris@148
|
141 if (factor != 0) value = mag / factor;
|
Chris@148
|
142 else value = 0.f;
|
Chris@148
|
143 break;
|
Chris@148
|
144 }
|
Chris@148
|
145 }
|
Chris@148
|
146
|
Chris@148
|
147 return value;
|
Chris@148
|
148 }
|
Chris@148
|
149
|
Chris@148
|
150 float
|
Chris@148
|
151 FFTFileCache::getMaximumMagnitudeAt(size_t x) const
|
Chris@148
|
152 {
|
Chris@509
|
153 return getNormalizationFactor(x, true);
|
Chris@148
|
154 }
|
Chris@148
|
155
|
Chris@148
|
156 float
|
Chris@148
|
157 FFTFileCache::getPhaseAt(size_t x, size_t y) const
|
Chris@148
|
158 {
|
Chris@148
|
159 float value = 0.f;
|
Chris@148
|
160
|
Chris@148
|
161 switch (m_storageType) {
|
Chris@148
|
162
|
Chris@148
|
163 case Compact:
|
Chris@509
|
164 value = (getFromReadBufCompactSigned(x, y * 2 + 1, true) / 32767.0) * M_PI;
|
Chris@148
|
165 break;
|
Chris@148
|
166
|
Chris@148
|
167 case Rectangular:
|
Chris@148
|
168 {
|
Chris@148
|
169 float real, imag;
|
Chris@148
|
170 getValuesAt(x, y, real, imag);
|
Chris@408
|
171 value = atan2f(imag, real);
|
Chris@148
|
172 break;
|
Chris@148
|
173 }
|
Chris@148
|
174
|
Chris@148
|
175 case Polar:
|
Chris@509
|
176 value = getFromReadBufStandard(x, y * 2 + 1, true);
|
Chris@148
|
177 break;
|
Chris@148
|
178 }
|
Chris@148
|
179
|
Chris@148
|
180 return value;
|
Chris@148
|
181 }
|
Chris@148
|
182
|
Chris@148
|
183 void
|
Chris@148
|
184 FFTFileCache::getValuesAt(size_t x, size_t y, float &real, float &imag) const
|
Chris@148
|
185 {
|
Chris@148
|
186 switch (m_storageType) {
|
Chris@148
|
187
|
Chris@148
|
188 case Rectangular:
|
Chris@509
|
189 m_readbufMutex.lock();
|
Chris@509
|
190 real = getFromReadBufStandard(x, y * 2, false);
|
Chris@509
|
191 imag = getFromReadBufStandard(x, y * 2 + 1, false);
|
Chris@509
|
192 m_readbufMutex.unlock();
|
Chris@148
|
193 return;
|
Chris@148
|
194
|
Chris@148
|
195 default:
|
Chris@148
|
196 float mag = getMagnitudeAt(x, y);
|
Chris@148
|
197 float phase = getPhaseAt(x, y);
|
Chris@148
|
198 real = mag * cosf(phase);
|
Chris@148
|
199 imag = mag * sinf(phase);
|
Chris@148
|
200 return;
|
Chris@148
|
201 }
|
Chris@148
|
202 }
|
Chris@148
|
203
|
Chris@509
|
204 void
|
Chris@509
|
205 FFTFileCache::getMagnitudesAt(size_t x, float *values, size_t minbin, size_t count, size_t step) const
|
Chris@509
|
206 {
|
Chris@509
|
207 Profiler profiler("FFTFileCache::getMagnitudesAt");
|
Chris@509
|
208
|
Chris@509
|
209 m_readbufMutex.lock();
|
Chris@509
|
210
|
Chris@509
|
211 switch (m_storageType) {
|
Chris@509
|
212
|
Chris@509
|
213 case Compact:
|
Chris@509
|
214 for (size_t i = 0; i < count; ++i) {
|
Chris@509
|
215 size_t y = minbin + i * step;
|
Chris@509
|
216 values[i] = (getFromReadBufCompactUnsigned(x, y * 2, false) / 65535.0)
|
Chris@509
|
217 * getNormalizationFactor(x, false);
|
Chris@509
|
218 }
|
Chris@509
|
219 break;
|
Chris@509
|
220
|
Chris@509
|
221 case Rectangular:
|
Chris@509
|
222 {
|
Chris@509
|
223 float real, imag;
|
Chris@509
|
224 for (size_t i = 0; i < count; ++i) {
|
Chris@509
|
225 size_t y = minbin + i * step;
|
Chris@509
|
226 real = getFromReadBufStandard(x, y * 2, false);
|
Chris@509
|
227 imag = getFromReadBufStandard(x, y * 2 + 1, false);
|
Chris@509
|
228 values[i] = sqrtf(real * real + imag * imag);
|
Chris@509
|
229 }
|
Chris@509
|
230 break;
|
Chris@509
|
231 }
|
Chris@509
|
232
|
Chris@509
|
233 case Polar:
|
Chris@509
|
234 for (size_t i = 0; i < count; ++i) {
|
Chris@509
|
235 size_t y = minbin + i * step;
|
Chris@509
|
236 values[i] = getFromReadBufStandard(x, y * 2, false);
|
Chris@509
|
237 }
|
Chris@509
|
238 break;
|
Chris@509
|
239 }
|
Chris@509
|
240
|
Chris@509
|
241 m_readbufMutex.unlock();
|
Chris@509
|
242 }
|
Chris@509
|
243
|
Chris@148
|
244 bool
|
Chris@148
|
245 FFTFileCache::haveSetColumnAt(size_t x) const
|
Chris@148
|
246 {
|
Chris@148
|
247 return m_mfc->haveSetColumnAt(x);
|
Chris@148
|
248 }
|
Chris@148
|
249
|
Chris@148
|
250 void
|
Chris@148
|
251 FFTFileCache::setColumnAt(size_t x, float *mags, float *phases, float factor)
|
Chris@148
|
252 {
|
Chris@408
|
253 MutexLocker locker(&m_writeMutex, "FFTFileCache::setColumnAt::m_writeMutex");
|
Chris@148
|
254
|
Chris@148
|
255 size_t h = getHeight();
|
Chris@148
|
256
|
Chris@148
|
257 switch (m_storageType) {
|
Chris@148
|
258
|
Chris@148
|
259 case Compact:
|
Chris@148
|
260 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
261 ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mags[y] / factor) * 65535.0);
|
Chris@148
|
262 ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phases[y] * 32767) / M_PI));
|
Chris@148
|
263 }
|
Chris@148
|
264 break;
|
Chris@148
|
265
|
Chris@148
|
266 case Rectangular:
|
Chris@148
|
267 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
268 ((float *)m_writebuf)[y * 2] = mags[y] * cosf(phases[y]);
|
Chris@148
|
269 ((float *)m_writebuf)[y * 2 + 1] = mags[y] * sinf(phases[y]);
|
Chris@148
|
270 }
|
Chris@148
|
271 break;
|
Chris@148
|
272
|
Chris@148
|
273 case Polar:
|
Chris@148
|
274 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
275 ((float *)m_writebuf)[y * 2] = mags[y];
|
Chris@148
|
276 ((float *)m_writebuf)[y * 2 + 1] = phases[y];
|
Chris@148
|
277 }
|
Chris@148
|
278 break;
|
Chris@148
|
279 }
|
Chris@148
|
280
|
Chris@266
|
281 // static float maxFactor = 0;
|
Chris@266
|
282 // if (factor > maxFactor) maxFactor = factor;
|
Chris@266
|
283 // std::cerr << "Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << std::endl;
|
Chris@148
|
284
|
Chris@266
|
285 setNormalizationFactorToWritebuf(factor);
|
Chris@266
|
286
|
Chris@148
|
287 m_mfc->setColumnAt(x, m_writebuf);
|
Chris@148
|
288 }
|
Chris@148
|
289
|
Chris@148
|
290 void
|
Chris@148
|
291 FFTFileCache::setColumnAt(size_t x, float *real, float *imag)
|
Chris@148
|
292 {
|
Chris@408
|
293 MutexLocker locker(&m_writeMutex, "FFTFileCache::setColumnAt::m_writeMutex");
|
Chris@148
|
294
|
Chris@148
|
295 size_t h = getHeight();
|
Chris@148
|
296
|
Chris@266
|
297 float factor = 0.0f;
|
Chris@148
|
298
|
Chris@148
|
299 switch (m_storageType) {
|
Chris@148
|
300
|
Chris@148
|
301 case Compact:
|
Chris@148
|
302 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
303 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
|
Chris@266
|
304 if (mag > factor) factor = mag;
|
Chris@148
|
305 }
|
Chris@148
|
306 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
307 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
|
Chris@408
|
308 float phase = atan2f(imag[y], real[y]);
|
Chris@266
|
309 ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mag / factor) * 65535.0);
|
Chris@148
|
310 ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phase * 32767) / M_PI));
|
Chris@148
|
311 }
|
Chris@148
|
312 break;
|
Chris@148
|
313
|
Chris@148
|
314 case Rectangular:
|
Chris@148
|
315 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
316 ((float *)m_writebuf)[y * 2] = real[y];
|
Chris@148
|
317 ((float *)m_writebuf)[y * 2 + 1] = imag[y];
|
Chris@148
|
318 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
|
Chris@266
|
319 if (mag > factor) factor = mag;
|
Chris@148
|
320 }
|
Chris@148
|
321 break;
|
Chris@148
|
322
|
Chris@148
|
323 case Polar:
|
Chris@148
|
324 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
325 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
|
Chris@266
|
326 if (mag > factor) factor = mag;
|
Chris@148
|
327 ((float *)m_writebuf)[y * 2] = mag;
|
Chris@408
|
328 float phase = atan2f(imag[y], real[y]);
|
Chris@290
|
329 ((float *)m_writebuf)[y * 2 + 1] = phase;
|
Chris@148
|
330 }
|
Chris@148
|
331 break;
|
Chris@148
|
332 }
|
Chris@148
|
333
|
Chris@266
|
334 // static float maxFactor = 0;
|
Chris@266
|
335 // if (factor > maxFactor) maxFactor = factor;
|
Chris@266
|
336 // std::cerr << "[RI] Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << std::endl;
|
Chris@266
|
337
|
Chris@266
|
338 setNormalizationFactorToWritebuf(factor);
|
Chris@266
|
339
|
Chris@148
|
340 m_mfc->setColumnAt(x, m_writebuf);
|
Chris@148
|
341 }
|
Chris@148
|
342
|
Chris@170
|
343 size_t
|
Chris@170
|
344 FFTFileCache::getCacheSize(size_t width, size_t height, StorageType type)
|
Chris@170
|
345 {
|
Chris@266
|
346 return (height * 2 + (type == Compact ? 2 : 1)) * width *
|
Chris@170
|
347 (type == Compact ? sizeof(uint16_t) : sizeof(float)) +
|
Chris@170
|
348 2 * sizeof(size_t); // matrix file header size
|
Chris@170
|
349 }
|
Chris@170
|
350
|
Chris@183
|
351 void
|
Chris@458
|
352 FFTFileCache::populateReadBuf(size_t x) const // m_readbufMutex already held
|
Chris@183
|
353 {
|
Chris@183
|
354 Profiler profiler("FFTFileCache::populateReadBuf", false);
|
Chris@183
|
355
|
Chris@183
|
356 if (!m_readbuf) {
|
Chris@183
|
357 m_readbuf = new char[m_mfc->getHeight() * 2 * m_mfc->getCellSize()];
|
Chris@183
|
358 }
|
Chris@455
|
359 try {
|
Chris@455
|
360 m_mfc->getColumnAt(x, m_readbuf);
|
Chris@455
|
361 if (m_mfc->haveSetColumnAt(x + 1)) {
|
Chris@455
|
362 m_mfc->getColumnAt
|
Chris@455
|
363 (x + 1, m_readbuf + m_mfc->getCellSize() * m_mfc->getHeight());
|
Chris@455
|
364 m_readbufWidth = 2;
|
Chris@455
|
365 } else {
|
Chris@455
|
366 m_readbufWidth = 1;
|
Chris@455
|
367 }
|
Chris@455
|
368 } catch (FileReadFailed f) {
|
Chris@455
|
369 std::cerr << "ERROR: FFTFileCache::populateReadBuf: File read failed: "
|
Chris@455
|
370 << f.what() << std::endl;
|
Chris@455
|
371 memset(m_readbuf, 0, m_mfc->getHeight() * 2 * m_mfc->getCellSize());
|
Chris@183
|
372 }
|
Chris@183
|
373 m_readbufCol = x;
|
Chris@183
|
374 }
|
Chris@183
|
375
|