Chris@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: /* Chris@0: Sonic Visualiser Chris@0: An audio file viewer and annotation editor. Chris@0: Centre for Digital Music, Queen Mary, University of London. Chris@0: This file copyright 2006 Chris Cannam. Chris@0: Chris@0: This program is free software; you can redistribute it and/or Chris@0: modify it under the terms of the GNU General Public License as Chris@0: published by the Free Software Foundation; either version 2 of the Chris@0: License, or (at your option) any later version. See the file Chris@0: COPYING included with this distribution for more information. Chris@0: */ Chris@0: Chris@0: #include "PreferencesDialog.h" Chris@0: Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: Chris@0: #include Chris@0: Chris@0: #include "base/Preferences.h" Chris@1: #include "base/ConfigFile.h" Chris@0: Chris@0: PreferencesDialog::PreferencesDialog(QWidget *parent, Qt::WFlags flags) : Chris@0: QDialog(parent, flags) Chris@0: { Chris@0: setWindowTitle(tr("Application Preferences")); Chris@0: Chris@0: Preferences *prefs = Preferences::getInstance(); Chris@0: Chris@0: QGridLayout *grid = new QGridLayout; Chris@0: setLayout(grid); Chris@0: Chris@0: QGroupBox *groupBox = new QGroupBox; Chris@0: groupBox->setTitle(tr("Sonic Visualiser Application Preferences")); Chris@0: grid->addWidget(groupBox, 0, 0); Chris@0: Chris@0: QGridLayout *subgrid = new QGridLayout; Chris@0: groupBox->setLayout(subgrid); Chris@0: Chris@0: // Create this first, as slots that get called from the ctor will Chris@0: // refer to it Chris@0: m_applyButton = new QPushButton(tr("Apply")); Chris@0: Chris@0: // The WindowType enum is in rather a ragbag order -- reorder it here Chris@0: // in a more sensible order Chris@0: m_windows = new WindowType[9]; Chris@0: m_windows[0] = HanningWindow; Chris@0: m_windows[1] = HammingWindow; Chris@0: m_windows[2] = BlackmanWindow; Chris@0: m_windows[3] = BlackmanHarrisWindow; Chris@0: m_windows[4] = NuttallWindow; Chris@0: m_windows[5] = GaussianWindow; Chris@0: m_windows[6] = ParzenWindow; Chris@0: m_windows[7] = BartlettWindow; Chris@0: m_windows[8] = RectangularWindow; Chris@0: Chris@0: QComboBox *windowCombo = new QComboBox; Chris@0: int min, max, i; Chris@0: int window = prefs->getPropertyRangeAndValue("Window Type", &min, &max); Chris@0: m_windowType = window; Chris@0: int index = 0; Chris@0: Chris@0: for (i = 0; i <= 8; ++i) { Chris@0: windowCombo->addItem(prefs->getPropertyValueLabel("Window Type", Chris@0: m_windows[i])); Chris@0: if (m_windows[i] == window) index = i; Chris@0: } Chris@0: Chris@0: windowCombo->setCurrentIndex(index); Chris@0: Chris@0: m_windowTimeExampleLabel = new QLabel; Chris@0: m_windowFreqExampleLabel = new QLabel; Chris@0: Chris@0: connect(windowCombo, SIGNAL(currentIndexChanged(int)), Chris@0: this, SLOT(windowTypeChanged(int))); Chris@0: windowTypeChanged(index); Chris@0: Chris@0: QCheckBox *smoothing = new QCheckBox; Chris@0: m_smoothSpectrogram = prefs->getSmoothSpectrogram(); Chris@0: smoothing->setCheckState(m_smoothSpectrogram ? Chris@0: Qt::Checked : Qt::Unchecked); Chris@0: Chris@0: connect(smoothing, SIGNAL(stateChanged(int)), Chris@0: this, SLOT(smoothSpectrogramChanged(int))); Chris@0: Chris@0: QComboBox *propertyLayout = new QComboBox; Chris@0: int pl = prefs->getPropertyRangeAndValue("Property Box Layout", &min, &max); Chris@0: m_propertyLayout = pl; Chris@0: Chris@0: for (i = min; i <= max; ++i) { Chris@0: propertyLayout->addItem(prefs->getPropertyValueLabel("Property Box Layout", i)); Chris@0: } Chris@0: Chris@0: propertyLayout->setCurrentIndex(pl); Chris@0: Chris@0: connect(propertyLayout, SIGNAL(currentIndexChanged(int)), Chris@0: this, SLOT(propertyLayoutChanged(int))); Chris@0: Chris@0: m_tuningFrequency = prefs->getTuningFrequency(); Chris@0: Chris@0: QDoubleSpinBox *frequency = new QDoubleSpinBox; Chris@0: frequency->setMinimum(100.0); Chris@0: frequency->setMaximum(5000.0); Chris@0: frequency->setSuffix(" Hz"); Chris@0: frequency->setSingleStep(1); Chris@0: frequency->setValue(m_tuningFrequency); Chris@0: frequency->setDecimals(2); Chris@0: Chris@0: connect(frequency, SIGNAL(valueChanged(double)), Chris@0: this, SLOT(tuningFrequencyChanged(double))); Chris@0: Chris@0: int row = 0; Chris@0: Chris@0: subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel Chris@0: ("Property Box Layout"))), Chris@0: row, 0); Chris@0: subgrid->addWidget(propertyLayout, row++, 1, 1, 2); Chris@0: Chris@0: subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel Chris@0: ("Tuning Frequency"))), Chris@0: row, 0); Chris@0: subgrid->addWidget(frequency, row++, 1, 1, 2); Chris@0: Chris@0: subgrid->addWidget(new QLabel(prefs->getPropertyLabel Chris@0: ("Smooth Spectrogram")), Chris@0: row, 0, 1, 2); Chris@0: subgrid->addWidget(smoothing, row++, 2); Chris@0: Chris@0: subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel Chris@0: ("Window Type"))), Chris@0: row, 0); Chris@0: subgrid->addWidget(windowCombo, row++, 1, 1, 2); Chris@0: Chris@0: subgrid->addWidget(m_windowTimeExampleLabel, row, 1); Chris@0: subgrid->addWidget(m_windowFreqExampleLabel, row, 2); Chris@0: Chris@0: QHBoxLayout *hbox = new QHBoxLayout; Chris@0: grid->addLayout(hbox, 1, 0); Chris@0: Chris@0: QPushButton *ok = new QPushButton(tr("OK")); Chris@0: QPushButton *cancel = new QPushButton(tr("Cancel")); Chris@0: hbox->addStretch(10); Chris@0: hbox->addWidget(ok); Chris@0: hbox->addWidget(m_applyButton); Chris@0: hbox->addWidget(cancel); Chris@0: connect(ok, SIGNAL(clicked()), this, SLOT(okClicked())); Chris@0: connect(m_applyButton, SIGNAL(clicked()), this, SLOT(applyClicked())); Chris@0: connect(cancel, SIGNAL(clicked()), this, SLOT(cancelClicked())); Chris@0: Chris@0: m_applyButton->setEnabled(false); Chris@0: } Chris@0: Chris@0: PreferencesDialog::~PreferencesDialog() Chris@0: { Chris@0: std::cerr << "PreferencesDialog::~PreferencesDialog()" << std::endl; Chris@0: Chris@0: delete[] m_windows; Chris@0: } Chris@0: Chris@0: void Chris@0: PreferencesDialog::windowTypeChanged(int value) Chris@0: { Chris@0: int step = 24; Chris@0: int peak = 48; Chris@0: int w = step * 4, h = 64; Chris@0: WindowType type = m_windows[value]; Chris@0: Window windower = Window(type, step * 2); Chris@0: Chris@0: QPixmap timeLabel(w, h + 1); Chris@0: timeLabel.fill(Qt::white); Chris@0: QPainter timePainter(&timeLabel); Chris@0: Chris@0: QPainterPath path; Chris@0: Chris@0: path.moveTo(0, h - peak + 1); Chris@0: path.lineTo(w, h - peak + 1); Chris@0: Chris@0: timePainter.setPen(Qt::gray); Chris@0: timePainter.setRenderHint(QPainter::Antialiasing, true); Chris@0: timePainter.drawPath(path); Chris@0: Chris@0: path = QPainterPath(); Chris@0: Chris@0: float acc[w]; Chris@0: for (int i = 0; i < w; ++i) acc[i] = 0.f; Chris@0: for (int j = 0; j < 3; ++j) { Chris@0: for (int i = 0; i < step * 2; ++i) { Chris@0: acc[j * step + i] += windower.getValue(i); Chris@0: } Chris@0: } Chris@0: for (int i = 0; i < w; ++i) { Chris@0: int y = h - int(peak * acc[i] + 0.001) + 1; Chris@0: if (i == 0) path.moveTo(i, y); Chris@0: else path.lineTo(i, y); Chris@0: } Chris@0: Chris@0: timePainter.drawPath(path); Chris@0: timePainter.setRenderHint(QPainter::Antialiasing, false); Chris@0: Chris@0: path = QPainterPath(); Chris@0: Chris@0: timePainter.setPen(Qt::black); Chris@0: Chris@0: for (int i = 0; i < step * 2; ++i) { Chris@0: int y = h - int(peak * windower.getValue(i) + 0.001) + 1; Chris@0: if (i == 0) path.moveTo(i + step, float(y)); Chris@0: else path.lineTo(i + step, float(y)); Chris@0: } Chris@0: Chris@0: if (type == RectangularWindow) { Chris@0: timePainter.drawPath(path); Chris@0: path = QPainterPath(); Chris@0: } Chris@0: Chris@0: timePainter.setRenderHint(QPainter::Antialiasing, true); Chris@0: path.addRect(0, 0, w, h + 1); Chris@0: timePainter.drawPath(path); Chris@0: Chris@0: QFont font; Chris@0: font.setPixelSize(10); Chris@0: font.setItalic(true); Chris@0: timePainter.setFont(font); Chris@0: QString label = tr("V / time"); Chris@0: timePainter.drawText(w - timePainter.fontMetrics().width(label) - 4, Chris@0: timePainter.fontMetrics().ascent() + 1, label); Chris@0: Chris@0: m_windowTimeExampleLabel->setPixmap(timeLabel); Chris@0: Chris@0: int fw = 100; Chris@0: Chris@0: QPixmap freqLabel(fw, h + 1); Chris@0: freqLabel.fill(Qt::white); Chris@0: QPainter freqPainter(&freqLabel); Chris@0: path = QPainterPath(); Chris@0: Chris@0: size_t fftsize = 512; Chris@0: Chris@0: float *input = (float *)fftwf_malloc(fftsize * sizeof(float)); Chris@0: fftwf_complex *output = Chris@0: (fftwf_complex *)fftwf_malloc(fftsize * sizeof(fftwf_complex)); Chris@0: fftwf_plan plan = fftwf_plan_dft_r2c_1d(fftsize, input, output, Chris@0: FFTW_ESTIMATE); Chris@0: for (int i = 0; i < fftsize; ++i) input[i] = 0.f; Chris@0: for (int i = 0; i < step * 2; ++i) { Chris@0: input[fftsize/2 - step + i] = windower.getValue(i); Chris@0: } Chris@0: Chris@0: fftwf_execute(plan); Chris@0: fftwf_destroy_plan(plan); Chris@0: Chris@0: float maxdb = 0.f; Chris@0: float mindb = 0.f; Chris@0: bool first = true; Chris@0: for (int i = 0; i < fftsize/2; ++i) { Chris@0: float power = output[i][0] * output[i][0] + output[i][1] * output[i][1]; Chris@0: float db = mindb; Chris@0: if (power > 0) { Chris@0: db = 20 * log10(power); Chris@0: if (first || db > maxdb) maxdb = db; Chris@0: if (first || db < mindb) mindb = db; Chris@0: first = false; Chris@0: } Chris@0: } Chris@0: Chris@0: if (mindb > -80.f) mindb = -80.f; Chris@0: Chris@0: // -- no, don't use the actual mindb -- it's easier to compare Chris@0: // plots with a fixed min value Chris@0: mindb = -170.f; Chris@0: Chris@0: float maxval = maxdb + -mindb; Chris@0: Chris@0: float ly = h - ((-80.f + -mindb) / maxval) * peak + 1; Chris@0: Chris@0: path.moveTo(0, h - peak + 1); Chris@0: path.lineTo(fw, h - peak + 1); Chris@0: Chris@0: freqPainter.setPen(Qt::gray); Chris@0: freqPainter.setRenderHint(QPainter::Antialiasing, true); Chris@0: freqPainter.drawPath(path); Chris@0: Chris@0: path = QPainterPath(); Chris@0: freqPainter.setPen(Qt::black); Chris@0: Chris@0: // std::cerr << "maxdb = " << maxdb << ", mindb = " << mindb << ", maxval = " <setPixmap(freqLabel); Chris@0: Chris@0: m_windowType = type; Chris@0: m_applyButton->setEnabled(true); Chris@0: } Chris@0: Chris@0: void Chris@0: PreferencesDialog::smoothSpectrogramChanged(int state) Chris@0: { Chris@0: m_smoothSpectrogram = (state == Qt::Checked); Chris@0: m_applyButton->setEnabled(true); Chris@0: } Chris@0: Chris@0: void Chris@0: PreferencesDialog::propertyLayoutChanged(int layout) Chris@0: { Chris@0: m_propertyLayout = layout; Chris@0: m_applyButton->setEnabled(true); Chris@0: } Chris@0: Chris@0: void Chris@0: PreferencesDialog::tuningFrequencyChanged(double freq) Chris@0: { Chris@0: m_tuningFrequency = freq; Chris@0: m_applyButton->setEnabled(true); Chris@0: } Chris@0: Chris@0: void Chris@0: PreferencesDialog::okClicked() Chris@0: { Chris@0: applyClicked(); Chris@0: Preferences::getInstance()->getConfigFile()->commit(); Chris@0: accept(); Chris@0: } Chris@0: Chris@0: void Chris@0: PreferencesDialog::applyClicked() Chris@0: { Chris@0: Preferences *prefs = Preferences::getInstance(); Chris@0: prefs->setWindowType(WindowType(m_windowType)); Chris@0: prefs->setSmoothSpectrogram(m_smoothSpectrogram); Chris@0: prefs->setPropertyBoxLayout(Preferences::PropertyBoxLayout Chris@0: (m_propertyLayout)); Chris@0: prefs->setTuningFrequency(m_tuningFrequency); Chris@0: m_applyButton->setEnabled(false); Chris@0: } Chris@0: Chris@0: void Chris@0: PreferencesDialog::cancelClicked() Chris@0: { Chris@0: reject(); Chris@0: } Chris@0: