1
/****************************************************************************************
2
 * Copyright (c) 2004 Frederik Holljen <fh@ez.no>                                       *
3
 * Copyright (c) 2004,2005 Max Howell <max.howell@methylblue.com>                       *
4
 * Copyright (c) 2004-2010 Mark Kretschmann <kretschmann@kde.org>                       *
5
 * Copyright (c) 2008 Jason A. Donenfeld <Jason@zx2c4.com>                              *
6
 * Copyright (c) 2009 Artur Szymiec <artur.szymiec@gmail.com>                           *
7
 *                                                                                      *
8
 * This program is free software; you can redistribute it and/or modify it under        *
9
 * the terms of the GNU General Public License as published by the Free Software        *
10
 * Foundation; either version 2 of the License, or (at your option) any later           *
11
 * version.                                                                             *
12
 *                                                                                      *
13
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
14
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
15
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
16
 *                                                                                      *
17
 * You should have received a copy of the GNU General Public License along with         *
18
 * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
19
 ****************************************************************************************/
20
21
#ifndef AMAROK_ENGINECONTROLLER_H
22
#define AMAROK_ENGINECONTROLLER_H
23
24
#include "core/capabilities/BoundedPlaybackCapability.h"
25
#include "core/engine/EngineObserver.h"
26
#include "core/meta/Meta.h"
27
28
#include <QMap>
29
#include <QMutex>
30
#include <QObject>
31
#include <QPointer>
32
33
#include <Phonon/Path>
34
#include <Phonon/MediaController>
35
#include <Phonon/MediaObject>
36
#include <Phonon/Effect>
37
#include <Phonon/EffectParameter>
38
39
class QTimer;
40
41
namespace KIO { class Job; }
42
namespace Capabilities { class MultiPlayableCapability; class MultiSourceCapability; }
43
namespace Phonon { class AudioOutput; class MediaSource; class VolumeFaderEffect; }
44
45
/**
46
 * A thin wrapper around Phonon that implements Amarok-specific functionality like
47
 * replay gain, fade-out on stop and various track capabilities that affect
48
 * playback.
49
 */
