~neon/juk/master

« back to all changes in this revision

Viewing changes to playermanager.cpp

  • Committer: Michael Pyne
  • Date: 2008-01-09 03:54:23 UTC
  • Revision ID: git-v1:b8d97f6ad4dc58441f9e3b100150ee753c3a4c94
Bump trunk version before I forget

svn path=/trunk/KDE/kdemultimedia/juk/; revision=758776

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
    begin                : Sat Feb 14 2004
3
3
    copyright            : (C) 2004 by Scott Wheeler
4
4
    email                : wheeler@kde.org
5
 
 
6
 
    copyright            : (C) 2007 by Matthias Kretz
7
 
    email                : kretz@kde.org
8
 
 
9
 
    copyright            : (C) 2008, 2009 by Michael Pyne
10
 
    email                : michael.pyne@kdemail.net
11
5
***************************************************************************/
12
6
 
13
7
/***************************************************************************
20
14
 ***************************************************************************/
21
15
 
22
16
#include "playermanager.h"
 
17
#include <config.h>
23
18
 
24
19
#include <kdebug.h>
25
20
#include <kmessagebox.h>
60
55
    m_sliderAction(0),
61
56
    m_playlistInterface(0),
62
57
    m_statusLabel(0),
 
58
    m_noSeek(false),
63
59
    m_muted(false),
64
60
    m_setup(false),
65
 
    m_crossfadeTracks(true),
66
 
    m_curOutputPath(0)
 
61
    m_output(0),
 
62
    m_media(0)
67
63
{
68
64
// This class is the first thing constructed during program startup, and
69
65
// therefore has no access to the widgets needed by the setup() method.
71
67
// later, just disable it here. -- mpyne
72
68
//    setup();
73
69
    new PlayerAdaptor( this );
 
70
    QDBusConnection::sessionBus().registerObject("/Player", this);
 
71
 
74
72
}
75
73
 
76
74
PlayerManager::~PlayerManager()
77
75
{
78
 
    delete m_sliderAction;
79
 
    m_sliderAction = 0;
80
76
}
81
77
 
82
78
////////////////////////////////////////////////////////////////////////////////
83
79
// public members
84
80
////////////////////////////////////////////////////////////////////////////////
85
81
 
 
82
PlayerManager *PlayerManager::instance() // static
 
83
{
 
84
    static PlayerManager manager;
 
85
    return &manager;
 
86
}
 
87
 
86
88
bool PlayerManager::playing() const
87
89
{
88
 
    if(!m_setup)
 
90
    if(!m_media)
89
91
        return false;
90
92
 
91
 
    Phonon::State state = m_media[m_curOutputPath]->state();
92
 
    return (state == Phonon::PlayingState || state == Phonon::BufferingState);
 
93
    return (m_media->state() == Phonon::PlayingState || m_media->state() == Phonon::BufferingState);
93
94
}
94
95
 
95
96
bool PlayerManager::paused() const
96
97
{
97
 
    if(!m_setup)
 
98
    if(!m_media)
98
99
        return false;
99
100
 
100
 
    return m_media[m_curOutputPath]->state() == Phonon::PausedState;
 
101
    return m_media->state() == Phonon::PausedState;
101
102
}
102
103
 
103
104
float PlayerManager::volume() const
104
105
{
105
 
    if(!m_setup)
 
106
    if(!m_output)
106
107
        return 1.0;
107
108
 
108
 
    return m_output[m_curOutputPath]->volume();
 
109
    return m_output->volume();
109
110
}
110
111
 
