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