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