playermanager.cpp 16.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/***************************************************************************
    begin                : Sat Feb 14 2004
    copyright            : (C) 2004 by Scott Wheeler
    email                : wheeler@kde.org
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

16 17 18 19 20 21 22 23 24
/**
 * Note to those who work here.  The preprocessor variables HAVE_ARTS and HAVE_GSTREAMER
 * are ::ALWAYS DEFINED::.  You can't use #ifdef to see if they're present, you should just
 * use #if.
 *
 * However, HAVE_AKODE is #define'd if present, and undefined if not present.
 * - mpyne
 */

25 26 27
#include "playermanager.h"
#include <config.h>

28
#include <kdebug.h>
Matthias Kretz's avatar
Matthias Kretz committed
29
#include <kmessagebox.h>
30
#include <klocale.h>
31 32 33
#include <kactioncollection.h>
#include <kselectaction.h>
#include <ktoggleaction.h>
34

35 36 37
#include <Phonon/AudioOutput>
#include <Phonon/MediaObject>
#include <Phonon/VolumeFaderEffect>
38

39
#include <QSlider>
Laurent Montel's avatar
Laurent Montel committed
40
#include <QPixmap>
41 42 43
#include <QtDBus>
#include <QTimer>

44 45
#include <math.h>

46
#include "playlistinterface.h"
47
#include "playeradaptor.h"
48
#include "slideraction.h"
49
#include "statuslabel.h"
50
#include "actioncollection.h"
51
#include "collectionlist.h"
Michael Pyne's avatar
Michael Pyne committed
52
#include "coverinfo.h"
53
#include "tag.h"
54 55

using namespace ActionCollection;
56

Stephan Kulow's avatar
Stephan Kulow committed
57
enum PlayerManagerStatus { StatusStopped = -1, StatusPaused = 1, StatusPlaying = 2 };
58

59 60 61 62 63
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////

PlayerManager::PlayerManager() :
64
    QObject(),
65
    m_sliderAction(0),
66
    m_playlistInterface(0),
67
    m_statusLabel(0),
68
    m_noSeek(false),
69
    m_muted(false),
70 71 72
    m_setup(false),
    m_output(0),
    m_media(0)
73
{
Michael Pyne's avatar
Michael Pyne committed
74 75 76 77 78
// This class is the first thing constructed during program startup, and
// therefore has no access to the widgets needed by the setup() method.
// Since the setup() method will be called indirectly by the player() method
// later, just disable it here. -- mpyne
//    setup();
Laurent Montel's avatar
Laurent Montel committed
79
    new PlayerAdaptor( this );
80
    QDBusConnection::sessionBus().registerObject("/Player", this);
Laurent Montel's avatar
Laurent Montel committed
81

82 83 84 85 86 87 88 89 90 91 92 93
}

PlayerManager::~PlayerManager()
{
}

////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

PlayerManager *PlayerManager::instance() // static
{
94 95
    static PlayerManager manager;
    return &manager;
96 97
}

98 99
bool PlayerManager::playing() const
{
100
    if(!m_media)
101 102
        return false;

103
    return (m_media->state() == Phonon::PlayingState || m_media->state() == Phonon::BufferingState);
104 105 106 107
}

bool PlayerManager::paused() const
{
108
    if(!m_media)
109 110
        return false;

111
    return m_media->state() == Phonon::PausedState;
112 113 114 115
}

float PlayerManager::volume() const
{
116
    if(!m_output)
117
        return 1.0;
118

119
    return m_output->volume();
120 121
}

122 123
int PlayerManager::status() const
{
124
    if(!m_media)
125
        return StatusStopped;
126

127
    if(paused())
128
        return StatusPaused;
129

130
    if(playing())
131
        return StatusPlaying;
132

133 134 135
    return 0;
}

Scott Wheeler's avatar
Scott Wheeler committed
136
int PlayerManager::totalTime() const
137
{
138
    if(!m_media)
139 140
        return 0;

141
    return m_media->totalTime() / 1000;
142 143
}

Scott Wheeler's avatar
Scott Wheeler committed
144
int PlayerManager::currentTime() const
145
{
146
    if(!m_media)
147 148
        return 0;

149
    return m_media->currentTime() / 1000;
150 151
}

