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 *)