Mercurial > hg > sonic-visualiser
comparison main/PreferencesDialog.cpp @ 9:8b34a6460545
* Pull window type selector and shape preview out into their own widgets
(from the preferences dialog)
author | Chris Cannam |
---|---|
date | Mon, 11 Sep 2006 15:32:49 +0000 |
parents | d4487202d0e8 |
children | e3b32dc5180b |
comparison
equal
deleted
inserted
replaced
8:92cb01225e7a | 9:8b34a6460545 |
---|---|
21 #include <QGroupBox> | 21 #include <QGroupBox> |
22 #include <QDoubleSpinBox> | 22 #include <QDoubleSpinBox> |
23 #include <QLabel> | 23 #include <QLabel> |
24 #include <QPushButton> | 24 #include <QPushButton> |
25 #include <QHBoxLayout> | 25 #include <QHBoxLayout> |
26 #include <QPainter> | |
27 #include <QPainterPath> | |
28 #include <QFont> | |
29 #include <QString> | 26 #include <QString> |
30 | 27 |
31 #include <fftw3.h> | 28 #include <fftw3.h> |
32 | 29 |
30 #include "widgets/WindowTypeSelector.h" | |
33 #include "base/Preferences.h" | 31 #include "base/Preferences.h" |
34 | 32 |
35 PreferencesDialog::PreferencesDialog(QWidget *parent, Qt::WFlags flags) : | 33 PreferencesDialog::PreferencesDialog(QWidget *parent, Qt::WFlags flags) : |
36 QDialog(parent, flags) | 34 QDialog(parent, flags) |
37 { | 35 { |
51 | 49 |
52 // Create this first, as slots that get called from the ctor will | 50 // Create this first, as slots that get called from the ctor will |
53 // refer to it | 51 // refer to it |
54 m_applyButton = new QPushButton(tr("Apply")); | 52 m_applyButton = new QPushButton(tr("Apply")); |
55 | 53 |
56 // The WindowType enum is in rather a ragbag order -- reorder it here | 54 int min, max, i; |
57 // in a more sensible order | |
58 m_windows = new WindowType[9]; | |
59 m_windows[0] = HanningWindow; | |
60 m_windows[1] = HammingWindow; | |
61 m_windows[2] = BlackmanWindow; | |
62 m_windows[3] = BlackmanHarrisWindow; | |
63 m_windows[4] = NuttallWindow; | |
64 m_windows[5] = GaussianWindow; | |
65 m_windows[6] = ParzenWindow; | |
66 m_windows[7] = BartlettWindow; | |
67 m_windows[8] = RectangularWindow; | |
68 | 55 |
69 QComboBox *windowCombo = new QComboBox; | 56 m_windowType = WindowType(prefs->getPropertyRangeAndValue |
70 int min, max, i; | 57 ("Window Type", &min, &max)); |
71 int window = prefs->getPropertyRangeAndValue("Window Type", &min, &max); | 58 m_windowTypeSelector = new WindowTypeSelector(m_windowType); |
72 m_windowType = window; | |
73 int index = 0; | |
74 | |
75 for (i = 0; i <= 8; ++i) { | |
76 windowCombo->addItem(prefs->getPropertyValueLabel("Window Type", | |
77 m_windows[i])); | |
78 if (m_windows[i] == window) index = i; | |
79 } | |
80 | 59 |
81 windowCombo->setCurrentIndex(index); | 60 connect(m_windowTypeSelector, SIGNAL(windowTypeChanged(WindowType)), |
82 | 61 this, SLOT(windowTypeChanged(WindowType))); |
83 m_windowTimeExampleLabel = new QLabel; | |
84 m_windowFreqExampleLabel = new QLabel; | |
85 | |
86 connect(windowCombo, SIGNAL(currentIndexChanged(int)), | |
87 this, SLOT(windowTypeChanged(int))); | |
88 windowTypeChanged(index); | |
89 | 62 |
90 QCheckBox *smoothing = new QCheckBox; | 63 QCheckBox *smoothing = new QCheckBox; |
91 m_smoothSpectrogram = prefs->getSmoothSpectrogram(); | 64 m_smoothSpectrogram = prefs->getSmoothSpectrogram(); |
92 smoothing->setCheckState(m_smoothSpectrogram ? | 65 smoothing->setCheckState(m_smoothSpectrogram ? |
93 Qt::Checked : Qt::Unchecked); | 66 Qt::Checked : Qt::Unchecked); |
139 subgrid->addWidget(smoothing, row++, 2); | 112 subgrid->addWidget(smoothing, row++, 2); |
140 | 113 |
141 subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel | 114 subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel |
142 ("Window Type"))), | 115 ("Window Type"))), |
143 row, 0); | 116 row, 0); |
144 subgrid->addWidget(windowCombo, row++, 1, 1, 2); | 117 subgrid->addWidget(m_windowTypeSelector, row++, 1, 2, 2); |
145 | 118 subgrid->setRowStretch(row, 10); |
146 subgrid->addWidget(m_windowTimeExampleLabel, row, 1); | 119 row++; |
147 subgrid->addWidget(m_windowFreqExampleLabel, row, 2); | |
148 | 120 |
149 QHBoxLayout *hbox = new QHBoxLayout; | 121 QHBoxLayout *hbox = new QHBoxLayout; |
150 grid->addLayout(hbox, 1, 0); | 122 grid->addLayout(hbox, 1, 0); |
151 | 123 |
152 QPushButton *ok = new QPushButton(tr("OK")); | 124 QPushButton *ok = new QPushButton(tr("OK")); |
163 } | 135 } |
164 | 136 |
165 PreferencesDialog::~PreferencesDialog() | 137 PreferencesDialog::~PreferencesDialog() |
166 { | 138 { |
167 std::cerr << "PreferencesDialog::~PreferencesDialog()" << std::endl; | 139 std::cerr << "PreferencesDialog::~PreferencesDialog()" << std::endl; |
168 | |
169 delete[] m_windows; | |
170 } | 140 } |
171 | 141 |
172 void | 142 void |
173 PreferencesDialog::windowTypeChanged(int value) | 143 PreferencesDialog::windowTypeChanged(WindowType type) |
174 { | 144 { |
175 int step = 24; | |
176 int peak = 48; | |
177 int w = step * 4, h = 64; | |
178 WindowType type = m_windows[value]; | |
179 Window<float> windower = Window<float>(type, step * 2); | |
180 | |
181 QPixmap timeLabel(w, h + 1); | |
182 timeLabel.fill(Qt::white); | |
183 QPainter timePainter(&timeLabel); | |
184 | |
185 QPainterPath path; | |
186 | |
187 path.moveTo(0, h - peak + 1); | |
188 path.lineTo(w, h - peak + 1); | |
189 | |
190 timePainter.setPen(Qt::gray); | |
191 timePainter.setRenderHint(QPainter::Antialiasing, true); | |
192 timePainter.drawPath(path); | |
193 | |
194 path = QPainterPath(); | |
195 | |
196 float acc[w]; | |
197 for (int i = 0; i < w; ++i) acc[i] = 0.f; | |
198 for (int j = 0; j < 3; ++j) { | |
199 for (int i = 0; i < step * 2; ++i) { | |
200 acc[j * step + i] += windower.getValue(i); | |
201 } | |
202 } | |
203 for (int i = 0; i < w; ++i) { | |
204 int y = h - int(peak * acc[i] + 0.001) + 1; | |
205 if (i == 0) path.moveTo(i, y); | |
206 else path.lineTo(i, y); | |
207 } | |
208 | |
209 timePainter.drawPath(path); | |
210 timePainter.setRenderHint(QPainter::Antialiasing, false); | |
211 | |
212 path = QPainterPath(); | |
213 | |
214 timePainter.setPen(Qt::black); | |
215 | |
216 for (int i = 0; i < step * 2; ++i) { | |
217 int y = h - int(peak * windower.getValue(i) + 0.001) + 1; | |
218 if (i == 0) path.moveTo(i + step, float(y)); | |
219 else path.lineTo(i + step, float(y)); | |
220 } | |
221 | |
222 if (type == RectangularWindow) { | |
223 timePainter.drawPath(path); | |
224 path = QPainterPath(); | |
225 } | |
226 | |
227 timePainter.setRenderHint(QPainter::Antialiasing, true); | |
228 path.addRect(0, 0, w, h + 1); | |
229 timePainter.drawPath(path); | |
230 | |
231 QFont font; | |
232 font.setPixelSize(10); | |
233 font.setItalic(true); | |
234 timePainter.setFont(font); | |
235 QString label = tr("V / time"); | |
236 timePainter.drawText(w - timePainter.fontMetrics().width(label) - 4, | |
237 timePainter.fontMetrics().ascent() + 1, label); | |
238 | |
239 m_windowTimeExampleLabel->setPixmap(timeLabel); | |
240 | |
241 int fw = 100; | |
242 | |
243 QPixmap freqLabel(fw, h + 1); | |
244 freqLabel.fill(Qt::white); | |
245 QPainter freqPainter(&freqLabel); | |
246 path = QPainterPath(); | |
247 | |
248 size_t fftsize = 512; | |
249 | |
250 float *input = (float *)fftwf_malloc(fftsize * sizeof(float)); | |
251 fftwf_complex *output = | |
252 (fftwf_complex *)fftwf_malloc(fftsize * sizeof(fftwf_complex)); | |
253 fftwf_plan plan = fftwf_plan_dft_r2c_1d(fftsize, input, output, | |
254 FFTW_ESTIMATE); | |
255 for (int i = 0; i < fftsize; ++i) input[i] = 0.f; | |
256 for (int i = 0; i < step * 2; ++i) { | |
257 input[fftsize/2 - step + i] = windower.getValue(i); | |
258 } | |
259 | |
260 fftwf_execute(plan); | |
261 fftwf_destroy_plan(plan); | |
262 | |
263 float maxdb = 0.f; | |
264 float mindb = 0.f; | |
265 bool first = true; | |
266 for (int i = 0; i < fftsize/2; ++i) { | |
267 float power = output[i][0] * output[i][0] + output[i][1] * output[i][1]; | |
268 float db = mindb; | |
269 if (power > 0) { | |
270 db = 20 * log10(power); | |
271 if (first || db > maxdb) maxdb = db; | |
272 if (first || db < mindb) mindb = db; | |
273 first = false; | |
274 } | |
275 } | |
276 | |
277 if (mindb > -80.f) mindb = -80.f; | |
278 | |
279 // -- no, don't use the actual mindb -- it's easier to compare | |
280 // plots with a fixed min value | |
281 mindb = -170.f; | |
282 | |
283 float maxval = maxdb + -mindb; | |
284 | |
285 float ly = h - ((-80.f + -mindb) / maxval) * peak + 1; | |
286 | |
287 path.moveTo(0, h - peak + 1); | |
288 path.lineTo(fw, h - peak + 1); | |
289 | |
290 freqPainter.setPen(Qt::gray); | |
291 freqPainter.setRenderHint(QPainter::Antialiasing, true); | |
292 freqPainter.drawPath(path); | |
293 | |
294 path = QPainterPath(); | |
295 freqPainter.setPen(Qt::black); | |
296 | |
297 // std::cerr << "maxdb = " << maxdb << ", mindb = " << mindb << ", maxval = " <<maxval << std::endl; | |
298 | |
299 for (int i = 0; i < fftsize/2; ++i) { | |
300 float power = output[i][0] * output[i][0] + output[i][1] * output[i][1]; | |
301 float db = 20 * log10(power); | |
302 float val = db + -mindb; | |
303 if (val < 0) val = 0; | |
304 float norm = val / maxval; | |
305 float x = (fw / float(fftsize/2)) * i; | |
306 float y = h - norm * peak + 1; | |
307 if (i == 0) path.moveTo(x, y); | |
308 else path.lineTo(x, y); | |
309 } | |
310 | |
311 freqPainter.setRenderHint(QPainter::Antialiasing, true); | |
312 path.addRect(0, 0, fw, h + 1); | |
313 freqPainter.drawPath(path); | |
314 | |
315 fftwf_free(input); | |
316 fftwf_free(output); | |
317 | |
318 freqPainter.setFont(font); | |
319 label = tr("dB / freq"); | |
320 freqPainter.drawText(fw - freqPainter.fontMetrics().width(label) - 4, | |
321 freqPainter.fontMetrics().ascent() + 1, label); | |
322 | |
323 m_windowFreqExampleLabel->setPixmap(freqLabel); | |
324 | |
325 m_windowType = type; | 145 m_windowType = type; |
326 m_applyButton->setEnabled(true); | 146 m_applyButton->setEnabled(true); |
327 } | 147 } |
328 | 148 |
329 void | 149 void |