MythTV master
musiccommon.cpp
Go to the documentation of this file.
1// C++ includes
2#include <cstdlib>
3#include <iostream>
4
5// Qt includes
6#include <QApplication>
7#include <QLocale>
8
9// mythtv
27
28// MythMusic includes
29#include "constants.h"
30#include "decoder.h"
31#include "editmetadata.h"
32#include "lyricsview.h"
33#include "mainvisual.h"
34#include "musiccommon.h"
35#include "musicdata.h"
36#include "playlist.h"
37#include "playlistcontainer.h"
38#include "playlisteditorview.h"
39#include "playlistview.h"
40#include "search.h"
41#include "searchview.h"
42#include "smartplaylist.h"
43#include "streamview.h"
44#include "visualizerview.h"
45
47 const QString &name)
48 : MythScreenType(parent, name),
49 m_parentScreen(parentScreen)
50{
51 m_cycleVisualizer = gCoreContext->GetBoolSetting("VisualCycleOnSongChange", false);
52
53 if (LCD *lcd = LCD::Get())
54 {
55 lcd->switchToTime();
56 lcd->setFunctionLEDs(FUNC_MUSIC, true);
57 }
58}
59
61{
63
64 if (m_mainvisual)
65 {
67 delete m_mainvisual;
68 m_mainvisual = nullptr;
69 }
70
71 if (LCD *lcd = LCD::Get())
72 {
73 lcd->switchToTime();
74 lcd->setFunctionLEDs(FUNC_MUSIC, false);
75 }
76}
77
79{
80 bool err = false;
81 UIUtilW::Assign(this, m_timeText, "time", &err);
82 UIUtilW::Assign(this, m_infoText, "info", &err);
83 UIUtilW::Assign(this, m_visualText, "visualizername", &err);
84 UIUtilW::Assign(this, m_noTracksText, "notracks", &err);
85
86 UIUtilW::Assign(this, m_shuffleState, "shufflestate", &err);
87 UIUtilW::Assign(this, m_repeatState, "repeatstate", &err);
88 UIUtilW::Assign(this, m_movingTracksState, "movingtracksstate", &err);
89
90 UIUtilW::Assign(this, m_ratingState, "ratingstate", &err);
91
92 UIUtilW::Assign(this, m_trackProgress, "progress", &err);
93 UIUtilW::Assign(this, m_trackProgressText, "trackprogress", &err);
94 UIUtilW::Assign(this, m_trackSpeedText, "trackspeed", &err);
95 UIUtilW::Assign(this, m_trackState, "trackstate", &err);
96
97 UIUtilW::Assign(this, m_volumeText, "volume", &err);
98 UIUtilW::Assign(this, m_muteState, "mutestate", &err);
99
100 UIUtilW::Assign(this, m_playlistProgress, "playlistprogress", &err);
101
102 UIUtilW::Assign(this, m_prevButton, "prev", &err);
103 UIUtilW::Assign(this, m_rewButton, "rew", &err);
104 UIUtilW::Assign(this, m_pauseButton, "pause", &err);
105 UIUtilW::Assign(this, m_playButton, "play", &err);
106 UIUtilW::Assign(this, m_stopButton, "stop", &err);
107 UIUtilW::Assign(this, m_ffButton, "ff", &err);
108 UIUtilW::Assign(this, m_nextButton, "next", &err);
109
110 UIUtilW::Assign(this, m_coverartImage, "coverart", &err);
111
112 UIUtilW::Assign(this, m_currentPlaylist, "currentplaylist", &err);
113 UIUtilW::Assign(this, m_playedTracksList, "playedtrackslist", &err);
114
115 UIUtilW::Assign(this, m_visualizerVideo, "visualizer", &err);
116
117 if (m_prevButton)
119
120 if (m_rewButton)
122
123 if (m_pauseButton)
124 {
127 }
128
129 if (m_playButton)
130 {
133 }
134
135 if (m_stopButton)
136 {
139 }
140
141 if (m_ffButton)
143
144 if (m_nextButton)
146
148 {
153
154 m_currentPlaylist->SetSearchFields("**search**");
155 }
156
157 init();
158
159 return err;
160}
161
163{
164 gPlayer->addListener(this);
165
166 if (startPlayback)
167 {
168 if (!gPlayer->isPlaying())
169 {
171 m_currentView == MV_VISUALIZERINFO) // keep playmode
173 else if (m_currentView == MV_RADIO)
177 else
179
181 }
182 else
183 {
184 // if we are playing but we are switching to a view from a different playmode
185 // we need to restart playback in the new mode
187 {
188 // these views can be used in both play modes
189 }
191 {
192 gPlayer->stop(true);
193
196 else
198
200 }
202 {
203 gPlayer->stop(true);
206 }
207 }
208 }
209
211
213 if (curMeta)
214 updateTrackInfo(curMeta);
215
217
220
222 {
224
226
227 m_fullscreenBlank = false;
228
229 m_randomVisualizer = gCoreContext->GetBoolSetting("VisualRandomize", false);
230
232
233 // sanity check
234 if (m_currentVisual >= static_cast<uint>(m_visualModes.count()))
235 {
236 LOG(VB_GENERAL, LOG_ERR, QString("MusicCommon: Got a bad saved visualizer: %1").arg(m_currentVisual));
237 m_currentVisual = 0;
238 }
239
241
242 if (gPlayer->isPlaying())
244 }
245
246 m_controlVolume = gCoreContext->GetBoolSetting("MythControlsVolume", false);
247 updateVolume();
248
251
252 if (m_stopButton)
254 if (m_playButton)
256 if (m_pauseButton)
258 if (m_trackState)
259 {
260 if (gPlayer->isPlaying())
261 m_trackState->DisplayState("playing");
262 else if (gPlayer->isPaused())
263 m_trackState->DisplayState("paused");
264 else
265 m_trackState->DisplayState("stopped");
266
267 }
268
271
275
277 {
280 }
281
283
285}
286
288{
289 if (m_repeatState)
290 {
291 switch (gPlayer->getRepeatMode())
292 {
295 if (class LCD *lcd = LCD::Get())
296 lcd->setMusicRepeat (LCD::MUSIC_REPEAT_NONE);
297 break;
299 m_repeatState->DisplayState("track");
300 if (class LCD *lcd = LCD::Get())
301 lcd->setMusicRepeat (LCD::MUSIC_REPEAT_TRACK);
302 break;
305 if (class LCD *lcd = LCD::Get())
306 lcd->setMusicRepeat (LCD::MUSIC_REPEAT_ALL);
307 break;
308 default:
310 if (class LCD *lcd = LCD::Get())
311 lcd->setMusicRepeat (LCD::MUSIC_REPEAT_NONE);
312 break;
313 }
314 }
315
316 // need this to update the next track info
318 if (curMeta)
319 updateTrackInfo(curMeta);
320}
321
322void MusicCommon::updateShuffleMode(bool updateUIList)
323{
324 if (m_shuffleState)
325 {
326 switch (gPlayer->getShuffleMode())
327 {
330 if (class LCD *lcd = LCD::Get())
331 lcd->setMusicShuffle(LCD::MUSIC_SHUFFLE_NONE);
332 break;
334 m_shuffleState->DisplayState("random");
335 if (class LCD *lcd = LCD::Get())
336 lcd->setMusicShuffle(LCD::MUSIC_SHUFFLE_RAND);
337 break;
339 m_shuffleState->DisplayState("intelligent");
340 if (class LCD *lcd = LCD::Get())
341 lcd->setMusicShuffle(LCD::MUSIC_SHUFFLE_SMART);
342 break;
345 if (class LCD *lcd = LCD::Get())
346 lcd->setMusicShuffle(LCD::MUSIC_SHUFFLE_ALBUM);
347 break;
349 m_shuffleState->DisplayState("artist");
350 if (class LCD *lcd = LCD::Get())
351 lcd->setMusicShuffle(LCD::MUSIC_SHUFFLE_ARTIST);
352 break;
353 default:
355 if (class LCD *lcd = LCD::Get())
356 lcd->setMusicShuffle(LCD::MUSIC_SHUFFLE_NONE);
357 break;
358 }
359 }
360
361 if (updateUIList)
362 {
364
369
370 // need this to update the next track info
372 if (curMeta)
373 updateTrackInfo(curMeta);
374 }
375}
376
378{
379 // can we switch to this view from the current view?
380 switch (m_currentView)
381 {
382 case MV_PLAYLIST:
383 {
384 if (view != MV_PLAYLISTEDITORTREE && view != MV_PLAYLISTEDITORGALLERY &&
385 view != MV_SEARCH && view != MV_VISUALIZER && view != MV_LYRICS)
386 return;
387 break;
388 }
389
391 {
392 if (view != MV_PLAYLISTEDITORGALLERY && view != MV_SEARCH && view != MV_VISUALIZER && view != MV_LYRICS)
393 return;
394 break;
395 }
396
398 {
399 if (view != MV_PLAYLISTEDITORTREE && view != MV_SEARCH && view != MV_VISUALIZER && view != MV_LYRICS)
400 return;
401 break;
402 }
403
404 case MV_SEARCH:
405 {
406 if (view != MV_VISUALIZER && view != MV_LYRICS)
407 return;
408 break;
409 }
410
411 case MV_VISUALIZER:
412 {
413 return;
414 }
415
416 case MV_LYRICS:
417 {
418 if (view != MV_VISUALIZER && view != MV_SEARCH)
419 return;
420 break;
421 }
422
423 case MV_RADIO:
424 {
425 if (view != MV_VISUALIZER && view != MV_LYRICS)
426 return;
427 break;
428 }
429
430 default:
431 return;
432 }
433
435
437
438 if (m_mainvisual)
439 {
440 delete m_mainvisual;
441 m_mainvisual = nullptr;
442 }
443
444 gPlayer->removeListener(this);
446
447 switch (view)
448 {
449 case MV_PLAYLIST:
450 {
451 auto *plview = new PlaylistView(mainStack, this);
452
453 if (plview->Create())
454 {
455 mainStack->AddScreen(plview);
456 connect(plview, &MythScreenType::Exiting, this, &MusicCommon::viewExited);
457 }
458 else
459 {
460 delete plview;
461 }
462
463 break;
464 }
465
467 {
468 // if we are switching playlist editor views save and restore
469 // the current position in the tree
470 bool restorePos = (m_currentView == MV_PLAYLISTEDITORGALLERY);
471 auto *oldView = qobject_cast<PlaylistEditorView *>(this);
472 if (oldView)
473 oldView->saveTreePosition();
474
475 MythScreenType *parentScreen = (oldView != nullptr ? m_parentScreen : this);
476
477 auto *pleview = new PlaylistEditorView(mainStack, parentScreen, "tree", restorePos);
478
479 if (pleview->Create())
480 {
481 mainStack->AddScreen(pleview);
482 connect(pleview, &MythScreenType::Exiting, this, &MusicCommon::viewExited);
483 }
484 else
485 {
486 delete pleview;
487 }
488
489 if (oldView)
490 Close();
491
492 break;
493 }
494
496 {
497 // if we are switching playlist editor views save and restore
498 // the current position in the tree
499 bool restorePos = (m_currentView == MV_PLAYLISTEDITORTREE);
500 auto *oldView = qobject_cast<PlaylistEditorView *>(this);
501 if (oldView)
502 oldView->saveTreePosition();
503
504 MythScreenType *parentScreen = (oldView != nullptr ? m_parentScreen : this);
505
506 auto *pleview = new PlaylistEditorView(mainStack, parentScreen, "gallery", restorePos);
507
508 if (pleview->Create())
509 {
510 mainStack->AddScreen(pleview);
511 connect(pleview, &MythScreenType::Exiting, this, &MusicCommon::viewExited);
512 }
513 else
514 {
515 delete pleview;
516 }
517
518 if (oldView)
519 Close();
520
521 break;
522 }
523
524 case MV_SEARCH:
525 {
526 auto *sview = new SearchView(mainStack, this);
527
528 if (sview->Create())
529 {
530 mainStack->AddScreen(sview);
531 connect(sview, &MythScreenType::Exiting, this, &MusicCommon::viewExited);
532 }
533 else
534 {
535 delete sview;
536 }
537
538 break;
539 }
540
541 case MV_VISUALIZER:
542 {
543 auto *vview = new VisualizerView(mainStack, this);
544
545 if (vview->Create())
546 {
547 mainStack->AddScreen(vview);
548 connect(vview, &MythScreenType::Exiting, this, &MusicCommon::viewExited);
549 }
550 else
551 {
552 delete vview;
553 }
554
555 break;
556 }
557
558 case MV_LYRICS:
559 {
560 auto *lview = new LyricsView(mainStack, this);
561
562 if (lview->Create())
563 {
564 mainStack->AddScreen(lview);
565 connect(lview, &MythScreenType::Exiting, this, &MusicCommon::viewExited);
566 }
567 else
568 {
569 delete lview;
570 }
571
572 break;
573 }
574
575 default:
576 break;
577 }
578
580}
581
583{
584 init(false);
585}
586
588{
589 bool handled = false;
590
591 // if there is a pending jump point pass the key press to the default handler
592 if (GetMythMainWindow()->IsExitingToMain())
593 {
595
596 // do we need to stop playing?
597 if (gPlayer->isPlaying() && gCoreContext->GetSetting("MusicJumpPointAction", "stop") == "stop")
598 gPlayer->stop(true);
599
601 }
602
603 QStringList actions;
604 handled = GetMythMainWindow()->TranslateKeyPress("Music", e, actions, true);
605
606 for (int i = 0; i < actions.size() && !handled; i++)
607 {
608 const QString& action = actions[i];
609 handled = true;
610
611 // if we are currently moving an item,
612 // we only accept UP/DOWN/SELECT/ESCAPE
614 {
616 if (!item)
617 return false;
618
619 if (action == "SELECT" || action == "ESCAPE")
620 {
621 m_movingTrack = false;
622 item->DisplayState("off", "movestate");
623 }
624 else if (action == "UP")
625 {
627 item->MoveUpDown(true);
634 }
635 else if (action == "DOWN")
636 {
638 item->MoveUpDown(false);
645 }
646
647 return true;
648 }
649
650 if (action == "ESCAPE")
651 {
652 // if we was started from another music view screen return to it
653 if (m_parentScreen)
654 {
655 handled = false;
656 }
657 else
658 {
659 // this is the top music view screen so prompt to continue playing
660 QString exit_action = gCoreContext->GetSetting("MusicExitAction", "prompt");
661
662 if (!gPlayer->isPlaying())
663 {
665 stopAll();
666 Close();
667 }
668 else
669 {
670 if (exit_action == "stop")
671 {
673 stopAll();
674 Close();
675 }
676 else if (exit_action == "play")
677 {
678 Close();
679 }
680 else
681 {
682 showExitMenu();
683 }
684 }
685 }
686 }
687 else if (action == "THMBUP")
688 {
689 changeRating(true);
690 }
691 else if (action == "THMBDOWN")
692 {
693 changeRating(false);
694 }
695 else if (action == "NEXTTRACK")
696 {
697 if (m_nextButton)
699 else
700 next();
701 }
702 else if (action == "PREVTRACK")
703 {
704 if (m_prevButton)
706 else
707 previous();
708 }
709 else if (action == "FFWD")
710 {
712 {
713 if (m_ffButton)
714 m_ffButton->Push();
715 else
716 seekforward();
717 }
718 }
719 else if (action == "RWND")
720 {
722 {
723 if (m_rewButton)
724 m_rewButton->Push();
725 else
726 seekback();
727 }
728 }
729 else if (action == "PAUSE")
730 {
732 {
733 // ignore if we are already playing a radio stream
734 }
735 else if (gPlayer->isPlaying() || (gPlayer->getOutput() && gPlayer->getOutput()->IsPaused()))
736 {
737 // if we are playing or are paused PAUSE will toggle the pause state
738 if (m_pauseButton)
740 else
741 pause();
742 }
743 else
744 {
745 // not playing or paused so PAUSE acts the same as PLAY
746 if (m_playButton)
748 else
749 play();
750 }
751 }
752 else if (action == "PLAY")
753 {
754 if (m_playButton)
756 else
757 play();
758 }
759 else if (action == "STOP")
760 {
761 if (m_stopButton)
763 else
764 stop();
765 m_currentTime = 0s;
766 }
767 else if (action == "CYCLEVIS")
768 {
770 }
771 else if (action == "BLANKSCR")
772 {
773 // change to the blank visualizer
774 if (m_mainvisual)
775 switchVisualizer("Blank");
776
777 // switch to the full screen visualiser view
780 }
781 else if (action == "VOLUMEDOWN")
782 {
783 changeVolume(false);
784 }
785 else if (action == "VOLUMEUP")
786 {
787 changeVolume(true);
788 }
789 else if (action == "SPEEDDOWN")
790 {
791 changeSpeed(false);
792 }
793 else if (action == "SPEEDUP")
794 {
795 changeSpeed(true);
796 }
797 else if (action == "MUTE")
798 {
799 toggleMute();
800 }
801 else if (action == "TOGGLEUPMIX")
802 {
803 toggleUpmix();
804 }
805 else if (action == "INFO" || action == "EDIT")
806 {
808 {
810 {
811 auto *mdata = m_currentPlaylist->GetItemCurrent()->GetData().value<MusicMetadata*>();
812 if (mdata)
813 {
814 if (action == "INFO")
815 showTrackInfo(mdata);
816 else
817 editTrackInfo(mdata);
818 }
819 }
820 }
821 else
822 {
823 if (action == "INFO")
825 else
827 }
828 }
829 else if (action == "DELETE" && m_currentPlaylist && GetFocusWidget() == m_currentPlaylist)
830 {
832 if (item)
833 {
834 auto *mdata = item->GetData().value<MusicMetadata*>();
835 if (mdata)
836 gPlayer->removeTrack(mdata->ID());
837 }
838 }
839 else if (action == "MENU")
840 {
841 ShowMenu();
842 }
843 else if (action == "REFRESH")
844 {
847 }
848 else if (action == "MARK")
849 {
850 if (!m_moveTrackMode)
851 {
852 m_moveTrackMode = true;
853 m_movingTrack = false;
854
857 }
858 else
859 {
860 m_moveTrackMode = false;
861
863 {
865 if (item)
866 item->DisplayState("off", "movestate");
867
868 m_movingTrack = false;
869 }
870
873 }
874 }
875 else if (action == "SWITCHTOPLAYLIST" && m_currentView != MV_PLAYLIST)
876 {
878 }
879 else if (action == "SWITCHTOPLAYLISTEDITORTREE" && m_currentView != MV_PLAYLISTEDITORTREE)
880 {
882 }
883 else if (action == "SWITCHTOPLAYLISTEDITORGALLERY" && m_currentView != MV_PLAYLISTEDITORGALLERY)
884 {
886 }
887 else if (action == "SWITCHTOSEARCH" && m_currentView != MV_SEARCH)
888 {
890 }
891 else if (action == "SWITCHTOVISUALISER" && m_currentView != MV_VISUALIZER)
892 {
894 }
895 else if (action == "SWITCHTORADIO" && m_currentView != MV_RADIO)
896 {
898 }
899 else if (action == "TOGGLESHUFFLE")
900 {
902 updateShuffleMode(true);
903 }
904 else if (action == "TOGGLEREPEAT")
905 {
908 }
909 else
910 {
911 handled = false;
912 }
913 }
914
915 if (!handled && MythScreenType::keyPressEvent(e))
916 handled = true;
917 return handled;
918}
919
920void MusicCommon::changeVolume(bool up) const
921{
923 {
924 if (up)
926 else
928 showVolume();
929 }
930}
931
933{
935 {
936 if (up)
937 gPlayer->incSpeed();
938 else
939 gPlayer->decSpeed();
940 showSpeed(true);
941 updatePlaylistStats(); // update trackspeed and map for templates
942 }
943}
944
946{
947 if (m_controlVolume)
948 {
950 showVolume();
951 }
952}
953
955{
956 if (gPlayer->getOutput())
958}
959
961{
962 if (!m_trackProgress)
963 return;
964
966 {
967 // radio mode so show the buffer fill level since we don't know the track length
968 int available = 0;
969 int maxSize = 0;
970 gPlayer->getBufferStatus(&available, &maxSize);
971
972 if (m_infoText)
973 {
974 QString status = QString("%1%").arg((int)(100.0 / ((double)maxSize / (double)available)));
975 m_infoText->SetText(status);
976 }
977
978 if (m_trackProgress)
979 {
980 m_trackProgress->SetTotal(maxSize);
981 m_trackProgress->SetUsed(available);
982 }
983 }
984 else
985 {
986 // show the track played time
987 int percentplayed = 1;
988 if (m_maxTime > 0s)
989 percentplayed = m_currentTime * 100 / m_maxTime;
991 m_trackProgress->SetUsed(percentplayed);
992 }
993}
994
996{
997 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
998
999 auto *vol = new MythMusicVolumeDialog(popupStack, "volumepopup");
1000
1001 if (!vol->Create())
1002 {
1003 delete vol;
1004 return;
1005 }
1006
1007 popupStack->AddScreen(vol);
1008}
1009
1010void MusicCommon::showSpeed([[maybe_unused]] bool show)
1011{
1012}
1013
1014void MusicCommon::switchVisualizer(const QString &visual)
1015{
1016 switchVisualizer(m_visualModes.indexOf(visual));
1017}
1018
1020{
1021 if (!m_mainvisual)
1022 return;
1023
1024 if (visual < 0 || visual > m_visualModes.count() - 1)
1025 visual = 0;
1026
1027 m_currentVisual = visual;
1028
1030
1031 if (m_visualText)
1033}
1034
1036{
1037 if (!m_mainvisual)
1038 return;
1039
1040 // Only change the visualizer if there is more than 1 visualizer
1041 // and the user currently has a visualizer active
1042 if (m_visualModes.count() > 1)
1043 {
1045 {
1046 unsigned int next_visualizer = m_currentVisual;
1047
1048 //Find a visual thats not like the previous visual
1049 while (next_visualizer == m_currentVisual)
1050 next_visualizer = MythRandom(0, m_visualModes.count() - 1);
1051 m_currentVisual = next_visualizer;
1052 }
1053 else
1054 {
1055 //Change to the next selected visual
1057 }
1058
1059 //Change to the new visualizer
1061 }
1062}
1063
1065{
1067 return;
1068
1070}
1071
1073{
1075 return;
1076
1078}
1079
1081{
1082 LCD *lcd = LCD::Get();
1083 if (!lcd || !mdata)
1084 return;
1085
1086 // Set the Artist and Tract on the LCD
1087 lcd->switchToMusic(mdata->Artist(),
1088 mdata->Album(),
1089 mdata->Title());
1090}
1091
1093{
1094 if (LCD *lcd = LCD::Get())
1095 {
1096 lcd->switchToTime();
1097 }
1098
1100
1101 gPlayer->stop(true);
1102}
1103
1105{
1106 gPlayer->play();
1107}
1108
1110{
1111 gPlayer->pause();
1112}
1113
1115{
1116 gPlayer->stop();
1117
1118 if (m_timeText)
1120 if (m_infoText)
1121 m_infoText->Reset();
1122}
1123
1125{
1128
1129
1130 gPlayer->next();
1131}
1132
1134{
1137
1138 gPlayer->previous();
1139}
1140
1142{
1143 std::chrono::seconds nextTime = m_currentTime + 5s;
1144 nextTime = std::clamp(nextTime, 0s, m_maxTime);
1145 seek(nextTime);
1146}
1147
1149{
1150 // I don't know why, but seeking before 00:05 fails. Repeated
1151 // rewind under 00:05 can hold time < 5s while the music plays.
1152 // Time starts incrementing from zero but is now several seconds
1153 // behind. Finishing a track after this records a truncated
1154 // length. We can workaround this by limiting rewind to 1s.
1155 std::chrono::seconds nextTime = m_currentTime - 5s;
1156 nextTime = std::clamp(nextTime, 1s, m_maxTime); // #787
1157 seek(nextTime);
1158}
1159
1160void MusicCommon::seek(std::chrono::seconds pos)
1161{
1162 if (gPlayer->getOutput())
1163 {
1164 Decoder *decoder = gPlayer->getDecoder();
1165 if (decoder && decoder->isRunning())
1166 {
1167 decoder->lock();
1168 decoder->seek(pos.count());
1169
1170 if (m_mainvisual)
1171 {
1172 m_mainvisual->mutex()->lock();
1174 m_mainvisual->mutex()->unlock();
1175 }
1176
1177 decoder->unlock();
1178 }
1179
1180 gPlayer->getOutput()->SetTimecode(pos);
1181
1182 if (!gPlayer->isPlaying())
1183 {
1184 m_currentTime = pos;
1185 if (m_timeText)
1187
1189
1190 if (LCD *lcd = LCD::Get())
1191 {
1192 float percent_heard = m_maxTime <= 0s ? 0.0F : ((float)pos.count() /
1193 (float)m_maxTime.count());
1194
1195 QString lcd_time_string = getTimeString(pos, m_maxTime);
1196
1197 // if the string is longer than the LCD width, remove all spaces
1198 if (lcd_time_string.length() > lcd->getLCDWidth())
1199 lcd_time_string.remove(' ');
1200
1201 lcd->setMusicProgress(lcd_time_string, percent_heard);
1202 }
1203 }
1204 }
1205}
1206
1207void MusicCommon::changeRating(bool increase)
1208{
1210 return;
1211
1212 // Rationale here is that if you can't get visual feedback on ratings
1213 // adjustments, you probably should not be changing them
1214 // TODO: should check if the rating is visible in the playlist buttontlist
1215 //if (!m_ratingState)
1216 // return;
1217
1219 if (!curMeta)
1220 return;
1221
1222
1223 if (increase)
1224 curMeta->incRating();
1225 else
1226 curMeta->decRating();
1227
1229}
1230
1231void MusicCommon::customEvent(QEvent *event)
1232{
1233 QString statusString;
1234
1235 if (event->type() == AudioOutput::Event::kPlaying)
1236 {
1238 if (curMeta)
1239 updateTrackInfo(curMeta);
1240
1241 statusString = tr("Playing stream.");
1242 if (gPlayer->isPlaying())
1243 {
1244 if (m_stopButton)
1245 m_stopButton->SetLocked(false);
1246 if (m_playButton)
1247 m_playButton->SetLocked(true);
1248 if (m_pauseButton)
1249 m_pauseButton->SetLocked(false);
1250 if (m_trackState)
1251 m_trackState->DisplayState("playing");
1252
1254 {
1256 if (item)
1257 {
1258 item->SetFontState("running");
1259 item->DisplayState("playing", "playstate");
1260 }
1261 }
1262
1264
1265 updateVolume();
1266 }
1267 }
1268 else if (event->type() == AudioOutput::Event::kBuffering)
1269 {
1270 statusString = tr("Buffering stream.");
1271 }
1272 else if (event->type() == AudioOutput::Event::kPaused)
1273 {
1274 statusString = tr("Stream paused.");
1275
1276 if (m_stopButton)
1277 m_stopButton->SetLocked(false);
1278 if (m_playButton)
1279 m_playButton->SetLocked(false);
1280 if (m_pauseButton)
1281 m_pauseButton->SetLocked(true);
1282 if (m_trackState)
1283 m_trackState->DisplayState("paused");
1284
1286 {
1288 if (item)
1289 {
1290 item->SetFontState("idle");
1291 item->DisplayState("paused", "playstate");
1292 }
1293 }
1294 }
1295 else if (event->type() == AudioOutput::Event::kInfo)
1296 {
1297
1298 auto *oe = dynamic_cast<AudioOutput::Event *>(event);
1299
1300 if (!oe)
1301 return;
1302
1303 std::chrono::seconds rs = 0s;
1305
1307 {
1308 if (curMeta)
1309 m_currentTime = rs = duration_cast<std::chrono::seconds>(curMeta->Length());
1310 else
1311 m_currentTime = 0s;
1312 }
1313 else
1314 {
1315 m_currentTime = rs = oe->elapsedSeconds();
1316 }
1317
1318 QString time_string = getTimeString(rs, m_maxTime);
1319
1321
1322 if (curMeta)
1323 {
1324 if (LCD *lcd = LCD::Get())
1325 {
1326 float percent_heard = m_maxTime <= 0s ?
1327 0.0F:((float)rs.count() / (float)curMeta->Length().count()) * 1000.0F;
1328
1329 QString lcd_time_string = time_string;
1330
1331 // if the string is longer than the LCD width, remove all spaces
1332 if (time_string.length() > lcd->getLCDWidth())
1333 lcd_time_string.remove(' ');
1334
1335 lcd->setMusicProgress(lcd_time_string, percent_heard);
1336 }
1337 }
1338
1339 QString info_string;
1340
1341 // Hack around for cd bitrates
1342 if (oe->bitrate() < 2000)
1343 {
1344 info_string = QString("%1 "+tr("kbps")+ " %2 "+ tr("kHz")+ " %3 "+ tr("ch"))
1345 .arg(oe->bitrate())
1346 .arg(static_cast<double>(oe->frequency()) / 1000.0,0,'f',1,QChar('0'))
1347 .arg(oe->channels() > 1 ? "2" : "1");
1348 }
1349 else
1350 {
1351 info_string = QString("%1 "+ tr("kHz")+ " %2 "+ tr("ch"))
1352 .arg(static_cast<double>(oe->frequency()) / 1000.0,0,'f',1,QChar('0'))
1353 .arg(oe->channels() > 1 ? "2" : "1");
1354 }
1355
1356 if (curMeta)
1357 {
1358 if (m_timeText)
1359 m_timeText->SetText(time_string);
1360 if (m_infoText)
1361 m_infoText->SetText(info_string);
1362 }
1363
1364 // TODO only need to update the playlist times here
1366 }
1367 else if (event->type() == AudioOutput::Event::kStopped)
1368 {
1369 statusString = tr("Stream stopped.");
1370 if (m_stopButton)
1371 m_stopButton->SetLocked(true);
1372 if (m_playButton)
1373 m_playButton->SetLocked(false);
1374 if (m_pauseButton)
1375 m_pauseButton->SetLocked(false);
1376 if (m_trackState)
1377 m_trackState->DisplayState("stopped");
1378
1380 {
1382 if (item)
1383 {
1384 item->SetFontState("normal");
1385 item->DisplayState("stopped", "playstate");
1386 }
1387 }
1388
1390 }
1391 else if (event->type() == DialogCompletionEvent::kEventType)
1392 {
1393 auto *dce = dynamic_cast<DialogCompletionEvent*>(event);
1394
1395 // make sure the user didn't ESCAPE out of the menu
1396 if ((dce == nullptr) || (dce->GetResult() < 0))
1397 return;
1398
1399 QString resultid = dce->GetId();
1400 QString resulttext = dce->GetResultText();
1401
1402 if (resultid == "mainmenu")
1403 {
1404 if (resulttext == tr("Fullscreen Visualizer"))
1405 {
1407 }
1408 else if (resulttext == tr("Playlist Editor") ||
1409 resulttext == tr("Browse Music Library"))
1410 {
1411 if (gCoreContext->GetSetting("MusicPlaylistEditorView", "tree") == "tree")
1413 else
1415 }
1416 else if (resulttext == tr("Search for Music"))
1417 {
1419 }
1420 else if (resulttext == tr("Switch To Gallery View"))
1421 {
1423 }
1424 else if (resulttext == tr("Switch To Tree View"))
1425 {
1427 }
1428 else if (resulttext == tr("Lyrics"))
1429 {
1431 }
1432 }
1433 else if (resultid == "submenu")
1434 {
1435 if (resulttext == tr("Search List..."))
1437 }
1438 else if (resultid == "playlistmenu")
1439 {
1440 if (resulttext == tr("Sync List With Current Track"))
1441 {
1443 }
1444 else if (resulttext == tr("Remove Selected Track"))
1445 {
1447 if (item)
1448 {
1449 auto *mdata = item->GetData().value<MusicMetadata*>();
1450 if (mdata)
1451 gPlayer->removeTrack(mdata->ID());
1452 }
1453 }
1454 else if (resulttext == tr("Remove All Tracks"))
1455 {
1457 {
1459 gPlayer->activePlaylistChanged(-1, true);
1460 }
1461 }
1462 else if (resulttext == tr("Save To New Playlist"))
1463 {
1464 QString message = tr("Enter new playlist name");
1465
1466 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1467
1468 auto *inputdialog = new MythTextInputDialog(popupStack, message);
1469
1470 if (inputdialog->Create())
1471 {
1472 inputdialog->SetReturnEvent(this, "addplaylist");
1473 popupStack->AddScreen(inputdialog);
1474 }
1475 else
1476 {
1477 delete inputdialog;
1478 }
1479 }
1480 else if (resulttext == tr("Save To Existing Playlist"))
1481 {
1482 QString message = tr("Select the playlist to save to");
1483 QStringList playlists = gMusicData->m_all_playlists->getPlaylistNames();
1484
1485 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1486
1487 auto *searchdialog = new MythUISearchDialog(popupStack, message, playlists);
1488
1489 if (searchdialog->Create())
1490 {
1491 searchdialog->SetReturnEvent(this, "updateplaylist");
1492 popupStack->AddScreen(searchdialog);
1493 }
1494 }
1495 else if (resulttext == tr("Switch To Move Mode"))
1496 {
1497 m_moveTrackMode = true;
1498 m_movingTrack = false;
1499
1502 }
1503 else if (resulttext == tr("Switch To Select Mode"))
1504 {
1505 m_moveTrackMode = false;
1506
1508 {
1510 if (item)
1511 item->DisplayState("off", "movestate");
1512
1513 m_movingTrack = false;
1514 }
1515
1518 }
1519 }
1520 else if (resultid == "repeatmenu")
1521 {
1522 int mode = dce->GetData().toInt();
1525 }
1526 else if (resultid == "shufflemenu")
1527 {
1528 int mode = dce->GetData().toInt();
1530 updateShuffleMode(true);
1531 }
1532 else if (resultid == "exitmenu")
1533 {
1534 if (resulttext == tr("No - Exit, Stop Playing"))
1535 {
1537 stopAll();
1538 Close();
1539 }
1540 else if (resulttext == tr("Yes - Exit, Continue Playing"))
1541 {
1542 Close();
1543 }
1544 }
1545 else if (resultid == "playermenu")
1546 {
1547 if (resulttext == tr("Change Volume"))
1548 showVolume();
1549 else if (resulttext == tr("Mute"))
1550 toggleMute();
1551 else if (resulttext == tr("Previous Track"))
1552 previous();
1553 else if (resulttext == tr("Next Track"))
1554 next();
1555 else if (resulttext == tr("Jump Back"))
1556 seekback();
1557 else if (resulttext == tr("Jump Forward"))
1558 seekforward();
1559 else if (resulttext == tr("Play"))
1560 play();
1561 else if (resulttext == tr("Stop"))
1562 stop();
1563 else if (resulttext == tr("Pause"))
1564 pause();
1565 }
1566 else if (resultid == "quickplaylistmenu")
1567 {
1568 if (resulttext == tr("All Tracks"))
1569 allTracks();
1570 else if (resulttext == tr("From CD"))
1571 fromCD();
1572 else if (resulttext == tr("Tracks By Current Artist"))
1573 byArtist();
1574 else if (resulttext == tr("Tracks From Current Genre"))
1575 byGenre();
1576 else if (resulttext == tr("Tracks From Current Album"))
1577 byAlbum();
1578 else if (resulttext == tr("Tracks From Current Year"))
1579 byYear();
1580 else if (resulttext == tr("Tracks With Same Title"))
1581 byTitle();
1582 }
1583 else if (resultid == "playlistoptionsmenu")
1584 {
1585 if (resulttext == tr("Replace Tracks"))
1586 {
1589 }
1590 else if (resulttext == tr("Add Tracks"))
1591 {
1594 }
1595 else if (resulttext == tr("Play Now"))
1596 { // cancel shuffles and repeats to play only this now
1604 }
1605 else if (resulttext == tr("Prefer Play Now"))
1606 {
1608 }
1609 else if (resulttext == tr("Prefer Add Tracks"))
1610 {
1612 }
1613 }
1614 else if (resultid == "visualizermenu")
1615 {
1616 if (dce->GetResult() >= 0)
1617 {
1618 m_currentVisual = dce->GetData().toInt();
1619
1620 //Change to the new visualizer
1622 }
1623 }
1624 else if (resultid == "addplaylist")
1625 {
1627 Playlist *playlist = gMusicData->m_all_playlists->getPlaylist(resulttext);
1628 gPlayer->playlistChanged(playlist->getID());
1629 }
1630 else if (resultid == "updateplaylist")
1631 {
1633 {
1634 Playlist *playlist = gMusicData->m_all_playlists->getPlaylist(resulttext);
1635 QString songList = gPlayer->getCurrentPlaylist()->toRawSonglist();
1636 playlist->removeAllTracks();
1637 playlist->fillSongsFromSonglist(songList);
1638 playlist->changed();
1639 gPlayer->playlistChanged(playlist->getID());
1640 }
1641 }
1642 }
1643 else if (event->type() == MusicPlayerEvent::kTrackChangeEvent)
1644 {
1645 auto *mpe = dynamic_cast<MusicPlayerEvent *>(event);
1646
1647 if (!mpe)
1648 return;
1649
1650 int trackNo = mpe->m_trackID;
1651
1653 {
1654 if (m_currentTrack >= 0 && m_currentTrack < m_currentPlaylist->GetCount())
1655 {
1657 if (item)
1658 {
1659 item->SetFontState("normal");
1660 item->DisplayState("default", "playstate");
1661 }
1662 }
1663
1664 if (trackNo >= 0 && trackNo < m_currentPlaylist->GetCount())
1665 {
1668
1670 if (item)
1671 {
1672 item->SetFontState("running");
1673 item->DisplayState("playing", "playstate");
1674 }
1675 }
1676 }
1677
1678 m_currentTrack = trackNo;
1679
1684 {
1687 }
1688
1691 }
1692 else if (event->type() == MusicPlayerEvent::kVolumeChangeEvent)
1693 {
1694 updateVolume();
1695 }
1696 else if (event->type() == MusicPlayerEvent::kTrackRemovedEvent)
1697 {
1698 auto *mpe = dynamic_cast<MusicPlayerEvent *>(event);
1699
1700 if (!mpe)
1701 return;
1702
1703 int trackID = mpe->m_trackID;
1704
1706 {
1707 // find and remove the list item for the removed track
1708 for (int x = 0; x < m_currentPlaylist->GetCount(); x++)
1709 {
1711 auto *mdata = item->GetData().value<MusicMetadata*>();
1712 if (mdata && mdata->ID() == (MusicMetadata::IdType) trackID)
1713 {
1715 x -= 1; // remove all entries, or:
1716 // break; // remove only first entry
1717 }
1718 }
1719 }
1720
1722
1723 // if we have just removed the playing track from the playlist
1724 // move to the next track
1726 {
1727 if (gPlayer->getCurrentMetadata()->ID() == (MusicMetadata::IdType) trackID)
1728 gPlayer->next();
1729 }
1730
1736
1739 }
1740 else if (event->type() == MusicPlayerEvent::kTrackAddedEvent)
1741 {
1742 auto *mpe = dynamic_cast<MusicPlayerEvent *>(event);
1743
1744 if (!mpe)
1745 return;
1746
1747 int trackID = mpe->m_trackID;
1748
1750 {
1751 if (trackID == -1)
1752 {
1753 // more than one track has been added so easier to just reload the list
1755 }
1756 else
1757 {
1758 // just one track was added so just add that track to the end of the list
1759 MusicMetadata *mdata = gMusicData->m_all_music->getMetadata(trackID);
1760
1761 if (mdata)
1762 {
1763 InfoMap metadataMap;
1764 mdata->toMap(metadataMap);
1765
1766 auto *item = new MythUIButtonListItem(m_currentPlaylist, "",
1767 QVariant::fromValue(mdata));
1768
1769 item->SetTextFromMap(metadataMap);
1770
1772 mdata->ID() == gPlayer->getCurrentMetadata()->ID())
1773 {
1774 item->SetFontState("running");
1775 item->DisplayState("playing", "playstate");
1776 }
1777 else
1778 {
1779 item->SetFontState("normal");
1780 item->DisplayState("default", "playstate");
1781 }
1782
1785 }
1786
1789 }
1790 }
1791
1795
1796 updateUIPlaylist(); // else album art doesn't update
1799 }
1800 else if (event->type() == MusicPlayerEvent::kAllTracksRemovedEvent)
1801 {
1804 updateTrackInfo(nullptr);
1805 }
1806 else if (event->type() == MusicPlayerEvent::kMetadataChangedEvent ||
1808 {
1809 auto *mpe = dynamic_cast<MusicPlayerEvent *>(event);
1810
1811 if (!mpe)
1812 return;
1813
1814 uint trackID = mpe->m_trackID;
1815
1817 {
1818 for (int x = 0; x < m_currentPlaylist->GetCount(); x++)
1819 {
1821 auto *mdata = item->GetData().value<MusicMetadata*>();
1822
1823 if (mdata && mdata->ID() == trackID)
1824 {
1825 InfoMap metadataMap;
1826 mdata->toMap(metadataMap);
1827 item->SetTextFromMap(metadataMap);
1828
1829 item->DisplayState(QString("%1").arg(mdata->Rating()), "ratingstate");
1830 }
1831 }
1832 }
1833
1835 {
1836 for (int x = 0; x < m_playedTracksList->GetCount(); x++)
1837 {
1839 auto *mdata = item->GetData().value<MusicMetadata*>();
1840
1841 if (mdata && mdata->ID() == trackID)
1842 {
1843 InfoMap metadataMap;
1844 mdata->toMap(metadataMap);
1845 item->SetTextFromMap(metadataMap);
1846 }
1847 }
1848 }
1849
1850 if (gPlayer->getCurrentMetadata() && trackID == gPlayer->getCurrentMetadata()->ID())
1852
1853 // this will ensure the next track info gets updated
1854 if (gPlayer->getNextMetadata() && trackID == gPlayer->getNextMetadata()->ID())
1856 }
1857 else if (event->type() == MusicPlayerEvent::kAlbumArtChangedEvent)
1858 {
1859 auto *mpe = dynamic_cast<MusicPlayerEvent *>(event);
1860
1861 if (!mpe)
1862 return;
1863
1864 uint trackID = mpe->m_trackID;
1865
1867 {
1868 for (int x = 0; x < m_currentPlaylist->GetCount(); x++)
1869 {
1871 auto *mdata = item->GetData().value<MusicMetadata*>();
1872 if (mdata && mdata->ID() == trackID)
1873 {
1874 // reload the albumart image if one has already been loaded for this track
1875 if (!item->GetImageFilename().isEmpty())
1876 {
1877 QString artFile = mdata->getAlbumArtFile();
1878 if (artFile.isEmpty())
1879 {
1880 item->SetImage("");
1881 item->SetImage("", "coverart");
1882 }
1883 else
1884 {
1885 item->SetImage(mdata->getAlbumArtFile());
1886 item->SetImage(mdata->getAlbumArtFile(), "coverart");
1887 }
1888 }
1889 }
1890 }
1891 }
1892
1893 if (gPlayer->getCurrentMetadata() && trackID == gPlayer->getCurrentMetadata()->ID())
1895 }
1896 else if (event->type() == MusicPlayerEvent::kTrackUnavailableEvent)
1897 {
1898 auto *mpe = dynamic_cast<MusicPlayerEvent *>(event);
1899
1900 if (!mpe)
1901 return;
1902
1903 uint trackID = mpe->m_trackID;
1904
1906 {
1907 for (int x = 0; x < m_currentPlaylist->GetCount(); x++)
1908 {
1910 auto *mdata = item->GetData().value<MusicMetadata*>();
1911 if (mdata && mdata->ID() == trackID)
1912 {
1913 item->SetFontState("disabled");
1914 item->DisplayState("unavailable", "playstate");
1915 }
1916 }
1917 }
1918 }
1919}
1920
1922{
1923 if (!m_controlVolume)
1924 {
1925 if (m_volumeText)
1926 m_volumeText->Hide();
1927
1928 if (m_muteState)
1929 m_muteState->Hide();
1930
1931 return;
1932 }
1933
1934 if (m_volumeText)
1935 {
1936 InfoMap map;
1937 gPlayer->toMap(map);
1939 }
1940
1941 if (m_muteState)
1942 {
1943 bool muted = gPlayer->isMuted();
1944 m_muteState->DisplayState(muted ? "on" : "off");
1945 }
1946}
1947
1949{
1950 if (!mdata)
1951 return;
1952
1954
1955 auto *editDialog = new EditMetadataDialog(mainStack, mdata);
1956
1957 if (!editDialog->Create())
1958 {
1959 delete editDialog;
1960 return;
1961 }
1962
1963 mainStack->AddScreen(editDialog);
1964}
1965
1967{
1968 if (!mdata)
1969 {
1970 InfoMap metadataMap;
1971 MusicMetadata metadata;
1972 metadata.toMap(metadataMap);
1973 metadata.toMap(metadataMap, "next");
1974 ResetMap(metadataMap);
1975
1976 if (m_coverartImage)
1978 if (m_ratingState)
1980 if (m_timeText)
1981 m_timeText->Reset();
1982 if (m_infoText)
1983 m_infoText->Reset();
1984 if (m_trackProgress)
1986
1987 if (m_mainvisual)
1989
1990 return;
1991 }
1992
1994 m_maxTime = 0s;
1995 else
1996 m_maxTime = duration_cast<std::chrono::seconds>(mdata->Length());
1997
1998 // get map for current track
1999 InfoMap metadataMap;
2000 mdata->toMap(metadataMap);
2001
2002 // add the map from the next track
2003 MusicMetadata *nextMetadata = gPlayer->getNextMetadata();
2004 if (nextMetadata)
2005 nextMetadata->toMap(metadataMap, "next");
2006
2007 // now set text using the map
2008 SetTextFromMap(metadataMap);
2009
2010 if (m_coverartImage)
2011 {
2012 QString filename = mdata->getAlbumArtFile();
2013 if (!filename.isEmpty())
2014 {
2017 }
2018 else
2019 {
2021 }
2022 }
2023
2024 if (m_ratingState)
2025 m_ratingState->DisplayState(QString("%1").arg(mdata->Rating()));
2026
2027 setTrackOnLCD(mdata);
2028}
2029
2031{
2032 if (!mdata)
2033 return;
2034
2035 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
2036
2037 auto *dlg = new TrackInfoDialog(popupStack, mdata, "trackinfopopup");
2038
2039 if (!dlg->Create())
2040 {
2041 delete dlg;
2042 return;
2043 }
2044
2045 popupStack->AddScreen(dlg);
2046}
2047
2049{
2050 if (!item)
2051 return;
2052
2053 if (m_moveTrackMode)
2054 {
2056
2057 if (m_movingTrack)
2058 item->DisplayState("on", "movestate");
2059 else
2060 item->DisplayState("off", "movestate");
2061 }
2062 else
2063 {
2065 }
2066
2069}
2070
2072{
2073 if (!item)
2074 return;
2075
2076 auto *mdata = item->GetData().value<MusicMetadata*>();
2077 if (mdata && item->GetText() == " ")
2078 {
2079 if (item->GetImageFilename().isEmpty())
2080 {
2081 QString artFile = mdata->getAlbumArtFile();
2082 if (artFile.isEmpty())
2083 {
2084 item->SetImage("");
2085 item->SetImage("", "coverart");
2086 }
2087 else
2088 {
2089 item->SetImage(mdata->getAlbumArtFile());
2090 item->SetImage(mdata->getAlbumArtFile(), "coverart");
2091 }
2092 }
2093
2094 InfoMap metadataMap;
2095 mdata->toMap(metadataMap);
2096 item->SetText("");
2097 item->SetTextFromMap(metadataMap);
2098 item->DisplayState(QString("%1").arg(mdata->Rating()), "ratingstate");
2099 }
2100}
2101
2103{
2106
2107 if (!m_currentPlaylist)
2108 return;
2109
2111
2112 m_currentTrack = -1;
2113
2114 Playlist *playlist = gPlayer->getCurrentPlaylist();
2115
2116 if (!playlist)
2117 return;
2118
2119 for (int x = 0; x < playlist->getTrackCount(); x++)
2120 {
2121 MusicMetadata *mdata = playlist->getSongAt(x);
2122 if (mdata)
2123 {
2124 auto *item = new MythUIButtonListItem(m_currentPlaylist, " ",
2125 QVariant::fromValue(mdata));
2126
2127 item->SetText(mdata->Artist() + mdata->Album() + mdata->Title(), "**search**");
2128 item->SetFontState("normal");
2129 item->DisplayState("default", "playstate");
2130
2131 // if this is the current track update its play state to match the player
2132 if (gPlayer->getCurrentMetadata() && mdata->ID() == gPlayer->getCurrentMetadata()->ID())
2133 {
2134 if (gPlayer->isPlaying())
2135 {
2136 item->SetFontState("running");
2137 item->DisplayState("playing", "playstate");
2138 }
2139 else if (gPlayer->isPaused())
2140 {
2141 item->SetFontState("idle");
2142 item->DisplayState("paused", "playstate");
2143 }
2144 else
2145 {
2146 item->SetFontState("normal");
2147 item->DisplayState("stopped", "playstate");
2148 }
2149
2152 }
2153 }
2154 }
2155}
2156
2158{
2159 if (!m_playedTracksList)
2160 return;
2161
2163
2164 QList<MusicMetadata*> playedList = gPlayer->getPlayedTracksList();
2165
2166 for (int x = playedList.count(); x > 0; x--)
2167 {
2168 MusicMetadata *mdata = playedList[x-1];
2169 auto *item = new MythUIButtonListItem(m_playedTracksList, "",
2170 QVariant::fromValue(mdata));
2171
2172 InfoMap metadataMap;
2173 mdata->toMap(metadataMap);
2174 item->SetTextFromMap(metadataMap);
2175
2176 item->SetFontState("normal");
2177 item->DisplayState("default", "playstate");
2178
2179 item->SetImage(mdata->getAlbumArtFile());
2180 }
2181}
2182
2184{
2185 int trackCount = 0;
2186
2188 trackCount = gPlayer->getCurrentPlaylist()->getTrackCount();
2189
2190 InfoMap map;
2191 if (gPlayer->isPlaying() && trackCount > 0)
2192 {
2193 QString playlistcurrent = QLocale::system().toString(m_currentTrack + 1);
2194 QString playlisttotal = QLocale::system().toString(trackCount);
2195
2196 map["playlistposition"] = tr("%1 of %2").arg(playlistcurrent, playlisttotal);
2197 map["playlistcurrent"] = playlistcurrent;
2198 map["playlistcount"] = playlisttotal;
2200 map["playlistplayedtime"] = getTimeString(m_playlistPlayedTime + m_currentTime, 0s);
2201 map["playlisttotaltime"] = getTimeString(m_playlistMaxTime, 0s);
2202 QString playlistName = gPlayer->getCurrentPlaylist() ? gPlayer->getCurrentPlaylist()->getName() : "";
2203 if (playlistName == "default_playlist_storage")
2204 playlistName = tr("Default Playlist");
2205 else if (playlistName == "stream_playlist")
2206 playlistName = tr("Stream Playlist");
2207 map["playlistname"] = playlistName;
2208 map["playedtime"] = getTimeString(m_currentTime, 0s); // v34 - parts
2209 map["totaltime"] = getTimeString(m_maxTime, 0s);
2210 map["trackspeed"] = getTimeString(-1s, 0s);
2211 }
2212 else
2213 {
2214 map["playlistposition"] = "";
2215 map["playlistcurrent"] = "";
2216 map["playlistcount"] = "";
2217 map["playlisttime"] = "";
2218 map["playlistplayedtime"] = "";
2219 map["playlisttotaltime"] = "";
2220 map["playlistname"] = "";
2221 map["playedtime"] = ""; // v34 - parts for track templates
2222 map["totaltime"] = "";
2223 map["trackspeed"] = "";
2224 }
2225
2226 SetTextFromMap(map);
2227
2230}
2231
2232QString MusicCommon::getTimeString(std::chrono::seconds exTime, std::chrono::seconds maxTime)
2233{
2234 if (exTime > -1s && maxTime <= 0s)
2235 return MythDate::formatTime(exTime,
2236 (exTime >= 1h) ? "H:mm:ss" : "mm:ss");
2237
2238 QString fmt = (maxTime >= 1h) ? "H:mm:ss" : "mm:ss";
2239 QString out = MythDate::formatTime(exTime, fmt)
2240 + " / " + MythDate::formatTime(maxTime, fmt);
2241 float speed = gPlayer->getSpeed(); // v34 - show altered speed
2242 QString speedstr = "";
2243 if (lroundf(speed * 100.0F) != 100.0F)
2244 {
2245 speedstr = QString("%1").arg(speed);
2246 out += ", " + speedstr + "X";
2247 }
2248 if (exTime <= -1s)
2249 return speedstr;
2250 return out;
2251}
2252
2254{
2255 auto *buttonList = dynamic_cast<MythUIButtonList *>(GetFocusWidget());
2256 if (buttonList)
2257 buttonList->ShowSearchDialog();
2258
2259 auto *buttonTree = dynamic_cast<MythUIButtonTree *>(GetFocusWidget());
2260 if (buttonTree)
2261 buttonTree->ShowSearchDialog();
2262}
2263
2265{
2266 MythMenu *mainMenu = createMainMenu();
2267
2268 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
2269
2270 auto *menuPopup = new MythDialogBox(mainMenu, popupStack, "actionmenu");
2271
2272 if (menuPopup->Create())
2273 popupStack->AddScreen(menuPopup);
2274 else
2275 delete mainMenu;
2276}
2277
2279{
2280 QString label = tr("View Actions");
2281
2282 auto *menu = new MythMenu(label, this, "mainmenu");
2283
2285 {
2286 menu->AddItem(tr("Switch To Gallery View"));
2287 }
2289 {
2290 menu->AddItem(tr("Switch To Tree View"));
2291 }
2292
2293 QStringList screenList;
2294 MythScreenType *screen = this;
2295 while (screen)
2296 {
2297 screenList.append(screen->objectName());
2298 screen = qobject_cast<MusicCommon*>(screen)->m_parentScreen;
2299 }
2300
2301 if (!screenList.contains("visualizerview"))
2302 menu->AddItem(tr("Fullscreen Visualizer"));
2303
2305 menu->AddItem(tr("Browse Music Library")); // v33- was "Playlist Editor"
2306
2308 if (!screenList.contains("searchview") && !screenList.contains("streamview"))
2309 menu->AddItem(tr("Search for Music"));
2310
2311 if (!screenList.contains("lyricsview"))
2312 menu->AddItem(tr("Lyrics"));
2313 }
2314
2315 menu->AddItem(tr("More Options"), nullptr, createSubMenu());
2316
2317 return menu;
2318}
2319
2321{
2322 QString label = tr("Actions");
2323
2324 auto *menu = new MythMenu(label, this, "submenu");
2325
2326 if (GetFocusWidget() && (GetFocusWidget()->inherits("MythUIButtonList") ||
2327 GetFocusWidget()->inherits("MythUIButtonTree")))
2328 menu->AddItem(tr("Search List..."));
2329
2331 {
2332 menu->AddItem(tr("Playlist Options"), nullptr, createPlaylistMenu());
2333 menu->AddItem(tr("Set Shuffle Mode"), nullptr, createShuffleMenu());
2334 menu->AddItem(tr("Set Repeat Mode"), nullptr, createRepeatMenu());
2335 }
2336
2337 menu->AddItem(tr("Player Options"), nullptr, createPlayerMenu());
2338
2340 menu->AddItem(tr("Quick Playlists"), nullptr, createQuickPlaylistsMenu());
2341
2343 menu->AddItem(tr("Change Visualizer"), nullptr, createVisualizerMenu());
2344
2345 return menu;
2346}
2347
2349{
2350 QString label = tr("Playlist Options");
2351
2352 auto *menu = new MythMenu(label, this, "playlistmenu");
2353
2355 {
2356 menu->AddItem(tr("Sync List With Current Track"));
2357 menu->AddItem(tr("Remove Selected Track"));
2358 }
2359
2360 menu->AddItem(tr("Remove All Tracks"));
2361
2363 {
2364 menu->AddItem(tr("Save To New Playlist"));
2365 menu->AddItem(tr("Save To Existing Playlist"));
2366
2367 if (m_moveTrackMode)
2368 menu->AddItem(tr("Switch To Select Mode"));
2369 else
2370 menu->AddItem(tr("Switch To Move Mode"));
2371 }
2372
2373 return menu;
2374}
2375
2377{
2378 QString label = tr("Exiting Music Player.\n\n"
2379 "Do you want to continue playing in the background?");
2380
2381 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
2382
2383 auto *menu = new MythDialogBox(label, popupStack, "exitmenu");
2384
2385 if (!menu->Create())
2386 {
2387 delete menu;
2388 return;
2389 }
2390
2391 menu->SetReturnEvent(this, "exitmenu");
2392
2393 menu->AddButton(tr("No - Exit, Stop Playing"));
2394 menu->AddButton(tr("Yes - Exit, Continue Playing"));
2395 menu->AddButton(tr("Cancel"));
2396
2397 popupStack->AddScreen(menu);
2398}
2399
2401{
2402 QString label = tr("Player Actions");
2403
2404 auto *menu = new MythMenu(label, this, "playermenu");
2405
2406 menu->AddItem(tr("Change Volume"));
2407 menu->AddItem(tr("Mute"));
2408 menu->AddItem(tr("Previous Track"));
2409 menu->AddItem(tr("Next Track"));
2410
2412 {
2413 menu->AddItem(tr("Jump Back"));
2414 menu->AddItem(tr("Jump Forward"));
2415 }
2416
2417 menu->AddItem(tr("Play"));
2418 menu->AddItem(tr("Stop"));
2419
2421 menu->AddItem(tr("Pause"));
2422
2423 return menu;
2424}
2425
2427{
2428 QString label = tr("Set Repeat Mode");
2429
2430 auto *menu = new MythMenu(label, this, "repeatmenu");
2431
2432 menu->AddItemV(tr("None"), QVariant::fromValue((int)MusicPlayer::REPEAT_OFF));
2433 menu->AddItemV(tr("Track"), QVariant::fromValue((int)MusicPlayer::REPEAT_TRACK));
2434 menu->AddItemV(tr("All"), QVariant::fromValue((int)MusicPlayer::REPEAT_ALL));
2435
2436 menu->SetSelectedByData(static_cast<int>(gPlayer->getRepeatMode()));
2437
2438 return menu;
2439}
2440
2442{
2443 QString label = tr("Set Shuffle Mode");
2444
2445 auto *menu = new MythMenu(label, this, "shufflemenu");
2446
2447 menu->AddItemV(tr("None"), QVariant::fromValue((int)MusicPlayer::SHUFFLE_OFF));
2448 menu->AddItemV(tr("Random"), QVariant::fromValue((int)MusicPlayer::SHUFFLE_RANDOM));
2449 menu->AddItemV(tr("Smart"), QVariant::fromValue((int)MusicPlayer::SHUFFLE_INTELLIGENT));
2450 menu->AddItemV(tr("Album"), QVariant::fromValue((int)MusicPlayer::SHUFFLE_ALBUM));
2451 menu->AddItemV(tr("Artist"), QVariant::fromValue((int)MusicPlayer::SHUFFLE_ARTIST));
2452
2453 menu->SetSelectedByData(static_cast<int>(gPlayer->getShuffleMode()));
2454
2455 return menu;
2456}
2457
2459{
2460 QString label = tr("Quick Playlists");
2461
2462 auto *menu = new MythMenu(label, this, "quickplaylistmenu");
2463
2464 menu->AddItem(tr("All Tracks"));
2465
2467 menu->AddItem(tr("From CD"));
2468
2470 {
2471 menu->AddItem(tr("Tracks By Current Artist"));
2472 menu->AddItem(tr("Tracks From Current Album"));
2473 menu->AddItem(tr("Tracks From Current Genre"));
2474 menu->AddItem(tr("Tracks From Current Year"));
2475 menu->AddItem(tr("Tracks With Same Title"));
2476 }
2477
2478 return menu;
2479}
2480
2482{
2483 QString label = tr("Choose Visualizer");
2484
2485 auto *menu = new MythMenu(label, this, "visualizermenu");
2486
2487 for (uint x = 0; x < static_cast<uint>(m_visualModes.count()); x++)
2488 menu->AddItemV(m_visualModes.at(x), QVariant::fromValue(x));
2489
2490 menu->SetSelectedByData(m_currentVisual);
2491
2492 return menu;
2493}
2494
2496{
2497 QString label = tr("Add to Playlist Options");
2498
2499 auto *menu = new MythMenu(label, this, "playlistoptionsmenu");
2500
2502 {
2503 menu->AddItem(tr("Play Now"));
2504 menu->AddItem(tr("Add Tracks"));
2505 menu->AddItem(tr("Replace Tracks"));
2506 menu->AddItem(tr("Prefer Add Tracks"));
2507 }
2508 else
2509 {
2510 menu->AddItem(tr("Add Tracks"));
2511 menu->AddItem(tr("Play Now"));
2512 menu->AddItem(tr("Replace Tracks"));
2513 menu->AddItem(tr("Prefer Play Now"));
2514 }
2515
2516 return menu;
2517}
2518
2520{
2521 m_whereClause = "ORDER BY music_artists.artist_name, album_name, disc_number, track";
2523}
2524
2526{
2527 m_whereClause = "";
2528 m_songList.clear();
2529
2530 // get the list of cd tracks
2531 for (int x = 1; x <= gMusicData->m_all_music->getCDTrackCount(); x++)
2532 {
2534 if (mdata)
2535 {
2536 m_songList.append(mdata->ID());
2537 }
2538 }
2539
2541}
2542
2544{
2546 if (!mdata)
2547 return;
2548
2549 QString value = formattedFieldValue(mdata->Artist().toUtf8().constData());
2550 m_whereClause = "WHERE music_artists.artist_name = " + value +
2551 " ORDER BY album_name, disc_number, track";
2552
2554}
2555
2557{
2559 if (!mdata)
2560 return;
2561
2562 QString value = formattedFieldValue(mdata->Album().toUtf8().constData());
2563 m_whereClause = "WHERE album_name = " + value +
2564 " ORDER BY disc_number, track";
2565
2567}
2568
2570{
2572 if (!mdata)
2573 return;
2574
2575 QString value = formattedFieldValue(mdata->Genre().toUtf8().constData());
2576 m_whereClause = "WHERE genre = " + value +
2577 " ORDER BY music_artists.artist_name, album_name, disc_number, track";
2578
2580}
2581
2583{
2585 if (!mdata)
2586 return;
2587
2588 QString value = formattedFieldValue(mdata->Year());
2589 m_whereClause = "WHERE music_songs.year = " + value +
2590 " ORDER BY music_artists.artist_name, album_name, disc_number, track";
2591
2593}
2594
2596{
2598 if (!mdata)
2599 return;
2600
2601 QString value = formattedFieldValue(mdata->Title().toUtf8().constData());
2602 m_whereClause = "WHERE music_songs.name = " + value +
2603 " ORDER BY music_artists.artist_name, album_name, disc_number, track";
2604
2606}
2607
2609{
2611 return;
2612
2614
2615 // Don't bother showing the dialog if the current playlist is empty
2617 {
2620 return;
2621 }
2622
2624
2625 if (addMainMenu)
2626 menu->AddItem(tr("More Options"), nullptr, createMainMenu());
2627
2628 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
2629
2630 auto *menuPopup = new MythDialogBox(menu, popupStack, "playlistoptionsmenu");
2631
2632 if (menuPopup->Create())
2633 popupStack->AddScreen(menuPopup);
2634 else
2635 delete menu;
2636}
2637
2639{
2640 int curTrackID = -1;
2641 int added = 0;
2642 int curPos = gPlayer->getCurrentTrackPos();
2643
2644 // store id of current track
2646 curTrackID = gPlayer->getCurrentMetadata()->ID();
2647
2648 if (!m_whereClause.isEmpty())
2649 {
2650 // update playlist from quick playlist
2652 m_whereClause, true,
2653 m_playlistOptions.insertPLOption, curTrackID);
2654 m_whereClause.clear();
2655 }
2656 else if (!m_songList.isEmpty())
2657 {
2658 // update playlist from song list (from the playlist editor)
2660 m_songList, true,
2661 m_playlistOptions.insertPLOption, curTrackID);
2662
2663 m_songList.clear();
2664 }
2665
2667
2669 Playlist *playlist = gPlayer->getCurrentPlaylist();
2670 if (nullptr == playlist)
2671 return;
2672
2673 // if (m_currentTrack == -1) // why? non-playing should also
2674 // playFirstTrack(); // start playing per options -twitham
2675 // else
2676 {
2678 {
2679 case PL_CURRENT:
2680 {
2681 if (!restorePosition(curTrackID))
2683
2684 break;
2685 }
2686
2687 case PL_FIRST:
2689 break;
2690
2691 case PL_FIRSTNEW:
2692 {
2694 {
2695 case PL_REPLACE:
2697 break;
2698
2699 case PL_INSERTATEND:
2700 {
2701 pause();
2702 if (!gPlayer->setCurrentTrackPos(playlist->getTrackCount() - added))
2704 break;
2705 }
2706
2708 if (!gPlayer->setCurrentTrackPos(curPos + 1))
2710 break;
2711
2712 default:
2714 }
2715
2716 break;
2717 }
2718 }
2719 }
2720
2725}
2726
2728{
2729 // try to move to the current track
2730 bool foundTrack = false;
2731
2732 Playlist *playlist = gPlayer->getCurrentPlaylist();
2733 if (nullptr == playlist)
2734 return false;
2735
2736 if (trackID != -1)
2737 {
2738 for (int x = 0; x < playlist->getTrackCount(); x++)
2739 {
2740 MusicMetadata *mdata = playlist->getSongAt(x);
2741 if (mdata && mdata->ID() == (MusicMetadata::IdType) trackID)
2742 {
2743 m_currentTrack = x;
2745 {
2748 if (item)
2749 {
2750 item->SetFontState("running");
2751 item->DisplayState("playing", "playstate");
2752 }
2753 }
2754
2755 foundTrack = true;
2756
2757 break;
2758 }
2759 }
2760 }
2761
2762 return foundTrack;
2763}
2764
2766{
2768}
2769
2770//---------------------------------------------------------
2771// MythMusicVolumeDialog
2772//---------------------------------------------------------
2773static constexpr std::chrono::milliseconds MUSICVOLUMEPOPUPTIME { 4s };
2774
2776{
2777 if (m_displayTimer)
2778 {
2779 m_displayTimer->stop();
2780 delete m_displayTimer;
2781 m_displayTimer = nullptr;
2782 }
2783}
2784
2786{
2787 if (!LoadWindowFromXML("music-ui.xml", "volume_popup", this))
2788 return false;
2789
2790 UIUtilW::Assign(this, m_volText, "volume");
2791 UIUtilW::Assign(this, m_volProgress, "volumeprogress");
2792 UIUtilW::Assign(this, m_muteState, "mutestate");
2793
2794 if (m_volProgress)
2795 m_volProgress->SetTotal(100);
2796
2797 updateDisplay();
2798
2799 m_displayTimer = new QTimer(this);
2801 m_displayTimer->setSingleShot(true);
2803
2804 return true;
2805}
2806
2808{
2809 QStringList actions;
2810 bool handled = GetMythMainWindow()->TranslateKeyPress("Music", event, actions, false);
2811
2812 for (int i = 0; i < actions.size() && !handled; i++)
2813 {
2814 const QString& action = actions[i];
2815 handled = true;
2816
2817 if (action == "UP" || action == "VOLUMEUP")
2819 else if (action == "DOWN" || action == "VOLUMEDOWN")
2821 else if (action == "MUTE" || action == "SELECT")
2822 toggleMute();
2823 else
2824 handled = false;
2825 }
2826
2827 if (!handled && MythScreenType::keyPressEvent(event))
2828 handled = true;
2829
2830 // Restart the display timer only if we handled this keypress, if nothing
2831 // has changed there's no need to keep the volume on-screen
2832 if (handled)
2834
2835 return handled;
2836}
2837
2839{
2840 gPlayer->incVolume();
2841 updateDisplay();
2842}
2843
2845{
2846 gPlayer->decVolume();
2847 updateDisplay();
2848}
2849
2851{
2853 updateDisplay();
2854}
2855
2857{
2858 if (m_muteState)
2859 m_muteState->DisplayState(gPlayer->isMuted() ? "on" : "off");
2860
2861 if (m_volProgress)
2863
2864 if (m_volText)
2865 {
2866 InfoMap map;
2867 gPlayer->toMap(map);
2869 }
2870}
2871
2872//---------------------------------------------------------
2873// TrackInfoDialog
2874//---------------------------------------------------------
2876{
2877 if (!LoadWindowFromXML("music-ui.xml", "trackdetail_popup", this))
2878 return false;
2879
2880 InfoMap metadataMap;
2881 m_metadata->toMap(metadataMap);
2882 SetTextFromMap(metadataMap);
2883
2884 MythUIStateType *ratingState = dynamic_cast<MythUIStateType *>(GetChild("rating_state"));
2885 if (ratingState)
2886 ratingState->DisplayState(QString("%1").arg(m_metadata->Rating()));
2887
2888 MythUIImage *albumImage = dynamic_cast<MythUIImage *>(GetChild("coverart"));
2889 if (albumImage)
2890 {
2891 if (!m_metadata->getAlbumArtFile().isEmpty())
2892 {
2893 albumImage->SetFilename(m_metadata->getAlbumArtFile());
2894 albumImage->Load();
2895 }
2896 }
2897
2898 // hide the song ID by default
2899 MythUIText *songID = dynamic_cast<MythUIText *>(GetChild("songid"));
2900 if (songID)
2901 songID->Hide();
2902
2903 return true;
2904}
2905
2907{
2908 QStringList actions;
2909 bool handled = GetMythMainWindow()->TranslateKeyPress("Music", event, actions, false);
2910
2911 for (int i = 0; i < actions.size() && !handled; i++)
2912 {
2913 const QString& action = actions[i];
2914 handled = true;
2915
2916 if (action == "INFO")
2917 Close();
2918 if (action == "0")
2919 {
2920 // if it's available show the song ID
2921 MythUIText *songID = dynamic_cast<MythUIText *>(GetChild("songid"));
2922 if (songID)
2923 songID->Show();
2924 }
2925 else
2926 {
2927 handled = false;
2928 }
2929 }
2930
2931 if (!handled && MythScreenType::keyPressEvent(event))
2932 handled = true;
2933
2934 return handled;
2935}
MusicMetadata * getCDMetadata(int m_the_track)
MusicMetadata * getMetadata(int an_id)
int getCDTrackCount(void) const
Event(Type type)
Definition: audiooutput.h:235
virtual bool IsPaused(void) const =0
virtual void SetTimecode(std::chrono::milliseconds timecode)=0
virtual bool ToggleUpmix(void)
Definition: audiooutput.h:164
virtual void lock(void)
Definition: decoder.h:84
virtual void unlock(void)
Definition: decoder.h:85
virtual void seek(double)=0
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:40
static const Type kEventType
Definition: mythdialogbox.h:55
Definition: lcddevice.h:170
static LCD * Get(void)
Definition: lcddevice.cpp:68
void switchToMusic(const QString &artist, const QString &album, const QString &track)
Definition: lcddevice.cpp:564
@ MUSIC_REPEAT_ALL
Definition: lcddevice.h:187
@ MUSIC_REPEAT_TRACK
Definition: lcddevice.h:186
@ MUSIC_REPEAT_NONE
Definition: lcddevice.h:185
@ MUSIC_SHUFFLE_ALBUM
Definition: lcddevice.h:194
@ MUSIC_SHUFFLE_NONE
Definition: lcddevice.h:191
@ MUSIC_SHUFFLE_ARTIST
Definition: lcddevice.h:195
@ MUSIC_SHUFFLE_RAND
Definition: lcddevice.h:192
@ MUSIC_SHUFFLE_SMART
Definition: lcddevice.h:193
bool isRunning(void) const
Definition: mthread.cpp:247
void prepare(void) override
Definition: mainvisual.cpp:138
void setVisual(const QString &name)
Definition: mainvisual.cpp:80
QStringList getVisualizations(void)
Definition: mainvisual.h:56
int getCurrentVisual(void) const
Definition: mainvisual.h:58
void seek(std::chrono::seconds pos)
MythUIButton * m_nextButton
Definition: musiccommon.h:203
void updateRepeatMode(void)
void changeVolume(bool up) const
MythMenu * createRepeatMenu(void)
bool m_movingTrack
Definition: musiccommon.h:155
bool m_cycleVisualizer
Definition: musiccommon.h:148
void stop(void)
void byAlbum(void)
void cycleVisualizer(void)
MythUIButton * m_ffButton
Definition: musiccommon.h:202
static void play(void)
MythMenu * createPlaylistOptionsMenu(void)
bool m_moveTrackMode
Definition: musiccommon.h:154
void fromCD(void)
std::chrono::seconds m_currentTime
Definition: musiccommon.h:160
std::chrono::seconds m_maxTime
Definition: musiccommon.h:161
void viewExited(void)
void playlistItemClicked(MythUIButtonListItem *item)
void seekforward(void)
void next(void)
void init(bool startPlayback=true)
void switchVisualizer(const QString &visual)
static void showSpeed(bool show)
MythUIProgressBar * m_trackProgress
Definition: musiccommon.h:187
void allTracks(void)
QStringList m_visualModes
Definition: musiccommon.h:151
static void toggleUpmix(void)
QList< int > m_songList
Definition: musiccommon.h:172
MythUIVideo * m_visualizerVideo
Definition: musiccommon.h:210
void updateShuffleMode(bool updateUIList=false)
MythUIButton * m_playButton
Definition: musiccommon.h:200
MythMenu * createMainMenu(void)
MythMenu * createPlaylistMenu(void)
MythUIText * m_trackSpeedText
Definition: musiccommon.h:189
MythUIStateType * m_shuffleState
Definition: musiccommon.h:180
void doUpdatePlaylist(void)
MythScreenType * m_parentScreen
Definition: musiccommon.h:142
static void setTrackOnLCD(MusicMetadata *mdata)
MythMenu * createShuffleMenu(void)
MythMenu * createVisualizerMenu(void)
std::chrono::seconds m_playlistPlayedTime
Definition: musiccommon.h:164
void changeSpeed(bool up)
bool m_randomVisualizer
Definition: musiccommon.h:149
void stopVisualizer(void)
~MusicCommon(void) override
Definition: musiccommon.cpp:60
PlaylistOptions m_playlistOptions
Definition: musiccommon.h:168
MythUIText * m_infoText
Definition: musiccommon.h:176
MythUIButton * m_pauseButton
Definition: musiccommon.h:199
void byYear(void)
void seekback(void)
MythUIButton * m_rewButton
Definition: musiccommon.h:198
static void showTrackInfo(MusicMetadata *mdata)
static void showVolume(void)
void byGenre(void)
void stopAll(void)
MusicView m_currentView
Definition: musiccommon.h:143
int m_currentTrack
Definition: musiccommon.h:159
void showExitMenu(void)
void updateTrackInfo(MusicMetadata *mdata)
static void pause(void)
MythUIText * m_trackProgressText
Definition: musiccommon.h:188
MythMenu * createQuickPlaylistsMenu(void)
static void changeRating(bool increase)
void updateVolume(void)
void ShowMenu(void) override
MythUIStateType * m_ratingState
Definition: musiccommon.h:185
bool CreateCommon(void)
Definition: musiccommon.cpp:78
void updateUIPlayedList(void)
static void playlistItemVisible(MythUIButtonListItem *item)
void byTitle(void)
static void editTrackInfo(MusicMetadata *mdata)
void previous(void)
QString m_whereClause
Definition: musiccommon.h:169
MythMenu * createSubMenu(void)
std::chrono::seconds m_playlistMaxTime
Definition: musiccommon.h:165
bool keyPressEvent(QKeyEvent *e) override
Key event handler.
void byArtist(void)
MythUIText * m_visualText
Definition: musiccommon.h:177
MythUIProgressBar * m_playlistProgress
Definition: musiccommon.h:195
unsigned int m_currentVisual
Definition: musiccommon.h:152
bool m_controlVolume
Definition: musiccommon.h:157
bool restorePosition(int trackID)
bool m_fullscreenBlank
Definition: musiccommon.h:147
MythUIButtonList * m_playedTracksList
Definition: musiccommon.h:208
MainVisual * m_mainvisual
Definition: musiccommon.h:146
MythUIText * m_volumeText
Definition: musiccommon.h:193
MythUIText * m_noTracksText
Definition: musiccommon.h:178
static QString getTimeString(std::chrono::seconds exTime, std::chrono::seconds maxTime)
MythUIStateType * m_repeatState
Definition: musiccommon.h:181
void showPlaylistOptionsMenu(bool addMainMenu=false)
void searchButtonList(void)
MythUIButton * m_prevButton
Definition: musiccommon.h:197
MythUIButton * m_stopButton
Definition: musiccommon.h:201
void updateProgressBar(void)
uint m_playlistTrackCount
Definition: musiccommon.h:163
void switchView(MusicView view)
MythUIImage * m_coverartImage
Definition: musiccommon.h:205
MythUIStateType * m_trackState
Definition: musiccommon.h:190
MusicCommon(MythScreenStack *parent, MythScreenType *parentScreen, const QString &name)
Definition: musiccommon.cpp:46
MythUIText * m_timeText
Definition: musiccommon.h:175
void customEvent(QEvent *event) override
MythUIStateType * m_movingTracksState
Definition: musiccommon.h:183
MythUIButtonList * m_currentPlaylist
Definition: musiccommon.h:207
void toggleMute(void) const
static void playFirstTrack()
void updatePlaylistStats(void)
void startVisualizer(void)
MythMenu * createPlayerMenu(void)
void updateUIPlaylist(void)
MythUIStateType * m_muteState
Definition: musiccommon.h:192
AllMusic * m_all_music
Definition: musicdata.h:52
PlaylistContainer * m_all_playlists
Definition: musicdata.h:51
int Year() const
std::chrono::milliseconds Length() const
void toMap(InfoMap &metadataMap, const QString &prefix="")
QString Title() const
QString getAlbumArtFile(void)
IdType ID() const
QString Artist() const
int Rating() const
uint32_t IdType
Definition: musicmetadata.h:87
QString Genre() const
QString Album() const
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 kTrackRemovedEvent
Definition: musicplayer.h:42
MusicMetadata * getCurrentMetadata(void)
get the metadata for the current track in the playlist
ShuffleMode toggleShuffleMode(void)
PlayMode getPlayMode(void)
Definition: musicplayer.h:77
void savePosition(void)
void playlistChanged(int playlistID)
void next(void)
@ SHUFFLE_INTELLIGENT
Definition: musicplayer.h:177
uint getVolume(void) const
void decVolume(void)
void setPlayMode(PlayMode mode)
void removeTrack(int trackID)
void stop(bool stopAll=false)
void activePlaylistChanged(int trackID, bool deleted)
bool isStopped(void)
Definition: musicplayer.h:111
void play(void)
bool isPaused(void)
Definition: musicplayer.h:110
@ PLAYMODE_TRACKSPLAYLIST
Definition: musicplayer.h:71
@ PLAYMODE_TRACKSEDITOR
Definition: musicplayer.h:72
void getBufferStatus(int *bufferAvailable, int *bufferSize) const
void previous(void)
void setAllowRestorePos(bool allow)
Definition: musicplayer.h:154
QList< MusicMetadata * > & getPlayedTracksList(void)
Definition: musicplayer.h:141
void moveTrackUpDown(bool moveUp, int whichTrack)
void restorePosition(void)
void removeListener(QObject *listener)
bool isPlaying(void) const
Definition: musicplayer.h:109
void removeVisual(MainVisual *visual)
bool setCurrentTrackPos(int pos)
ShuffleMode getShuffleMode(void)
Definition: musicplayer.h:195
void incVolume(void)
AudioOutput * getOutput(void)
Definition: musicplayer.h:128
float getSpeed() const
Definition: musicplayer.h:99
void sendTrackStatsChangedEvent(int trackID)
void addListener(QObject *listener)
RepeatMode getRepeatMode(void)
Definition: musicplayer.h:191
static bool getPlayNow(void)
void pause(void)
void setShuffleMode(ShuffleMode mode)
Playlist * getCurrentPlaylist(void)
static void setPlayNow(bool PlayNow)
whether we prefer Play Now over Add Tracks
void setRepeatMode(RepeatMode mode)
Definition: musicplayer.h:192
void toggleMute(void)
bool isMuted(void) const
Definition: musicplayer.h:89
Decoder * getDecoder(void)
Definition: musicplayer.h:126
MusicMetadata * getNextMetadata(void)
get the metadata for the next track in the playlist
RepeatMode toggleRepeatMode(void)
void changeCurrentTrack(int trackNo)
change the current track to the given track
int getCurrentTrackPos(void) const
Definition: musicplayer.h:143
void addVisual(MainVisual *visual)
void toMap(InfoMap &infoMap) const
QString GetSetting(const QString &key, const QString &defaultval="")
bool GetBoolSetting(const QString &key, bool defaultval=false)
Basic menu dialog, message and a list of options.
MythScreenStack * GetMainStack()
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
MythScreenStack * GetStack(const QString &Stackname)
~MythMusicVolumeDialog(void) override
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
MythUIProgressBar * m_volProgress
Definition: musiccommon.h:235
MythUIText * m_volText
Definition: musiccommon.h:233
bool Create(void) override
MythUIStateType * m_muteState
Definition: musiccommon.h:234
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Screen in which all other widgets are contained and rendered.
MythUIType * GetFocusWidget(void) const
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
virtual void Close()
Dialog prompting the user to enter a text string.
bool Create(void) override
void SetFontState(const QString &state, const QString &name="")
void DisplayState(const QString &state, const QString &name)
bool MoveUpDown(bool flag)
void SetTextFromMap(const InfoMap &infoMap, const QString &state="")
void SetImage(MythImage *image, const QString &name="")
Sets an image directly, should only be used in special circumstances since it bypasses the cache.
QString GetImageFilename(const QString &name="") const
QString GetText(const QString &name="") const
void SetText(const QString &text, const QString &name="", const QString &state="")
List widget, displays list items in a variety of themeable arrangements and can trigger signals when ...
MythUIButtonListItem * GetItemCurrent() const
void itemVisible(MythUIButtonListItem *item)
void SetItemCurrent(MythUIButtonListItem *item)
void RemoveItem(MythUIButtonListItem *item)
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
void SetSearchFields(const QString &fields)
int GetCurrentPos() const
void itemClicked(MythUIButtonListItem *item)
MythUIButtonListItem * GetItemAt(int pos) const
A tree widget for displaying and navigating a MythGenericTree()
void ShowSearchDialog(void)
void SetLocked(bool locked)
void Push(bool lock=false)
void Clicked()
void SetLockable(bool lockable)
Definition: mythuibutton.h:39
virtual void SetTextFromMap(const InfoMap &infoMap)
virtual void ResetMap(const InfoMap &infoMap)
Image widget, displays a single image or multiple images in sequence.
Definition: mythuiimage.h:98
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
void SetFilename(const QString &filename)
Must be followed by a call to Load() to load the image.
void Reset(void) override
Reset the image back to the default defined in the theme.
void SetUsed(int value)
void SetTotal(int value)
Provide a dialog to quickly find an entry in a list.
This widget is used for grouping other widgets for display when a particular named state is called.
bool DisplayState(const QString &name)
All purpose text widget, displays a text string.
Definition: mythuitext.h:29
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitext.cpp:65
void SetTextFromMap(const InfoMap &map)
Definition: mythuitext.cpp:138
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:115
virtual void SetVisible(bool visible)
void Hide(void)
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:130
void Show(void)
Playlist * getPlaylist(int id)
Playlist * getActive(void)
void copyNewPlaylist(const QString &name)
QStringList getPlaylistNames(void)
int getID(void) const
Definition: playlist.h:111
QString getName(void)
Definition: playlist.h:106
QString toRawSonglist(bool shuffled=false, bool tracksOnly=false)
Definition: playlist.cpp:848
void removeAllTracks(void)
Definition: playlist.cpp:90
int fillSonglistFromQuery(const QString &whereClause, bool removeDuplicates=false, InsertPLOption insertOption=PL_REPLACE, int currentTrackID=0)
Definition: playlist.cpp:679
void changed(void)
Definition: playlist.cpp:983
void fillSongsFromSonglist(const QString &songList)
Definition: playlist.cpp:630
int getTrackCount(void)
Definition: playlist.h:82
void getStats(uint *trackCount, std::chrono::seconds *totalLength, uint currentTrack=0, std::chrono::seconds *playedLength=nullptr) const
Definition: playlist.cpp:490
MusicMetadata * getSongAt(int pos) const
Definition: playlist.cpp:1089
int fillSonglistFromList(const QList< int > &songList, bool removeDuplicates, InsertPLOption insertOption, int currentTrackID)
Definition: playlist.cpp:778
MusicMetadata * m_metadata
Definition: musiccommon.h:251
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
bool Create(void) override
QMutex * mutex()
Definition: visualization.h:24
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
unsigned int uint
Definition: compat.h:60
@ FUNC_MUSIC
Definition: lcddevice.h:160
static constexpr std::chrono::milliseconds MUSICVOLUMEPOPUPTIME
MusicView
Definition: musiccommon.h:32
@ MV_SEARCH
Definition: musiccommon.h:38
@ MV_PLAYLIST
Definition: musiccommon.h:33
@ MV_LYRICS
Definition: musiccommon.h:34
@ MV_PLAYLISTEDITORTREE
Definition: musiccommon.h:35
@ MV_RADIO
Definition: musiccommon.h:42
@ MV_VISUALIZERINFO
Definition: musiccommon.h:44
@ MV_MINIPLAYER
Definition: musiccommon.h:43
@ MV_PLAYLISTEDITORGALLERY
Definition: musiccommon.h:36
@ MV_VISUALIZER
Definition: musiccommon.h:37
MusicData * gMusicData
Definition: musicdata.cpp:23
MusicPlayer * gPlayer
Definition: musicplayer.cpp:38
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static void startPlayback(void)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
Convenience inline random number generator functions.
static MythThemedMenu * menu
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
QString formatTime(std::chrono::milliseconds msecs, QString fmt)
Format a milliseconds time value.
Definition: mythdate.cpp:242
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20
@ PL_FIRST
Definition: playlist.h:32
@ PL_FIRSTNEW
Definition: playlist.h:33
@ PL_CURRENT
Definition: playlist.h:34
@ PL_INSERTAFTERCURRENT
Definition: playlist.h:27
@ PL_REPLACE
Definition: playlist.h:24
@ PL_INSERTATEND
Definition: playlist.h:26
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:204
static void show(uint8_t *buf, int length)
Definition: ringbuffer.cpp:339
QString formattedFieldValue(const QVariant &value)
PlayPLOption playPLOption
Definition: playlist.h:40
InsertPLOption insertPLOption
Definition: playlist.h:39
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27