111
112
int PlayerManager::status() const
112
113
{
113
 
    if(!m_setup)
 
114
    if(!m_media)
114
115
        return StatusStopped;
115
116
 
116
117
    if(paused())
124
125
 
125
126
int PlayerManager::totalTime() const
126
127
{
127
 
    if(!m_setup)
 
128
    if(!m_media)
128
129
        return 0;
129
130
 
130
 
    return m_media[m_curOutputPath]->totalTime() / 1000;
 
131
    return m_media->totalTime() / 1000;
131
132
}
132
133
 
133
134
int PlayerManager::currentTime() const
134
135
{
135
 
    if(!m_setup)
136
 
        return 0;
137
 
 
138
 
    return m_media[m_curOutputPath]->currentTime() / 1000;
139
 
}
 
136
    if(!m_media)
 
137
        return 0;
 
138
 
 
139
    return m_media->currentTime() / 1000;
 
140
}
 
141
 
 
142
/*
 
143
int PlayerManager::position() const
 
144
{
 
145
    if(!m_media)
 
146
        return 0;
 
147
 
 
148
    long curr = m_media->currentTime();
 
149
    if(curr > 0)
 
150
        return static_cast<int>(static_cast<float>(curr * SliderAction::maxPosition) / m_media->totalTime() + 0.5f);
 
151
    return -1;
 
152
}
 
153
*/
140
154
 
141
155
QStringList PlayerManager::trackProperties()
142
156
{
171
185
 
172
186
QString PlayerManager::playingString() const
173
187
{
174
 
    if(!playing() || m_file.isNull())
 
188
    if(!playing())
175
189
        return QString();
176
190
 
177
 
    return m_file.tag()->playingString();
 
191
    QString str = m_file.tag()->artist() + " - " + m_file.tag()->title();
 
192
    if(m_file.tag()->artist().isEmpty())
 
193
        str = m_file.tag()->title();
 
194
 
 
195
    return str;
178
196
}
179
197
 
180
198
void PlayerManager::setPlaylistInterface(PlaylistInterface *interface)
193
211
 
194
212
void PlayerManager::play(const FileHandle &file)
195
213
{
196
 
    if(!m_setup)
 
214
    if(!m_media)
197
215
        setup();
198
216
 
199
 
    if(!m_media[0] || !m_media[1] || !m_playlistInterface)
 
217
    if(!m_media || !m_playlistInterface)
200
218
        return;
201
219
 
202
 
    stopCrossfade();
203
 
 
204
 
    // The "currently playing" media object.
205
 
    Phonon::MediaObject *mediaObject = m_media[m_curOutputPath];
206
 
 
207
220
    if(file.isNull()) {
208
221
        if(paused())
209
 
            mediaObject->play();
 
222
            m_media->play();
210
223
        else if(playing()) {
211
 
            mediaObject->seek(0);
 
224
            m_media->seek(0);
212
225
        }
213
226
        else {
214
227
            m_playlistInterface->playNext();
216
229
 
217
230
            if(!m_file.isNull())
218
231
            {
219
 
                mediaObject->setCurrentSource(KUrl::fromPath(m_file.absFilePath()));
220
 
                mediaObject->play();
221
 
 
222
 
                emit signalItemChanged(m_file);
 
232
                m_media->setCurrentSource(KUrl::fromPath(m_file.absFilePath()));
 
233
                m_media->play();
223
234
            }
224
235
        }
225
236
    }
226
237
    else {
227
 
        mediaObject->setCurrentSource(KUrl::fromPath(file.absFilePath()));
228
 
        mediaObject->play();
229
 
 
230
 
        if(m_file != file)
231
 
            emit signalItemChanged(file);
232
 
 
233
238
        m_file = file;
234
 
    }
235
 
 
236
 
    // Our state changed handler will perform the follow up actions necessary
237
 
    // once we actually start playing.
 
239
        if(m_media->state() == Phonon::PlayingState)
 
240
        {
 
241
            // do a crossfade
 
242
            Phonon::VolumeFaderEffect *fader1 = new Phonon::VolumeFaderEffect(m_media);
 
243
            m_audioPath.insertEffect(fader1);
 
244
            Phonon::MediaObject *mo = m_media;
 
245
            Phonon::AudioOutput *out = m_output;
 
246
 
 
247
            mo->disconnect(this);
 
248
 
 
249
            m_media = new Phonon::MediaObject(this);
 
250
            m_output = new Phonon::AudioOutput(Phonon::MusicCategory, this);
 
251
            Phonon::VolumeFaderEffect *fader2 = new Phonon::VolumeFaderEffect(m_media);
 
252
            m_audioPath = Phonon::createPath(m_media, m_output);
 
253
            m_audioPath.insertEffect(fader2);
 
254
            m_output->setVolume(out->volume());
 
255
            m_output->setMuted(out->isMuted());
 
256
            m_media->setTickInterval(200);
 
257
 
 
258
            connect(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), SLOT(slotStateChanged(Phonon::State)));
 
259
            connect(m_media, SIGNAL(aboutToFinish()), SLOT(slotNeedNextUrl()));
 
260
            if(m_sliderAction->trackPositionSlider())
 
261
                m_sliderAction->trackPositionSlider()->setMediaObject(m_media);
 
262
            connect(m_media, SIGNAL(totalTimeChanged(qint64)), SLOT(slotLength(qint64)));
 
263
            connect(m_media, SIGNAL(tick(qint64)), SLOT(slotTick(qint64)));
 
264
            connect(m_media, SIGNAL(finished()), SLOT(slotFinished()));
 
265
            m_media->setCurrentSource(KUrl::fromPath(m_file.absFilePath()));
 
266
 
 
267
            fader2->setVolume(0.0f);
 
268
            fader1->fadeOut(2000);
 
269
            fader2->fadeIn(2000);
 
270
            m_media->play();
 
271
            QTimer::singleShot(3000, mo, SLOT(deleteLater()));
 
272
            QTimer::singleShot(3000, out, SLOT(deleteLater()));
 
273
            QTimer::singleShot(3000, fader2, SLOT(deleteLater()));
 
274
 
 
275
            if(m_sliderAction->trackPositionSlider()) {
 
276
                m_sliderAction->trackPositionSlider()->setMediaObject(m_media);
 
277
            }
 
278
            if(m_sliderAction->volumeSlider()) {
 
279
                m_sliderAction->volumeSlider()->setAudioOutput(m_output);
 
280
            }
 
281
        }
 
282
        else
 
283
        {
 
284
            m_media->setCurrentSource(KUrl::fromPath(m_file.absFilePath()));
 
285
            m_media->play();
 
286
        }
 
287
    }
 