152
/*
153 154
int PlayerManager::position() const
{
155
    if(!m_media)
156 157
        return 0;

158
    long curr = m_media->currentTime();
159 160
    if(curr > 0)
        return static_cast<int>(static_cast<float>(curr * SliderAction::maxPosition) / m_media->totalTime() + 0.5f);
161
    return -1;
162
}
163
*/
164

165 166 167 168 169 170 171
QStringList PlayerManager::trackProperties()
{
    return FileHandle::properties();
}

QString PlayerManager::trackProperty(const QString &property) const
{
Michael Pyne's avatar
Michael Pyne committed
172
    if(!playing() && !paused())
173
        return QString();
174 175 176 177

    return m_file.property(property);
}

Michael Pyne's avatar
Michael Pyne committed
178 179 180 181 182
QPixmap PlayerManager::trackCover(const QString &size) const
{
    if(!playing() && !paused())
        return QPixmap();

183
    if(size.toLower() == "small")
Michael Pyne's avatar
Michael Pyne committed
184
        return m_file.coverInfo()->pixmap(CoverInfo::Thumbnail);
185
    if(size.toLower() == "large")
Michael Pyne's avatar
Michael Pyne committed
186 187 188 189 190
        return m_file.coverInfo()->pixmap(CoverInfo::FullSize);

    return QPixmap();
}

191 192 193 194 195
FileHandle PlayerManager::playingFile() const
{
    return m_file;
}

196 197 198
QString PlayerManager::playingString() const
{
    if(!playing())
199
        return QString();
200

201 202 203 204 205
    QString str = m_file.tag()->artist() + " - " + m_file.tag()->title();
    if(m_file.tag()->artist().isEmpty())
        str = m_file.tag()->title();

    return str;
206 207
}

208
void PlayerManager::setPlaylistInterface(PlaylistInterface *interface)
209 210 211 212
{
    m_playlistInterface = interface;
}

213 214 215 216 217
void PlayerManager::setStatusLabel(StatusLabel *label)
{
    m_statusLabel = label;
}

218 219 220 221
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

222
void PlayerManager::play(const FileHandle &file)
223
{
224 225 226 227
    if(!m_media)
        setup();

    if(!m_media || !m_playlistInterface)
228 229
        return;

230
    if(file.isNull()) {
231 232 233 234
        if(paused())
            m_media->play();
        else if(playing()) {
            m_media->seek(0);
235
        }
236
        else {
237
            m_playlistInterface->playNext();
238
            m_file = m_playlistInterface->currentFile();
239

240
            if(!m_file.isNull())
241
            {
Matthias Kretz's avatar
Matthias Kretz committed
242
                m_media->setCurrentSource(KUrl::fromPath(m_file.absFilePath()));
243 244
                m_media->play();
            }
245
        }
246
    }
247
    else {
248
        m_file = file;
Matthias Kretz's avatar
Matthias Kretz committed
249 250 251
        if(m_media->state() == Phonon::PlayingState)
        {
            // do a crossfade
252 253
            Phonon::VolumeFaderEffect *fader1 = new Phonon::VolumeFaderEffect(m_media);
            m_audioPath.insertEffect(fader1);
Matthias Kretz's avatar
Matthias Kretz committed
254
            Phonon::MediaObject *mo = m_media;
255
            Phonon::AudioOutput *out = m_output;
Matthias Kretz's avatar
Matthias Kretz committed
256 257

            m_media = new Phonon::MediaObject(this);
258 259 260 261
            m_output = new Phonon::AudioOutput(Phonon::MusicCategory, this);
            Phonon::VolumeFaderEffect *fader2 = new Phonon::VolumeFaderEffect(m_media);
            m_audioPath.insertEffect(fader2);

Matthias Kretz's avatar
Matthias Kretz committed
262
            connect(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), SLOT(slotStateChanged(Phonon::State)));
Matthias Kretz's avatar
Matthias Kretz committed
263
            connect(m_media, SIGNAL(aboutToFinsh()), SLOT(slotNeedNextUrl()));
264
            m_audioPath = Phonon::createPath(m_media, m_output);
Matthias Kretz's avatar
Matthias Kretz committed
265 266 267
            m_media->setTickInterval(200);
            if(m_sliderAction->trackPositionSlider())
                m_sliderAction->trackPositionSlider()->setMediaObject(m_media);
Matthias Kretz's avatar
Matthias Kretz committed
268
            connect(m_media, SIGNAL(totalTimeChanged(qint64)), SLOT(slotLength(qint64)));
Matthias Kretz's avatar
Matthias Kretz committed
269 270 271 272 273 274 275 276 277
            connect(m_media, SIGNAL(tick(qint64)), SLOT(slotTick(qint64)));
            connect(m_media, SIGNAL(finished()), SLOT(slotFinished()));
            m_media->setCurrentSource(KUrl::fromPath(m_file.absFilePath()));

            fader2->setVolume(0.0f);
            fader1->fadeOut(2000);
            fader2->fadeIn(2000);
            m_media->play();
            QTimer::singleShot(3000, mo, SLOT(deleteLater()));
278
            QTimer::singleShot(3000, out, SLOT(deleteLater()));
Matthias Kretz's avatar
Matthias Kretz committed
279 280 281 282 283 284
        }
        else
        {
            m_media->setCurrentSource(KUrl::fromPath(m_file.absFilePath()));
            m_media->play();
        }
285
    }
