annotate widgets/WindowShapePreview.cpp @ 1386:fc3d89f88690 spectrogramparam

Use log-frequency rather than log-bin for calculating x coord in spectrum. This has the advantage that frequency positions don't move when we change the window size or oversampling ratio, but it does give us an unhelpfully large amount of space for very low frequencies - to be considered
author Chris Cannam
date Mon, 12 Nov 2018 11:34:34 +0000
parents 6796afa25c88
children d39db4673676
rev   line source
Chris@139 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@139 2
Chris@139 3 /*
Chris@139 4 Sonic Visualiser
Chris@139 5 An audio file viewer and annotation editor.
Chris@139 6 Centre for Digital Music, Queen Mary, University of London.
Chris@139 7 This file copyright 2006 Chris Cannam.
Chris@139 8
Chris@139 9 This program is free software; you can redistribute it and/or
Chris@139 10 modify it under the terms of the GNU General Public License as
Chris@139 11 published by the Free Software Foundation; either version 2 of the
Chris@139 12 License, or (at your option) any later version. See the file
Chris@139 13 COPYING included with this distribution for more information.
Chris@139 14 */
Chris@139 15
Chris@139 16 #include "WindowShapePreview.h"
Chris@139 17
Chris@139 18 #include <QHBoxLayout>
Chris@139 19 #include <QLabel>
Chris@139 20 #include <QPainter>
Chris@139 21 #include <QPainterPath>
Chris@139 22 #include <QFont>
Chris@139 23 #include <QString>
Chris@139 24
Chris@1168 25 #include <bqfft/FFT.h>
Chris@139 26
Chris@1168 27 #include <vector>
Chris@1168 28 #include <complex>
Chris@139 29 #include <iostream>
Chris@139 30
Chris@1168 31 using namespace std;
Chris@1168 32
Chris@545 33
Chris@139 34 WindowShapePreview::WindowShapePreview(QWidget *parent) :
Chris@139 35 QFrame(parent),
Chris@908 36 m_windowType(HanningWindow)
Chris@139 37 {
Chris@139 38 QHBoxLayout *layout = new QHBoxLayout;
Chris@139 39 layout->setMargin(0);
Chris@139 40 setLayout(layout);
Chris@139 41 m_windowTimeExampleLabel = new QLabel;
Chris@139 42 m_windowFreqExampleLabel = new QLabel;
Chris@139 43 layout->addWidget(m_windowTimeExampleLabel);
Chris@139 44 layout->addWidget(m_windowFreqExampleLabel);
Chris@139 45 }
Chris@139 46
Chris@139 47 WindowShapePreview::~WindowShapePreview()
Chris@139 48 {
Chris@139 49 }
Chris@139 50
Chris@139 51 void
Chris@139 52 WindowShapePreview::updateLabels()
Chris@139 53 {
Chris@1157 54 float scaleRatio = float(QFontMetrics(font()).height()) / 14.f;
Chris@1154 55 if (scaleRatio < 1.f) scaleRatio = 1.f;
Chris@1154 56
Chris@1154 57 int step = int(24 * scaleRatio);
Chris@1157 58 float peak = float(48 * scaleRatio);
Chris@1154 59
Chris@1157 60 int w = step * 4, h = int((peak * 4) / 3);
Chris@1154 61
Chris@139 62 WindowType type = m_windowType;
Chris@139 63 Window<float> windower = Window<float>(type, step * 2);
Chris@1154 64
Chris@139 65 QPixmap timeLabel(w, h + 1);
Chris@139 66 timeLabel.fill(Qt::white);
Chris@139 67 QPainter timePainter(&timeLabel);
Chris@139 68
Chris@139 69 QPainterPath path;
Chris@139 70
Chris@908 71 path.moveTo(0, float(h) - peak + 1);
Chris@908 72 path.lineTo(w, float(h) - peak + 1);
Chris@139 73
Chris@139 74 timePainter.setPen(Qt::gray);
Chris@139 75 timePainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139 76 timePainter.drawPath(path);
Chris@139 77
Chris@139 78 path = QPainterPath();
Chris@139 79
Chris@1153 80 float *acc = new float[w];
Chris@139 81 for (int i = 0; i < w; ++i) acc[i] = 0.f;
Chris@139 82 for (int j = 0; j < 3; ++j) {
Chris@139 83 for (int i = 0; i < step * 2; ++i) {
Chris@139 84 acc[j * step + i] += windower.getValue(i);
Chris@139 85 }
Chris@139 86 }
Chris@139 87 for (int i = 0; i < w; ++i) {
Chris@908 88 int y = h - int(peak * acc[i] + 0.001f) + 1;
Chris@139 89 if (i == 0) path.moveTo(i, y);
Chris@139 90 else path.lineTo(i, y);
Chris@139 91 }
Chris@1150 92 delete[] acc;
Chris@139 93
Chris@139 94 timePainter.drawPath(path);
Chris@139 95 timePainter.setRenderHint(QPainter::Antialiasing, false);
Chris@139 96
Chris@139 97 path = QPainterPath();
Chris@139 98
Chris@139 99 timePainter.setPen(Qt::black);
Chris@139 100
Chris@139 101 for (int i = 0; i < step * 2; ++i) {
Chris@139 102 int y = h - int(peak * windower.getValue(i) + 0.001) + 1;
Chris@139 103 if (i == 0) path.moveTo(i + step, float(y));
Chris@139 104 else path.lineTo(i + step, float(y));
Chris@139 105 }
Chris@139 106
Chris@139 107 if (type == RectangularWindow) {
Chris@139 108 timePainter.drawPath(path);
Chris@139 109 path = QPainterPath();
Chris@139 110 }
Chris@139 111
Chris@139 112 timePainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139 113 path.addRect(0, 0, w, h + 1);
Chris@139 114 timePainter.drawPath(path);
Chris@139 115
Chris@139 116 QFont font;
Chris@1154 117 font.setPixelSize(int(10 * scaleRatio));
Chris@139 118 font.setItalic(true);
Chris@139 119 timePainter.setFont(font);
Chris@139 120 QString label = tr("V / time");
Chris@139 121 timePainter.drawText(w - timePainter.fontMetrics().width(label) - 4,
Chris@139 122 timePainter.fontMetrics().ascent() + 1, label);
Chris@139 123
Chris@139 124 m_windowTimeExampleLabel->setPixmap(timeLabel);
Chris@139 125
Chris@1154 126 QPixmap freqLabel(w, h + 1);
Chris@139 127 freqLabel.fill(Qt::white);
Chris@139 128 QPainter freqPainter(&freqLabel);
Chris@139 129 path = QPainterPath();
Chris@139 130
Chris@139 131 int fftsize = 512;
Chris@139 132
Chris@1168 133 breakfastquay::FFT fft(fftsize);
Chris@1168 134
Chris@1168 135 vector<float> input(fftsize);
Chris@1168 136 vector<complex<float>> output(fftsize/2 + 1);
Chris@1168 137
Chris@139 138 for (int i = 0; i < fftsize; ++i) input[i] = 0.f;
Chris@139 139 for (int i = 0; i < step * 2; ++i) {
Chris@139 140 input[fftsize/2 - step + i] = windower.getValue(i);
Chris@139 141 }
Chris@1168 142
Chris@1168 143 fft.forwardInterleaved(input.data(), reinterpret_cast<float *>(output.data()));
Chris@139 144
Chris@139 145 float maxdb = 0.f;
Chris@139 146 float mindb = 0.f;
Chris@139 147 bool first = true;
Chris@139 148 for (int i = 0; i < fftsize/2; ++i) {
Chris@1168 149 float power =
Chris@1168 150 output[i].real() * output[i].real() +
Chris@1168 151 output[i].imag() * output[i].imag();
Chris@139 152 float db = mindb;
Chris@139 153 if (power > 0) {
Chris@908 154 db = 20.f * log10f(power);
Chris@139 155 if (first || db > maxdb) maxdb = db;
Chris@139 156 if (first || db < mindb) mindb = db;
Chris@139 157 first = false;
Chris@139 158 }
Chris@139 159 }
Chris@139 160
Chris@139 161 if (mindb > -80.f) mindb = -80.f;
Chris@139 162
Chris@139 163 // -- no, don't use the actual mindb -- it's easier to compare
Chris@139 164 // plots with a fixed min value
Chris@139 165 mindb = -170.f;
Chris@139 166
Chris@139 167 float maxval = maxdb + -mindb;
Chris@139 168
Chris@139 169 // float ly = h - ((-80.f + -mindb) / maxval) * peak + 1;
Chris@139 170
Chris@908 171 path.moveTo(0, float(h) - peak + 1);
Chris@1154 172 path.lineTo(w, float(h) - peak + 1);
Chris@139 173
Chris@139 174 freqPainter.setPen(Qt::gray);
Chris@139 175 freqPainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139 176 freqPainter.drawPath(path);
Chris@139 177
Chris@139 178 path = QPainterPath();
Chris@139 179 freqPainter.setPen(Qt::black);
Chris@139 180
Chris@682 181 // cerr << "maxdb = " << maxdb << ", mindb = " << mindb << ", maxval = " <<maxval << endl;
Chris@139 182
Chris@139 183 for (int i = 0; i < fftsize/2; ++i) {
Chris@1168 184 float power =
Chris@1168 185 output[i].real() * output[i].real() +
Chris@1168 186 output[i].imag() * output[i].imag();
Chris@908 187 float db = 20.f * log10f(power);
Chris@139 188 float val = db + -mindb;
Chris@139 189 if (val < 0) val = 0;
Chris@139 190 float norm = val / maxval;
Chris@1154 191 float x = (float(w) / float(fftsize/2)) * float(i);
Chris@908 192 float y = float(h) - norm * peak + 1;
Chris@139 193 if (i == 0) path.moveTo(x, y);
Chris@139 194 else path.lineTo(x, y);
Chris@139 195 }
Chris@139 196
Chris@139 197 freqPainter.setRenderHint(QPainter::Antialiasing, true);
Chris@1154 198 path.addRect(0, 0, w, h + 1);
Chris@139 199 freqPainter.drawPath(path);
Chris@139 200
Chris@139 201 freqPainter.setFont(font);
Chris@139 202 label = tr("dB / freq");
Chris@1154 203 freqPainter.drawText(w - freqPainter.fontMetrics().width(label) - 4,
Chris@139 204 freqPainter.fontMetrics().ascent() + 1, label);
Chris@139 205
Chris@139 206 m_windowFreqExampleLabel->setPixmap(freqLabel);
Chris@139 207 }
Chris@139 208
Chris@139 209 void
Chris@139 210 WindowShapePreview::setWindowType(WindowType type)
Chris@139 211 {
Chris@139 212 m_windowType = type;
Chris@139 213 updateLabels();
Chris@139 214 }
Chris@139 215