File indexing completed on 2025-01-05 04:25:40
0001 /* 0002 * Copyright 2018 Malte Veerman <malte.veerman@gmail.com> 0003 * 0004 * This program is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU General Public License as 0006 * published by the Free Software Foundation; either version 2 of 0007 * the License or (at your option) version 3 or any later version 0008 * accepted by the membership of KDE e.V. (or its successor approved 0009 * by the membership of KDE e.V.), which shall act as a proxy 0010 * defined in Section 14 of version 3 of the license. 0011 * 0012 * This program is distributed in the hope that it will be useful, 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0015 * GNU General Public License for more details. 0016 * 0017 * You should have received a copy of the GNU General Public License 0018 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0019 */ 0020 0021 #include "AnalyzerWorker.h" 0022 0023 #include "core/support/Debug.h" 0024 #include "EngineController.h" 0025 0026 #include <QThread> 0027 #include <QTimer> 0028 0029 0030 Analyzer::Worker::Worker() 0031 : m_currentScope( QVector<double>( 1, 0.0 ) ) 0032 , m_size( 0 ) 0033 , m_windowFunction( Base::Hann ) 0034 , m_expectedDataTime( 20 ) 0035 , m_demoT( 201 ) 0036 , m_lastUpdate() 0037 , m_demoTimer( new QTimer( this ) ) 0038 , m_processTimer( new QTimer( this ) ) 0039 { 0040 m_in = (double*) fftw_malloc( m_size * sizeof( double ) ); 0041 m_out = (std::complex<double>*) fftw_malloc( ( m_size / 2 + 1 ) * sizeof( std::complex<double> ) ); 0042 m_plan = fftw_plan_dft_r2c_1d( m_size, m_in, reinterpret_cast<fftw_complex*>( m_out ), FFTW_ESTIMATE ); 0043 0044 m_demoTimer->setInterval( Analyzer::Base::DEMO_INTERVAL ); 0045 m_processTimer->setInterval( PROCESSING_INTERVAL ); 0046 if( EngineController::instance()->isPlaying() ) 0047 m_processTimer->start(); 0048 else 0049 m_demoTimer->start(); 0050 m_lastUpdate.start(); 0051 0052 connect( m_demoTimer, &QTimer::timeout, this, &Worker::demo ); 0053 connect( m_processTimer, &QTimer::timeout, this, &Worker::processData ); 0054 } 0055 0056 Analyzer::Worker::~Worker() 0057 { 0058 fftw_destroy_plan( m_plan ); 0059 fftw_free( m_in ); 0060 fftw_free( m_out ); 0061 } 0062 0063 void Analyzer::Worker::receiveData( const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > &newData ) 0064 { 0065 const int newDataSize = EngineController::DATAOUTPUT_DATA_SIZE; 0066 0067 if( newData.isEmpty() || newData[Phonon::AudioDataOutput::LeftChannel].size() != newDataSize ) 0068 return; 0069 0070 m_rawInMutex.lock(); 0071 0072 for( int x = 0; x < newDataSize; x++ ) 0073 { 0074 if( newData.size() == 1 ) // Mono 0075 { 0076 m_rawIn << double( newData[Phonon::AudioDataOutput::LeftChannel][x] ); 0077 } 0078 else // Anything > Mono is treated as Stereo 0079 { 0080 m_rawIn << ( double( newData[Phonon::AudioDataOutput::LeftChannel][x] ) 0081 + double( newData[Phonon::AudioDataOutput::RightChannel][x] ) ) 0082 / 2; // Average between the channels 0083 } 0084 m_rawIn.last() /= ( 1 << 15 ); // Scale to [0, 1] 0085 } 0086 0087 m_rawInMutex.unlock(); 0088 } 0089 0090 void Analyzer::Worker::processData() 0091 { 0092 int timeElapsed = m_lastUpdate.elapsed(); 0093 0094 // Delay if processing is too fast 0095 if( timeElapsed < m_expectedDataTime - 1 ) 0096 QThread::currentThread()->msleep( m_expectedDataTime - timeElapsed - 1 ); 0097 0098 applyWindowFunction(); 0099 } 0100 0101 void Analyzer::Worker::applyWindowFunction() 0102 { 0103 m_rawInMutex.lock(); 0104 0105 if( m_rawIn.size() < (int)m_size ) 0106 { 0107 m_rawInMutex.unlock(); 0108 return; 0109 } 0110 0111 const int newDataSize = EngineController::DATAOUTPUT_DATA_SIZE; 0112 0113 while( m_rawIn.size() > (int)m_size + DATA_BUFFER_SIZE * newDataSize ) 0114 m_rawIn.removeFirst(); 0115 0116 // Apply window function 0117 for( uint i = 0; i < m_size; i++ ) 0118 { 0119 double windowFactor; 0120 switch( m_windowFunction ) 0121 { 0122 case Base::Rectangular: 0123 { 0124 windowFactor = 1.0; 0125 break; 0126 } 0127 case Base::Hann: 0128 { 0129 windowFactor = ( 1.0 - cos( 2.0 * M_PI * i / ( m_size - 1 ) ) ) / 2.0; 0130 break; 0131 } 0132 case Base::Nuttall: 0133 { 0134 const double a = 0.355768; 0135 const double b = 0.487396 * cos( 2 * M_PI * i / ( m_size - 1 ) ); 0136 const double c = 0.144232 * cos( 4 * M_PI * i / ( m_size - 1 ) ); 0137 const double d = 0.012604 * cos( 6 * M_PI * i / ( m_size - 1 ) ); 0138 windowFactor = a - b + c - d; 0139 break; 0140 } 0141 case Base::Lanczos: 0142 { 0143 const double x = 2.0 * i / ( m_size - 1 ) - 1; 0144 windowFactor = sin( M_PI * x ) / M_PI / x; 0145 break; 0146 } 0147 case Base::Sine: 0148 { 0149 windowFactor = ( M_PI * i ) / ( m_size - 1 ); 0150 break; 0151 } 0152 }; 0153 0154 if( i < newDataSize ) 0155 m_in[i] = m_rawIn.takeFirst() * windowFactor; 0156 else 0157 m_in[i] = m_rawIn.at( i - newDataSize ) * windowFactor; 0158 } 0159 0160 m_rawInMutex.unlock(); 0161 0162 fftw_execute( m_plan ); 0163 makeScope(); 0164 } 0165 0166 void Analyzer::Worker::makeScope() 0167 { 0168 for( const auto& band : m_notInterpolatedScopeBands ) 0169 { 0170 m_currentScope[band.scopeIndex] = 0.0; 0171 uint numValues = 0; 0172 for( long k = std::lround( std::ceil( band.lowerK ) ); k <= std::lround( std::floor( band.upperK ) ); k++ ) 0173 { 0174 m_currentScope[band.scopeIndex] += std::abs( m_out[k] ) * sqrt( k ); 0175 numValues++; 0176 } 0177 m_currentScope[band.scopeIndex] /= numValues; 0178 m_currentScope[band.scopeIndex] /= m_size / 2; 0179 } 0180 0181 // monotone cubic interpolation 0182 if( !m_interpolatedScopeBands.isEmpty() ) 0183 { 0184 QVector<QPointF> data; 0185 for( uint k = 0; k < m_size / 2 + 1 && k <= m_interpolatedScopeBands.last().midK; k++ ) 0186 { 0187 data << QPointF( k, std::abs( m_out[k] ) * sqrt( k ) / m_size * 2 ); 0188 } 0189 // Get consecutive differences and slopes 0190 QVector<double> dys, dxs, ms; 0191 for( int i = 0; i < data.size() - 1; i++ ) 0192 { 0193 double dx = data[i + 1].x() - data[i].x(); 0194 double dy = data[i + 1].y() - data[i].y(); 0195 dxs << dx; 0196 dys << dy; 0197 ms << dy / dx; 0198 } 0199 // Get degree-1 coefficients 0200 QVector<double> c1s = QVector<double>() << ms[0]; 0201 for( int i = 0; i < dxs.size() - 1; i++) 0202 { 0203 double m = ms[i], mNext = ms[i + 1]; 0204 if( m * mNext <= 0 ) 0205 c1s << 0.0; 0206 else 0207 { 0208 double dx_ = dxs[i], dxNext = dxs[i + 1], common = dx_ + dxNext; 0209 c1s << ( 3 * common / ( ( common + dxNext ) / m + ( common + dx_ ) / mNext ) ); 0210 } 0211 } 0212 c1s << ms.last(); 0213 // Get degree-2 and degree-3 coefficients 0214 QVector<double> c2s, c3s; 0215 for( int i = 0; i < c1s.size() - 1; i++ ) 0216 { 0217 double c1 = c1s[i], m_ = ms[i], invDx = 1 / dxs[i], common_ = c1 + c1s[i + 1] - m_ - m_; 0218 c2s << ( m_ - c1 - common_ ) * invDx; 0219 c3s << common_ * invDx * invDx; 0220 } 0221 // write interpolated data to scope 0222 for( auto &band : m_interpolatedScopeBands ) 0223 { 0224 const double x = band.midK; 0225 auto &scope = m_currentScope[band.scopeIndex]; 0226 0227 // Search for the interval x is in, returning the corresponding y if x is one of the original xs 0228 int low = 0, mid, high = c3s.size() - 1; 0229 while ( low <= high ) 0230 { 0231 mid = std::floor( 0.5 * ( low + high ) ); 0232 double xHere = data[mid].x(); 0233 if( xHere < x ) 0234 low = mid + 1; 0235 else if( xHere > x ) 0236 high = mid - 1; 0237 else 0238 scope = data[mid].y(); 0239 } 0240 int i = qMax( 0, high ); 0241 0242 // Interpolate 0243 double diff = x - data[i].x(), diffSq = diff * diff; 0244 scope = qMax( 0.0, data[i].y() + c1s[i] * diff + c2s[i] * diffSq + c3s[i] * diff * diffSq ); 0245 } 0246 } 0247 0248 analyze(); 0249 } 0250 0251 void Analyzer::Worker::setSampleSize( uint size ) 0252 { 0253 if( m_size == size ) 0254 return; 0255 0256 m_size = size; 0257 0258 fftw_destroy_plan( m_plan ); 0259 fftw_free( m_in ); 0260 fftw_free( m_out ); 0261 0262 m_in = (double*) fftw_malloc( m_size * sizeof( double ) ); 0263 m_out = (std::complex<double>*) fftw_malloc( ( m_size / 2 + 1 ) * sizeof( std::complex<double> ) ); 0264 m_plan = fftw_plan_dft_r2c_1d( m_size, m_in, reinterpret_cast<fftw_complex*>( m_out ), FFTW_ESTIMATE ); 0265 } 0266 0267 void Analyzer::Worker::setWindowFunction( Base::WindowFunction windowFunction ) 0268 { 0269 if( m_windowFunction == windowFunction ) 0270 return; 0271 0272 m_windowFunction = windowFunction; 0273 } 0274 0275 void Analyzer::Worker::setScopeSize( int size ) 0276 { 0277 m_currentScope.resize( size ); 0278 } 0279 0280 void Analyzer::Worker::calculateExpFactor( qreal minFreq, qreal maxFreq, int sampleRate ) 0281 { 0282 DEBUG_BLOCK 0283 0284 if( minFreq <= 0.0 ) 0285 { 0286 warning() << "Minimum frequency must be greater than zero!"; 0287 minFreq = 1.0; 0288 } 0289 0290 if( minFreq >= maxFreq ) 0291 { 0292 warning() << "Minimum frequency must be smaller than maximum frequency!"; 0293 maxFreq = minFreq + 1.0; 0294 } 0295 0296 if( sampleRate == 0 ) 0297 { 0298 debug() << "Reported impossible sample rate of zero. Assuming 44.1KHz."; 0299 sampleRate = 44100; 0300 } 0301 0302 m_expFactor = pow( maxFreq / minFreq, 1.0 / m_currentScope.size() ); 0303 m_expectedDataTime = std::floor( (qreal)EngineController::DATAOUTPUT_DATA_SIZE * 1000.0 / sampleRate ); 0304 0305 m_interpolatedScopeBands.clear(); 0306 m_notInterpolatedScopeBands.clear(); 0307 const uint outputSize = m_size / 2 + 1; 0308 0309 for( int scopeIndex = 0; scopeIndex < m_currentScope.size(); scopeIndex++ ) 0310 { 0311 BandInfo newBandInfo; 0312 newBandInfo.lowerFreq = minFreq * pow( m_expFactor, double( scopeIndex ) - 0.5 ); 0313 newBandInfo.midFreq = minFreq * pow( m_expFactor, scopeIndex ); 0314 newBandInfo.upperFreq = minFreq * pow( m_expFactor, double( scopeIndex ) + 0.5 ); 0315 newBandInfo.lowerK = newBandInfo.lowerFreq / ( sampleRate / 2 ) * outputSize; 0316 newBandInfo.midK = newBandInfo.midFreq / ( sampleRate / 2 ) * outputSize; 0317 newBandInfo.upperK = newBandInfo.upperFreq / ( sampleRate / 2 ) * outputSize; 0318 newBandInfo.scopeIndex = scopeIndex; 0319 0320 if ( std::floor( newBandInfo.upperK ) >= std::ceil( newBandInfo.lowerK ) ) 0321 m_notInterpolatedScopeBands << newBandInfo; 0322 else 0323 m_interpolatedScopeBands << newBandInfo; 0324 } 0325 } 0326 0327 void Analyzer::Worker::demo() 0328 { 0329 if( m_demoT > 300 ) 0330 m_demoT = 1; //0 = wasted calculations 0331 0332 if( m_demoT < 201 ) 0333 { 0334 const double dt = double( m_demoT ) / 200; 0335 for( int i = 0; i < m_currentScope.size(); ++i ) 0336 { 0337 m_currentScope[i] = dt * ( sin( M_PI + ( i * M_PI ) / m_currentScope.size() ) + 1.0 ); 0338 } 0339 } 0340 else 0341 { 0342 for( int i = 0; i < m_currentScope.size(); ++i ) 0343 { 0344 m_currentScope[i] = 0.0; 0345 } 0346 } 0347 0348 ++m_demoT; 0349 0350 int timeElapsed = m_lastUpdate.elapsed(); 0351 0352 // Delay if interval is too low 0353 if( timeElapsed < Analyzer::Base::DEMO_INTERVAL - 1 ) 0354 QThread::currentThread()->msleep( Analyzer::Base::DEMO_INTERVAL - 1 - timeElapsed ); 0355 0356 m_lastUpdate.restart(); 0357 0358 analyze(); 0359 } 0360 0361 void Analyzer::Worker::playbackStateChanged() 0362 { 0363 bool playing = EngineController::instance()->isPlaying(); 0364 playing ? m_demoTimer->stop() : m_demoTimer->start(); 0365 playing ? m_processTimer->start() : m_processTimer->stop(); 0366 resetDemo(); 0367 }