comparison data/model/FFTModel.cpp @ 1837:1b688ab5f1b3

Unify various vectors to our base floatvec_t type; store columns in fft model cache at their desired height so we can return a reference (speeding up the peak-frequency spectrogram in particular)
author Chris Cannam
date Thu, 09 Apr 2020 11:22:55 +0100
parents dd51797e528e
children 915d316a5609
comparison
equal deleted inserted replaced
1835:804dd0c06f0e 1837:1b688ab5f1b3
49 m_fft(fftSize), 49 m_fft(fftSize),
50 m_maximumFrequency(0.0), 50 m_maximumFrequency(0.0),
51 m_cacheWriteIndex(0), 51 m_cacheWriteIndex(0),
52 m_cacheSize(3) 52 m_cacheSize(3)
53 { 53 {
54 while (m_cached.size() < m_cacheSize) { 54 clearCaches();
55 m_cached.push_back({ -1, cvec(m_fftSize / 2 + 1) });
56 }
57 55
58 if (m_windowSize > m_fftSize) { 56 if (m_windowSize > m_fftSize) {
59 SVCERR << "ERROR: FFTModel::FFTModel: window size (" << m_windowSize 57 SVCERR << "ERROR: FFTModel::FFTModel: window size (" << m_windowSize
60 << ") may not exceed FFT size (" << m_fftSize << ")" << endl; 58 << ") may not exceed FFT size (" << m_fftSize << ")" << endl;
61 throw invalid_argument("FFTModel window size may not exceed FFT size"); 59 throw invalid_argument("FFTModel window size may not exceed FFT size");
78 76
79 FFTModel::~FFTModel() 77 FFTModel::~FFTModel()
80 { 78 {
81 } 79 }
82 80
81 void
82 FFTModel::clearCaches()
83 {
84 m_cached.clear();
85 while (m_cached.size() < m_cacheSize) {
86 m_cached.push_back({ -1, complexvec_t(m_fftSize / 2 + 1) });
87 }
88 m_cacheWriteIndex = 0;
89 m_savedData.range = { 0, 0 };
90 }
91
83 bool 92 bool
84 FFTModel::isOK() const 93 FFTModel::isOK() const
85 { 94 {
86 auto model = ModelById::getAs<DenseTimeValueModel>(m_model); 95 auto model = ModelById::getAs<DenseTimeValueModel>(m_model);
87 if (!model) { 96 if (!model) {
108 117
109 void 118 void
110 FFTModel::setMaximumFrequency(double freq) 119 FFTModel::setMaximumFrequency(double freq)
111 { 120 {
112 m_maximumFrequency = freq; 121 m_maximumFrequency = freq;
122 clearCaches();
113 } 123 }
114 124
115 int 125 int
116 FFTModel::getWidth() const 126 FFTModel::getWidth() const
117 { 127 {
244 imags[i] = col[minbin + i].imag(); 254 imags[i] = col[minbin + i].imag();
245 } 255 }
246 return true; 256 return true;
247 } 257 }
248 258
249 FFTModel::fvec 259 floatvec_t
250 FFTModel::getSourceSamples(int column) const 260 FFTModel::getSourceSamples(int column) const
251 { 261 {
252 // m_fftSize may be greater than m_windowSize, but not the reverse 262 // m_fftSize may be greater than m_windowSize, but not the reverse
253 263
254 // cerr << "getSourceSamples(" << column << ")" << endl; 264 // cerr << "getSourceSamples(" << column << ")" << endl;
260 270
261 if (off == 0) { 271 if (off == 0) {
262 return data; 272 return data;
263 } else { 273 } else {
264 vector<float> pad(off, 0.f); 274 vector<float> pad(off, 0.f);
265 fvec padded; 275 floatvec_t padded;
266 padded.reserve(m_fftSize); 276 padded.reserve(m_fftSize);
267 padded.insert(padded.end(), pad.begin(), pad.end()); 277 padded.insert(padded.end(), pad.begin(), pad.end());
268 padded.insert(padded.end(), data.begin(), data.end()); 278 padded.insert(padded.end(), data.begin(), data.end());
269 padded.insert(padded.end(), pad.begin(), pad.end()); 279 padded.insert(padded.end(), pad.begin(), pad.end());
270 return padded; 280 return padded;
271 } 281 }
272 } 282 }
273 283
274 FFTModel::fvec 284 floatvec_t
275 FFTModel::getSourceData(pair<sv_frame_t, sv_frame_t> range) const 285 FFTModel::getSourceData(pair<sv_frame_t, sv_frame_t> range) const
276 { 286 {
277 // cerr << "getSourceData(" << range.first << "," << range.second 287 // cerr << "getSourceData(" << range.first << "," << range.second
278 // << "): saved range is (" << m_savedData.range.first 288 // << "): saved range is (" << m_savedData.range.first
279 // << "," << m_savedData.range.second << ")" << endl; 289 // << "," << m_savedData.range.second << ")" << endl;
291 301
292 inSourceCache.partial(); 302 inSourceCache.partial();
293 303
294 sv_frame_t discard = range.first - m_savedData.range.first; 304 sv_frame_t discard = range.first - m_savedData.range.first;
295 305
296 fvec data; 306 floatvec_t data;
297 data.reserve(range.second - range.first); 307 data.reserve(range.second - range.first);
298 308
299 data.insert(data.end(), 309 data.insert(data.end(),
300 m_savedData.data.begin() + discard, 310 m_savedData.data.begin() + discard,
301 m_savedData.data.end()); 311 m_savedData.data.end());
302 312
303 fvec rest = getSourceDataUncached 313 floatvec_t rest = getSourceDataUncached
304 ({ m_savedData.range.second, range.second }); 314 ({ m_savedData.range.second, range.second });
305 315
306 data.insert(data.end(), rest.begin(), rest.end()); 316 data.insert(data.end(), rest.begin(), rest.end());
307 317
308 m_savedData = { range, data }; 318 m_savedData = { range, data };
316 m_savedData = { range, data }; 326 m_savedData = { range, data };
317 return data; 327 return data;
318 } 328 }
319 } 329 }
320 330
321 FFTModel::fvec 331 floatvec_t
322 FFTModel::getSourceDataUncached(pair<sv_frame_t, sv_frame_t> range) const 332 FFTModel::getSourceDataUncached(pair<sv_frame_t, sv_frame_t> range) const
323 { 333 {
324 Profiler profiler("FFTModel::getSourceDataUncached"); 334 Profiler profiler("FFTModel::getSourceDataUncached");
325 335
326 auto model = ModelById::getAs<DenseTimeValueModel>(m_model); 336 auto model = ModelById::getAs<DenseTimeValueModel>(m_model);
364 } 374 }
365 375
366 return data; 376 return data;
367 } 377 }
368 378
369 FFTModel::cvec 379 const complexvec_t &
370 FFTModel::getFFTColumn(int n) const 380 FFTModel::getFFTColumn(int n) const
371 { 381 {
372 int h = getHeight();
373 bool truncate = (h < m_fftSize / 2 + 1);
374
375 // The small cache (i.e. the m_cached deque) is for cases where 382 // The small cache (i.e. the m_cached deque) is for cases where
376 // values are looked up individually, and for e.g. peak-frequency 383 // values are looked up individually, and for e.g. peak-frequency
377 // spectrograms where values from two consecutive columns are 384 // spectrograms where values from two consecutive columns are
378 // needed at once. This cache gets essentially no hits when 385 // needed at once. This cache gets essentially no hits when
379 // scrolling through a magnitude spectrogram, but 95%+ hits with a 386 // scrolling through a magnitude spectrogram, but 95%+ hits with a
380 // peak-frequency spectrogram or spectrum. 387 // peak-frequency spectrogram or spectrum.
381 for (const auto &incache : m_cached) { 388 for (const auto &incache : m_cached) {
382 if (incache.n == n) { 389 if (incache.n == n) {
383 inSmallCache.hit(); 390 inSmallCache.hit();
384 if (!truncate) { 391 return incache.col;
385 return incache.col;
386 } else {
387 return cvec(incache.col.begin(), incache.col.begin() + h);
388 }
389 } 392 }
390 } 393 }
391 inSmallCache.miss(); 394 inSmallCache.miss();
392 395
393 Profiler profiler("FFTModel::getFFTColumn (cache miss)"); 396 Profiler profiler("FFTModel::getFFTColumn (cache miss)");
394 397
395 auto samples = getSourceSamples(n); 398 auto samples = getSourceSamples(n);
396 m_windower.cut(samples.data() + (m_fftSize - m_windowSize) / 2); 399 m_windower.cut(samples.data() + (m_fftSize - m_windowSize) / 2);
397 breakfastquay::v_fftshift(samples.data(), m_fftSize); 400 breakfastquay::v_fftshift(samples.data(), m_fftSize);
398 401
399 cvec &col = m_cached[m_cacheWriteIndex].col; 402 complexvec_t &col = m_cached[m_cacheWriteIndex].col;
403
404 // expand to large enough for fft destination, if truncated previously
405 col.resize(m_fftSize / 2 + 1);
400 406
401 m_fft.forwardInterleaved(samples.data(), 407 m_fft.forwardInterleaved(samples.data(),
402 reinterpret_cast<float *>(col.data())); 408 reinterpret_cast<float *>(col.data()));
403 409
410 // keep only the number of elements we need - so that we can
411 // return a const ref without having to resize on a cache hit
412 col.resize(getHeight());
413
404 m_cached[m_cacheWriteIndex].n = n; 414 m_cached[m_cacheWriteIndex].n = n;
405 415
406 m_cacheWriteIndex = (m_cacheWriteIndex + 1) % m_cacheSize; 416 m_cacheWriteIndex = (m_cacheWriteIndex + 1) % m_cacheSize;
407 417
408 if (!truncate) { 418 return col;
409 return col;
410 } else {
411 return cvec(col.begin(), col.begin() + h);
412 }
413 } 419 }
414 420
415 bool 421 bool
416 FFTModel::estimateStableFrequency(int x, int y, double &frequency) 422 FFTModel::estimateStableFrequency(int x, int y, double &frequency)
417 { 423 {