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