286

287
    // Make sure that the player() actually starts before doing anything.
288

289
    if(!playing()) {
290
        kWarning(65432) << "Unable to play " << file.absFilePath();
291 292 293
        stop();
        return;
    }
294

295 296 297
    action("pause")->setEnabled(true);
    action("stop")->setEnabled(true);
    action("forward")->setEnabled(true);
298 299
    if(action<KToggleAction>("albumRandomPlay")->isChecked())
        action("forwardAlbum")->setEnabled(true);
300
    action("back")->setEnabled(true);
301

302
    emit signalPlay();
303 304
}

305 306 307 308 309 310 311 312 313
void PlayerManager::play(const QString &file)
{
    CollectionListItem *item = CollectionList::instance()->lookup(file);
    if(item) {
        Playlist::setPlaying(item);
        play(item->file());
    }
}

Scott Wheeler's avatar
Scott Wheeler committed
314 315 316 317 318
void PlayerManager::play()
{
    play(FileHandle::null());
}

319 320
void PlayerManager::pause()
{
321
    if(!m_media)
322 323
        return;

324
    if(paused()) {
325 326 327 328
        play();
        return;
    }

329
    action("pause")->setEnabled(false);
330

331
    m_media->pause();
332 333

    emit signalPause();
334 335 336 337
}

void PlayerManager::stop()
{
338
    if(!m_media || !m_playlistInterface)
339 340
        return;

341 342 343 344
    action("pause")->setEnabled(false);
    action("stop")->setEnabled(false);
    action("back")->setEnabled(false);
    action("forward")->setEnabled(false);
345
    action("forwardAlbum")->setEnabled(false);
346

Matthias Kretz's avatar
Matthias Kretz committed
347
    Phonon::VolumeFaderEffect *fader = new Phonon::VolumeFaderEffect(m_media);
348
    m_audioPath.insertEffect(fader);
Matthias Kretz's avatar
Matthias Kretz committed
349
    fader->setFadeCurve(Phonon::VolumeFaderEffect::Fade9Decibel);
350 351 352
    fader->fadeOut(200);
    QTimer::singleShot(1000, m_media, SLOT(stop()));
    QTimer::singleShot(1200, fader, SLOT(deleteLater()));
353
    m_playlistInterface->stop();
354

355 356
    m_file = FileHandle::null();

357
    emit signalStop();
358 359
}

Laurent Montel's avatar
Laurent Montel committed
360
void PlayerManager::setVolume(float volume)
361
{
362 363 364
    if(!m_output)
        setup();
    m_output->setVolume(volume);
365 366
}

Scott Wheeler's avatar
Scott Wheeler committed
367
void PlayerManager::seek(int seekTime)
368
{
369
    if(!m_media)
370
        return;
371

372
    m_media->seek(seekTime);
373 374
}

375
/*
376
void PlayerManager::seekPosition(int position)
377
{
378
    if(!m_media)
379 380
        return;

381
    if(!playing() || m_noSeek)
382
        return;
383

384
    slotUpdateTime(position);
385
    m_media->seek(static_cast<qint64>(static_cast<float>(m_media->totalTime() * position) / SliderAction::maxPosition + 0.5f));
386
}
387
*/
388 389 390

