playermanager.cpp 13.9 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
#include <phonon/mediaobject.h>
29
#include <phonon/mediaqueue.h>
30 31 32
#include <phonon/audiopath.h>
#include <phonon/audiooutput.h>

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 54
#include "playeradaptor.h"

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
    m_setup(false),
71
    m_ignoreFinished(false),
72 73
    m_output(0),
    m_audioPath(0),
74
    m_mqueue(0),
75
    m_media(0)
76
{
Michael Pyne's avatar
Michael Pyne committed
77 78 79 80 81
// 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
82
    new PlayerAdaptor( this );
83
    QDBusConnection::sessionBus().registerObject("/Player", this);
Laurent Montel's avatar
Laurent Montel committed
84

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

PlayerManager::~PlayerManager()
{
}

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

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

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

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

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

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

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

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

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

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

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

136 137 138
    return 0;
}

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

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

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

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

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

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

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

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

    return m_file.property(property);
}

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

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

    return QPixmap();
}

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

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

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

    return str;
209 210
}

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

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

221 222 223 224
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

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

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

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

243
            if(!m_file.isNull())
244 245 246 247
            {
                m_media->setUrl(KUrl::fromPath(m_file.absFilePath()));
                m_media->play();
            }
248
        }
249
    }
250
    else {
251
        m_file = file;
252 253
        m_media->setUrl(KUrl::fromPath(m_file.absFilePath()));
        m_media->play();
254
    }
255

256
    // Make sure that the player() actually starts before doing anything.
257

258
    if(!playing()) {
Scott Wheeler's avatar
Scott Wheeler committed
259
        kWarning(65432) << "Unable to play " << file.absFilePath() << endl;
260 261 262
        stop();
        return;
    }
263

264 265 266
    action("pause")->setEnabled(true);
    action("stop")->setEnabled(true);
    action("forward")->setEnabled(true);
267 268
    if(action<KToggleAction>("albumRandomPlay")->isChecked())
        action("forwardAlbum")->setEnabled(true);
269
    action("back")->setEnabled(true);
270

271
    emit signalPlay();
272 273
}

274 275 276 277 278 279 280 281 282
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
283 284 285 286 287
void PlayerManager::play()
{
    play(FileHandle::null());
}

288 289
void PlayerManager::pause()
{
290
    if(!m_media)
291 292
        return;

293
    if(paused()) {
294 295 296 297
        play();
        return;
    }

298
    action("pause")->setEnabled(false);
299

300
    m_media->pause();
301 302

    emit signalPause();
303 304 305 306
}

void PlayerManager::stop()
{
307
    if(!m_media || !m_playlistInterface)
308 309
        return;

310 311 312 313
    action("pause")->setEnabled(false);
    action("stop")->setEnabled(false);
    action("back")->setEnabled(false);
    action("forward")->setEnabled(false);
314
    action("forwardAlbum")->setEnabled(false);
315

316
    m_media->stop();
317
    m_playlistInterface->stop();
318

319 320
    m_file = FileHandle::null();

321
    emit signalStop();
322 323
}

Laurent Montel's avatar
Laurent Montel committed
324
void PlayerManager::setVolume(float volume)
325
{
326 327 328
    if(!m_output)
        setup();
    m_output->setVolume(volume);
329 330
}

Scott Wheeler's avatar
Scott Wheeler committed
331
void PlayerManager::seek(int seekTime)
332
{
333
    if(!m_media)
334
        return;
335

336
    m_media->seek(seekTime);
337 338
}

339
/*
340
void PlayerManager::seekPosition(int position)
341
{
342
    if(!m_media)
343 344
        return;

345
    if(!playing() || m_noSeek)
346
        return;
347

348
    slotUpdateTime(position);
349
    m_media->seek(static_cast<qint64>(static_cast<float>(m_media->totalTime() * position) / SliderAction::maxPosition + 0.5f));
350
}
351
*/
352 353 354

void PlayerManager::seekForward()
{
355 356 357
    const qint64 total = m_media->totalTime();
    const qint64 newtime = m_media->currentTime() + total / 100;
    m_media->seek(qMin(total, newtime));
358 359 360 361
}

void PlayerManager::seekBack()
{
362 363 364
    const qint64 total = m_media->totalTime();
    const qint64 newtime = m_media->currentTime() - total / 100;
    m_media->seek(qMax(qint64(0), newtime));
365 366
}

367 368
void PlayerManager::playPause()
{
David Faure's avatar
David Faure committed
369
    playing() ? action("pause")->trigger() : action("play")->trigger();
370 371
}

372 373
void PlayerManager::forward()
{
374 375
    m_playlistInterface->playNext();
    FileHandle file = m_playlistInterface->currentFile();
376

377 378 379 380 381 382 383 384
    if(!file.isNull())
        play(file);
    else
        stop();
}

void PlayerManager::back()
{
385 386
    m_playlistInterface->playPrevious();
    FileHandle file = m_playlistInterface->currentFile();
387

388 389 390 391 392 393
    if(!file.isNull())
        play(file);
    else
        stop();
}

394 395
void PlayerManager::volumeUp()
{
396
    if(!m_output)
397
        return;
398

399 400
    float volume = m_output->volume() + 0.04; // 4% up
    m_output->setVolume(volume);
401 402 403 404
}

void PlayerManager::volumeDown()
{
405
    if(!m_output)
406
        return;
407

408 409
    float volume = m_output->volume() - 0.04; // 4% up
    m_output->setVolume(volume);
410 411 412 413
}

void PlayerManager::mute()
{
414
    if(!m_output)
415 416
        return;

417
    m_output->setMuted(!m_output->isMuted());
418 419
}

