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 play();
616 else if (list[2] == "STOP")
617 stop();
618 else if (list[2] == "PAUSE")
619 pause();
620 else if (list[2] == "SET_VOLUME")
621 {
622 if (list.size() > 3)
623 {
624 int volume = list[3].toInt();
625 if (volume >= 0 && volume <= 100)
626 setVolume(volume);
627 }
628 }
629 else if (list[2] == "GET_VOLUME")
630 {
631 QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
632 .arg(gCoreContext->GetHostName()).arg(getVolume());
633 MythEvent me2(message);
635 }
636 else if (list[2] == "PLAY_FILE")
637 {
638 int start = me->Message().indexOf("'");
639 int end = me->Message().lastIndexOf("'");
640
641 if (start != -1 && end != -1 && start != end)
642 {
643 QString filename = me->Message().mid(start + 1, end - start - 1);
644 MusicMetadata mdata;
645 mdata.setFilename(filename);
646 playFile(mdata);
647 }
648 else
649 {
650 LOG(VB_GENERAL, LOG_ERR,
651 QString("MusicPlayer: got invalid MUSIC_COMMAND "
652 "PLAY_FILE - %1").arg(me->Message()));
653 }
654 }
655 else if (list[2] == "PLAY_URL")
656 {
657 if (list.size() == 4)
658 {
659 const QString& filename = list[3];
660 MusicMetadata mdata;
661 mdata.setFilename(filename);
662 playFile(mdata);
663 }
664 else
665 {
666 LOG(VB_GENERAL, LOG_ERR,
667 QString("MusicPlayer: got invalid MUSIC_COMMAND "
668 "PLAY_URL - %1").arg(me->Message()));
669 }
670 }
671 else if (list[2] == "PLAY_TRACK")
672 {
673 if (list.size() == 4)
674 {
675 int trackID = list[3].toInt();
677 if (mdata)
678 playFile(*mdata);
679 }
680 else
681 {
682 LOG(VB_GENERAL, LOG_ERR,
683 QString("MusicPlayer: got invalid MUSIC_COMMAND "
684 "PLAY_TRACK - %1").arg(me->Message()));
685 }
686 }
687 else if (list[2] == "GET_METADATA")
688 {
689 QString mdataStr;
691 if (mdata)
692 mdataStr = QString("%1 by %2 from %3").arg(mdata->Title(), mdata->Artist(), mdata->Album());
693 else
694 mdataStr = "Unknown Track2";
695
696 QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
697 .arg(gCoreContext->GetHostName(), mdataStr);
698 MythEvent me2(message);
700 }
701 else if (list[2] == "GET_STATUS")
702 {
703 QString statusStr = "STOPPED";
704
705 if (gPlayer->isPlaying())
706 statusStr = "PLAYING";
707 else if (gPlayer->isPaused())
708 statusStr = "PAUSED";
709
710 QString message = QString("MUSIC_CONTROL ANSWER %1 %2")
711 .arg(gCoreContext->GetHostName(), statusStr);
712 MythEvent me2(message);
714 }
715 }
716 else
717 {
718 LOG(VB_GENERAL, LOG_ERR,
719 QString("MusicPlayer: got unknown/invalid MUSIC_COMMAND "
720 "- %1").arg(me->Message()));
721 }
722 }
723 else if (me->Message().startsWith("MUSIC_SETTINGS_CHANGED"))
724 {
725 loadSettings();
726 }
727 else if (me->Message().startsWith("MUSIC_METADATA_CHANGED"))
728 {
730 {
731 QStringList list = me->Message().simplified().split(' ');
732 if (list.size() == 2)
733 {
734 int songID = list[1].toInt();
736
737 if (mdata)
738 {
739 mdata->reloadMetadata();
740
741 // tell any listeners the metadata has changed for this track
743 }
744 }
745 }
746 }
747 else if (me->Message().startsWith("MUSIC_SCANNER_STARTED"))
748 {
749 QStringList list = me->Message().simplified().split(' ');
750 if (list.size() == 2)
751 {
752 const QString& host = list[1];
753 int id = getNotificationID(host);
755 tr("A music file scan has started on %1").arg(host),
756 tr("Music File Scanner"),
757 tr("This may take a while I'll give a shout when finished"));
758 }
759 }
760 else if (me->Message().startsWith("MUSIC_SCANNER_FINISHED"))
761 {
762 QStringList list = me->Message().simplified().split(' ');
763 if (list.size() == 6)
764 {
765 const QString& host = list[1];
766 int id = getNotificationID(host);
767 int totalTracks = list[2].toInt();
768 int newTracks = list[3].toInt();
769 int totalCoverart = list[4].toInt();
770 int newCoverart = list[5].toInt();
771
772 QString summary = QString("Total Tracks: %2, new tracks: %3,\nTotal Coverart: %4, New CoverArt %5")
773 .arg(totalTracks).arg(newTracks).arg(totalCoverart).arg(newCoverart);
775 tr("A music file scan has finished on %1").arg(host),
776 tr("Music File Scanner"), summary);
777
779 }
780 }
781 else if (me->Message().startsWith("MUSIC_SCANNER_ERROR"))
782 {
783 QStringList list = me->Message().simplified().split(' ');
784 if (list.size() == 3)
785 {
786 const QString& host = list[1];
787 const QString& error = list[2];
788 int id = getNotificationID(host);
789
790 if (error == "Already_Running")
791 {
792 sendNotification(id, tr(""),
793 tr("Music File Scanner"),
794 tr("Can't run the music file scanner because it is already running on %1").arg(host));
795 }
796 else if (error == "Stalled")
797 {
798 sendNotification(id, tr(""),
799 tr("Music File Scanner"),
800 tr("The music file scanner has been running for more than 60 minutes on %1.\nResetting and trying again")
801 .arg(host));
802 }
803 }
804 }
805 }
806
807 if (event->type() == AudioOutput::Event::kError)
808 {
809 auto *aoe = dynamic_cast<AudioOutput::Event *>(event);
810
811 if (!aoe)
812 return;
813
814 LOG(VB_GENERAL, LOG_ERR, QString("Audio Output Error: %1").arg(aoe->errorMessage()));
815
816 MythErrorNotification n(tr("Audio Output Error"), tr("MythMusic"), aoe->errorMessage());
818
819 m_errorCount++;
820
821 if (m_errorCount <= 5)
822 nextAuto();
823 else
824 {
825 m_errorCount = 0;
826 stop(true);
827 }
828 }
829 else if (event->type() == DecoderEvent::kError)
830 {
831 auto *dxe = dynamic_cast<DecoderEvent *>(event);
832
833 if (!dxe)
834 return;
835
836 LOG(VB_GENERAL, LOG_ERR, QString("Decoder Error: %2").arg(*dxe->errorMessage()));
837
838 MythErrorNotification n(tr("Decoder Error"), tr("MythMusic"), *dxe->errorMessage());
840
841 m_errorCount++;
842
844 nextAuto();
845 else
846 {
847 m_errorCount = 0;
848 stop(true);
849 }
850 }
851 else if (event->type() == DecoderHandlerEvent::kError)
852 {
853 auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
854
855 if (!dhe)
856 return;
857
858 LOG(VB_GENERAL, LOG_ERR, QString("Decoder Handler Error - %1").arg(*dhe->getMessage()));
859
860 MythErrorNotification n(tr("Decoder Handler Error"), tr("MythMusic"), *dhe->getMessage());
862
863 m_errorCount++;
864
865 if (m_errorCount <= 5)
866 nextAuto();
867 else
868 {
869 m_errorCount = 0;
870 stop(true);
871 }
872 }
873 else if (event->type() == AudioOutput::Event::kInfo)
874 {
875 auto *oe = dynamic_cast<AudioOutput::Event*>(event);
876
877 if (!oe)
878 return;
879
881 m_currentTime = oe->elapsedSeconds();
882 else
883 m_currentTime = oe->elapsedSeconds() - m_lastTrackStart;
884
886 {
887 // we update the lastplay and playcount after playing
888 // for m_lastplayDelay seconds or half the total track time
889 if ((getCurrentMetadata() &&
890 m_currentTime > duration_cast<std::chrono::seconds>(getCurrentMetadata()->Length()) / 2) ||
892 {
894 }
895 }
896
897 // update the current tracks time in the last played list
899 {
900 if (!m_playedList.isEmpty() && m_currentTime > 0s)
901 {
902 m_playedList.last()->setLength(m_currentTime);
903 // this will update any track lengths displayed on screen
905 }
906 }
907 }
908 else if (event->type() == DecoderEvent::kFinished)
909 {
911 {
912 delete m_oneshotMetadata;
913 m_oneshotMetadata = nullptr;
914 stop(true);
915 }
916 else
917 {
918 auto metadataSecs = duration_cast<std::chrono::seconds>(getCurrentMetadata()->Length());
920 m_currentTime != metadataSecs)
921 {
922 LOG(VB_GENERAL, LOG_NOTICE, QString("MusicPlayer: Updating track length was %1s, should be %2s")
923 .arg(metadataSecs.count()).arg(m_currentTime.count()));
924
927
928 // this will update any track lengths displayed on screen
930
931 // this will force the playlist stats to update
933 dispatch(me);
934 }
935
936 nextAuto();
937 }
938 }
939 else if (event->type() == DecoderEvent::kStopped)
940 {
941 }
942 else if (event->type() == DecoderHandlerEvent::kBufferStatus)
943 {
944 auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
945 if (!dhe)
946 return;
947
949 }
950
951 QObject::customEvent(event);
952}
953
954void MusicPlayer::getBufferStatus(int *bufferAvailable, int *bufferSize) const
955{
956 *bufferAvailable = m_bufferAvailable;
957 *bufferSize = m_bufferSize;
958}
959
961{
962 if (m_playMode == mode)
963 return;
964
965 savePosition();
966
967 m_playMode = mode;
968
969 loadPlaylist();
970}
971
973{
975 {
977 {
978 int bookmark = gCoreContext->GetNumSetting("MusicRadioBookmark", 0);
979 Playlist *playlist = getCurrentPlaylist();
980 if ((bookmark < 0) ||
981 (playlist && bookmark >= playlist->getTrackCount()))
982 bookmark = 0;
983
984 m_currentTrack = bookmark;
985 }
986 else
987 {
988 m_currentTrack = 0;
989 }
990
992 }
993 else
994 {
996 {
997 int bookmark = gCoreContext->GetNumSetting("MusicBookmark", 0);
998 Playlist *playlist = getCurrentPlaylist();
999 if ((bookmark < 0) ||
1000 (playlist && bookmark >= getCurrentPlaylist()->getTrackCount()))
1001 bookmark = 0;
1002
1003 m_currentTrack = bookmark;
1004 }
1005 else
1006 {
1007 m_currentTrack = 0;
1008 }
1009 }
1010}
1011
1013{
1015
1016 // create the radio playlist
1020
1021 for (int x = 0; x < list->count(); x++)
1022 {
1023 MusicMetadata *mdata = list->at(x);
1025
1026 if (mdata->ID() == id)
1027 m_currentTrack = x;
1028 }
1029
1031}
1032
1033void MusicPlayer::moveTrackUpDown(bool moveUp, int whichTrack)
1034{
1035 Playlist *playlist = getCurrentPlaylist();
1036 if (nullptr == playlist)
1037 return;
1038
1039 if (moveUp && whichTrack <= 0)
1040 return;
1041
1042 if (!moveUp && whichTrack >= getCurrentPlaylist()->getTrackCount() - 1)
1043 return;
1044
1045 MusicMetadata *currTrack = playlist->getSongAt(m_currentTrack);
1046
1047 playlist->moveTrackUpDown(moveUp, whichTrack);
1048
1049 m_currentTrack = playlist->getTrackPosition(currTrack->ID());
1050}
1051
1053{
1054 changeCurrentTrack(pos);
1055
1056 if (!getCurrentMetadata())
1057 {
1058 stop();
1059 return false;
1060 }
1061
1062 play();
1063
1064 return true;
1065}
1066
1068{
1069 // can't save a bookmark if we don't know what we are playing
1070 if (!getCurrentMetadata())
1071 return;
1072
1074 {
1075 gCoreContext->SaveSetting("MusicRadioBookmark", getCurrentMetadata()->ID());
1076 }
1077 else
1078 {
1079 gCoreContext->SaveSetting("MusicBookmark", getCurrentMetadata()->ID());
1080 gCoreContext->SaveDurSetting("MusicBookmarkPosition", m_currentTime);
1081 }
1082}
1083
1085{
1086 // if we are switching views we don't wont to restore the position
1087 if (!m_allowRestorePos)
1088 return;
1089
1090 m_currentTrack = 0;
1091 uint id = -1;
1092
1094 {
1096 id = gCoreContext->GetNumSetting("MusicRadioBookmark", 0);
1097 else
1098 id = gCoreContext->GetNumSetting("MusicBookmark", 0);
1099 }
1100
1101 Playlist *playlist = gPlayer->getCurrentPlaylist();
1102 if (playlist)
1103 {
1104 for (int x = 0; x < playlist->getTrackCount(); x++)
1105 {
1106 if (playlist->getSongAt(x) &&
1107 playlist->getSongAt(x)->ID() == id)
1108 {
1109 m_currentTrack = x;
1110 break;
1111 }
1112 }
1113 }
1114
1115 if (getCurrentMetadata())
1116 {
1118 play();
1119
1121 seek(gCoreContext->GetDurSetting<std::chrono::seconds>("MusicBookmarkPosition", 0s));
1122 }
1123}
1124
1125void MusicPlayer::seek(std::chrono::seconds pos)
1126{
1127 if (m_output)
1128 {
1129 Decoder *decoder = getDecoder();
1130 if (decoder && decoder->isRunning())
1131 decoder->seek(pos.count());
1132
1133 m_output->SetTimecode(pos);
1134 }
1135}
1136
1138{
1139 if (m_canShowPlayer)
1140 {
1141 MythScreenStack *popupStack =
1142 GetMythMainWindow()->GetStack("popup stack");
1143
1144 auto *miniplayer = new MiniPlayer(popupStack);
1145
1146 if (miniplayer->Create())
1147 popupStack->AddScreen(miniplayer);
1148 else
1149 delete miniplayer;
1150 }
1151}
1152
1155{
1156 Playlist *playlist = getCurrentPlaylist();
1157 if (nullptr == playlist)
1158 return;
1159
1160 // check to see if we need to save the current tracks volatile metadata (playcount, last played etc)
1162
1163 m_currentTrack = trackNo;
1164
1165 // sanity check the current track
1166 if (m_currentTrack < 0 || m_currentTrack >= playlist->getTrackCount())
1167 {
1168 LOG(VB_GENERAL, LOG_ERR,
1169 QString("MusicPlayer: asked to set the current track to an invalid track no. %1")
1170 .arg(trackNo));
1171 m_currentTrack = -1;
1172 return;
1173 }
1174}
1175
1178{
1180 return m_oneshotMetadata;
1181
1182 Playlist *playlist = getCurrentPlaylist();
1183 if (!playlist || !playlist->getSongAt(m_currentTrack))
1184 return nullptr;
1185
1186 return playlist->getSongAt(m_currentTrack);
1187}
1188
1191{
1193 return nullptr;
1194
1196 return getCurrentMetadata();
1197
1198 Playlist *playlist = gPlayer->getCurrentPlaylist();
1199 if (!playlist || !playlist->getSongAt(m_currentTrack))
1200 return nullptr;
1201
1203 return getCurrentMetadata();
1204
1205 // if we are not playing the last track then just return the next track
1206 if (m_currentTrack < playlist->getTrackCount() - 1)
1207 return playlist->getSongAt(m_currentTrack + 1);
1208
1209 // if we are playing the last track then we need to take the
1210 // repeat mode into account
1211 if (m_repeatMode == REPEAT_ALL)
1212 return playlist->getSongAt(0);
1213 return nullptr;
1214}
1215
1217{
1218 switch (m_repeatMode)
1219 {
1220 case REPEAT_OFF:
1222 break;
1223 case REPEAT_TRACK:
1225 break;
1226 case REPEAT_ALL:
1227 default:
1229 break;
1230 }
1231
1232 return m_repeatMode;
1233}
1234
1236{
1237 switch (m_shuffleMode)
1238 {
1239 case SHUFFLE_OFF:
1241 break;
1242 case SHUFFLE_RANDOM:
1244 break;
1247 break;
1248 case SHUFFLE_ALBUM:
1250 break;
1251 case SHUFFLE_ARTIST:
1252 default:
1254 break;
1255 }
1256
1258
1259 return m_shuffleMode;
1260}
1261
1263{
1264 int curTrackID = -1;
1265 if (getCurrentMetadata())
1266 curTrackID = getCurrentMetadata()->ID();
1267
1268 // only save the mode if we are playing tracks
1270 m_shuffleMode = mode;
1271
1272 Playlist *playlist = getCurrentPlaylist();
1273 if (nullptr == playlist)
1274 return;
1275
1276 playlist->shuffleTracks(mode);
1277
1278 if (curTrackID != -1)
1279 {
1280 for (int x = 0; x < playlist->getTrackCount(); x++)
1281 {
1282 MusicMetadata *mdata = playlist->getSongAt(x);
1283 if (mdata && mdata->ID() == (MusicMetadata::IdType) curTrackID)
1284 {
1285 m_currentTrack = x;
1286 break;
1287 }
1288 }
1289 }
1290}
1291
1293{
1295 {
1298 }
1299
1300 m_updatedLastplay = true;
1301}
1302
1304{
1306 {
1307 if (getCurrentMetadata()->hasChanged())
1308 {
1310
1311 // only write the last played, playcount & rating to the tag if it's enabled by the user
1312 if (GetMythDB()->GetNumSetting("AllowTagWriting", 0) == 1)
1313 {
1314 QStringList strList;
1315 strList << QString("MUSIC_TAG_UPDATE_VOLATILE")
1317 << QString::number(getCurrentMetadata()->ID())
1318 << QString::number(getCurrentMetadata()->Rating())
1319 << QString::number(getCurrentMetadata()->Playcount())
1320 << getCurrentMetadata()->LastPlay().toString(Qt::ISODate);
1321 auto *thread = new SendStringListThread(strList);
1322 MThreadPool::globalInstance()->start(thread, "UpdateVolatile");
1323 }
1324
1326 }
1327 }
1328}
1329
1330void MusicPlayer::setSpeed(float newspeed)
1331{
1332 if (m_output)
1333 {
1334 m_playSpeed = newspeed;
1336 }
1337}
1338
1340{
1341 m_playSpeed += 0.05F;
1343}
1344
1346{
1347 m_playSpeed -= 0.05F;
1349}
1350
1352{
1354 dispatch(me);
1355}
1356
1358{
1360 dispatch(me);
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{
1389 if (getOutput())
1390 {
1393 }
1394}
1395
1397{
1398 if (getOutput())
1399 {
1402 }
1403}
1404
1406{
1407 if (getOutput())
1408 {
1409 getOutput()->SetCurrentVolume(volume);
1411 }
1412}
1413
1415{
1416 if (m_output)
1417 return m_output->GetCurrentVolume();
1418 return 0;
1419}
1420
1422{
1423 if (m_output)
1424 {
1427 }
1428}
1429
1431{
1432 if (m_output)
1433 return m_output->GetMuteState();
1434 return kMuteOff;
1435}
1436
1438{
1439 map["volumemute"] = isMuted() ? tr("%1% (Muted)", "Zero Audio Volume").arg(getVolume()) :
1440 QString("%1%").arg(getVolume());
1441 map["volume"] = QString("%1").arg(getVolume());
1442 map["volumepercent"] = QString("%1%").arg(getVolume());
1443 map["mute"] = isMuted() ? tr("Muted") : "";
1444}
1445
1446void MusicPlayer::activePlaylistChanged(int trackID, bool deleted)
1447{
1448 if (trackID == -1)
1449 {
1450 if (deleted)
1451 {
1453 dispatch(me);
1454 }
1455 else
1456 {
1458 dispatch(me);
1459 }
1460 }
1461 else
1462 {
1463 if (deleted)
1464 {
1466 dispatch(me);
1467 }
1468 else
1469 {
1471 dispatch(me);
1472 }
1473 }
1474
1475 // if we don't have any tracks to play stop here
1476 Playlist *playlist = getCurrentPlaylist();
1477 if (!playlist || playlist->getTrackCount() == 0)
1478 {
1479 m_currentTrack = -1;
1480 if (isPlaying())
1481 stop(true);
1482 return;
1483 }
1484
1485 int trackPos = -1;
1486
1487 // make sure the current playing track is still valid
1488 if (isPlaying() && getDecoderHandler())
1489 {
1490 for (int x = 0; x < playlist->getTrackCount(); x++)
1491 {
1492 if (playlist->getSongAt(x)->ID() == getDecoderHandler()->getMetadata().ID())
1493 {
1494 trackPos = x;
1495 break;
1496 }
1497 }
1498 }
1499
1500 if (trackPos != m_currentTrack)
1501 m_currentTrack = trackPos;
1502
1503 if (!getCurrentMetadata())
1504 {
1505 m_currentTrack = -1;
1506 stop(true);
1507 }
1508}
1509
1511{
1513 dispatch(me);
1514}
1515
1517{
1520
1521 // add any listeners to the decoderHandler
1522 {
1523 QMutexLocker locker(m_lock);
1524 // NOLINTNEXTLINE(modernize-loop-convert)
1525 for (auto it = m_listeners.begin(); it != m_listeners.end() ; ++it)
1527 }
1528}
1529
1531{
1532 Decoder *decoder = getDecoder();
1533 if (!decoder)
1534 return;
1535
1536 LOG(VB_PLAYBACK, LOG_INFO, QString ("decoder handler is ready, decoding %1")
1537 .arg(decoder->getURL()));
1538
1539#ifdef HAVE_CDIO
1540 auto *cddecoder = dynamic_cast<CdDecoder*>(decoder);
1541 if (cddecoder)
1542 cddecoder->setDevice(gCDdevice);
1543#endif
1544
1545 // Decoder thread can't be running while being initialized
1546 if (decoder->isRunning())
1547 {
1548 decoder->stop();
1549 decoder->wait();
1550 }
1551
1552 decoder->setOutput(m_output);
1553 //decoder-> setBlockSize(2 * 1024);
1554 decoder->addListener(this);
1555
1556 // add any listeners to the decoder
1557 {
1558 QMutexLocker locker(m_lock);
1559 // NOLINTNEXTLINE(modernize-loop-convert)
1560 for (auto it = m_listeners.begin(); it != m_listeners.end() ; ++it)
1561 decoder->addListener(*it);
1562 }
1563
1564 m_currentTime = 0s;
1565 m_lastTrackStart = 0s;
1566
1567 // NOLINTNEXTLINE(modernize-loop-convert)
1568 for (auto it = m_visualisers.begin(); it != m_visualisers.end() ; ++it)
1569 {
1570 //m_output->addVisual((Visualization*)(*it));
1571 //(*it)->setDecoder(decoder);
1572 //m_visual->setOutput(m_output);
1573 }
1574
1575 if (decoder->initialize())
1576 {
1577 if (m_output)
1579
1580 decoder->start();
1581
1583 gCoreContext->GetDurSetting<std::chrono::seconds>("MusicBookmarkPosition", 0s) > 0s)
1584 {
1585 seek(gCoreContext->GetDurSetting<std::chrono::seconds>("MusicBookmarkPosition", 0s));
1586 gCoreContext->SaveDurSetting("MusicBookmarkPosition", 0s);
1587 }
1588
1589 m_isPlaying = true;
1590 m_updatedLastplay = false;
1591 }
1592 else
1593 {
1594 LOG(VB_PLAYBACK, LOG_ERR, QString("Cannot initialise decoder for %1")
1595 .arg(decoder->getURL()));
1596 return;
1597 }
1598
1599 // tell any listeners we've started playing a new track
1601 dispatch(me);
1602}
1603
1605{
1606 MusicMetadata *mdata = gMusicData->m_all_music->getMetadata(trackID);
1607 if (mdata)
1608 {
1609 Playlist *playlist = getCurrentPlaylist();
1610 if (nullptr == playlist)
1611 return;
1612 int trackPos = playlist->getTrackPosition(mdata->ID());
1613 if (m_currentTrack > 0 && m_currentTrack >= trackPos)
1615
1616 playlist->removeTrack(trackID);
1617 }
1618}
1619
1620void MusicPlayer::addTrack(int trackID, bool updateUI)
1621{
1622 getCurrentPlaylist()->addTrack(trackID, updateUI);
1623}
1624
1626{
1628 return nullptr;
1629
1631 {
1633 }
1635}
1636
1638{
1640}
1641
1643{
1644 if (!m_notificationMap.contains(hostname))
1645 m_notificationMap.insert(hostname, GetNotificationCenter()->Register(this));
1646
1648}
1649
1650void MusicPlayer::sendNotification(int notificationID, const QString &title, const QString &author, const QString &desc)
1651{
1652 QString image = "musicscanner.png";
1653 if (!GetMythUI()->FindThemeFile(image))
1654 LOG(VB_GENERAL, LOG_ERR, "MusicPlayer: sendNotification failed to find the 'musicscanner.png' image");
1655
1656 DMAP map;
1657 map["asar"] = title;
1658 map["minm"] = author;
1659 map["asal"] = desc;
1660
1661 auto *n = new MythImageNotification(MythNotification::kInfo, image, map);
1662
1663 n->SetId(notificationID);
1664 n->SetParent(this);
1665 n->SetDuration(5s);
1666 n->SetFullScreen(false);
1668 delete n;
1669}
@ 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:64
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:104
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:42
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)
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: compat.h:68
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