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  // NOLINTNEXTLINE(modernize-loop-convert)
264  for (auto it = m_listeners.begin(); it != m_listeners.end() ; ++it)
265  getDecoder()->removeListener(*it);
266  }
267  }
268 
269  if (stopAll && m_output)
270  {
271  m_output->removeListener(this);
272  delete m_output;
273  m_output = nullptr;
274  }
275 
276  // because we don't actually stop the audio output we have to fake a Stopped
277  // event so any listeners can act on it
279  dispatch(oe);
280 
282 
284 }
285 
287 {
288  if (m_output)
289  {
292  }
293  // wake up threads
294  if (getDecoder())
295  {
296  getDecoder()->lock();
297  getDecoder()->cond()->wakeAll();
298  getDecoder()->unlock();
299  }
300 
302 }
303 
305 {
306  stopDecoder();
307 
309  if (!meta)
310  return;
311 
312  if (meta->Filename() == METADATA_INVALID_FILENAME)
313  {
314  // put an upper limit on the number of consecutive track unavailable errors
315  if (m_errorCount >= 1000)
316  {
317  ShowOkPopup(tr("Got too many track unavailable errors. Maybe the host with the music on is off-line?"));
318  stop(true);
319  m_errorCount = 0;
320  return;
321  }
322 
323  if (m_errorCount < 5)
324  {
325  MythErrorNotification n(tr("Track Unavailable"), tr("MythMusic"), QString("Cannot find file '%1'").arg(meta->Filename(false)));
327  }
328 
329  m_errorCount++;
330 
331  sendTrackUnavailableEvent(meta->ID());
332  next();
333  return;
334  }
335 
336  // Notify others that we are about to play
338 
339  if (!m_output)
340  {
341  if (!openOutputDevice())
342  return;
343  }
344 
345  if (!getDecoderHandler())
347 
348  getDecoderHandler()->start(meta);
349 
351 }
352 
354 {
355  if (getDecoderHandler())
356  getDecoderHandler()->stop();
357 }
358 
360 {
361  QString adevice;
362  QString pdevice;
363 
364  adevice = gCoreContext->GetSetting("MusicAudioDevice", "default");
365  if (adevice == "default" || adevice.isEmpty())
366  adevice = gCoreContext->GetSetting("AudioOutputDevice");
367  else
368  adevice = gCoreContext->GetSetting("MusicAudioDevice");
369 
370  pdevice = gCoreContext->GetBoolSetting("PassThruDeviceOverride", false) ?
371  gCoreContext->GetSetting("PassThruOutputDevice") : "auto";
372 
374  adevice, pdevice, FORMAT_S16, 2, AV_CODEC_ID_NONE, 44100,
375  AUDIOOUTPUT_MUSIC, true, false,
376  gCoreContext->GetNumSetting("MusicDefaultUpmix", 0) + 1);
377 
378  if (!m_output)
379  {
380  LOG(VB_GENERAL, LOG_ERR,
381  QString("MusicPlayer: Cannot open audio output device: %1").arg(adevice));
382 
383  return false;
384  }
385 
386  if (!m_output->GetError().isEmpty())
387  {
388  LOG(VB_GENERAL, LOG_ERR,
389  QString("MusicPlayer: Cannot open audio output device: %1").arg(adevice));
390  LOG(VB_GENERAL, LOG_ERR,
391  QString("Error was: %1").arg(m_output->GetError()));
392 
393  delete m_output;
394  m_output = nullptr;
395 
396  return false;
397  }
398 
399  m_output->setBufferSize(256 * 1024);
400 
401  m_output->addListener(this);
402 
403  // add any visuals to the audio output
404  // NOLINTNEXTLINE(modernize-loop-convert)
405  for (auto it = m_visualisers.begin(); it != m_visualisers.end() ; ++it)
407 
408  // add any listeners to the audio output
409  QMutexLocker locker(m_lock);
410  // NOLINTNEXTLINE(modernize-loop-convert)
411  for (auto it = m_listeners.begin(); it != m_listeners.end() ; ++it)
412  m_output->addListener(*it);
413 
414  return true;
415 }
416 
418 {
419  int currentTrack = m_currentTrack;
420 
421  if (!getCurrentPlaylist())
422  return;
423 
424  if (m_oneshotMetadata)
425  {
426  delete m_oneshotMetadata;
427  m_oneshotMetadata = nullptr;
428  }
429  else
430  currentTrack++;
431 
432  if (currentTrack >= getCurrentPlaylist()->getTrackCount())
433  {
434  if (m_repeatMode == REPEAT_ALL)
435  {
436  // start playing again from first track
437  currentTrack = 0;
438  }
439  else
440  {
441  stop();
442  return;
443  }
444  }
445 
446  changeCurrentTrack(currentTrack);
447 
448  if (getCurrentMetadata())
449  play();
450  else
451  stop();
452 }
453 
455 {
456  int currentTrack = m_currentTrack;
457 
458  if (!getCurrentPlaylist())
459  return;
460 
461  if (m_oneshotMetadata)
462  {
463  delete m_oneshotMetadata;
464  m_oneshotMetadata = nullptr;
465  }
466  else
467  currentTrack--;
468 
469  if (currentTrack >= 0)
470  {
471  changeCurrentTrack(currentTrack);
472 
473  if (getCurrentMetadata())
474  play();
475  else
476  return;//stop();
477  }
478  else
479  {
480  // FIXME take repeat mode into account
481  return; //stop();
482  }
483 }
484 
486 {
487  if (!getCurrentPlaylist())
488  return;
489 
490  if (m_oneshotMetadata)
491  {
492  delete m_oneshotMetadata;
493  m_oneshotMetadata = nullptr;
494  stop(true);
495  return;
496  }
497 
498  if (m_repeatMode == REPEAT_TRACK)
499  {
500  play();
501  return;
502  }
503  if (!m_decoderHandler->next())
504  next();
505 
506  // if we don't already have a gui attached show the miniplayer if configured to do so
508  {
509  MythScreenStack *popupStack =
510  GetMythMainWindow()->GetStack("popup stack");
511 
512  auto *miniplayer = new MiniPlayer(popupStack);
513 
514  if (miniplayer->Create())
515  popupStack->AddScreen(miniplayer);
516  else
517  delete miniplayer;
518  }
519 }
520 
522 {
524  {
525  play();
526 
527  m_wasPlaying = false;
528  }
529 }
530 
532 {
533  if (m_output)
534  {
536 
537  savePosition();
538  stop(true);
539  }
540 }
541 
542 void MusicPlayer::customEvent(QEvent *event)
543 {
544  // handle decoderHandler events
545  if (event->type() == DecoderHandlerEvent::Ready)
546  {
547  m_errorCount = 0;
549  }
550  else if (event->type() == DecoderHandlerEvent::Meta)
551  {
552  auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
553  if (!dhe)
554  return;
555 
556  auto *mdata = new MusicMetadata(*dhe->getMetadata());
557 
559 
560  if (getCurrentMetadata())
561  {
562  mdata->setID(getCurrentMetadata()->ID());
563  mdata->setHostname(gCoreContext->GetMasterHostName());
564  mdata->setTrack(m_playedList.count() + 1);
565  mdata->setBroadcaster(getCurrentMetadata()->Broadcaster());
566  mdata->setChannel(getCurrentMetadata()->Channel());
567 
568  getCurrentMetadata()->setTitle(mdata->Title());
569  getCurrentMetadata()->setArtist(mdata->Artist());
570  getCurrentMetadata()->setAlbum(mdata->Album());
571  getCurrentMetadata()->setTrack(mdata->Track());
572  }
573 
574  m_playedList.append(mdata);
575 
577  {
578  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
579 
580  auto *miniplayer = new MiniPlayer(popupStack);
581 
582  if (miniplayer->Create())
583  popupStack->AddScreen(miniplayer);
584  else
585  delete miniplayer;
586  }
587 
588  // tell any listeners we've added a new track to the played list
590  dispatch(me);
591  }
592  // handle MythEvent events
593  else if (event->type() == MythEvent::MythEventMessage)
594  {
595  auto *me = dynamic_cast<MythEvent*>(event);
596  if (!me)
597  return;
598 
599  if (me->Message().left(13) == "MUSIC_COMMAND")
600  {
601  QStringList list = me->Message().simplified().split(' ');
602 
603  if (list.size() >= 3 && list[1] == gCoreContext->GetHostName())
604  {
605  if (list[2] == "PLAY")
606  play();
607  else if (list[2] == "STOP")
608  stop();
609  else if (list[2] == "PAUSE")
610  pause();
611  else if (list[2] == "SET_VOLUME")
612  {
613  if (list.size() >= 3)
614  {
615  int volume = list[3].toInt();
616  if (volume >= 0 && volume <= 100)
617  setVolume(volume);
618  }
619  }
620  else if (list[2] == "GET_VOLUME")
621  {
622  QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
623  .arg(gCoreContext->GetHostName()).arg(getVolume());
624  MythEvent me2(message);
625  gCoreContext->dispatch(me2);
626  }
627  else if (list[2] == "PLAY_FILE")
628  {
629  int start = me->Message().indexOf("'");
630  int end = me->Message().lastIndexOf("'");
631 
632  if (start != -1 && end != -1 && start != end)
633  {
634  QString filename = me->Message().mid(start + 1, end - start - 1);
635  MusicMetadata mdata;
636  mdata.setFilename(filename);
637  playFile(mdata);
638  }
639  else
640  {
641  LOG(VB_GENERAL, LOG_ERR,
642  QString("MusicPlayer: got invalid MUSIC_COMMAND "
643  "PLAY_FILE - %1").arg(me->Message()));
644  }
645  }
646  else if (list[2] == "PLAY_URL")
647  {
648  if (list.size() == 4)
649  {
650  QString filename = list[3];
651  MusicMetadata mdata;
652  mdata.setFilename(filename);
653  playFile(mdata);
654  }
655  else
656  {
657  LOG(VB_GENERAL, LOG_ERR,
658  QString("MusicPlayer: got invalid MUSIC_COMMAND "
659  "PLAY_URL - %1").arg(me->Message()));
660  }
661  }
662  else if (list[2] == "PLAY_TRACK")
663  {
664  if (list.size() == 4)
665  {
666  int trackID = list[3].toInt();
667  MusicMetadata *mdata = gMusicData->m_all_music->getMetadata(trackID);
668  if (mdata)
669  playFile(*mdata);
670  }
671  else
672  {
673  LOG(VB_GENERAL, LOG_ERR,
674  QString("MusicPlayer: got invalid MUSIC_COMMAND "
675  "PLAY_TRACK - %1").arg(me->Message()));
676  }
677  }
678  else if (list[2] == "GET_METADATA")
679  {
680  QString mdataStr;
682  if (mdata)
683  mdataStr = QString("%1 by %2 from %3").arg(mdata->Title()).arg(mdata->Artist()).arg(mdata->Album());
684  else
685  mdataStr = "Unknown Track2";
686 
687  QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
688  .arg(gCoreContext->GetHostName()).arg(mdataStr);
689  MythEvent me2(message);
690  gCoreContext->dispatch(me2);
691  }
692  else if (list[2] == "GET_STATUS")
693  {
694  QString statusStr = "STOPPED";
695 
696  if (gPlayer->isPlaying())
697  statusStr = "PLAYING";
698  else if (gPlayer->isPaused())
699  statusStr = "PAUSED";
700 
701  QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
702  .arg(gCoreContext->GetHostName()).arg(statusStr);
703  MythEvent me2(message);
704  gCoreContext->dispatch(me2);
705  }
706  }
707  else
708  {
709  LOG(VB_GENERAL, LOG_ERR,
710  QString("MusicPlayer: got unknown/invalid MUSIC_COMMAND "
711  "- %1").arg(me->Message()));
712  }
713  }
714  else if (me->Message().startsWith("MUSIC_SETTINGS_CHANGED"))
715  {
716  loadSettings();
717  }
718  else if (me->Message().startsWith("MUSIC_METADATA_CHANGED"))
719  {
721  {
722  QStringList list = me->Message().simplified().split(' ');
723  if (list.size() == 2)
724  {
725  int songID = list[1].toInt();
726  MusicMetadata *mdata = gMusicData->m_all_music->getMetadata(songID);
727 
728  if (mdata)
729  {
730  mdata->reloadMetadata();
731 
732  // tell any listeners the metadata has changed for this track
733  sendMetadataChangedEvent(songID);
734  }
735  }
736  }
737  }
738  else if (me->Message().startsWith("MUSIC_SCANNER_STARTED"))
739  {
740  QStringList list = me->Message().simplified().split(' ');
741  if (list.size() == 2)
742  {
743  QString host = list[1];
744  int id = getNotificationID(host);
745  sendNotification(id,
746  tr("A music file scan has started on %1").arg(host),
747  tr("Music File Scanner"),
748  tr("This may take a while I'll give a shout when finished"));
749  }
750  }
751  else if (me->Message().startsWith("MUSIC_SCANNER_FINISHED"))
752  {
753  QStringList list = me->Message().simplified().split(' ');
754  if (list.size() == 6)
755  {
756  QString host = list[1];
757  int id = getNotificationID(host);
758  int totalTracks = list[2].toInt();
759  int newTracks = list[3].toInt();
760  int totalCoverart = list[4].toInt();
761  int newCoverart = list[5].toInt();
762 
763  QString summary = QString("Total Tracks: %2, new tracks: %3,\nTotal Coverart: %4, New CoverArt %5")
764  .arg(totalTracks).arg(newTracks).arg(totalCoverart).arg(newCoverart);
765  sendNotification(id,
766  tr("A music file scan has finished on %1").arg(host),
767  tr("Music File Scanner"), summary);
768 
770  }
771  }
772  else if (me->Message().startsWith("MUSIC_SCANNER_ERROR"))
773  {
774  QStringList list = me->Message().simplified().split(' ');
775  if (list.size() == 3)
776  {
777  QString host = list[1];
778  QString error = list[2];
779  int id = getNotificationID(host);
780 
781  if (error == "Already_Running")
782  {
783  sendNotification(id, tr(""),
784  tr("Music File Scanner"),
785  tr("Can't run the music file scanner because it is already running on %1").arg(host));
786  }
787  else if (error == "Stalled")
788  {
789  sendNotification(id, tr(""),
790  tr("Music File Scanner"),
791  tr("The music file scanner has been running for more than 60 minutes on %1.\nResetting and trying again")
792  .arg(host));
793  }
794  }
795  }
796  }
797 
798  if (event->type() == OutputEvent::Error)
799  {
800  auto *aoe = dynamic_cast<OutputEvent *>(event);
801 
802  if (!aoe)
803  return;
804 
805  LOG(VB_GENERAL, LOG_ERR, QString("Audio Output Error: %1").arg(*aoe->errorMessage()));
806 
807  MythErrorNotification n(tr("Audio Output Error"), tr("MythMusic"), *aoe->errorMessage());
809 
810  m_errorCount++;
811 
812  if (m_errorCount <= 5)
813  nextAuto();
814  else
815  {
816  m_errorCount = 0;
817  stop(true);
818  }
819  }
820  else if (event->type() == DecoderEvent::Error)
821  {
822  auto *dxe = dynamic_cast<DecoderEvent *>(event);
823 
824  if (!dxe)
825  return;
826 
827  LOG(VB_GENERAL, LOG_ERR, QString("Decoder Error: %2").arg(*dxe->errorMessage()));
828 
829  MythErrorNotification n(tr("Decoder Error"), tr("MythMusic"), *dxe->errorMessage());
831 
832  m_errorCount++;
833 
834  if (m_playMode != PLAYMODE_RADIO && m_errorCount <= 5)
835  nextAuto();
836  else
837  {
838  m_errorCount = 0;
839  stop(true);
840  }
841  }
842  else if (event->type() == DecoderHandlerEvent::Error)
843  {
844  auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
845 
846  if (!dhe)
847  return;
848 
849  LOG(VB_GENERAL, LOG_ERR, QString("Decoder Handler Error - %1").arg(*dhe->getMessage()));
850 
851  MythErrorNotification n(tr("Decoder Handler Error"), tr("MythMusic"), *dhe->getMessage());
853 
854  m_errorCount++;
855 
856  if (m_errorCount <= 5)
857  nextAuto();
858  else
859  {
860  m_errorCount = 0;
861  stop(true);
862  }
863  }
864  else if (event->type() == OutputEvent::Info)
865  {
866  auto *oe = dynamic_cast<OutputEvent*>(event);
867 
868  if (!oe)
869  return;
870 
871  if (m_playMode != PLAYMODE_RADIO)
872  m_currentTime = oe->elapsedSeconds();
873  else
874  m_currentTime = oe->elapsedSeconds() - m_lastTrackStart;
875 
877  {
878  // we update the lastplay and playcount after playing
879  // for m_lastplayDelay seconds or half the total track time
881  (getCurrentMetadata()->Length() / 1000) / 2) ||
883  {
884  updateLastplay();
885  }
886  }
887 
888  // update the current tracks time in the last played list
889  if (m_playMode == PLAYMODE_RADIO)
890  {
891  if (!m_playedList.isEmpty())
892  {
893  m_playedList.last()->setLength(m_currentTime * 1000);
894  // this will update any track lengths displayed on screen
896  }
897  }
898  }
899  else if (event->type() == DecoderEvent::Finished)
900  {
901  if (m_oneshotMetadata)
902  {
903  delete m_oneshotMetadata;
904  m_oneshotMetadata = nullptr;
905  stop(true);
906  }
907  else
908  {
910  m_currentTime != getCurrentMetadata()->Length() / 1000)
911  {
912  LOG(VB_GENERAL, LOG_NOTICE, QString("MusicPlayer: Updating track length was %1s, should be %2s")
913  .arg(getCurrentMetadata()->Length() / 1000).arg(m_currentTime));
914 
917 
918  // this will update any track lengths displayed on screen
920 
921  // this will force the playlist stats to update
923  dispatch(me);
924  }
925 
926  nextAuto();
927  }
928  }
929  else if (event->type() == DecoderEvent::Stopped)
930  {
931  }
932  else if (event->type() == DecoderHandlerEvent::BufferStatus)
933  {
934  auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
935  if (!dhe)
936  return;
937 
938  dhe->getBufferStatus(&m_bufferAvailable, &m_bufferSize);
939  }
940 
941  QObject::customEvent(event);
942 }
943 
944 void MusicPlayer::getBufferStatus(int *bufferAvailable, int *bufferSize)
945 {
946  *bufferAvailable = m_bufferAvailable;
947  *bufferSize = m_bufferSize;
948 }
949 
951 {
952  if (m_playMode == mode)
953  return;
954 
955  savePosition();
956 
957  m_playMode = mode;
958 
959  loadPlaylist();
960 }
961 
963 {
964  if (m_playMode == PLAYMODE_RADIO)
965  {
967  {
968  int bookmark = gCoreContext->GetNumSetting("MusicRadioBookmark", 0);
969  if (bookmark < 0 || bookmark >= getCurrentPlaylist()->getTrackCount())
970  bookmark = 0;
971 
972  m_currentTrack = bookmark;
973  }
974  else
975  m_currentTrack = 0;
976 
978  }
979  else
980  {
982  {
983  int bookmark = gCoreContext->GetNumSetting("MusicBookmark", 0);
984  if (bookmark < 0 || bookmark >= getCurrentPlaylist()->getTrackCount())
985  bookmark = 0;
986 
987  m_currentTrack = bookmark;
988  }
989  else
990  m_currentTrack = 0;
991  }
992 }
993 
995 {
997 
998  // create the radio playlist
1002 
1003  for (int x = 0; x < list->count(); x++)
1004  {
1005  MusicMetadata *mdata = list->at(x);
1006  gMusicData->m_all_playlists->getStreamPlaylist()->addTrack(mdata->ID(), false);
1007 
1008  if (mdata->ID() == id)
1009  m_currentTrack = x;
1010  }
1011 
1013 }
1014 
1015 void MusicPlayer::moveTrackUpDown(bool moveUp, int whichTrack)
1016 {
1017  if (!getCurrentPlaylist())
1018  return;
1019 
1020  if (moveUp && whichTrack <= 0)
1021  return;
1022 
1023  if (!moveUp && whichTrack >= getCurrentPlaylist()->getTrackCount() - 1)
1024  return;
1025 
1027 
1028  getCurrentPlaylist()->moveTrackUpDown(moveUp, whichTrack);
1029 
1031 }
1032 
1034 {
1035  changeCurrentTrack(pos);
1036 
1037  if (!getCurrentMetadata())
1038  {
1039  stop();
1040  return false;
1041  }
1042 
1043  play();
1044 
1045  return true;
1046 }
1047 
1049 {
1050  // can't save a bookmark if we don't know what we are playing
1051  if (!getCurrentMetadata())
1052  return;
1053 
1054  if (m_playMode == PLAYMODE_RADIO)
1055  {
1056  gCoreContext->SaveSetting("MusicRadioBookmark", getCurrentMetadata()->ID());
1057  }
1058  else
1059  {
1060  gCoreContext->SaveSetting("MusicBookmark", getCurrentMetadata()->ID());
1061  gCoreContext->SaveSetting("MusicBookmarkPosition", m_currentTime);
1062  }
1063 }
1064 
1066 {
1067  // if we are switching views we don't wont to restore the position
1068  if (!m_allowRestorePos)
1069  return;
1070 
1071  m_currentTrack = 0;
1072  uint id = -1;
1073 
1075  {
1076  if (m_playMode == PLAYMODE_RADIO)
1077  id = gCoreContext->GetNumSetting("MusicRadioBookmark", 0);
1078  else
1079  id = gCoreContext->GetNumSetting("MusicBookmark", 0);
1080  }
1081 
1082  if (getCurrentPlaylist())
1083  {
1084  for (int x = 0; x < getCurrentPlaylist()->getTrackCount(); x++)
1085  {
1086  if (getCurrentPlaylist()->getSongAt(x) &&
1087  getCurrentPlaylist()->getSongAt(x)->ID() == id)
1088  {
1089  m_currentTrack = x;
1090  break;
1091  }
1092  }
1093  }
1094 
1095  if (getCurrentMetadata())
1096  {
1098  play();
1099 
1101  seek(gCoreContext->GetNumSetting("MusicBookmarkPosition", 0));
1102  }
1103 }
1104 
1105 void MusicPlayer::seek(int pos)
1106 {
1107  if (m_output)
1108  {
1109  if (getDecoder() && getDecoder()->isRunning())
1110  getDecoder()->seek(pos);
1111 
1112  m_output->SetTimecode(pos*1000);
1113  }
1114 }
1115 
1117 {
1118  if (m_canShowPlayer)
1119  {
1120  MythScreenStack *popupStack =
1121  GetMythMainWindow()->GetStack("popup stack");
1122 
1123  auto *miniplayer = new MiniPlayer(popupStack);
1124 
1125  if (miniplayer->Create())
1126  popupStack->AddScreen(miniplayer);
1127  else
1128  delete miniplayer;
1129  }
1130 }
1131 
1134 {
1135  if (!getCurrentPlaylist())
1136  return;
1137 
1138  // check to see if we need to save the current tracks volatile metadata (playcount, last played etc)
1140 
1141  m_currentTrack = trackNo;
1142 
1143  // sanity check the current track
1144  if (m_currentTrack < 0 || m_currentTrack >= getCurrentPlaylist()->getTrackCount())
1145  {
1146  LOG(VB_GENERAL, LOG_ERR,
1147  QString("MusicPlayer: asked to set the current track to an invalid track no. %1")
1148  .arg(trackNo));
1149  m_currentTrack = -1;
1150  return;
1151  }
1152 }
1153 
1156 {
1157  if (m_oneshotMetadata)
1158  return m_oneshotMetadata;
1159 
1160  if (!getCurrentPlaylist() || !getCurrentPlaylist()->getSongAt(m_currentTrack))
1161  return nullptr;
1162 
1164 }
1165 
1168 {
1169  if (m_playMode == PLAYMODE_RADIO)
1170  return nullptr;
1171 
1172  if (m_oneshotMetadata)
1173  return getCurrentMetadata();
1174 
1175  if (!getCurrentPlaylist() || !getCurrentPlaylist()->getSongAt(m_currentTrack))
1176  return nullptr;
1177 
1178  if (m_repeatMode == REPEAT_TRACK)
1179  return getCurrentMetadata();
1180 
1181  // if we are not playing the last track then just return the next track
1182  if (m_currentTrack < getCurrentPlaylist()->getTrackCount() - 1)
1184 
1185  // if we are playing the last track then we need to take the
1186  // repeat mode into account
1187  if (m_repeatMode == REPEAT_ALL)
1188  return getCurrentPlaylist()->getSongAt(0);
1189  return nullptr;
1190 }
1191 
1193 {
1194  switch (m_repeatMode)
1195  {
1196  case REPEAT_OFF:
1198  break;
1199  case REPEAT_TRACK:
1201  break;
1202  case REPEAT_ALL:
1203  default:
1205  break;
1206  }
1207 
1208  return m_repeatMode;
1209 }
1210 
1212 {
1213  switch (m_shuffleMode)
1214  {
1215  case SHUFFLE_OFF:
1217  break;
1218  case SHUFFLE_RANDOM:
1220  break;
1221  case SHUFFLE_INTELLIGENT:
1223  break;
1224  case SHUFFLE_ALBUM:
1226  break;
1227  case SHUFFLE_ARTIST:
1228  default:
1230  break;
1231  }
1232 
1234 
1235  return m_shuffleMode;
1236 }
1237 
1239 {
1240  int curTrackID = -1;
1241  if (getCurrentMetadata())
1242  curTrackID = getCurrentMetadata()->ID();
1243 
1244  // only save the mode if we are playing tracks
1245  if (m_playMode != PLAYMODE_RADIO)
1246  m_shuffleMode = mode;
1247 
1248  if (!getCurrentPlaylist())
1249  return;
1250 
1252 
1253  if (curTrackID != -1)
1254  {
1255  for (int x = 0; x < getCurrentPlaylist()->getTrackCount(); x++)
1256  {
1258  if (mdata && mdata->ID() == (MusicMetadata::IdType) curTrackID)
1259  {
1260  m_currentTrack = x;
1261  break;
1262  }
1263  }
1264  }
1265 }
1266 
1268 {
1270  {
1273  }
1274 
1275  m_updatedLastplay = true;
1276 }
1277 
1279 {
1281  {
1282  if (getCurrentMetadata()->hasChanged())
1283  {
1285 
1286  // only write the last played, playcount & rating to the tag if it's enabled by the user
1287  if (GetMythDB()->GetNumSetting("AllowTagWriting", 0) == 1)
1288  {
1289  QStringList strList;
1290  strList << QString("MUSIC_TAG_UPDATE_VOLATILE")
1292  << QString::number(getCurrentMetadata()->ID())
1293  << QString::number(getCurrentMetadata()->Rating())
1294  << QString::number(getCurrentMetadata()->Playcount())
1295  << getCurrentMetadata()->LastPlay().toString(Qt::ISODate);
1296  auto *thread = new SendStringListThread(strList);
1297  MThreadPool::globalInstance()->start(thread, "UpdateVolatile");
1298  }
1299 
1301  }
1302  }
1303 }
1304 
1305 void MusicPlayer::setSpeed(float newspeed)
1306 {
1307  if (m_output)
1308  {
1309  m_playSpeed = newspeed;
1311  }
1312 }
1313 
1315 {
1316  m_playSpeed += 0.05F;
1318 }
1319 
1321 {
1322  m_playSpeed -= 0.05F;
1324 }
1325 
1327 {
1329  dispatch(me);
1330 }
1331 
1333 {
1335  dispatch(me);
1336 }
1337 
1339 {
1341  dispatch(me);
1342 }
1343 
1345 {
1347  dispatch(me);
1348 }
1349 
1351 {
1353  dispatch(me);
1354 }
1355 
1357 {
1359  dispatch(me);
1360 }
1361 
1363 {
1364  if (getOutput())
1365  {
1368  }
1369 }
1370 
1372 {
1373  if (getOutput())
1374  {
1377  }
1378 }
1379 
1380 void MusicPlayer::setVolume(int volume)
1381 {
1382  if (getOutput())
1383  {
1384  getOutput()->SetCurrentVolume(volume);
1386  }
1387 }
1388 
1390 {
1391  if (m_output)
1392  return m_output->GetCurrentVolume();
1393  return 0;
1394 }
1395 
1397 {
1398  if (m_output)
1399  {
1400  m_output->ToggleMute();
1402  }
1403 }
1404 
1406 {
1407  if (m_output)
1408  return m_output->GetMuteState();
1409  return kMuteOff;
1410 }
1411 
1413 {
1414  map["volumemute"] = isMuted() ? tr("%1% (Muted)", "Zero Audio Volume").arg(getVolume()) :
1415  QString("%1%").arg(getVolume());
1416  map["volume"] = QString("%1").arg(getVolume());
1417  map["volumepercent"] = QString("%1%").arg(getVolume());
1418  map["mute"] = isMuted() ? tr("Muted") : "";
1419 }
1420 
1421 void MusicPlayer::activePlaylistChanged(int trackID, bool deleted)
1422 {
1423  if (trackID == -1)
1424  {
1425  if (deleted)
1426  {
1428  dispatch(me);
1429  }
1430  else
1431  {
1433  dispatch(me);
1434  }
1435  }
1436  else
1437  {
1438  if (deleted)
1439  {
1441  dispatch(me);
1442  }
1443  else
1444  {
1446  dispatch(me);
1447  }
1448  }
1449 
1450  // if we don't have any tracks to play stop here
1451  if (!getCurrentPlaylist() || getCurrentPlaylist()->getTrackCount() == 0)
1452  {
1453  m_currentTrack = -1;
1454  if (isPlaying())
1455  stop(true);
1456  return;
1457  }
1458 
1459  int trackPos = -1;
1460 
1461  // make sure the current playing track is still valid
1462  if (isPlaying() && getDecoderHandler())
1463  {
1464  for (int x = 0; x < getCurrentPlaylist()->getTrackCount(); x++)
1465  {
1467  {
1468  trackPos = x;
1469  break;
1470  }
1471  }
1472  }
1473 
1474  if (trackPos != m_currentTrack)
1475  m_currentTrack = trackPos;
1476 
1477  if (!getCurrentMetadata())
1478  {
1479  m_currentTrack = -1;
1480  stop(true);
1481  }
1482 }
1483 
1484 void MusicPlayer::playlistChanged(int playlistID)
1485 {
1487  dispatch(me);
1488 }
1489 
1491 {
1494 
1495  // add any listeners to the decoderHandler
1496  {
1497  QMutexLocker locker(m_lock);
1498  // NOLINTNEXTLINE(modernize-loop-convert)
1499  for (auto it = m_listeners.begin(); it != m_listeners.end() ; ++it)
1501  }
1502 }
1503 
1505 {
1506  if (!getDecoder())
1507  return;
1508 
1509  LOG(VB_PLAYBACK, LOG_INFO, QString ("decoder handler is ready, decoding %1")
1510  .arg(getDecoder()->getURL()));
1511 
1512 #ifdef HAVE_CDIO
1513  auto *cddecoder = dynamic_cast<CdDecoder*>(getDecoder());
1514  if (cddecoder)
1515  cddecoder->setDevice(gCDdevice);
1516 #endif
1517 
1518  // Decoder thread can't be running while being initialized
1519  if (getDecoder()->isRunning())
1520  {
1521  getDecoder()->stop();
1522  getDecoder()->wait();
1523  }
1524 
1526  //getDecoder()-> setBlockSize(2 * 1024);
1527  getDecoder()->addListener(this);
1528 
1529  // add any listeners to the decoder
1530  {
1531  QMutexLocker locker(m_lock);
1532  // NOLINTNEXTLINE(modernize-loop-convert)
1533  for (auto it = m_listeners.begin(); it != m_listeners.end() ; ++it)
1534  getDecoder()->addListener(*it);
1535  }
1536 
1537  m_currentTime = 0;
1538  m_lastTrackStart = 0;
1539 
1540  // NOLINTNEXTLINE(modernize-loop-convert)
1541  for (auto it = m_visualisers.begin(); it != m_visualisers.end() ; ++it)
1542  {
1543  //m_output->addVisual((MythTV::Visual*)(*it));
1544  //(*it)->setDecoder(getDecoder());
1545  //m_visual->setOutput(m_output);
1546  }
1547 
1548  if (getDecoder()->initialize())
1549  {
1550  if (m_output)
1552 
1553  getDecoder()->start();
1554 
1556  gCoreContext->GetNumSetting("MusicBookmarkPosition", 0) > 0)
1557  {
1558  seek(gCoreContext->GetNumSetting("MusicBookmarkPosition", 0));
1559  gCoreContext->SaveSetting("MusicBookmarkPosition", 0);
1560  }
1561 
1562  m_isPlaying = true;
1563  m_updatedLastplay = false;
1564  }
1565  else
1566  {
1567  LOG(VB_PLAYBACK, LOG_ERR, QString("Cannot initialise decoder for %1")
1568  .arg(getDecoder()->getURL()));
1569  return;
1570  }
1571 
1572  // tell any listeners we've started playing a new track
1574  dispatch(me);
1575 }
1576 
1577 void MusicPlayer::removeTrack(int trackID)
1578 {
1579  MusicMetadata *mdata = gMusicData->m_all_music->getMetadata(trackID);
1580  if (mdata)
1581  {
1582  int trackPos = getCurrentPlaylist()->getTrackPosition(mdata->ID());
1583  if (m_currentTrack > 0 && m_currentTrack >= trackPos)
1584  m_currentTrack--;
1585 
1586  getCurrentPlaylist()->removeTrack(trackID);
1587  }
1588 }
1589 
1590 void MusicPlayer::addTrack(int trackID, bool updateUI)
1591 {
1592  getCurrentPlaylist()->addTrack(trackID, updateUI);
1593 }
1594 
1596 {
1598  return nullptr;
1599 
1600  if (m_playMode == PLAYMODE_RADIO)
1601  {
1603  }
1605 }
1606 
1608 {
1609  return gMusicData->m_all_streams->getStreams();
1610 }
1611 
1613 {
1614  if (m_notificationMap.find(hostname) == m_notificationMap.end())
1615  m_notificationMap.insert(hostname, GetNotificationCenter()->Register(this));
1616 
1617  return m_notificationMap[hostname];
1618 }
1619 
1620 void MusicPlayer::sendNotification(int notificationID, const QString &title, const QString &author, const QString &desc)
1621 {
1622  QString image = "musicscanner.png";
1623  if (!GetMythUI()->FindThemeFile(image))
1624  LOG(VB_GENERAL, LOG_ERR, "MusicPlayer: sendNotification failed to find the 'musicscanner.png' image");
1625 
1626  DMAP map;
1627  map["asar"] = title;
1628  map["minm"] = author;
1629  map["asal"] = desc;
1630 
1631  auto *n = new MythImageNotification(MythNotification::Info, image, map);
1632 
1633  n->SetId(notificationID);
1634  n->SetParent(this);
1635  n->SetDuration(5);
1636  n->SetFullScreen(false);
1638  delete n;
1639 }
static 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
MusicMetadata * getSongAt(int pos) const
Definition: playlist.cpp:1086
int m_errorCount
Definition: musicplayer.h:251
void persist(void)
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
~MusicPlayer(void) override
Definition: musicplayer.cpp:86
void setShuffleMode(ShuffleMode mode)
void changeCurrentTrack(int trackNo)
change the current track to the given track
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:48
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)
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
void removeVisual(MainVisual *visual)
static Type TrackAddedEvent
Definition: musicplayer.h:42
void SaveSetting(const QString &key, int newValue)
static Type MythEventMessage
Definition: mythevent.h:73
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:81
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:58
bool openOutputDevice(void)
uint32_t IdType
Definition: musicmetadata.h:86
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
void setLength(int llength)
virtual void ToggleMute(void)
Definition: volumebase.cpp:144
bool m_isAutoplay
Definition: musicplayer.h:224
MuteState
Definition: volumebase.h:6
QMap< QString, QString > DMAP
Playlist * getActive(void)
AllStream * m_all_streams
Definition: musicdata.h:56
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)
void incVolume(void)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
void enableSaves(void)
Definition: playlist.h:103
void addVisual(MainVisual *visual)
ResumeMode getResumeMode(void)
void setFilename(const QString &lfilename)
void stopDecoder(void)
void setOutput(AudioOutput *o)
Definition: decoder.cpp:46
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
virtual void AdjustCurrentVolume(int change)
Definition: volumebase.cpp:132
QList< MusicMetadata * > StreamList
virtual void lock(void)
Definition: decoder.h:80
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:54
static Type Error
Definition: decoder.h:49
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
int getTrackCount(void)
Definition: playlist.h:82
MusicPlayer(QObject *parent)
Definition: musicplayer.cpp:53
void addTrack(int trackID, bool updateUI)
QString GetSetting(const QString &key, const QString &defaultval="")
bool hasListeners(void)
IdType ID() const
int m_lastTrackStart
Definition: musicplayer.h:247
QList< MusicMetadata * > m_playedList
Definition: musicplayer.h:246
void setAlbum(const QString &lalbum, const QString &lalbum_sort=nullptr)
void addVisual(MythTV::Visual *v)
Definition: output.cpp:36
int getNotificationID(const QString &hostname)
void decoderHandlerReady(void)
void getBufferStatus(int *bufferAvailable, int *bufferSize)
bool isMuted(void) const
Definition: musicplayer.h:82
QSet< QObject * > m_listeners
void moveTrackUpDown(bool flag, int where_its_at)
Definition: playlist.cpp:128
static Type Stopped
Definition: decoder.h:47
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
string hostname
Definition: caa.py:17
void disableSaves(void)
whether any changes should be saved to the DB
Definition: playlist.h:102
void pause(void)
void loadSettings(void)
void updateVolatileMetadata(void)
DecoderHandler * m_decoderHandler
Definition: musicplayer.h:218
unsigned int uint
Definition: compat.h:140
QString Album() const
void showMiniPlayer(void)
virtual void SetStretchFactor(float factor)
ShuffleMode toggleShuffleMode(void)
int m_currentTrack
Definition: musicplayer.h:212
StreamList * getStreams(void)
QWaitCondition * cond()
Definition: decoder.h:84
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
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:57
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 start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
void reloadMetadata(void)
bool m_updatedLastplay
Definition: musicplayer.h:228
static Type PlaylistChangedEvent
Definition: musicplayer.h:50
void removeVisual(MythTV::Visual *v)
Definition: output.cpp:43
MusicData * gMusicData
Definition: musicdata.cpp:20
void toMap(InfoMap &infoMap)
int m_bufferSize
Definition: musicplayer.h:249
Class for starting stream decoding.
int getTrackPosition(MusicMetadata::IdType trackID)
Definition: playlist.h:90
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:73
static Type PlayedTracksChangedEvent
Definition: musicplayer.h:51
QDateTime LastPlay() const
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.
Default UTC.
Definition: mythdate.h:14
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
static Type VolumeChangeEvent
Definition: musicplayer.h:41
virtual void PauseUntilBuffered(void)=0
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
bool InWantingPlayback(void)
Returns true if a client has requested playback.
void activePlaylistChanged(int trackID, bool deleted)
send a message to the master BE without blocking the UI thread
Definition: musicdata.h:21
QString GetError(void) const
Definition: audiooutput.h:145
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:55
virtual MuteState GetMuteState(void) const
Definition: volumebase.cpp:150
MythNotificationCenter * GetNotificationCenter(void)
void start(MusicMetadata *mdata)
static Type Stopped
Definition: output.h:65
ResumeMode m_resumeModePlayback
Definition: musicplayer.h:235