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
291 AudioOutput::Event oe(AudioOutput::Event::kStopped);
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 == nullptr || !m_output->isConfigured())
393 {
394 LOG(VB_GENERAL, LOG_ERR,
395 QString("MusicPlayer: Cannot open audio output device: %1").arg(adevice));
396
397 delete m_output;
398 m_output = nullptr;
399
400 return false;
401 }
402
403 m_output->addListener(this);
404
405 // add any visuals to the audio output
406 // NOLINTNEXTLINE(modernize-loop-convert)
407 for (auto it = m_visualisers.begin(); it != m_visualisers.end() ; ++it)
409
410 // add any listeners to the audio output
411 QMutexLocker locker(m_lock);
412 // NOLINTNEXTLINE(modernize-loop-convert)
413 for (auto it = m_listeners.begin(); it != m_listeners.end() ; ++it)
414 m_output->addListener(*it);
415
416 return true;
417}
418
420{
421 int currentTrack = m_currentTrack;
422
423 Playlist *playlist = getCurrentPlaylist();
424 if (nullptr == playlist)
425 return;
426
428 {
429 delete m_oneshotMetadata;
430 m_oneshotMetadata = nullptr;
431 }
432 else
433 {
434 currentTrack++;
435 }
436
437 if (currentTrack >= playlist->getTrackCount())
438 {
440 {
441 // start playing again from first track
442 currentTrack = 0;
443 }
444 else
445 {
446 stop();
447 return;
448 }
449 }
450
451 changeCurrentTrack(currentTrack);
452
453 if (getCurrentMetadata())
454 play();
455 else
456 stop();
457}
458
460{
461 int currentTrack = m_currentTrack;
462
463 Playlist *playlist = getCurrentPlaylist();
464 if (nullptr == playlist)
465 return;
466
468 {
469 delete m_oneshotMetadata;
470 m_oneshotMetadata = nullptr;
471 }
472 else
473 {
474 currentTrack--;
475 }
476
477 if (currentTrack >= 0)
478 {
479 changeCurrentTrack(currentTrack);
480
481 if (getCurrentMetadata())
482 play();
483 else
484 return;//stop();
485 }
486 else
487 {
488 // FIXME take repeat mode into account
489 return; //stop();
490 }
491}
492
494{
495 Playlist *playlist = getCurrentPlaylist();
496 if (nullptr == playlist)
497 return;
498
500 {
501 delete m_oneshotMetadata;
502 m_oneshotMetadata = nullptr;
503 stop(true);
504 return;
505 }
506
508 {
509 play();
510 return;
511 }
512 if (!m_decoderHandler->next())
513 next();
514
515 // if we don't already have a gui attached show the miniplayer if configured to do so
517 {
518 MythScreenStack *popupStack =
519 GetMythMainWindow()->GetStack("popup stack");
520
521 auto *miniplayer = new MiniPlayer(popupStack);
522
523 if (miniplayer->Create())
524 popupStack->AddScreen(miniplayer);
525 else
526 delete miniplayer;
527 }
528}
529
531{
533 {
534 play();
535
536 m_wasPlaying = false;
537 }
538}
539
541{
542 if (m_output)
543 {
545
546 savePosition();
547 stop(true);
548 }
549}
550
551void MusicPlayer::customEvent(QEvent *event)
552{
553 // handle decoderHandler events
554 if (event->type() == DecoderHandlerEvent::kReady)
555 {
556 m_errorCount = 0;
558 }
559 else if (event->type() == DecoderHandlerEvent::kMeta)
560 {
561 auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
562 if (!dhe)
563 return;
564
565 auto *mdata = new MusicMetadata(*dhe->getMetadata());
566
568
569 if (getCurrentMetadata())
570 {
571 mdata->setID(getCurrentMetadata()->ID());
572 mdata->setHostname(gCoreContext->GetMasterHostName());
573 mdata->setTrack(m_playedList.count() + 1);
574 mdata->setBroadcaster(getCurrentMetadata()->Broadcaster());
575 mdata->setChannel(getCurrentMetadata()->Channel());
576
577 getCurrentMetadata()->setTitle(mdata->Title());
578 getCurrentMetadata()->setArtist(mdata->Artist());
579 getCurrentMetadata()->setAlbum(mdata->Album());
580 getCurrentMetadata()->setTrack(mdata->Track());
581 }
582
583 m_playedList.append(mdata);
584
586 {
587 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
588
589 auto *miniplayer = new MiniPlayer(popupStack);
590
591 if (miniplayer->Create())
592 popupStack->AddScreen(miniplayer);
593 else
594 delete miniplayer;
595 }
596
597 // tell any listeners we've added a new track to the played list
599 dispatch(me);
600 }
601 // handle MythEvent events
602 else if (event->type() == MythEvent::kMythEventMessage)
603 {
604 auto *me = dynamic_cast<MythEvent*>(event);
605 if (!me)
606 return;
607
608 if (me->Message().left(13) == "MUSIC_COMMAND")
609 {
610 QStringList list = me->Message().simplified().split(' ');
611
612 if (list.size() >= 3 && list[1] == gCoreContext->GetHostName())
613 {
614 if (list[2] == "PLAY")
615 {
616 play();
617 }
618 else if (list[2] == "STOP")
619 {
620 stop();
621 }
622 else if (list[2] == "PAUSE")
623 {
624 pause();
625 }
626 else if (list[2] == "SET_VOLUME")
627 {
628 if (list.size() > 3)
629 {
630 int volume = list[3].toInt();
631 if (volume >= 0 && volume <= 100)
632 setVolume(volume);
633 }
634 }
635 else if (list[2] == "GET_VOLUME")
636 {
637 QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
638 .arg(gCoreContext->GetHostName()).arg(getVolume());
639 MythEvent me2(message);
641 }
642 else if (list[2] == "PLAY_FILE")
643 {
644 int start = me->Message().indexOf("'");
645 int end = me->Message().lastIndexOf("'");
646
647 if (start != -1 && end != -1 && start != end)
648 {
649 QString filename = me->Message().mid(start + 1, end - start - 1);
650 MusicMetadata mdata;
651 mdata.setFilename(filename);
652 playFile(mdata);
653 }
654 else
655 {
656 LOG(VB_GENERAL, LOG_ERR,
657 QString("MusicPlayer: got invalid MUSIC_COMMAND "
658 "PLAY_FILE - %1").arg(me->Message()));
659 }
660 }
661 else if (list[2] == "PLAY_URL")
662 {
663 if (list.size() == 4)
664 {
665 const QString& filename = list[3];
666 MusicMetadata mdata;
667 mdata.setFilename(filename);
668 playFile(mdata);
669 }
670 else
671 {
672 LOG(VB_GENERAL, LOG_ERR,
673 QString("MusicPlayer: got invalid MUSIC_COMMAND "
674 "PLAY_URL - %1").arg(me->Message()));
675 }
676 }
677 else if (list[2] == "PLAY_TRACK")
678 {
679 if (list.size() == 4)
680 {
681 int trackID = list[3].toInt();
683 if (mdata)
684 playFile(*mdata);
685 }
686 else
687 {
688 LOG(VB_GENERAL, LOG_ERR,
689 QString("MusicPlayer: got invalid MUSIC_COMMAND "
690 "PLAY_TRACK - %1").arg(me->Message()));
691 }
692 }
693 else if (list[2] == "GET_METADATA")
694 {
695 QString mdataStr;
697 if (mdata)
698 mdataStr = QString("%1 by %2 from %3").arg(mdata->Title(), mdata->Artist(), mdata->Album());
699 else
700 mdataStr = "Unknown Track2";
701
702 QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
703 .arg(gCoreContext->GetHostName(), mdataStr);
704 MythEvent me2(message);
706 }
707 else if (list[2] == "GET_STATUS")
708 {
709 QString statusStr = "STOPPED";
710
711 if (gPlayer->isPlaying())
712 statusStr = "PLAYING";
713 else if (gPlayer->isPaused())
714 statusStr = "PAUSED";
715
716 QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
717 .arg(gCoreContext->GetHostName(), statusStr);
718 MythEvent me2(message);
720 }
721 }
722 else
723 {
724 LOG(VB_GENERAL, LOG_ERR,
725 QString("MusicPlayer: got unknown/invalid MUSIC_COMMAND "
726 "- %1").arg(me->Message()));
727 }
728 }
729 else if (me->Message().startsWith("MUSIC_SETTINGS_CHANGED"))
730 {
731 loadSettings();
732 }
733 else if (me->Message().startsWith("MUSIC_METADATA_CHANGED"))
734 {
736 {
737 QStringList list = me->Message().simplified().split(' ');
738 if (list.size() == 2)
739 {
740 int songID = list[1].toInt();
742
743 if (mdata)
744 {
745 mdata->reloadMetadata();
746
747 // tell any listeners the metadata has changed for this track
749 }
750 }
751 }
752 }
753 else if (me->Message().startsWith("MUSIC_SCANNER_STARTED"))
754 {
755 QStringList list = me->Message().simplified().split(' ');
756 if (list.size() == 2)
757 {
758 const QString& host = list[1];
759 int id = getNotificationID(host);
761 tr("A music file scan has started on %1").arg(host),
762 tr("Music File Scanner"),
763 tr("This may take a while I'll give a shout when finished"));
764 }
765 }
766 else if (me->Message().startsWith("MUSIC_SCANNER_FINISHED"))
767 {
768 QStringList list = me->Message().simplified().split(' ');
769 if (list.size() == 6)
770 {
771 const QString& host = list[1];
772 int id = getNotificationID(host);
773 int totalTracks = list[2].toInt();
774 int newTracks = list[3].toInt();
775 int totalCoverart = list[4].toInt();
776 int newCoverart = list[5].toInt();
777
778 QString summary = QString("Total Tracks: %2, new tracks: %3,\nTotal Coverart: %4, New CoverArt %5")
779 .arg(totalTracks).arg(newTracks).arg(totalCoverart).arg(newCoverart);
781 tr("A music file scan has finished on %1").arg(host),
782 tr("Music File Scanner"), summary);
783
785 }
786 }
787 else if (me->Message().startsWith("MUSIC_SCANNER_ERROR"))
788 {
789 QStringList list = me->Message().simplified().split(' ');
790 if (list.size() == 3)
791 {
792 const QString& host = list[1];
793 const QString& error = list[2];
794 int id = getNotificationID(host);
795
796 if (error == "Already_Running")
797 {
798 sendNotification(id, tr(""),
799 tr("Music File Scanner"),
800 tr("Can't run the music file scanner because it is already running on %1").arg(host));
801 }
802 else if (error == "Stalled")
803 {
804 sendNotification(id, tr(""),
805 tr("Music File Scanner"),
806 tr("The music file scanner has been running for more than 60 minutes on %1.\nResetting and trying again")
807 .arg(host));
808 }
809 }
810 }
811 }
812
813 if (event->type() == AudioOutput::Event::kError)
814 {
815 auto *aoe = dynamic_cast<AudioOutput::Event *>(event);
816
817 if (!aoe)
818 return;
819
820 LOG(VB_GENERAL, LOG_ERR, QString("Audio Output Error: %1").arg(aoe->errorMessage()));
821
822 MythErrorNotification n(tr("Audio Output Error"), tr("MythMusic"), aoe->errorMessage());
824
825 m_errorCount++;
826
827 if (m_errorCount <= 5)
828 {
829 nextAuto();
830 }
831 else
832 {
833 m_errorCount = 0;
834 stop(true);
835 }
836 }
837 else if (event->type() == DecoderEvent::kError)
838 {
839 auto *dxe = dynamic_cast<DecoderEvent *>(event);
840
841 if (!dxe)
842 return;
843
844 LOG(VB_GENERAL, LOG_ERR, QString("Decoder Error: %2").arg(*dxe->errorMessage()));
845
846 MythErrorNotification n(tr("Decoder Error"), tr("MythMusic"), *dxe->errorMessage());
848
849 m_errorCount++;
850
852 {
853 nextAuto();
854 }
855 else
856 {
857 m_errorCount = 0;
858 stop(true);
859 }
860 }
861 else if (event->type() == DecoderHandlerEvent::kError)
862 {
863 auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
864
865 if (!dhe)
866 return;
867
868 LOG(VB_GENERAL, LOG_ERR, QString("Decoder Handler Error - %1").arg(*dhe->getMessage()));
869
870 MythErrorNotification n(tr("Decoder Handler Error"), tr("MythMusic"), *dhe->getMessage());
872
873 m_errorCount++;
874
875 if (m_errorCount <= 5)
876 {
877 nextAuto();
878 }
879 else
880 {
881 m_errorCount = 0;
882 stop(true);
883 }
884 }
885 else if (event->type() == AudioOutput::Event::kInfo)
886 {
887 auto *oe = dynamic_cast<AudioOutput::Event*>(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((Visualization*)(*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)
Event(Type type)
Definition: audiooutput.h:235
void addVisual(Visualization *v)
bool isConfigured() const
Definition: audiooutput.h:80
virtual bool IsPaused(void) const =0
virtual void SetTimecode(std::chrono::milliseconds timecode)=0
void removeVisual(Visualization *v)
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:67
virtual void PauseUntilBuffered(void)=0
virtual void Reset(void)=0
virtual void Pause(bool paused)=0
virtual void SetStretchFactor(float factor)
void setDevice(const QString &dev)
Definition: cddecoder.cpp:108
static const Type kStopped
Definition: decoder.h:45
static const Type kFinished
Definition: decoder.h:46
static const Type kError
Definition: decoder.h:47
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:84
virtual void stop()=0
QString getURL(void) const
Definition: decoder.h:90
virtual void unlock(void)
Definition: decoder.h:85
virtual bool initialize()=0
virtual void seek(double)=0
QWaitCondition * cond()
Definition: decoder.h:88
void setOutput(AudioOutput *o)
Definition: decoder.cpp:43
static MThreadPool * globalInstance(void)
void start(QRunnable *runnable, const QString &debugName, int priority=0)
bool isRunning(void) const
Definition: mthread.cpp:247
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:267
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:284
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:87
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 SaveDurSetting(const QString &key, T newValue)
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.
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.
T GetDurSetting(const QString &key, T defaultval=T::zero())
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)
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)
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:90
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:131
void shuffleTracks(MusicPlayer::ShuffleMode mode)
Definition: playlist.cpp:158
MusicMetadata * getSongAt(int pos) const
Definition: playlist.cpp:1089
void removeTrack(MusicMetadata::IdType trackID)
Definition: playlist.cpp:120
void addTrack(MusicMetadata::IdType trackID, bool update_display)
Given a tracks ID, add that track to this playlist.
Definition: playlist.cpp:64
send a message to the master BE without blocking the UI thread
Definition: musicdata.h:22
virtual MuteState GetMuteState(void) const
Definition: volumebase.cpp:151
virtual void SetCurrentVolume(int value)
Definition: volumebase.cpp:124
virtual uint GetCurrentVolume(void) const
Definition: volumebase.cpp:119
virtual void ToggleMute(void)
Definition: volumebase.cpp:145
virtual void AdjustCurrentVolume(int change)
Definition: volumebase.cpp:133
unsigned int uint
Definition: compat.h:60
MusicData * gMusicData
Definition: musicdata.cpp:23
QList< MusicMetadata * > StreamList
static constexpr const char * METADATA_INVALID_FILENAME
Definition: musicmetadata.h:75
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:50
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