288
 
 
289
    // Make sure that the player() actually starts before doing anything.
 
290
 
 
291
    if(!playing()) {
 
292
        kWarning(65432) << "Unable to play " << file.absFilePath();
 
293
        stop();
 
294
        return;
 
295
    }
 
296
 
 
297
    action("pause")->setEnabled(true);
 
298
    action("stop")->setEnabled(true);
 
299
    action("forward")->setEnabled(true);
 
300
    if(action<KToggleAction>("albumRandomPlay")->isChecked())
 
301
        action("forwardAlbum")->setEnabled(true);
 
302
    action("back")->setEnabled(true);
 
303
 
 
304
    emit signalPlay();
238
305
}
239
306
 
240
307
void PlayerManager::play(const QString &file)
253
320
 
254
321
void PlayerManager::pause()
255
322
{
256
 
    if(!m_setup)
 
323
    if(!m_media)
257
324
        return;
258
325
 
259
326
    if(paused()) {
263
330
 
264
331
    action("pause")->setEnabled(false);
265
332
 
266
 
    m_media[m_curOutputPath]->pause();
 
333
    m_media->pause();
267
334
 
268
335
    emit signalPause();
269
336
}
270
337
 
271
338
void PlayerManager::stop()
272
339
{
273
 
    if(!m_setup || !m_playlistInterface)
 
340
    if(!m_media || !m_playlistInterface)
274
341
        return;
275
342
 
276
343
    action("pause")->setEnabled(false);
279
346
    action("forward")->setEnabled(false);
280
347
    action("forwardAlbum")->setEnabled(false);
281
348
 
282
 
    // Fading out playback is for chumps.
283
 
    stopCrossfade();
284
 
    m_media[0]->stop();
285
 
    m_media[1]->stop();
286
 
 
287
 
    if(!m_file.isNull()) {
288
 
        m_file = FileHandle::null();
289
 
        emit signalItemChanged(m_file);
 
349
    switch(m_media->state())
 
350
    {
 
351
    case Phonon::PlayingState:
 
352
    case Phonon::BufferingState:
 
353
        {
 
354
            Phonon::VolumeFaderEffect *fader = new Phonon::VolumeFaderEffect(m_media);
 
355
            m_audioPath.insertEffect(fader);
 
356
            fader->setFadeCurve(Phonon::VolumeFaderEffect::Fade9Decibel);
 
357
            fader->fadeOut(200);
 
358
            QTimer::singleShot(1000, m_media, SLOT(stop()));
 
359
            QTimer::singleShot(1200, fader, SLOT(deleteLater()));
 
360
            break;
 
361
        }
 
362
    default:
 
363
        m_media->stop();
290
364
    }
 
365
    m_playlistInterface->stop();
 
366
 
 
367
    m_file = FileHandle::null();
 
368
 
 
369
    emit signalStop();
291
370
}
292
371
 
293
372
void PlayerManager::setVolume(float volume)
294
373
{
295
 
    if(!m_setup)
 
374
    if(!m_output)
296
375
        setup();
297
 
 
298
 
    m_output[0]->setVolume(volume);
299
 
    m_output[1]->setVolume(volume);
 
376
    m_output->setVolume(volume);
300
377
}
301
378
 
302
379
void PlayerManager::seek(int seekTime)
303
380
{
304
 
    if(!m_setup || m_media[m_curOutputPath]->currentTime() == seekTime)
305
 
        return;
306
 
 
307
 
    kDebug() << "Stopping crossfade to seek from" << m_media[m_curOutputPath]->currentTime()
308
 
             << "to" << seekTime;
309
 
    stopCrossfade();
310
 
    m_media[m_curOutputPath]->seek(seekTime);
311
 
}
 
381
    if(!m_media)
 
382
        return;
 
383
 
 
384
    m_media->seek(seekTime);
 
385
}
 
386
 
 
387
/*
 
388
void PlayerManager::seekPosition(int position)
 
389
{
 
390
    if(!m_media)
 
391
        return;
 
392
 
 
393
    if(!playing() || m_noSeek)
 
394
        return;
 
395
 
 
396
    slotUpdateTime(position);
 
397
    m_media->seek(static_cast<qint64>(static_cast<float>(m_media->totalTime() * position) / SliderAction::maxPosition + 0.5f));
 
398
}
 
399
*/
312
400
 
313
401
void PlayerManager::seekForward()
314
402
{
315
 
    Phonon::MediaObject *mediaObject = m_media[m_curOutputPath];
316
 
    const qint64 total = mediaObject->totalTime();
317
 
    const qint64 newtime = mediaObject->currentTime() + total / 100;
318
 
 
319
 
    stopCrossfade();
320
 
    mediaObject->seek(qMin(total, newtime));
 
403
    const qint64 total = m_media->totalTime();
 
404
    const qint64 newtime = m_media->currentTime() + total / 100;
 
405
    m_media->seek(qMin(total, newtime));
321
406
}
322
407
 
323
408
void PlayerManager::seekBack()
324
409
{
325
 
    Phonon::MediaObject *mediaObject = m_media[m_curOutputPath];
326
 
    const qint64 total = mediaObject->totalTime();
327
 
    const qint64 newtime = mediaObject->currentTime() - total / 100;
328
 
 
329
 
    stopCrossfade();
330
 
    mediaObject->seek(qMax(qint64(0), newtime));
 
410
    const qint64 total = m_media->totalTime();
 
411
    const qint64 newtime = m_media->currentTime() - total / 100;
 
412
    m_media->seek(qMax(qint64(0), newtime));
331
413
}
332
414
 
333
415
void PlayerManager::playPause()
359
441
 
360
442
void PlayerManager::volumeUp()
361
443
{
362
 
    if(!m_setup)
 
444
    if(!m_output)
363
445
        return;
364
446
 
365
 
    setVolume(volume() + 0.04); // 4% up
 
447
    float volume = m_output->volume() + 0.04; // 4% up
 
448
    m_output->setVolume(volume);
366
449
}
367
450
 
368
451
void PlayerManager::volumeDown()
370
453
    if(!m_output)
371
454
        return;
372
455
 
373
 
    setVolume(volume() - 0.04); // 4% down
 
456
    float volume = m_output->volume() - 0.04; // 4% up
 
457
    m_output->setVolume(volume);
374
458
}
375
459
 
