File indexing completed on 2025-01-05 04:25:40
0001 /**************************************************************************************** 0002 * Copyright (c) 2003 Max Howell <max.howell@methylblue.com> * 0003 * Copyright (c) 2009 Martin Sandsmark <martin.sandsmark@kde.org> * 0004 * Copyright (c) 2013 Mark Kretschmann <kretschmann@kde.org> * 0005 * Copyright (c) 2017 Malte Veerman <malte.veerman@gmail.com> * 0006 * * 0007 * This program is free software; you can redistribute it and/or modify it under * 0008 * the terms of the GNU General Public License as published by the Free Software * 0009 * Foundation; either version 2 of the License, or (at your option) any later * 0010 * version. * 0011 * * 0012 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0013 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0014 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0015 * * 0016 * You should have received a copy of the GNU General Public License along with * 0017 * this program. If not, see <http://www.gnu.org/licenses/>. * 0018 ****************************************************************************************/ 0019 0020 #include "AnalyzerBase.h" 0021 #include "AnalyzerWorker.h" 0022 0023 #include "core/support/Amarok.h" 0024 #include "core/support/Debug.h" 0025 #include "EngineController.h" 0026 #include "MainWindow.h" 0027 0028 #include <cmath> 0029 0030 #ifdef Q_WS_X11 0031 #include <KWindowSystem> 0032 #endif 0033 0034 #include <QQuickWindow> 0035 #include <QTimer> 0036 0037 0038 // INSTRUCTIONS 0039 // 1. Reimplement QQuickFramebufferObject::createRenderer(). 0040 // 2. Reimplement Analyzer::Base::createWorker(). 0041 // 3. Set your preferred scope width with setScopeSize(). 0042 0043 0044 Analyzer::Base::Base( QQuickItem *parent ) 0045 : QQuickFramebufferObject( parent ) 0046 , m_sampleRate( 44100 ) 0047 , m_scopeSize( 0 ) 0048 , m_worker( nullptr ) 0049 { 0050 DEBUG_BLOCK 0051 0052 qRegisterMetaType<WindowFunction>("WindowFunction"); 0053 0054 m_minFreq = config().readEntry( "minFreq", 50.0 ); 0055 m_maxFreq = config().readEntry( "maxFreq", 15000.0 ); 0056 0057 connect( The::engineController(), &EngineController::trackChanged, this, &Base::refreshSampleRate ); 0058 connect( The::engineController(), &EngineController::trackMetadataChanged, this, &Base::refreshSampleRate ); 0059 0060 #ifdef Q_WS_X11 0061 connect( KWindowSystem::self(), &KWindowSystem::currentDesktopChanged, this, &Base::currentDesktopChanged ); 0062 #endif 0063 0064 QTimer::singleShot( 0, this, &Base::connectSignals ); 0065 } 0066 0067 Analyzer::Base::~Base() 0068 { 0069 DEBUG_BLOCK 0070 0071 if( m_worker ) 0072 { 0073 m_worker->deleteLater(); 0074 m_worker = nullptr; 0075 } 0076 0077 m_workerThread.quit(); 0078 m_workerThread.wait(); 0079 } 0080 0081 void 0082 Analyzer::Base::connectSignals() 0083 { 0084 DEBUG_BLOCK 0085 0086 if( !m_worker ) 0087 { 0088 m_worker = createWorker(); 0089 m_worker->setSampleSize( sampleSize() ); 0090 m_worker->setScopeSize( m_scopeSize ); 0091 m_worker->setWindowFunction( windowFunction() ); 0092 m_worker->moveToThread( &m_workerThread ); 0093 m_workerThread.start(); 0094 0095 connect( this, &Base::calculateExpFactorNeeded, m_worker, &Worker::calculateExpFactor ); 0096 connect( this, &Base::windowFunctionChanged, m_worker, &Worker::setWindowFunction ); 0097 connect( this, &Base::sampleSizeChanged, m_worker, &Worker::setSampleSize ); 0098 connect( this, &Base::scopeSizeChanged, m_worker, &Worker::setScopeSize ); 0099 connect( The::engineController(), &EngineController::playbackStateChanged, m_worker, &Worker::playbackStateChanged ); 0100 connect( The::engineController(), &EngineController::audioDataReady, m_worker, &Worker::receiveData, Qt::DirectConnection ); 0101 0102 setSampleSize( config().readEntry( "sampleSize", 4096 ) ); 0103 setWindowFunction( (WindowFunction) config().readEntry( "windowFunction", (int)Hann ) ); 0104 Q_EMIT calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate); 0105 } 0106 } 0107 0108 void 0109 Analyzer::Base::disconnectSignals() 0110 { 0111 DEBUG_BLOCK 0112 0113 if( m_worker ) 0114 disconnect( The::engineController(), &EngineController::audioDataReady, m_worker, &Worker::receiveData ); 0115 } 0116 0117 void 0118 Analyzer::Base::currentDesktopChanged() 0119 { 0120 // Optimization for X11/Linux desktops: 0121 // Don't update the analyzer if Amarok is not on the active virtual desktop. 0122 0123 if( The::mainWindow()->isOnCurrentDesktop() ) 0124 connectSignals(); 0125 else 0126 disconnectSignals(); 0127 } 0128 0129 void 0130 Analyzer::Base::refreshSampleRate() 0131 { 0132 const auto currentTrack = The::engineController()->currentTrack(); 0133 int sampleRate = currentTrack ? currentTrack->sampleRate() : 44100; 0134 0135 if( m_sampleRate == sampleRate ) 0136 return; 0137 0138 m_sampleRate = sampleRate; 0139 0140 Q_EMIT calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate ); 0141 } 0142 0143 KConfigGroup 0144 Analyzer::Base::config() const 0145 { 0146 return Amarok::config( QStringLiteral( "Context" ) ).group( "Analyzer" ); 0147 } 0148 0149 void 0150 Analyzer::Base::setScopeSize( int scopeSize ) 0151 { 0152 if( scopeSize <= 0 ) 0153 { 0154 debug() << "Scope size must be greater than zero"; 0155 return; 0156 } 0157 0158 if( m_scopeSize == scopeSize ) 0159 return; 0160 0161 m_scopeSize = scopeSize; 0162 Q_EMIT scopeSizeChanged( scopeSize ); 0163 Q_EMIT calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate ); 0164 } 0165 0166 void 0167 Analyzer::Base::setMaxFreq( qreal maxFreq ) 0168 { 0169 DEBUG_BLOCK 0170 0171 debug() << "Set maximum frequency to:" << maxFreq; 0172 0173 if( m_maxFreq == maxFreq || maxFreq < 0.0 ) 0174 return; 0175 0176 config().writeEntry( "maxFreq", maxFreq ); 0177 m_maxFreq = maxFreq; 0178 Q_EMIT maxFreqChanged(); 0179 Q_EMIT calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate ); 0180 } 0181 0182 void 0183 Analyzer::Base::setMinFreq( qreal minFreq ) 0184 { 0185 DEBUG_BLOCK 0186 0187 debug() << "Set minimum frequency to:" << minFreq; 0188 0189 if( m_minFreq == minFreq || minFreq <= 0.0 ) 0190 return; 0191 0192 config().writeEntry( "minFreq", minFreq ); 0193 m_minFreq = minFreq; 0194 Q_EMIT minFreqChanged(); 0195 Q_EMIT calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate ); 0196 } 0197 0198 Analyzer::Base::WindowFunction 0199 Analyzer::Base::windowFunction() const 0200 { 0201 return (WindowFunction)config().readEntry( "windowFunction", (int)Hann ); 0202 } 0203 0204 void 0205 Analyzer::Base::setWindowFunction( WindowFunction windowFunction ) 0206 { 0207 DEBUG_BLOCK 0208 0209 debug() << "Set window function to:" << windowFunction; 0210 0211 config().writeEntry( "windowFunction", (int)windowFunction ); 0212 Q_EMIT windowFunctionChanged( windowFunction ); 0213 } 0214 0215 int Analyzer::Base::sampleSize() const 0216 { 0217 return config().readEntry( "sampleSize", 2048 ); 0218 } 0219 0220 void 0221 Analyzer::Base::setSampleSize( uint sampleSize ) 0222 { 0223 DEBUG_BLOCK 0224 0225 debug() << "Set sample size to:" << sampleSize; 0226 0227 if( sampleSize < (int) EngineController::DATAOUTPUT_DATA_SIZE ) 0228 { 0229 warning() << "Sample size must be at least" << EngineController::DATAOUTPUT_DATA_SIZE; 0230 sampleSize = EngineController::DATAOUTPUT_DATA_SIZE; 0231 } 0232 0233 config().writeEntry( "sampleSize", sampleSize ); 0234 Q_EMIT sampleSizeChanged( sampleSize ); 0235 Q_EMIT calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate ); 0236 } 0237 0238 const Analyzer::Worker * 0239 Analyzer::Base::worker() const 0240 { 0241 return const_cast<const Worker*>( m_worker ); 0242 } 0243