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