annotate widgets/WindowShapePreview.cpp @ 1557:3607b0092019 spectrogram-export

Comment only
author Chris Cannam
date Tue, 07 Jan 2020 11:19:02 +0000
parents d39db4673676
children
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@1478 116 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
Chris@1478 117 // replacement (horizontalAdvance) was only added in Qt 5.11
Chris@1478 118 // which is too new for us
Chris@1478 119 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Chris@1478 120
Chris@139 121 QFont font;
Chris@1154 122 font.setPixelSize(int(10 * scaleRatio));
Chris@139 123 font.setItalic(true);
Chris@139 124 timePainter.setFont(font);
Chris@139 125 QString label = tr("V / time");
Chris@139 126 timePainter.drawText(w - timePainter.fontMetrics().width(label) - 4,
Chris@139 127 timePainter.fontMetrics().ascent() + 1, label);
Chris@139 128
Chris@139 129 m_windowTimeExampleLabel->setPixmap(timeLabel);
Chris@139 130
Chris@1154 131 QPixmap freqLabel(w, h + 1);
Chris@139 132 freqLabel.fill(Qt::white);
Chris@139 133 QPainter freqPainter(&freqLabel);
Chris@139 134 path = QPainterPath();
Chris@139 135
Chris@139 136 int fftsize = 512;
Chris@139 137
Chris@1168 138 breakfastquay::FFT fft(fftsize);
Chris@1168 139
Chris@1168 140 vector<float> input(fftsize);
Chris@1168 141 vector<complex<float>> output(fftsize/2 + 1);
Chris@1168 142
Chris@139 143 for (int i = 0; i < fftsize; ++i) input[i] = 0.f;
Chris@139 144 for (int i = 0; i < step * 2; ++i) {
Chris@139 145 input[fftsize/2 - step + i] = windower.getValue(i);
Chris@139 146 }
Chris@1168 147
Chris@1168 148 fft.forwardInterleaved(input.data(), reinterpret_cast<float *>(output.data()));
Chris@139 149
Chris@139 150 float maxdb = 0.f;
Chris@139 151 float mindb = 0.f;
Chris@139 152 bool first = true;
Chris@139 153 for (int i = 0; i < fftsize/2; ++i) {
Chris@1168 154 float power =
Chris@1168 155 output[i].real() * output[i].real() +
Chris@1168 156 output[i].imag() * output[i].imag();
Chris@139 157 float db = mindb;
Chris@139 158 if (power > 0) {
Chris@908 159 db = 20.f * log10f(power);
Chris@139 160 if (first || db > maxdb) maxdb = db;
Chris@139 161 if (first || db < mindb) mindb = db;
Chris@139 162 first = false;
Chris@139 163 }
Chris@139 164 }
Chris@139 165
Chris@139 166 if (mindb > -80.f) mindb = -80.f;
Chris@139 167
Chris@139 168 // -- no, don't use the actual mindb -- it's easier to compare
Chris@139 169 // plots with a fixed min value
Chris@139 170 mindb = -170.f;
Chris@139 171
Chris@139 172 float maxval = maxdb + -mindb;
Chris@139 173
Chris@139 174 // float ly = h - ((-80.f + -mindb) / maxval) * peak + 1;
Chris@139 175
Chris@908 176 path.moveTo(0, float(h) - peak + 1);
Chris@1154 177 path.lineTo(w, float(h) - peak + 1);
Chris@139 178
Chris@139 179 freqPainter.setPen(Qt::gray);
Chris@139 180 freqPainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139 181 freqPainter.drawPath(path);
Chris@139 182
Chris@139 183 path = QPainterPath();
Chris@139 184 freqPainter.setPen(Qt::black);
Chris@139 185
Chris@682 186 // cerr << "maxdb = " << maxdb << ", mindb = " << mindb << ", maxval = " <<maxval << endl;
Chris@139 187
Chris@139 188 for (int i = 0; i < fftsize/2; ++i) {
Chris@1168 189 float power =
Chris@1168 190 output[i].real() * output[i].real() +
Chris@1168 191 output[i].imag() * output[i].imag();
Chris@908 192 float db = 20.f * log10f(power);
Chris@139 193 float val = db + -mindb;
Chris@139 194 if (val < 0) val = 0;
Chris@139 195 float norm = val / maxval;
Chris@1154 196 float x = (float(w) / float(fftsize/2)) * float(i);
Chris@908 197 float y = float(h) - norm * peak + 1;
Chris@139 198 if (i == 0) path.moveTo(x, y);
Chris@139 199 else path.lineTo(x, y);
Chris@139 200 }
Chris@139 201
Chris@139 202 freqPainter.setRenderHint(QPainter::Antialiasing, true);
Chris@1154 203 path.addRect(0, 0, w, h + 1);
Chris@139 204 freqPainter.drawPath(path);
Chris@139 205
Chris@139 206 freqPainter.setFont(font);
Chris@139 207 label = tr("dB / freq");
Chris@1154 208 freqPainter.drawText(w - freqPainter.fontMetrics().width(label) - 4,
Chris@139 209 freqPainter.fontMetrics().ascent() + 1, label);
Chris@139 210
Chris@139 211 m_windowFreqExampleLabel->setPixmap(freqLabel);
Chris@139 212 }
Chris@139 213
Chris@139 214 void
Chris@139 215 WindowShapePreview::setWindowType(WindowType type)
Chris@139 216 {
Chris@139 217 m_windowType = type;
Chris@139 218 updateLabels();
Chris@139 219 }
Chris@139 220