376
460
void PlayerManager::mute()
377
461
{
378
 
    if(!m_setup)
 
462
    if(!m_output)
379
463
        return;
380
464
 
381
 
    m_output[m_curOutputPath]->setMuted(!m_output[m_curOutputPath]->isMuted());
 
465
    m_output->setMuted(!m_output->isMuted());
382
466
}
383
467
 
384
468
////////////////////////////////////////////////////////////////////////////////
387
471
 
388
472
void PlayerManager::slotNeedNextUrl()
389
473
{
390
 
    if(m_file.isNull() || !m_crossfadeTracks)
 
474
    if(m_file.isNull())
 
475
    {
391
476
        return;
392
 
 
 
477
    }
393
478
    m_playlistInterface->playNext();
394
479
    FileHandle nextFile = m_playlistInterface->currentFile();
395
 
 
396
 
    if(!nextFile.isNull()) {
 
480
    if(!nextFile.isNull())
 
481
    {
 
482
        //kDebug() << m_file.absFilePath();
397
483
        m_file = nextFile;
398
 
        crossfadeToFile(m_file);
 
484
        m_media->enqueue(KUrl::fromPath(m_file.absFilePath()));
 
485
 
 
486
        emit signalPlay();
399
487
    }
400
488
}
401
489
 
402
490
void PlayerManager::slotFinished()
403
491
{
404
 
    // It is possible to end up in this function if a file simply fails to play or if the
405
 
    // user moves the slider all the way to the end, therefore see if we can keep playing
406
 
    // and if we can, do so.  Otherwise, stop.  Note that this slot should
407
 
    // only be called by the currently "main" output path (i.e. not from the
408
 
    // crossfading one).  However life isn't always so nice apparently, so do some
409
 
    // sanity-checking.
410
 
 
411
 
    Phonon::MediaObject *mediaObject = qobject_cast<Phonon::MediaObject *>(sender());
412
 
    if(mediaObject != m_media[m_curOutputPath])
413
 
        return;
414
 
 
415
 
    m_playlistInterface->playNext();
416
 
    m_file = m_playlistInterface->currentFile();
417
 
 
418
 
    if(m_file.isNull()) {
419
 
        stop();
420
 
    }
421
 
    else {
422
 
        emit signalItemChanged(m_file);
423
 
        m_media[m_curOutputPath]->setCurrentSource(m_file.absFilePath());
424
 
        m_media[m_curOutputPath]->play();
425
 
    }
 
492
    action("pause")->setEnabled(false);
 
493
    action("stop")->setEnabled(false);
 
494
    action("back")->setEnabled(false);
 
495
    action("forward")->setEnabled(false);
 
496
    action("forwardAlbum")->setEnabled(false);
 
497
 
 
498
    m_playlistInterface->stop();
 
499
 
 
500
    m_file = FileHandle::null();
 
501
 
 
502
    emit signalStop();
426
503
}
427
504
 
428
505
void PlayerManager::slotLength(qint64 msec)
432
509
 
433
510
void PlayerManager::slotTick(qint64 msec)
434
511
{
435
 
    if(!m_setup || !m_playlistInterface)
 
512
    if(!m_media || !m_playlistInterface)
436
513
        return;
437
514
 
438
 
    if(m_statusLabel)
 
515
    m_noSeek = true;
 
516
 
 
517
    if(m_statusLabel) {
439
518
        m_statusLabel->setItemCurrentTime(msec / 1000);
 
519
    }
 
520
 
 
521
    m_noSeek = false;
440
522
}
441
523
 
442
 
void PlayerManager::slotStateChanged(Phonon::State newstate, Phonon::State oldstate)
 
524
void PlayerManager::slotStateChanged(Phonon::State newstate)
443
525
{
444
 
    // Use sender() since either media object may have sent the signal.
445
 
    Phonon::MediaObject *mediaObject = qobject_cast<Phonon::MediaObject *>(sender());
446
 
    if(!mediaObject)
447
 
        return;
448
 
 
449
 
    // Handle errors for either media object
450
 
    if(newstate == Phonon::ErrorState) {
451
 
        QString errorMessage =
452
 
            i18nc(
453
 
              "%1 will be the /path/to/file, %2 will be some string from Phonon describing the error",
454
 
              "JuK is unable to play the audio file<nl/><filename>%1</filename><nl/>"
455
 
                "for the following reason:<nl/><message>%2</message>",
456
 
              m_file.absFilePath(),
457
 
              mediaObject->errorString()
458
 
            );
459
 
 
460
 
        switch(mediaObject->errorType()) {
 
526
    if(newstate == Phonon::ErrorState)
 
527
    {
 
528
        switch(m_media->errorType())
 
529
        {
461
530
            case Phonon::NoError:
462
531
                kDebug() << "received a state change to ErrorState but errorType is NoError!?";
463
532
                break;
464
 
 
465
533
            case Phonon::NormalError:
466
534
                forward();
467
 
                KMessageBox::information(0, errorMessage);
 
535
                KMessageBox::information(0, m_media->errorString());
468
536
                break;
469
 
 
470
537
            case Phonon::FatalError:
 
538
                // stop playback
471
539
                stop();
472
 
                KMessageBox::sorry(0, errorMessage);
 
540
                KMessageBox::sorry(0, m_media->errorString());
473
541
                break;
474
542
        }
475
543
    }
476
 
 
477
 
    // Now bail out if we're not dealing with the currently playing media
478
 
    // object.
479
 
 
480
 
    if(mediaObject != m_media[m_curOutputPath])
 
544
}
 
545
 
 
546
/*
 
547
void PlayerManager::slotUpdateTime(int position)
 
548
{
 
549
    if(!m_statusLabel)
481
550
        return;
482
551
 
483
 
    // Handle state changes for the playing media object.
484
 
    if(newstate == Phonon::StoppedState && oldstate != Phonon::LoadingState) {
485
 
        // If this occurs it should be due to a transitory shift (i.e. playing a different
486
 
        // song when one is playing now), since it didn't occur in the error handler.  Just
487
 
        // in case we really did abruptly stop, handle that case in a couple of seconds.
488
 
        QTimer::singleShot(2000, this, SLOT(slotUpdateGuiIfStopped()));
489
 
        emit signalStop();
490
 
    }
491
 
    else if(newstate == Phonon::PlayingState) {
492
 
        action("pause")->setEnabled(true);
493
 
        action("stop")->setEnabled(true);
494
 
        action("forward")->setEnabled(true);
495
 
        if(action<KToggleAction>("albumRandomPlay")->isChecked())
496
 
            action("forwardAlbum")->setEnabled(true);
497
 
        action("back")->setEnabled(true);
 
552
    float positionFraction = float(position) / SliderAction::maxPosition;
 
553
    float totalTime = float(m_media->totalTime()) / 1000.0f;
 
554
    int seekTime = int(positionFraction * totalTime + 0.5); // "+0.5" for rounding
498
555
 
499
 
        emit signalPlay();
500
 
    }
 
556
    m_statusLabel->setItemCurrentTime(seekTime);
501
557
}
 
558
*/
502
559
 
503
560
////////////////////////////////////////////////////////////////////////////////
504
561
// private members
515
572
       !action("forward") ||
516
573
       !action("trackPositionAction"))
517
574
    {
518
 
        kWarning() << "Could not find all of the required actions.";
 
575
        kWarning(65432) << "Could not find all of the required actions.";
519
576
        return;
520
577
    }
521
578
 
523
580
        return;
524
581
    m_setup = true;
525
582
 
526
 
    // We use two audio paths at all times to make cross fading easier (and to also easily
527
 
    // support not using cross fading with the same code).  The currently playing audio
528
 
    // path is controlled using m_curOutputPath.
529
 
 
530
 
    for(int i = 0; i < 2; ++i) {
531
 
        m_output[i] = new Phonon::AudioOutput(Phonon::MusicCategory, this);
532
 
 
533
 
        m_media[i] = new Phonon::MediaObject(this);
534
 
        m_audioPath[i] = Phonon::createPath(m_media[i], m_output[i]);
535
 
        m_media[i]->setTickInterval(200);
536
 
        m_media[i]->setPrefinishMark(2000);
537
 
 
538
 
        // Pre-cache a volume fader object
539
 
        m_fader[i] = new Phonon::VolumeFaderEffect(m_media[i]);
540
 
        m_audioPath[i].insertEffect(m_fader[i]);
541
 
        m_fader[i]->setVolume(1.0f);
542
 
 
543
 
        connect(m_media[i], SIGNAL(stateChanged(Phonon::State, Phonon::State)), SLOT(slotStateChanged(Phonon::State, Phonon::State)));
544
 
        connect(m_media[i], SIGNAL(prefinishMarkReached(qint32)), SLOT(slotNeedNextUrl()));
545
 
        connect(m_media[i], SIGNAL(totalTimeChanged(qint64)), SLOT(slotLength(qint64)));
546
 
        connect(m_media[i], SIGNAL(tick(qint64)), SLOT(slotTick(qint64)));
547
 
        connect(m_media[i], SIGNAL(finished()), SLOT(slotFinished()));
548
 
    }
 
583
    m_output = new Phonon::AudioOutput(Phonon::MusicCategory, this);
 
584
 
 
585
    m_media = new Phonon::MediaObject(this);
 
586
    connect(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), SLOT(slotStateChanged(Phonon::State)));
 
587
    connect(m_media, SIGNAL(aboutToFinish()), SLOT(slotNeedNextUrl()));
 
588
    m_audioPath = Phonon::createPath(m_media, m_output);
 
589
    m_media->setTickInterval(200);
549
590
 
550
591
    // initialize action states
551
592
 
555
596
    action("forward")->setEnabled(false);
556
597
    action("forwardAlbum")->setEnabled(false);
557
598
 
558
 
    // setup sliders (a separate slot is used to switch as needed)
 
599
    // setup sliders
559
600
 
560
601
    m_sliderAction = action<SliderAction>("trackPositionAction");
561
602
 
 
603
    /*
 
604
    connect(m_sliderAction, SIGNAL(signalPositionChanged(int)),
 
605
            this, SLOT(seekPosition(int)));
 
606
    connect(m_sliderAction->trackPositionSlider(), SIGNAL(valueChanged(int)),
 
607
            this, SLOT(slotUpdateTime(int)));
 
608
            */
 
609
 
562
610
    if(m_sliderAction->trackPositionSlider())
563
 
        m_sliderAction->trackPositionSlider()->setMediaObject(m_media[0]);
564
 
 
 
611
    {
 
612
        m_sliderAction->trackPositionSlider()->setMediaObject(m_media);
 
613
    }
565
614
    if(m_sliderAction->volumeSlider())
566
 
        m_sliderAction->volumeSlider()->setAudioOutput(m_output[0]);
567
 
 
568
 
    QDBusConnection::sessionBus().registerObject("/Player", this);
569
 
}
570
 
 
571
 
