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@148
|
106 value = (getFromReadBufCompactUnsigned(x, y * 2) / 65535.0)
|
Chris@148
|
107 * getNormalizationFactor(x);
|
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@148
|
119 value = getFromReadBufStandard(x, y * 2);
|
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@148
|
134 value = getFromReadBufCompactUnsigned(x, y * 2) / 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@148
|
140 float factor = getNormalizationFactor(x);
|
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@148
|
153 return getNormalizationFactor(x);
|
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@148
|
164 value = (getFromReadBufCompactSigned(x, y * 2 + 1) / 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@148
|
176 value = getFromReadBufStandard(x, y * 2 + 1);
|
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@148
|
189 real = getFromReadBufStandard(x, y * 2);
|
Chris@148
|
190 imag = getFromReadBufStandard(x, y * 2 + 1);
|
Chris@148
|
191 return;
|
Chris@148
|
192
|
Chris@148
|
193 default:
|
Chris@148
|
194 float mag = getMagnitudeAt(x, y);
|
Chris@148
|
195 float phase = getPhaseAt(x, y);
|
Chris@148
|
196 real = mag * cosf(phase);
|
Chris@148
|
197 imag = mag * sinf(phase);
|
Chris@148
|
198 return;
|
Chris@148
|
199 }
|
Chris@148
|
200 }
|
Chris@148
|
201
|
Chris@148
|
202 bool
|
Chris@148
|
203 FFTFileCache::haveSetColumnAt(size_t x) const
|
Chris@148
|
204 {
|
Chris@148
|
205 return m_mfc->haveSetColumnAt(x);
|
Chris@148
|
206 }
|
Chris@148
|
207
|
Chris@148
|
208 void
|
Chris@148
|
209 FFTFileCache::setColumnAt(size_t x, float *mags, float *phases, float factor)
|
Chris@148
|
210 {
|
Chris@408
|
211 MutexLocker locker(&m_writeMutex, "FFTFileCache::setColumnAt::m_writeMutex");
|
Chris@148
|
212
|
Chris@148
|
213 size_t h = getHeight();
|
Chris@148
|
214
|
Chris@148
|
215 switch (m_storageType) {
|
Chris@148
|
216
|
Chris@148
|
217 case Compact:
|
Chris@148
|
218 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
219 ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mags[y] / factor) * 65535.0);
|
Chris@148
|
220 ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phases[y] * 32767) / M_PI));
|
Chris@148
|
221 }
|
Chris@148
|
222 break;
|
Chris@148
|
223
|
Chris@148
|
224 case Rectangular:
|
Chris@148
|
225 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
226 ((float *)m_writebuf)[y * 2] = mags[y] * cosf(phases[y]);
|
Chris@148
|
227 ((float *)m_writebuf)[y * 2 + 1] = mags[y] * sinf(phases[y]);
|
Chris@148
|
228 }
|
Chris@148
|
229 break;
|
Chris@148
|
230
|
Chris@148
|
231 case Polar:
|
Chris@148
|
232 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
233 ((float *)m_writebuf)[y * 2] = mags[y];
|
Chris@148
|
234 ((float *)m_writebuf)[y * 2 + 1] = phases[y];
|
Chris@148
|
235 }
|
Chris@148
|
236 break;
|
Chris@148
|
237 }
|
Chris@148
|
238
|
Chris@266
|
239 // static float maxFactor = 0;
|
Chris@266
|
240 // if (factor > maxFactor) maxFactor = factor;
|
Chris@266
|
241 // std::cerr << "Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << std::endl;
|
Chris@148
|
242
|
Chris@266
|
243 setNormalizationFactorToWritebuf(factor);
|
Chris@266
|
244
|
Chris@148
|
245 m_mfc->setColumnAt(x, m_writebuf);
|
Chris@148
|
246 }
|
Chris@148
|
247
|
Chris@148
|
248 void
|
Chris@148
|
249 FFTFileCache::setColumnAt(size_t x, float *real, float *imag)
|
Chris@148
|
250 {
|
Chris@408
|
251 MutexLocker locker(&m_writeMutex, "FFTFileCache::setColumnAt::m_writeMutex");
|
Chris@148
|
252
|
Chris@148
|
253 size_t h = getHeight();
|
Chris@148
|
254
|
Chris@266
|
255 float factor = 0.0f;
|
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 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
|
Chris@266
|
262 if (mag > factor) factor = mag;
|
Chris@148
|
263 }
|
Chris@148
|
264 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
265 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
|
Chris@408
|
266 float phase = atan2f(imag[y], real[y]);
|
Chris@266
|
267 ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mag / factor) * 65535.0);
|
Chris@148
|
268 ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phase * 32767) / M_PI));
|
Chris@148
|
269 }
|
Chris@148
|
270 break;
|
Chris@148
|
271
|
Chris@148
|
272 case Rectangular:
|
Chris@148
|
273 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
274 ((float *)m_writebuf)[y * 2] = real[y];
|
Chris@148
|
275 ((float *)m_writebuf)[y * 2 + 1] = imag[y];
|
Chris@148
|
276 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
|
Chris@266
|
277 if (mag > factor) factor = mag;
|
Chris@148
|
278 }
|
Chris@148
|
279 break;
|
Chris@148
|
280
|
Chris@148
|
281 case Polar:
|
Chris@148
|
282 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
283 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
|
Chris@266
|
284 if (mag > factor) factor = mag;
|
Chris@148
|
285 ((float *)m_writebuf)[y * 2] = mag;
|
Chris@408
|
286 float phase = atan2f(imag[y], real[y]);
|
Chris@290
|
287 ((float *)m_writebuf)[y * 2 + 1] = phase;
|
Chris@148
|
288 }
|
Chris@148
|
289 break;
|
Chris@148
|
290 }
|
Chris@148
|
291
|
Chris@266
|
292 // static float maxFactor = 0;
|
Chris@266
|
293 // if (factor > maxFactor) maxFactor = factor;
|
Chris@266
|
294 // std::cerr << "[RI] Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << std::endl;
|
Chris@266
|
295
|
Chris@266
|
296 setNormalizationFactorToWritebuf(factor);
|
Chris@266
|
297
|
Chris@148
|
298 m_mfc->setColumnAt(x, m_writebuf);
|
Chris@148
|
299 }
|
Chris@148
|
300
|
Chris@170
|
301 size_t
|
Chris@170
|
302 FFTFileCache::getCacheSize(size_t width, size_t height, StorageType type)
|
Chris@170
|
303 {
|
Chris@266
|
304 return (height * 2 + (type == Compact ? 2 : 1)) * width *
|
Chris@170
|
305 (type == Compact ? sizeof(uint16_t) : sizeof(float)) +
|
Chris@170
|
306 2 * sizeof(size_t); // matrix file header size
|
Chris@170
|
307 }
|
Chris@170
|
308
|
Chris@183
|
309 void
|
Chris@458
|
310 FFTFileCache::populateReadBuf(size_t x) const // m_readbufMutex already held
|
Chris@183
|
311 {
|
Chris@183
|
312 Profiler profiler("FFTFileCache::populateReadBuf", false);
|
Chris@183
|
313
|
Chris@183
|
314 if (!m_readbuf) {
|
Chris@183
|
315 m_readbuf = new char[m_mfc->getHeight() * 2 * m_mfc->getCellSize()];
|
Chris@183
|
316 }
|
Chris@455
|
317 try {
|
Chris@455
|
318 m_mfc->getColumnAt(x, m_readbuf);
|
Chris@455
|
319 if (m_mfc->haveSetColumnAt(x + 1)) {
|
Chris@455
|
320 m_mfc->getColumnAt
|
Chris@455
|
321 (x + 1, m_readbuf + m_mfc->getCellSize() * m_mfc->getHeight());
|
Chris@455
|
322 m_readbufWidth = 2;
|
Chris@455
|
323 } else {
|
Chris@455
|
324 m_readbufWidth = 1;
|
Chris@455
|
325 }
|
Chris@455
|
326 } catch (FileReadFailed f) {
|
Chris@455
|
327 std::cerr << "ERROR: FFTFileCache::populateReadBuf: File read failed: "
|
Chris@455
|
328 << f.what() << std::endl;
|
Chris@455
|
329 memset(m_readbuf, 0, m_mfc->getHeight() * 2 * m_mfc->getCellSize());
|
Chris@183
|
330 }
|
Chris@183
|
331 m_readbufCol = x;
|
Chris@183
|
332 }
|
Chris@183
|
333
|