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  auto *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  auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
559  if (!dhe)
560  return;
561 
562  auto *mdata = new MusicMetadata(*dhe->getMetadata());
563 
565 
566  if (getCurrentMetadata())
567  {
568  mdata->setID(getCurrentMetadata()->ID());
569  mdata->setHostname(gCoreContext->GetMasterHostName());
570  mdata->setTrack(m_playedList.count() + 1);
571  mdata->setBroadcaster(getCurrentMetadata()->Broadcaster());
572  mdata->setChannel(getCurrentMetadata()->Channel());
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  auto *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  auto *me = dynamic_cast<MythEvent*>(event);
602  if (!me)
603  return;
604 
605  if (me->Message().left(13) == "MUSIC_COMMAND")
606  {
607  QStringList list = me->Message().simplified().split(' ');
608 
609  if (list.size() >= 3 && list[1] == gCoreContext->GetHostName())
610  {
611  if (list[2] == "PLAY")
612  play();
613  else if (list[2] == "STOP")
614  stop();
615  else if (list[2] == "PAUSE")
616  pause();
617  else if (list[2] == "SET_VOLUME")
618  {
619  if (list.size() >= 3)
620  {
621  int volume = list[3].toInt();
622  if (volume >= 0 && volume <= 100)
623  setVolume(volume);
624  }
625  }
626  else if (list[2] == "GET_VOLUME")
627  {
628  QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
629  .arg(gCoreContext->GetHostName()).arg(getVolume());
630  MythEvent me2(message);
631  gCoreContext->dispatch(me2);
632  }
633  else if (list[2] == "PLAY_FILE")
634  {
635  int start = me->Message().indexOf("'");
636  int end = me->Message().lastIndexOf("'");
637 
638  if (start != -1 && end != -1 && start != end)
639  {
640  QString filename = me->Message().mid(start + 1, end - start - 1);
641  MusicMetadata mdata;
642  mdata.setFilename(filename);
643  playFile(mdata);
644  }
645  else
646  LOG(VB_GENERAL, LOG_ERR,
647  QString("MusicPlayer: got invalid MUSIC_COMMAND "
648  "PLAY_FILE - %1").arg(me->Message()));
649  }
650  else if (list[2] == "PLAY_URL")
651  {
652  if (list.size() == 4)
653  {
654  QString filename = list[3];
655  MusicMetadata mdata;
656  mdata.setFilename(filename);
657  playFile(mdata);
658  }
659  else
660  LOG(VB_GENERAL, LOG_ERR,
661  QString("MusicPlayer: got invalid MUSIC_COMMAND "
662  "PLAY_URL - %1").arg(me->Message()));
663  }
664  else if (list[2] == "PLAY_TRACK")
665  {
666  if (list.size() == 4)
667  {
668  int trackID = list[3].toInt();
669  MusicMetadata *mdata = gMusicData->m_all_music->getMetadata(trackID);
670  if (mdata)
671  playFile(*mdata);
672  }
673  else
674  LOG(VB_GENERAL, LOG_ERR,
675  QString("MusicPlayer: got invalid MUSIC_COMMAND "
676  "PLAY_TRACK - %1").arg(me->Message()));
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  LOG(VB_GENERAL, LOG_ERR,
709  QString("MusicPlayer: got unknown/invalid MUSIC_COMMAND "
710  "- %1").arg(me->Message()));
711  }
712  else if (me->Message().startsWith("MUSIC_SETTINGS_CHANGED"))
713  {
714  loadSettings();
715  }
716  else if (me->Message().startsWith("MUSIC_METADATA_CHANGED"))
717  {
719  {
720  QStringList list = me->Message().simplified().split(' ');
721  if (list.size() == 2)
722  {
723  int songID = list[1].toInt();
724  MusicMetadata *mdata = gMusicData->m_all_music->getMetadata(songID);
725 
726  if (mdata)
727  {
728  mdata->reloadMetadata();
729 
730  // tell any listeners the metadata has changed for this track
731  sendMetadataChangedEvent(songID);
732  }
733  }
734  }
735  }
736  else if (me->Message().startsWith("MUSIC_SCANNER_STARTED"))
737  {
738  QStringList list = me->Message().simplified().split(' ');
739  if (list.size() == 2)
740  {
741  QString host = list[1];
742  int id = getNotificationID(host);
743  sendNotification(id,
744  tr("A music file scan has started on %1").arg(host),
745  tr("Music File Scanner"),
746  tr("This may take a while I'll give a shout when finished"));
747  }
748  }
749  else if (me->Message().startsWith("MUSIC_SCANNER_FINISHED"))
750  {
751  QStringList list = me->Message().simplified().split(' ');
752  if (list.size() == 6)
753  {
754  QString host = list[1];
755  int id = getNotificationID(host);
756  int totalTracks = list[2].toInt();
757  int newTracks = list[3].toInt();
758  int totalCoverart = list[4].toInt();
759  int newCoverart = list[5].toInt();
760 
761  QString summary = QString("Total Tracks: %2, new tracks: %3,\nTotal Coverart: %4, New CoverArt %5")
762  .arg(totalTracks).arg(newTracks).arg(totalCoverart).arg(newCoverart);
763  sendNotification(id,
764  tr("A music file scan has finished on %1").arg(host),
765  tr("Music File Scanner"), summary);
766 
768  }
769  }
770  else if (me->Message().startsWith("MUSIC_SCANNER_ERROR"))
771  {
772  QStringList list = me->Message().simplified().split(' ');
773  if (list.size() == 3)
774  {
775  QString host = list[1];
776  QString error = list[2];
777  int id = getNotificationID(host);
778 
779  if (error == "Already_Running")
780  sendNotification(id, tr(""),
781  tr("Music File Scanner"),
782  tr("Can't run the music file scanner because it is already running on %1").arg(host));
783  else if (error == "Stalled")
784  sendNotification(id, tr(""),
785  tr("Music File Scanner"),
786  tr("The music file scanner has been running for more than 60 minutes on %1.\nResetting and trying again")
787  .arg(host));
788  }
789  }
790  }
791 
792  if (event->type() == OutputEvent::Error)
793  {
794  auto *aoe = dynamic_cast<OutputEvent *>(event);
795 
796  if (!aoe)
797  return;
798 
799  LOG(VB_GENERAL, LOG_ERR, QString("Audio Output Error: %1").arg(*aoe->errorMessage()));
800 
801  MythErrorNotification n(tr("Audio Output Error"), tr("MythMusic"), *aoe->errorMessage());
803 
804  m_errorCount++;
805 
806  if (m_errorCount <= 5)
807  nextAuto();
808  else
809  {
810  m_errorCount = 0;
811  stop(true);
812  }
813  }
814  else if (event->type() == DecoderEvent::Error)
815  {
816  auto *dxe = dynamic_cast<DecoderEvent *>(event);
817 
818  if (!dxe)
819  return;
820 
821  LOG(VB_GENERAL, LOG_ERR, QString("Decoder Error: %2").arg(*dxe->errorMessage()));
822 
823  MythErrorNotification n(tr("Decoder Error"), tr("MythMusic"), *dxe->errorMessage());
825 
826  m_errorCount++;
827 
828  if (m_playMode != PLAYMODE_RADIO && m_errorCount <= 5)
829  nextAuto();
830  else
831  {
832  m_errorCount = 0;
833  stop(true);
834  }
835  }
836  else if (event->type() == DecoderHandlerEvent::Error)
837  {
838  auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
839 
840  if (!dhe)
841  return;
842 
843  LOG(VB_GENERAL, LOG_ERR, QString("Decoder Handler Error - %1").arg(*dhe->getMessage()));
844 
845  MythErrorNotification n(tr("Decoder Handler Error"), tr("MythMusic"), *dhe->getMessage());
847 
848  m_errorCount++;
849 
850  if (m_errorCount <= 5)
851  nextAuto();
852  else
853  {
854  m_errorCount = 0;
855  stop(true);
856  }
857  }
858  else if (event->type() == OutputEvent::Info)
859  {
860  auto *oe = dynamic_cast<OutputEvent*>(event);
861 
862  if (!oe)
863  return;
864 
865  if (m_playMode != PLAYMODE_RADIO)
866  m_currentTime = oe->elapsedSeconds();
867  else
868  m_currentTime = oe->elapsedSeconds() - m_lastTrackStart;
869 
871  {
872  // we update the lastplay and playcount after playing
873  // for m_lastplayDelay seconds or half the total track time
875  (getCurrentMetadata()->Length() / 1000) / 2) ||
877  {
878  updateLastplay();
879  }
880  }
881 
882  // update the current tracks time in the last played list
883  if (m_playMode == PLAYMODE_RADIO)
884  {
885  if (!m_playedList.isEmpty())
886  {
887  m_playedList.last()->setLength(m_currentTime * 1000);
888  // this will update any track lengths displayed on screen
890  }
891  }
892  }
893  else if (event->type() == DecoderEvent::Finished)
894  {
895  if (m_oneshotMetadata)
896  {
897  delete m_oneshotMetadata;
898  m_oneshotMetadata = nullptr;
899  stop(true);
900  }
901  else
902  {
904  m_currentTime != getCurrentMetadata()->Length() / 1000)
905  {
906  LOG(VB_GENERAL, LOG_NOTICE, QString("MusicPlayer: Updating track length was %1s, should be %2s")
907  .arg(getCurrentMetadata()->Length() / 1000).arg(m_currentTime));
908 
911 
912  // this will update any track lengths displayed on screen
914 
915  // this will force the playlist stats to update
917  dispatch(me);
918  }
919 
920  nextAuto();
921  }
922  }
923  else if (event->type() == DecoderEvent::Stopped)
924  {
925  }
926  else if (event->type() == DecoderHandlerEvent::BufferStatus)
927  {
928  auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
929  if (!dhe)
930  return;
931 
932  dhe->getBufferStatus(&m_bufferAvailable, &m_bufferSize);
933  }
934 
935  QObject::customEvent(event);
936 }
937 
938 void MusicPlayer::getBufferStatus(int *bufferAvailable, int *bufferSize)
939 {
940  *bufferAvailable = m_bufferAvailable;
941  *bufferSize = m_bufferSize;
942 }
943 
945 {
946  if (m_playMode == mode)
947  return;
948 
949  savePosition();
950 
951  m_playMode = mode;
952 
953  loadPlaylist();
954 }
955 
957 {
958  if (m_playMode == PLAYMODE_RADIO)
959  {
961  {
962  int bookmark = gCoreContext->GetNumSetting("MusicRadioBookmark", 0);
963  if (bookmark < 0 || bookmark >= getCurrentPlaylist()->getTrackCount())
964  bookmark = 0;
965 
966  m_currentTrack = bookmark;
967  }
968  else
969  m_currentTrack = 0;
970 
972  }
973  else
974  {
976  {
977  int bookmark = gCoreContext->GetNumSetting("MusicBookmark", 0);
978  if (bookmark < 0 || bookmark >= getCurrentPlaylist()->getTrackCount())
979  bookmark = 0;
980 
981  m_currentTrack = bookmark;
982  }
983  else
984  m_currentTrack = 0;
985  }
986 }
987 
989 {
991 
992  // create the radio playlist
996 
997  for (int x = 0; x < list->count(); x++)
998  {
999  MusicMetadata *mdata = list->at(x);
1000  gMusicData->m_all_playlists->getStreamPlaylist()->addTrack(mdata->ID(), false);
1001 
1002  if (mdata->ID() == id)
1003  m_currentTrack = x;
1004  }
1005 
1007 }
1008 
1009 void MusicPlayer::moveTrackUpDown(bool moveUp, int whichTrack)
1010 {
1011  if (!getCurrentPlaylist())
1012  return;
1013 
1014  if (moveUp && whichTrack <= 0)
1015  return;
1016 
1017  if (!moveUp && whichTrack >= getCurrentPlaylist()->getTrackCount() - 1)
1018  return;
1019 
1021 
1022  getCurrentPlaylist()->moveTrackUpDown(moveUp, whichTrack);
1023 
1025 }
1026 
1028 {
1029  changeCurrentTrack(pos);
1030 
1031  if (!getCurrentMetadata())
1032  {
1033  stop();
1034  return false;
1035  }
1036 
1037  play();
1038 
1039  return true;
1040 }
1041 
1043 {
1044  // can't save a bookmark if we don't know what we are playing
1045  if (!getCurrentMetadata())
1046  return;
1047 
1048  if (m_playMode == PLAYMODE_RADIO)
1049  {
1050  gCoreContext->SaveSetting("MusicRadioBookmark", getCurrentMetadata()->ID());
1051  }
1052  else
1053  {
1054  gCoreContext->SaveSetting("MusicBookmark", getCurrentMetadata()->ID());
1055  gCoreContext->SaveSetting("MusicBookmarkPosition", m_currentTime);
1056  }
1057 }
1058 
1060 {
1061  // if we are switching views we don't wont to restore the position
1062  if (!m_allowRestorePos)
1063  return;
1064 
1065  m_currentTrack = 0;
1066  uint id = -1;
1067 
1069  {
1070  if (m_playMode == PLAYMODE_RADIO)
1071  id = gCoreContext->GetNumSetting("MusicRadioBookmark", 0);
1072  else
1073  id = gCoreContext->GetNumSetting("MusicBookmark", 0);
1074  }
1075 
1076  if (getCurrentPlaylist())
1077  {
1078  for (int x = 0; x < getCurrentPlaylist()->getTrackCount(); x++)
1079  {
1080  if (getCurrentPlaylist()->getSongAt(x) &&
1081  getCurrentPlaylist()->getSongAt(x)->ID() == id)
1082  {
1083  m_currentTrack = x;
1084  break;
1085  }
1086  }
1087  }
1088 
1089  if (getCurrentMetadata())
1090  {
1092  play();
1093 
1095  seek(gCoreContext->GetNumSetting("MusicBookmarkPosition", 0));
1096  }
1097 }
1098 
1099 void MusicPlayer::seek(int pos)
1100 {
1101  if (m_output)
1102  {
1103  if (getDecoder() && getDecoder()->isRunning())
1104  getDecoder()->seek(pos);
1105 
1106  m_output->SetTimecode(pos*1000);
1107  }
1108 }
1109 
1111 {
1112  if (m_canShowPlayer)
1113  {
1114  MythScreenStack *popupStack =
1115  GetMythMainWindow()->GetStack("popup stack");
1116 
1117  auto *miniplayer = new MiniPlayer(popupStack);
1118 
1119  if (miniplayer->Create())
1120  popupStack->AddScreen(miniplayer);
1121  else
1122  delete miniplayer;
1123  }
1124 }
1125 
1128 {
1129  if (!getCurrentPlaylist())
1130  return;
1131 
1132  // check to see if we need to save the current tracks volatile metadata (playcount, last played etc)
1134 
1135  m_currentTrack = trackNo;
1136 
1137  // sanity check the current track
1138  if (m_currentTrack < 0 || m_currentTrack >= getCurrentPlaylist()->getTrackCount())
1139  {
1140  LOG(VB_GENERAL, LOG_ERR,
1141  QString("MusicPlayer: asked to set the current track to an invalid track no. %1")
1142  .arg(trackNo));
1143  m_currentTrack = -1;
1144  return;
1145  }
1146 }
1147 
1150 {
1151  if (m_oneshotMetadata)
1152  return m_oneshotMetadata;
1153 
1154  if (!getCurrentPlaylist() || !getCurrentPlaylist()->getSongAt(m_currentTrack))
1155  return nullptr;
1156 
1158 }
1159 
1162 {
1163  if (m_playMode == PLAYMODE_RADIO)
1164  return nullptr;
1165 
1166  if (m_oneshotMetadata)
1167  return getCurrentMetadata();
1168 
1169  if (!getCurrentPlaylist() || !getCurrentPlaylist()->getSongAt(m_currentTrack))
1170  return nullptr;
1171 
1172  if (m_repeatMode == REPEAT_TRACK)
1173  return getCurrentMetadata();
1174 
1175  // if we are not playing the last track then just return the next track
1176  if (m_currentTrack < getCurrentPlaylist()->getTrackCount() - 1)
1178 
1179  // if we are playing the last track then we need to take the
1180  // repeat mode into account
1181  if (m_repeatMode == REPEAT_ALL)
1182  return getCurrentPlaylist()->getSongAt(0);
1183  return nullptr;
1184 }
1185 
1187 {
1188  switch (m_repeatMode)
1189  {
1190  case REPEAT_OFF:
1192  break;
1193  case REPEAT_TRACK:
1195  break;
1196  case REPEAT_ALL:
1197  default:
1199  break;
1200  }
1201 
1202  return m_repeatMode;
1203 }
1204 
1206 {
1207  switch (m_shuffleMode)
1208  {
1209  case SHUFFLE_OFF:
1211  break;
1212  case SHUFFLE_RANDOM:
1214  break;
1215  case SHUFFLE_INTELLIGENT:
1217  break;
1218  case SHUFFLE_ALBUM:
1220  break;
1221  case SHUFFLE_ARTIST:
1222  default:
1224  break;
1225  }
1226 
1228 
1229  return m_shuffleMode;
1230 }
1231 
1233 {
1234  int curTrackID = -1;
1235  if (getCurrentMetadata())
1236  curTrackID = getCurrentMetadata()->ID();
1237 
1238  // only save the mode if we are playing tracks
1239  if (m_playMode != PLAYMODE_RADIO)
1240  m_shuffleMode = mode;
1241 
1242  if (!getCurrentPlaylist())
1243  return;
1244 
1246 
1247  if (curTrackID != -1)
1248  {
1249  for (int x = 0; x < getCurrentPlaylist()->getTrackCount(); x++)
1250  {
1252  if (mdata && mdata->ID() == (MusicMetadata::IdType) curTrackID)
1253  {
1254  m_currentTrack = x;
1255  break;
1256  }
1257  }
1258  }
1259 }
1260 
1262 {
1264  {
1267  }
1268 
1269  m_updatedLastplay = true;
1270 }
1271 
1273 {
1275  {
1276  if (getCurrentMetadata()->hasChanged())
1277  {
1279 
1280  // only write the last played, playcount & rating to the tag if it's enabled by the user
1281  if (GetMythDB()->GetNumSetting("AllowTagWriting", 0) == 1)
1282  {
1283  QStringList strList;
1284  strList << QString("MUSIC_TAG_UPDATE_VOLATILE")
1286  << QString::number(getCurrentMetadata()->ID())
1287  << QString::number(getCurrentMetadata()->Rating())
1288  << QString::number(getCurrentMetadata()->Playcount())
1289  << getCurrentMetadata()->LastPlay().toString(Qt::ISODate);
1290  auto *thread = new SendStringListThread(strList);
1291  MThreadPool::globalInstance()->start(thread, "UpdateVolatile");
1292  }
1293 
1295  }
1296  }
1297 }
1298 
1299 void MusicPlayer::setSpeed(float newspeed)
1300 {
1301  if (m_output)
1302  {
1303  m_playSpeed = newspeed;
1305  }
1306 }
1307 
1309 {
1310  m_playSpeed += 0.05F;
1312 }
1313 
1315 {
1316  m_playSpeed -= 0.05F;
1318 }
1319 
1321 {
1323  dispatch(me);
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 {
1358  if (getOutput())
1359  {
1362  }
1363 }
1364 
1366 {
1367  if (getOutput())
1368  {
1371  }
1372 }
1373 
1374 void MusicPlayer::setVolume(int volume)
1375 {
1376  if (getOutput())
1377  {
1378  getOutput()->SetCurrentVolume(volume);
1380  }
1381 }
1382 
1384 {
1385  if (m_output)
1386  return m_output->GetCurrentVolume();
1387  return 0;
1388 }
1389 
1391 {
1392  if (m_output)
1393  {
1394  m_output->ToggleMute();
1396  }
1397 }
1398 
1400 {
1401  if (m_output)
1402  return m_output->GetMuteState();
1403  return kMuteOff;
1404 }
1405 
1407 {
1408  map["volumemute"] = isMuted() ? tr("%1% (Muted)", "Zero Audio Volume").arg(getVolume()) :
1409  QString("%1%").arg(getVolume());
1410  map["volume"] = QString("%1").arg(getVolume());
1411  map["volumepercent"] = QString("%1%").arg(getVolume());
1412  map["mute"] = isMuted() ? tr("Muted") : "";
1413 }
1414 
1415 void MusicPlayer::activePlaylistChanged(int trackID, bool deleted)
1416 {
1417  if (trackID == -1)
1418  {
1419  if (deleted)
1420  {
1422  dispatch(me);
1423  }
1424  else
1425  {
1427  dispatch(me);
1428  }
1429  }
1430  else
1431  {
1432  if (deleted)
1433  {
1435  dispatch(me);
1436  }
1437  else
1438  {
1440  dispatch(me);
1441  }
1442  }
1443 
1444  // if we don't have any tracks to play stop here
1445  if (!getCurrentPlaylist() || getCurrentPlaylist()->getTrackCount() == 0)
1446  {
1447  m_currentTrack = -1;
1448  if (isPlaying())
1449  stop(true);
1450  return;
1451  }
1452 
1453  int trackPos = -1;
1454 
1455  // make sure the current playing track is still valid
1456  if (isPlaying() && getDecoderHandler())
1457  {
1458  for (int x = 0; x < getCurrentPlaylist()->getTrackCount(); x++)
1459  {
1461  {
1462  trackPos = x;
1463  break;
1464  }
1465  }
1466  }
1467 
1468  if (trackPos != m_currentTrack)
1469  m_currentTrack = trackPos;
1470 
1471  if (!getCurrentMetadata())
1472  {
1473  m_currentTrack = -1;
1474  stop(true);
1475  }
1476 }
1477 
1478 void MusicPlayer::playlistChanged(int playlistID)
1479 {
1481  dispatch(me);
1482 }
1483 
1485 {
1488 
1489  // add any listeners to the decoderHandler
1490  {
1491  QMutexLocker locker(m_lock);
1492  QSet<QObject*>::const_iterator it = m_listeners.begin();
1493  for (; it != m_listeners.end() ; ++it)
1494  {
1496  }
1497  }
1498 }
1499 
1501 {
1502  if (!getDecoder())
1503  return;
1504 
1505  LOG(VB_PLAYBACK, LOG_INFO, QString ("decoder handler is ready, decoding %1")
1506  .arg(getDecoder()->getURL()));
1507 
1508 #ifdef HAVE_CDIO
1509  auto *cddecoder = dynamic_cast<CdDecoder*>(getDecoder());
1510  if (cddecoder)
1511  cddecoder->setDevice(gCDdevice);
1512 #endif
1513 
1514  // Decoder thread can't be running while being initialized
1515  if (getDecoder()->isRunning())
1516  {
1517  getDecoder()->stop();
1518  getDecoder()->wait();
1519  }
1520 
1522  //getDecoder()-> setBlockSize(2 * 1024);
1523  getDecoder()->addListener(this);
1524 
1525  // add any listeners to the decoder
1526  {
1527  QMutexLocker locker(m_lock);
1528  QSet<QObject*>::const_iterator it = m_listeners.begin();
1529  for (; it != m_listeners.end() ; ++it)
1530  {
1531  getDecoder()->addListener(*it);
1532  }
1533  }
1534 
1535  m_currentTime = 0;
1536  m_lastTrackStart = 0;
1537 
1538  QSet<QObject*>::const_iterator it = m_visualisers.begin();
1539  for (; it != m_visualisers.end() ; ++it)
1540  {
1541  //m_output->addVisual((MythTV::Visual*)(*it));
1542  //(*it)->setDecoder(getDecoder());
1543  //m_visual->setOutput(m_output);
1544  }
1545 
1546  if (getDecoder()->initialize())
1547  {
1548  if (m_output)
1549  m_output->Reset();
1550 
1551  getDecoder()->start();
1552 
1554  gCoreContext->GetNumSetting("MusicBookmarkPosition", 0) > 0)
1555  {
1556  seek(gCoreContext->GetNumSetting("MusicBookmarkPosition", 0));
1557  gCoreContext->SaveSetting("MusicBookmarkPosition", 0);
1558  }
1559 
1560  m_isPlaying = true;
1561  m_updatedLastplay = false;
1562  }
1563  else
1564  {
1565  LOG(VB_PLAYBACK, LOG_ERR, QString("Cannot initialise decoder for %1")
1566  .arg(getDecoder()->getURL()));
1567  return;
1568  }
1569 
1570  // tell any listeners we've started playing a new track
1572  dispatch(me);
1573 }
1574 
1575 void MusicPlayer::removeTrack(int trackID)
1576 {
1577  MusicMetadata *mdata = gMusicData->m_all_music->getMetadata(trackID);
1578  if (mdata)
1579  {
1580  int trackPos = getCurrentPlaylist()->getTrackPosition(mdata->ID());
1581  if (m_currentTrack > 0 && m_currentTrack >= trackPos)
1582  m_currentTrack--;
1583 
1584  getCurrentPlaylist()->removeTrack(trackID);
1585  }
1586 }
1587 
1588 void MusicPlayer::addTrack(int trackID, bool updateUI)
1589 {
1590  getCurrentPlaylist()->addTrack(trackID, updateUI);
1591 }
1592 
1594 {
1596  return nullptr;
1597 
1598  if (m_playMode == PLAYMODE_RADIO)
1599  {
1601  }
1603 }
1604 
1606 {
1607  return gMusicData->m_all_streams->getStreams();
1608 }
1609 
1611 {
1612  if (m_notificationMap.find(hostname) == m_notificationMap.end())
1613  m_notificationMap.insert(hostname, GetNotificationCenter()->Register(this));
1614 
1615  return m_notificationMap[hostname];
1616 }
1617 
1618 void MusicPlayer::sendNotification(int notificationID, const QString &title, const QString &author, const QString &desc)
1619 {
1620  QString image = "musicscanner.png";
1621  if (!GetMythUI()->FindThemeFile(image))
1622  LOG(VB_GENERAL, LOG_ERR, "MusicPlayer: sendNotification failed to find the 'musicscanner.png' image");
1623 
1624  DMAP map;
1625  map["asar"] = title;
1626  map["minm"] = author;
1627  map["asal"] = desc;
1628 
1629  auto *n = new MythImageNotification(MythNotification::Info, image, map);
1630 
1631  n->SetId(notificationID);
1632  n->SetParent(this);
1633  n->SetDuration(5);
1634  n->SetFullScreen(false);
1636  delete n;
1637 }
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
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:1084
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
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: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: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: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:84
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: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)
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)
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:53
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:79
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)
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:99
void pause(void)
void loadSettings(void)
void updateVolatileMetadata(void)
DecoderHandler * m_decoderHandler
Definition: musicplayer.h:218
unsigned int uint
Definition: compat.h:140
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: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: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
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
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
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: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
MythNotificationCenter * GetNotificationCenter(void)
void start(MusicMetadata *mdata)
static Type Stopped
Definition: output.h:65
ResumeMode m_resumeModePlayback
Definition: musicplayer.h:235