void PlayerManager::seekForward()
{
391 392 393
    const qint64 total = m_media->totalTime();
    const qint64 newtime = m_media->currentTime() + total / 100;
    m_media->seek(qMin(total, newtime));
394 395 396 397
}

void PlayerManager::seekBack()
{
398 399 400
    const qint64 total = m_media->totalTime();
    const qint64 newtime = m_media->currentTime() - total / 100;
    m_media->seek(qMax(qint64(0), newtime));
401 402
}

403 404
void PlayerManager::playPause()
{
David Faure's avatar
David Faure committed
405
    playing() ? action("pause")->trigger() : action("play")->trigger();
406 407
}

408 409
void PlayerManager::forward()
{
410 411
    m_playlistInterface->playNext();
    FileHandle file = m_playlistInterface->currentFile();
412

413 414 415 416 417 418 419 420
    if(!file.isNull())
        play(file);
    else
        stop();
}

void PlayerManager::back()
{
421 422
    m_playlistInterface->playPrevious();
    FileHandle file = m_playlistInterface->currentFile();
423

424 425 426 427 428 429
    if(!file.isNull())
        play(file);
    else
        stop();
}

430 431
void PlayerManager::volumeUp()
{
432
    if(!m_output)
433
        return;
434

435 436
    float volume = m_output->volume() + 0.04; // 4% up
    m_output->setVolume(volume);
437 438 439 440
}

void PlayerManager::volumeDown()
{
441
    if(!m_output)
442
        return;
443

444 445
    float volume = m_output->volume() - 0.04; // 4% up
    m_output->setVolume(volume);
446 447 448 449
}

void PlayerManager::mute()
{
450
    if(!m_output)
451 452
        return;

453
    m_output->setMuted(!m_output->isMuted());
454 455
}

456 457 458
////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////
459

460 461
void PlayerManager::slotNeedNextUrl()
{
Matthias Kretz's avatar
Matthias Kretz committed
462 463 464 465
    if(m_file.isNull())
    {
        return;
    }
466 467 468 469
    m_playlistInterface->playNext();
    FileHandle nextFile = m_playlistInterface->currentFile();
    if(!nextFile.isNull())
    {
470
        //kDebug() << k_funcinfo << m_file.absFilePath();
471
        m_file = nextFile;
Matthias Kretz's avatar
Matthias Kretz committed
472
        m_media->enqueue(KUrl::fromPath(m_file.absFilePath()));
473
    }
Matthias Kretz's avatar
Matthias Kretz committed
474
    // at this point the new totalTime is not known, but the totalTimeChanged signal will tell us
475 476
}

477 478
void PlayerManager::slotFinished()
{
Matthias Kretz's avatar
Matthias Kretz committed
479
    if(m_file.isNull())
480
    {
481
        //kDebug() << k_funcinfo << "ignoring finished signal";
482 483
        return;
    }
484 485 486 487 488 489 490 491 492
    m_playlistInterface->playNext();
    FileHandle nextFile = m_playlistInterface->currentFile();
    if(!nextFile.isNull())
        play(nextFile);
    else
        stop();
    m_statusLabel->setItemTotalTime(totalTime());
}

493 494 495 496 497
void PlayerManager::slotLength(qint64 msec)
{
    m_statusLabel->setItemTotalTime(msec / 1000);
}

498
void PlayerManager::slotTick(qint64 msec)
499
{
500
    if(!m_media || !m_playlistInterface)
501
        return;
502

503
    m_noSeek = true;
504

505 506
    if(m_statusLabel) {
        m_statusLabel->setItemCurrentTime(msec / 1000);
507
    }
508

509
    m_noSeek = false;
510 511
}

Matthias Kretz's avatar
Matthias Kretz committed
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
void PlayerManager::slotStateChanged(Phonon::State newstate)
{
    if(newstate == Phonon::ErrorState)
    {
        switch(m_media->errorType())
        {
            case Phonon::NormalError:
                forward();
                KMessageBox::information(0, m_media->errorString());
                break;
            case Phonon::FatalError:
                // stop playback
                stop();
                KMessageBox::sorry(0, m_media->errorString());
                break;
        }
    }
}

