1 /***************************************************************************
2 * Copyright (C) 2006-2021 by Ilya Kotov *
3 * forkotov02@ya.ru *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
20
21 #include <QIODevice>
22 #include <QFile>
23 #include <QApplication>
24 #include <QSettings>
25 #include <QDir>
26 #include "qmmpevents_p.h"
27 #include "qmmpaudioengine_p.h"
28 #include "decoderfactory.h"
29 #include "effect.h"
30 #include "statehandler.h"
31 #include "inputsource.h"
32 #include "volumehandler.h"
33 #include "enginefactory.h"
34 #include "metadatamanager.h"
35 #include "qmmpsettings.h"
36 #include "soundcore.h"
37
38 SoundCore *SoundCore::m_instance = nullptr;
39
SoundCore(QObject * parent)40 SoundCore::SoundCore(QObject *parent)
41 : QObject(parent)
42 {
43 if(m_instance)
44 qFatal("SoundCore: only one instance is allowed");
45 qRegisterMetaType<Qmmp::State>("Qmmp::State");
46 m_instance = this;
47 m_handler = new StateHandler(this);
48 m_volumeControl = new VolumeHandler(this);
49 connect(m_handler, SIGNAL(elapsedChanged(qint64)), SIGNAL(elapsedChanged(qint64)));
50 connect(m_handler, SIGNAL(bitrateChanged(int)), SIGNAL(bitrateChanged(int)));
51 connect(m_handler, SIGNAL(audioParametersChanged(AudioParameters)), SIGNAL(audioParametersChanged(AudioParameters)));
52 connect(m_handler, SIGNAL(bufferingProgress(int)), SIGNAL(bufferingProgress(int)));
53 connect(QmmpSettings::instance(), SIGNAL(eqSettingsChanged()), SIGNAL(eqSettingsChanged()));
54 connect(QmmpSettings::instance(), SIGNAL(audioSettingsChanged()), m_volumeControl, SLOT(reload()));
55 connect(m_volumeControl, SIGNAL(volumeChanged(int, int)), SIGNAL(volumeChanged(int, int)));
56 connect(m_volumeControl, SIGNAL(volumeChanged(int)), SIGNAL(volumeChanged(int)));
57 connect(m_volumeControl, SIGNAL(balanceChanged(int)), SIGNAL(balanceChanged(int)));
58 connect(m_volumeControl, SIGNAL(mutedChanged(bool)), SIGNAL(mutedChanged(bool)));
59 }
60
~SoundCore()61 SoundCore::~SoundCore()
62 {
63 stop();
64 m_instance = nullptr;
65 }
66
play(const QString & source,bool queue,qint64 offset)67 bool SoundCore::play(const QString &source, bool queue, qint64 offset)
68 {
69 if(!queue)
70 stop();
71
72 MetaDataManager::instance(); //create metadata manager
73
74 InputSource *s = InputSource::create(source, this);
75 s->setOffset(offset);
76 m_sources.enqueue(s);
77
78 connect(s, SIGNAL(ready()), SLOT(startNextSource()));
79 connect(s, SIGNAL(error()), SLOT(startNextSource()));
80
81 if(!s->initialize())
82 {
83 m_sources.removeAll(s);
84 s->deleteLater();
85 if(m_handler->state() == Qmmp::Stopped || m_handler->state() == Qmmp::Buffering)
86 m_handler->dispatch(Qmmp::NormalError);
87 return false;
88 }
89 if(m_handler->state() == Qmmp::Stopped)
90 m_handler->dispatch(Qmmp::Buffering);
91 return true;
92 }
93
stop()94 void SoundCore::stop()
95 {
96 qApp->sendPostedEvents(this, 0);
97 m_path.clear();
98 qDeleteAll(m_sources);
99 m_sources.clear();
100 m_nextState = NO_ENGINE;
101 if(m_engine)
102 {
103 m_engine->stop();
104 qApp->sendPostedEvents(this, 0);
105 //m_engine->deleteLater();
106 //m_engine = 0;
107 }
108 m_volumeControl->reload();
109 if(state() == Qmmp::NormalError || state() == Qmmp::FatalError || state() == Qmmp::Buffering)
110 StateHandler::instance()->dispatch(Qmmp::Stopped); //clear error and buffering state
111 }
112
pause()113 void SoundCore::pause()
114 {
115 if(m_engine)
116 m_engine->pause();
117 }
118
seek(qint64 pos)119 void SoundCore::seek(qint64 pos)
120 {
121 if(m_engine)
122 m_engine->seek(pos);
123 }
124
path() const125 const QString SoundCore::path() const
126 {
127 return m_path;
128 }
129
nextTrackAccepted() const130 bool SoundCore::nextTrackAccepted() const
131 {
132 return m_nextState == SAME_ENGINE;
133 }
134
duration() const135 qint64 SoundCore::duration() const
136 {
137 return m_handler->duration();
138 }
139
eqSettings() const140 EqSettings SoundCore::eqSettings() const
141 {
142 return QmmpSettings::instance()->eqSettings();
143 }
144
setEqSettings(const EqSettings & settings)145 void SoundCore::setEqSettings(const EqSettings &settings)
146 {
147 QmmpSettings::instance()->setEqSettings(settings);
148 }
149
setVolume(int L,int R)150 void SoundCore::setVolume(int L, int R)
151 {
152 setMuted(false);
153 m_volumeControl->setVolume(L, R);
154 }
155
setMuted(bool mute)156 void SoundCore::setMuted(bool mute)
157 {
158 m_volumeControl->setMuted(mute);
159 }
160
changeVolume(int delta)161 void SoundCore::changeVolume(int delta)
162 {
163 setMuted(false);
164 m_volumeControl->changeVolume(delta);
165 }
166
setVolume(int volume)167 void SoundCore::setVolume(int volume)
168 {
169 setMuted(false);
170 m_volumeControl->setVolume(volume);
171 }
172
volumeUp()173 void SoundCore::volumeUp()
174 {
175 changeVolume(QmmpSettings::instance()->volumeStep());
176 }
177
volumeDown()178 void SoundCore::volumeDown()
179 {
180 changeVolume(-QmmpSettings::instance()->volumeStep());
181 }
182
setBalance(int balance)183 void SoundCore::setBalance(int balance)
184 {
185 setMuted(false);
186 m_volumeControl->setBalance(balance);
187 }
188
leftVolume() const189 int SoundCore::leftVolume() const
190 {
191 return m_volumeControl->left();
192 }
193
rightVolume() const194 int SoundCore::rightVolume() const
195 {
196 return m_volumeControl->right();
197 }
198
volume() const199 int SoundCore::volume() const
200 {
201 return m_volumeControl->volume();
202 }
203
balance() const204 int SoundCore::balance() const
205 {
206 return m_volumeControl->balance();
207 }
208
isMuted() const209 bool SoundCore::isMuted() const
210 {
211 return m_volumeControl->isMuted();
212 }
213
elapsed() const214 qint64 SoundCore::elapsed() const
215 {
216 return m_handler->elapsed();
217 }
218
bitrate() const219 int SoundCore::bitrate() const
220 {
221 return m_handler->bitrate();
222 }
223
audioParameters() const224 AudioParameters SoundCore::audioParameters() const
225 {
226 return m_handler->audioParameters();
227 }
228
state() const229 Qmmp::State SoundCore::state() const
230 {
231 return m_handler->state();
232 }
233
metaData() const234 const QMap<Qmmp::MetaData, QString> &SoundCore::metaData() const
235 {
236 return m_info.metaData();
237 }
238
metaData(Qmmp::MetaData key) const239 QString SoundCore::metaData(Qmmp::MetaData key) const
240 {
241 return m_info.value(key);
242 }
243
streamInfo() const244 QHash<QString, QString> SoundCore::streamInfo() const
245 {
246 return m_streamInfo;
247 }
248
trackInfo() const249 const TrackInfo &SoundCore::trackInfo() const
250 {
251 return m_info;
252 }
253
startNextSource()254 void SoundCore::startNextSource()
255 {
256 if(m_sources.isEmpty())
257 return;
258
259 InputSource *s = m_sources.dequeue();
260 m_path = s->path();
261
262 if(s->ioDevice() && !s->ioDevice()->isOpen() && !s->ioDevice()->open(QIODevice::ReadOnly))
263 {
264 qWarning("SoundCore: input error: %s", qPrintable(s->ioDevice()->errorString()));
265 m_path.clear();
266 s->deleteLater();
267 m_nextState = INVALID_SOURCE;
268 if(m_handler->state() == Qmmp::Stopped || m_handler->state() == Qmmp::Buffering)
269 m_handler->dispatch(Qmmp::NormalError);
270 return;
271 }
272
273 if(!m_engine)
274 {
275 if((m_engine = AbstractEngine::create(s, this)))
276 {
277 m_engine->play();
278 m_nextState = NO_ENGINE;
279 return;
280 }
281 else
282 {
283 s->deleteLater();
284 m_handler->dispatch(Qmmp::NormalError);
285 return;
286 }
287 }
288 else if(AbstractEngine::isEnabled(m_engine) && m_engine->enqueue(s))
289 {
290 if(state() == Qmmp::Stopped || state() == Qmmp::Buffering)
291 {
292 m_engine->play();
293 m_nextState = NO_ENGINE;
294 }
295 else
296 {
297 m_nextState = SAME_ENGINE;
298 }
299 }
300 else
301 {
302 m_sources.prepend(s); //try next engine
303 m_nextState = ANOTHER_ENGINE;
304 if(state() == Qmmp::Stopped || state() == Qmmp::Buffering)
305 {
306 startNextEngine();
307 }
308 }
309 }
310
startNextEngine()311 void SoundCore::startNextEngine()
312 {
313 switch(m_nextState)
314 {
315 case NO_ENGINE:
316 case SAME_ENGINE:
317 {
318 if(m_sources.isEmpty())
319 m_nextState = NO_ENGINE;
320 else if(!m_sources.first()->isReady() && state() == Qmmp::Stopped)
321 m_handler->dispatch(Qmmp::Buffering);
322 break;
323 }
324 case ANOTHER_ENGINE:
325 {
326 m_nextState = NO_ENGINE;
327 if(m_engine)
328 {
329 m_engine->deleteLater();
330 m_engine = nullptr;
331 }
332 if(!m_sources.isEmpty())
333 {
334 m_handler->dispatch(Qmmp::Buffering);
335 startNextSource();
336 }
337 break;
338 }
339 case INVALID_SOURCE:
340 m_handler->dispatch(Qmmp::NormalError);
341 }
342 }
343
instance()344 SoundCore* SoundCore::instance()
345 {
346 return m_instance;
347 }
348
event(QEvent * e)349 bool SoundCore::event(QEvent *e)
350 {
351 if(e->type() == EVENT_STATE_CHANGED)
352 {
353 Qmmp::State st = ((StateChangedEvent *) e)->currentState();
354 emit stateChanged(st);
355 if(st == Qmmp::Stopped)
356 {
357 m_streamInfo.clear();
358 startNextEngine();
359 }
360 }
361 else if(e->type() == EVENT_STREAM_INFO_CHANGED)
362 {
363 m_streamInfo = ((StreamInfoChangedEvent *) e)->streamInfo();
364 emit streamInfoChanged();
365 }
366 else if(e->type() == EVENT_TRACK_INFO_CHANGED)
367 {
368 m_info = ((TrackInfoEvent *) e)->trackInfo();
369 emit trackInfoChanged();
370 }
371 else if(e->type() == EVENT_NEXT_TRACK_REQUEST)
372 emit nextTrackRequest();
373 else if(e->type() == EVENT_FINISHED)
374 emit finished();
375
376 return QObject::event(e);;
377 }
378