playermanager.cpp 15.5 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
#include <kdebug.h>
26
#include <klocale.h>
27

28 29 30
#include <phonon/mediaobject.h>
#include <phonon/audiopath.h>
#include <phonon/audiooutput.h>
Matthias Kretz's avatar
Matthias Kretz committed
31
#include <phonon/volumefadereffect.h>
32

33
#include <qslider.h>
Laurent Montel's avatar
Laurent Montel committed
34 35
//Added by qt3to4:
#include <QPixmap>
Laurent Montel's avatar
Poirt  
Laurent Montel committed
36 37 38
#include <ktoggleaction.h>
#include <kactioncollection.h>
#include <kselectaction.h>
39 40
#include <math.h>

41
#include "playermanager.h"
42
#include "playlistinterface.h"
43
#include "slideraction.h"
44
#include "statuslabel.h"
45
#include "actioncollection.h"
46
#include "collectionlist.h"
Michael Pyne's avatar
Michael Pyne committed
47
#include "coverinfo.h"
48
#include "tag.h"
49

50 51
#include "config.h"

Laurent Montel's avatar
Laurent Montel committed
52
#include <QtDBus>
Laurent Montel's avatar
Laurent Montel committed
53
#include "playeradaptor.h"
Matthias Kretz's avatar
Matthias Kretz committed
54
#include <QTimer>
Laurent Montel's avatar
Laurent Montel committed
55

56
using namespace ActionCollection;
57

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

60 61 62 63 64
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////

PlayerManager::PlayerManager() :
65
    QObject(),
66
    m_sliderAction(0),
67
    m_playlistInterface(0),
68
    m_statusLabel(0),
69
    m_noSeek(false),
70
    m_muted(false),
71 72 73 74
    m_setup(false),
    m_output(0),
    m_audioPath(0),
    m_media(0)
75
{
Michael Pyne's avatar
Michael Pyne committed
76 77 78 79 80
// 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
81
    new PlayerAdaptor( this );
82
    QDBusConnection::sessionBus().registerObject("/Player", this);
Laurent Montel's avatar
Laurent Montel committed
83

84 85 86 87 88 89 90 91 92 93 94 95
}

PlayerManager::~PlayerManager()
{
}

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

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

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

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

bool PlayerManager::paused() const
{
110
    if(!m_media)
111 112
        return false;

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

float PlayerManager::volume() const
{
118
    if(!m_output)
119
        return 1.0;
120

121
    return m_output->volume();
122 123
}

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

129
    if(paused())
130
        return StatusPaused;
131

132
    if(playing())
133
        return StatusPlaying;
134

135 136 137
    return 0;
}

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

143
    return m_media->totalTime() / 1000;
144 145
}

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

151
    return m_media->currentTime() / 1000;
152 153
}

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

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

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

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

    return m_file.property(property);
}

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

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

    return QPixmap();
}

193 194 195 196 197
FileHandle PlayerManager::playingFile() const
{
    return m_file;
}

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

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

    return str;
208 209
}

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

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

220 221 222 223
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

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

    if(!m_media || !m_playlistInterface)
230 231
        return;

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

242
            if(!m_file.isNull())
243
            {
Matthias Kretz's avatar
Matthias Kretz committed
244
                m_media->setCurrentSource(KUrl::fromPath(m_file.absFilePath()));
245 246
                m_media->play();
            }
247
        }
248
    }
249
    else {
250
        m_file = file;
Matthias Kretz's avatar
Matthias Kretz committed
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
        if(m_media->state() == Phonon::PlayingState)
        {
            // do a crossfade
            Phonon::VolumeFaderEffect *fader1 = new Phonon::VolumeFaderEffect(m_audioPath);
            m_audioPath->insertEffect(fader1);
            Phonon::MediaObject *mo = m_media;
            Phonon::AudioPath *ap = m_audioPath;

            m_audioPath = new Phonon::AudioPath(this);
            m_audioPath->addOutput(m_output);
            Phonon::VolumeFaderEffect *fader2 = new Phonon::VolumeFaderEffect(m_audioPath);
            m_audioPath->insertEffect(fader2);

            m_media = new Phonon::MediaObject(this);
            connect(m_media, SIGNAL(aboutToFinsh()), SLOT(slotNeedNextUrl()));
            m_media->addAudioPath(m_audioPath);
            m_media->setTickInterval(200);
            if(m_sliderAction->trackPositionSlider())
                m_sliderAction->trackPositionSlider()->setMediaObject(m_media);
            connect(m_media, SIGNAL(length(qint64)), SLOT(slotLength(qint64)));
            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()));
            QTimer::singleShot(3000, ap, SLOT(deleteLater()));
        }
        else
        {
            m_media->setCurrentSource(KUrl::fromPath(m_file.absFilePath()));
            m_media->play();
        }
287
    }
288

289
    // Make sure that the player() actually starts before doing anything.
290

291
    if(!playing()) {
Scott Wheeler's avatar
Scott Wheeler committed
292
        kWarning(65432) << "Unable to play " << file.absFilePath() << endl;
293 294 295
        stop();
        return;
    }
296

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

304
    emit signalPlay();
305 306
}

307 308 309 310 311 312 313 314 315
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
316 317 318 319 320
void PlayerManager::play()
{
    play(FileHandle::null());
}

