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 Chris@139: #include Chris@139: #include Chris@139: #include Chris@139: #include Chris@139: #include Chris@139: Chris@202: #include "data/fft/FFTapi.h" Chris@139: Chris@139: #include Chris@139: Chris@545: #ifndef __GNUC__ Chris@545: #include 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 windower = Window(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 = " <setPixmap(freqLabel); Chris@139: } Chris@139: Chris@139: void Chris@139: WindowShapePreview::setWindowType(WindowType type) Chris@139: { Chris@139: if (m_windowType == type) return; Chris@139: m_windowType = type; Chris@139: updateLabels(); Chris@139: } Chris@139: