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 <QDir>
7#include <QFile>
8#include <QList>
9#include <QWidget>
10
11// mythtv
15#include <libmythbase/mythdb.h>
22
23// mythmusic
24#include "config.h"
25#include "constants.h"
26#include "decoder.h"
27#include "decoderhandler.h"
28#include "mainvisual.h"
29#include "miniplayer.h"
30#include "musicdata.h"
31#include "musicplayer.h"
32#include "playlistcontainer.h"
33
34#ifdef HAVE_CDIO
35#include "cddecoder.h"
36#endif
37
39QString gCDdevice = "";
40
42
43const QEvent::Type MusicPlayerEvent::kTrackChangeEvent = (QEvent::Type) QEvent::registerEventType();
44const QEvent::Type MusicPlayerEvent::kVolumeChangeEvent = (QEvent::Type) QEvent::registerEventType();
45const QEvent::Type MusicPlayerEvent::kTrackAddedEvent = (QEvent::Type) QEvent::registerEventType();
46const QEvent::Type MusicPlayerEvent::kTrackRemovedEvent = (QEvent::Type) QEvent::registerEventType();
47const QEvent::Type MusicPlayerEvent::kTrackUnavailableEvent = (QEvent::Type) QEvent::registerEventType();
48const QEvent::Type MusicPlayerEvent::kAllTracksRemovedEvent = (QEvent::Type) QEvent::registerEventType();
49const QEvent::Type MusicPlayerEvent::kMetadataChangedEvent = (QEvent::Type) QEvent::registerEventType();
50const QEvent::Type MusicPlayerEvent::kTrackStatsChangedEvent = (QEvent::Type) QEvent::registerEventType();
51const QEvent::Type MusicPlayerEvent::kAlbumArtChangedEvent = (QEvent::Type) QEvent::registerEventType();
52const QEvent::Type MusicPlayerEvent::kCDChangedEvent = (QEvent::Type) QEvent::registerEventType();
53const QEvent::Type MusicPlayerEvent::kPlaylistChangedEvent = (QEvent::Type) QEvent::registerEventType();
54const QEvent::Type MusicPlayerEvent::kPlayedTracksChangedEvent = (QEvent::Type) QEvent::registerEventType();
55
57 :QObject(parent)
58{
59 setObjectName("MusicPlayer");
60
61 QString playmode = gCoreContext->GetSetting("PlayMode", "none");
62 if (playmode.toLower() == "random")
64 else if (playmode.toLower() == "intelligent")
66 else if (playmode.toLower() == "album")
68 else if (playmode.toLower() == "artist")
70 else
72
73 QString repeatmode = gCoreContext->GetSetting("RepeatMode", "all");
74 if (repeatmode.toLower() == "track")
76 else if (repeatmode.toLower() == "all")
78 else
80
82
87}
88
90{
91 if (!hasClient())
93
96
97 QMap<QString, int>::Iterator i;
98 for (i = m_notificationMap.begin(); i != m_notificationMap.end(); i++)
99 GetNotificationCenter()->UnRegister(this, (*i));
100
101 m_notificationMap.clear();
102
103 stop(true);
104
106 {
108 m_decoderHandler->deleteLater();
109 m_decoderHandler = nullptr;
110 }
111
113 {
114 delete m_oneshotMetadata;
115 m_oneshotMetadata = nullptr;
116 }
117
118 while (!m_playedList.empty())
119 {
120 delete m_playedList.back();
121 m_playedList.pop_back();
122 }
123
125 gCoreContext->SaveSetting("PlayMode", "intelligent");
126 else if (m_shuffleMode == SHUFFLE_RANDOM)
127 gCoreContext->SaveSetting("PlayMode", "random");
128 else if (m_shuffleMode == SHUFFLE_ALBUM)
129 gCoreContext->SaveSetting("PlayMode", "album");
130 else if (m_shuffleMode == SHUFFLE_ARTIST)
131 gCoreContext->SaveSetting("PlayMode", "artist");
132 else
133 gCoreContext->SaveSetting("PlayMode", "none");
134
136 gCoreContext->SaveSetting("RepeatMode", "track");
137 else if (m_repeatMode == REPEAT_ALL)
138 gCoreContext->SaveSetting("RepeatMode", "all");
139 else
140 gCoreContext->SaveSetting("RepeatMode", "none");
141
142 gCoreContext->SaveSetting("MusicAutoShowPlayer",
143 (m_autoShowPlayer ? "1" : "0"));
144}
145
146void MusicPlayer::addListener(QObject *listener)
147{
148 if (listener && m_output)
149 m_output->addListener(listener);
150
151 if (listener && getDecoder())
152 getDecoder()->addListener(listener);
153
154 if (listener && m_decoderHandler)
155 m_decoderHandler->addListener(listener);
156
158
160}
161
162void MusicPlayer::removeListener(QObject *listener)
163{
164 if (listener && m_output)
165 m_output->removeListener(listener);
166
167 if (listener && getDecoder())
168 getDecoder()->removeListener(listener);
169
170 if (listener && m_decoderHandler)
172
174
176}
177
179{
180 if (visual && !m_visualisers.contains(visual))
181 {
182 if (m_output)
183 {
184 m_output->addListener(visual);
185 m_output->addVisual(visual);
186 }
187
188 m_visualisers.insert(visual);
189 }
190}
191
193{
194 if (visual)
195 {
196 if (m_output)
197 {
198 m_output->removeListener(visual);
199 m_output->removeVisual(visual);
200 }
201
202 m_visualisers.remove(visual);
203 }
204}
205
207{
209 return m_resumeModeRadio;
211 return m_resumeModeEditor;
213}
214
216{
220
221 m_lastplayDelay = gCoreContext->GetDurSetting<std::chrono::seconds>("MusicLastPlayDelay", LASTPLAY_DELAY);
222 m_autoShowPlayer = (gCoreContext->GetNumSetting("MusicAutoShowPlayer", 1) > 0);
223
224}
225
226void MusicPlayer::setPlayNow(bool PlayNow)
227{
228 gCoreContext->SaveBoolSetting("MusicPreferPlayNow", PlayNow);
229}
231{
232 return gCoreContext->GetBoolSetting("MusicPreferPlayNow", false);
233}
234
235// this stops playing the playlist and plays the file pointed to by mdata
237{
239 {
240 delete m_oneshotMetadata;
241 m_oneshotMetadata = nullptr;
242 }
243
245 *m_oneshotMetadata = mdata;
246
247 play();
248}
249
250void MusicPlayer::stop(bool stopAll)
251{
252 stopDecoder();
253
254 if (m_output)
255 {
256 if (m_output->IsPaused())
257 pause();
258 m_output->Reset();
259 }
260
262 {
263 delete m_oneshotMetadata;
264 m_oneshotMetadata = nullptr;
265 }
266
267 m_isPlaying = false;
268
269 if (stopAll && getDecoder())
270 {
271 getDecoder()->removeListener(this);
272
273 // remove any listeners from the decoder
274 {
275 QMutexLocker locker(m_lock);
276 // NOLINTNEXTLINE(modernize-loop-convert)
277 for (auto it = m_listeners.begin(); it != m_listeners.end() ; ++it)
279 }
280 }
281
282 if (stopAll && m_output)
283 {
285 delete m_output;
286 m_output = nullptr;
287 }
288
289 // because we don't actually stop the audio output we have to fake a Stopped
290 // event so any listeners can act on it
292 dispatch(oe);
293
295
297}
298
300{
301 if (m_output)
302 {
305 }
306 // wake up threads
307 Decoder *decoder = getDecoder();
308 if (decoder)
309 {
310 decoder->lock();
311 decoder->cond()->wakeAll();
312 decoder->unlock();
313 }
314
316}
317
319{
320 stopDecoder();
321
323 if (!meta)
324 return;
325
326 if (meta->Filename() == METADATA_INVALID_FILENAME)
327 {
328 // put an upper limit on the number of consecutive track unavailable errors
329 if (m_errorCount >= 1000)
330 {
331 ShowOkPopup(tr("Got too many track unavailable errors. Maybe the host with the music on is off-line?"));
332 stop(true);
333 m_errorCount = 0;
334 return;
335 }
336
337 if (m_errorCount < 5)
338 {
339 MythErrorNotification n(tr("Track Unavailable"), tr("MythMusic"), QString("Cannot find file '%1'").arg(meta->Filename(false)));
341 }
342
343 m_errorCount++;
344
346 next();
347 return;
348 }
349
350 // Notify others that we are about to play
352
353 if (!m_output)
354 {
355 if (!openOutputDevice())
356 return;
357 }
358
359 if (!getDecoderHandler())
361
362 getDecoderHandler()->start(meta);
363
365}
366
368{
369 if (getDecoderHandler())
371}
372
374{
375 QString adevice;
376 QString pdevice;
377
378 adevice = gCoreContext->GetSetting("MusicAudioDevice", "default");
379 if (adevice == "default" || adevice.isEmpty())
380 adevice = gCoreContext->GetSetting("AudioOutputDevice");
381 else
382 adevice = gCoreContext->GetSetting("MusicAudioDevice");
383
384 pdevice = gCoreContext->GetBoolSetting("PassThruDeviceOverride", false) ?
385 gCoreContext->GetSetting("PassThruOutputDevice") : "auto";
386
388 adevice, pdevice, FORMAT_S16, 2, AV_CODEC_ID_NONE, 44100,
389 AUDIOOUTPUT_MUSIC, true, false,
390 gCoreContext->GetNumSetting("MusicDefaultUpmix", 0) + 1);
391
392 if (!m_output)
393 {
394 LOG(VB_GENERAL, LOG_ERR,
395 QString("MusicPlayer: Cannot open audio output device: %1").arg(adevice));
396
397 return false;
398 }
399
400 if (!m_output->GetError().isEmpty())
401 {
402 LOG(VB_GENERAL, LOG_ERR,
403 QString("MusicPlayer: Cannot open audio output device: %1").arg(adevice));
404 LOG(VB_GENERAL, LOG_ERR,
405 QString("Error was: %1").arg(m_output->GetError()));
406
407 delete m_output;
408 m_output = nullptr;
409
410 return false;
411 }
412
413 m_output->setBufferSize(256 * 1024);
414
415 m_output->addListener(this);
416
417 // add any visuals to the audio output
418 // NOLINTNEXTLINE(modernize-loop-convert)
419 for (auto it = m_visualisers.begin(); it != m_visualisers.end() ; ++it)
421
422 // add any listeners to the audio output
423 QMutexLocker locker(m_lock);
424 // NOLINTNEXTLINE(modernize-loop-convert)
425 for (auto it = m_listeners.begin(); it != m_listeners.end() ; ++it)
426 m_output->addListener(*it);
427
428 return true;
429}
430
432{
433 int currentTrack = m_currentTrack;
434
435 Playlist *playlist = getCurrentPlaylist();
436 if (nullptr == playlist)
437 return;
438
440 {
441 delete m_oneshotMetadata;
442 m_oneshotMetadata = nullptr;
443 }
444 else
445 {
446 currentTrack++;
447 }
448
449 if (currentTrack >= playlist->getTrackCount())
450 {
452 {
453 // start playing again from first track
454 currentTrack = 0;
455 }
456 else
457 {
458 stop();
459 return;
460 }
461 }
462
463 changeCurrentTrack(currentTrack);
464
465 if (getCurrentMetadata())
466 play();
467 else
468 stop();
469}
470
472{
473 int currentTrack = m_currentTrack;
474
475 Playlist *playlist = getCurrentPlaylist();
476 if (nullptr == playlist)
477 return;
478
480 {
481 delete m_oneshotMetadata;
482 m_oneshotMetadata = nullptr;
483 }
484 else
485 {
486 currentTrack--;
487 }
488
489 if (currentTrack >= 0)
490 {
491 changeCurrentTrack(currentTrack);
492
493 if (getCurrentMetadata())
494 play();
495 else
496 return;//stop();
497 }
498 else
499 {
500 // FIXME take repeat mode into account
501 return; //stop();
502 }
503}
504
506{
507 Playlist *playlist = getCurrentPlaylist();
508 if (nullptr == playlist)
509 return;
510
512 {
513 delete m_oneshotMetadata;
514 m_oneshotMetadata = nullptr;
515 stop(true);
516 return;
517 }
518
520 {
521 play();
522 return;
523 }
524 if (!m_decoderHandler->next())
525 next();
526
527 // if we don't already have a gui attached show the miniplayer if configured to do so
529 {
530 MythScreenStack *popupStack =
531 GetMythMainWindow()->GetStack("popup stack");
532
533 auto *miniplayer = new MiniPlayer(popupStack);
534
535 if (miniplayer->Create())
536 popupStack->AddScreen(miniplayer);
537 else
538 delete miniplayer;
539 }
540}
541
543{
545 {
546 play();
547
548 m_wasPlaying = false;
549 }
550}
551
553{
554 if (m_output)
555 {
557
558 savePosition();
559 stop(true);
560 }
561}
562
563void MusicPlayer::customEvent(QEvent *event)
564{
565 // handle decoderHandler events
566 if (event->type() == DecoderHandlerEvent::kReady)
567 {
568 m_errorCount = 0;
570 }
571 else if (event->type() == DecoderHandlerEvent::kMeta)
572 {
573 auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
574 if (!dhe)
575 return;
576
577 auto *mdata = new MusicMetadata(*dhe->getMetadata());
578
580
581 if (getCurrentMetadata())
582 {
583 mdata->setID(getCurrentMetadata()->ID());
584 mdata->setHostname(gCoreContext->GetMasterHostName());
585 mdata->setTrack(m_playedList.count() + 1);
586 mdata->setBroadcaster(getCurrentMetadata()->Broadcaster());
587 mdata->setChannel(getCurrentMetadata()->Channel());
588
589 getCurrentMetadata()->setTitle(mdata->Title());
590 getCurrentMetadata()->setArtist(mdata->Artist());
591 getCurrentMetadata()->setAlbum(mdata->Album());
592 getCurrentMetadata()->setTrack(mdata->Track());
593 }
594
595 m_playedList.append(mdata);
596
598 {
599 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
600
601 auto *miniplayer = new MiniPlayer(popupStack);
602
603 if (miniplayer->Create())
604 popupStack->AddScreen(miniplayer);
605 else
606 delete miniplayer;
607 }
608
609 // tell any listeners we've added a new track to the played list
611 dispatch(me);
612 }
613 // handle MythEvent events
614 else if (event->type() == MythEvent::kMythEventMessage)
615 {
616 auto *me = dynamic_cast<MythEvent*>(event);
617 if (!me)
618 return;
619
620 if (me->Message().left(13) == "MUSIC_COMMAND")
621 {
622 QStringList list = me->Message().simplified().split(' ');
623
624 if (list.size() >= 3 && list[1] == gCoreContext->GetHostName())
625 {
626 if (list[2] == "PLAY")
627 play();
628 else if (list[2] == "STOP")
629 stop();
630 else if (list[2] == "PAUSE")
631 pause();
632 else if (list[2] == "SET_VOLUME")
633 {
634 if (list.size() > 3)
635 {
636 int volume = list[3].toInt();
637 if (volume >= 0 && volume <= 100)
638 setVolume(volume);
639 }
640 }
641 else if (list[2] == "GET_VOLUME")
642 {
643 QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
644 .arg(gCoreContext->GetHostName()).arg(getVolume());
645 MythEvent me2(message);
647 }
648 else if (list[2] == "PLAY_FILE")
649 {
650 int start = me->Message().indexOf("'");
651 int end = me->Message().lastIndexOf("'");
652
653 if (start != -1 && end != -1 && start != end)
654 {
655 QString filename = me->Message().mid(start + 1, end - start - 1);
656 MusicMetadata mdata;
657 mdata.setFilename(filename);
658 playFile(mdata);
659 }
660 else
661 {
662 LOG(VB_GENERAL, LOG_ERR,
663 QString("MusicPlayer: got invalid MUSIC_COMMAND "
664 "PLAY_FILE - %1").arg(me->Message()));
665 }
666 }
667 else if (list[2] == "PLAY_URL")
668 {
669 if (list.size() == 4)
670 {
671 const QString& filename = list[3];
672 MusicMetadata mdata;
673 mdata.setFilename(filename);
674 playFile(mdata);
675 }
676 else
677 {
678 LOG(VB_GENERAL, LOG_ERR,
679 QString("MusicPlayer: got invalid MUSIC_COMMAND "
680 "PLAY_URL - %1").arg(me->Message()));
681 }
682 }
683 else if (list[2] == "PLAY_TRACK")
684 {
685 if (list.size() == 4)
686 {
687 int trackID = list[3].toInt();
689 if (mdata)
690 playFile(*mdata);
691 }
692 else
693 {
694 LOG(VB_GENERAL, LOG_ERR,
695 QString("MusicPlayer: got invalid MUSIC_COMMAND "
696 "PLAY_TRACK - %1").arg(me->Message()));
697 }
698 }
699 else if (list[2] == "GET_METADATA")
700 {
701 QString mdataStr;
703 if (mdata)
704 mdataStr = QString("%1 by %2 from %3").arg(mdata->Title(), mdata->Artist(), mdata->Album());
705 else
706 mdataStr = "Unknown Track2";
707
708 QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
709 .arg(gCoreContext->GetHostName(), mdataStr);
710 MythEvent me2(message);
712 }
713 else if (list[2] == "GET_STATUS")
714 {
715 QString statusStr = "STOPPED";
716
717 if (gPlayer->isPlaying())
718 statusStr = "PLAYING";
719 else if (gPlayer->isPaused())
720 statusStr = "PAUSED";
721
722 QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
723 .arg(gCoreContext->GetHostName(), statusStr);
724 MythEvent me2(message);
726 }
727 }
728 else
729 {
730 LOG(VB_GENERAL, LOG_ERR,
731 QString("MusicPlayer: got unknown/invalid MUSIC_COMMAND "
732 "- %1").arg(me->Message()));
733 }
734 }
735 else if (me->Message().startsWith("MUSIC_SETTINGS_CHANGED"))
736 {
737 loadSettings();
738 }
739 else if (me->Message().startsWith("MUSIC_METADATA_CHANGED"))
740 {
742 {
743 QStringList list = me->Message().simplified().split(' ');
744 if (list.size() == 2)
745 {
746 int songID = list[1].toInt();
748
749 if (mdata)
750 {
751 mdata->reloadMetadata();
752
753 // tell any listeners the metadata has changed for this track
755 }
756 }
757 }
758 }
759 else if (me->Message().startsWith("MUSIC_SCANNER_STARTED"))
760 {
761 QStringList list = me->Message().simplified().split(' ');
762 if (list.size() == 2)
763 {
764 const QString& host = list[1];
765 int id = getNotificationID(host);
767 tr("A music file scan has started on %1").arg(host),
768 tr("Music File Scanner"),
769 tr("This may take a while I'll give a shout when finished"));
770 }
771 }
772 else if (me->Message().startsWith("MUSIC_SCANNER_FINISHED"))
773 {
774 QStringList list = me->Message().simplified().split(' ');
775 if (list.size() == 6)
776 {
777 const QString& host = list[1];
778 int id = getNotificationID(host);
779 int totalTracks = list[2].toInt();
780 int newTracks = list[3].toInt();
781 int totalCoverart = list[4].toInt();
782 int newCoverart = list[5].toInt();
783
784 QString summary = QString("Total Tracks: %2, new tracks: %3,\nTotal Coverart: %4, New CoverArt %5")
785 .arg(totalTracks).arg(newTracks).arg(totalCoverart).arg(newCoverart);
787 tr("A music file scan has finished on %1").arg(host),
788 tr("Music File Scanner"), summary);
789
791 }
792 }
793 else if (me->Message().startsWith("MUSIC_SCANNER_ERROR"))
794 {
795 QStringList list = me->Message().simplified().split(' ');
796 if (list.size() == 3)
797 {
798 const QString& host = list[1];
799 const QString& error = list[2];
800 int id = getNotificationID(host);
801
802 if (error == "Already_Running")
803 {
804 sendNotification(id, tr(""),
805 tr("Music File Scanner"),
806 tr("Can't run the music file scanner because it is already running on %1").arg(host));
807 }
808 else if (error == "Stalled")
809 {
810 sendNotification(id, tr(""),
811 tr("Music File Scanner"),
812 tr("The music file scanner has been running for more than 60 minutes on %1.\nResetting and trying again")
813 .arg(host));
814 }
815 }
816 }
817 }
818
819 if (event->type() == OutputEvent::kError)
820 {
821 auto *aoe = dynamic_cast<OutputEvent *>(event);
822
823 if (!aoe)
824 return;
825
826 LOG(VB_GENERAL, LOG_ERR, QString("Audio Output Error: %1").arg(*aoe->errorMessage()));
827
828 MythErrorNotification n(tr("Audio Output Error"), tr("MythMusic"), *aoe->errorMessage());
830
831 m_errorCount++;
832
833 if (m_errorCount <= 5)
834 nextAuto();
835 else
836 {
837 m_errorCount = 0;
838 stop(true);
839 }
840 }
841 else if (event->type() == DecoderEvent::kError)
842 {
843 auto *dxe = dynamic_cast<DecoderEvent *>(event);
844
845 if (!dxe)
846 return;
847
848 LOG(VB_GENERAL, LOG_ERR, QString("Decoder Error: %2").arg(*dxe->errorMessage()));
849
850 MythErrorNotification n(tr("Decoder Error"), tr("MythMusic"), *dxe->errorMessage());
852
853 m_errorCount++;
854
856 nextAuto();
857 else
858 {
859 m_errorCount = 0;
860 stop(true);
861 }
862 }
863 else if (event->type() == DecoderHandlerEvent::kError)
864 {
865 auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
866
867 if (!dhe)
868 return;
869
870 LOG(VB_GENERAL, LOG_ERR, QString("Decoder Handler Error - %1").arg(*dhe->getMessage()));
871
872 MythErrorNotification n(tr("Decoder Handler Error"), tr("MythMusic"), *dhe->getMessage());
874
875 m_errorCount++;
876
877 if (m_errorCount <= 5)
878 nextAuto();
879 else
880 {
881 m_errorCount = 0;
882 stop(true);
883 }
884 }
885 else if (event->type() == OutputEvent::kInfo)
886 {
887 auto *oe = dynamic_cast<OutputEvent*>(event);
888
889 if (!oe)
890 return;
891
893 m_currentTime = oe->elapsedSeconds();
894 else
895 m_currentTime = oe->elapsedSeconds() - m_lastTrackStart;
896
898 {
899 // we update the lastplay and playcount after playing
900 // for m_lastplayDelay seconds or half the total track time
901 if ((getCurrentMetadata() &&
902 m_currentTime > duration_cast<std::chrono::seconds>(getCurrentMetadata()->Length()) / 2) ||
904 {
906 }
907 }
908
909 // update the current tracks time in the last played list
911 {
912 if (!m_playedList.isEmpty() && m_currentTime > 0s)
913 {
914 m_playedList.last()->setLength(m_currentTime);
915 // this will update any track lengths displayed on screen
917 }
918 }
919 }
920 else if (event->type() == DecoderEvent::kFinished)
921 {
923 {
924 delete m_oneshotMetadata;
925 m_oneshotMetadata = nullptr;
926 stop(true);
927 }
928 else
929 {
930 auto metadataSecs = duration_cast<std::chrono::seconds>(getCurrentMetadata()->Length());
932 m_currentTime != metadataSecs)
933 {
934 LOG(VB_GENERAL, LOG_NOTICE, QString("MusicPlayer: Updating track length was %1s, should be %2s")
935 .arg(metadataSecs.count()).arg(m_currentTime.count()));
936
939
940 // this will update any track lengths displayed on screen
942
943 // this will force the playlist stats to update
945 dispatch(me);
946 }
947
948 nextAuto();
949 }
950 }
951 else if (event->type() == DecoderEvent::kStopped)
952 {
953 }
954 else if (event->type() == DecoderHandlerEvent::kBufferStatus)
955 {
956 auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
957 if (!dhe)
958 return;
959
961 }
962
963 QObject::customEvent(event);
964}
965
966void MusicPlayer::getBufferStatus(int *bufferAvailable, int *bufferSize) const
967{
968 *bufferAvailable = m_bufferAvailable;
969 *bufferSize = m_bufferSize;
970}
971
973{
974 if (m_playMode == mode)
975 return;
976
977 savePosition();
978
979 m_playMode = mode;
980
981 loadPlaylist();
982}
983
985{
987 {
989 {
990 int bookmark = gCoreContext->GetNumSetting("MusicRadioBookmark", 0);
991 Playlist *playlist = getCurrentPlaylist();
992 if ((bookmark < 0) ||
993 (playlist && bookmark >= playlist->getTrackCount()))
994 bookmark = 0;
995
996 m_currentTrack = bookmark;
997 }
998 else
999 {
1000 m_currentTrack = 0;
1001 }
1002
1004 }
1005 else
1006 {
1008 {
1009 int bookmark = gCoreContext->GetNumSetting("MusicBookmark", 0);
1010 Playlist *playlist = getCurrentPlaylist();
1011 if ((bookmark < 0) ||
1012 (playlist && bookmark >= getCurrentPlaylist()->getTrackCount()))
1013 bookmark = 0;
1014
1015 m_currentTrack = bookmark;
1016 }
1017 else
1018 {
1019 m_currentTrack = 0;
1020 }
1021 }
1022}
1023
1025{
1027
1028 // create the radio playlist
1032
1033 for (int x = 0; x < list->count(); x++)
1034 {
1035 MusicMetadata *mdata = list->at(x);
1037
1038 if (mdata->ID() == id)
1039 m_currentTrack = x;
1040 }
1041
1043}
1044
1045void MusicPlayer::moveTrackUpDown(bool moveUp, int whichTrack)
1046{
1047 Playlist *playlist = getCurrentPlaylist();
1048 if (nullptr == playlist)
1049 return;
1050
1051 if (moveUp && whichTrack <= 0)
1052 return;
1053
1054 if (!moveUp && whichTrack >= getCurrentPlaylist()->getTrackCount() - 1)
1055 return;
1056
1057 MusicMetadata *currTrack = playlist->getSongAt(m_currentTrack);
1058
1059 playlist->moveTrackUpDown(moveUp, whichTrack);
1060
1061 m_currentTrack = playlist->getTrackPosition(currTrack->ID());
1062}
1063
1065{
1066 changeCurrentTrack(pos);
1067
1068 if (!getCurrentMetadata())
1069 {
1070 stop();
1071 return false;
1072 }
1073
1074 play();
1075
1076 return true;
1077}
1078
1080{
1081 // can't save a bookmark if we don't know what we are playing
1082 if (!getCurrentMetadata())
1083 return;
1084
1086 {
1087 gCoreContext->SaveSetting("MusicRadioBookmark", getCurrentMetadata()->ID());
1088 }
1089 else
1090 {
1091 gCoreContext->SaveSetting("MusicBookmark", getCurrentMetadata()->ID());
1092 gCoreContext->SaveDurSetting("MusicBookmarkPosition", m_currentTime);
1093 }
1094}
1095
1097{
1098 // if we are switching views we don't wont to restore the position
1099 if (!m_allowRestorePos)
1100 return;
1101
1102 m_currentTrack = 0;
1103 uint id = -1;
1104
1106 {
1108 id = gCoreContext->GetNumSetting("MusicRadioBookmark", 0);
1109 else
1110 id = gCoreContext->GetNumSetting("MusicBookmark", 0);
1111 }
1112
1113 Playlist *playlist = gPlayer->getCurrentPlaylist();
1114 if (playlist)
1115 {
1116 for (int x = 0; x < playlist->getTrackCount(); x++)
1117 {
1118 if (playlist->getSongAt(x) &&
1119 playlist->getSongAt(x)->ID() == id)
1120 {
1121 m_currentTrack = x;
1122 break;
1123 }
1124 }
1125 }
1126
1127 if (getCurrentMetadata())
1128 {
1130 play();
1131
1133 seek(gCoreContext->GetDurSetting<std::chrono::seconds>("MusicBookmarkPosition", 0s));
1134 }
1135}
1136
1137void MusicPlayer::seek(std::chrono::seconds pos)
1138{
1139 if (m_output)
1140 {
1141 Decoder *decoder = getDecoder();
1142 if (decoder && decoder->isRunning())
1143 decoder->seek(pos.count());
1144
1145 m_output->SetTimecode(pos);
1146 }
1147}
1148
1150{
1151 if (m_canShowPlayer)
1152 {
1153 MythScreenStack *popupStack =
1154 GetMythMainWindow()->GetStack("popup stack");
1155
1156 auto *miniplayer = new MiniPlayer(popupStack);
1157
1158 if (miniplayer->Create())
1159 popupStack->AddScreen(miniplayer);
1160 else
1161 delete miniplayer;
1162 }
1163}
1164
1167{
1168 Playlist *playlist = getCurrentPlaylist();
1169 if (nullptr == playlist)
1170 return;
1171
1172 // check to see if we need to save the current tracks volatile metadata (playcount, last played etc)
1174
1175 m_currentTrack = trackNo;
1176
1177 // sanity check the current track
1178 if (m_currentTrack < 0 || m_currentTrack >= playlist->getTrackCount())
1179 {
1180 LOG(VB_GENERAL, LOG_ERR,
1181 QString("MusicPlayer: asked to set the current track to an invalid track no. %1")
1182 .arg(trackNo));
1183 m_currentTrack = -1;
1184 return;
1185 }
1186}
1187
1190{
1192 return m_oneshotMetadata;
1193
1194 Playlist *playlist = getCurrentPlaylist();
1195 if (!playlist || !playlist->getSongAt(m_currentTrack))
1196 return nullptr;
1197
1198 return playlist->getSongAt(m_currentTrack);
1199}
1200
1203{
1205 return nullptr;
1206
1208 return getCurrentMetadata();
1209
1210 Playlist *playlist = gPlayer->getCurrentPlaylist();
1211 if (!playlist || !playlist->getSongAt(m_currentTrack))
1212 return nullptr;
1213
1215 return getCurrentMetadata();
1216
1217 // if we are not playing the last track then just return the next track
1218 if (m_currentTrack < playlist->getTrackCount() - 1)
1219 return playlist->getSongAt(m_currentTrack + 1);
1220
1221 // if we are playing the last track then we need to take the
1222 // repeat mode into account
1223 if (m_repeatMode == REPEAT_ALL)
1224 return playlist->getSongAt(0);
1225 return nullptr;
1226}
1227
1229{
1230 switch (m_repeatMode)
1231 {
1232 case REPEAT_OFF:
1234 break;
1235 case REPEAT_TRACK:
1237 break;
1238 case REPEAT_ALL:
1239 default:
1241 break;
1242 }
1243
1244 return m_repeatMode;
1245}
1246
1248{
1249 switch (m_shuffleMode)
1250 {
1251 case SHUFFLE_OFF:
1253 break;
1254 case SHUFFLE_RANDOM:
1256 break;
1259 break;
1260 case SHUFFLE_ALBUM:
1262 break;
1263 case SHUFFLE_ARTIST:
1264 default:
1266 break;
1267 }
1268
1270
1271 return m_shuffleMode;
1272}
1273
1275{
1276 int curTrackID = -1;
1277 if (getCurrentMetadata())
1278 curTrackID = getCurrentMetadata()->ID();
1279
1280 // only save the mode if we are playing tracks
1282 m_shuffleMode = mode;
1283
1284 Playlist *playlist = getCurrentPlaylist();
1285 if (nullptr == playlist)
1286 return;
1287
1288 playlist->shuffleTracks(mode);
1289
1290 if (curTrackID != -1)
1291 {
1292 for (int x = 0; x < playlist->getTrackCount(); x++)
1293 {
1294 MusicMetadata *mdata = playlist->getSongAt(x);
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 auto *thread = new SendStringListThread(strList);
1334 MThreadPool::globalInstance()->start(thread, "UpdateVolatile");
1335 }
1336
1338 }
1339 }
1340}
1341
1342void 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
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 {
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
1458void 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 Playlist *playlist = getCurrentPlaylist();
1489 if (!playlist || playlist->getTrackCount() == 0)
1490 {
1491 m_currentTrack = -1;
1492 if (isPlaying())
1493 stop(true);
1494 return;
1495 }
1496
1497 int trackPos = -1;
1498
1499 // make sure the current playing track is still valid
1500 if (isPlaying() && getDecoderHandler())
1501 {
1502 for (int x = 0; x < playlist->getTrackCount(); x++)
1503 {
1504 if (playlist->getSongAt(x)->ID() == getDecoderHandler()->getMetadata().ID())
1505 {
1506 trackPos = x;
1507 break;
1508 }
1509 }
1510 }
1511
1512 if (trackPos != m_currentTrack)
1513 m_currentTrack = trackPos;
1514
1515 if (!getCurrentMetadata())
1516 {
1517 m_currentTrack = -1;
1518 stop(true);
1519 }
1520}
1521
1523{
1525 dispatch(me);
1526}
1527
1529{
1532
1533 // add any listeners to the decoderHandler
1534 {
1535 QMutexLocker locker(m_lock);
1536 // NOLINTNEXTLINE(modernize-loop-convert)
1537 for (auto it = m_listeners.begin(); it != m_listeners.end() ; ++it)
1539 }
1540}
1541
1543{
1544 Decoder *decoder = getDecoder();
1545 if (!decoder)
1546 return;
1547
1548 LOG(VB_PLAYBACK, LOG_INFO, QString ("decoder handler is ready, decoding %1")
1549 .arg(decoder->getURL()));
1550
1551#ifdef HAVE_CDIO
1552 auto *cddecoder = dynamic_cast<CdDecoder*>(decoder);
1553 if (cddecoder)
1554 cddecoder->setDevice(gCDdevice);
1555#endif
1556
1557 // Decoder thread can't be running while being initialized
1558 if (decoder->isRunning())
1559 {
1560 decoder->stop();
1561 decoder->wait();
1562 }
1563
1564 decoder->setOutput(m_output);
1565 //decoder-> setBlockSize(2 * 1024);
1566 decoder->addListener(this);
1567
1568 // add any listeners to the decoder
1569 {
1570 QMutexLocker locker(m_lock);
1571 // NOLINTNEXTLINE(modernize-loop-convert)
1572 for (auto it = m_listeners.begin(); it != m_listeners.end() ; ++it)
1573 decoder->addListener(*it);
1574 }
1575
1576 m_currentTime = 0s;
1577 m_lastTrackStart = 0s;
1578
1579 // NOLINTNEXTLINE(modernize-loop-convert)
1580 for (auto it = m_visualisers.begin(); it != m_visualisers.end() ; ++it)
1581 {
1582 //m_output->addVisual((MythTV::Visual*)(*it));
1583 //(*it)->setDecoder(decoder);
1584 //m_visual->setOutput(m_output);
1585 }
1586
1587 if (decoder->initialize())
1588 {
1589 if (m_output)
1591
1592 decoder->start();
1593
1595 gCoreContext->GetDurSetting<std::chrono::seconds>("MusicBookmarkPosition", 0s) > 0s)
1596 {
1597 seek(gCoreContext->GetDurSetting<std::chrono::seconds>("MusicBookmarkPosition", 0s));
1598 gCoreContext->SaveDurSetting("MusicBookmarkPosition", 0s);
1599 }
1600
1601 m_isPlaying = true;
1602 m_updatedLastplay = false;
1603 }
1604 else
1605 {
1606 LOG(VB_PLAYBACK, LOG_ERR, QString("Cannot initialise decoder for %1")
1607 .arg(decoder->getURL()));
1608 return;
1609 }
1610
1611 // tell any listeners we've started playing a new track
1613 dispatch(me);
1614}
1615
1617{
1618 MusicMetadata *mdata = gMusicData->m_all_music->getMetadata(trackID);
1619 if (mdata)
1620 {
1621 Playlist *playlist = getCurrentPlaylist();
1622 if (nullptr == playlist)
1623 return;
1624 int trackPos = playlist->getTrackPosition(mdata->ID());
1625 if (m_currentTrack > 0 && m_currentTrack >= trackPos)
1627
1628 playlist->removeTrack(trackID);
1629 }
1630}
1631
1632void MusicPlayer::addTrack(int trackID, bool updateUI)
1633{
1634 getCurrentPlaylist()->addTrack(trackID, updateUI);
1635}
1636
1638{
1640 return nullptr;
1641
1643 {
1645 }
1647}
1648
1650{
1652}
1653
1655{
1656 if (!m_notificationMap.contains(hostname))
1657 m_notificationMap.insert(hostname, GetNotificationCenter()->Register(this));
1658
1660}
1661
1662void MusicPlayer::sendNotification(int notificationID, const QString &title, const QString &author, const QString &desc)
1663{
1664 QString image = "musicscanner.png";
1665 if (!GetMythUI()->FindThemeFile(image))
1666 LOG(VB_GENERAL, LOG_ERR, "MusicPlayer: sendNotification failed to find the 'musicscanner.png' image");
1667
1668 DMAP map;
1669 map["asar"] = title;
1670 map["minm"] = author;
1671 map["asal"] = desc;
1672
1673 auto *n = new MythImageNotification(MythNotification::kInfo, image, map);
1674
1675 n->SetId(notificationID);
1676 n->SetParent(this);
1677 n->SetDuration(5s);
1678 n->SetFullScreen(false);
1680 delete n;
1681}
@ FORMAT_S16
@ AUDIOOUTPUT_MUSIC
Definition: audiosettings.h:24
MusicMetadata * getMetadata(int an_id)
StreamList * getStreams(void)
virtual bool IsPaused(void) const =0
virtual void SetTimecode(std::chrono::milliseconds timecode)=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:60
virtual void PauseUntilBuffered(void)=0
virtual void Reset(void)=0
virtual void Pause(bool paused)=0
virtual void SetStretchFactor(float factor)
QString GetError(void) const
Definition: audiooutput.h:146
void setDevice(const QString &dev)
Definition: cddecoder.cpp:104
static const Type kStopped
Definition: decoder.h:46
static const Type kFinished
Definition: decoder.h:47
static const Type kError
Definition: decoder.h:48
Events sent by the DecoderHandler and it's helper classes.
static const Type kBufferStatus
static const Type kError
void getBufferStatus(int *available, int *maxSize) const
static const Type kReady
static const Type kMeta
Class for starting stream decoding.
void start(MusicMetadata *mdata)
virtual void lock(void)
Definition: decoder.h:85
virtual void stop()=0
QString getURL(void) const
Definition: decoder.h:91
virtual void unlock(void)
Definition: decoder.h:86
virtual bool initialize()=0
virtual void seek(double)=0
QWaitCondition * cond()
Definition: decoder.h:89
void setOutput(AudioOutput *o)
Definition: decoder.cpp:44
static MThreadPool * globalInstance(void)
void start(QRunnable *runnable, const QString &debugName, int priority=0)
bool isRunning(void) const
Definition: mthread.cpp:263
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
bool m_initialized
Definition: musicdata.h:54
void reloadMusic(void) const
reload music after a scan, rip or import
Definition: musicdata.cpp:61
AllMusic * m_all_music
Definition: musicdata.h:52
PlaylistContainer * m_all_playlists
Definition: musicdata.h:51
AllStream * m_all_streams
Definition: musicdata.h:53
void persist(void)
void reloadMetadata(void)
QDateTime LastPlay() const
QString Hostname(void)
void setLength(T llength)
QString Title() const
void setTitle(const QString &ltitle, const QString &ltitle_sort=nullptr)
void setFilename(const QString &lfilename)
IdType ID() const
QString Filename(bool find=true)
QString Artist() const
void setAlbum(const QString &lalbum, const QString &lalbum_sort=nullptr)
uint32_t IdType
Definition: musicmetadata.h:91
void setTrack(int ltrack)
QString Album() const
void setArtist(const QString &lartist, const QString &lartist_sort=nullptr)
void dumpToDatabase(void)
static const Type kPlayedTracksChangedEvent
Definition: musicplayer.h:50
static const Type kCDChangedEvent
Definition: musicplayer.h:48
static const Type kMetadataChangedEvent
Definition: musicplayer.h:45
static const Type kTrackStatsChangedEvent
Definition: musicplayer.h:46
static const Type kTrackUnavailableEvent
Definition: musicplayer.h:43
static const Type kAlbumArtChangedEvent
Definition: musicplayer.h:47
static const Type kVolumeChangeEvent
Definition: musicplayer.h:40
static const Type kAllTracksRemovedEvent
Definition: musicplayer.h:44
static const Type kTrackAddedEvent
Definition: musicplayer.h:41
static const Type kTrackChangeEvent
Definition: musicplayer.h:39
static const Type kPlaylistChangedEvent
Definition: musicplayer.h:49
static const Type kTrackRemovedEvent
Definition: musicplayer.h:42
MusicMetadata * getCurrentMetadata(void)
get the metadata for the current track in the playlist
void loadSettings(void)
bool m_updatedLastplay
Definition: musicplayer.h:239
void loadPlaylist(void)
void sendMetadataChangedEvent(int trackID)
void setSpeed(float speed)
void sendVolumeChangedEvent(void)
ShuffleMode toggleShuffleMode(void)
bool m_isAutoplay
Definition: musicplayer.h:235
void savePosition(void)
QSet< QObject * > m_visualisers
Definition: musicplayer.h:231
void playlistChanged(int playlistID)
void showMiniPlayer(void) const
void StopPlayback(void)
void next(void)
void sendNotification(int notificationID, const QString &title, const QString &author, const QString &desc)
@ SHUFFLE_INTELLIGENT
Definition: musicplayer.h:177
std::chrono::seconds m_lastTrackStart
Definition: musicplayer.h:258
uint getVolume(void) const
void decVolume(void)
void setPlayMode(PlayMode mode)
void setVolume(int volume)
void removeTrack(int trackID)
~MusicPlayer(void) override
Definition: musicplayer.cpp:89
bool m_canShowPlayer
Definition: musicplayer.h:236
MusicMetadata * m_oneshotMetadata
Definition: musicplayer.h:226
void updateVolatileMetadata(void)
void stop(bool stopAll=false)
void activePlaylistChanged(int trackID, bool deleted)
DecoderHandler * m_decoderHandler
Definition: musicplayer.h:229
void loadStreamPlaylist(void)
DecoderHandler * getDecoderHandler(void)
Definition: musicplayer.h:127
void sendCDChangedEvent(void)
AudioOutput * m_output
Definition: musicplayer.h:228
void play(void)
bool isPaused(void)
Definition: musicplayer.h:110
@ PLAYMODE_TRACKSEDITOR
Definition: musicplayer.h:72
QList< MusicMetadata * > m_playedList
Definition: musicplayer.h:257
void getBufferStatus(int *bufferAvailable, int *bufferSize) const
PlayMode m_playMode
Definition: musicplayer.h:233
std::chrono::seconds m_lastplayDelay
Definition: musicplayer.h:242
QMap< QString, int > m_notificationMap
Definition: musicplayer.h:254
void customEvent(QEvent *event) override
void previous(void)
int getNotificationID(const QString &hostname)
void stopDecoder(void)
void setupDecoderHandler(void)
MusicPlayer(QObject *parent)
Definition: musicplayer.cpp:56
std::chrono::seconds m_currentTime
Definition: musicplayer.h:224
void moveTrackUpDown(bool moveUp, int whichTrack)
RepeatMode m_repeatMode
Definition: musicplayer.h:245
void restorePosition(void)
ResumeMode getResumeMode(void)
int m_bufferSize
Definition: musicplayer.h:260
int m_currentTrack
Definition: musicplayer.h:223
void updateLastplay(void)
void nextAuto(void)
void addTrack(int trackID, bool updateUI)
void removeListener(QObject *listener)
bool isPlaying(void) const
Definition: musicplayer.h:109
void removeVisual(MainVisual *visual)
bool openOutputDevice(void)
bool setCurrentTrackPos(int pos)
void playFile(const MusicMetadata &mdata)
void incVolume(void)
AudioOutput * getOutput(void)
Definition: musicplayer.h:128
ResumeMode m_resumeModeRadio
Definition: musicplayer.h:248
float m_playSpeed
Definition: musicplayer.h:250
bool m_wasPlaying
Definition: musicplayer.h:238
void sendTrackStatsChangedEvent(int trackID)
void seek(std::chrono::seconds pos)
bool hasClient(void)
Definition: musicplayer.h:112
static StreamList * getStreamList(void)
void addListener(QObject *listener)
int m_errorCount
Definition: musicplayer.h:262
void decoderHandlerReady(void)
static bool getPlayNow(void)
void pause(void)
void setShuffleMode(ShuffleMode mode)
bool m_autoShowPlayer
Definition: musicplayer.h:237
Playlist * getCurrentPlaylist(void)
static void setPlayNow(bool PlayNow)
whether we prefer Play Now over Add Tracks
void sendTrackUnavailableEvent(int trackID)
void setRepeatMode(RepeatMode mode)
Definition: musicplayer.h:192
bool m_isPlaying
Definition: musicplayer.h:234
void toggleMute(void)
ResumeMode m_resumeModeEditor
Definition: musicplayer.h:247
bool isMuted(void) const
Definition: musicplayer.h:89
ShuffleMode m_shuffleMode
Definition: musicplayer.h:244
Decoder * getDecoder(void)
Definition: musicplayer.h:126
int m_bufferAvailable
Definition: musicplayer.h:259
MusicMetadata * getNextMetadata(void)
get the metadata for the next track in the playlist
MuteState getMuteState(void) const
bool m_allowRestorePos
Definition: musicplayer.h:240
RepeatMode toggleRepeatMode(void)
void changeCurrentTrack(int trackNo)
change the current track to the given track
void StartPlayback(void)
ResumeMode m_resumeModePlayback
Definition: musicplayer.h:246
void addVisual(MainVisual *visual)
void toMap(InfoMap &infoMap) const
void sendAlbumArtChangedEvent(int trackID)
void UnregisterForPlayback(QObject *sender)
Unregister sender from being called when TVPlaybackAboutToStart signal is emitted.
void emitTVPlaybackStopped(void)
void SaveBoolSetting(const QString &key, bool newValue)
QString GetHostName(void)
void SaveSetting(const QString &key, int newValue)
void RegisterForPlayback(QObject *sender, PlaybackStartCb method)
Register sender for TVPlaybackAboutToStart signal.
std::enable_if_t< std::chrono::__is_duration< T >::value, void > SaveDurSetting(const QString &key, T newValue)
void TVPlaybackAborted(void)
QString GetSetting(const QString &key, const QString &defaultval="")
void TVPlaybackStopped(void)
bool InWantingPlayback(void)
Returns true if a client has requested playback.
void WantingPlayback(QObject *sender)
All the objects that have registered using MythCoreContext::RegisterForPlayback but sender will be ca...
void dispatch(const MythEvent &event)
int GetNumSetting(const QString &key, int defaultval=0)
QString GetMasterHostName(void)
std::enable_if_t< std::chrono::__is_duration< T >::value, T > GetDurSetting(const QString &key, T defaultval=T::zero())
bool GetBoolSetting(const QString &key, bool defaultval=false)
This class is used as a container for messages.
Definition: mythevent.h:17
const QString & Message() const
Definition: mythevent.h:65
static const Type kMythEventMessage
Definition: mythevent.h:79
void PauseIdleTimer(bool Pause)
Pause the idle timeout timer.
MythScreenStack * GetStack(const QString &Stackname)
void UnRegister(void *from, int id, bool closeimemdiately=false)
Unregister the client.
bool Queue(const MythNotification &notification)
Queue a notification Queue() is thread-safe and can be called from anywhere.
static const Type kInfo
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
void addListener(QObject *listener)
Add a listener to the observable.
QSet< QObject * > m_listeners
bool hasListeners(void)
void removeListener(QObject *listener)
Remove a listener to the observable.
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
static const Type kInfo
Definition: output.h:65
static const Type kStopped
Definition: output.h:67
static const Type kError
Definition: output.h:68
void removeVisual(MythTV::Visual *v)
Definition: output.cpp:42
void setBufferSize(unsigned int sz)
Definition: output.h:114
void addVisual(MythTV::Visual *v)
Definition: output.cpp:35
Playlist * getStreamPlaylist(void)
Playlist * getActive(void)
int getTrackPosition(MusicMetadata::IdType trackID)
Definition: playlist.h:90
void enableSaves(void)
Definition: playlist.h:103
void removeAllTracks(void)
Definition: playlist.cpp:89
int getTrackCount(void)
Definition: playlist.h:82
void disableSaves(void)
whether any changes should be saved to the DB
Definition: playlist.h:102
void moveTrackUpDown(bool flag, int where_its_at)
Definition: playlist.cpp:130
void shuffleTracks(MusicPlayer::ShuffleMode mode)
Definition: playlist.cpp:157
MusicMetadata * getSongAt(int pos) const
Definition: playlist.cpp:1096
void removeTrack(MusicMetadata::IdType trackID)
Definition: playlist.cpp:119
void addTrack(MusicMetadata::IdType trackID, bool update_display)
Given a tracks ID, add that track to this playlist.
Definition: playlist.cpp:63
send a message to the master BE without blocking the UI thread
Definition: musicdata.h:22
virtual MuteState GetMuteState(void) const
Definition: volumebase.cpp:150
virtual void SetCurrentVolume(int value)
Definition: volumebase.cpp:123
virtual uint GetCurrentVolume(void) const
Definition: volumebase.cpp:118
virtual void ToggleMute(void)
Definition: volumebase.cpp:144
virtual void AdjustCurrentVolume(int change)
Definition: volumebase.cpp:132
unsigned int uint
Definition: freesurround.h:24
MusicData * gMusicData
Definition: musicdata.cpp:23
QList< MusicMetadata * > StreamList
static constexpr const char * METADATA_INVALID_FILENAME
Definition: musicmetadata.h:79
MusicPlayer * gPlayer
Definition: musicplayer.cpp:38
QString gCDdevice
Definition: musicplayer.cpp:39
static constexpr std::chrono::seconds LASTPLAY_DELAY
Definition: musicplayer.h:14
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythDB * GetMythDB(void)
Definition: mythdb.cpp:51
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythNotificationCenter * GetNotificationCenter(void)
MythMainWindow * GetMythMainWindow(void)
QMap< QString, QString > DMAP
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
MythUIHelper * GetMythUI()
@ ISODate
Default UTC.
Definition: mythdate.h:17
def error(message)
Definition: smolt.py:409
string hostname
Definition: caa.py:17
MuteState
Definition: volumebase.h:8
@ kMuteOff
Definition: volumebase.h:9