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