annotate widgets/WindowShapePreview.cpp @ 1135:628cd329c241 spectrogram-minor-refactor

Use a count of bins rather than min and max bins (because the name maxbin tells us nothing about whether the range is inclusive or not)
author Chris Cannam
date Wed, 03 Aug 2016 14:20:27 +0100
parents 6645b6b8356f
children 0edfed2c8482
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@202 25 #include "data/fft/FFTapi.h"
Chris@139 26
Chris@139 27 #include <iostream>
Chris@139 28
Chris@545 29 #ifndef __GNUC__
Chris@545 30 #include <alloca.h>
Chris@545 31 #endif
Chris@545 32
Chris@139 33 WindowShapePreview::WindowShapePreview(QWidget *parent) :
Chris@139 34 QFrame(parent),
Chris@908 35 m_windowType(HanningWindow)
Chris@139 36 {
Chris@139 37 QHBoxLayout *layout = new QHBoxLayout;
Chris@139 38 layout->setMargin(0);
Chris@139 39 setLayout(layout);
Chris@139 40 m_windowTimeExampleLabel = new QLabel;
Chris@139 41 m_windowFreqExampleLabel = new QLabel;
Chris@139 42 layout->addWidget(m_windowTimeExampleLabel);
Chris@139 43 layout->addWidget(m_windowFreqExampleLabel);
Chris@139 44 }
Chris@139 45
Chris@139 46 WindowShapePreview::~WindowShapePreview()
Chris@139 47 {
Chris@139 48 }
Chris@139 49
Chris@139 50 void
Chris@139 51 WindowShapePreview::updateLabels()
Chris@139 52 {
Chris@139 53 int step = 24;
Chris@908 54 float peak = 48;
Chris@139 55 int w = step * 4, h = 64;
Chris@139 56 WindowType type = m_windowType;
Chris@139 57 Window<float> windower = Window<float>(type, step * 2);
Chris@139 58
Chris@139 59 QPixmap timeLabel(w, h + 1);
Chris@139 60 timeLabel.fill(Qt::white);
Chris@139 61 QPainter timePainter(&timeLabel);
Chris@139 62
Chris@139 63 QPainterPath path;
Chris@139 64
Chris@908 65 path.moveTo(0, float(h) - peak + 1);
Chris@908 66 path.lineTo(w, float(h) - peak + 1);
Chris@139 67
Chris@139 68 timePainter.setPen(Qt::gray);
Chris@139 69 timePainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139 70 timePainter.drawPath(path);
Chris@139 71
Chris@139 72 path = QPainterPath();
Chris@139 73
Chris@545 74 #ifdef __GNUC__
Chris@139 75 float acc[w];
Chris@545 76 #else
Chris@545 77 float *acc = (float *)alloca(w * sizeof(float));
Chris@545 78 #endif
Chris@545 79
Chris@139 80 for (int i = 0; i < w; ++i) acc[i] = 0.f;
Chris@139 81 for (int j = 0; j < 3; ++j) {
Chris@139 82 for (int i = 0; i < step * 2; ++i) {
Chris@139 83 acc[j * step + i] += windower.getValue(i);
Chris@139 84 }
Chris@139 85 }
Chris@139 86 for (int i = 0; i < w; ++i) {
Chris@908 87 int y = h - int(peak * acc[i] + 0.001f) + 1;
Chris@139 88 if (i == 0) path.moveTo(i, y);
Chris@139 89 else path.lineTo(i, y);
Chris@139 90 }
Chris@139 91
Chris@139 92 timePainter.drawPath(path);
Chris@139 93 timePainter.setRenderHint(QPainter::Antialiasing, false);
Chris@139 94
Chris@139 95 path = QPainterPath();
Chris@139 96
Chris@139 97 timePainter.setPen(Qt::black);
Chris@139 98
Chris@139 99 for (int i = 0; i < step * 2; ++i) {
Chris@139 100 int y = h - int(peak * windower.getValue(i) + 0.001) + 1;
Chris@139 101 if (i == 0) path.moveTo(i + step, float(y));
Chris@139 102 else path.lineTo(i + step, float(y));
Chris@139 103 }
Chris@139 104
Chris@139 105 if (type == RectangularWindow) {
Chris@139 106 timePainter.drawPath(path);
Chris@139 107 path = QPainterPath();
Chris@139 108 }
Chris@139 109
Chris@139 110 timePainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139 111 path.addRect(0, 0, w, h + 1);
Chris@139 112 timePainter.drawPath(path);
Chris@139 113
Chris@139 114 QFont font;
Chris@139 115 font.setPixelSize(10);
Chris@139 116 font.setItalic(true);
Chris@139 117 timePainter.setFont(font);
Chris@139 118 QString label = tr("V / time");
Chris@139 119 timePainter.drawText(w - timePainter.fontMetrics().width(label) - 4,
Chris@139 120 timePainter.fontMetrics().ascent() + 1, label);
Chris@139 121
Chris@139 122 m_windowTimeExampleLabel->setPixmap(timeLabel);
Chris@139 123
Chris@139 124 int fw = 100;
Chris@139 125
Chris@139 126 QPixmap freqLabel(fw, 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@202 133 float *input = (float *)fftf_malloc(fftsize * sizeof(float));
Chris@202 134 fftf_complex *output =
Chris@202 135 (fftf_complex *)fftf_malloc(fftsize * sizeof(fftf_complex));
Chris@202 136 fftf_plan plan = fftf_plan_dft_r2c_1d(fftsize, input, output,
Chris@139 137 FFTW_ESTIMATE);
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@139 142
Chris@202 143 fftf_execute(plan);
Chris@202 144 fftf_destroy_plan(plan);
Chris@139 145
Chris@139 146 float maxdb = 0.f;
Chris@139 147 float mindb = 0.f;
Chris@139 148 bool first = true;
Chris@139 149 for (int i = 0; i < fftsize/2; ++i) {
Chris@139 150 float power = output[i][0] * output[i][0] + output[i][1] * output[i][1];
Chris@139 151 float db = mindb;
Chris@139 152 if (power > 0) {
Chris@908 153 db = 20.f * log10f(power);
Chris@139 154 if (first || db > maxdb) maxdb = db;
Chris@139 155 if (first || db < mindb) mindb = db;
Chris@139 156 first = false;
Chris@139 157 }
Chris@139 158 }
Chris@139 159
Chris@139 160 if (mindb > -80.f) mindb = -80.f;
Chris@139 161
Chris@139 162 // -- no, don't use the actual mindb -- it's easier to compare
Chris@139 163 // plots with a fixed min value
Chris@139 164 mindb = -170.f;
Chris@139 165
Chris@139 166 float maxval = maxdb + -mindb;
Chris@139 167
Chris@139 168 // float ly = h - ((-80.f + -mindb) / maxval) * peak + 1;
Chris@139 169
Chris@908 170 path.moveTo(0, float(h) - peak + 1);
Chris@908 171 path.lineTo(fw, float(h) - peak + 1);
Chris@139 172
Chris@139 173 freqPainter.setPen(Qt::gray);
Chris@139 174 freqPainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139 175 freqPainter.drawPath(path);
Chris@139 176
Chris@139 177 path = QPainterPath();
Chris@139 178 freqPainter.setPen(Qt::black);
Chris@139 179
Chris@682 180 // cerr << "maxdb = " << maxdb << ", mindb = " << mindb << ", maxval = " <<maxval << endl;
Chris@139 181
Chris@139 182 for (int i = 0; i < fftsize/2; ++i) {
Chris@139 183 float power = output[i][0] * output[i][0] + output[i][1] * output[i][1];
Chris@908 184 float db = 20.f * log10f(power);
Chris@139 185 float val = db + -mindb;
Chris@139 186 if (val < 0) val = 0;
Chris@139 187 float norm = val / maxval;
Chris@908 188 float x = (float(fw) / float(fftsize/2)) * float(i);
Chris@908 189 float y = float(h) - norm * peak + 1;
Chris@139 190 if (i == 0) path.moveTo(x, y);
Chris@139 191 else path.lineTo(x, y);
Chris@139 192 }
Chris@139 193
Chris@139 194 freqPainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139 195 path.addRect(0, 0, fw, h + 1);
Chris@139 196 freqPainter.drawPath(path);
Chris@139 197
Chris@202 198 fftf_free(input);
Chris@202 199 fftf_free(output);
Chris@139 200
Chris@139 201 freqPainter.setFont(font);
Chris@139 202 label = tr("dB / freq");
Chris@139 203 freqPainter.drawText(fw - 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