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@148
|
22
|
Chris@148
|
23 #include <iostream>
|
Chris@148
|
24
|
Chris@374
|
25
|
Chris@148
|
26 // The underlying matrix has height (m_height * 2 + 1). In each
|
Chris@148
|
27 // column we store magnitude at [0], [2] etc and phase at [1], [3]
|
Chris@148
|
28 // etc, and then store the normalization factor (maximum magnitude) at
|
Chris@266
|
29 // [m_height * 2]. In compact mode, the factor takes two cells.
|
Chris@148
|
30
|
Chris@148
|
31 FFTFileCache::FFTFileCache(QString fileBase, MatrixFile::Mode mode,
|
Chris@148
|
32 StorageType storageType) :
|
Chris@148
|
33 m_writebuf(0),
|
Chris@148
|
34 m_readbuf(0),
|
Chris@148
|
35 m_readbufCol(0),
|
Chris@148
|
36 m_readbufWidth(0),
|
Chris@148
|
37 m_mfc(new MatrixFile
|
Chris@148
|
38 (fileBase, mode,
|
Chris@148
|
39 storageType == Compact ? sizeof(uint16_t) : sizeof(float),
|
Chris@148
|
40 mode == MatrixFile::ReadOnly)),
|
Chris@266
|
41 m_storageType(storageType),
|
Chris@266
|
42 m_factorSize(storageType == Compact ? 2 : 1)
|
Chris@148
|
43 {
|
Chris@259
|
44 // std::cerr << "FFTFileCache: storage type is " << (storageType == Compact ? "Compact" : storageType == Polar ? "Polar" : "Rectangular") << std::endl;
|
Chris@148
|
45 }
|
Chris@148
|
46
|
Chris@148
|
47 FFTFileCache::~FFTFileCache()
|
Chris@148
|
48 {
|
Chris@148
|
49 if (m_readbuf) delete[] m_readbuf;
|
Chris@148
|
50 if (m_writebuf) delete[] m_writebuf;
|
Chris@148
|
51 delete m_mfc;
|
Chris@148
|
52 }
|
Chris@148
|
53
|
Chris@148
|
54 size_t
|
Chris@148
|
55 FFTFileCache::getWidth() const
|
Chris@148
|
56 {
|
Chris@148
|
57 return m_mfc->getWidth();
|
Chris@148
|
58 }
|
Chris@148
|
59
|
Chris@148
|
60 size_t
|
Chris@148
|
61 FFTFileCache::getHeight() const
|
Chris@148
|
62 {
|
Chris@148
|
63 size_t mh = m_mfc->getHeight();
|
Chris@266
|
64 if (mh > m_factorSize) return (mh - m_factorSize) / 2;
|
Chris@148
|
65 else return 0;
|
Chris@148
|
66 }
|
Chris@148
|
67
|
Chris@148
|
68 void
|
Chris@148
|
69 FFTFileCache::resize(size_t width, size_t height)
|
Chris@148
|
70 {
|
Chris@408
|
71 MutexLocker locker(&m_writeMutex, "FFTFileCache::resize::m_writeMutex");
|
Chris@148
|
72
|
Chris@266
|
73 m_mfc->resize(width, height * 2 + m_factorSize);
|
Chris@148
|
74 if (m_readbuf) {
|
Chris@148
|
75 delete[] m_readbuf;
|
Chris@148
|
76 m_readbuf = 0;
|
Chris@148
|
77 }
|
Chris@148
|
78 if (m_writebuf) {
|
Chris@148
|
79 delete[] m_writebuf;
|
Chris@148
|
80 }
|
Chris@266
|
81 m_writebuf = new char[(height * 2 + m_factorSize) * m_mfc->getCellSize()];
|
Chris@148
|
82 }
|
Chris@148
|
83
|
Chris@148
|
84 void
|
Chris@148
|
85 FFTFileCache::reset()
|
Chris@148
|
86 {
|
Chris@148
|
87 m_mfc->reset();
|
Chris@148
|
88 }
|
Chris@148
|
89
|
Chris@148
|
90 float
|
Chris@148
|
91 FFTFileCache::getMagnitudeAt(size_t x, size_t y) const
|
Chris@148
|
92 {
|
Chris@183
|
93 Profiler profiler("FFTFileCache::getMagnitudeAt", false);
|
Chris@183
|
94
|
Chris@148
|
95 float value = 0.f;
|
Chris@148
|
96
|
Chris@148
|
97 switch (m_storageType) {
|
Chris@148
|
98
|
Chris@148
|
99 case Compact:
|
Chris@148
|
100 value = (getFromReadBufCompactUnsigned(x, y * 2) / 65535.0)
|
Chris@148
|
101 * getNormalizationFactor(x);
|
Chris@148
|
102 break;
|
Chris@148
|
103
|
Chris@148
|
104 case Rectangular:
|
Chris@148
|
105 {
|
Chris@148
|
106 float real, imag;
|
Chris@148
|
107 getValuesAt(x, y, real, imag);
|
Chris@148
|
108 value = sqrtf(real * real + imag * imag);
|
Chris@148
|
109 break;
|
Chris@148
|
110 }
|
Chris@148
|
111
|
Chris@148
|
112 case Polar:
|
Chris@148
|
113 value = getFromReadBufStandard(x, y * 2);
|
Chris@148
|
114 break;
|
Chris@148
|
115 }
|
Chris@148
|
116
|
Chris@148
|
117 return value;
|
Chris@148
|
118 }
|
Chris@148
|
119
|
Chris@148
|
120 float
|
Chris@148
|
121 FFTFileCache::getNormalizedMagnitudeAt(size_t x, size_t y) const
|
Chris@148
|
122 {
|
Chris@148
|
123 float value = 0.f;
|
Chris@148
|
124
|
Chris@148
|
125 switch (m_storageType) {
|
Chris@148
|
126
|
Chris@148
|
127 case Compact:
|
Chris@148
|
128 value = getFromReadBufCompactUnsigned(x, y * 2) / 65535.0;
|
Chris@148
|
129 break;
|
Chris@148
|
130
|
Chris@148
|
131 default:
|
Chris@148
|
132 {
|
Chris@148
|
133 float mag = getMagnitudeAt(x, y);
|
Chris@148
|
134 float factor = getNormalizationFactor(x);
|
Chris@148
|
135 if (factor != 0) value = mag / factor;
|
Chris@148
|
136 else value = 0.f;
|
Chris@148
|
137 break;
|
Chris@148
|
138 }
|
Chris@148
|
139 }
|
Chris@148
|
140
|
Chris@148
|
141 return value;
|
Chris@148
|
142 }
|
Chris@148
|
143
|
Chris@148
|
144 float
|
Chris@148
|
145 FFTFileCache::getMaximumMagnitudeAt(size_t x) const
|
Chris@148
|
146 {
|
Chris@148
|
147 return getNormalizationFactor(x);
|
Chris@148
|
148 }
|
Chris@148
|
149
|
Chris@148
|
150 float
|
Chris@148
|
151 FFTFileCache::getPhaseAt(size_t x, size_t y) const
|
Chris@148
|
152 {
|
Chris@148
|
153 float value = 0.f;
|
Chris@148
|
154
|
Chris@148
|
155 switch (m_storageType) {
|
Chris@148
|
156
|
Chris@148
|
157 case Compact:
|
Chris@148
|
158 value = (getFromReadBufCompactSigned(x, y * 2 + 1) / 32767.0) * M_PI;
|
Chris@148
|
159 break;
|
Chris@148
|
160
|
Chris@148
|
161 case Rectangular:
|
Chris@148
|
162 {
|
Chris@148
|
163 float real, imag;
|
Chris@148
|
164 getValuesAt(x, y, real, imag);
|
Chris@408
|
165 value = atan2f(imag, real);
|
Chris@148
|
166 break;
|
Chris@148
|
167 }
|
Chris@148
|
168
|
Chris@148
|
169 case Polar:
|
Chris@148
|
170 value = getFromReadBufStandard(x, y * 2 + 1);
|
Chris@148
|
171 break;
|
Chris@148
|
172 }
|
Chris@148
|
173
|
Chris@148
|
174 return value;
|
Chris@148
|
175 }
|
Chris@148
|
176
|
Chris@148
|
177 void
|
Chris@148
|
178 FFTFileCache::getValuesAt(size_t x, size_t y, float &real, float &imag) const
|
Chris@148
|
179 {
|
Chris@148
|
180 switch (m_storageType) {
|
Chris@148
|
181
|
Chris@148
|
182 case Rectangular:
|
Chris@148
|
183 real = getFromReadBufStandard(x, y * 2);
|
Chris@148
|
184 imag = getFromReadBufStandard(x, y * 2 + 1);
|
Chris@148
|
185 return;
|
Chris@148
|
186
|
Chris@148
|
187 default:
|
Chris@148
|
188 float mag = getMagnitudeAt(x, y);
|
Chris@148
|
189 float phase = getPhaseAt(x, y);
|
Chris@148
|
190 real = mag * cosf(phase);
|
Chris@148
|
191 imag = mag * sinf(phase);
|
Chris@148
|
192 return;
|
Chris@148
|
193 }
|
Chris@148
|
194 }
|
Chris@148
|
195
|
Chris@148
|
196 bool
|
Chris@148
|
197 FFTFileCache::haveSetColumnAt(size_t x) const
|
Chris@148
|
198 {
|
Chris@148
|
199 return m_mfc->haveSetColumnAt(x);
|
Chris@148
|
200 }
|
Chris@148
|
201
|
Chris@148
|
202 void
|
Chris@148
|
203 FFTFileCache::setColumnAt(size_t x, float *mags, float *phases, float factor)
|
Chris@148
|
204 {
|
Chris@408
|
205 MutexLocker locker(&m_writeMutex, "FFTFileCache::setColumnAt::m_writeMutex");
|
Chris@148
|
206
|
Chris@148
|
207 size_t h = getHeight();
|
Chris@148
|
208
|
Chris@148
|
209 switch (m_storageType) {
|
Chris@148
|
210
|
Chris@148
|
211 case Compact:
|
Chris@148
|
212 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
213 ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mags[y] / factor) * 65535.0);
|
Chris@148
|
214 ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phases[y] * 32767) / M_PI));
|
Chris@148
|
215 }
|
Chris@148
|
216 break;
|
Chris@148
|
217
|
Chris@148
|
218 case Rectangular:
|
Chris@148
|
219 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
220 ((float *)m_writebuf)[y * 2] = mags[y] * cosf(phases[y]);
|
Chris@148
|
221 ((float *)m_writebuf)[y * 2 + 1] = mags[y] * sinf(phases[y]);
|
Chris@148
|
222 }
|
Chris@148
|
223 break;
|
Chris@148
|
224
|
Chris@148
|
225 case Polar:
|
Chris@148
|
226 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
227 ((float *)m_writebuf)[y * 2] = mags[y];
|
Chris@148
|
228 ((float *)m_writebuf)[y * 2 + 1] = phases[y];
|
Chris@148
|
229 }
|
Chris@148
|
230 break;
|
Chris@148
|
231 }
|
Chris@148
|
232
|
Chris@266
|
233 // static float maxFactor = 0;
|
Chris@266
|
234 // if (factor > maxFactor) maxFactor = factor;
|
Chris@266
|
235 // std::cerr << "Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << std::endl;
|
Chris@148
|
236
|
Chris@266
|
237 setNormalizationFactorToWritebuf(factor);
|
Chris@266
|
238
|
Chris@148
|
239 m_mfc->setColumnAt(x, m_writebuf);
|
Chris@148
|
240 }
|
Chris@148
|
241
|
Chris@148
|
242 void
|
Chris@148
|
243 FFTFileCache::setColumnAt(size_t x, float *real, float *imag)
|
Chris@148
|
244 {
|
Chris@408
|
245 MutexLocker locker(&m_writeMutex, "FFTFileCache::setColumnAt::m_writeMutex");
|
Chris@148
|
246
|
Chris@148
|
247 size_t h = getHeight();
|
Chris@148
|
248
|
Chris@266
|
249 float factor = 0.0f;
|
Chris@148
|
250
|
Chris@148
|
251 switch (m_storageType) {
|
Chris@148
|
252
|
Chris@148
|
253 case Compact:
|
Chris@148
|
254 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
255 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
|
Chris@266
|
256 if (mag > factor) factor = mag;
|
Chris@148
|
257 }
|
Chris@148
|
258 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
259 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
|
Chris@408
|
260 float phase = atan2f(imag[y], real[y]);
|
Chris@266
|
261 ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mag / factor) * 65535.0);
|
Chris@148
|
262 ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phase * 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] = real[y];
|
Chris@148
|
269 ((float *)m_writebuf)[y * 2 + 1] = imag[y];
|
Chris@148
|
270 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
|
Chris@266
|
271 if (mag > factor) factor = mag;
|
Chris@148
|
272 }
|
Chris@148
|
273 break;
|
Chris@148
|
274
|
Chris@148
|
275 case Polar:
|
Chris@148
|
276 for (size_t y = 0; y < h; ++y) {
|
Chris@148
|
277 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
|
Chris@266
|
278 if (mag > factor) factor = mag;
|
Chris@148
|
279 ((float *)m_writebuf)[y * 2] = mag;
|
Chris@408
|
280 float phase = atan2f(imag[y], real[y]);
|
Chris@290
|
281 ((float *)m_writebuf)[y * 2 + 1] = phase;
|
Chris@148
|
282 }
|
Chris@148
|
283 break;
|
Chris@148
|
284 }
|
Chris@148
|
285
|
Chris@266
|
286 // static float maxFactor = 0;
|
Chris@266
|
287 // if (factor > maxFactor) maxFactor = factor;
|
Chris@266
|
288 // std::cerr << "[RI] Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << std::endl;
|
Chris@266
|
289
|
Chris@266
|
290 setNormalizationFactorToWritebuf(factor);
|
Chris@266
|
291
|
Chris@148
|
292 m_mfc->setColumnAt(x, m_writebuf);
|
Chris@148
|
293 }
|
Chris@148
|
294
|
Chris@170
|
295 size_t
|
Chris@170
|
296 FFTFileCache::getCacheSize(size_t width, size_t height, StorageType type)
|
Chris@170
|
297 {
|
Chris@266
|
298 return (height * 2 + (type == Compact ? 2 : 1)) * width *
|
Chris@170
|
299 (type == Compact ? sizeof(uint16_t) : sizeof(float)) +
|
Chris@170
|
300 2 * sizeof(size_t); // matrix file header size
|
Chris@170
|
301 }
|
Chris@170
|
302
|
Chris@183
|
303 void
|
Chris@183
|
304 FFTFileCache::populateReadBuf(size_t x) const
|
Chris@183
|
305 {
|
Chris@183
|
306 Profiler profiler("FFTFileCache::populateReadBuf", false);
|
Chris@183
|
307
|
Chris@183
|
308 if (!m_readbuf) {
|
Chris@183
|
309 m_readbuf = new char[m_mfc->getHeight() * 2 * m_mfc->getCellSize()];
|
Chris@183
|
310 }
|
Chris@183
|
311 m_mfc->getColumnAt(x, m_readbuf);
|
Chris@183
|
312 if (m_mfc->haveSetColumnAt(x + 1)) {
|
Chris@183
|
313 m_mfc->getColumnAt
|
Chris@183
|
314 (x + 1, m_readbuf + m_mfc->getCellSize() * m_mfc->getHeight());
|
Chris@183
|
315 m_readbufWidth = 2;
|
Chris@183
|
316 } else {
|
Chris@183
|
317 m_readbufWidth = 1;
|
Chris@183
|
318 }
|
Chris@183
|
319 m_readbufCol = x;
|
Chris@183
|
320 }
|
Chris@183
|
321
|