comparison main/PreferencesDialog.cpp @ 0:cd5d7ff8ef38

* Reorganising code base. This revision will not compile.
author Chris Cannam
date Mon, 31 Jul 2006 12:03:45 +0000
parents
children 40116f709d3b
comparison
equal deleted inserted replaced
-1:000000000000 0:cd5d7ff8ef38
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Sonic Visualiser
5 An audio file viewer and annotation editor.
6 Centre for Digital Music, Queen Mary, University of London.
7 This file copyright 2006 Chris Cannam.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file
13 COPYING included with this distribution for more information.
14 */
15
16 #include "PreferencesDialog.h"
17
18 #include <QGridLayout>
19 #include <QComboBox>
20 #include <QCheckBox>
21 #include <QGroupBox>
22 #include <QDoubleSpinBox>
23 #include <QLabel>
24 #include <QPushButton>
25 #include <QHBoxLayout>
26 #include <QPainter>
27 #include <QPainterPath>
28 #include <QFont>
29 #include <QString>
30
31 #include <fftw3.h>
32
33 #include "base/Preferences.h"
34 #include "fileio/ConfigFile.h"
35
36 PreferencesDialog::PreferencesDialog(QWidget *parent, Qt::WFlags flags) :
37 QDialog(parent, flags)
38 {
39 setWindowTitle(tr("Application Preferences"));
40
41 Preferences *prefs = Preferences::getInstance();
42
43 QGridLayout *grid = new QGridLayout;
44 setLayout(grid);
45
46 QGroupBox *groupBox = new QGroupBox;
47 groupBox->setTitle(tr("Sonic Visualiser Application Preferences"));
48 grid->addWidget(groupBox, 0, 0);
49
50 QGridLayout *subgrid = new QGridLayout;
51 groupBox->setLayout(subgrid);
52
53 // Create this first, as slots that get called from the ctor will
54 // refer to it
55 m_applyButton = new QPushButton(tr("Apply"));
56
57 // The WindowType enum is in rather a ragbag order -- reorder it here
58 // in a more sensible order
59 m_windows = new WindowType[9];
60 m_windows[0] = HanningWindow;
61 m_windows[1] = HammingWindow;
62 m_windows[2] = BlackmanWindow;
63 m_windows[3] = BlackmanHarrisWindow;
64 m_windows[4] = NuttallWindow;
65 m_windows[5] = GaussianWindow;
66 m_windows[6] = ParzenWindow;
67 m_windows[7] = BartlettWindow;
68 m_windows[8] = RectangularWindow;
69
70 QComboBox *windowCombo = new QComboBox;
71 int min, max, i;
72 int window = prefs->getPropertyRangeAndValue("Window Type", &min, &max);
73 m_windowType = window;
74 int index = 0;
75
76 for (i = 0; i <= 8; ++i) {
77 windowCombo->addItem(prefs->getPropertyValueLabel("Window Type",
78 m_windows[i]));
79 if (m_windows[i] == window) index = i;
80 }
81
82 windowCombo->setCurrentIndex(index);
83
84 m_windowTimeExampleLabel = new QLabel;
85 m_windowFreqExampleLabel = new QLabel;
86
87 connect(windowCombo, SIGNAL(currentIndexChanged(int)),
88 this, SLOT(windowTypeChanged(int)));
89 windowTypeChanged(index);
90
91 QCheckBox *smoothing = new QCheckBox;
92 m_smoothSpectrogram = prefs->getSmoothSpectrogram();
93 smoothing->setCheckState(m_smoothSpectrogram ?
94 Qt::Checked : Qt::Unchecked);
95
96 connect(smoothing, SIGNAL(stateChanged(int)),
97 this, SLOT(smoothSpectrogramChanged(int)));
98
99 QComboBox *propertyLayout = new QComboBox;
100 int pl = prefs->getPropertyRangeAndValue("Property Box Layout", &min, &max);
101 m_propertyLayout = pl;
102
103 for (i = min; i <= max; ++i) {
104 propertyLayout->addItem(prefs->getPropertyValueLabel("Property Box Layout", i));
105 }
106
107 propertyLayout->setCurrentIndex(pl);
108
109 connect(propertyLayout, SIGNAL(currentIndexChanged(int)),
110 this, SLOT(propertyLayoutChanged(int)));
111
112 m_tuningFrequency = prefs->getTuningFrequency();
113
114 QDoubleSpinBox *frequency = new QDoubleSpinBox;
115 frequency->setMinimum(100.0);
116 frequency->setMaximum(5000.0);
117 frequency->setSuffix(" Hz");
118 frequency->setSingleStep(1);
119 frequency->setValue(m_tuningFrequency);
120 frequency->setDecimals(2);
121
122 connect(frequency, SIGNAL(valueChanged(double)),
123 this, SLOT(tuningFrequencyChanged(double)));
124
125 int row = 0;
126
127 subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel
128 ("Property Box Layout"))),
129 row, 0);
130 subgrid->addWidget(propertyLayout, row++, 1, 1, 2);
131
132 subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel
133 ("Tuning Frequency"))),
134 row, 0);
135 subgrid->addWidget(frequency, row++, 1, 1, 2);
136
137 subgrid->addWidget(new QLabel(prefs->getPropertyLabel
138 ("Smooth Spectrogram")),
139 row, 0, 1, 2);
140 subgrid->addWidget(smoothing, row++, 2);
141
142 subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel
143 ("Window Type"))),
144 row, 0);
145 subgrid->addWidget(windowCombo, row++, 1, 1, 2);
146
147 subgrid->addWidget(m_windowTimeExampleLabel, row, 1);
148 subgrid->addWidget(m_windowFreqExampleLabel, row, 2);
149
150 QHBoxLayout *hbox = new QHBoxLayout;
151 grid->addLayout(hbox, 1, 0);
152
153 QPushButton *ok = new QPushButton(tr("OK"));
154 QPushButton *cancel = new QPushButton(tr("Cancel"));
155 hbox->addStretch(10);
156 hbox->addWidget(ok);
157 hbox->addWidget(m_applyButton);
158 hbox->addWidget(cancel);
159 connect(ok, SIGNAL(clicked()), this, SLOT(okClicked()));
160 connect(m_applyButton, SIGNAL(clicked()), this, SLOT(applyClicked()));
161 connect(cancel, SIGNAL(clicked()), this, SLOT(cancelClicked()));
162
163 m_applyButton->setEnabled(false);
164 }
165
166 PreferencesDialog::~PreferencesDialog()
167 {
168 std::cerr << "PreferencesDialog::~PreferencesDialog()" << std::endl;
169
170 delete[] m_windows;
171 }
172
173 void
174 PreferencesDialog::windowTypeChanged(int value)
175 {
176 int step = 24;
177 int peak = 48;
178 int w = step * 4, h = 64;
179 WindowType type = m_windows[value];
180 Window<float> windower = Window<float>(type, step * 2);
181
182 QPixmap timeLabel(w, h + 1);
183 timeLabel.fill(Qt::white);
184 QPainter timePainter(&timeLabel);
185
186 QPainterPath path;
187
188 path.moveTo(0, h - peak + 1);
189 path.lineTo(w, h - peak + 1);
190
191 timePainter.setPen(Qt::gray);
192 timePainter.setRenderHint(QPainter::Antialiasing, true);
193 timePainter.drawPath(path);
194
195 path = QPainterPath();
196
197 float acc[w];
198 for (int i = 0; i < w; ++i) acc[i] = 0.f;
199 for (int j = 0; j < 3; ++j) {
200 for (int i = 0; i < step * 2; ++i) {
201 acc[j * step + i] += windower.getValue(i);
202 }
203 }
204 for (int i = 0; i < w; ++i) {
205 int y = h - int(peak * acc[i] + 0.001) + 1;
206 if (i == 0) path.moveTo(i, y);
207 else path.lineTo(i, y);
208 }
209
210 timePainter.drawPath(path);
211 timePainter.setRenderHint(QPainter::Antialiasing, false);
212
213 path = QPainterPath();
214
215 timePainter.setPen(Qt::black);
216
217 for (int i = 0; i < step * 2; ++i) {
218 int y = h - int(peak * windower.getValue(i) + 0.001) + 1;
219 if (i == 0) path.moveTo(i + step, float(y));
220 else path.lineTo(i + step, float(y));
221 }
222
223 if (type == RectangularWindow) {
224 timePainter.drawPath(path);
225 path = QPainterPath();
226 }
227
228 timePainter.setRenderHint(QPainter::Antialiasing, true);
229 path.addRect(0, 0, w, h + 1);
230 timePainter.drawPath(path);
231
232 QFont font;
233 font.setPixelSize(10);
234 font.setItalic(true);
235 timePainter.setFont(font);
236 QString label = tr("V / time");
237 timePainter.drawText(w - timePainter.fontMetrics().width(label) - 4,
238 timePainter.fontMetrics().ascent() + 1, label);
239
240 m_windowTimeExampleLabel->setPixmap(timeLabel);
241
242 int fw = 100;
243
244 QPixmap freqLabel(fw, h + 1);
245 freqLabel.fill(Qt::white);
246 QPainter freqPainter(&freqLabel);
247 path = QPainterPath();
248
249 size_t fftsize = 512;
250
251 float *input = (float *)fftwf_malloc(fftsize * sizeof(float));
252 fftwf_complex *output =
253 (fftwf_complex *)fftwf_malloc(fftsize * sizeof(fftwf_complex));
254 fftwf_plan plan = fftwf_plan_dft_r2c_1d(fftsize, input, output,
255 FFTW_ESTIMATE);
256 for (int i = 0; i < fftsize; ++i) input[i] = 0.f;
257 for (int i = 0; i < step * 2; ++i) {
258 input[fftsize/2 - step + i] = windower.getValue(i);
259 }
260
261 fftwf_execute(plan);
262 fftwf_destroy_plan(plan);
263
264 float maxdb = 0.f;
265 float mindb = 0.f;
266 bool first = true;
267 for (int i = 0; i < fftsize/2; ++i) {
268 float power = output[i][0] * output[i][0] + output[i][1] * output[i][1];
269 float db = mindb;
270 if (power > 0) {
271 db = 20 * log10(power);
272 if (first || db > maxdb) maxdb = db;
273 if (first || db < mindb) mindb = db;
274 first = false;
275 }
276 }
277
278 if (mindb > -80.f) mindb = -80.f;
279
280 // -- no, don't use the actual mindb -- it's easier to compare
281 // plots with a fixed min value
282 mindb = -170.f;
283
284 float maxval = maxdb + -mindb;
285
286 float ly = h - ((-80.f + -mindb) / maxval) * peak + 1;
287
288 path.moveTo(0, h - peak + 1);
289 path.lineTo(fw, h - peak + 1);
290
291 freqPainter.setPen(Qt::gray);
292 freqPainter.setRenderHint(QPainter::Antialiasing, true);
293 freqPainter.drawPath(path);
294
295 path = QPainterPath();
296 freqPainter.setPen(Qt::black);
297
298 // std::cerr << "maxdb = " << maxdb << ", mindb = " << mindb << ", maxval = " <<maxval << std::endl;
299
300 for (int i = 0; i < fftsize/2; ++i) {
301 float power = output[i][0] * output[i][0] + output[i][1] * output[i][1];
302 float db = 20 * log10(power);
303 float val = db + -mindb;
304 if (val < 0) val = 0;
305 float norm = val / maxval;
306 float x = (fw / float(fftsize/2)) * i;
307 float y = h - norm * peak + 1;
308 if (i == 0) path.moveTo(x, y);
309 else path.lineTo(x, y);
310 }
311
312 freqPainter.setRenderHint(QPainter::Antialiasing, true);
313 path.addRect(0, 0, fw, h + 1);
314 freqPainter.drawPath(path);
315
316 fftwf_free(input);
317 fftwf_free(output);
318
319 freqPainter.setFont(font);
320 label = tr("dB / freq");
321 freqPainter.drawText(fw - freqPainter.fontMetrics().width(label) - 4,
322 freqPainter.fontMetrics().ascent() + 1, label);
323
324 m_windowFreqExampleLabel->setPixmap(freqLabel);
325
326 m_windowType = type;
327 m_applyButton->setEnabled(true);
328 }
329
330 void
331 PreferencesDialog::smoothSpectrogramChanged(int state)
332 {
333 m_smoothSpectrogram = (state == Qt::Checked);
334 m_applyButton->setEnabled(true);
335 }
336
337 void
338 PreferencesDialog::propertyLayoutChanged(int layout)
339 {
340 m_propertyLayout = layout;
341 m_applyButton->setEnabled(true);
342 }
343
344 void
345 PreferencesDialog::tuningFrequencyChanged(double freq)
346 {
347 m_tuningFrequency = freq;
348 m_applyButton->setEnabled(true);
349 }
350
351 void
352 PreferencesDialog::okClicked()
353 {
354 applyClicked();
355 Preferences::getInstance()->getConfigFile()->commit();
356 accept();
357 }
358
359 void
360 PreferencesDialog::applyClicked()
361 {
362 Preferences *prefs = Preferences::getInstance();
363 prefs->setWindowType(WindowType(m_windowType));
364 prefs->setSmoothSpectrogram(m_smoothSpectrogram);
365 prefs->setPropertyBoxLayout(Preferences::PropertyBoxLayout
366 (m_propertyLayout));
367 prefs->setTuningFrequency(m_tuningFrequency);
368 m_applyButton->setEnabled(false);
369 }
370
371 void
372 PreferencesDialog::cancelClicked()
373 {
374 reject();
375 }
376