comparison data/model/FFTModel.cpp @ 1780:6d6740b075c3

Support optional max frequency setting, useful when we want to store caches of very constrained frequency ranges (as in melodic-range spectrogram, potentially)
author Chris Cannam
date Thu, 12 Sep 2019 11:52:19 +0100
parents fadd9f8aaa27
children 4eac4bf35b45
comparison
equal deleted inserted replaced
1779:85903b0e9b42 1780:6d6740b075c3
37 WindowType windowType, 37 WindowType windowType,
38 int windowSize, 38 int windowSize,
39 int windowIncrement, 39 int windowIncrement,
40 int fftSize) : 40 int fftSize) :
41 m_model(modelId), 41 m_model(modelId),
42 m_sampleRate(0),
42 m_channel(channel), 43 m_channel(channel),
43 m_windowType(windowType), 44 m_windowType(windowType),
44 m_windowSize(windowSize), 45 m_windowSize(windowSize),
45 m_windowIncrement(windowIncrement), 46 m_windowIncrement(windowIncrement),
46 m_fftSize(fftSize), 47 m_fftSize(fftSize),
47 m_windower(windowType, windowSize), 48 m_windower(windowType, windowSize),
48 m_fft(fftSize), 49 m_fft(fftSize),
50 m_maximumFrequency(0.0),
49 m_cacheWriteIndex(0), 51 m_cacheWriteIndex(0),
50 m_cacheSize(3) 52 m_cacheSize(3)
51 { 53 {
52 while (m_cached.size() < m_cacheSize) { 54 while (m_cached.size() < m_cacheSize) {
53 m_cached.push_back({ -1, cvec(m_fftSize / 2 + 1) }); 55 m_cached.push_back({ -1, cvec(m_fftSize / 2 + 1) });
61 63
62 m_fft.initFloat(); 64 m_fft.initFloat();
63 65
64 auto model = ModelById::getAs<DenseTimeValueModel>(m_model); 66 auto model = ModelById::getAs<DenseTimeValueModel>(m_model);
65 if (model) { 67 if (model) {
68 m_sampleRate = model->getSampleRate();
69
66 connect(model.get(), SIGNAL(modelChanged(ModelId)), 70 connect(model.get(), SIGNAL(modelChanged(ModelId)),
67 this, SIGNAL(modelChanged(ModelId))); 71 this, SIGNAL(modelChanged(ModelId)));
68 connect(model.get(), SIGNAL(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)), 72 connect(model.get(), SIGNAL(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)),
69 this, SIGNAL(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t))); 73 this, SIGNAL(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)));
70 } 74 }
93 } 97 }
94 98
95 sv_samplerate_t 99 sv_samplerate_t
96 FFTModel::getSampleRate() const 100 FFTModel::getSampleRate() const
97 { 101 {
98 auto model = ModelById::getAs<DenseTimeValueModel>(m_model); 102 return m_sampleRate;
99 if (model) return model->getSampleRate(); 103 }
100 else return 0; 104
105 void
106 FFTModel::setMaximumFrequency(double freq)
107 {
108 m_maximumFrequency = freq;
101 } 109 }
102 110
103 int 111 int
104 FFTModel::getWidth() const 112 FFTModel::getWidth() const
105 { 113 {
110 } 118 }
111 119
112 int 120 int
113 FFTModel::getHeight() const 121 FFTModel::getHeight() const
114 { 122 {
115 return m_fftSize / 2 + 1; 123 int height = m_fftSize / 2 + 1;
124 if (m_maximumFrequency != 0.0) {
125 int maxBin = int(ceil(m_maximumFrequency * m_fftSize) / m_sampleRate);
126 if (maxBin >= 0 && maxBin < height) {
127 return maxBin + 1;
128 }
129 }
130 return height;
116 } 131 }
117 132
118 QString 133 QString
119 FFTModel::getBinName(int n) const 134 FFTModel::getBinName(int n) const
120 { 135 {
121 sv_samplerate_t sr = getSampleRate(); 136 sv_samplerate_t sr = getSampleRate();
122 if (!sr) return ""; 137 if (!sr) return "";
123 QString name = tr("%1 Hz").arg((n * sr) / ((getHeight()-1) * 2)); 138 QString name = tr("%1 Hz").arg((double(n) * sr) / m_fftSize);
124 return name; 139 return name;
125 } 140 }
126 141
127 FFTModel::Column 142 FFTModel::Column
128 FFTModel::getColumn(int x) const 143 FFTModel::getColumn(int x) const
176 } 191 }
177 192
178 void 193 void
179 FFTModel::getValuesAt(int x, int y, float &re, float &im) const 194 FFTModel::getValuesAt(int x, int y, float &re, float &im) const
180 { 195 {
196 if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) {
197 re = 0.f;
198 im = 0.f;
199 return;
200 }
181 auto col = getFFTColumn(x); 201 auto col = getFFTColumn(x);
182 re = col[y].real(); 202 re = col[y].real();
183 im = col[y].imag(); 203 im = col[y].imag();
184 } 204 }
185 205
337 } 357 }
338 358
339 return data; 359 return data;
340 } 360 }
341 361
342 const FFTModel::cvec & 362 FFTModel::cvec
343 FFTModel::getFFTColumn(int n) const 363 FFTModel::getFFTColumn(int n) const
344 { 364 {
365 int h = getHeight();
366 bool truncate = (h < m_fftSize / 2 + 1);
367
345 // The small cache (i.e. the m_cached deque) is for cases where 368 // The small cache (i.e. the m_cached deque) is for cases where
346 // values are looked up individually, and for e.g. peak-frequency 369 // values are looked up individually, and for e.g. peak-frequency
347 // spectrograms where values from two consecutive columns are 370 // spectrograms where values from two consecutive columns are
348 // needed at once. This cache gets essentially no hits when 371 // needed at once. This cache gets essentially no hits when
349 // scrolling through a magnitude spectrogram, but 95%+ hits with a 372 // scrolling through a magnitude spectrogram, but 95%+ hits with a
350 // peak-frequency spectrogram or spectrum. 373 // peak-frequency spectrogram or spectrum.
351 for (const auto &incache : m_cached) { 374 for (const auto &incache : m_cached) {
352 if (incache.n == n) { 375 if (incache.n == n) {
353 inSmallCache.hit(); 376 inSmallCache.hit();
354 return incache.col; 377 if (!truncate) {
378 return incache.col;
379 } else {
380 return cvec(incache.col.begin(), incache.col.begin() + h);
381 }
355 } 382 }
356 } 383 }
357 inSmallCache.miss(); 384 inSmallCache.miss();
358 385
359 Profiler profiler("FFTModel::getFFTColumn (cache miss)"); 386 Profiler profiler("FFTModel::getFFTColumn (cache miss)");
369 396
370 m_cached[m_cacheWriteIndex].n = n; 397 m_cached[m_cacheWriteIndex].n = n;
371 398
372 m_cacheWriteIndex = (m_cacheWriteIndex + 1) % m_cacheSize; 399 m_cacheWriteIndex = (m_cacheWriteIndex + 1) % m_cacheSize;
373 400
374 return col; 401 if (!truncate) {
402 return col;
403 } else {
404 return cvec(col.begin(), col.begin() + h);
405 }
375 } 406 }
376 407
377 bool 408 bool
378 FFTModel::estimateStableFrequency(int x, int y, double &frequency) 409 FFTModel::estimateStableFrequency(int x, int y, double &frequency)
379 { 410 {