420 421 422
////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////
423

424 425 426 427 428 429 430 431 432 433 434 435 436 437
void PlayerManager::slotNeedNextUrl()
{
    m_playlistInterface->playNext();
    FileHandle nextFile = m_playlistInterface->currentFile();
    if(!nextFile.isNull())
    {
        //kDebug() << k_funcinfo << m_file.absFilePath() << endl;
        m_file = nextFile;
        m_mqueue->setNextUrl(KUrl::fromPath(m_file.absFilePath()));
        m_ignoreFinished = true;
    }
    // at this point the new totalTime is not known, but the length signal will tell us
}

438 439
void PlayerManager::slotFinished()
{
440 441 442 443 444 445
    if(m_ignoreFinished)
    {
        //kDebug() << k_funcinfo << "ignoring finished signal" << endl;
        m_ignoreFinished = false;
        return;
    }
446 447 448 449 450 451 452 453 454
    m_playlistInterface->playNext();
    FileHandle nextFile = m_playlistInterface->currentFile();
    if(!nextFile.isNull())
        play(nextFile);
    else
        stop();
    m_statusLabel->setItemTotalTime(totalTime());
}

455 456 457 458 459
void PlayerManager::slotLength(qint64 msec)
{
    m_statusLabel->setItemTotalTime(msec / 1000);
}

460
void PlayerManager::slotTick(qint64 msec)
461
{
462
    if(!m_media || !m_playlistInterface)
463
        return;
464

465
    m_noSeek = true;
466

467 468
    if(m_statusLabel) {
        m_statusLabel->setItemCurrentTime(msec / 1000);
469
    }
470

471
    m_noSeek = false;
472 473
}

474
/*
475 476 477 478 479
void PlayerManager::slotUpdateTime(int position)
{
    if(!m_statusLabel)
        return;

480
    float positionFraction = float(position) / SliderAction::maxPosition;
481
    float totalTime = float(m_media->totalTime()) / 1000.0f;
Scott Wheeler's avatar
Scott Wheeler committed
482
    int seekTime = int(positionFraction * totalTime + 0.5); // "+0.5" for rounding
483 484 485

    m_statusLabel->setItemCurrentTime(seekTime);
}
486
*/
487

488 489 490 491 492 493 494 495
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

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

496 497 498
    if(!action("pause") ||
       !action("stop") ||
       !action("back") ||
499
       !action("forwardAlbum") ||
500 501
       !action("forward") ||
       !action("trackPositionAction"))
502
    {
Laurent Montel's avatar
Laurent Montel committed
503
        kWarning(65432) << k_funcinfo << "Could not find all of the required actions." << endl;
504 505 506
        return;
    }

507 508 509 510
    if(m_setup)
        return;
    m_setup = true;

511 512 513
    m_output = new Phonon::AudioOutput(Phonon::MusicCategory, this);
    m_audioPath = new Phonon::AudioPath(this);
    m_audioPath->addOutput(m_output);
514 515 516 517 518 519 520 521 522 523

    m_mqueue = new Phonon::MediaQueue(this);
    if(m_mqueue->isValid())
        m_media = m_mqueue;
    else
    {
        delete m_mqueue;
        m_mqueue = 0;
        m_media = new Phonon::MediaObject(this);
    }
524
    m_media->addAudioPath(m_audioPath);
525
    m_media->setTickInterval(200);
526

527 528
    // initialize action states

529 530 531 532
    action("pause")->setEnabled(false);
    action("stop")->setEnabled(false);
    action("back")->setEnabled(false);
    action("forward")->setEnabled(false);
533
    action("forwardAlbum")->setEnabled(false);
534

535 536
    // setup sliders

537
    m_sliderAction = action<SliderAction>("trackPositionAction");
538

539
    /*
540 541 542 543
    connect(m_sliderAction, SIGNAL(signalPositionChanged(int)),
            this, SLOT(seekPosition(int)));
    connect(m_sliderAction->trackPositionSlider(), SIGNAL(valueChanged(int)),
            this, SLOT(slotUpdateTime(int)));
544
            */
545

546 547 548 549 550 551 552
    if(m_sliderAction->trackPositionSlider())
    {
        m_sliderAction->trackPositionSlider()->setMediaProducer(m_media);
    }
    if(m_sliderAction->volumeSlider())
    {
        m_sliderAction->volumeSlider()->setAudioOutput(m_output);
553
    }
554

555 556
    connect(m_media, SIGNAL(length(qint64)), SLOT(slotLength(qint64)));
    connect(m_media, SIGNAL(tick(qint64)), SLOT(slotTick(qint64)));
557
    connect(m_media, SIGNAL(finished()), SLOT(slotFinished()));
558
    connect(m_mqueue, SIGNAL(needNextUrl()), SLOT(slotNeedNextUrl()));
559 560
}

Michael Pyne's avatar
Michael Pyne committed
561 562 563 564 565 566 567 568 569 570 571
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)
{
572
    if(randomMode.toLower() == "random")
Michael Pyne's avatar
Michael Pyne committed
573
        action<KToggleAction>("randomPlay")->setChecked(true);
574
    if(randomMode.toLower() == "albumrandom")
Michael Pyne's avatar
Michael Pyne committed
575
        action<KToggleAction>("albumRandomPlay")->setChecked(true);
576
    if(randomMode.toLower() == "norandom")
Michael Pyne's avatar
Michael Pyne committed
577 578 579
        action<KToggleAction>("disableRandomPlay")->setChecked(true);
}

580
#include "playermanager.moc"
581

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