1 #include "audiospectrum.h"
2 #include "tools/kiss_fftr.h"
7 // Linear interpolation.
11 bool fileWritten = false;
13 AudioSpectrum::AudioSpectrum(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent) :
14 AbstractAudioScopeWidget(projMonitor, clipMonitor, true, parent)
16 ui = new Ui::AudioSpectrum_UI;
19 m_distance = QSize(65, 30);
25 m_aLin = new QAction(i18n("Linear scale"), this);
26 m_aLin->setCheckable(true);
27 m_aLog = new QAction(i18n("Logarithmic scale"), this);
28 m_aLog->setCheckable(true);
30 m_agScale = new QActionGroup(this);
31 m_agScale->addAction(m_aLin);
32 m_agScale->addAction(m_aLog);
34 m_menu->addSeparator()->setText(i18n("Scale"));
35 m_menu->addAction(m_aLin);
36 m_menu->addAction(m_aLog);
38 ui->windowSize->addItem("256", QVariant(256));
39 ui->windowSize->addItem("512", QVariant(512));
40 ui->windowSize->addItem("1024", QVariant(1024));
41 ui->windowSize->addItem("2048", QVariant(2048));
43 m_cfg = kiss_fftr_alloc(ui->windowSize->itemData(ui->windowSize->currentIndex()).toInt(), 0,0,0);
47 b &= connect(ui->windowSize, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateCfg()));
52 AudioSpectrum::~AudioSpectrum()
60 void AudioSpectrum::readConfig()
62 AbstractAudioScopeWidget::readConfig();
64 KSharedConfigPtr config = KGlobal::config();
65 KConfigGroup scopeConfig(config, configName());
66 QString scale = scopeConfig.readEntry("scale");
68 m_aLin->setChecked(true);
70 m_aLog->setChecked(true);
72 ui->windowSize->setCurrentIndex(scopeConfig.readEntry("windowSize", 0));
75 void AudioSpectrum::writeConfig()
77 KSharedConfigPtr config = KGlobal::config();
78 KConfigGroup scopeConfig(config, configName());
80 if (m_aLin->isChecked()) {
85 scopeConfig.writeEntry("scale", scale);
86 scopeConfig.writeEntry("windowSize", ui->windowSize->currentIndex());
90 QString AudioSpectrum::widgetName() const { return QString("audiospectrum"); }
92 bool AudioSpectrum::isBackgroundDependingOnInput() const { return false; }
93 bool AudioSpectrum::isScopeDependingOnInput() const { return true; }
94 bool AudioSpectrum::isHUDDependingOnInput() const { return false; }
96 QImage AudioSpectrum::renderBackground(uint) { return QImage(); }
97 QImage AudioSpectrum::renderScope(uint, const QVector<int16_t> audioFrame, const int freq, const int num_channels, const int num_samples)
99 if (audioFrame.size() > 63) {
100 m_freqMax = freq / 2;
102 QTime start = QTime::currentTime();
104 bool customCfg = false;
105 kiss_fftr_cfg myCfg = m_cfg;
106 int fftWindow = ui->windowSize->itemData(ui->windowSize->currentIndex()).toInt();
107 if (fftWindow > num_samples) {
108 fftWindow = num_samples;
111 if ((fftWindow & 1) == 1) {
116 myCfg = kiss_fftr_alloc(fftWindow, 0,0,0);
119 float data[fftWindow];
120 float freqSpectrum[fftWindow/2];
123 for (int i = 0; i < fftWindow; i++) {
124 if (audioFrame.data()[i*num_channels] > maxSig) {
125 maxSig = audioFrame.data()[i*num_channels];
128 qDebug() << "Max audio signal is " << maxSig;
130 // The resulting FFT vector is only half as long
131 kiss_fft_cpx freqData[fftWindow/2];
133 // Copy the first channel's audio into a vector for the FFT display
134 // (only one channel handled at the moment)
135 for (int i = 0; i < fftWindow; i++) {
136 // Normalize signals to [0,1] to get correct dB values later on
137 data[i] = (float) audioFrame.data()[i*num_channels] / 32767.0f;
139 // Calculate the Fast Fourier Transform for the input data
140 kiss_fftr(m_cfg, data, freqData);
144 // Get the minimum and the maximum value of the Fourier transformed (for scaling)
145 for (int i = 0; i < fftWindow/2; i++) {
146 if (m_aLog->isChecked()) {
147 // Logarithmic scale: 20 * log ( 2 * magnitude / N )
148 // with N = FFT size (after FFT, 1/2 window size)
149 val = 20*log(pow(pow(fabs(freqData[i].r),2) + pow(fabs(freqData[i].i),2), .5)/((float)fftWindow/2.0f))/log(10);
152 val = pow(pow(fabs(freqData[i].r),2) + pow(fabs(freqData[i].i),2), .5);
154 freqSpectrum[i] = val;
161 QImage spectrum(scopeRect().size(), QImage::Format_ARGB32);
162 spectrum.fill(qRgba(0,0,0,0));
163 uint w = scopeRect().size().width();
164 uint h = scopeRect().size().height();
166 for (uint i = 0; i < w; i++) {
168 x = i/((float) w) * fftWindow/2;
170 // Use linear interpolation in order to get smoother display
171 if (i == 0 || i == w-1) {
172 val = freqSpectrum[i];
174 // Use floor(x)+1 instead of ceil(x) as floor(x) == ceil(x) is possible.
175 val = (floor(x)+1 - x)*freqSpectrum[(int) floor(x)] + (x-floor(x))*freqSpectrum[(int) floor(x)+1];
178 // freqSpectrum values range from 0 to -inf as they are relative dB values.
179 qDebug() << val << "/" << (1 - (val - m_dBmax)/(m_dBmin-m_dBmax));
180 for (uint y = 0; y < h*(1 - (val - m_dBmax)/(m_dBmin-m_dBmax)) && y < h; y++) {
181 spectrum.setPixel(i, h-y-1, qRgba(225, 182, 255, 255));
185 emit signalScopeRenderingFinished(start.elapsed(), 1);
188 if (!fileWritten || true) {
190 mFile.open("/tmp/freq.m");
192 qDebug() << "Opening file failed.";
196 for (int sample = 0; sample < 256; sample++) {
197 mFile << data[sample] << " ";
201 mFile << "freq = [ ";
202 for (int sample = 0; sample < 256; sample++) {
203 mFile << freqData[sample].r << "+" << freqData[sample].i << "*i ";
209 qDebug() << "File written.";
212 qDebug() << "File already written.";
222 emit signalScopeRenderingFinished(0, 1);
226 QImage AudioSpectrum::renderHUD(uint)
228 QTime start = QTime::currentTime();
230 const QRect rect = scopeRect();
231 // Minimum distance between two lines
232 const uint minDistY = 30;
233 const uint minDistX = 40;
234 const uint textDist = 5;
235 const uint dbDiff = ceil((float)minDistY/rect.height() * (m_dBmax-m_dBmin));
237 QImage hud(AbstractAudioScopeWidget::rect().size(), QImage::Format_ARGB32);
238 hud.fill(qRgba(0,0,0,0));
240 QPainter davinci(&hud);
241 davinci.setPen(AbstractAudioScopeWidget::penLight);
244 for (int db = -dbDiff; db > m_dBmin; db -= dbDiff) {
245 y = rect.height() * ((float)db)/(m_dBmin - m_dBmax);
246 davinci.drawLine(0, y, rect.width()-1, y);
247 davinci.drawText(rect.width() + textDist, y + 8, i18n("%1 dB", db));
251 qDebug() << "max freq: " << m_freqMax;
252 const uint hzDiff = ceil( ((float)minDistX)/rect.width() * m_freqMax / 1000 ) * 1000;
255 for (int hz = hzDiff; hz < m_freqMax; hz += hzDiff) {
256 x = rect.width() * ((float)hz)/m_freqMax;
257 davinci.drawLine(x, 0, x, rect.height()+4);
258 davinci.drawText(x-4, rect.height() + 20, QVariant(hz/1000).toString());
260 davinci.drawText(rect.width(), rect.height() + 20, "[kHz]");
263 emit signalHUDRenderingFinished(start.elapsed(), 1);
267 QRect AudioSpectrum::scopeRect() {
268 return QRect(QPoint(0, 0), AbstractAudioScopeWidget::rect().size() - m_distance);
272 void AudioSpectrum::slotUpdateCfg()
275 m_cfg = kiss_fftr_alloc(ui->windowSize->itemData(ui->windowSize->currentIndex()).toInt(), 0,0,0);