playermanager.cpp 15.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/***************************************************************************
    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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <kdebug.h>
17
#include <klocale.h>
18 19

#include <qslider.h>
20
#include <qtimer.h>
21

22 23
#include <math.h>

24
#include "artsplayer.h"
25
#include "akodeplayer.h"
26
#include "gstreamerplayer.h"
27
#include "playermanager.h"
28
#include "playlistinterface.h"
29
#include "slideraction.h"
30
#include "statuslabel.h"
31
#include "actioncollection.h"
32
#include "collectionlist.h"
33
#include "tag.h"
34

35 36
#include "config.h"

37
using namespace ActionCollection;
38 39 40

PlayerManager *PlayerManager::m_instance = 0;

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

43 44 45 46
////////////////////////////////////////////////////////////////////////////////
// helper functions
////////////////////////////////////////////////////////////////////////////////

47
enum SoundSystem { ArtsBackend = 0, GStreamerBackend = 1, AkodeBackend = 2 };
48 49 50 51 52 53

static Player *createPlayer(int system = ArtsBackend)
{

    Player *p = 0;
    switch(system) {
54 55 56 57
    case AkodeBackend:
        p = new aKodePlayer;
        break;
#if HAVE_ARTS
58 59 60
    case ArtsBackend:
        p = new ArtsPlayer;
        break;
61 62
#endif
#if HAVE_GSTREAMER
63 64 65
    case GStreamerBackend:
        p = new GStreamerPlayer;
        break;
66
#endif
67
    default:
68
#if HAVE_ARTS
69
        p = new ArtsPlayer;
İsmail Dönmez's avatar
 
İsmail Dönmez committed
70
#elif HAVE_GSTREAMER
71
        p = new GStreamerPlayer;
72
#else
İsmail Dönmez's avatar
 
İsmail Dönmez committed
73
        p = new aKodePlayer;
74
#endif
75 76
        break;
    }
77 78 79
    return p;
}

80 81 82 83 84
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////

PlayerManager::PlayerManager() :
Scott Wheeler's avatar
Scott Wheeler committed
85
    Player(),
86
    m_sliderAction(0),
87
    m_playlistInterface(0),
88
    m_statusLabel(0),
89 90
    m_player(0),
    m_timer(0),
91 92
    m_noSeek(false),
    m_muted(false)
93
{
Michael Pyne's avatar
Michael Pyne committed
94 95 96 97 98
// 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();
99 100 101 102
}

PlayerManager::~PlayerManager()
{
103
    delete m_player;
104 105 106 107 108 109 110 111 112 113 114 115 116
}

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

PlayerManager *PlayerManager::instance() // static
{
    if(!m_instance)
        m_instance = new PlayerManager;
    return m_instance;
}

117 118
bool PlayerManager::playing() const
{
119
    if(!player())
120 121
        return false;

122
    return player()->playing();
123 124 125 126
}

bool PlayerManager::paused() const
{
127
    if(!player())
128 129
        return false;

130
    return player()->paused();
131 132 133 134
}

float PlayerManager::volume() const
{
135
    if(!player())
136 137
        return 0;

138
    return player()->volume();
139 140
}

141 142 143
int PlayerManager::status() const
{
    if(!player())
144
        return StatusStopped;
145 146

    if(player()->paused())
147
        return StatusPaused;
148 149

    if(player()->playing())
150
        return StatusPlaying;
151

152 153 154
    return 0;
}

Scott Wheeler's avatar
Scott Wheeler committed
155
int PlayerManager::totalTime() const
156
{
157
    if(!player())
158 159
        return 0;

160
    return player()->totalTime();
161 162
}

Scott Wheeler's avatar
Scott Wheeler committed
163
int PlayerManager::currentTime() const
164
{
165
    if(!player())
166 167
        return 0;

168
    return player()->currentTime();
169 170 171 172
}

int PlayerManager::position() const
{
173
    if(!player())
174 175
        return 0;

176
    return player()->position();
177 178
}

179 180 181 182 183 184 185 186 187 188 189 190 191
QStringList PlayerManager::trackProperties()
{
    return FileHandle::properties();
}

QString PlayerManager::trackProperty(const QString &property) const
{
    if(!playing())
        return QString::null;

    return m_file.property(property);
}

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

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

    return m_file.tag()->artist() + " - " + m_file.tag()->title();
}

205
void PlayerManager::setPlaylistInterface(PlaylistInterface *interface)
206 207 208 209
{
    m_playlistInterface = interface;
}

210 211 212 213 214
void PlayerManager::setStatusLabel(StatusLabel *label)
{
    m_statusLabel = label;
}

215 216 217 218 219
KSelectAction *PlayerManager::playerSelectAction(QObject *parent) // static
{
    KSelectAction *action = 0;
    action = new KSelectAction(i18n("&Output To"), 0, parent, "outputSelect");
    QStringList l;
220 221 222 223 224 225
    l << "aKode"
#if HAVE_ARTS
      << "aRts"
#endif
#if HAVE_GSTREAMER
      << "GStreamer"
226
#endif
227 228
      ;
    action->setItems(l);
229 230 231
    return action;
}

232 233 234 235
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

236
void PlayerManager::play(const FileHandle &file)
237
{
238
    if(!player() || !m_playlistInterface)
239 240
        return;

241 242
    if(file.isNull()) {
        if(player()->paused())
243
            player()->play();
244
        else if(player()->playing()) {
245 246
            if(m_sliderAction->trackPositionSlider())
                m_sliderAction->trackPositionSlider()->setValue(0);
247
            player()->seekPosition(0);
248
        }
249
        else {
250
	    m_playlistInterface->playNext();
251
            m_file = m_playlistInterface->currentFile();
252

253
            if(!m_file.isNull())
254
                player()->play(m_file);
255
        }
256
    }
257
    else {
258
        m_file = file;
259
        player()->play(file);
260
    }
261

262
    // Make sure that the player() actually starts before doing anything.
263

264
    if(!player()->playing()) {
265 266 267
        stop();
        return;
    }
268

269 270 271
    action("pause")->setEnabled(true);
    action("stop")->setEnabled(true);
    action("forward")->setEnabled(true);
272 273
    if(action<KToggleAction>("albumRandomPlay")->isChecked())
        action("forwardAlbum")->setEnabled(true);
274
    action("back")->setEnabled(true);
275

276 277
    if(m_sliderAction->trackPositionSlider())
        m_sliderAction->trackPositionSlider()->setEnabled(true);
278 279

    m_timer->start(m_pollInterval);
280 281

    emit signalPlay();
282 283
}

284 285 286 287 288 289 290 291 292
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
293 294 295 296 297
void PlayerManager::play()
{
    play(FileHandle::null());
}

298 299
void PlayerManager::pause()
{
300
    if(!player())
301 302
        return;

303 304 305 306 307 308
    if(player()->paused()) {
        play();
        return;
    }

    m_timer->stop();
309
    action("pause")->setEnabled(false);
310 311

    player()->pause();
312 313

    emit signalPause();
314 315 316 317
}

void PlayerManager::stop()
{
318
    if(!player() || !m_playlistInterface)
319 320
        return;

321 322
    m_timer->stop();

323 324 325 326
    action("pause")->setEnabled(false);
    action("stop")->setEnabled(false);
    action("back")->setEnabled(false);
    action("forward")->setEnabled(false);
327
    action("forwardAlbum")->setEnabled(false);
328

329 330 331 332
    if(m_sliderAction->trackPositionSlider()) {
        m_sliderAction->trackPositionSlider()->setValue(0);
        m_sliderAction->trackPositionSlider()->setEnabled(false);
    }
333 334

    player()->stop();
335
    m_playlistInterface->stop();
336

337 338
    m_file = FileHandle::null();

339
    emit signalStop();
340 341
}

Laurent Montel's avatar
Laurent Montel committed
342
void PlayerManager::setVolume(float volume)
343
{
344
    if(!player())
345 346
        return;

347
    player()->setVolume(volume);
348 349
}

Scott Wheeler's avatar
Scott Wheeler committed
350
void PlayerManager::seek(int seekTime)
351
{
352
    if(!player())
353
        return;
354

355
    player()->seek(seekTime);
356 357
}

358
void PlayerManager::seekPosition(int position)
359
{
360 361 362 363
    if(!player())
        return;

    if(!player()->playing() || m_noSeek)
364
        return;
365

366 367
    slotUpdateTime(position);
    player()->seekPosition(position);
368 369 370

    if(m_sliderAction->trackPositionSlider())
        m_sliderAction->trackPositionSlider()->setValue(position);
371 372 373 374
}

void PlayerManager::seekForward()
{
375
    seekPosition(kMin(SliderAction::maxPosition, position() + 10));
376 377 378 379
}

void PlayerManager::seekBack()
{
380
    seekPosition(kMax(SliderAction::minPosition, position() - 10));
381 382
}

383 384
void PlayerManager::playPause()
{
385
    playing() ? action("pause")->activate() : action("play")->activate();
386 387
}

388 389
void PlayerManager::forward()
{
390 391
    m_playlistInterface->playNext();
    FileHandle file = m_playlistInterface->currentFile();
392

393 394 395 396 397 398 399 400
    if(!file.isNull())
        play(file);
    else
        stop();
}

void PlayerManager::back()
{
401 402
    m_playlistInterface->playPrevious();
    FileHandle file = m_playlistInterface->currentFile();
403

404 405 406 407 408 409
    if(!file.isNull())
        play(file);
    else
        stop();
}

410 411
void PlayerManager::volumeUp()
{
412
    if(!player() || !m_sliderAction || !m_sliderAction->volumeSlider())
413
        return;
414

415
    int volume = m_sliderAction->volumeSlider()->volume() +
416
        m_sliderAction->volumeSlider()->maxValue() / 25; // 4% up
417 418

    slotSetVolume(volume);
419
    m_sliderAction->volumeSlider()->setVolume(volume);
420 421 422 423
}

void PlayerManager::volumeDown()
{
424
    if(!player() || !m_sliderAction || !m_sliderAction->volumeSlider())
425
        return;
426 427

    int volume = m_sliderAction->volumeSlider()->value() -
428
        m_sliderAction->volumeSlider()->maxValue() / 25; // 4% down
429 430

    slotSetVolume(volume);
431
    m_sliderAction->volumeSlider()->setVolume(volume);
432 433 434 435
}

void PlayerManager::mute()
{
436
    if(!player() || !m_sliderAction || !m_sliderAction->volumeSlider())
437 438 439 440 441 442
        return;

    slotSetVolume(m_muted ? m_sliderAction->volumeSlider()->value() : 0);
    m_muted = !m_muted;
}

443 444 445
////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////
446

447
void PlayerManager::slotPollPlay()
448
{
449
    if(!player() || !m_playlistInterface)
450
        return;
451

452
    m_noSeek = true;
453

454
    if(!player()->playing()) {
455
        m_timer->stop();
456

457 458
        m_playlistInterface->playNext();
        FileHandle nextFile = m_playlistInterface->currentFile();
459
        if(!nextFile.isNull())
460
            play(nextFile);
461 462
        else
            stop();
463 464
    }
    else if(!m_sliderAction->dragging()) {
465 466
        if(m_sliderAction->trackPositionSlider())
            m_sliderAction->trackPositionSlider()->setValue(player()->position());
467

468 469 470 471
        if(m_statusLabel) {
            m_statusLabel->setItemTotalTime(player()->totalTime());
            m_statusLabel->setItemCurrentTime(player()->currentTime());
        }
472
    }
473

474 475 476 477 478 479 480 481
    // This call is done because when the user adds the slider to the toolbar
    // while playback is occuring the volume slider generally defaults to 0,
    // and doesn't get updated to the correct volume.  It might be better to
    // have the SliderAction class fill in the correct volume, but I'm trying
    // to avoid having it depend on PlayerManager since it may not be
    // constructed in time during startup. -mpyne

    if(!m_sliderAction->volumeDragging() && m_sliderAction->volumeSlider())
482 483 484 485 486 487 488 489
    {
        int maxV = m_sliderAction->volumeSlider()->maxValue();
        float v = sqrtf(sqrtf(volume())); // Cancel out exponential scaling

        m_sliderAction->volumeSlider()->blockSignals(true);
        m_sliderAction->volumeSlider()->setVolume((int)((v) * maxV));
        m_sliderAction->volumeSlider()->blockSignals(false);
    }
490

491 492 493 494
    // Ok, this is weird stuff, but it works pretty well.  Ordinarily we don't
    // need to check up on our playing time very often, but in the span of the
    // last interval, we want to check a lot -- to figure out that we've hit the
    // end of the song as soon as possible.
495

496 497 498
    if(player()->playing() &&
       player()->totalTime() > 0 &&
       float(player()->totalTime() - player()->currentTime()) < m_pollInterval * 2)
499 500 501
    {
        m_timer->changeInterval(50);
    }
502

503
    m_noSeek = false;
504 505
}

506 507 508 509
void PlayerManager::slotSetOutput(int system)
{
    stop();
    delete m_player;
510
    m_player = createPlayer(system);
511
    setup();
512 513 514 515
}

void PlayerManager::slotSetVolume(int volume)
{
516 517
    float scaledVolume;

518
    if(m_sliderAction->volumeSlider())
519 520 521 522 523 524 525 526 527 528 529 530
        scaledVolume = float(volume) / m_sliderAction->volumeSlider()->maxValue();
    else {
        scaledVolume = float(volume) / 100.0; // Hopefully this is accurate
        scaledVolume = kMin(1.0f, scaledVolume);
    }

    // Perform exponential scaling to counteract the fact that humans perceive
    // volume changes logarithmically.

    scaledVolume *= scaledVolume;
    scaledVolume *= scaledVolume;
    setVolume(scaledVolume); // scaledVolume ^ 4
531 532 533 534 535 536 537
}

void PlayerManager::slotUpdateTime(int position)
{
    if(!m_statusLabel)
        return;

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

    m_statusLabel->setItemCurrentTime(seekTime);
}

545 546 547 548
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

549 550 551 552 553 554 555 556
Player *PlayerManager::player() const
{
    if(!m_player)
        instance()->setup();

    return m_player;
}

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

561 562 563
    if(!action("pause") ||
       !action("stop") ||
       !action("back") ||
564
       !action("forwardAlbum") ||
565 566
       !action("forward") ||
       !action("trackPositionAction"))
567 568 569 570 571
    {
        kdWarning(65432) << k_funcinfo << "Could not find all of the required actions." << endl;
        return;
    }

572 573
    // initialize action states

574 575 576 577
    action("pause")->setEnabled(false);
    action("stop")->setEnabled(false);
    action("back")->setEnabled(false);
    action("forward")->setEnabled(false);
578
    action("forwardAlbum")->setEnabled(false);
579

580 581
    // setup sliders

582
    m_sliderAction = action<SliderAction>("trackPositionAction");
583

584 585 586 587
    connect(m_sliderAction, SIGNAL(signalPositionChanged(int)),
            this, SLOT(seekPosition(int)));
    connect(m_sliderAction->trackPositionSlider(), SIGNAL(valueChanged(int)),
            this, SLOT(slotUpdateTime(int)));
588
    connect(m_sliderAction, SIGNAL(signalVolumeChanged(int)),
589 590
            this, SLOT(slotSetVolume(int)));

591
    // Call this method manually to avoid warnings.
592

593
    KAction *outputAction = actions()->action("outputSelect");
594 595 596

    if(outputAction) {
        int mediaSystem = static_cast<KSelectAction *>(outputAction)->currentItem();
597 598 599 600
#if (!HAVE_ARTS)
        // Fix problem with position->enum
        if (mediaSystem == ArtsBackend) mediaSystem = GStreamerBackend;
#endif
601
        m_player = createPlayer(mediaSystem);
Scott Wheeler's avatar
Scott Wheeler committed
602
        connect(outputAction, SIGNAL(activated(int)), this, SLOT(slotSetOutput(int)));
603 604
    }
    else
605
        m_player = createPlayer();
606

607 608 609 610 611 612 613 614 615
    float volume;
    
    if(m_sliderAction->volumeSlider()) {
        volume = 
            float(m_sliderAction->volumeSlider()->volume()) /
            float(m_sliderAction->volumeSlider()->maxValue());
    }
    else
        volume = 1; // Assume user wants full volume
616 617 618

    m_player->setVolume(volume);

619 620
    m_timer = new QTimer(this, "play timer");
    connect(m_timer, SIGNAL(timeout()), this, SLOT(slotPollPlay()));
621 622 623
}

#include "playermanager.moc"
624 625

// vim: set et ts=4 sw=4: