MythTV  0.26-pre
musicplayer.cpp
Go to the documentation of this file.
00001 // ANSI C includes
00002 #include <cstdlib>
00003 
00004 // qt
00005 #include <QApplication>
00006 #include <QWidget>
00007 #include <QFile>
00008 #include <QList>
00009 #include <QDir>
00010 
00011 // mythtv
00012 #include <mythcontext.h>
00013 #include <audiooutput.h>
00014 #include <mythdb.h>
00015 #include <mythdialogbox.h>
00016 #include <mythmainwindow.h>
00017 
00018 // mythmusic
00019 #include "musicplayer.h"
00020 #include "decoder.h"
00021 #include "decoderhandler.h"
00022 #ifdef HAVE_CDIO
00023 #include "cddecoder.h"
00024 #endif
00025 #include "constants.h"
00026 #include "mainvisual.h"
00027 #include "miniplayer.h"
00028 #include "playlistcontainer.h"
00029 
00030 // how long to wait before updating the lastplay and playcount fields
00031 #define LASTPLAY_DELAY 15
00032 
00033 MusicPlayer  *gPlayer = NULL;
00034 
00036 
00037 QEvent::Type MusicPlayerEvent::TrackChangeEvent = (QEvent::Type) QEvent::registerEventType();
00038 QEvent::Type MusicPlayerEvent::VolumeChangeEvent = (QEvent::Type) QEvent::registerEventType();
00039 QEvent::Type MusicPlayerEvent::TrackAddedEvent = (QEvent::Type) QEvent::registerEventType();
00040 QEvent::Type MusicPlayerEvent::TrackRemovedEvent = (QEvent::Type) QEvent::registerEventType();
00041 QEvent::Type MusicPlayerEvent::AllTracksRemovedEvent = (QEvent::Type) QEvent::registerEventType();
00042 QEvent::Type MusicPlayerEvent::MetadataChangedEvent = (QEvent::Type) QEvent::registerEventType();
00043 QEvent::Type MusicPlayerEvent::TrackStatsChangedEvent = (QEvent::Type) QEvent::registerEventType();
00044 QEvent::Type MusicPlayerEvent::AlbumArtChangedEvent = (QEvent::Type) QEvent::registerEventType();
00045 QEvent::Type MusicPlayerEvent::CDChangedEvent = (QEvent::Type) QEvent::registerEventType();
00046 QEvent::Type MusicPlayerEvent::PlaylistChangedEvent = (QEvent::Type) QEvent::registerEventType();
00047 
00048 MusicPlayer::MusicPlayer(QObject *parent, const QString &dev)
00049     :QObject(parent)
00050 {
00051     setObjectName("MusicPlayer");
00052 
00053     m_CDdevice = dev;
00054     m_output = NULL;
00055     m_decoderHandler = NULL;
00056     m_cdWatcher = NULL;
00057     m_currentPlaylist = NULL;
00058     m_currentTrack = -1;
00059 
00060     m_currentTime = 0;
00061     m_lastTrackStart = 0;
00062 
00063     m_currentMetadata = NULL;
00064     m_oneshotMetadata = NULL;
00065 
00066     m_isAutoplay = false;
00067     m_isPlaying = false;
00068     m_isStreaming = false;
00069     m_canShowPlayer = true;
00070     m_wasPlaying = true;
00071     m_updatedLastplay = false;
00072     m_allowRestorePos = true;
00073 
00074     m_playSpeed = 1.0;
00075 
00076     QString playmode = gCoreContext->GetSetting("PlayMode", "none");
00077     if (playmode.toLower() == "random")
00078         setShuffleMode(SHUFFLE_RANDOM);
00079     else if (playmode.toLower() == "intelligent")
00080         setShuffleMode(SHUFFLE_INTELLIGENT);
00081     else if (playmode.toLower() == "album")
00082         setShuffleMode(SHUFFLE_ALBUM);
00083     else if (playmode.toLower() == "artist")
00084         setShuffleMode(SHUFFLE_ARTIST);
00085     else
00086         setShuffleMode(SHUFFLE_OFF);
00087 
00088     QString repeatmode = gCoreContext->GetSetting("RepeatMode", "all");
00089     if (repeatmode.toLower() == "track")
00090         setRepeatMode(REPEAT_TRACK);
00091     else if (repeatmode.toLower() == "all")
00092         setRepeatMode(REPEAT_ALL);
00093     else
00094         setRepeatMode(REPEAT_OFF);
00095 
00096     loadSettings();
00097 
00098     gCoreContext->addListener(this);
00099 }
00100 
00101 MusicPlayer::~MusicPlayer()
00102 {
00103     if (m_cdWatcher)
00104     {
00105         m_cdWatcher->stop();
00106         m_cdWatcher->wait();
00107         delete m_cdWatcher;
00108     }
00109 
00110     if (!hasClient())
00111         savePosition();
00112 
00113     gCoreContext->removeListener(this);
00114 
00115     stop(true);
00116 
00117     if (m_decoderHandler)
00118     {
00119         m_decoderHandler->removeListener(this);
00120         m_decoderHandler->deleteLater();
00121         m_decoderHandler = NULL;
00122     }
00123 
00124     if (m_oneshotMetadata)
00125     {
00126         delete m_oneshotMetadata;
00127         m_oneshotMetadata = NULL;
00128     }
00129 
00130     if (m_shuffleMode == SHUFFLE_INTELLIGENT)
00131         gCoreContext->SaveSetting("PlayMode", "intelligent");
00132     else if (m_shuffleMode == SHUFFLE_RANDOM)
00133         gCoreContext->SaveSetting("PlayMode", "random");
00134     else if (m_shuffleMode == SHUFFLE_ALBUM)
00135         gCoreContext->SaveSetting("PlayMode", "album");
00136     else if (m_shuffleMode == SHUFFLE_ARTIST)
00137         gCoreContext->SaveSetting("PlayMode", "artist");
00138     else
00139         gCoreContext->SaveSetting("PlayMode", "none");
00140 
00141     if (m_repeatMode == REPEAT_TRACK)
00142         gCoreContext->SaveSetting("RepeatMode", "track");
00143     else if (m_repeatMode == REPEAT_ALL)
00144         gCoreContext->SaveSetting("RepeatMode", "all");
00145     else
00146         gCoreContext->SaveSetting("RepeatMode", "none");
00147 
00148     gCoreContext->SaveSetting("MusicAutoShowPlayer",
00149                           (m_autoShowPlayer ? "1" : "0"));
00150 }
00151 
00152 void MusicPlayer::addListener(QObject *listener)
00153 {
00154     if (listener && m_output)
00155         m_output->addListener(listener);
00156 
00157     if (listener && getDecoder())
00158         getDecoder()->addListener(listener);
00159 
00160     if (listener && m_decoderHandler)
00161         m_decoderHandler->addListener(listener);
00162 
00163     MythObservable::addListener(listener);
00164 
00165     m_isAutoplay = !hasListeners();
00166 }
00167 
00168 void MusicPlayer::removeListener(QObject *listener)
00169 {
00170     if (listener && m_output)
00171         m_output->removeListener(listener);
00172 
00173     if (listener && getDecoder())
00174         getDecoder()->removeListener(listener);
00175 
00176     if (listener && m_decoderHandler)
00177         m_decoderHandler->removeListener(listener);
00178 
00179     MythObservable::removeListener(listener);
00180 
00181     m_isAutoplay = !hasListeners();
00182 }
00183 
00184 void MusicPlayer::addVisual(MainVisual *visual)
00185 {
00186     if (visual && !m_visualisers.contains(visual))
00187     {
00188         if (m_output)
00189         {
00190             m_output->addListener(visual);
00191             m_output->addVisual(visual);
00192         }
00193 
00194         m_visualisers.insert(visual);
00195     }
00196 }
00197 
00198 void MusicPlayer::removeVisual(MainVisual *visual)
00199 {
00200     if (visual)
00201     {
00202         if (m_output)
00203         {
00204             m_output->removeListener(visual);
00205             m_output->removeVisual(visual);
00206         }
00207 
00208         m_visualisers.remove(visual);
00209     }
00210 }
00211 
00212 void MusicPlayer::loadSettings(void )
00213 {
00214     QString resumestring = gCoreContext->GetSetting("ResumeMode", "off");
00215     if (resumestring.toLower() == "off")
00216         m_resumeMode = RESUME_OFF;
00217     else if (resumestring.toLower() == "track")
00218         m_resumeMode = RESUME_TRACK;
00219     else
00220         m_resumeMode = RESUME_EXACT;
00221 
00222     m_lastplayDelay = gCoreContext->GetNumSetting("MusicLastPlayDelay", LASTPLAY_DELAY);
00223 
00224     m_autoShowPlayer = (gCoreContext->GetNumSetting("MusicAutoShowPlayer", 1) > 0);
00225 
00226     //  Do we check the CD?
00227     bool checkCD = gCoreContext->GetNumSetting("AutoLookupCD");
00228     if (checkCD)
00229     {
00230         m_cdWatcher = new CDWatcherThread(m_CDdevice);
00231         // don't start the cd watcher here
00232         // since the playlists haven't been loaded yet
00233     }
00234 }
00235 
00236 // this stops playing the playlist and plays the file pointed to by mdata
00237 void MusicPlayer::playFile(const Metadata &mdata)
00238 {
00239     if (m_oneshotMetadata)
00240     {
00241         delete m_oneshotMetadata;
00242         m_oneshotMetadata = NULL;
00243     }
00244 
00245     m_oneshotMetadata = new Metadata();
00246     *m_oneshotMetadata = mdata;
00247 
00248     play();
00249 }
00250 
00251 void MusicPlayer::stop(bool stopAll)
00252 {
00253     stopDecoder();
00254 
00255     if (m_output)
00256     {
00257         if (m_output->IsPaused())
00258             pause();
00259         m_output->Reset();
00260     }
00261 
00262     m_isPlaying = false;
00263 
00264     if (stopAll && getDecoder())
00265     {
00266         getDecoder()->removeListener(this);
00267 
00268         // remove any listeners from the decoder
00269         {
00270             QMutexLocker locker(m_lock);
00271             QSet<QObject*>::const_iterator it = m_listeners.begin();
00272             for (; it != m_listeners.end() ; ++it)
00273             {
00274                 getDecoder()->removeListener(*it);
00275             }
00276         }
00277     }
00278 
00279     if (stopAll && m_output)
00280     {
00281         m_output->removeListener(this);
00282         delete m_output;
00283         m_output = NULL;
00284     }
00285 
00286     // because we don't actually stop the audio output we have to fake a Stopped
00287     // event so any listeners can act on it
00288     OutputEvent oe(OutputEvent::Stopped);
00289     dispatch(oe);
00290 
00291     GetMythMainWindow()->PauseIdleTimer(false);
00292 }
00293 
00294 void MusicPlayer::pause(void)
00295 {
00296     if (m_output)
00297     {
00298         m_isPlaying = !m_isPlaying;
00299         m_output->Pause(!m_isPlaying);
00300     }
00301     // wake up threads
00302     if (getDecoder())
00303     {
00304         getDecoder()->lock();
00305         getDecoder()->cond()->wakeAll();
00306         getDecoder()->unlock();
00307     }
00308 
00309     GetMythMainWindow()->PauseIdleTimer(false);
00310 }
00311 
00312 void MusicPlayer::play(void)
00313 {
00314     Metadata *meta = getCurrentMetadata();
00315     if (!meta)
00316         return;
00317 
00318     stopDecoder();
00319 
00320 
00321     if (!m_output)
00322     {
00323         if (!openOutputDevice())
00324             return;
00325     }
00326 
00327     if (!getDecoderHandler())
00328         setupDecoderHandler();
00329 
00330     getDecoderHandler()->start(meta);
00331 
00332     m_isStreaming = (meta->Format() == "cast");
00333 
00334     GetMythMainWindow()->PauseIdleTimer(true);
00335 }
00336 
00337 void MusicPlayer::stopDecoder(void)
00338 {
00339     if (getDecoderHandler())
00340         getDecoderHandler()->stop();
00341 }
00342 
00343 bool MusicPlayer::openOutputDevice(void)
00344 {
00345     QString adevice, pdevice;
00346 
00347     if (gCoreContext->GetSetting("MusicAudioDevice") == "default")
00348         adevice = gCoreContext->GetSetting("AudioOutputDevice");
00349     else
00350         adevice = gCoreContext->GetSetting("MusicAudioDevice");
00351 
00352     pdevice = gCoreContext->GetNumSetting("PassThruDeviceOverride", false) ?
00353               gCoreContext->GetSetting("PassThruOutputDevice") : "auto";
00354 
00355     m_output = AudioOutput::OpenAudio(
00356                    adevice, pdevice, FORMAT_S16, 2, 0, 44100,
00357                    AUDIOOUTPUT_MUSIC, true, false,
00358                    gCoreContext->GetNumSetting("MusicDefaultUpmix", 0) + 1);
00359 
00360     if (!m_output)
00361     {
00362         LOG(VB_GENERAL, LOG_ERR,
00363             QString("MusicPlayer: Cannot open audio output device: %1").arg(adevice));
00364 
00365         return false;
00366     }
00367 
00368     if (!m_output->GetError().isEmpty())
00369     {
00370         LOG(VB_GENERAL, LOG_ERR,
00371             QString("MusicPlayer: Cannot open audio output device: %1").arg(adevice));
00372         LOG(VB_GENERAL, LOG_ERR,
00373             QString("Error was: %1").arg(m_output->GetError()));
00374 
00375         delete m_output;
00376         m_output = NULL;
00377 
00378         return false;
00379     }
00380 
00381     m_output->setBufferSize(256 * 1024);
00382 
00383     m_output->addListener(this);
00384 
00385     // add any visuals to the audio output
00386     QSet<QObject*>::const_iterator it = m_visualisers.begin();
00387 
00388     for (; it != m_visualisers.end() ; ++it)
00389     {
00390         m_output->addVisual((MythTV::Visual*)(*it));
00391     }
00392 
00393     // add any listeners to the audio output
00394     QMutexLocker locker(m_lock);
00395     it = m_listeners.begin();
00396     for (; it != m_listeners.end() ; ++it)
00397     {
00398         m_output->addListener(*it);
00399     }
00400 
00401     return true;
00402 }
00403 
00404 void MusicPlayer::next(void)
00405 {
00406     int currentTrack = m_currentTrack;
00407 
00408     if (!m_currentPlaylist)
00409         return;
00410 
00411     if (m_oneshotMetadata)
00412     {
00413         delete m_oneshotMetadata;
00414         m_oneshotMetadata = NULL;
00415     }
00416     else
00417         currentTrack++;
00418 
00419     if (currentTrack >= m_currentPlaylist->getSongs().size())
00420     {
00421         if (m_repeatMode == REPEAT_ALL)
00422         {
00423             // start playing again from first track
00424             currentTrack = 0;
00425         }
00426         else
00427         {
00428             stop();
00429             return;
00430         }
00431     }
00432 
00433     changeCurrentTrack(currentTrack);
00434 
00435     if (m_currentMetadata)
00436         play();
00437     else
00438         stop();
00439 }
00440 
00441 void MusicPlayer::previous(void)
00442 {
00443     int currentTrack = m_currentTrack;
00444 
00445     if (!m_currentPlaylist)
00446         return;
00447 
00448     if (m_oneshotMetadata)
00449     {
00450         delete m_oneshotMetadata;
00451         m_oneshotMetadata = NULL;
00452     }
00453     else
00454         currentTrack--;
00455 
00456     if (currentTrack >= 0)
00457     {
00458         changeCurrentTrack(currentTrack);
00459 
00460         if (m_currentMetadata)
00461             play();
00462         else
00463             return;//stop();
00464     }
00465     else
00466     {
00467         // FIXME take repeat mode into account
00468         return; //stop();
00469     }
00470 }
00471 
00472 void MusicPlayer::nextAuto(void)
00473 {
00474     if (!m_currentPlaylist)
00475         return;
00476 
00477     if (m_oneshotMetadata)
00478     {
00479         delete m_oneshotMetadata;
00480         m_oneshotMetadata = NULL;
00481         play();
00482         return;
00483     }
00484 
00485     if (m_repeatMode == REPEAT_TRACK)
00486     {
00487         play();
00488         return;
00489     }
00490     else
00491     {
00492         if (!m_decoderHandler->next())
00493             next();
00494     }
00495 
00496     // if we don't already have a gui attached show the miniplayer if configured to do so
00497     if (m_isAutoplay && m_canShowPlayer && m_autoShowPlayer)
00498     {
00499         MythScreenStack *popupStack =
00500                             GetMythMainWindow()->GetStack("popup stack");
00501 
00502         MiniPlayer *miniplayer = new MiniPlayer(popupStack);
00503 
00504         if (miniplayer->Create())
00505             popupStack->AddScreen(miniplayer);
00506         else
00507             delete miniplayer;
00508     }
00509 }
00510 
00511 void MusicPlayer::customEvent(QEvent *event)
00512 {
00513     // handle decoderHandler events
00514     if (event->type() == DecoderHandlerEvent::Ready)
00515     {
00516         decoderHandlerReady();
00517     }
00518     else if (event->type() == DecoderHandlerEvent::OperationStart)
00519     {
00520     }
00521     else if (event->type() == DecoderHandlerEvent::OperationStop)
00522     {
00523     }
00524     else if (event->type() == DecoderHandlerEvent::Info)
00525     {
00526         DecoderHandlerEvent *dxe = dynamic_cast<DecoderHandlerEvent*>(event);
00527         if (!dxe)
00528             return;
00529 
00530         if (getCurrentMetadata())
00531             m_displayMetadata = *getCurrentMetadata();
00532         m_displayMetadata.setArtist("");
00533         m_displayMetadata.setTitle(*dxe->getMessage());
00534     }
00535     else if (event->type() == DecoderHandlerEvent::Error)
00536     {
00537     }
00538     else if (event->type() == DecoderHandlerEvent::Meta)
00539     {
00540         DecoderHandlerEvent *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
00541         if (!dhe)
00542             return;
00543 
00544         Metadata mdata(*dhe->getMetadata());
00545 
00546         if (!m_playedList.isEmpty())
00547             m_playedList.last().setLength((m_currentTime - m_lastTrackStart) * 1000);
00548         m_lastTrackStart = m_currentTime;
00549 
00550         mdata.setTrack(m_playedList.count() + 1);
00551 
00552         m_playedList.append(mdata);
00553         m_currentMetadata = &m_playedList.last();
00554 
00555         if (m_isAutoplay && m_canShowPlayer && m_autoShowPlayer)
00556         {
00557             MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
00558 
00559             MiniPlayer *miniplayer = new MiniPlayer(popupStack);
00560 
00561             if (miniplayer->Create())
00562                 popupStack->AddScreen(miniplayer);
00563             else
00564                 delete miniplayer;
00565         }
00566 
00567         // tell any listeners we've started playing a new track
00568         MusicPlayerEvent me(MusicPlayerEvent::TrackChangeEvent, -1);
00569         dispatch(me);
00570     }
00571 
00572     // handle decoder events
00573     else if (event->type() == DecoderEvent::Decoding)
00574     {
00575         if (getCurrentMetadata())
00576             m_displayMetadata = *getCurrentMetadata();
00577     }
00578 
00579     // handle MythEvent events
00580     else if (event->type() == MythEvent::MythEventMessage)
00581     {
00582         MythEvent *me = dynamic_cast<MythEvent*>(event);
00583 
00584         if (!me)
00585             return;
00586 
00587         if (me->Message().left(14) == "PLAYBACK_START")
00588         {
00589             m_wasPlaying = m_isPlaying;
00590             QString hostname = me->Message().mid(15);
00591 
00592             if (hostname == gCoreContext->GetHostName())
00593             {
00594                 if (m_isPlaying)
00595                     savePosition();
00596                 stop(true);
00597             }
00598         }
00599         else if (me->Message().left(12) == "PLAYBACK_END")
00600         {
00601             if (m_wasPlaying)
00602             {
00603                 QString hostname = me->Message().mid(13);
00604                 if (hostname == gCoreContext->GetHostName())
00605                 {
00606                     play();
00607                     seek(gCoreContext->GetNumSetting(
00608                                 "MusicBookmarkPosition", 0));
00609                     gCoreContext->SaveSetting("MusicBookmark", "");
00610                     gCoreContext->SaveSetting("MusicBookmarkPosition", 0);
00611                 }
00612 
00613                 m_wasPlaying = false;
00614             }
00615         }
00616         else if (me->Message().left(13) == "MUSIC_COMMAND")
00617         {
00618             QStringList list = me->Message().simplified().split(' ');
00619 
00620             if (list.size() >= 3 && list[1] == gCoreContext->GetHostName())
00621             {
00622                 if (list[2] == "PLAY")
00623                     play();
00624                 else if (list[2] == "STOP")
00625                     stop();
00626                 else if (list[2] == "PAUSE")
00627                     pause();
00628                 else if (list[2] == "SET_VOLUME")
00629                 {
00630                     if (list.size() >= 3)
00631                     {
00632                         int volume = list[3].toInt();
00633                         if (volume >= 0 && volume <= 100)
00634                             setVolume(volume);
00635                     }
00636                 }
00637                 else if (list[2] == "GET_VOLUME")
00638                 {
00639                     QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
00640                             .arg(gCoreContext->GetHostName()).arg(getVolume());
00641                     MythEvent me(message);
00642                     gCoreContext->dispatch(me);
00643                 }
00644                 else if (list[2] == "PLAY_FILE")
00645                 {
00646                     int start = me->Message().indexOf("'");
00647                     int end = me->Message().lastIndexOf("'");
00648 
00649                     if (start != -1 && end != -1 && start != end)
00650                     {
00651                         QString filename = me->Message().mid(start + 1, end - start - 1);
00652                         Metadata mdata;
00653                         mdata.setFilename(filename);
00654                         playFile(mdata);
00655                     }
00656                     else
00657                         LOG(VB_GENERAL, LOG_ERR,
00658                             QString("MusicPlayer: got invalid MUSIC_COMMAND "
00659                                     "PLAY_FILE - %1").arg(me->Message()));
00660                 }
00661                 else if (list[2] == "PLAY_URL")
00662                 {
00663                     if (list.size() == 4)
00664                     {
00665                         QString filename = list[3];
00666                         Metadata mdata;
00667                         mdata.setFilename(filename);
00668                         playFile(mdata);
00669                     }
00670                     else
00671                         LOG(VB_GENERAL, LOG_ERR,
00672                             QString("MusicPlayer: got invalid MUSIC_COMMAND "
00673                                     "PLAY_URL - %1").arg(me->Message()));
00674                 }
00675                 else if (list[2] == "PLAY_TRACK")
00676                 {
00677                     if (list.size() == 4)
00678                     {
00679                         int trackID = list[3].toInt();
00680                         Metadata *mdata = gMusicData->all_music->getMetadata(trackID);
00681                         if (mdata)
00682                             playFile(*mdata);
00683                     }
00684                     else
00685                         LOG(VB_GENERAL, LOG_ERR,
00686                              QString("MusicPlayer: got invalid MUSIC_COMMAND "
00687                                      "PLAY_TRACK - %1").arg(me->Message()));
00688                 }
00689                 else if (list[2] == "GET_METADATA")
00690                 {
00691                     QString mdataStr;
00692                     Metadata *mdata = getCurrentMetadata();
00693                     if (mdata)
00694                         mdataStr = QString("%1 by %2 from %3").arg(mdata->Title()).arg(mdata->Artist()).arg(mdata->Album());
00695                     else
00696                         mdataStr = "Unknown Track2";
00697 
00698                     QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
00699                             .arg(gCoreContext->GetHostName()).arg(mdataStr);
00700                     MythEvent me(message);
00701                     gCoreContext->dispatch(me);
00702                 }
00703             }
00704             else
00705                 LOG(VB_GENERAL, LOG_ERR,
00706                     QString("MusicPlayer: got unknown/invalid MUSIC_COMMAND "
00707                             "- %1").arg(me->Message()));
00708         }
00709         else if (me->Message().startsWith("MUSIC_SETTINGS_CHANGED"))
00710         {
00711             QString startdir = gCoreContext->GetSetting("MusicLocation");
00712             startdir = QDir::cleanPath(startdir);
00713             if (!startdir.isEmpty() && !startdir.endsWith("/"))
00714                 startdir += "/";
00715 
00716             gMusicData->musicDir = startdir;
00717 
00718             loadSettings();
00719         }
00720     }
00721 
00722     if (m_isAutoplay)
00723     {
00724         if (event->type() == OutputEvent::Error)
00725         {
00726             OutputEvent *aoe = dynamic_cast<OutputEvent*>(event);
00727 
00728             if (!aoe)
00729                 return;
00730 
00731             LOG(VB_GENERAL, LOG_ERR, QString("Output Error - %1")
00732                     .arg(*aoe->errorMessage()));
00733 
00734             ShowOkPopup(QString("MythMusic has encountered the following error:\n%1")
00735                     .arg(*aoe->errorMessage()));
00736             stop(true);
00737         }
00738         else if (event->type() == DecoderEvent::Error)
00739         {
00740             stop(true);
00741 
00742             QApplication::sendPostedEvents();
00743 
00744             DecoderEvent *dxe = dynamic_cast<DecoderEvent*>(event);
00745 
00746             if (!dxe)
00747                 return;
00748 
00749             LOG(VB_GENERAL, LOG_ERR, QString("Decoder Error - %1")
00750                     .arg(*dxe->errorMessage()));
00751             ShowOkPopup(QString("MythMusic has encountered the following error:\n%1")
00752                     .arg(*dxe->errorMessage()));
00753         }
00754     }
00755 
00756     if (event->type() == OutputEvent::Info)
00757     {
00758         OutputEvent *oe = dynamic_cast<OutputEvent*>(event);
00759 
00760         if (!oe)
00761             return;
00762 
00763         m_currentTime = oe->elapsedSeconds();
00764 
00765         if (!m_updatedLastplay)
00766         {
00767             // we update the lastplay and playcount after playing
00768             // for m_lastplayDelay seconds or half the total track time
00769             if ((m_currentMetadata &&  m_currentTime >
00770                  (m_currentMetadata->Length() / 1000) / 2) ||
00771                  m_currentTime >= m_lastplayDelay)
00772             {
00773                 updateLastplay();
00774             }
00775         }
00776     }
00777     else if (event->type() == DecoderEvent::Finished)
00778     {
00779         if (m_currentMetadata && m_currentTime != m_currentMetadata->Length() / 1000)
00780         {
00781             LOG(VB_GENERAL, LOG_NOTICE, QString("MusicPlayer: Updating track length was %1s, should be %2s")
00782                 .arg(m_currentMetadata->Length() / 1000).arg(m_currentTime));
00783 
00784             m_currentMetadata->setLength(m_currentTime * 1000);
00785             m_currentMetadata->dumpToDatabase();
00786 
00787             // this will update any track lengths displayed on screen
00788             gPlayer->sendMetadataChangedEvent(m_currentMetadata->ID());
00789 
00790             // this will force the playlist stats to update
00791             MusicPlayerEvent me(MusicPlayerEvent::TrackChangeEvent, m_currentTrack);
00792             dispatch(me);
00793         }
00794         nextAuto();
00795     }
00796     else if (event->type() == DecoderEvent::Stopped)
00797     {
00798     }
00799 
00800     QObject::customEvent(event);
00801 }
00802 
00803 void MusicPlayer::switchPlayMode(bool playStreams)
00804 {
00805     savePosition();
00806 
00807     m_isStreaming = playStreams;
00808 
00809     loadPlaylist();
00810 }
00811 
00812 void MusicPlayer::loadPlaylist(void)
00813 {
00814     if (m_isStreaming)
00815     {
00816         m_currentPlaylist  = gMusicData->all_playlists->getStreamPlaylist();
00817 
00818         if (getResumeMode() > MusicPlayer::RESUME_OFF)
00819         {
00820             int bookmark = gCoreContext->GetNumSetting("MusicStreamBookmark", 0);
00821             if (bookmark < 0 || bookmark >= m_currentPlaylist->getSongs().size())
00822                 bookmark = 0;
00823 
00824             m_currentTrack = bookmark;
00825         }
00826         else
00827             m_currentTrack = 0;
00828 
00829         setShuffleMode(SHUFFLE_OFF);
00830     }
00831     else
00832     {
00833         m_currentPlaylist  = gMusicData->all_playlists->getActive();
00834 
00835         if (getResumeMode() > MusicPlayer::RESUME_OFF)
00836         {
00837             int bookmark = gCoreContext->GetNumSetting("MusicBookmark", 0);
00838             if (bookmark < 0 || bookmark >= m_currentPlaylist->getSongs().size())
00839                 bookmark = 0;
00840 
00841             m_currentTrack = bookmark;
00842         }
00843         else
00844             m_currentTrack = 0;
00845     }
00846 
00847     m_currentMetadata = NULL;
00848 
00849     // now we have the playlist loaded we can start the cd watcher
00850     if (m_cdWatcher)
00851         m_cdWatcher->start();
00852 }
00853 
00854 void MusicPlayer::moveTrackUpDown(bool moveUp, int whichTrack)
00855 {
00856     if (moveUp && whichTrack <= 0)
00857         return;
00858 
00859     if (!moveUp && whichTrack >=  m_currentPlaylist->getSongs().size() - 1)
00860         return;
00861 
00862     Metadata *currTrack = m_currentPlaylist->getSongs().at(m_currentTrack);
00863 
00864     m_currentPlaylist->moveTrackUpDown(moveUp, whichTrack);
00865 
00866     m_currentTrack = m_currentPlaylist->getSongs().indexOf(currTrack);
00867 }
00868 
00869 bool MusicPlayer::setCurrentTrackPos(int pos)
00870 {
00871     changeCurrentTrack(pos);
00872 
00873     if (!m_currentMetadata)
00874     {
00875         stop();
00876         return false;
00877     }
00878 
00879     play();
00880 
00881     return true;
00882 }
00883 
00884 void MusicPlayer::savePosition(void)
00885 {
00886     if (m_isStreaming || !m_currentMetadata)
00887     {
00888         // FIXME
00889         gCoreContext->SaveSetting("MusicBookmark", -1);
00890         gCoreContext->SaveSetting("MusicBookmarkPosition", 0);
00891     }
00892     else
00893     {
00894         gCoreContext->SaveSetting("MusicBookmark", m_currentMetadata->ID());
00895         gCoreContext->SaveSetting("MusicBookmarkPosition", m_currentTime);
00896     }
00897 }
00898 
00899 void MusicPlayer::restorePosition(void)
00900 {
00901     // if we are switching views we don't wont to restore the position
00902     if (!m_allowRestorePos)
00903         return;
00904 
00905     m_currentTrack = 0;
00906     uint trackID = 0;
00907 
00908     if (gPlayer->getResumeMode() > MusicPlayer::RESUME_OFF)
00909     {
00910         trackID = gCoreContext->GetNumSetting("MusicBookmark", 0);
00911 
00912         for (int x = 0; x < m_currentPlaylist->getSongs().size(); x++)
00913         {
00914             if (m_currentPlaylist->getSongs().at(x)->ID() == trackID)
00915             {
00916                 m_currentTrack = x;
00917                 break;
00918             }
00919         }
00920     }
00921 
00922     m_currentMetadata = m_currentPlaylist->getSongAt(m_currentTrack);
00923 
00924     if (m_currentMetadata)
00925     {
00926         play();
00927 
00928         if (gPlayer->getResumeMode() == MusicPlayer::RESUME_EXACT)
00929             seek(gCoreContext->GetNumSetting("MusicBookmarkPosition", 0));
00930     }
00931 }
00932 
00933 void MusicPlayer::seek(int pos)
00934 {
00935     if (m_output)
00936     {
00937         if (getDecoder() && getDecoder()->isRunning())
00938             getDecoder()->seek(pos);
00939 
00940         m_output->SetTimecode(pos*1000);
00941     }
00942 }
00943 
00944 void MusicPlayer::showMiniPlayer(void)
00945 {
00946     if (m_canShowPlayer)
00947     {
00948         MythScreenStack *popupStack =
00949                             GetMythMainWindow()->GetStack("popup stack");
00950 
00951         MiniPlayer *miniplayer = new MiniPlayer(popupStack);
00952 
00953         if (miniplayer->Create())
00954             popupStack->AddScreen(miniplayer);
00955         else
00956             delete miniplayer;
00957     }
00958 }
00959 
00961 void MusicPlayer::changeCurrentTrack(int trackNo)
00962 {
00963     if (!m_currentPlaylist)
00964         return;
00965 
00966     // check to see if we need to save the current tracks volatile  metadata (playcount, last played etc)
00967     updateVolatileMetadata();
00968 
00969     m_currentTrack = trackNo;
00970 
00971     // sanity check the current track
00972     if (m_currentTrack < 0 || m_currentTrack >= m_currentPlaylist->getSongs().size())
00973     {
00974         LOG(VB_GENERAL, LOG_ERR,
00975             QString("MusicPlayer: asked to set the current track to an invalid track no. %1")
00976             .arg(trackNo));
00977         m_currentTrack = -1;
00978         m_currentMetadata = NULL;
00979         return;
00980     }
00981 
00982     m_currentMetadata = m_currentPlaylist->getSongAt(m_currentTrack);
00983 }
00984 
00986 Metadata *MusicPlayer::getCurrentMetadata(void)
00987 {
00988     if (m_oneshotMetadata)
00989         return m_oneshotMetadata;
00990 
00991     if (m_currentMetadata)
00992         return m_currentMetadata;
00993 
00994     if (!m_currentPlaylist || !m_currentPlaylist->getSongAt(m_currentTrack))
00995         return NULL;
00996 
00997     m_currentMetadata = m_currentPlaylist->getSongAt(m_currentTrack);
00998 
00999     return m_currentMetadata;
01000 }
01001 
01003 Metadata *MusicPlayer::getNextMetadata(void)
01004 {
01005     if (m_isStreaming)
01006         return NULL;
01007 
01008     if (m_oneshotMetadata)
01009         return m_currentMetadata;
01010 
01011     if (!m_currentPlaylist || !m_currentPlaylist->getSongAt(m_currentTrack))
01012         return NULL;
01013 
01014     if (m_repeatMode == REPEAT_TRACK)
01015         return getCurrentMetadata();
01016 
01017     // if we are not playing the last track then just return the next track
01018     if (m_currentTrack < m_currentPlaylist->getSongs().size() - 1)
01019         return m_currentPlaylist->getSongAt(m_currentTrack + 1);
01020     else
01021     {
01022         // if we are playing the last track then we need to take the
01023         // repeat mode into account
01024         if (m_repeatMode == REPEAT_ALL)
01025             return m_currentPlaylist->getSongAt(0);
01026         else
01027             return NULL;
01028     }
01029 
01030     return NULL;
01031 }
01032 
01033 MusicPlayer::RepeatMode MusicPlayer::toggleRepeatMode(void)
01034 {
01035     switch (m_repeatMode)
01036     {
01037         case REPEAT_OFF:
01038             m_repeatMode = REPEAT_TRACK;
01039             break;
01040         case REPEAT_TRACK:
01041             m_repeatMode = REPEAT_ALL;
01042             break;
01043         case REPEAT_ALL:
01044             m_repeatMode = REPEAT_OFF;
01045            break;
01046         default:
01047             m_repeatMode = REPEAT_OFF;
01048             break;
01049     }
01050 
01051     return m_repeatMode;
01052 }
01053 
01054 MusicPlayer::ShuffleMode MusicPlayer::toggleShuffleMode(void)
01055 {
01056     switch (m_shuffleMode)
01057     {
01058         case SHUFFLE_OFF:
01059             m_shuffleMode = SHUFFLE_RANDOM;
01060             break;
01061         case SHUFFLE_RANDOM:
01062             m_shuffleMode = SHUFFLE_INTELLIGENT;
01063             break;
01064         case SHUFFLE_INTELLIGENT:
01065             m_shuffleMode = SHUFFLE_ALBUM;
01066            break;
01067         case SHUFFLE_ALBUM:
01068             m_shuffleMode = SHUFFLE_ARTIST;
01069            break;
01070         case SHUFFLE_ARTIST:
01071             m_shuffleMode = SHUFFLE_OFF;
01072            break;
01073         default:
01074             m_shuffleMode = SHUFFLE_OFF;
01075             break;
01076     }
01077 
01078     setShuffleMode(m_shuffleMode);
01079 
01080     return m_shuffleMode;
01081 }
01082 
01083 void MusicPlayer::setShuffleMode(ShuffleMode mode)
01084 {
01085     int curTrackID = -1;
01086     if (getCurrentMetadata())
01087         curTrackID = getCurrentMetadata()->ID();
01088 
01089     m_shuffleMode = mode;
01090 
01091     if (m_currentPlaylist)
01092         m_currentPlaylist->shuffleTracks(m_shuffleMode);
01093 
01094     if (curTrackID != -1)
01095     {
01096         for (int x = 0; x < getPlaylist()->getSongs().size(); x++)
01097         {
01098             Metadata *mdata = getPlaylist()->getSongs().at(x);
01099             if (mdata && mdata->ID() == (Metadata::IdType) curTrackID)
01100             {
01101                 m_currentTrack = x;
01102                 break;
01103             }
01104         }
01105     }
01106 }
01107 
01108 void MusicPlayer::updateLastplay()
01109 {
01110     if (!m_isStreaming && m_currentMetadata)
01111     {
01112         m_currentMetadata->incPlayCount();
01113         m_currentMetadata->setLastPlay();
01114     }
01115 
01116     m_updatedLastplay = true;
01117 }
01118 
01119 void MusicPlayer::updateVolatileMetadata(void)
01120 {
01121     if (!m_isStreaming && m_currentMetadata && getDecoder())
01122     {
01123         if (m_currentMetadata->hasChanged())
01124         {
01125             m_currentMetadata->persist();
01126             if (getDecoder())
01127                 getDecoder()->commitVolatileMetadata(m_currentMetadata);
01128 
01129             sendTrackStatsChangedEvent(m_currentMetadata->ID());
01130         }
01131     }
01132 }
01133 
01134 void MusicPlayer::setSpeed(float newspeed)
01135 {
01136     if (m_output)
01137     {
01138         m_playSpeed = newspeed;
01139         m_output->SetStretchFactor(m_playSpeed);
01140     }
01141 }
01142 
01143 void MusicPlayer::incSpeed()
01144 {
01145     m_playSpeed += 0.05;
01146     setSpeed(m_playSpeed);
01147 }
01148 
01149 void MusicPlayer::decSpeed()
01150 {
01151     m_playSpeed -= 0.05;
01152     setSpeed(m_playSpeed);
01153 }
01154 
01155 void MusicPlayer::sendVolumeChangedEvent(void)
01156 {
01157     MusicPlayerEvent me(MusicPlayerEvent::VolumeChangeEvent, getVolume(), isMuted());
01158     dispatch(me);
01159 }
01160 
01161 void MusicPlayer::sendMetadataChangedEvent(int trackID)
01162 {
01163     MusicPlayerEvent me(MusicPlayerEvent::MetadataChangedEvent, trackID);
01164     dispatch(me);
01165 }
01166 
01167 void MusicPlayer::sendTrackStatsChangedEvent(int trackID)
01168 {
01169     MusicPlayerEvent me(MusicPlayerEvent::TrackStatsChangedEvent, trackID);
01170     dispatch(me);
01171 }
01172 
01173 void MusicPlayer::sendAlbumArtChangedEvent(int trackID)
01174 {
01175     MusicPlayerEvent me(MusicPlayerEvent::AlbumArtChangedEvent, trackID);
01176     dispatch(me);
01177 }
01178 
01179 void MusicPlayer::sendCDChangedEvent(void)
01180 {
01181     MusicPlayerEvent me(MusicPlayerEvent::CDChangedEvent, -1);
01182     dispatch(me);
01183 }
01184 
01185 void MusicPlayer::incVolume()
01186 {
01187     if (getOutput())
01188     {
01189         getOutput()->AdjustCurrentVolume(2);
01190         sendVolumeChangedEvent();
01191     }
01192 }
01193 
01194 void MusicPlayer::decVolume()
01195 {
01196     if (getOutput())
01197     {
01198         getOutput()->AdjustCurrentVolume(-2);
01199         sendVolumeChangedEvent();
01200     }
01201 }
01202 
01203 void MusicPlayer::setVolume(int volume)
01204 {
01205     if (getOutput())
01206     {
01207         getOutput()->SetCurrentVolume(volume);
01208         sendVolumeChangedEvent();
01209     }
01210 }
01211 
01212 uint MusicPlayer::getVolume(void) const
01213 {
01214     if (m_output)
01215         return m_output->GetCurrentVolume();
01216     return 0;
01217 }
01218 
01219 void MusicPlayer::toggleMute(void)
01220 {
01221     if (m_output)
01222     {
01223         m_output->ToggleMute();
01224         sendVolumeChangedEvent();
01225     }
01226 }
01227 
01228 MuteState MusicPlayer::getMuteState(void) const
01229 {
01230     if (m_output)
01231         return m_output->GetMuteState();
01232     return kMuteOff;
01233 }
01234 
01235 void MusicPlayer::toMap(QHash<QString, QString> &map)
01236 {
01237     map["volumemute"] = QString("%1%").arg(getVolume()) +
01238                         (isMuted() ? " (" + tr("Muted") + ")" : "");
01239     map["volume"] = QString("%1").arg(getVolume());
01240     map["volumepercent"] = QString("%1%").arg(getVolume());
01241     map["mute"] = isMuted() ? tr("Muted") : "";
01242 }
01243 
01244 void MusicPlayer::activePlaylistChanged(int trackID, bool deleted)
01245 {
01246     if (trackID == -1)
01247     {
01248         if (deleted)
01249         {
01250             // all tracks were removed
01251             m_currentTrack = -1;
01252             m_currentMetadata = NULL;
01253             stop(true);
01254             MusicPlayerEvent me(MusicPlayerEvent::AllTracksRemovedEvent, 0);
01255             dispatch(me);
01256         }
01257         else
01258         {
01259             MusicPlayerEvent me(MusicPlayerEvent::TrackAddedEvent, trackID);
01260             dispatch(me);
01261         }
01262     }
01263     else
01264     {
01265         if (deleted)
01266         {
01267             MusicPlayerEvent me(MusicPlayerEvent::TrackRemovedEvent, trackID);
01268             dispatch(me);
01269         }
01270         else
01271         {
01272             MusicPlayerEvent me(MusicPlayerEvent::TrackAddedEvent, trackID);
01273             dispatch(me);
01274         }
01275     }
01276 }
01277 
01278 void MusicPlayer::playlistChanged(int playlistID)
01279 {
01280     MusicPlayerEvent me(MusicPlayerEvent::PlaylistChangedEvent, playlistID);
01281     dispatch(me);
01282 }
01283 
01284 void MusicPlayer::setupDecoderHandler(void)
01285 {
01286     m_decoderHandler = new DecoderHandler();
01287     m_decoderHandler->addListener(this);
01288 
01289     // add any listeners to the decoderHandler
01290     {
01291         QMutexLocker locker(m_lock);
01292         QSet<QObject*>::const_iterator it = m_listeners.begin();
01293         for (; it != m_listeners.end() ; ++it)
01294         {
01295             m_decoderHandler->addListener(*it);
01296         }
01297     }
01298 }
01299 
01300 void MusicPlayer::decoderHandlerReady(void)
01301 {
01302     LOG(VB_PLAYBACK, LOG_INFO, QString ("decoder handler is ready, decoding %1")
01303             .arg(getDecoder()->getFilename()));
01304 
01305 #ifdef HAVE_CDIO
01306     CdDecoder *cddecoder = dynamic_cast<CdDecoder*>(getDecoder());
01307     if (cddecoder)
01308         cddecoder->setDevice(m_CDdevice);
01309 #endif
01310 
01311     getDecoder()->setOutput(m_output);
01312     //getDecoder()-> setBlockSize(2 * 1024);
01313     getDecoder()->addListener(this);
01314 
01315     // add any listeners to the decoder
01316     {
01317         QMutexLocker locker(m_lock);
01318         QSet<QObject*>::const_iterator it = m_listeners.begin();
01319         for (; it != m_listeners.end() ; ++it)
01320         {
01321             getDecoder()->addListener(*it);
01322         }
01323     }
01324 
01325     m_currentTime = 0;
01326 
01327     QSet<QObject*>::const_iterator it = m_visualisers.begin();
01328     for (; it != m_visualisers.end() ; ++it)
01329     {
01330         //m_output->addVisual((MythTV::Visual*)(*it));
01331         //(*it)->setDecoder(getDecoder());
01332         //m_visual->setOutput(m_output);
01333     }
01334 
01335     if (getDecoder()->initialize())
01336     {
01337         if (m_output)
01338              m_output->Reset();
01339 
01340         getDecoder()->start();
01341 
01342         if (m_resumeMode == RESUME_EXACT &&
01343             gCoreContext->GetNumSetting("MusicBookmarkPosition", 0) > 0)
01344         {
01345             seek(gCoreContext->GetNumSetting("MusicBookmarkPosition", 0));
01346             gCoreContext->SaveSetting("MusicBookmarkPosition", 0);
01347         }
01348 
01349         m_isPlaying = true;
01350         m_updatedLastplay = false;
01351     }
01352     else
01353     {
01354         LOG(VB_PLAYBACK, LOG_ERR, QString("Cannot initialise decoder for %1")
01355                 .arg(getDecoder()->getFilename()));
01356         return;
01357     }
01358 
01359     // tell any listeners we've started playing a new track
01360     MusicPlayerEvent me(MusicPlayerEvent::TrackChangeEvent, m_currentTrack);
01361     dispatch(me);
01362 }
01363 
01364 void MusicPlayer::removeTrack(int trackID)
01365 {
01366     Metadata *mdata = gMusicData->all_music->getMetadata(trackID);
01367     if (mdata)
01368     {
01369         int trackPos = gPlayer->getPlaylist()->getSongs().indexOf(mdata);
01370         if (m_currentTrack > 0 && m_currentTrack >= trackPos)
01371             m_currentTrack--;
01372 
01373         getPlaylist()->removeTrack(trackID);
01374     }
01375 }
01376 
01377 void MusicPlayer::addTrack(int trackID, bool updateUI)
01378 {
01379     getPlaylist()->addTrack(trackID, updateUI);
01380 }
01381 
01383 
01384 CDWatcherThread::CDWatcherThread(const QString &dev)
01385 {
01386     m_cdDevice = dev;
01387     m_cdStatusChanged = false;
01388     m_stopped = false;
01389 }
01390 
01391 void CDWatcherThread::run()
01392 {
01393 #ifdef HAVE_CDIO
01394     while (!m_stopped)
01395     {
01396         // lock all_music and cd_status_changed while running thread
01397         QMutexLocker locker(getLock());
01398 
01399         m_cdStatusChanged = false;
01400 
01401         CdDecoder *decoder = new CdDecoder("cda", NULL, NULL, NULL);
01402         decoder->setDevice(m_cdDevice);
01403         int numTracks = decoder->getNumCDAudioTracks();
01404         bool redo = false;
01405 
01406         if (numTracks != gMusicData->all_music->getCDTrackCount())
01407         {
01408             m_cdStatusChanged = true;
01409             LOG(VB_GENERAL, LOG_NOTICE, QString("CD status has changed."));
01410         }
01411 
01412         if (numTracks == 0)
01413         {
01414             // No CD, or no recognizable CD
01415             gMusicData->all_music->clearCDData();
01416             gMusicData->all_playlists->clearCDList();
01417         }
01418         else if (numTracks > 0)
01419         {
01420             // Check the last track to see if it's changed
01421             Metadata *checker = decoder->getLastMetadata();
01422             if (checker)
01423             {
01424                 if (!gMusicData->all_music->checkCDTrack(checker))
01425                 {
01426                     redo = true;
01427                     m_cdStatusChanged = true;
01428                     gMusicData->all_music->clearCDData();
01429                     gMusicData->all_playlists->clearCDList();
01430                 }
01431                 else
01432                     m_cdStatusChanged = false;
01433                 delete checker;
01434             }
01435             else
01436             {
01437                 LOG(VB_GENERAL, LOG_ERR, "The cddecoder said it had audio tracks, "
01438                                          "but it won't tell me about them");
01439             }
01440         }
01441 
01442         int tracks = decoder->getNumTracks();
01443         bool setTitle = false;
01444 
01445         for (int actual_tracknum = 1;
01446             redo && actual_tracknum <= tracks; actual_tracknum++)
01447         {
01448             Metadata *track = decoder->getMetadata(actual_tracknum);
01449             if (track)
01450             {
01451                 gMusicData->all_music->addCDTrack(*track);
01452 
01453                 if (!setTitle)
01454                 {
01455 
01456                     QString parenttitle = " ";
01457                     if (track->FormatArtist().length() > 0)
01458                     {
01459                         parenttitle += track->FormatArtist();
01460                         parenttitle += " ~ ";
01461                     }
01462 
01463                     if (track->Album().length() > 0)
01464                         parenttitle += track->Album();
01465                     else
01466                     {
01467                         parenttitle = " " + QObject::tr("Unknown");
01468                         LOG(VB_GENERAL, LOG_INFO, "Couldn't find your "
01469                         " CD. It may not be in the freedb database.\n"
01470                         "    More likely, however, is that you need to delete\n"
01471                         "    ~/.cddb and ~/.cdserverrc and restart MythMusic.");
01472                     }
01473                     gMusicData->all_music->setCDTitle(parenttitle);
01474                     setTitle = true;
01475                 }
01476                 delete track;
01477             }
01478         }
01479 
01480         delete decoder;
01481 
01482         if (m_cdStatusChanged)
01483             gPlayer->sendCDChangedEvent();
01484 
01485         usleep(1000000);
01486     }
01487 #endif // HAVE_CDIO
01488 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends