MythTV  master
musicplayer.cpp
Go to the documentation of this file.
1 // ANSI C includes
2 #include <cstdlib>
3 
4 // qt
5 #include <QApplication>
6 #include <QWidget>
7 #include <QFile>
8 #include <QList>
9 #include <QDir>
10 
11 // mythtv
12 #include <mythcontext.h>
13 #include <audiooutput.h>
14 #include <mythdb.h>
15 #include <mythdialogbox.h>
16 #include <mythmainwindow.h>
17 #include <musicutils.h>
18 #include <mthreadpool.h>
19 #include <mythuihelper.h>
20 
21 // mythmusic
22 #include "musicdata.h"
23 #include "musicplayer.h"
24 #include "decoder.h"
25 #include "decoderhandler.h"
26 #include "metaio.h"
27 #ifdef HAVE_CDIO
28 #include "cddecoder.h"
29 #endif
30 #include "constants.h"
31 #include "mainvisual.h"
32 #include "miniplayer.h"
33 #include "playlistcontainer.h"
34 
35 MusicPlayer *gPlayer = nullptr;
36 QString gCDdevice = "";
37 
39 
40 QEvent::Type MusicPlayerEvent::TrackChangeEvent = (QEvent::Type) QEvent::registerEventType();
41 QEvent::Type MusicPlayerEvent::VolumeChangeEvent = (QEvent::Type) QEvent::registerEventType();
42 QEvent::Type MusicPlayerEvent::TrackAddedEvent = (QEvent::Type) QEvent::registerEventType();
43 QEvent::Type MusicPlayerEvent::TrackRemovedEvent = (QEvent::Type) QEvent::registerEventType();
44 QEvent::Type MusicPlayerEvent::TrackUnavailableEvent = (QEvent::Type) QEvent::registerEventType();
45 QEvent::Type MusicPlayerEvent::AllTracksRemovedEvent = (QEvent::Type) QEvent::registerEventType();
46 QEvent::Type MusicPlayerEvent::MetadataChangedEvent = (QEvent::Type) QEvent::registerEventType();
47 QEvent::Type MusicPlayerEvent::TrackStatsChangedEvent = (QEvent::Type) QEvent::registerEventType();
48 QEvent::Type MusicPlayerEvent::AlbumArtChangedEvent = (QEvent::Type) QEvent::registerEventType();
49 QEvent::Type MusicPlayerEvent::CDChangedEvent = (QEvent::Type) QEvent::registerEventType();
50 QEvent::Type MusicPlayerEvent::PlaylistChangedEvent = (QEvent::Type) QEvent::registerEventType();
51 QEvent::Type MusicPlayerEvent::PlayedTracksChangedEvent = (QEvent::Type) QEvent::registerEventType();
52 
53 MusicPlayer::MusicPlayer(QObject *parent)
54  :QObject(parent)
55 {
56  setObjectName("MusicPlayer");
57 
58  QString playmode = gCoreContext->GetSetting("PlayMode", "none");
59  if (playmode.toLower() == "random")
61  else if (playmode.toLower() == "intelligent")
63  else if (playmode.toLower() == "album")
65  else if (playmode.toLower() == "artist")
67  else
69 
70  QString repeatmode = gCoreContext->GetSetting("RepeatMode", "all");
71  if (repeatmode.toLower() == "track")
73  else if (repeatmode.toLower() == "all")
75  else
77 
78  loadSettings();
79 
82  connect(gCoreContext, SIGNAL(TVPlaybackStopped()), this, SLOT(StartPlayback()));
83  connect(gCoreContext, SIGNAL(TVPlaybackAborted()), this, SLOT(StartPlayback()));
84 }
85 
87 {
88  if (!hasClient())
89  savePosition();
90 
93 
94  QMap<QString, int>::Iterator i;
95  for (i = m_notificationMap.begin(); i != m_notificationMap.end(); i++)
96  GetNotificationCenter()->UnRegister(this, (*i));
97 
98  m_notificationMap.clear();
99 
100  stop(true);
101 
102  if (m_decoderHandler)
103  {
105  m_decoderHandler->deleteLater();
106  m_decoderHandler = nullptr;
107  }
108 
109  if (m_oneshotMetadata)
110  {
111  delete m_oneshotMetadata;
112  m_oneshotMetadata = nullptr;
113  }
114 
115  while (!m_playedList.empty())
116  {
117  delete m_playedList.back();
118  m_playedList.pop_back();
119  }
120 
122  gCoreContext->SaveSetting("PlayMode", "intelligent");
123  else if (m_shuffleMode == SHUFFLE_RANDOM)
124  gCoreContext->SaveSetting("PlayMode", "random");
125  else if (m_shuffleMode == SHUFFLE_ALBUM)
126  gCoreContext->SaveSetting("PlayMode", "album");
127  else if (m_shuffleMode == SHUFFLE_ARTIST)
128  gCoreContext->SaveSetting("PlayMode", "artist");
129  else
130  gCoreContext->SaveSetting("PlayMode", "none");
131 
132  if (m_repeatMode == REPEAT_TRACK)
133  gCoreContext->SaveSetting("RepeatMode", "track");
134  else if (m_repeatMode == REPEAT_ALL)
135  gCoreContext->SaveSetting("RepeatMode", "all");
136  else
137  gCoreContext->SaveSetting("RepeatMode", "none");
138 
139  gCoreContext->SaveSetting("MusicAutoShowPlayer",
140  (m_autoShowPlayer ? "1" : "0"));
141 }
142 
143 void MusicPlayer::addListener(QObject *listener)
144 {
145  if (listener && m_output)
146  m_output->addListener(listener);
147 
148  if (listener && getDecoder())
149  getDecoder()->addListener(listener);
150 
151  if (listener && m_decoderHandler)
152  m_decoderHandler->addListener(listener);
153 
154  MythObservable::addListener(listener);
155 
157 }
158 
159 void MusicPlayer::removeListener(QObject *listener)
160 {
161  if (listener && m_output)
162  m_output->removeListener(listener);
163 
164  if (listener && getDecoder())
165  getDecoder()->removeListener(listener);
166 
167  if (listener && m_decoderHandler)
168  m_decoderHandler->removeListener(listener);
169 
171 
173 }
174 
176 {
177  if (visual && !m_visualisers.contains(visual))
178  {
179  if (m_output)
180  {
181  m_output->addListener(visual);
182  m_output->addVisual(visual);
183  }
184 
185  m_visualisers.insert(visual);
186  }
187 }
188 
190 {
191  if (visual)
192  {
193  if (m_output)
194  {
195  m_output->removeListener(visual);
196  m_output->removeVisual(visual);
197  }
198 
199  m_visualisers.remove(visual);
200  }
201 }
202 
204 {
205  if (m_playMode == PLAYMODE_RADIO)
206  return m_resumeModeRadio;
208  return m_resumeModeEditor;
209  return m_resumeModePlayback;
210 }
211 
213 {
217 
218  m_lastplayDelay = gCoreContext->GetNumSetting("MusicLastPlayDelay", LASTPLAY_DELAY);
219  m_autoShowPlayer = (gCoreContext->GetNumSetting("MusicAutoShowPlayer", 1) > 0);
220 }
221 
222 // this stops playing the playlist and plays the file pointed to by mdata
224 {
225  if (m_oneshotMetadata)
226  {
227  delete m_oneshotMetadata;
228  m_oneshotMetadata = nullptr;
229  }
230 
232  *m_oneshotMetadata = mdata;
233 
234  play();
235 }
236 
237 void MusicPlayer::stop(bool stopAll)
238 {
239  stopDecoder();
240 
241  if (m_output)
242  {
243  if (m_output->IsPaused())
244  pause();
245  m_output->Reset();
246  }
247 
248  if (m_oneshotMetadata)
249  {
250  delete m_oneshotMetadata;
251  m_oneshotMetadata = nullptr;
252  }
253 
254  m_isPlaying = false;
255 
256  if (stopAll && getDecoder())
257  {
258  getDecoder()->removeListener(this);
259 
260  // remove any listeners from the decoder
261  {
262  QMutexLocker locker(m_lock);
263  QSet<QObject*>::const_iterator it = m_listeners.begin();
264  for (; it != m_listeners.end() ; ++it)
265  {
266  getDecoder()->removeListener(*it);
267  }
268  }
269  }
270 
271  if (stopAll && m_output)
272  {
273  m_output->removeListener(this);
274  delete m_output;
275  m_output = nullptr;
276  }
277 
278  // because we don't actually stop the audio output we have to fake a Stopped
279  // event so any listeners can act on it
281  dispatch(oe);
282 
284 
286 }
287 
289 {
290  if (m_output)
291  {
294  }
295  // wake up threads
296  if (getDecoder())
297  {
298  getDecoder()->lock();
299  getDecoder()->cond()->wakeAll();
300  getDecoder()->unlock();
301  }
302 
304 }
305 
307 {
308  stopDecoder();
309 
311  if (!meta)
312  return;
313 
314  if (meta->Filename() == METADATA_INVALID_FILENAME)
315  {
316  // put an upper limit on the number of consecutive track unavailable errors
317  if (m_errorCount >= 1000)
318  {
319  ShowOkPopup(tr("Got too many track unavailable errors. Maybe the host with the music on is off-line?"));
320  stop(true);
321  m_errorCount = 0;
322  return;
323  }
324 
325  if (m_errorCount < 5)
326  {
327  MythErrorNotification n(tr("Track Unavailable"), tr("MythMusic"), QString("Cannot find file '%1'").arg(meta->Filename(false)));
329  }
330 
331  m_errorCount++;
332 
333  sendTrackUnavailableEvent(meta->ID());
334  next();
335  return;
336  }
337 
338  // Notify others that we are about to play
340 
341  if (!m_output)
342  {
343  if (!openOutputDevice())
344  return;
345  }
346 
347  if (!getDecoderHandler())
349 
350  getDecoderHandler()->start(meta);
351 
353 }
354 
356 {
357  if (getDecoderHandler())
358  getDecoderHandler()->stop();
359 }
360 
362 {
363  QString adevice, pdevice;
364 
365  adevice = gCoreContext->GetSetting("MusicAudioDevice", "default");
366  if (adevice == "default" || adevice.isEmpty())
367  adevice = gCoreContext->GetSetting("AudioOutputDevice");
368  else
369  adevice = gCoreContext->GetSetting("MusicAudioDevice");
370 
371  pdevice = gCoreContext->GetBoolSetting("PassThruDeviceOverride", false) ?
372  gCoreContext->GetSetting("PassThruOutputDevice") : "auto";
373 
375  adevice, pdevice, FORMAT_S16, 2, AV_CODEC_ID_NONE, 44100,
376  AUDIOOUTPUT_MUSIC, true, false,
377  gCoreContext->GetNumSetting("MusicDefaultUpmix", 0) + 1);
378 
379  if (!m_output)
380  {
381  LOG(VB_GENERAL, LOG_ERR,
382  QString("MusicPlayer: Cannot open audio output device: %1").arg(adevice));
383 
384  return false;
385  }
386 
387  if (!m_output->GetError().isEmpty())
388  {
389  LOG(VB_GENERAL, LOG_ERR,
390  QString("MusicPlayer: Cannot open audio output device: %1").arg(adevice));
391  LOG(VB_GENERAL, LOG_ERR,
392  QString("Error was: %1").arg(m_output->GetError()));
393 
394  delete m_output;
395  m_output = nullptr;
396 
397  return false;
398  }
399 
400  m_output->setBufferSize(256 * 1024);
401 
402  m_output->addListener(this);
403 
404  // add any visuals to the audio output
405  QSet<QObject*>::const_iterator it = m_visualisers.begin();
406 
407  for (; it != m_visualisers.end() ; ++it)
408  {
410  }
411 
412  // add any listeners to the audio output
413  QMutexLocker locker(m_lock);
414  it = m_listeners.begin();
415  for (; it != m_listeners.end() ; ++it)
416  {
417  m_output->addListener(*it);
418  }
419 
420  return true;
421 }
422 
424 {
425  int currentTrack = m_currentTrack;
426 
427  if (!getCurrentPlaylist())
428  return;
429 
430  if (m_oneshotMetadata)
431  {
432  delete m_oneshotMetadata;
433  m_oneshotMetadata = nullptr;
434  }
435  else
436  currentTrack++;
437 
438  if (currentTrack >= getCurrentPlaylist()->getTrackCount())
439  {
440  if (m_repeatMode == REPEAT_ALL)
441  {
442  // start playing again from first track
443  currentTrack = 0;
444  }
445  else
446  {
447  stop();
448  return;
449  }
450  }
451 
452  changeCurrentTrack(currentTrack);
453 
454  if (getCurrentMetadata())
455  play();
456  else
457  stop();
458 }
459 
461 {
462  int currentTrack = m_currentTrack;
463 
464  if (!getCurrentPlaylist())
465  return;
466 
467  if (m_oneshotMetadata)
468  {
469  delete m_oneshotMetadata;
470  m_oneshotMetadata = nullptr;
471  }
472  else
473  currentTrack--;
474 
475  if (currentTrack >= 0)
476  {
477  changeCurrentTrack(currentTrack);
478 
479  if (getCurrentMetadata())
480  play();
481  else
482  return;//stop();
483  }
484  else
485  {
486  // FIXME take repeat mode into account
487  return; //stop();
488  }
489 }
490 
492 {
493  if (!getCurrentPlaylist())
494  return;
495 
496  if (m_oneshotMetadata)
497  {
498  delete m_oneshotMetadata;
499  m_oneshotMetadata = nullptr;
500  stop(true);
501  return;
502  }
503 
504  if (m_repeatMode == REPEAT_TRACK)
505  {
506  play();
507  return;
508  }
509  if (!m_decoderHandler->next())
510  next();
511 
512  // if we don't already have a gui attached show the miniplayer if configured to do so
514  {
515  MythScreenStack *popupStack =
516  GetMythMainWindow()->GetStack("popup stack");
517 
518  MiniPlayer *miniplayer = new MiniPlayer(popupStack);
519 
520  if (miniplayer->Create())
521  popupStack->AddScreen(miniplayer);
522  else
523  delete miniplayer;
524  }
525 }
526 
528 {
530  {
531  play();
532 
533  m_wasPlaying = false;
534  }
535 }
536 
538 {
539  if (m_output)
540  {
542 
543  savePosition();
544  stop(true);
545  }
546 }
547 
548 void MusicPlayer::customEvent(QEvent *event)
549 {
550  // handle decoderHandler events
551  if (event->type() == DecoderHandlerEvent::Ready)
552  {
553  m_errorCount = 0;
555  }
556  else if (event->type() == DecoderHandlerEvent::Meta)
557  {
558  DecoderHandlerEvent *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
559  if (!dhe)
560  return;
561 
562  MusicMetadata *mdata = new MusicMetadata(*dhe->getMetadata());
563 
565 
566  if (getCurrentMetadata())
567  {
568  mdata->setID(getCurrentMetadata()->ID());
570  mdata->setTrack(m_playedList.count() + 1);
571  mdata->setBroadcaster(getCurrentMetadata()->Broadcaster());
573 
574  getCurrentMetadata()->setTitle(mdata->Title());
575  getCurrentMetadata()->setArtist(mdata->Artist());
576  getCurrentMetadata()->setAlbum(mdata->Album());
577  getCurrentMetadata()->setTrack(mdata->Track());
578  }
579 
580  m_playedList.append(mdata);
581 
583  {
584  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
585 
586  MiniPlayer *miniplayer = new MiniPlayer(popupStack);
587 
588  if (miniplayer->Create())
589  popupStack->AddScreen(miniplayer);
590  else
591  delete miniplayer;
592  }
593 
594  // tell any listeners we've added a new track to the played list
596  dispatch(me);
597  }
598  // handle MythEvent events
599  else if (event->type() == MythEvent::MythEventMessage)
600  {
601  MythEvent *me = static_cast<MythEvent*>(event);
602 
603  if (!me)
604  return;
605 
606  if (me->Message().left(13) == "MUSIC_COMMAND")
607  {
608  QStringList list = me->Message().simplified().split(' ');
609 
610  if (list.size() >= 3 && list[1] == gCoreContext->GetHostName())
611  {
612  if (list[2] == "PLAY")
613  play();
614  else if (list[2] == "STOP")
615  stop();
616  else if (list[2] == "PAUSE")
617  pause();
618  else if (list[2] == "SET_VOLUME")
619  {
620  if (list.size() >= 3)
621  {
622  int volume = list[3].toInt();
623  if (volume >= 0 && volume <= 100)
624  setVolume(volume);
625  }
626  }
627  else if (list[2] == "GET_VOLUME")
628  {
629  QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
630  .arg(gCoreContext->GetHostName()).arg(getVolume());
631  MythEvent me2(message);
632  gCoreContext->dispatch(me2);
633  }
634  else if (list[2] == "PLAY_FILE")
635  {
636  int start = me->Message().indexOf("'");
637  int end = me->Message().lastIndexOf("'");
638 
639  if (start != -1 && end != -1 && start != end)
640  {
641  QString filename = me->Message().mid(start + 1, end - start - 1);
642  MusicMetadata mdata;
643  mdata.setFilename(filename);
644  playFile(mdata);
645  }
646  else
647  LOG(VB_GENERAL, LOG_ERR,
648  QString("MusicPlayer: got invalid MUSIC_COMMAND "
649  "PLAY_FILE - %1").arg(me->Message()));
650  }
651  else if (list[2] == "PLAY_URL")
652  {
653  if (list.size() == 4)
654  {
655  QString filename = list[3];
656  MusicMetadata mdata;
657  mdata.setFilename(filename);
658  playFile(mdata);
659  }
660  else
661  LOG(VB_GENERAL, LOG_ERR,
662  QString("MusicPlayer: got invalid MUSIC_COMMAND "
663  "PLAY_URL - %1").arg(me->Message()));
664  }
665  else if (list[2] == "PLAY_TRACK")
666  {
667  if (list.size() == 4)
668  {
669  int trackID = list[3].toInt();
670  MusicMetadata *mdata = gMusicData->m_all_music->getMetadata(trackID);
671  if (mdata)
672  playFile(*mdata);
673  }
674  else
675  LOG(VB_GENERAL, LOG_ERR,
676  QString("MusicPlayer: got invalid MUSIC_COMMAND "
677  "PLAY_TRACK - %1").arg(me->Message()));
678  }
679  else if (list[2] == "GET_METADATA")
680  {
681  QString mdataStr;
683  if (mdata)
684  mdataStr = QString("%1 by %2 from %3").arg(mdata->Title()).arg(mdata->Artist()).arg(mdata->Album());
685  else
686  mdataStr = "Unknown Track2";
687 
688  QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
689  .arg(gCoreContext->GetHostName()).arg(mdataStr);
690  MythEvent me2(message);
691  gCoreContext->dispatch(me2);
692  }
693  else if (list[2] == "GET_STATUS")
694  {
695  QString statusStr = "STOPPED";
696 
697  if (gPlayer->isPlaying())
698  statusStr = "PLAYING";
699  else if (gPlayer->isPaused())
700  statusStr = "PAUSED";
701 
702  QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
703  .arg(gCoreContext->GetHostName()).arg(statusStr);
704  MythEvent me2(message);
705  gCoreContext->dispatch(me2);
706  }
707  }
708  else
709  LOG(VB_GENERAL, LOG_ERR,
710  QString("MusicPlayer: got unknown/invalid MUSIC_COMMAND "
711  "- %1").arg(me->Message()));
712  }
713  else if (me->Message().startsWith("MUSIC_SETTINGS_CHANGED"))
714  {
715  loadSettings();
716  }
717  else if (me->Message().startsWith("MUSIC_METADATA_CHANGED"))
718  {
720  {
721  QStringList list = me->Message().simplified().split(' ');
722  if (list.size() == 2)
723  {
724  int songID = list[1].toInt();
725  MusicMetadata *mdata = gMusicData->m_all_music->getMetadata(songID);
726 
727  if (mdata)
728  {
729  mdata->reloadMetadata();
730 
731  // tell any listeners the metadata has changed for this track
732  sendMetadataChangedEvent(songID);
733  }
734  }
735  }
736  }
737  else if (me->Message().startsWith("MUSIC_SCANNER_STARTED"))
738  {
739  QStringList list = me->Message().simplified().split(' ');
740  if (list.size() == 2)
741  {
742  QString host = list[1];
743  int id = getNotificationID(host);
744  sendNotification(id,
745  tr("A music file scan has started on %1").arg(host),
746  tr("Music File Scanner"),
747  tr("This may take a while I'll give a shout when finished"));
748  }
749  }
750  else if (me->Message().startsWith("MUSIC_SCANNER_FINISHED"))
751  {
752  QStringList list = me->Message().simplified().split(' ');
753  if (list.size() == 6)
754  {
755  QString host = list[1];
756  int id = getNotificationID(host);
757  int totalTracks = list[2].toInt();
758  int newTracks = list[3].toInt();
759  int totalCoverart = list[4].toInt();
760  int newCoverart = list[5].toInt();
761 
762  QString summary = QString("Total Tracks: %2, new tracks: %3,\nTotal Coverart: %4, New CoverArt %5")
763  .arg(totalTracks).arg(newTracks).arg(totalCoverart).arg(newCoverart);
764  sendNotification(id,
765  tr("A music file scan has finished on %1").arg(host),
766  tr("Music File Scanner"), summary);
767 
769  }
770  }
771  else if (me->Message().startsWith("MUSIC_SCANNER_ERROR"))
772  {
773  QStringList list = me->Message().simplified().split(' ');
774  if (list.size() == 3)
775  {
776  QString host = list[1];
777  QString error = list[2];
778  int id = getNotificationID(host);
779 
780  if (error == "Already_Running")
781  sendNotification(id, tr(""),
782  tr("Music File Scanner"),
783  tr("Can't run the music file scanner because it is already running on %1").arg(host));
784  else if (error == "Stalled")
785  sendNotification(id, tr(""),
786  tr("Music File Scanner"),
787  tr("The music file scanner has been running for more than 60 minutes on %1.\nResetting and trying again")
788  .arg(host));
789  }
790  }
791  }
792 
793  if (event->type() == OutputEvent::Error)
794  {
795  OutputEvent *aoe = dynamic_cast<OutputEvent *>(event);
796 
797  if (!aoe)
798  return;
799 
800  LOG(VB_GENERAL, LOG_ERR, QString("Audio Output Error: %1").arg(*aoe->errorMessage()));
801 
802  MythErrorNotification n(tr("Audio Output Error"), tr("MythMusic"), *aoe->errorMessage());
804 
805  m_errorCount++;
806 
807  if (m_errorCount <= 5)
808  nextAuto();
809  else
810  {
811  m_errorCount = 0;
812  stop(true);
813  }
814  }
815  else if (event->type() == DecoderEvent::Error)
816  {
817  DecoderEvent *dxe = dynamic_cast<DecoderEvent *>(event);
818 
819  if (!dxe)
820  return;
821 
822  LOG(VB_GENERAL, LOG_ERR, QString("Decoder Error: %2").arg(*dxe->errorMessage()));
823 
824  MythErrorNotification n(tr("Decoder Error"), tr("MythMusic"), *dxe->errorMessage());
826 
827  m_errorCount++;
828 
829  if (m_playMode != PLAYMODE_RADIO && m_errorCount <= 5)
830  nextAuto();
831  else
832  {
833  m_errorCount = 0;
834  stop(true);
835  }
836  }
837  else if (event->type() == DecoderHandlerEvent::Error)
838  {
839  DecoderHandlerEvent *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
840 
841  if (!dhe)
842  return;
843 
844  LOG(VB_GENERAL, LOG_ERR, QString("Decoder Handler Error - %1").arg(*dhe->getMessage()));
845 
846  MythErrorNotification n(tr("Decoder Handler Error"), tr("MythMusic"), *dhe->getMessage());
848 
849  m_errorCount++;
850 
851  if (m_errorCount <= 5)
852  nextAuto();
853  else
854  {
855  m_errorCount = 0;
856  stop(true);
857  }
858  }
859  else if (event->type() == OutputEvent::Info)
860  {
861  OutputEvent *oe = dynamic_cast<OutputEvent*>(event);
862 
863  if (!oe)
864  return;
865 
866  if (m_playMode != PLAYMODE_RADIO)
868  else
870 
872  {
873  // we update the lastplay and playcount after playing
874  // for m_lastplayDelay seconds or half the total track time
876  (getCurrentMetadata()->Length() / 1000) / 2) ||
878  {
879  updateLastplay();
880  }
881  }
882 
883  // update the current tracks time in the last played list
884  if (m_playMode == PLAYMODE_RADIO)
885  {
886  if (!m_playedList.isEmpty())
887  {
888  m_playedList.last()->setLength(m_currentTime * 1000);
889  // this will update any track lengths displayed on screen
891  }
892  }
893  }
894  else if (event->type() == DecoderEvent::Finished)
895  {
896  if (m_oneshotMetadata)
897  {
898  delete m_oneshotMetadata;
899  m_oneshotMetadata = nullptr;
900  stop(true);
901  }
902  else
903  {
905  m_currentTime != getCurrentMetadata()->Length() / 1000)
906  {
907  LOG(VB_GENERAL, LOG_NOTICE, QString("MusicPlayer: Updating track length was %1s, should be %2s")
908  .arg(getCurrentMetadata()->Length() / 1000).arg(m_currentTime));
909 
912 
913  // this will update any track lengths displayed on screen
915 
916  // this will force the playlist stats to update
918  dispatch(me);
919  }
920 
921  nextAuto();
922  }
923  }
924  else if (event->type() == DecoderEvent::Stopped)
925  {
926  }
927  else if (event->type() == DecoderHandlerEvent::BufferStatus)
928  {
929  DecoderHandlerEvent *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
930  if (!dhe)
931  return;
932 
934  }
935 
936  QObject::customEvent(event);
937 }
938 
939 void MusicPlayer::getBufferStatus(int *bufferAvailable, int *bufferSize)
940 {
941  *bufferAvailable = m_bufferAvailable;
942  *bufferSize = m_bufferSize;
943 }
944 
946 {
947  if (m_playMode == mode)
948  return;
949 
950  savePosition();
951 
952  m_playMode = mode;
953 
954  loadPlaylist();
955 }
956 
958 {
959  if (m_playMode == PLAYMODE_RADIO)
960  {
962  {
963  int bookmark = gCoreContext->GetNumSetting("MusicRadioBookmark", 0);
964  if (bookmark < 0 || bookmark >= getCurrentPlaylist()->getTrackCount())
965  bookmark = 0;
966 
967  m_currentTrack = bookmark;
968  }
969  else
970  m_currentTrack = 0;
971 
973  }
974  else
975  {
977  {
978  int bookmark = gCoreContext->GetNumSetting("MusicBookmark", 0);
979  if (bookmark < 0 || bookmark >= getCurrentPlaylist()->getTrackCount())
980  bookmark = 0;
981 
982  m_currentTrack = bookmark;
983  }
984  else
985  m_currentTrack = 0;
986  }
987 }
988 
990 {
992 
993  // create the radio playlist
997 
998  for (int x = 0; x < list->count(); x++)
999  {
1000  MusicMetadata *mdata = list->at(x);
1001  gMusicData->m_all_playlists->getStreamPlaylist()->addTrack(mdata->ID(), false);
1002 
1003  if (mdata->ID() == id)
1004  m_currentTrack = x;
1005  }
1006 
1008 }
1009 
1010 void MusicPlayer::moveTrackUpDown(bool moveUp, int whichTrack)
1011 {
1012  if (!getCurrentPlaylist())
1013  return;
1014 
1015  if (moveUp && whichTrack <= 0)
1016  return;
1017 
1018  if (!moveUp && whichTrack >= getCurrentPlaylist()->getTrackCount() - 1)
1019  return;
1020 
1022 
1023  getCurrentPlaylist()->moveTrackUpDown(moveUp, whichTrack);
1024 
1026 }
1027 
1029 {
1030  changeCurrentTrack(pos);
1031 
1032  if (!getCurrentMetadata())
1033  {
1034  stop();
1035  return false;
1036  }
1037 
1038  play();
1039 
1040  return true;
1041 }
1042 
1044 {
1045  // can't save a bookmark if we don't know what we are playing
1046  if (!getCurrentMetadata())
1047  return;
1048 
1049  if (m_playMode == PLAYMODE_RADIO)
1050  {
1051  gCoreContext->SaveSetting("MusicRadioBookmark", getCurrentMetadata()->ID());
1052  }
1053  else
1054  {
1055  gCoreContext->SaveSetting("MusicBookmark", getCurrentMetadata()->ID());
1056  gCoreContext->SaveSetting("MusicBookmarkPosition", m_currentTime);
1057  }
1058 }
1059 
1061 {
1062  // if we are switching views we don't wont to restore the position
1063  if (!m_allowRestorePos)
1064  return;
1065 
1066  m_currentTrack = 0;
1067  uint id = -1;
1068 
1070  {
1071  if (m_playMode == PLAYMODE_RADIO)
1072  id = gCoreContext->GetNumSetting("MusicRadioBookmark", 0);
1073  else
1074  id = gCoreContext->GetNumSetting("MusicBookmark", 0);
1075  }
1076 
1077  if (getCurrentPlaylist())
1078  {
1079  for (int x = 0; x < getCurrentPlaylist()->getTrackCount(); x++)
1080  {
1081  if (getCurrentPlaylist()->getSongAt(x) &&
1082  getCurrentPlaylist()->getSongAt(x)->ID() == id)
1083  {
1084  m_currentTrack = x;
1085  break;
1086  }
1087  }
1088  }
1089 
1090  if (getCurrentMetadata())
1091  {
1093  play();
1094 
1096  seek(gCoreContext->GetNumSetting("MusicBookmarkPosition", 0));
1097  }
1098 }
1099 
1100 void MusicPlayer::seek(int pos)
1101 {
1102  if (m_output)
1103  {
1104  if (getDecoder() && getDecoder()->isRunning())
1105  getDecoder()->seek(pos);
1106 
1107  m_output->SetTimecode(pos*1000);
1108  }
1109 }
1110 
1112 {
1113  if (m_canShowPlayer)
1114  {
1115  MythScreenStack *popupStack =
1116  GetMythMainWindow()->GetStack("popup stack");
1117 
1118  MiniPlayer *miniplayer = new MiniPlayer(popupStack);
1119 
1120  if (miniplayer->Create())
1121  popupStack->AddScreen(miniplayer);
1122  else
1123  delete miniplayer;
1124  }
1125 }
1126 
1129 {
1130  if (!getCurrentPlaylist())
1131  return;
1132 
1133  // check to see if we need to save the current tracks volatile metadata (playcount, last played etc)
1135 
1136  m_currentTrack = trackNo;
1137 
1138  // sanity check the current track
1139  if (m_currentTrack < 0 || m_currentTrack >= getCurrentPlaylist()->getTrackCount())
1140  {
1141  LOG(VB_GENERAL, LOG_ERR,
1142  QString("MusicPlayer: asked to set the current track to an invalid track no. %1")
1143  .arg(trackNo));
1144  m_currentTrack = -1;
1145  return;
1146  }
1147 }
1148 
1151 {
1152  if (m_oneshotMetadata)
1153  return m_oneshotMetadata;
1154 
1155  if (!getCurrentPlaylist() || !getCurrentPlaylist()->getSongAt(m_currentTrack))
1156  return nullptr;
1157 
1159 }
1160 
1163 {
1164  if (m_playMode == PLAYMODE_RADIO)
1165  return nullptr;
1166 
1167  if (m_oneshotMetadata)
1168  return getCurrentMetadata();
1169 
1170  if (!getCurrentPlaylist() || !getCurrentPlaylist()->getSongAt(m_currentTrack))
1171  return nullptr;
1172 
1173  if (m_repeatMode == REPEAT_TRACK)
1174  return getCurrentMetadata();
1175 
1176  // if we are not playing the last track then just return the next track
1177  if (m_currentTrack < getCurrentPlaylist()->getTrackCount() - 1)
1179 
1180  // if we are playing the last track then we need to take the
1181  // repeat mode into account
1182  if (m_repeatMode == REPEAT_ALL)
1183  return getCurrentPlaylist()->getSongAt(0);
1184  return nullptr;
1185 }
1186 
1188 {
1189  switch (m_repeatMode)
1190  {
1191  case REPEAT_OFF:
1193  break;
1194  case REPEAT_TRACK:
1196  break;
1197  case REPEAT_ALL:
1199  break;
1200  default:
1202  break;
1203  }
1204 
1205  return m_repeatMode;
1206 }
1207 
1209 {
1210  switch (m_shuffleMode)
1211  {
1212  case SHUFFLE_OFF:
1214  break;
1215  case SHUFFLE_RANDOM:
1217  break;
1218  case SHUFFLE_INTELLIGENT:
1220  break;
1221  case SHUFFLE_ALBUM:
1223  break;
1224  case SHUFFLE_ARTIST:
1226  break;
1227  default:
1229  break;
1230  }
1231 
1233 
1234  return m_shuffleMode;
1235 }
1236 
1238 {
1239  int curTrackID = -1;
1240  if (getCurrentMetadata())
1241  curTrackID = getCurrentMetadata()->ID();
1242 
1243  // only save the mode if we are playing tracks
1244  if (m_playMode != PLAYMODE_RADIO)
1245  m_shuffleMode = mode;
1246 
1247  if (!getCurrentPlaylist())
1248  return;
1249 
1251 
1252  if (curTrackID != -1)
1253  {
1254  for (int x = 0; x < getCurrentPlaylist()->getTrackCount(); x++)
1255  {
1257  if (mdata && mdata->ID() == (MusicMetadata::IdType) curTrackID)
1258  {
1259  m_currentTrack = x;
1260  break;
1261  }
1262  }
1263  }
1264 }
1265 
1267 {
1269  {
1272  }
1273 
1274  m_updatedLastplay = true;
1275 }
1276 
1278 {
1280  {
1281  if (getCurrentMetadata()->hasChanged())
1282  {
1284 
1285  // only write the last played, playcount & rating to the tag if it's enabled by the user
1286  if (GetMythDB()->GetNumSetting("AllowTagWriting", 0) == 1)
1287  {
1288  QStringList strList;
1289  strList << QString("MUSIC_TAG_UPDATE_VOLATILE")
1291  << QString::number(getCurrentMetadata()->ID())
1292  << QString::number(getCurrentMetadata()->Rating())
1293  << QString::number(getCurrentMetadata()->Playcount())
1294  << getCurrentMetadata()->LastPlay().toString(Qt::ISODate);
1295  SendStringListThread *thread = new SendStringListThread(strList);
1296  MThreadPool::globalInstance()->start(thread, "UpdateVolatile");
1297  }
1298 
1300  }
1301  }
1302 }
1303 
1304 void MusicPlayer::setSpeed(float newspeed)
1305 {
1306  if (m_output)
1307  {
1308  m_playSpeed = newspeed;
1310  }
1311 }
1312 
1314 {
1315  m_playSpeed += 0.05F;
1317 }
1318 
1320 {
1321  m_playSpeed -= 0.05F;
1323 }
1324 
1326 {
1328  dispatch(me);
1329 }
1330 
1332 {
1334  dispatch(me);
1335 }
1336 
1338 {
1340  dispatch(me);
1341 }
1342 
1344 {
1346  dispatch(me);
1347 }
1348 
1350 {
1352  dispatch(me);
1353 }
1354 
1356 {
1358  dispatch(me);
1359 }
1360 
1362 {
1363  if (getOutput())
1364  {
1367  }
1368 }
1369 
1371 {
1372  if (getOutput())
1373  {
1376  }
1377 }
1378 
1379 void MusicPlayer::setVolume(int volume)
1380 {
1381  if (getOutput())
1382  {
1383  getOutput()->SetCurrentVolume(volume);
1385  }
1386 }
1387 
1389 {
1390  if (m_output)
1391  return m_output->GetCurrentVolume();
1392  return 0;
1393 }
1394 
1396 {
1397  if (m_output)
1398  {
1399  m_output->ToggleMute();
1401  }
1402 }
1403 
1405 {
1406  if (m_output)
1407  return m_output->GetMuteState();
1408  return kMuteOff;
1409 }
1410 
1412 {
1413  map["volumemute"] = isMuted() ? tr("%1% (Muted)", "Zero Audio Volume").arg(getVolume()) :
1414  QString("%1%").arg(getVolume());
1415  map["volume"] = QString("%1").arg(getVolume());
1416  map["volumepercent"] = QString("%1%").arg(getVolume());
1417  map["mute"] = isMuted() ? tr("Muted") : "";
1418 }
1419 
1420 void MusicPlayer::activePlaylistChanged(int trackID, bool deleted)
1421 {
1422  if (trackID == -1)
1423  {
1424  if (deleted)
1425  {
1427  dispatch(me);
1428  }
1429  else
1430  {
1432  dispatch(me);
1433  }
1434  }
1435  else
1436  {
1437  if (deleted)
1438  {
1440  dispatch(me);
1441  }
1442  else
1443  {
1445  dispatch(me);
1446  }
1447  }
1448 
1449  // if we don't have any tracks to play stop here
1450  if (!getCurrentPlaylist() || getCurrentPlaylist()->getTrackCount() == 0)
1451  {
1452  m_currentTrack = -1;
1453  if (isPlaying())
1454  stop(true);
1455  return;
1456  }
1457 
1458  int trackPos = -1;
1459 
1460  // make sure the current playing track is still valid
1461  if (isPlaying() && getDecoderHandler())
1462  {
1463  for (int x = 0; x < getCurrentPlaylist()->getTrackCount(); x++)
1464  {
1466  {
1467  trackPos = x;
1468  break;
1469  }
1470  }
1471  }
1472 
1473  if (trackPos != m_currentTrack)
1474  m_currentTrack = trackPos;
1475 
1476  if (!getCurrentMetadata())
1477  {
1478  m_currentTrack = -1;
1479  stop(true);
1480  }
1481 }
1482 
1483 void MusicPlayer::playlistChanged(int playlistID)
1484 {
1486  dispatch(me);
1487 }
1488 
1490 {
1493 
1494  // add any listeners to the decoderHandler
1495  {
1496  QMutexLocker locker(m_lock);
1497  QSet<QObject*>::const_iterator it = m_listeners.begin();
1498  for (; it != m_listeners.end() ; ++it)
1499  {
1501  }
1502  }
1503 }
1504 
1506 {
1507  if (!getDecoder())
1508  return;
1509 
1510  LOG(VB_PLAYBACK, LOG_INFO, QString ("decoder handler is ready, decoding %1")
1511  .arg(getDecoder()->getURL()));
1512 
1513 #ifdef HAVE_CDIO
1514  CdDecoder *cddecoder = dynamic_cast<CdDecoder*>(getDecoder());
1515  if (cddecoder)
1516  cddecoder->setDevice(gCDdevice);
1517 #endif
1518 
1519  // Decoder thread can't be running while being initialized
1520  if (getDecoder()->isRunning())
1521  {
1522  getDecoder()->stop();
1523  getDecoder()->wait();
1524  }
1525 
1527  //getDecoder()-> setBlockSize(2 * 1024);
1528  getDecoder()->addListener(this);
1529 
1530  // add any listeners to the decoder
1531  {
1532  QMutexLocker locker(m_lock);
1533  QSet<QObject*>::const_iterator it = m_listeners.begin();
1534  for (; it != m_listeners.end() ; ++it)
1535  {
1536  getDecoder()->addListener(*it);
1537  }
1538  }
1539 
1540  m_currentTime = 0;
1541  m_lastTrackStart = 0;
1542 
1543  QSet<QObject*>::const_iterator it = m_visualisers.begin();
1544  for (; it != m_visualisers.end() ; ++it)
1545  {
1546  //m_output->addVisual((MythTV::Visual*)(*it));
1547  //(*it)->setDecoder(getDecoder());
1548  //m_visual->setOutput(m_output);
1549  }
1550 
1551  if (getDecoder()->initialize())
1552  {
1553  if (m_output)
1554  m_output->Reset();
1555 
1556  getDecoder()->start();
1557 
1559  gCoreContext->GetNumSetting("MusicBookmarkPosition", 0) > 0)
1560  {
1561  seek(gCoreContext->GetNumSetting("MusicBookmarkPosition", 0));
1562  gCoreContext->SaveSetting("MusicBookmarkPosition", 0);
1563  }
1564 
1565  m_isPlaying = true;
1566  m_updatedLastplay = false;
1567  }
1568  else
1569  {
1570  LOG(VB_PLAYBACK, LOG_ERR, QString("Cannot initialise decoder for %1")
1571  .arg(getDecoder()->getURL()));
1572  return;
1573  }
1574 
1575  // tell any listeners we've started playing a new track
1577  dispatch(me);
1578 }
1579 
1580 void MusicPlayer::removeTrack(int trackID)
1581 {
1582  MusicMetadata *mdata = gMusicData->m_all_music->getMetadata(trackID);
1583  if (mdata)
1584  {
1585  int trackPos = getCurrentPlaylist()->getTrackPosition(mdata->ID());
1586  if (m_currentTrack > 0 && m_currentTrack >= trackPos)
1587  m_currentTrack--;
1588 
1589  getCurrentPlaylist()->removeTrack(trackID);
1590  }
1591 }
1592 
1593 void MusicPlayer::addTrack(int trackID, bool updateUI)
1594 {
1595  getCurrentPlaylist()->addTrack(trackID, updateUI);
1596 }
1597 
1599 {
1601  return nullptr;
1602 
1603  if (m_playMode == PLAYMODE_RADIO)
1604  {
1606  }
1608 }
1609 
1611 {
1612  return gMusicData->m_all_streams->getStreams();
1613 }
1614 
1616 {
1617  if (m_notificationMap.find(hostname) == m_notificationMap.end())
1618  m_notificationMap.insert(hostname, GetNotificationCenter()->Register(this));
1619 
1620  return m_notificationMap[hostname];
1621 }
1622 
1623 void MusicPlayer::sendNotification(int notificationID, const QString &title, const QString &author, const QString &desc)
1624 {
1625  QString image = "musicscanner.png";
1626  if (!GetMythUI()->FindThemeFile(image))
1627  LOG(VB_GENERAL, LOG_ERR, "MusicPlayer: sendNotification failed to find the 'musicscanner.png' image");
1628 
1629  DMAP map;
1630  map["asar"] = title;
1631  map["minm"] = author;
1632  map["asal"] = desc;
1633 
1635 
1636  n->SetId(notificationID);
1637  n->SetParent(this);
1638  n->SetDuration(5);
1639  n->SetFullScreen(false);
1641  delete n;
1642 }
StreamList * getStreamList(void)
static Type Info
Definition: output.h:63
virtual bool IsPaused(void) const =0
void decVolume(void)
bool m_isPlaying
Definition: musicplayer.h:223
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
MusicMetadata * getSongAt(int pos) const
Definition: playlist.cpp:1085
int m_errorCount
Definition: musicplayer.h:251
void persist(void)
void SetParent(void *parent)
contains the parent address.
void shuffleTracks(MusicPlayer::ShuffleMode mode)
Definition: playlist.cpp:155
int m_currentTime
Definition: musicplayer.h:213
void setPlayMode(PlayMode mode)
void sendVolumeChangedEvent(void)
virtual uint GetCurrentVolume(void) const
Definition: volumebase.cpp:118
QList< MusicMetadata * > StreamList
void setShuffleMode(ShuffleMode mode)
void changeCurrentTrack(int trackNo)
change the current track to the given track
~MusicPlayer(void)
Definition: musicplayer.cpp:86
MusicPlayer * gPlayer
Definition: musicplayer.cpp:35
uint getVolume(void) const
virtual void SetCurrentVolume(int value)
Definition: volumebase.cpp:123
static Type Finished
Definition: decoder.h:49
void setTrack(int ltrack)
static Type MetadataChangedEvent
Definition: musicplayer.h:46
void sendTrackStatsChangedEvent(int trackID)
void sendCDChangedEvent(void)
MusicMetadata * getCurrentMetadata(void)
get the metadata for the current track in the playlist
static Type Error
Definition: output.h:66
Playlist * getCurrentPlaylist(void)
void playFile(const MusicMetadata &mdata)
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
RepeatMode toggleRepeatMode(void)
float m_playSpeed
Definition: musicplayer.h:239
void removeListener(QObject *listener)
void SetId(int id)
Optional MythNotification elements.
void removeVisual(MainVisual *visual)
static Type TrackAddedEvent
Definition: musicplayer.h:42
void SaveSetting(const QString &key, int newValue)
static Type MythEventMessage
Definition: mythevent.h:66
static void error(const char *str,...)
Definition: vbi.c:42
virtual void SetTimecode(int64_t timecode)=0
void removeListener(QObject *listener)
Remove a listener to the observable.
virtual void unlock(void)
Definition: decoder.h:82
virtual void Reset(void)=0
static AudioOutput * OpenAudio(const QString &main_device, const QString &passthru_device, AudioFormat format, int channels, AVCodecID codec, int samplerate, AudioOutputSource source, bool set_initial_vol, bool passthru, int upmixer_startup=0, AudioOutputSettings *custom=nullptr)
Definition: audiooutput.cpp:61
bool openOutputDevice(void)
QString Hostname(void)
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
ResumeMode m_resumeModeRadio
Definition: musicplayer.h:237
MusicMetadata * getMetadata(void) const
void setLength(int llength)
virtual void ToggleMute(void)
Definition: volumebase.cpp:144
void setBroadcaster(const QString &broadcaster)
bool m_isAutoplay
Definition: musicplayer.h:224
MuteState
Definition: volumebase.h:6
const QString * errorMessage() const
Definition: decoder.h:42
Playlist * getActive(void)
AllStream * m_all_streams
Definition: musicdata.h:55
bool setCurrentTrackPos(int pos)
MusicMetadata & getMetadata()
bool isPlaying(void)
Definition: musicplayer.h:102
void setupDecoderHandler(void)
void UnRegister(void *from, int id, bool closeimemdiately=false)
Unregister the client.
void setVolume(int volume)
MythScreenStack * GetStack(const QString &stackname)
unsigned int uint
Definition: compat.h:140
void incVolume(void)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
void enableSaves(void)
Definition: playlist.h:100
void addVisual(MainVisual *visual)
ResumeMode getResumeMode(void)
void setFilename(const QString &lfilename)
void stopDecoder(void)
Events sent by the DecoderHandler and it's helper classes.
void addListener(QObject *listener)
Add a listener to the observable.
bool m_autoShowPlayer
Definition: musicplayer.h:226
QString gCDdevice
Definition: musicplayer.cpp:36
QMap< QString, int > m_notificationMap
Definition: musicplayer.h:243
void UnregisterForPlayback(QObject *sender)
Unregister sender from being called when TVPlaybackAboutToStart signal is emitted.
bool hasClient(void)
Definition: musicplayer.h:105
static Type CDChangedEvent
Definition: musicplayer.h:49
void setHostname(const QString &host)
virtual void AdjustCurrentVolume(int change)
Definition: volumebase.cpp:132
virtual void lock(void)
Definition: decoder.h:81
static Type TrackChangeEvent
Definition: musicplayer.h:40
void loadStreamPlaylist(void)
PlayMode m_playMode
Definition: musicplayer.h:222
QString Artist() const
void setBufferSize(unsigned int sz)
Definition: output.h:107
void savePosition(void)
void dumpToDatabase(void)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
MuteState getMuteState(void) const
PlaylistContainer * m_all_playlists
Definition: musicdata.h:53
static Type Error
Definition: decoder.h:50
void previous(void)
This class is used as a container for messages.
Definition: mythevent.h:16
static bool isRunning(const char *program)
Returns true if a program containing the specified string is running on this machine.
void removeTrack(MusicMetadata::IdType trackID)
Definition: playlist.cpp:117
void sendMetadataChangedEvent(int trackID)
DecoderHandler * getDecoderHandler(void)
Definition: musicplayer.h:116
void next(void)
MusicMetadata * getMetadata(int an_id)
virtual bool initialize()=0
bool m_allowRestorePos
Definition: musicplayer.h:229
void emitTVPlaybackStopped(void)
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
RepeatMode m_repeatMode
Definition: musicplayer.h:234
virtual void Pause(bool paused)=0
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
int getTrackCount(void)
Definition: playlist.h:79
MusicPlayer(QObject *parent)
Definition: musicplayer.cpp:53
void addTrack(int trackID, bool updateUI)
QString GetSetting(const QString &key, const QString &defaultval="")
QString * getMessage(void) const
bool hasListeners(void)
void setDevice(const QString &dev)
Definition: cddecoder.cpp:107
IdType ID() const
int m_lastTrackStart
Definition: musicplayer.h:247
void SetDuration(int duration)
contains a duration during which the notification will be displayed for.
void setChannel(const QString &channel)
QList< MusicMetadata * > m_playedList
Definition: musicplayer.h:246
void setAlbum(const QString &lalbum, const QString &lalbum_sort=nullptr)
int getNotificationID(const QString &hostname)
void decoderHandlerReady(void)
void getBufferStatus(int *bufferAvailable, int *bufferSize)
bool isMuted(void) const
Definition: musicplayer.h:82
int Track() const
QSet< QObject * > m_listeners
void moveTrackUpDown(bool flag, int where_its_at)
Definition: playlist.cpp:128
static Type Stopped
Definition: decoder.h:48
ResumeMode m_resumeModeEditor
Definition: musicplayer.h:236
void seek(int pos)
static Type TrackRemovedEvent
Definition: musicplayer.h:43
QString GetMasterHostName(void)
bool m_canShowPlayer
Definition: musicplayer.h:225
bool Queue(const MythNotification &notification)
Queue a notification Queue() is thread-safe and can be called from anywhere.
void setSpeed(float speed)
static Type TrackUnavailableEvent
Definition: musicplayer.h:44
bool Create(void) override
Definition: miniplayer.cpp:41
string hostname
Definition: caa.py:17
void disableSaves(void)
whether any changes should be saved to the DB
Definition: playlist.h:99
void pause(void)
void loadSettings(void)
void updateVolatileMetadata(void)
DecoderHandler * m_decoderHandler
Definition: musicplayer.h:218
void setOutput(AudioOutput *)
Definition: decoder.cpp:46
QString Album() const
void showMiniPlayer(void)
virtual void SetStretchFactor(float factor)
ShuffleMode toggleShuffleMode(void)
int m_currentTrack
Definition: musicplayer.h:212
void addVisual(MythTV::Visual *)
Definition: output.cpp:36
StreamList * getStreams(void)
QWaitCondition * cond()
Definition: decoder.h:85
bool m_wasPlaying
Definition: musicplayer.h:227
MythUIHelper * GetMythUI()
void setTitle(const QString &ltitle, const QString &ltitle_sort=nullptr)
virtual void seek(double)=0
void stop(bool stopAll=false)
Playlist * getStreamPlaylist(void)
void addTrack(MusicMetadata::IdType trackID, bool update_display)
Given a tracks ID, add that track to this playlist.
Definition: playlist.cpp:63
void setArtist(const QString &lartist, const QString &lartist_sort=nullptr)
void dispatch(const MythEvent &event)
MythMainWindow * GetMythMainWindow(void)
int m_lastplayDelay
Definition: musicplayer.h:231
AudioOutput * m_output
Definition: musicplayer.h:217
static MThreadPool * globalInstance(void)
void sendAlbumArtChangedEvent(int trackID)
void play(void)
MusicMetadata * m_oneshotMetadata
Definition: musicplayer.h:215
virtual void stop()=0
void PauseIdleTimer(bool pause)
void restorePosition(void)
int GetNumSetting(const QString &key, int defaultval=0)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
const QString * errorMessage() const
Definition: output.h:49
static Type AllTracksRemovedEvent
Definition: musicplayer.h:45
void start(QRunnable *runnable, const QString &debugName, int priority=0)
void addListener(QObject *listener)
bool GetBoolSetting(const QString &key, bool defaultval=false)
bool m_initialized
Definition: musicdata.h:56
QString Title() const
bool isPaused(void)
Definition: musicplayer.h:103
void updateLastplay(void)
void StopPlayback(void)
void nextAuto(void)
void sendTrackUnavailableEvent(int trackID)
void StartPlayback(void)
ShuffleMode m_shuffleMode
Definition: musicplayer.h:233
QString Filename(bool find=true)
static Type BufferStatus
void reloadMetadata(void)
bool m_updatedLastplay
Definition: musicplayer.h:228
static Type PlaylistChangedEvent
Definition: musicplayer.h:50
MusicData * gMusicData
Definition: musicdata.cpp:20
void toMap(InfoMap &infoMap)
int m_bufferSize
Definition: musicplayer.h:249
void removeVisual(MythTV::Visual *)
Definition: output.cpp:43
void getBufferStatus(int *available, int *maxSize) const
Class for starting stream decoding.
int getTrackPosition(MusicMetadata::IdType trackID)
Definition: playlist.h:87
QSet< QObject * > m_visualisers
Definition: musicplayer.h:220
void playlistChanged(int playlistID)
#define LASTPLAY_DELAY
Definition: musicplayer.h:15
#define METADATA_INVALID_FILENAME
Definition: musicmetadata.h:71
const long & elapsedSeconds() const
Definition: output.h:51
static Type PlayedTracksChangedEvent
Definition: musicplayer.h:51
QDateTime LastPlay() const
void SetFullScreen(bool f)
a notification may request to be displayed in full screen, this request may not be fullfilled should ...
void removeTrack(int trackID)
void sendNotification(int notificationID, const QString &title, const QString &author, const QString &desc)
void moveTrackUpDown(bool moveUp, int whichTrack)
void RegisterForPlayback(QObject *sender, const char *method)
Register sender for TVPlaybackAboutToStart signal.
MythDB * GetMythDB(void)
Definition: mythdb.cpp:46
int m_bufferAvailable
Definition: musicplayer.h:248
void toggleMute(void)
Decoder * getDecoder(void)
Definition: musicplayer.h:115
AudioOutput * getOutput(void)
Definition: musicplayer.h:117
void removeAllTracks(void)
Definition: playlist.cpp:87
const QString & Message() const
Definition: mythevent.h:58
static Type VolumeChangeEvent
Definition: musicplayer.h:41
void WantingPlayback(QObject *sender)
All the objects that have registered using MythCoreContext::RegisterForPlayback but sender will be ca...
MusicMetadata * getNextMetadata(void)
get the metadata for the next track in the playlist
QString GetHostName(void)
static Type AlbumArtChangedEvent
Definition: musicplayer.h:48
void customEvent(QEvent *event) override
uint32_t IdType
Definition: musicmetadata.h:84
bool InWantingPlayback(void)
Returns true if a client has requested playback.
Default UTC.
Definition: mythdate.h:14
void activePlaylistChanged(int trackID, bool deleted)
send a message to the master BE without blocking the UI thread
Definition: musicdata.h:20
QString GetError(void) const
Definition: audiooutput.h:143
void loadPlaylist(void)
static Type TrackStatsChangedEvent
Definition: musicplayer.h:47
void reloadMusic(void)
reload music after a scan, rip or import
Definition: musicdata.cpp:57
void setRepeatMode(RepeatMode mode)
Definition: musicplayer.h:181
AllMusic * m_all_music
Definition: musicdata.h:54
virtual MuteState GetMuteState(void) const
Definition: volumebase.cpp:150
QMap< QString, QString > DMAP
void setID(IdType lid)
MythNotificationCenter * GetNotificationCenter(void)
void start(MusicMetadata *mdata)
static Type Stopped
Definition: output.h:65
ResumeMode m_resumeModePlayback
Definition: musicplayer.h:235