Changeset View
Changeset View
Standalone View
Standalone View
src/context/applets/analyzer/plugin/AnalyzerBase.cpp
- This file was added.
1 | /**************************************************************************************** | ||||
---|---|---|---|---|---|
2 | * Copyright (c) 2003 Max Howell <max.howell@methylblue.com> * | ||||
3 | * Copyright (c) 2009 Martin Sandsmark <martin.sandsmark@kde.org> * | ||||
4 | * Copyright (c) 2013 Mark Kretschmann <kretschmann@kde.org> * | ||||
5 | * Copyright (c) 2017 Malte Veerman <malte.veerman@gmail.com> * | ||||
6 | * * | ||||
7 | * This program is free software; you can redistribute it and/or modify it under * | ||||
8 | * the terms of the GNU General Public License as published by the Free Software * | ||||
9 | * Foundation; either version 2 of the License, or (at your option) any later * | ||||
10 | * version. * | ||||
11 | * * | ||||
12 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY * | ||||
13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * | ||||
14 | * PARTICULAR PURPOSE. See the GNU General Public License for more details. * | ||||
15 | * * | ||||
16 | * You should have received a copy of the GNU General Public License along with * | ||||
17 | * this program. If not, see <http://www.gnu.org/licenses/>. * | ||||
18 | ****************************************************************************************/ | ||||
19 | | ||||
20 | #include "AnalyzerBase.h" | ||||
21 | #include "AnalyzerWorker.h" | ||||
22 | | ||||
23 | #include "core/support/Amarok.h" | ||||
24 | #include "core/support/Debug.h" | ||||
25 | #include "EngineController.h" | ||||
26 | #include "MainWindow.h" | ||||
27 | | ||||
28 | #include <cmath> | ||||
29 | | ||||
30 | #ifdef Q_WS_X11 | ||||
31 | #include <KWindowSystem> | ||||
32 | #endif | ||||
33 | | ||||
34 | #include <QQuickWindow> | ||||
35 | #include <QTimer> | ||||
36 | | ||||
37 | | ||||
38 | // INSTRUCTIONS | ||||
39 | // 1. Reimplement QQuickFramebufferObject::createRenderer(). | ||||
40 | // 2. Reimplement Analyzer::Base::createWorker(). | ||||
41 | // 3. Set your preferred scope width with setScopeSize(). | ||||
42 | | ||||
43 | | ||||
44 | Analyzer::Base::Base( QQuickItem *parent ) | ||||
45 | : QQuickFramebufferObject( parent ) | ||||
46 | , m_sampleRate( 44100 ) | ||||
47 | , m_scopeSize( 0 ) | ||||
48 | , m_worker( Q_NULLPTR ) | ||||
49 | { | ||||
50 | DEBUG_BLOCK | ||||
51 | | ||||
52 | qRegisterMetaType<WindowFunction>(); | ||||
53 | | ||||
54 | m_minFreq = config().readEntry( "minFreq", 50.0 ); | ||||
55 | m_maxFreq = config().readEntry( "maxFreq", 15000.0 ); | ||||
56 | | ||||
57 | connect( The::engineController(), &EngineController::trackChanged, this, &Base::refreshSampleRate ); | ||||
58 | connect( The::engineController(), &EngineController::trackMetadataChanged, this, &Base::refreshSampleRate ); | ||||
59 | | ||||
60 | #ifdef Q_WS_X11 | ||||
61 | connect( KWindowSystem::self(), &KWindowSystem::currentDesktopChanged, this, &Base::currentDesktopChanged ); | ||||
62 | #endif | ||||
63 | | ||||
64 | QTimer::singleShot( 0, this, &Base::connectSignals ); | ||||
65 | } | ||||
66 | | ||||
67 | Analyzer::Base::~Base() | ||||
68 | { | ||||
69 | DEBUG_BLOCK | ||||
70 | | ||||
71 | m_worker->deleteLater(); | ||||
72 | m_worker = Q_NULLPTR; | ||||
73 | m_workerThread.quit(); | ||||
74 | m_workerThread.wait(); | ||||
75 | } | ||||
76 | | ||||
77 | void | ||||
78 | Analyzer::Base::connectSignals() | ||||
79 | { | ||||
80 | DEBUG_BLOCK | ||||
81 | | ||||
82 | if( !m_worker ) | ||||
83 | { | ||||
84 | m_worker = createWorker(); | ||||
85 | m_worker->setSampleSize( sampleSize() ); | ||||
86 | m_worker->setScopeSize( m_scopeSize ); | ||||
87 | m_worker->setWindowFunction( windowFunction() ); | ||||
88 | m_worker->moveToThread( &m_workerThread ); | ||||
89 | m_workerThread.start(); | ||||
90 | | ||||
91 | connect( this, &Base::calculateExpFactorNeeded, m_worker, &Worker::calculateExpFactor ); | ||||
92 | connect( this, &Base::windowFunctionChanged, m_worker, &Worker::setWindowFunction ); | ||||
93 | connect( this, &Base::sampleSizeChanged, m_worker, &Worker::setSampleSize ); | ||||
94 | connect( this, &Base::scopeSizeChanged, m_worker, &Worker::setScopeSize ); | ||||
95 | connect( The::engineController(), &EngineController::playbackStateChanged, m_worker, &Worker::playbackStateChanged ); | ||||
96 | connect( The::engineController(), &EngineController::audioDataReady, m_worker, &Worker::receiveData, Qt::DirectConnection ); | ||||
97 | | ||||
98 | setSampleSize( config().readEntry( "sampleSize", 4096 ) ); | ||||
99 | setWindowFunction( (WindowFunction) config().readEntry( "windowFunction", (int)Hann ) ); | ||||
100 | emit calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate); | ||||
101 | } | ||||
102 | } | ||||
103 | | ||||
104 | void | ||||
105 | Analyzer::Base::disconnectSignals() | ||||
106 | { | ||||
107 | DEBUG_BLOCK | ||||
108 | | ||||
109 | if( m_worker ) | ||||
110 | disconnect( The::engineController(), &EngineController::audioDataReady, m_worker, &Worker::receiveData ); | ||||
111 | } | ||||
112 | | ||||
113 | void | ||||
114 | Analyzer::Base::currentDesktopChanged() | ||||
115 | { | ||||
116 | // Optimization for X11/Linux desktops: | ||||
117 | // Don't update the analyzer if Amarok is not on the active virtual desktop. | ||||
118 | | ||||
119 | if( The::mainWindow()->isOnCurrentDesktop() ) | ||||
120 | connectSignals(); | ||||
121 | else | ||||
122 | disconnectSignals(); | ||||
123 | } | ||||
124 | | ||||
125 | void | ||||
126 | Analyzer::Base::refreshSampleRate() | ||||
127 | { | ||||
128 | const auto currentTrack = The::engineController()->currentTrack(); | ||||
129 | int sampleRate = currentTrack ? currentTrack->sampleRate() : 44100; | ||||
130 | | ||||
131 | if( m_sampleRate == sampleRate ) | ||||
132 | return; | ||||
133 | | ||||
134 | m_sampleRate = sampleRate; | ||||
135 | | ||||
136 | emit calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate ); | ||||
137 | } | ||||
138 | | ||||
139 | KConfigGroup | ||||
140 | Analyzer::Base::config() const | ||||
141 | { | ||||
142 | return Amarok::config( QStringLiteral( "Context" ) ).group( "Analyzer" ); | ||||
143 | } | ||||
144 | | ||||
145 | void | ||||
146 | Analyzer::Base::setScopeSize( int scopeSize ) | ||||
147 | { | ||||
148 | if( scopeSize <= 0 ) | ||||
149 | { | ||||
150 | debug() << "Scope size must be greater than zero"; | ||||
151 | return; | ||||
152 | } | ||||
153 | | ||||
154 | if( m_scopeSize == scopeSize ) | ||||
155 | return; | ||||
156 | | ||||
157 | m_scopeSize = scopeSize; | ||||
158 | emit scopeSizeChanged( scopeSize ); | ||||
159 | emit calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate ); | ||||
160 | } | ||||
161 | | ||||
162 | void | ||||
163 | Analyzer::Base::setMaxFreq( qreal maxFreq ) | ||||
164 | { | ||||
165 | DEBUG_BLOCK | ||||
166 | | ||||
167 | debug() << "Set maximum frequency to:" << maxFreq; | ||||
168 | | ||||
169 | if( m_maxFreq == maxFreq || maxFreq < 0.0 ) | ||||
170 | return; | ||||
171 | | ||||
172 | config().writeEntry( "maxFreq", maxFreq ); | ||||
173 | m_maxFreq = maxFreq; | ||||
174 | emit maxFreqChanged(); | ||||
175 | emit calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate ); | ||||
176 | } | ||||
177 | | ||||
178 | void | ||||
179 | Analyzer::Base::setMinFreq( qreal minFreq ) | ||||
180 | { | ||||
181 | DEBUG_BLOCK | ||||
182 | | ||||
183 | debug() << "Set minimum frequency to:" << minFreq; | ||||
184 | | ||||
185 | if( m_minFreq == minFreq || minFreq <= 0.0 ) | ||||
186 | return; | ||||
187 | | ||||
188 | config().writeEntry( "minFreq", minFreq ); | ||||
189 | m_minFreq = minFreq; | ||||
190 | emit minFreqChanged(); | ||||
191 | emit calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate ); | ||||
192 | } | ||||
193 | | ||||
194 | Analyzer::Base::WindowFunction | ||||
195 | Analyzer::Base::windowFunction() const | ||||
196 | { | ||||
197 | return (WindowFunction)config().readEntry( "windowFunction", (int)Hann ); | ||||
198 | } | ||||
199 | | ||||
200 | void | ||||
201 | Analyzer::Base::setWindowFunction( WindowFunction windowFunction ) | ||||
202 | { | ||||
203 | DEBUG_BLOCK | ||||
204 | | ||||
205 | debug() << "Set window function to:" << windowFunction; | ||||
206 | | ||||
207 | config().writeEntry( "windowFunction", (int)windowFunction ); | ||||
208 | emit windowFunctionChanged( windowFunction ); | ||||
209 | } | ||||
210 | | ||||
211 | int Analyzer::Base::sampleSize() const | ||||
212 | { | ||||
213 | return config().readEntry( "sampleSize", 2048 ); | ||||
214 | } | ||||
215 | | ||||
216 | void | ||||
217 | Analyzer::Base::setSampleSize( uint sampleSize ) | ||||
218 | { | ||||
219 | DEBUG_BLOCK | ||||
220 | | ||||
221 | debug() << "Set sample size to:" << sampleSize; | ||||
222 | | ||||
223 | if( sampleSize < (int) EngineController::DATAOUTPUT_DATA_SIZE ) | ||||
224 | { | ||||
225 | warning() << "Sample size must be at least" << EngineController::DATAOUTPUT_DATA_SIZE; | ||||
226 | sampleSize = EngineController::DATAOUTPUT_DATA_SIZE; | ||||
227 | } | ||||
228 | | ||||
229 | config().writeEntry( "sampleSize", sampleSize ); | ||||
230 | emit sampleSizeChanged( sampleSize ); | ||||
231 | emit calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate ); | ||||
232 | } | ||||
233 | | ||||
234 | const Analyzer::Worker * | ||||
235 | Analyzer::Base::worker() const | ||||
236 | { | ||||
237 | return const_cast<const Worker*>( m_worker ); | ||||
238 | } | ||||
239 | |