Mercurial > hg > svgui
comparison layer/SpectrogramLayer.cpp @ 109:12340cb6e6cb
* Add FFT data server class to provide a file cache mapping for each
required set of FFT parameters and source model. Make use of it in
feature extraction plugin transform, though not in other places yet.
* Add zero-pad option to spectrogram layer and remove window shape option
from the property box. To be revised.
author | Chris Cannam |
---|---|
date | Mon, 26 Jun 2006 16:12:11 +0000 |
parents | bf196d6e8998 |
children | f262aa8973e3 |
comparison
equal
deleted
inserted
replaced
108:4772fc75ac7c | 109:12340cb6e6cb |
---|---|
33 | 33 |
34 #include <cassert> | 34 #include <cassert> |
35 #include <cmath> | 35 #include <cmath> |
36 | 36 |
37 //#define DEBUG_SPECTROGRAM_REPAINT 1 | 37 //#define DEBUG_SPECTROGRAM_REPAINT 1 |
38 | |
39 static double mod(double x, double y) | |
40 { | |
41 double a = floor(x / y); | |
42 double b = x - (y * a); | |
43 return b; | |
44 } | |
45 | |
46 static float modf(float x, float y) | |
47 { | |
48 float a = floorf(x / y); | |
49 float b = x - (y * a); | |
50 return b; | |
51 } | |
52 | |
53 static double princarg(double ang) | |
54 { | |
55 return mod(ang + M_PI, -2 * M_PI) + M_PI; | |
56 } | |
57 | |
58 static float princargf(float ang) | |
59 { | |
60 return modf(ang + M_PI, -2 * M_PI) + M_PI; | |
61 } | |
62 | |
63 | 38 |
64 SpectrogramLayer::SpectrogramLayer(Configuration config) : | 39 SpectrogramLayer::SpectrogramLayer(Configuration config) : |
65 Layer(), | 40 Layer(), |
66 m_model(0), | 41 m_model(0), |
67 m_channel(0), | 42 m_channel(0), |
68 m_windowSize(1024), | 43 m_windowSize(1024), |
69 m_windowType(HanningWindow), | 44 m_windowType(HanningWindow), |
70 m_windowHopLevel(2), | 45 m_windowHopLevel(2), |
46 m_zeroPadLevel(0), | |
71 m_fftSize(1024), | 47 m_fftSize(1024), |
72 m_gain(1.0), | 48 m_gain(1.0), |
73 m_threshold(0.0), | 49 m_threshold(0.0), |
74 m_colourRotation(0), | 50 m_colourRotation(0), |
75 m_minFrequency(0), | 51 m_minFrequency(0), |
89 m_exiting(false) | 65 m_exiting(false) |
90 { | 66 { |
91 if (config == MelodicRange) { | 67 if (config == MelodicRange) { |
92 setWindowSize(8192); | 68 setWindowSize(8192); |
93 setWindowHopLevel(4); | 69 setWindowHopLevel(4); |
94 setWindowType(ParzenWindow); | 70 // setWindowType(ParzenWindow); |
95 setMaxFrequency(1000); | 71 setMaxFrequency(1000); |
96 setColourScale(LinearColourScale); | 72 setColourScale(LinearColourScale); |
97 } else if (config == MelodicPeaks) { | 73 } else if (config == MelodicPeaks) { |
98 setWindowSize(4096); | 74 setWindowSize(4096); |
99 setWindowHopLevel(5); | 75 setWindowHopLevel(5); |
100 setWindowType(BlackmanWindow); | 76 // setWindowType(BlackmanWindow); |
101 setMaxFrequency(2000); | 77 setMaxFrequency(2000); |
102 setMinFrequency(40); | 78 setMinFrequency(40); |
103 setFrequencyScale(LogFrequencyScale); | 79 setFrequencyScale(LogFrequencyScale); |
104 setColourScale(MeterColourScale); | 80 setColourScale(MeterColourScale); |
105 setBinDisplay(PeakFrequencies); | 81 setBinDisplay(PeakFrequencies); |
152 SpectrogramLayer::getProperties() const | 128 SpectrogramLayer::getProperties() const |
153 { | 129 { |
154 PropertyList list; | 130 PropertyList list; |
155 list.push_back("Colour"); | 131 list.push_back("Colour"); |
156 list.push_back("Colour Scale"); | 132 list.push_back("Colour Scale"); |
157 list.push_back("Window Type"); | 133 // list.push_back("Window Type"); |
158 list.push_back("Window Size"); | 134 list.push_back("Window Size"); |
159 list.push_back("Window Increment"); | 135 list.push_back("Window Increment"); |
160 list.push_back("Normalize Columns"); | 136 list.push_back("Normalize Columns"); |
161 list.push_back("Bin Display"); | 137 list.push_back("Bin Display"); |
162 list.push_back("Threshold"); | 138 list.push_back("Threshold"); |
163 list.push_back("Gain"); | 139 list.push_back("Gain"); |
164 list.push_back("Colour Rotation"); | 140 list.push_back("Colour Rotation"); |
165 list.push_back("Min Frequency"); | 141 list.push_back("Min Frequency"); |
166 list.push_back("Max Frequency"); | 142 list.push_back("Max Frequency"); |
167 list.push_back("Frequency Scale"); | 143 list.push_back("Frequency Scale"); |
144 list.push_back("Zero Padding"); | |
168 return list; | 145 return list; |
169 } | 146 } |
170 | 147 |
171 QString | 148 QString |
172 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const | 149 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const |
182 if (name == "Gain") return tr("Gain"); | 159 if (name == "Gain") return tr("Gain"); |
183 if (name == "Colour Rotation") return tr("Colour Rotation"); | 160 if (name == "Colour Rotation") return tr("Colour Rotation"); |
184 if (name == "Min Frequency") return tr("Min Frequency"); | 161 if (name == "Min Frequency") return tr("Min Frequency"); |
185 if (name == "Max Frequency") return tr("Max Frequency"); | 162 if (name == "Max Frequency") return tr("Max Frequency"); |
186 if (name == "Frequency Scale") return tr("Frequency Scale"); | 163 if (name == "Frequency Scale") return tr("Frequency Scale"); |
164 if (name == "Zero Padding") return tr("Smoothing"); | |
187 return ""; | 165 return ""; |
188 } | 166 } |
189 | 167 |
190 Layer::PropertyType | 168 Layer::PropertyType |
191 SpectrogramLayer::getPropertyType(const PropertyName &name) const | 169 SpectrogramLayer::getPropertyType(const PropertyName &name) const |
192 { | 170 { |
193 if (name == "Gain") return RangeProperty; | 171 if (name == "Gain") return RangeProperty; |
194 if (name == "Colour Rotation") return RangeProperty; | 172 if (name == "Colour Rotation") return RangeProperty; |
195 if (name == "Normalize Columns") return ToggleProperty; | 173 if (name == "Normalize Columns") return ToggleProperty; |
196 if (name == "Threshold") return RangeProperty; | 174 if (name == "Threshold") return RangeProperty; |
175 if (name == "Zero Padding") return ToggleProperty; | |
197 return ValueProperty; | 176 return ValueProperty; |
198 } | 177 } |
199 | 178 |
200 QString | 179 QString |
201 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const | 180 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const |
202 { | 181 { |
203 if (name == "Window Size" || | 182 if (name == "Window Size" || |
204 name == "Window Type" || | 183 name == "Window Type" || |
205 name == "Window Increment") return tr("Window"); | 184 name == "Window Increment" || |
185 name == "Zero Padding") return tr("Window"); | |
206 if (name == "Colour" || | 186 if (name == "Colour" || |
207 name == "Gain" || | 187 name == "Gain" || |
208 name == "Threshold" || | 188 name == "Threshold" || |
209 name == "Colour Rotation") return tr("Colour"); | 189 name == "Colour Rotation") return tr("Colour"); |
210 if (name == "Normalize Columns" || | 190 if (name == "Normalize Columns" || |
287 *min = 0; | 267 *min = 0; |
288 *max = 5; | 268 *max = 5; |
289 | 269 |
290 deft = m_windowHopLevel; | 270 deft = m_windowHopLevel; |
291 | 271 |
272 } else if (name == "Zero Padding") { | |
273 | |
274 *min = 0; | |
275 *max = 1; | |
276 | |
277 deft = m_zeroPadLevel > 0 ? 1 : 0; | |
278 | |
292 } else if (name == "Min Frequency") { | 279 } else if (name == "Min Frequency") { |
293 | 280 |
294 *min = 0; | 281 *min = 0; |
295 *max = 9; | 282 *max = 9; |
296 | 283 |
396 case 2: return tr("1/2"); | 383 case 2: return tr("1/2"); |
397 case 3: return tr("1/4"); | 384 case 3: return tr("1/4"); |
398 case 4: return tr("1/8"); | 385 case 4: return tr("1/8"); |
399 case 5: return tr("1/16"); | 386 case 5: return tr("1/16"); |
400 } | 387 } |
388 } | |
389 if (name == "Zero Padding") { | |
390 if (value == 0) return tr("None"); | |
391 return QString("%1x").arg(value + 1); | |
401 } | 392 } |
402 if (name == "Min Frequency") { | 393 if (name == "Min Frequency") { |
403 switch (value) { | 394 switch (value) { |
404 default: | 395 default: |
405 case 0: return tr("No min"); | 396 case 0: return tr("No min"); |
472 setWindowType(WindowType(value)); | 463 setWindowType(WindowType(value)); |
473 } else if (name == "Window Size") { | 464 } else if (name == "Window Size") { |
474 setWindowSize(32 << value); | 465 setWindowSize(32 << value); |
475 } else if (name == "Window Increment") { | 466 } else if (name == "Window Increment") { |
476 setWindowHopLevel(value); | 467 setWindowHopLevel(value); |
468 } else if (name == "Zero Padding") { | |
469 setZeroPadLevel(value > 0.1 ? 3 : 0); | |
477 } else if (name == "Min Frequency") { | 470 } else if (name == "Min Frequency") { |
478 switch (value) { | 471 switch (value) { |
479 default: | 472 default: |
480 case 0: setMinFrequency(0); break; | 473 case 0: setMinFrequency(0); break; |
481 case 1: setMinFrequency(10); break; | 474 case 1: setMinFrequency(10); break; |
583 m_mutex.lock(); | 576 m_mutex.lock(); |
584 m_cacheInvalid = true; | 577 m_cacheInvalid = true; |
585 invalidatePixmapCaches(); | 578 invalidatePixmapCaches(); |
586 | 579 |
587 m_windowSize = ws; | 580 m_windowSize = ws; |
588 m_fftSize = ws; | 581 m_fftSize = ws * (m_zeroPadLevel + 1); |
589 | 582 |
590 m_mutex.unlock(); | 583 m_mutex.unlock(); |
591 | 584 |
592 emit layerParametersChanged(); | 585 emit layerParametersChanged(); |
593 | 586 |
620 | 613 |
621 size_t | 614 size_t |
622 SpectrogramLayer::getWindowHopLevel() const | 615 SpectrogramLayer::getWindowHopLevel() const |
623 { | 616 { |
624 return m_windowHopLevel; | 617 return m_windowHopLevel; |
618 } | |
619 | |
620 void | |
621 SpectrogramLayer::setZeroPadLevel(size_t v) | |
622 { | |
623 if (m_zeroPadLevel == v) return; | |
624 | |
625 m_mutex.lock(); | |
626 m_cacheInvalid = true; | |
627 invalidatePixmapCaches(); | |
628 | |
629 m_zeroPadLevel = v; | |
630 m_fftSize = m_windowSize * (v + 1); | |
631 | |
632 m_mutex.unlock(); | |
633 | |
634 emit layerParametersChanged(); | |
635 | |
636 fillCache(); | |
637 } | |
638 | |
639 size_t | |
640 SpectrogramLayer::getZeroPadLevel() const | |
641 { | |
642 return m_zeroPadLevel; | |
625 } | 643 } |
626 | 644 |
627 void | 645 void |
628 SpectrogramLayer::setWindowType(WindowType w) | 646 SpectrogramLayer::setWindowType(WindowType w) |
629 { | 647 { |
1148 input[off + i] = 0.0; | 1166 input[off + i] = 0.0; |
1149 } | 1167 } |
1150 } | 1168 } |
1151 | 1169 |
1152 size_t got = m_model->getValues(m_channel, startFrame + pfx, | 1170 size_t got = m_model->getValues(m_channel, startFrame + pfx, |
1153 endFrame, input + pfx); | 1171 endFrame, input + off + pfx); |
1172 | |
1154 while (got + pfx < windowSize) { | 1173 while (got + pfx < windowSize) { |
1155 input[off + got + pfx] = 0.0; | 1174 input[off + got + pfx] = 0.0; |
1156 ++got; | 1175 ++got; |
1157 } | 1176 } |
1158 | 1177 |
1163 input[off + i] /= channels; | 1182 input[off + i] /= channels; |
1164 } | 1183 } |
1165 } | 1184 } |
1166 } | 1185 } |
1167 | 1186 |
1168 windower.cut(input); | 1187 windower.cut(input + off); |
1169 | 1188 |
1170 for (size_t i = 0; i < windowSize/2; ++i) { | 1189 for (size_t i = 0; i < fftSize/2; ++i) { |
1171 fftsample temp = input[off + i]; | 1190 fftsample temp = input[i]; |
1172 input[off + i] = input[off + i + windowSize/2]; | 1191 input[i] = input[i + fftSize/2]; |
1173 input[off + i + windowSize/2] = temp; | 1192 input[i + fftSize/2] = temp; |
1174 } | 1193 } |
1175 | 1194 |
1176 fftwf_execute(plan); | 1195 fftwf_execute(plan); |
1177 | 1196 |
1178 fftsample factor = 0.0; | 1197 fftsample factor = 0.0; |
1179 | 1198 |
1180 for (size_t i = 0; i < fftSize/2; ++i) { | 1199 for (size_t i = 0; i < fftSize/2; ++i) { |
1181 | 1200 |
1182 fftsample mag = sqrtf(output[i][0] * output[i][0] + | 1201 fftsample mag = sqrtf(output[i][0] * output[i][0] + |
1183 output[i][1] * output[i][1]); | 1202 output[i][1] * output[i][1]); |
1184 mag /= fftSize / 2; | 1203 mag /= windowSize / 2; |
1185 | 1204 |
1186 if (mag > factor) factor = mag; | 1205 if (mag > factor) factor = mag; |
1187 | 1206 |
1188 fftsample phase = atan2f(output[i][1], output[i][0]); | 1207 fftsample phase = atan2f(output[i][1], output[i][0]); |
1189 phase = princargf(phase); | 1208 phase = princargf(phase); |
1339 // m_layer.m_cache = new FFTMemoryCache; | 1358 // m_layer.m_cache = new FFTMemoryCache; |
1340 // } | 1359 // } |
1341 if (!m_layer.m_writeCache) { | 1360 if (!m_layer.m_writeCache) { |
1342 m_layer.m_writeCache = new FFTFileCache | 1361 m_layer.m_writeCache = new FFTFileCache |
1343 (QString("%1").arg(getObjectExportId(&m_layer)), | 1362 (QString("%1").arg(getObjectExportId(&m_layer)), |
1344 MatrixFile::ReadWrite); | 1363 MatrixFile::ReadWrite, true); |
1345 } | 1364 } |
1346 m_layer.m_writeCache->resize(width, height); | 1365 m_layer.m_writeCache->resize(width, height); |
1347 if (m_layer.m_cache) delete m_layer.m_cache; | 1366 if (m_layer.m_cache) delete m_layer.m_cache; |
1348 m_layer.m_cache = new FFTFileCache | 1367 m_layer.m_cache = new FFTFileCache |
1349 (QString("%1").arg(getObjectExportId(&m_layer)), | 1368 (QString("%1").arg(getObjectExportId(&m_layer)), |
1350 MatrixFile::ReadOnly); | 1369 MatrixFile::ReadOnly, true); |
1351 | 1370 |
1352 m_layer.setColourmap(); | 1371 m_layer.setColourmap(); |
1353 //!!! m_layer.m_writeCache->reset(); | 1372 //!!! m_layer.m_writeCache->reset(); |
1354 | 1373 |
1355 fftsample *input = (fftsample *) | 1374 fftsample *input = (fftsample *) |