File indexing completed on 2024-04-21 04:47:50
0001 /**************************************************************************************** 0002 * Copyright (c) 2004 Frederik Holljen <fh@ez.no> * 0003 * Copyright (c) 2004,2005 Max Howell <max.howell@methylblue.com> * 0004 * Copyright (c) 2004-2013 Mark Kretschmann <kretschmann@kde.org> * 0005 * Copyright (c) 2008 Jason A. Donenfeld <Jason@zx2c4.com> * 0006 * Copyright (c) 2009 Artur Szymiec <artur.szymiec@gmail.com> * 0007 * * 0008 * This program is free software; you can redistribute it and/or modify it under * 0009 * the terms of the GNU General Public License as published by the Free Software * 0010 * Foundation; either version 2 of the License, or (at your option) any later * 0011 * version. * 0012 * * 0013 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0014 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0015 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0016 * * 0017 * You should have received a copy of the GNU General Public License along with * 0018 * this program. If not, see <http://www.gnu.org/licenses/>. * 0019 ****************************************************************************************/ 0020 0021 #ifndef AMAROK_ENGINECONTROLLER_H 0022 #define AMAROK_ENGINECONTROLLER_H 0023 0024 #include "amarok_export.h" 0025 #include "core/capabilities/BoundedPlaybackCapability.h" 0026 #include "core/meta/Observer.h" 0027 #include "playback/EqualizerController.h" 0028 #include "core/meta/Meta.h" 0029 0030 #include <QMutex> 0031 #include <QObject> 0032 #include <QPointer> 0033 #include <QSemaphore> 0034 #include <QStringList> 0035 #include <QUrl> 0036 0037 #include <phonon/Path> 0038 #include <phonon/MediaController> 0039 #include <phonon/MediaObject> 0040 #include <phonon/Effect> 0041 #include <phonon/EffectParameter> 0042 #include <phonon/audiodataoutput.h> 0043 0044 class Fadeouter; 0045 namespace Capabilities { class MultiPlayableCapability; class MultiSourceCapability; } 0046 namespace Phonon { class AudioOutput; class MediaSource; class VolumeFaderEffect; } 0047 class QTimer; 0048 0049 /** 0050 * A thin wrapper around Phonon that implements Amarok-specific functionality like 0051 * replay gain, fade-out on stop and various track capabilities that affect 0052 * playback. 0053 */ 0054 class AMAROK_EXPORT EngineController : public QObject, public Meta::Observer 0055 { 0056 Q_OBJECT 0057 0058 public: 0059 static const uint DATAOUTPUT_DATA_SIZE = 512; 0060 0061 /** 0062 * Construct EngineController. Must be called from the main thread. 0063 */ 0064 EngineController(); 0065 ~EngineController() override; 0066 0067 /** 0068 * Returns the global EngineController instance 0069 */ 0070 static EngineController* instance(); 0071 0072 /** 0073 * Loads and plays the track that was playing when endSession() was last 0074 * called (ie: when Amarok was quit) 0075 */ 0076 void restoreSession(); 0077 0078 /** 0079 * Saves the currently playing track and the playing/paused/stopped state 0080 */ 0081 void endSession(); 0082 0083 /** 0084 * Returns a list of backend supported mime types. This method is thread-safe. 0085 */ 0086 QStringList supportedMimeTypes(); 0087 0088 /** @return track position (elapsed time) in seconds */ 0089 int trackPosition() const; 0090 0091 /** @return track position (elapsed time) in milliseconds */ 0092 qint64 trackPositionMs() const; 0093 0094 /** 0095 * Returns the current track that is loaded into the engine. 0096 * @return a Meta::TrackPtr which is either the track, or empty if phonon 0097 * has a state of Phonon::ErrorState or Phonon::StoppedState 0098 */ 0099 Meta::TrackPtr currentTrack() const; 0100 /** 0101 * @return the length of the current track in milliseconds 0102 */ 0103 qint64 trackLength() const; 0104 0105 /** 0106 * Used to enqueue a track before it starts to play, for gapless playback. 0107 * 0108 * This will clear any tracks currently in the queue. If no track is playing, 0109 * @p track will be played immediately. 0110 */ 0111 void setNextTrack( Meta::TrackPtr track ); 0112 0113 /** 0114 * Gets the volume 0115 * @return the volume as a percentage 0116 */ 0117 int volume() const; 0118 0119 /** 0120 * @return @c true if sound output is disabled, @c false otherwise 0121 */ 0122 bool isMuted() const; 0123 0124 /** 0125 * @return @c true if Amarok is paused, @c false if it is stopped or playing 0126 */ 0127 bool isPaused() const; 0128 0129 /** 0130 * @return @c true if Amarok is playing, @c false if it is stopped or pause 0131 * Note: A fading out track is considered already stopped. 0132 */ 0133 bool isPlaying() const; 0134 0135 /** 0136 * @return @c true if Amarok is stopped, @c false if it is playing or pause 0137 * Note: A fading out track is considered already stopped. 0138 */ 0139 bool isStopped() const; 0140 0141 /** 0142 * Streams sometimes have to be treated specially. 0143 * For example, it is typically not possible to rewind a stream (at least, 0144 * not without returning to the start of it). 0145 * However for rewinding we have isSeekable(). 0146 * Also for streams usually the meta data received by currentTrack() is only 0147 * for the whole stream while the meta data received by currentMetaDataChanged 0148 * will be more current (or contain advertisement) 0149 * 0150 * @return @c true if the current track is a stream, @c false otherwise 0151 */ 0152 bool isStream(); 0153 0154 /** 0155 * @return @c true if the current track is seekable, @c false otherwise 0156 */ 0157 bool isSeekable() const; 0158 0159 /** 0160 * Returns the associated EqualizerController object. 0161 */ 0162 EqualizerController *equalizerController() const; 0163 0164 /** 0165 * @return QString with a pretty name for the current track 0166 * @param progress whether to include the playing progress (default false) 0167 */ 0168 QString prettyNowPlaying( bool progress = false ) const; 0169 0170 public Q_SLOTS: 0171 /** 0172 * Plays the current track, if there is one 0173 * This happens asynchronously. 0174 */ 0175 void play(); 0176 0177 /** 0178 * Plays the specified track 0179 * This happens asynchronously. 0180 */ 0181 void play( Meta::TrackPtr track, uint offset = 0, bool startPaused = false ); 0182 0183 /** 0184 * Replays the current track 0185 * 0186 * This is a convenience method, calls seek( 0 ) actually 0187 */ 0188 void replay(); 0189 0190 /** 0191 * Pauses the current track 0192 * This happens asynchronously. 0193 */ 0194 void pause(); 0195 0196 /** 0197 * Stops playing 0198 * This happens asynchronously. 0199 * 0200 * @param forceInstant skip any fade-out effects 0201 * @param playingWillContinue don't Q_EMIT stopped() or trackChanged( 0 ) signals 0202 */ 0203 void stop( bool forceInstant = false, bool playingWillContinue = false ); 0204 0205 /** 0206 * Stops playing 0207 * This happens asynchronously. 0208 * Doesn't skip any fade-out effects 0209 * Emits stopped() and trackChanged( 0 ) signals 0210 */ 0211 void regularStop(); 0212 0213 /** 0214 * Pauses if Amarok is currently playing, plays if Amarok is stopped or paused 0215 * This happens asynchronously. 0216 */ 0217 void playPause(); //pauses if playing, plays if paused or stopped 0218 0219 /** 0220 * Seeks to a position in the track 0221 * 0222 * If the media is not seekable, or the state is something other than 0223 * PlayingState, BufferingState or PausedState, has no effect. 0224 * 0225 * Deals correctly with tracks that have the BoundedPlayback capability. 0226 * 0227 * @param ms the position in milliseconds (counting from the start of the track) 0228 */ 0229 void seekTo( int ms ); 0230 0231 /** 0232 * Seeks forward or backward in the track 0233 * 0234 * If the media is not seekable, or the state is something other than 0235 * PlayingState, BufferingState or PausedState, has no effect. 0236 * 0237 * Deals correctly with tracks that have the BoundedPlayback capability. 0238 * 0239 * A negative value seeks backwards, a positive value seeks forwards. 0240 * 0241 * If the value of @p ms would move the position to before the start of the track, 0242 * the position is moved to the start of the track. 0243 * 0244 * @param ms the offset from the current position in milliseconds 0245 */ 0246 void seekBy( int ms ); 0247 0248 /** 0249 * Increases the volume 0250 * 0251 * @param ticks the amount to increase the volume by, given as a percentage of the 0252 * maximum possible volume (ie: the same units as for setVolume()). 0253 */ 0254 int increaseVolume( int ticks = 100/25 ); 0255 0256 /** 0257 * Decreases the volume 0258 * 0259 * @param ticks the amount to decrease the volume by, given as a percentage of the 0260 * maximum possible volume (ie: the same units as for setVolume()). 0261 */ 0262 int decreaseVolume( int ticks = 100/25 ); 0263 0264 /** 0265 * Increases the volume by default amount 0266 */ 0267 int regularIncreaseVolume(); 0268 0269 /** 0270 * Decreases the volume by default amount 0271 */ 0272 int regularDecreaseVolume(); 0273 0274 /** 0275 * Sets the volume 0276 * 0277 * @param percent the new volume as a percentage of the maximum possible volume. 0278 */ 0279 // this amplifier does not go up to 11 0280 int setVolume( int percent ); 0281 0282 /** 0283 * Mutes or unmutes playback 0284 * 0285 * @param mute if @c true, audio output will be disabled; if @c false, audio output 0286 * will be enabled. 0287 */ 0288 void setMuted( bool mute ); 0289 0290 /** 0291 * Toggles mute 0292 * 0293 * Works like setMuted( !isMuted() ); 0294 */ 0295 void toggleMute(); 0296 0297 /** 0298 * Return true if current Phonon back-end supports fade-out. 0299 */ 0300 bool supportsFadeout() const; 0301 0302 /** 0303 * Return true if current Phonon back-end supports our implementation of 0304 * Replay Gain adjustment. 0305 */ 0306 bool supportsGainAdjustments() const; 0307 0308 /** 0309 * Return true if the current Phonon backend supports visualizations. 0310 */ 0311 bool supportsAudioDataOutput() const; 0312 0313 Q_SIGNALS: 0314 /** 0315 * Emitted when the playback stops while playing a track. 0316 * This signal is not emitted when the track pauses or the playback stops because 0317 * Amarok was closed and "resume at start" is configured. 0318 * It is also not emitted if the playback continues with another track. In such 0319 * a case you would just get another trackPlaying signal. 0320 * Both parameters are in milliseconds. 0321 */ 0322 void stopped( qint64 /*ms*/ finalPosition, qint64 /*ms*/ trackLength ); 0323 0324 /** 0325 * Called when the playback is paused. 0326 * When the playback is resumed a trackPlaying signal will be emitted. 0327 * When the playback is stopped then a stopped signal will be emitted. 0328 */ 0329 void paused(); 0330 0331 /** While trying to play the track an error occurred. 0332 * This usually means that the engine will try to play the next track in 0333 * the playlist until it gives up. 0334 * So you will get a trackPlaying or stopped signal next. 0335 */ 0336 void trackError( Meta::TrackPtr track ); 0337 0338 /** 0339 * Called when a new track starts playing or an old track starts playing now. 0340 * 0341 * It also might be called several time in sequence 0342 * with the same track in cases when e.g. you have 0343 * a multi source track. 0344 * 0345 * Unlike trackChanged(), this is not called when playback stops. 0346 */ 0347 void trackPlaying( Meta::TrackPtr track ); 0348 0349 /** 0350 * Called when the current track changes 0351 * 0352 * Note that this is possibly only called once in case of a stream or on 0353 * the other hand multiple times with the same track in cases when e.g. you have 0354 * a multi source track. 0355 * 0356 * Unlike trackPlaying(), this is called when playback stops with Meta::TrackPtr( 0 ) for @p track. 0357 * 0358 * @param track The new track; may be null 0359 */ 0360 void trackChanged( Meta::TrackPtr track ); 0361 0362 /** 0363 * Emitted when the metadata of the current track changes. 0364 * 0365 * You might want to connect also to trackChanged() or trackPlaying() to get more 0366 * changes because this signal is only emitted when the track metadata changes 0367 * while it's playing, not when new track starts playing. This method now works 0368 * correctly also for streams and is preferred to currentMetaDataChanged() because 0369 * it providers somehow more filtered values. 0370 */ 0371 void trackMetadataChanged( Meta::TrackPtr track ); 0372 0373 /** Emitted when the metadata of the current album changes. 0374 */ 0375 void albumMetadataChanged( Meta::AlbumPtr album ); 0376 0377 /** 0378 * Emitted then the information for the current changed. 0379 * This signal contains data from Phonon about the meta data of the track or stream. 0380 * This signal is especially emitted when a stream changes it's metadata. 0381 * This can happen e.g. in a ogg stream where the currentTrack data will probably 0382 * not be updated. 0383 * 0384 * MetaStream::Track::Private in Stream_p.h will connect to this signal to update it's internal data 0385 * and then itself trigger a trackMetadataChanged. 0386 * @param metadata Contains the url, artist, album title, title, genre, tracknumber and length 0387 */ 0388 void currentMetadataChanged( QVariantMap metadata ); 0389 0390 /** 0391 * Called when the seekable value was changed 0392 */ 0393 void seekableChanged( bool seekable ); 0394 0395 /** 0396 * Called when the volume was changed 0397 */ 0398 void volumeChanged( int percent ); 0399 0400 /** 0401 * Called when audio output was enabled or disabled 0402 * 0403 * NB: if setMute() was called on the engine controller, but it didn't change the 0404 * mute state, this will not be called 0405 */ 0406 void muteStateChanged( bool mute ); 0407 0408 /** Called when the track position changes. 0409 If the track just progresses you will get a notification every couple of milliseconds. 0410 @param position The current position in milliseconds 0411 @param userSeek True if the position change was caused by the user 0412 */ 0413 void trackPositionChanged( qint64 position, bool userSeek ); 0414 0415 /** 0416 * Emitted when a track finished playing. You generally get this signal once per 0417 * played track, but in case of a stream this may be emitted more than once when 0418 * stream meta-data changes (which usually indicates that the next track started 0419 * playing) - meta-data in the track are updated in this case. When you receive 0420 * this signal, track score, play count etc. will be already updated. 0421 * 0422 * @param track track that has just finished playing 0423 * @param playedFraction played/total length fraction, between 0 and 1 0424 */ 0425 void trackFinishedPlaying( Meta::TrackPtr track, double playedFraction ); 0426 0427 /** 0428 Called when the track length changes, typically because the track has changed but 0429 also when phonon manages to determine the full track length. 0430 */ 0431 void trackLengthChanged( qint64 milliseconds ); 0432 0433 /** 0434 * Called when Amarok is closed and we disconnect from Phonon. 0435 * @param resumePlayback True if amarok will continue playback after a restart. 0436 */ 0437 void sessionEnded( bool resumePlayback ); 0438 0439 /** 0440 * Called when playback state changes to PlayingState, StoppedState or PausedState. 0441 */ 0442 void playbackStateChanged(); 0443 0444 /** 0445 * Is emitted when new audio Data is ready 0446 * @param audioData The audio data that is available 0447 */ 0448 void audioDataReady( const QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > &audioData ); 0449 0450 /** 0451 * A trick to call slotFillInSupportedMimeTypes() in a main thread, not to be used 0452 * anywhere else than in supportedMimeTypes(). 0453 */ 0454 void fillInSupportedMimeTypes(); 0455 0456 private Q_SLOTS: 0457 /** 0458 * Sets up the Phonon system 0459 */ 0460 void initializePhonon(); 0461 /** This slot is connected to the phonon finished signal. 0462 It is emitted when the queue is empty and the current media come to an end. 0463 */ 0464 void slotFinished(); 0465 void slotAboutToFinish(); 0466 void slotNewTrackPlaying( const Phonon::MediaSource &source); 0467 void slotStateChanged( Phonon::State newState, Phonon::State oldState); 0468 void slotPlayableUrlFetched( const QUrl &url ); 0469 void slotTick( qint64 ); 0470 void slotTrackLengthChanged( qint64 ); 0471 void slotMetaDataChanged(); 0472 void slotSeekableChanged( bool ); 0473 void slotPause(); 0474 0475 /** 0476 * For volume/mute changes from the phonon side 0477 */ 0478 void slotVolumeChanged( qreal ); 0479 void slotMutedChanged( bool ); 0480 0481 /** 0482 * Notify the engine that a new title has been reached when playing a cd. This 0483 * is needed as a cd counts as basically one lone track, and we want to be able 0484 * to play something else once one track has finished 0485 */ 0486 void slotTitleChanged( int titleNumber ); 0487 0488 /** 0489 * Fill in m_supportedMimeTypes list and release m_supportedMimeTypesSemaphore. This 0490 * method must be called in the main thread so that there is no chance 0491 * Phonon::BackendCapabilities::availableMimeTypes() is called in a non-gui thread 0492 * for the first time. 0493 */ 0494 void slotFillInSupportedMimeTypes(); 0495 0496 /** 0497 * Calls track->finishedPlaying(), connected to trackFinishedPlaying() signal to 0498 * reduce code duplication. 0499 */ 0500 void slotTrackFinishedPlaying( Meta::TrackPtr track, double playedFraction ); 0501 0502 protected: 0503 // reimplemented from Meta::Observer 0504 using Observer::metadataChanged; 0505 void metadataChanged( const Meta::TrackPtr &track ) override; 0506 void metadataChanged( const Meta::AlbumPtr &album ) override; 0507 0508 private: 0509 /** 0510 * Plays the media at a specified URL 0511 * 0512 * @param url the URL of the media 0513 * @param offset the position in the media to start at in milliseconds 0514 * @param startPaused if true, go to paused state. if false, go to playing state (default) 0515 */ 0516 void playUrl( const QUrl &url, uint offset, bool startPaused = false ); 0517 0518 /** 0519 * Try to detect MetaData spam in Streams etc. 0520 * 0521 * Some streams are doing advertisement in the metadata. We try to filter that 0522 * out. Additionally, some Phonon back-ends Q_EMIT more than one 0523 * metadataChanged() signals per on track, so filter it all altogether. 0524 */ 0525 bool isInRecentMetaDataHistory( const QVariantMap &meta ); 0526 0527 /** 0528 * If m_lastStreamStampPosition is non-negative, update it to current position 0529 * and update track length in current stream. 0530 */ 0531 void stampStreamTrackLength(); 0532 0533 /** 0534 * Q_EMIT metadataChanged() with info so that MetaStream::Track that is 0535 * currently listening updates its length. 0536 * 0537 * @param length new track length in milliseconds 0538 */ 0539 void updateStreamLength( qint64 length ); 0540 0541 Q_DISABLE_COPY( EngineController ) 0542 0543 EqualizerController *m_equalizerController; 0544 QPointer<Phonon::MediaObject> m_media; 0545 QPointer<Phonon::VolumeFaderEffect> m_preamp; 0546 QPointer<Phonon::AudioOutput> m_audio; 0547 QPointer<Phonon::AudioDataOutput> m_audioDataOutput; 0548 QPointer<Phonon::MediaController> m_controller; 0549 Phonon::Path m_path; 0550 Phonon::Path m_dataPath; 0551 0552 QPointer<Fadeouter> m_fadeouter; 0553 QPointer<Phonon::VolumeFaderEffect> m_fader; 0554 0555 Meta::TrackPtr m_currentTrack; 0556 Meta::AlbumPtr m_currentAlbum; 0557 Meta::TrackPtr m_nextTrack; 0558 QUrl m_nextUrl; 0559 Capabilities::BoundedPlaybackCapability* m_boundedPlayback; 0560 Capabilities::MultiPlayableCapability* m_multiPlayback; 0561 QScopedPointer<Capabilities::MultiSourceCapability> m_multiSource; 0562 bool m_playWhenFetched; 0563 int m_volume; 0564 int m_currentAudioCdTrack; 0565 QTimer *m_pauseTimer; 0566 0567 QList<QVariantMap> m_metaDataHistory; // against metadata spam 0568 // last position (in ms) when the song changed (within the current stream) or -1 for non-stream 0569 qint64 m_lastStreamStampPosition; 0570 0571 /** 0572 * Some flags to prevent feedback loops in volume updates 0573 */ 0574 bool m_ignoreVolumeChangeAction; 0575 bool m_ignoreVolumeChangeObserve; 0576 0577 // Used to get a more accurate estimate of the position for slotTick 0578 int m_tickInterval; 0579 qint64 m_lastTickPosition; 0580 qint64 m_lastTickCount; 0581 0582 QMutex m_mutex; 0583 0584 // FIXME: this variable should be updated when 0585 // Phonon::BackendCapabilities::notifier()'s capabilitiesChanged signal is emitted 0586 QStringList m_supportedMimeTypes; 0587 QSemaphore m_supportedMimeTypesSemaphore; 0588 }; 0589 0590 namespace The { 0591 AMAROK_EXPORT EngineController* engineController(); 0592 } 0593 0594 #endif