50
class AMAROK_EXPORT EngineController : public Engine::EngineSubject
51
{
52
    Q_OBJECT
53
54
public:
55
    EngineController();
56
    ~EngineController();
57
58
    /**
59
     * Returns the global EngineController instance
60
     */
61
    static EngineController* instance();
62
63
    /**
64
     * Destroys the global EngineController instance
65
     */
66
    static void destroy();
67
68
    /**
69
     * Loads and plays the track that was playing when endSession() was last
70
     * called (ie: when Amarok was quit)
71
     */
72
    void restoreSession();
73
74
    /**
75
     * Saves the currently playing track and the playing/paused/stopped state
76
     */
77
    void endSession();
78
79
    /**
80
     * Checks whether the media file at the specified URL can be decoded
81
     */
82
    static bool canDecode( const KUrl& );
83
84
    /**
85
     * Returns a list of backend supported mime types.
86
     */
87
    static QStringList supportedMimeTypes();
88
89
    /** @return track position (elapsed time) in seconds */
90
    int trackPosition() const;
91
92
    /** @return track position (elapsed time) in milliseconds */
93
    int trackPositionMs() const;
94
95
    /**
96
     * Returns the current track that is loaded into the engine.
97
     * @return a Meta::TrackPtr which is either the track, or empty if phonon
98
     * has a state of Phonon::ErrorState or Phonon::StoppedState
99
     */
100
    Meta::TrackPtr currentTrack() const;
101
    /**
102
     * @return the length of the current track in milliseconds
103
     */
104
    qint64 trackLength() const;
105
106
    /**
107
     * Used to enqueue a track before it starts to play, for gapless playback.
108
     *
109
     * This will clear any tracks currently in the queue.  If no track is playing,
110
     * @p track will be played immediately.
111
     */
112
    void setNextTrack( Meta::TrackPtr track );
113
114
    /**
115
     * The state of the engine
116
     */
117
    Phonon::State state() const;
118
119
    /*enum Filetype { MP3 };*/ //assuming MP3 for time being
120
    /*AMAROK_EXPORT*/ static bool installDistroCodec();
121
122
    /**
123
     * Provides access to the Phonon MediaObject for components that need more information
124
     */
125
    // const so that it can only be used for info
126
    const Phonon::MediaObject* phononMediaObject() const { return m_media; }
127
128
    /**
129
     * Gets the volume
130
     * @return the volume as a percentage
131
     */
132
    int volume() const;
133
134
    /**
135
     * @return @c true if sound output is disabled, @false otherwise
136
     */
137
    bool isMuted() const;
138
139
    /**
140
     * @return @c true if Amarok is paused, @c false if it is stopped or playing
141
     */
142
    bool isPaused() const;
143
144
    /**
145
     * Streams sometimes have to be treated specially.  For example, it is typically
146
     * not possible to rewind a stream (at least, not without returning to the
147
     * start of it).
148
     *
149
     * @return @c true if the current track is a stream, @c false otherwise
150
     */
151
    bool isStream();
152
153
    /**
154
     * Phonon kequalizer support is required for Amarok to enable equalizer
155
     * this method return whatever phonon support kequalizer effect.
156
     *
157
     * @return @c true if the phonon support kequalizer effect, @c false otherwise
158
     */
159
    bool isEqSupported() const;
160
161
    /**
162
     * kequalizer implementation for different backends may have different
163
     * gain scale. To properly display it we need to get a scale from effect
164
     *
165
     * @return maximum gain value for kequalizer parameters.
166
     */
167
    double eqMaxGain() const;
168
169
     /**
170
     * kequalizer implementation for different backends may have different
171
     * frequency bands. For proper display this will try to extract frequency values
172
     * from effect parameters info.
173
     *
174
     * @return QStringList with band labels (form xxx Hz or xxx kHz).
175
     */
176
    QStringList eqBandsFreq() const;
177
178
    /**
179
    * @return whether the currently playing track is from an audiocd
180
    */
181
    bool isPlayingAudioCd();
182
183
    /**
184
     * @return QString with a pretty name for the current track
185
     */
186
    QString prettyNowPlaying() const;
187
188
public slots:
189
    /**
190
     * Plays the current track, if there is one
191
     *
192
     * This happens asynchronously.  Use EngineObserver to find out when it actually happens.
193
     */
194
    void play();
195
196
    /**
197
     * Plays the specified track
198
     *
199
     * This happens asynchronously.  Use EngineObserver to find out when it actually happens.
200
     */
201
    void play( const Meta::TrackPtr&, uint offset = 0 );
202
203
    /**
204
     * Replays the current track
205
     *
206
     * This is a convenience method, calls seek( 0 ) actually
207
     */
208
    void replay();
209
210
    /**
211
     * Pauses the current track
212
     *
213
     * This happens asynchronously.  Use EngineObserver to find out when it actually happens.
214
     */
215
    void pause();
216
217
    /**
218
     * Stops playing
219
     *
220
     * This happens asynchronously.  Use EngineObserver to find out when it actually happens.
221
     */
222
    void stop( bool forceInstant = false );
223
224
    /**
225
     * Pauses if Amarok is currently playing, plays if Amarok is stopped or paused
226
     *
227
     * This happens asynchronously.  Use EngineObserver to find out when it actually happens.
228
     */
229
    void playPause(); //pauses if playing, plays if paused or stopped
230
231
    /**
232
     * Seeks to a position in the track
233
     *
234
     * If the media is not seekable, or the state is something other than
235
     * PlayingState, BufferingState or PausedState, has no effect.
236
     *
237
     * Deals correctly with tracks that have the BoundedPlayback capability.
238
     *
239
     * @param ms the position in milliseconds (counting from the start of the track)
240
     */
241
    void seek( int ms );
242
243
    /**
244
     * Seeks forward or backward in the track
245
     *
246
     * If the media is not seekable, or the state is something other than
247
     * PlayingState, BufferingState or PausedState, has no effect.
248
     *
249
     * Deals correctly with tracks that have the BoundedPlayback capability.
250
     *
251
     * A negative value seeks backwards, a positive value seeks forwards.
252
     *
253
     * If the value of @p ms would move the position to before the start of the track,
254
     * the position is moved to the start of the track.
255
     *
256
     * @param ms the offset from the current position in milliseconds
257
     */
258
    void seekRelative( int ms );
259
260
    /**
261
     * Seeks forward in the track
262
     *
263
     * Same as seekRelative()
264
     */
265
    void seekForward( int ms = 10000 );
266
267
    /**
268
     * Seeks backward in the track
269
     *
270
     * Works identically to seekRelative(), but seeks in the opposite direction.
271
     */
272
    void seekBackward( int ms = 10000 );
273
274
    /**
275
     * Increases the volume
276
     *
277
     * @param ticks the amount to increase the volume by, given as a percentage of the
278
     * maximum possible volume (ie: the same units as for setVolume()).
279
     */
280
    int increaseVolume( int ticks = 100/25 );
281
282
    /**
283
     * Decreases the volume
284
     *
285
     * @param ticks the amount to decrease the volume by, given as a percentage of the
286
     * maximum possible volume (ie: the same units as for setVolume()).
287
     */
288
    int decreaseVolume( int ticks = 100/25 );
289
290
    /**
291
     * Sets the volume
292
     *
293
     * @param percent the new volume as a percentage of the maximum possible volume.
294
     */
295
    // this amplifier does not go up to 11
296
    int setVolume( int percent );
297
298
    /**
299
     * Mutes or unmutes playback
300
     *
301
     * @param mute if @c true, audio output will be disabled; if @c false, audio output
302
     * will be enabled.
303
     */
304
    void setMuted( bool mute );
305
306
    /**
307
     * Toggles mute
308
     *
309
     * Works like setMuted( !isMuted() );
310
     */
311
    void toggleMute();
312
313
    /**
314
     * Update equalizer status - enabled,disabled,set values
315
     *
316
     *
317
     */
318
    void eqUpdate();
319
320
private slots:
321
    /**
322
     * Sets up the Phonon system
323
     */
324
    void initializePhonon();
325
    void slotQueueEnded();
326
    void slotAboutToFinish();
327
    void slotNewTrackPlaying( const Phonon::MediaSource &source);
328
    void slotStateChanged( Phonon::State newState, Phonon::State oldState);
329
    void slotPlayableUrlFetched(const KUrl&);
330
    void slotTick( qint64 );
331
    void slotTrackLengthChanged( qint64 );
332
    void slotMetaDataChanged();
333
    void slotStopFadeout(); //called after the fade-out has finished
334
335
    /**
336
     * For volume/mute changes from the phonon side
337
     */
338
    void slotVolumeChanged( qreal );
339
    void slotMutedChanged( bool );
340
341
    /**
342
     *  Notify the engine that a new title has been reached when playing a cd. This
343
     *  is needed as a cd counts as basically one lone track, and we want to be able
344
     *  to play something else once one track has finished
345
     */
346
    void slotTitleChanged( int titleNumber );
347
348
private:
349
    /**
350
     * Plays the media at a specified URL
351
     *
352
     * @param url the URL of the media
353
     * @param offset the position in the media to start at in milliseconds
354
     */
355
    void playUrl( const KUrl &url, uint offset );
356
357
    void createFadeoutEffect();
358
    void resetFadeout();
359
360
    Q_DISABLE_COPY( EngineController )
361
362
    QPointer<Phonon::MediaObject>       m_media;
363
    QPointer<Phonon::VolumeFaderEffect> m_preamp;
364
    QPointer<Phonon::Effect>            m_equalizer;
365
    QPointer<Phonon::AudioOutput>       m_audio;
366
    QPointer<Phonon::VolumeFaderEffect> m_fader;
367
    QPointer<Phonon::MediaController>   m_controller;
368
    Phonon::Path                        m_path;
369
370
    Meta::TrackPtr  m_currentTrack;
371
    Meta::TrackPtr  m_lastTrack;
372
    Meta::TrackPtr  m_nextTrack;
373
    KUrl            m_nextUrl;
374
    QPointer<Capabilities::BoundedPlaybackCapability> m_boundedPlayback;
375
    QPointer<Capabilities::MultiPlayableCapability> m_multiPlayback;
376
    QPointer<Capabilities::MultiSourceCapability> m_multiSource;
377
    bool m_playWhenFetched;
378
    QTimer* m_fadeoutTimer;
379
    int m_volume;
380
    bool m_currentIsAudioCd;
381
382
    /**
383
     * Some flags to prevent feedback loops in volume updates
384
     */
385
    bool m_ignoreVolumeChangeAction;
386
    bool m_ignoreVolumeChangeObserve;
387
388
    // Used to get a more accurate estimate of the position for slotTick
389
    int m_tickInterval;
390
    qint64 m_lastTickPosition;
391
    qint64 m_lastTickCount;
392
393
    QMutex m_mutex;
394
};
395
396
namespace The {
397
    AMAROK_EXPORT EngineController* engineController();
398
}
399
400
401
#endif