321 322
void PlayerManager::pause()
{
323
    if(!m_media)
324 325
        return;

326
    if(paused()) {
327 328 329 330
        play();
        return;
    }

331
    action("pause")->setEnabled(false);
332

333
    m_media->pause();
334 335

    emit signalPause();
336 337 338 339
}

void PlayerManager::stop()
{
340
    if(!m_media || !m_playlistInterface)
341 342
        return;

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

Matthias Kretz's avatar
Matthias Kretz committed
349 350 351 352 353 354
    Phonon::VolumeFaderEffect *fader = new Phonon::VolumeFaderEffect(m_media);
    m_audioPath->insertEffect(fader);
    fader->setFadeCurve(Phonon::VolumeFaderEffect::Fade9Decibel);
    fader->fadeOut(2000);
    QTimer::singleShot(3000, m_media, SLOT(stop()));
    QTimer::singleShot(3000, fader, SLOT(deleteLater()));
355
    m_playlistInterface->stop();
356

357 358
    m_file = FileHandle::null();

359
    emit signalStop();
360 361
}

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

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

374
    m_media->seek(seekTime);
375 376
}

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

383
    if(!playing() || m_noSeek)
384
        return;
385

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

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

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

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

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

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

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

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

432 433
void PlayerManager::volumeUp()
{
434
    if(!m_output)
435
        return;
436

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

void PlayerManager::volumeDown()
{
443
    if(!m_output)
444
        return;
445

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

void PlayerManager::mute()
{
452
    if(!m_output)
453 454
        return;

455
    m_output->setMuted(!m_output->isMuted());
456 457
}

458 459 460
////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////
461

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

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

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

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

505
    m_noSeek = true;
506

507 508
    if(m_statusLabel) {
        m_statusLabel->setItemCurrentTime(msec / 1000);
509
    }
510

511
    m_noSeek = false;
512 513
}

514
/*
515 516 517 518 519
void PlayerManager::slotUpdateTime(int position)
{
    if(!m_statusLabel)
        return;

520
    float positionFraction = float(position) / SliderAction::maxPosition;
521
    float totalTime = float(m_media->totalTime()) / 1000.0f;
Scott Wheeler's avatar
Scott Wheeler committed
522
    int seekTime = int(positionFraction * totalTime + 0.5); // "+0.5" for rounding
523 524 525

    m_statusLabel->setItemCurrentTime(seekTime);
}
526
*/
527

528 529 530 531 532 533 534 535
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

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

536 537 538
    if(!action("pause") ||
       !action("stop") ||
       !action("back") ||
539
       !action("forwardAlbum") ||
540 541
       !action("forward") ||
       !action("trackPositionAction"))
542
    {
Laurent Montel's avatar
Laurent Montel committed
543
        kWarning(65432) << k_funcinfo << "Could not find all of the required actions." << endl;
544 545 546
        return;
    }

547 548 549 550
    if(m_setup)
        return;
    m_setup = true;

551 552 553
    m_output = new Phonon::AudioOutput(Phonon::MusicCategory, this);
    m_audioPath = new Phonon::AudioPath(this);
    m_audioPath->addOutput(m_output);
554

Matthias Kretz's avatar
Matthias Kretz committed
555 556
    m_media = new Phonon::MediaObject(this);
    connect(m_media, SIGNAL(aboutToFinish()), SLOT(slotNeedNextUrl()));
557
    m_media->addAudioPath(m_audioPath);
558
    m_media->setTickInterval(200);
559

560 561
    // initialize action states

562 563 564 565
    action("pause")->setEnabled(false);
    action("stop")->setEnabled(false);
    action("back")->setEnabled(false);
    action("forward")->setEnabled(false);
566
    action("forwardAlbum")->setEnabled(false);
567

568 569
    // setup sliders

570
    m_sliderAction = action<SliderAction>("trackPositionAction");
571

572
    /*
573 574 575 576
    connect(m_sliderAction, SIGNAL(signalPositionChanged(int)),
            this, SLOT(seekPosition(int)));
    connect(m_sliderAction->trackPositionSlider(), SIGNAL(valueChanged(int)),
            this, SLOT(slotUpdateTime(int)));
577
            */
578

579 580
    if(m_sliderAction->trackPositionSlider())
    {
Matthias Kretz's avatar
Matthias Kretz committed
581
        m_sliderAction->trackPositionSlider()->setMediaObject(m_media);
582 583 584 585
    }
    if(m_sliderAction->volumeSlider())
    {
        m_sliderAction->volumeSlider()->setAudioOutput(m_output);
586
    }
587

588 589
    connect(m_media, SIGNAL(length(qint64)), SLOT(slotLength(qint64)));
    connect(m_media, SIGNAL(tick(qint64)), SLOT(slotTick(qint64)));
590
    connect(m_media, SIGNAL(finished()), SLOT(slotFinished()));
591 592
}

Michael Pyne's avatar
Michael Pyne committed
593 594 595 596 597 598 599 600 601 602 603
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)
{
604
    if(randomMode.toLower() == "random")
Michael Pyne's avatar
Michael Pyne committed
605
        action<KToggleAction>("randomPlay")->setChecked(true);
606
    if(randomMode.toLower() == "albumrandom")
Michael Pyne's avatar
Michael Pyne committed
607
        action<KToggleAction>("albumRandomPlay")->setChecked(true);
608
    if(randomMode.toLower() == "norandom")
Michael Pyne's avatar
Michael Pyne committed
609 610 611
        action<KToggleAction>("disableRandomPlay")->setChecked(true);
}

612
#include "playermanager.moc"
613

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