void PlayerManager::slotUpdateSliders()
572
 
{
573
 
    m_sliderAction->trackPositionSlider()->setMediaObject(m_media[m_curOutputPath]);
574
 
    m_sliderAction->volumeSlider()->setAudioOutput(m_output[m_curOutputPath]);
575
 
 
576
 
    disconnect(m_media[1 - m_curOutputPath], 0, this, SLOT(slotUpdateSliders()));
577
 
    connect(m_media[1 - m_curOutputPath], SIGNAL(finished()), SLOT(slotFinished()));
578
 
}
579
 
 
580
 
void PlayerManager::slotUpdateGuiIfStopped()
581
 
{
582
 
    if(m_media[0]->state() == Phonon::StoppedState && m_media[1]->state() == Phonon::StoppedState)
583
 
        stop();
584
 
}
585
 
 
586
 
void PlayerManager::crossfadeToFile(const FileHandle &newFile)
587
 
{
588
 
    int nextOutputPath = 1 - m_curOutputPath;
589
 
 
590
 
    // Don't need this anymore
591
 
    disconnect(m_media[m_curOutputPath], SIGNAL(finished()), this, 0);
592
 
    connect(m_media[nextOutputPath], SIGNAL(finished()), SLOT(slotFinished()));
593
 
 
594
 
    // Wait a couple of seconds and switch slider objects.  (We would simply
595
 
    // handle this when finished() is emitted but phonon-gst is buggy).
596
 
    QTimer::singleShot(2000, this, SLOT(slotUpdateSliders()));
597
 
 
598
 
    m_fader[nextOutputPath]->setVolume(0.0f);
599
 
 
600
 
    emit signalItemChanged(newFile);
601
 
    m_media[nextOutputPath]->setCurrentSource(newFile.absFilePath());
602
 
    m_media[nextOutputPath]->play();
603
 
 
604
 
    m_fader[m_curOutputPath]->setVolume(1.0f);
605
 
    m_fader[m_curOutputPath]->fadeTo(0.0f, 2000);
606
 
 
607
 
    m_fader[nextOutputPath]->fadeTo(1.0f, 2000);
608
 
 
609
 
    m_curOutputPath = nextOutputPath;
610
 
}
611
 
 
612
 
