raDIYo 0.4
swfftcalc.cpp
1/***************************************************************************
2
3 source::worx raDIYo
4 Copyright © 2020-2022 c.holzheuer
5 c.holzheuer@sourceworx.org
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12***************************************************************************/
13
14
15#include <QtDebug>
16//#include <QtAlgorithms>
17//#include <qalgorithms.h>
18
19
20#include <qmath.h>
21#include <swfftcalc.h>
22
23
24
25//const qint16 PCMS16MaxValue = 32767;
26const quint16 PCMS16MaxAmplitude = 32768; // because minimum is -32768
27
28
29qreal pcmToReal(qint16 pcm)
30{
31 return qreal(pcm) / PCMS16MaxAmplitude;
32}
33
34SWFFTCalc::SWFFTCalc( QObject* parent )
35 : QObject( parent )
36{
37
38 _timer.start();
39 // window functions are used to filter some undesired
40 // information for fft calculation.
41 _hannWindow.resize( SWNUMSAMPLES );
42
43 // the complex frame that is sent to fft function
44 _complexFrame.resize( SWNUMSAMPLES );
45
46 // only half spectrum is used because of the simetry property
47 _spectrum.resize( SWNUMSAMPLES/2 );
48
49 // logscale is used for audio spectrum display
50 //_logScale.resize( SWNUMSAMPLES/2+1 );
51
52 // by default, spectrum is log scaled (compressed)
53 //compressed = false;
54
55 // window function (HANN)
56 for( int i=0; i<SWNUMSAMPLES; i++ )
57 _hannWindow[i] = 0.5 * ( 1 - cos( (2*PI*i) / (SWNUMSAMPLES-1) ) );
58
59 //double hannWindow = 0.5 * (1 - cos((2 * M_PI * i) / (n - 1)));
60 // x = 0.5 * (1 - qCos((2 * M_PI * i) / (SWNUMSAMPLES - 1)));
61 // case HannWindow:
62 // x = 0.5 * (1 - qCos((2 * M_PI * i) / (SWNUMSAMPLES - 1)));
63
64 // the log scale
65 //for( int i=0; i<=SWNUMSAMPLES/2; i++ )
66 // _logScale[i] = powf( SWNUMSAMPLES/2, (float) 2*i / SWNUMSAMPLES ) - 0.5f;
67
68
69
70 /*
71
72 //sum result into 44 bins for visualization
73 int steps = n/44;
74 std::vector<double> bins;
75 for (int i=0; i<44; i++) {
76 double bin = 0;
77 for (int q=0; q<steps; q++) {
78 //calculate amplitude = sqrt(re*re+im*im)
79 double amplitude = sqrt(y[i*steps+q][0]*y[i*steps+q][0]+y[i*steps+q][1]*y[i*steps+q][1]);
80 //overwrite NaN with 0. Got a lot of them apparently, without these two lines it crashes immediatly, I guess somewhere downstream
81 if (amplitude != amplitude) bin += 0;
82 //convert to dB
83 else bin += 20*log10(amplitude);
84 }
85 bins.push_back(bin);
86 qDebug() << bins[i];
87 }
88 */
89}
90
91
92SWFFTCalc::~SWFFTCalc()
93{
94
95}
96
97/*
98
99 Also: Wir bekommen zu willkürlichen Zeitpunkten im millisekunden
100 Bereich einen Buffer mit Rohdaten im Format X.
101
102 TODO:
103 - Format X PeakValue rausfinden
104 - einen Buffer<double> anlegen
105 - mit Peak(X) auf [0.0 ... 1.0 [ normalisieren
106 - FFT drüber
107
108
109*/
110
111
120void SWFFTCalc::collectFrames( QAudioBuffer& audiobuffer )
121{
122
123 //
124 // Step 1. Daten anreichern bis ca. 100 ms
125 //
126
127
128 QAudioFormat format = audiobuffer.format();
129
130 if( !format.isValid()
131 || format.channelCount() != 2
132 || format.sampleType() == QAudioFormat::Unknown )
133 return;
134
135 //int duration = format.durationForFrames( audiobuffer.frameCount() ) / 1000;
136 //qDebug() << "onAudioProbed:" << duration << " ms frames:" << audiobuffer.frameCount() << " time:" << _timer.elapsed();
137 //qDebug() << "zeit: " << _timer.elapsed() << " ms";
138 //_timer.start();
139
140 //if( duration < 70 )
141 //return;
142
143 // Initialize data array
144 _complexFrame = CArray( 0.0, SWNUMSAMPLES );
145 const char* ptr = (char*) audiobuffer.constData();
146 int to = qMin( SWNUMSAMPLES, audiobuffer.frameCount() );
147
148 //for( int i=0; i<SWNUMSAMPLES; ++i )
149 for( int i=0; i<to; ++i )
150 {
151 const qint16 pcmSample = *reinterpret_cast<const qint16*>( ptr );
152 // Scale down to range [-1.0, 1.0]
153 //_input[i] = pcmToReal(pcmSample) * _hannWindow[i];;
154 _complexFrame[i] = Complex( _hannWindow[i] * pcmToReal(pcmSample), 0 );
155 ptr += 4; //bytesPerSample;
156 }
157
158 // do the magic
159 bareFFT( _complexFrame );
160
161 // Analyze output to obtain amplitude and phase for each frequency
162 //for( int i=2; i < SWNUMSAMPLES/2; ++i )
163 for( int i=0; i < SWNUMSAMPLES/2; ++i )
164 {
165 const qreal magnitude = abs( _complexFrame[i] );
166
167 //??
168 //qreal amplitude = qLn( magnitude );
169 qreal amplitude = 0.2 * qLn( magnitude );
170 //qreal amplitude = SpectrumAnalyserMultiplier * qLn( magnitude );
171 //qreal amplitude = SpectrumAnalyserMultiplier * magnitude;
172 //qreal amplitude = magnitude;
173
174 _spectrum[i] = qBound( 0.0, amplitude, 1.0 );
175 }
176
177 //for( int i=0; i<10; ++i )
178 // qDebug() << "Ready: " << i << ": " << QString::number( _spectrum[i], 'f', 20 );
179
180 emit spectrumReady( _spectrum );
181
182 //
183 // 3. Hier haben wir in '_input' ein nettes. normalisiertes Array<double>
184 //
185
186}
187
188/*
189template<class T>
190void SWFFTCalc::prepareInput( QAudioBuffer& audiobuffer, T& dataType, qreal peakValue )
191{
192 Q_UNUSED( dataType );
193
194 int frameCount = audiobuffer.frameCount();
195 _input = SWDoubleVec( SWNUMSAMPLES, 0.0 );
196 //_input.resize( frameCount );
197 const T* frames = audiobuffer.data<T>();
198
199 //for( int i=0; i<100; ++i )
200 // qDebug() << "frames: " << i << ": " <<frames[i].average() << " left:" << frames[i].left << " right: " << frames[i].right;
201 int limit = qMin( frameCount, SWNUMSAMPLES );
202 qDebug() << "peak: " << peakValue << "Frames: " << frameCount << " limit:" << limit;
203
204 for( int i=0; i < limit; i++)
205 {
206 //_input[i] = frames[i].left / peakValue;
207 _input[i] = frames[i].average() / peakValue * _hannWindow[i];
208 //levelLeft += abs( data[i].left ) / peakValue;
209 //levelRight += abs(data[i].right)/peakValue;
210 }
211
212}
213*/
214
215
216void SWFFTCalc::bareFFT( CArray& x )
217{
218
219 // fft function.
220 //
221 // ATTENTION ////////////////
222 //
223 // size of x must be a power of two
224 //
226
227 const size_t N = x.size();
228 if (N <= 1)
229 return;
230
231 // divide
232 CArray even = x[std::slice( 0, N/2, 2 )];
233 CArray odd = x[std::slice( 1, N/2, 2 )];
234
235 // conquer
236 bareFFT( even );
237 bareFFT( odd );
238
239 // combine
240 for (size_t k = 0; k < N/2; ++k)
241 {
242 Complex t = std::polar( 1.0, -2 * PI * k / N ) * odd[k];
243 x[k ] = even[k] + t;
244 x[k+N/2] = even[k] - t;
245 }
246
247}
248
249
250/*
251switch( format.sampleType() )
252{
253
254 case QAudioFormat::Float:
255
256 qDebug() << "QAudioFormat::Float:";
257 if( format.sampleSize() != 32 ) // other sample formats are not supported
258 prepareInput( audiobuffer, _s32f, 0 );
259 else
260 prepareInput( audiobuffer, _s32f, 1.00003 );
261
262 break;
263
264 case QAudioFormat::SignedInt:
265
266 qDebug() << "QAudioFormat::SignedInt:";
267 if( format.sampleSize() == 32 )
268
269#ifdef Q_OS_WIN
270 prepareInput( audiobuffer, _s16s, INT_MAX );
271#endif
272#ifdef Q_OS_UNIX
273 prepareInput( audiobuffer, _s16s, SHRT_MAX );
274#endif
275
276 else if( format.sampleSize() == 16 )
277 prepareInput( audiobuffer, _s16s, SHRT_MAX );
278 else if( format.sampleSize() == 8 )
279 prepareInput( audiobuffer, _s8s, CHAR_MAX );
280
281 break;
282
283 case QAudioFormat::UnSignedInt:
284
285 qDebug() << "QAudioFormat::UnSignedInt:";
286 if( format.sampleSize() == 32 )
287 prepareInput( audiobuffer, _s16s, UINT_MAX );
288 else if( format.sampleSize() == 16 )
289 prepareInput( audiobuffer, _s16s, USHRT_MAX );
290 else if( format.sampleSize() == 8 )
291 prepareInput( audiobuffer, _s16s, UCHAR_MAX );
292
293 default:
294
295 break;
296
297} // switch
298*/
299
300
301
302/*
303qreal getPeakValue(const QAudioFormat& format)
304{
305 // Note: Only the most common sample formats are supported
306 if (!format.isValid())
307 return qreal(0);
308
309 if (format.codec() != "audio/pcm")
310 return qreal(0);
311
312 switch (format.sampleType()) {
313 case QAudioFormat::Unknown:
314 break;
315 case QAudioFormat::Float:
316 if (format.sampleSize() != 32) // other sample formats are not supported
317 return qreal(0);
318 return qreal(1.00003);
319 case QAudioFormat::SignedInt:
320 if (format.sampleSize() == 32)
321#ifdef Q_OS_WIN
322 return qreal(INT_MAX);
323#endif
324#ifdef Q_OS_UNIX
325 return qreal(SHRT_MAX);
326#endif
327 if (format.sampleSize() == 16)
328 return qreal(SHRT_MAX);
329 if (format.sampleSize() == 8)
330 return qreal(CHAR_MAX);
331 break;
332 case QAudioFormat::UnSignedInt:
333 if (format.sampleSize() == 32)
334 return qreal(UINT_MAX);
335 if (format.sampleSize() == 16)
336 return qreal(USHRT_MAX);
337 if (format.sampleSize() == 8)
338 return qreal(UCHAR_MAX);
339 break;
340 }
341
342 return qreal(0);
343}
344*/
void collectFrames(QAudioBuffer &audiobuffer)
Track::getPeakValue.
Definition: swfftcalc.cpp:120