Chris@139: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@139: 
Chris@139: /*
Chris@139:     Sonic Visualiser
Chris@139:     An audio file viewer and annotation editor.
Chris@139:     Centre for Digital Music, Queen Mary, University of London.
Chris@139:     This file copyright 2006 Chris Cannam.
Chris@139:     
Chris@139:     This program is free software; you can redistribute it and/or
Chris@139:     modify it under the terms of the GNU General Public License as
Chris@139:     published by the Free Software Foundation; either version 2 of the
Chris@139:     License, or (at your option) any later version.  See the file
Chris@139:     COPYING included with this distribution for more information.
Chris@139: */
Chris@139: 
Chris@139: #include "WindowShapePreview.h"
Chris@139: 
Chris@139: #include <QHBoxLayout>
Chris@139: #include <QLabel>
Chris@139: #include <QPainter>
Chris@139: #include <QPainterPath>
Chris@139: #include <QFont>
Chris@139: #include <QString>
Chris@139: 
Chris@202: #include "data/fft/FFTapi.h"
Chris@139: 
Chris@139: #include <iostream>
Chris@139: 
Chris@545: #ifndef __GNUC__
Chris@545: #include <alloca.h>
Chris@545: #endif
Chris@545: 
Chris@139: WindowShapePreview::WindowShapePreview(QWidget *parent) :
Chris@139:     QFrame(parent),
Chris@908:     m_windowType(HanningWindow)
Chris@139: {
Chris@139:     QHBoxLayout *layout = new QHBoxLayout;
Chris@139:     layout->setMargin(0);
Chris@139:     setLayout(layout);
Chris@139:     m_windowTimeExampleLabel = new QLabel;
Chris@139:     m_windowFreqExampleLabel = new QLabel;
Chris@139:     layout->addWidget(m_windowTimeExampleLabel);
Chris@139:     layout->addWidget(m_windowFreqExampleLabel);
Chris@139: }
Chris@139: 
Chris@139: WindowShapePreview::~WindowShapePreview()
Chris@139: {
Chris@139: }
Chris@139: 
Chris@139: void
Chris@139: WindowShapePreview::updateLabels()
Chris@139: {
Chris@139:     int step = 24;
Chris@908:     float peak = 48;
Chris@139:     int w = step * 4, h = 64;
Chris@139:     WindowType type = m_windowType;
Chris@139:     Window<float> windower = Window<float>(type, step * 2);
Chris@139: 
Chris@139:     QPixmap timeLabel(w, h + 1);
Chris@139:     timeLabel.fill(Qt::white);
Chris@139:     QPainter timePainter(&timeLabel);
Chris@139: 
Chris@139:     QPainterPath path;
Chris@139: 
Chris@908:     path.moveTo(0, float(h) - peak + 1);
Chris@908:     path.lineTo(w, float(h) - peak + 1);
Chris@139: 
Chris@139:     timePainter.setPen(Qt::gray);
Chris@139:     timePainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139:     timePainter.drawPath(path);
Chris@139:     
Chris@139:     path = QPainterPath();
Chris@139: 
Chris@545: #ifdef __GNUC__
Chris@139:     float acc[w];
Chris@545: #else
Chris@545:     float *acc = (float *)alloca(w * sizeof(float));
Chris@545: #endif
Chris@545: 
Chris@139:     for (int i = 0; i < w; ++i) acc[i] = 0.f;
Chris@139:     for (int j = 0; j < 3; ++j) {
Chris@139:         for (int i = 0; i < step * 2; ++i) {
Chris@139:             acc[j * step + i] += windower.getValue(i);
Chris@139:         }
Chris@139:     }
Chris@139:     for (int i = 0; i < w; ++i) {
Chris@908:         int y = h - int(peak * acc[i] + 0.001f) + 1;
Chris@139:         if (i == 0) path.moveTo(i, y);
Chris@139:         else path.lineTo(i, y);
Chris@139:     }
Chris@139: 
Chris@139:     timePainter.drawPath(path);
Chris@139:     timePainter.setRenderHint(QPainter::Antialiasing, false);
Chris@139: 
Chris@139:     path = QPainterPath();
Chris@139: 
Chris@139:     timePainter.setPen(Qt::black);
Chris@139:     
Chris@139:     for (int i = 0; i < step * 2; ++i) {
Chris@139:         int y = h - int(peak * windower.getValue(i) + 0.001) + 1;
Chris@139:         if (i == 0) path.moveTo(i + step, float(y));
Chris@139:         else path.lineTo(i + step, float(y));
Chris@139:     }
Chris@139: 
Chris@139:     if (type == RectangularWindow) {
Chris@139:         timePainter.drawPath(path);
Chris@139:         path = QPainterPath();
Chris@139:     }
Chris@139: 
Chris@139:     timePainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139:     path.addRect(0, 0, w, h + 1);
Chris@139:     timePainter.drawPath(path);
Chris@139: 
Chris@139:     QFont font;
Chris@139:     font.setPixelSize(10);
Chris@139:     font.setItalic(true);
Chris@139:     timePainter.setFont(font);
Chris@139:     QString label = tr("V / time");
Chris@139:     timePainter.drawText(w - timePainter.fontMetrics().width(label) - 4,
Chris@139:                          timePainter.fontMetrics().ascent() + 1, label);
Chris@139: 
Chris@139:     m_windowTimeExampleLabel->setPixmap(timeLabel);
Chris@139:     
Chris@139:     int fw = 100;
Chris@139: 
Chris@139:     QPixmap freqLabel(fw, h + 1);
Chris@139:     freqLabel.fill(Qt::white);
Chris@139:     QPainter freqPainter(&freqLabel);
Chris@139:     path = QPainterPath();
Chris@139: 
Chris@139:     int fftsize = 512;
Chris@139: 
Chris@202:     float *input = (float *)fftf_malloc(fftsize * sizeof(float));
Chris@202:     fftf_complex *output =
Chris@202:         (fftf_complex *)fftf_malloc(fftsize * sizeof(fftf_complex));
Chris@202:     fftf_plan plan = fftf_plan_dft_r2c_1d(fftsize, input, output,
Chris@139:                                             FFTW_ESTIMATE);
Chris@139:     for (int i = 0; i < fftsize; ++i) input[i] = 0.f;
Chris@139:     for (int i = 0; i < step * 2; ++i) {
Chris@139:         input[fftsize/2 - step + i] = windower.getValue(i);
Chris@139:     }
Chris@139:     
Chris@202:     fftf_execute(plan);
Chris@202:     fftf_destroy_plan(plan);
Chris@139: 
Chris@139:     float maxdb = 0.f;
Chris@139:     float mindb = 0.f;
Chris@139:     bool first = true;
Chris@139:     for (int i = 0; i < fftsize/2; ++i) {
Chris@139:         float power = output[i][0] * output[i][0] + output[i][1] * output[i][1];
Chris@139:         float db = mindb;
Chris@139:         if (power > 0) {
Chris@908:             db = 20.f * log10f(power);
Chris@139:             if (first || db > maxdb) maxdb = db;
Chris@139:             if (first || db < mindb) mindb = db;
Chris@139:             first = false;
Chris@139:         }
Chris@139:     }
Chris@139: 
Chris@139:     if (mindb > -80.f) mindb = -80.f;
Chris@139: 
Chris@139:     // -- no, don't use the actual mindb -- it's easier to compare
Chris@139:     // plots with a fixed min value
Chris@139:     mindb = -170.f;
Chris@139: 
Chris@139:     float maxval = maxdb + -mindb;
Chris@139: 
Chris@139: //    float ly = h - ((-80.f + -mindb) / maxval) * peak + 1;
Chris@139: 
Chris@908:     path.moveTo(0, float(h) - peak + 1);
Chris@908:     path.lineTo(fw, float(h) - peak + 1);
Chris@139: 
Chris@139:     freqPainter.setPen(Qt::gray);
Chris@139:     freqPainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139:     freqPainter.drawPath(path);
Chris@139:     
Chris@139:     path = QPainterPath();
Chris@139:     freqPainter.setPen(Qt::black);
Chris@139: 
Chris@682: //    cerr << "maxdb = " << maxdb << ", mindb = " << mindb << ", maxval = " <<maxval << endl;
Chris@139: 
Chris@139:     for (int i = 0; i < fftsize/2; ++i) {
Chris@139:         float power = output[i][0] * output[i][0] + output[i][1] * output[i][1];
Chris@908:         float db = 20.f * log10f(power);
Chris@139:         float val = db + -mindb;
Chris@139:         if (val < 0) val = 0;
Chris@139:         float norm = val / maxval;
Chris@908:         float x = (float(fw) / float(fftsize/2)) * float(i);
Chris@908:         float y = float(h) - norm * peak + 1;
Chris@139:         if (i == 0) path.moveTo(x, y);
Chris@139:         else path.lineTo(x, y);
Chris@139:     }
Chris@139: 
Chris@139:     freqPainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139:     path.addRect(0, 0, fw, h + 1);
Chris@139:     freqPainter.drawPath(path);
Chris@139: 
Chris@202:     fftf_free(input);
Chris@202:     fftf_free(output);
Chris@139: 
Chris@139:     freqPainter.setFont(font);
Chris@139:     label = tr("dB / freq");
Chris@139:     freqPainter.drawText(fw - freqPainter.fontMetrics().width(label) - 4,
Chris@139:                          freqPainter.fontMetrics().ascent() + 1, label);
Chris@139: 
Chris@139:     m_windowFreqExampleLabel->setPixmap(freqLabel);
Chris@139: }
Chris@139: 
Chris@139: void
Chris@139: WindowShapePreview::setWindowType(WindowType type)
Chris@139: {
Chris@139:     m_windowType = type;
Chris@139:     updateLabels();
Chris@139: }
Chris@139: