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