annotate widgets/WindowShapePreview.cpp @ 162:f32212631b9c

* Handle generator transforms (plugins whose channel count isn't dependent on number of audio inputs, as they have none) * Be less keen to suspend writing FFT data in spectrogram repaint -- only do it if we find we actually need to query the FFT data (i.e. we aren't repainting an area that hasn't been generated at all yet)
author Chris Cannam
date Tue, 10 Oct 2006 19:04:57 +0000
parents 5ec6b60658d8
children cec1f78fbfca
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@139 25 #include <fftw3.h>
Chris@139 26
Chris@139 27 #include <iostream>
Chris@139 28
Chris@139 29 WindowShapePreview::WindowShapePreview(QWidget *parent) :
Chris@139 30 QFrame(parent),
Chris@139 31 m_windowType(WindowType(999))
Chris@139 32 {
Chris@139 33 QHBoxLayout *layout = new QHBoxLayout;
Chris@139 34 layout->setMargin(0);
Chris@139 35 setLayout(layout);
Chris@139 36 m_windowTimeExampleLabel = new QLabel;
Chris@139 37 m_windowFreqExampleLabel = new QLabel;
Chris@139 38 layout->addWidget(m_windowTimeExampleLabel);
Chris@139 39 layout->addWidget(m_windowFreqExampleLabel);
Chris@139 40 }
Chris@139 41
Chris@139 42 WindowShapePreview::~WindowShapePreview()
Chris@139 43 {
Chris@139 44 }
Chris@139 45
Chris@139 46 void
Chris@139 47 WindowShapePreview::updateLabels()
Chris@139 48 {
Chris@139 49 int step = 24;
Chris@139 50 int peak = 48;
Chris@139 51 int w = step * 4, h = 64;
Chris@139 52 WindowType type = m_windowType;
Chris@139 53 Window<float> windower = Window<float>(type, step * 2);
Chris@139 54
Chris@139 55 QPixmap timeLabel(w, h + 1);
Chris@139 56 timeLabel.fill(Qt::white);
Chris@139 57 QPainter timePainter(&timeLabel);
Chris@139 58
Chris@139 59 QPainterPath path;
Chris@139 60
Chris@139 61 path.moveTo(0, h - peak + 1);
Chris@139 62 path.lineTo(w, h - peak + 1);
Chris@139 63
Chris@139 64 timePainter.setPen(Qt::gray);
Chris@139 65 timePainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139 66 timePainter.drawPath(path);
Chris@139 67
Chris@139 68 path = QPainterPath();
Chris@139 69
Chris@139 70 float acc[w];
Chris@139 71 for (int i = 0; i < w; ++i) acc[i] = 0.f;
Chris@139 72 for (int j = 0; j < 3; ++j) {
Chris@139 73 for (int i = 0; i < step * 2; ++i) {
Chris@139 74 acc[j * step + i] += windower.getValue(i);
Chris@139 75 }
Chris@139 76 }
Chris@139 77 for (int i = 0; i < w; ++i) {
Chris@139 78 int y = h - int(peak * acc[i] + 0.001) + 1;
Chris@139 79 if (i == 0) path.moveTo(i, y);
Chris@139 80 else path.lineTo(i, y);
Chris@139 81 }
Chris@139 82
Chris@139 83 timePainter.drawPath(path);
Chris@139 84 timePainter.setRenderHint(QPainter::Antialiasing, false);
Chris@139 85
Chris@139 86 path = QPainterPath();
Chris@139 87
Chris@139 88 timePainter.setPen(Qt::black);
Chris@139 89
Chris@139 90 for (int i = 0; i < step * 2; ++i) {
Chris@139 91 int y = h - int(peak * windower.getValue(i) + 0.001) + 1;
Chris@139 92 if (i == 0) path.moveTo(i + step, float(y));
Chris@139 93 else path.lineTo(i + step, float(y));
Chris@139 94 }
Chris@139 95
Chris@139 96 if (type == RectangularWindow) {
Chris@139 97 timePainter.drawPath(path);
Chris@139 98 path = QPainterPath();
Chris@139 99 }
Chris@139 100
Chris@139 101 timePainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139 102 path.addRect(0, 0, w, h + 1);
Chris@139 103 timePainter.drawPath(path);
Chris@139 104
Chris@139 105 QFont font;
Chris@139 106 font.setPixelSize(10);
Chris@139 107 font.setItalic(true);
Chris@139 108 timePainter.setFont(font);
Chris@139 109 QString label = tr("V / time");
Chris@139 110 timePainter.drawText(w - timePainter.fontMetrics().width(label) - 4,
Chris@139 111 timePainter.fontMetrics().ascent() + 1, label);
Chris@139 112
Chris@139 113 m_windowTimeExampleLabel->setPixmap(timeLabel);
Chris@139 114
Chris@139 115 int fw = 100;
Chris@139 116
Chris@139 117 QPixmap freqLabel(fw, h + 1);
Chris@139 118 freqLabel.fill(Qt::white);
Chris@139 119 QPainter freqPainter(&freqLabel);
Chris@139 120 path = QPainterPath();
Chris@139 121
Chris@139 122 int fftsize = 512;
Chris@139 123
Chris@139 124 float *input = (float *)fftwf_malloc(fftsize * sizeof(float));
Chris@139 125 fftwf_complex *output =
Chris@139 126 (fftwf_complex *)fftwf_malloc(fftsize * sizeof(fftwf_complex));
Chris@139 127 fftwf_plan plan = fftwf_plan_dft_r2c_1d(fftsize, input, output,
Chris@139 128 FFTW_ESTIMATE);
Chris@139 129 for (int i = 0; i < fftsize; ++i) input[i] = 0.f;
Chris@139 130 for (int i = 0; i < step * 2; ++i) {
Chris@139 131 input[fftsize/2 - step + i] = windower.getValue(i);
Chris@139 132 }
Chris@139 133
Chris@139 134 fftwf_execute(plan);
Chris@139 135 fftwf_destroy_plan(plan);
Chris@139 136
Chris@139 137 float maxdb = 0.f;
Chris@139 138 float mindb = 0.f;
Chris@139 139 bool first = true;
Chris@139 140 for (int i = 0; i < fftsize/2; ++i) {
Chris@139 141 float power = output[i][0] * output[i][0] + output[i][1] * output[i][1];
Chris@139 142 float db = mindb;
Chris@139 143 if (power > 0) {
Chris@139 144 db = 20 * log10(power);
Chris@139 145 if (first || db > maxdb) maxdb = db;
Chris@139 146 if (first || db < mindb) mindb = db;
Chris@139 147 first = false;
Chris@139 148 }
Chris@139 149 }
Chris@139 150
Chris@139 151 if (mindb > -80.f) mindb = -80.f;
Chris@139 152
Chris@139 153 // -- no, don't use the actual mindb -- it's easier to compare
Chris@139 154 // plots with a fixed min value
Chris@139 155 mindb = -170.f;
Chris@139 156
Chris@139 157 float maxval = maxdb + -mindb;
Chris@139 158
Chris@139 159 // float ly = h - ((-80.f + -mindb) / maxval) * peak + 1;
Chris@139 160
Chris@139 161 path.moveTo(0, h - peak + 1);
Chris@139 162 path.lineTo(fw, h - peak + 1);
Chris@139 163
Chris@139 164 freqPainter.setPen(Qt::gray);
Chris@139 165 freqPainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139 166 freqPainter.drawPath(path);
Chris@139 167
Chris@139 168 path = QPainterPath();
Chris@139 169 freqPainter.setPen(Qt::black);
Chris@139 170
Chris@139 171 // std::cerr << "maxdb = " << maxdb << ", mindb = " << mindb << ", maxval = " <<maxval << std::endl;
Chris@139 172
Chris@139 173 for (int i = 0; i < fftsize/2; ++i) {
Chris@139 174 float power = output[i][0] * output[i][0] + output[i][1] * output[i][1];
Chris@139 175 float db = 20 * log10(power);
Chris@139 176 float val = db + -mindb;
Chris@139 177 if (val < 0) val = 0;
Chris@139 178 float norm = val / maxval;
Chris@139 179 float x = (fw / float(fftsize/2)) * i;
Chris@139 180 float y = h - norm * peak + 1;
Chris@139 181 if (i == 0) path.moveTo(x, y);
Chris@139 182 else path.lineTo(x, y);
Chris@139 183 }
Chris@139 184
Chris@139 185 freqPainter.setRenderHint(QPainter::Antialiasing, true);
Chris@139 186 path.addRect(0, 0, fw, h + 1);
Chris@139 187 freqPainter.drawPath(path);
Chris@139 188
Chris@139 189 fftwf_free(input);
Chris@139 190 fftwf_free(output);
Chris@139 191
Chris@139 192 freqPainter.setFont(font);
Chris@139 193 label = tr("dB / freq");
Chris@139 194 freqPainter.drawText(fw - freqPainter.fontMetrics().width(label) - 4,
Chris@139 195 freqPainter.fontMetrics().ascent() + 1, label);
Chris@139 196
Chris@139 197 m_windowFreqExampleLabel->setPixmap(freqLabel);
Chris@139 198 }
Chris@139 199
Chris@139 200 void
Chris@139 201 WindowShapePreview::setWindowType(WindowType type)
Chris@139 202 {
Chris@139 203 if (m_windowType == type) return;
Chris@139 204 m_windowType = type;
Chris@139 205 updateLabels();
Chris@139 206 }
Chris@139 207