531
/*
532 533 534 535 536
void PlayerManager::slotUpdateTime(int position)
{
    if(!m_statusLabel)
        return;

537
    float positionFraction = float(position) / SliderAction::maxPosition;
538
    float totalTime = float(m_media->totalTime()) / 1000.0f;
Scott Wheeler's avatar
Scott Wheeler committed
539
    int seekTime = int(positionFraction * totalTime + 0.5); // "+0.5" for rounding
540 541 542

    m_statusLabel->setItemCurrentTime(seekTime);
}
543
*/
544

545 546 547 548 549 550 551 552
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

void PlayerManager::setup()
{
    // All of the actions required by this class should be listed here.

553 554 555
    if(!action("pause") ||
       !action("stop") ||
       !action("back") ||
556
       !action("forwardAlbum") ||
557 558
       !action("forward") ||
       !action("trackPositionAction"))
559
    {
560
        kWarning(65432) << k_funcinfo << "Could not find all of the required actions.";
561 562 563
        return;
    }

564 565 566 567
    if(m_setup)
        return;
    m_setup = true;

568
    m_output = new Phonon::AudioOutput(Phonon::MusicCategory, this);
569

Matthias Kretz's avatar
Matthias Kretz committed
570
    m_media = new Phonon::MediaObject(this);
Matthias Kretz's avatar
Matthias Kretz committed
571
    connect(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), SLOT(slotStateChanged(Phonon::State)));
Matthias Kretz's avatar
Matthias Kretz committed
572
    connect(m_media, SIGNAL(aboutToFinish()), SLOT(slotNeedNextUrl()));
573
    m_audioPath = Phonon::createPath(m_media, m_output);
574
    m_media->setTickInterval(200);
575

576 577
    // initialize action states

578 579 580 581
    action("pause")->setEnabled(false);
    action("stop")->setEnabled(false);
    action("back")->setEnabled(false);
    action("forward")->setEnabled(false);
582
    action("forwardAlbum")->setEnabled(false);
583

584 585
    // setup sliders

586
    m_sliderAction = action<SliderAction>("trackPositionAction");
587

588
    /*
589 590 591 592
    connect(m_sliderAction, SIGNAL(signalPositionChanged(int)),
            this, SLOT(seekPosition(int)));
    connect(m_sliderAction->trackPositionSlider(), SIGNAL(valueChanged(int)),
            this, SLOT(slotUpdateTime(int)));
593
            */
594

595 596
    if(m_sliderAction->trackPositionSlider())
    {
Matthias Kretz's avatar
Matthias Kretz committed
597
        m_sliderAction->trackPositionSlider()->setMediaObject(m_media);
598 599 600 601
    }
    if(m_sliderAction->volumeSlider())
    {
        m_sliderAction->volumeSlider()->setAudioOutput(m_output);
602
    }
603

Matthias Kretz's avatar
Matthias Kretz committed
604
    connect(m_media, SIGNAL(totalTimeChanged(qint64)), SLOT(slotLength(qint64)));
605
    connect(m_media, SIGNAL(tick(qint64)), SLOT(slotTick(qint64)));
606
    connect(m_media, SIGNAL(finished()), SLOT(slotFinished()));
607 608
}

Michael Pyne's avatar
Michael Pyne committed
609 610 611 612 613 614 615 616 617 618 619
QString PlayerManager::randomPlayMode() const
{
    if(action<KToggleAction>("randomPlay")->isChecked())
        return "Random";
    if(action<KToggleAction>("albumRandomPlay")->isChecked())
        return "AlbumRandom";
    return "NoRandom";
}

void PlayerManager::setRandomPlayMode(const QString &randomMode)
{
620
    if(randomMode.toLower() == "random")
Michael Pyne's avatar
Michael Pyne committed
621
        action<KToggleAction>("randomPlay")->setChecked(true);
622
    if(randomMode.toLower() == "albumrandom")
Michael Pyne's avatar
Michael Pyne committed
623
        action<KToggleAction>("albumRandomPlay")->setChecked(true);
624
    if(randomMode.toLower() == "norandom")
Michael Pyne's avatar
Michael Pyne committed
625 626 627
        action<KToggleAction>("disableRandomPlay")->setChecked(true);
}

628
#include "playermanager.moc"
629

630
// vim: set et sw=4 tw=0 sta: