Mercurial > hg > sonic-visualiser
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 | 
