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 }