void PlayerManager::stopCrossfade()
613
 
{
614
 
    // According to the Phonon docs, setVolume immediately takes effect,
615
 
    // which is "good enough for government work" ;)
616
 
 
617
 
    // 1 - curOutputPath is the other output path...
618
 
    m_fader[m_curOutputPath]->setVolume(1.0f);
619
 
    m_fader[1 - m_curOutputPath]->setVolume(0.0f);
620
 
 
621
 
    // We don't actually need to physically stop crossfading as the playback
622
 
    // code will call ->play() when necessary anyways.  If we hit stop()
623
 
    // here instead of pause() then we will trick our stateChanged handler
624
 
    // into thinking Phonon had a spurious stop and we'll switch tracks
625
 
    // unnecessarily.  (This isn't a problem after crossfade completes due to
626
 
    // the signals being disconnected).
627
 
 
628
 
    m_media[1 - m_curOutputPath]->pause();
 
615
    {
 
616
        m_sliderAction->volumeSlider()->setAudioOutput(m_output);
 
617
    }
 
618
 
 
619
    connect(m_media, SIGNAL(totalTimeChanged(qint64)), SLOT(slotLength(qint64)));
 
620
    connect(m_media, SIGNAL(tick(qint64)), SLOT(slotTick(qint64)));
 
621
    connect(m_media, SIGNAL(finished()), SLOT(slotFinished()));
629
622
}
630
623
 
631
624
QString PlayerManager::randomPlayMode() const
637
630
    return "NoRandom";
638
631
}
639
632
 
640
 
void PlayerManager::setCrossfadeEnabled(bool crossfadeEnabled)
641
 
{
642
 
    m_crossfadeTracks = crossfadeEnabled;
643
 
}
644
 
 
645
633
void PlayerManager::setRandomPlayMode(const QString &randomMode)
646
634
{
647
635
    if(randomMode.toLower() == "random")