5 #include <QApplication> 56 setObjectName(
"MusicPlayer");
59 if (playmode.toLower() ==
"random")
61 else if (playmode.toLower() ==
"intelligent")
63 else if (playmode.toLower() ==
"album")
65 else if (playmode.toLower() ==
"artist")
71 if (repeatmode.toLower() ==
"track")
73 else if (repeatmode.toLower() ==
"all")
94 QMap<QString, int>::Iterator i;
262 QMutexLocker locker(
m_lock);
263 QSet<QObject*>::const_iterator it =
m_listeners.begin();
319 ShowOkPopup(tr(
"Got too many track unavailable errors. Maybe the host with the music on is off-line?"));
363 QString adevice, pdevice;
366 if (adevice ==
"default" || adevice.isEmpty())
375 adevice, pdevice,
FORMAT_S16, 2, AV_CODEC_ID_NONE, 44100,
381 LOG(VB_GENERAL, LOG_ERR,
382 QString(
"MusicPlayer: Cannot open audio output device: %1").arg(adevice));
389 LOG(VB_GENERAL, LOG_ERR,
390 QString(
"MusicPlayer: Cannot open audio output device: %1").arg(adevice));
391 LOG(VB_GENERAL, LOG_ERR,
413 QMutexLocker locker(
m_lock);
475 if (currentTrack >= 0)
518 auto *miniplayer =
new MiniPlayer(popupStack);
520 if (miniplayer->Create())
558 auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
586 auto *miniplayer =
new MiniPlayer(popupStack);
588 if (miniplayer->Create())
601 auto *me = dynamic_cast<MythEvent*>(event);
605 if (me->Message().left(13) ==
"MUSIC_COMMAND")
607 QStringList list = me->Message().simplified().split(
' ');
611 if (list[2] ==
"PLAY")
613 else if (list[2] ==
"STOP")
615 else if (list[2] ==
"PAUSE")
617 else if (list[2] ==
"SET_VOLUME")
619 if (list.size() >= 3)
621 int volume = list[3].toInt();
622 if (volume >= 0 && volume <= 100)
626 else if (list[2] ==
"GET_VOLUME")
628 QString message = QString(
"MUSIC_CONTROL ANSWER %1 %2")
633 else if (list[2] ==
"PLAY_FILE")
635 int start = me->Message().indexOf(
"'");
636 int end = me->Message().lastIndexOf(
"'");
638 if (start != -1 && end != -1 && start != end)
640 QString
filename = me->Message().mid(start + 1, end - start - 1);
646 LOG(VB_GENERAL, LOG_ERR,
647 QString(
"MusicPlayer: got invalid MUSIC_COMMAND " 648 "PLAY_FILE - %1").arg(me->Message()));
650 else if (list[2] ==
"PLAY_URL")
652 if (list.size() == 4)
660 LOG(VB_GENERAL, LOG_ERR,
661 QString(
"MusicPlayer: got invalid MUSIC_COMMAND " 662 "PLAY_URL - %1").arg(me->Message()));
664 else if (list[2] ==
"PLAY_TRACK")
666 if (list.size() == 4)
668 int trackID = list[3].toInt();
674 LOG(VB_GENERAL, LOG_ERR,
675 QString(
"MusicPlayer: got invalid MUSIC_COMMAND " 676 "PLAY_TRACK - %1").arg(me->Message()));
678 else if (list[2] ==
"GET_METADATA")
683 mdataStr = QString(
"%1 by %2 from %3").arg(mdata->
Title()).arg(mdata->
Artist()).arg(mdata->
Album());
685 mdataStr =
"Unknown Track2";
687 QString message = QString(
"MUSIC_CONTROL ANSWER %1 %2")
692 else if (list[2] ==
"GET_STATUS")
694 QString statusStr =
"STOPPED";
697 statusStr =
"PLAYING";
699 statusStr =
"PAUSED";
701 QString message = QString(
"MUSIC_CONTROL ANSWER %1 %2")
708 LOG(VB_GENERAL, LOG_ERR,
709 QString(
"MusicPlayer: got unknown/invalid MUSIC_COMMAND " 710 "- %1").arg(me->Message()));
712 else if (me->Message().startsWith(
"MUSIC_SETTINGS_CHANGED"))
716 else if (me->Message().startsWith(
"MUSIC_METADATA_CHANGED"))
720 QStringList list = me->Message().simplified().split(
' ');
721 if (list.size() == 2)
723 int songID = list[1].toInt();
736 else if (me->Message().startsWith(
"MUSIC_SCANNER_STARTED"))
738 QStringList list = me->Message().simplified().split(
' ');
739 if (list.size() == 2)
741 QString host = list[1];
744 tr(
"A music file scan has started on %1").arg(host),
745 tr(
"Music File Scanner"),
746 tr(
"This may take a while I'll give a shout when finished"));
749 else if (me->Message().startsWith(
"MUSIC_SCANNER_FINISHED"))
751 QStringList list = me->Message().simplified().split(
' ');
752 if (list.size() == 6)
754 QString host = list[1];
756 int totalTracks = list[2].toInt();
757 int newTracks = list[3].toInt();
758 int totalCoverart = list[4].toInt();
759 int newCoverart = list[5].toInt();
761 QString summary = QString(
"Total Tracks: %2, new tracks: %3,\nTotal Coverart: %4, New CoverArt %5")
762 .arg(totalTracks).arg(newTracks).arg(totalCoverart).arg(newCoverart);
764 tr(
"A music file scan has finished on %1").arg(host),
765 tr(
"Music File Scanner"), summary);
770 else if (me->Message().startsWith(
"MUSIC_SCANNER_ERROR"))
772 QStringList list = me->Message().simplified().split(
' ');
773 if (list.size() == 3)
775 QString host = list[1];
776 QString
error = list[2];
779 if (
error ==
"Already_Running")
781 tr(
"Music File Scanner"),
782 tr(
"Can't run the music file scanner because it is already running on %1").arg(host));
783 else if (
error ==
"Stalled")
785 tr(
"Music File Scanner"),
786 tr(
"The music file scanner has been running for more than 60 minutes on %1.\nResetting and trying again")
794 auto *aoe = dynamic_cast<OutputEvent *>(event);
799 LOG(VB_GENERAL, LOG_ERR, QString(
"Audio Output Error: %1").arg(*aoe->errorMessage()));
816 auto *dxe = dynamic_cast<DecoderEvent *>(event);
821 LOG(VB_GENERAL, LOG_ERR, QString(
"Decoder Error: %2").arg(*dxe->errorMessage()));
838 auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
843 LOG(VB_GENERAL, LOG_ERR, QString(
"Decoder Handler Error - %1").arg(*dhe->getMessage()));
860 auto *oe = dynamic_cast<OutputEvent*>(event);
906 LOG(VB_GENERAL, LOG_NOTICE, QString(
"MusicPlayer: Updating track length was %1s, should be %2s")
928 auto *dhe = dynamic_cast<DecoderHandlerEvent*>(event);
935 QObject::customEvent(event);
997 for (
int x = 0; x < list->count(); x++)
1002 if (mdata->
ID() == id)
1014 if (moveUp && whichTrack <= 0)
1117 auto *miniplayer =
new MiniPlayer(popupStack);
1119 if (miniplayer->Create())
1140 LOG(VB_GENERAL, LOG_ERR,
1141 QString(
"MusicPlayer: asked to set the current track to an invalid track no. %1")
1234 int curTrackID = -1;
1247 if (curTrackID != -1)
1281 if (
GetMythDB()->GetNumSetting(
"AllowTagWriting", 0) == 1)
1283 QStringList strList;
1284 strList << QString(
"MUSIC_TAG_UPDATE_VOLATILE")
1408 map[
"volumemute"] =
isMuted() ? tr(
"%1% (Muted)",
"Zero Audio Volume").arg(
getVolume()) :
1410 map[
"volume"] = QString(
"%1").arg(
getVolume());
1411 map[
"volumepercent"] = QString(
"%1%").arg(
getVolume());
1412 map[
"mute"] =
isMuted() ? tr(
"Muted") :
"";
1491 QMutexLocker locker(
m_lock);
1492 QSet<QObject*>::const_iterator it =
m_listeners.begin();
1505 LOG(VB_PLAYBACK, LOG_INFO, QString (
"decoder handler is ready, decoding %1")
1509 auto *cddecoder = dynamic_cast<CdDecoder*>(
getDecoder());
1527 QMutexLocker locker(
m_lock);
1528 QSet<QObject*>::const_iterator it =
m_listeners.begin();
1565 LOG(VB_PLAYBACK, LOG_ERR, QString(
"Cannot initialise decoder for %1")
1620 QString image =
"musicscanner.png";
1622 LOG(VB_GENERAL, LOG_ERR,
"MusicPlayer: sendNotification failed to find the 'musicscanner.png' image");
1625 map[
"asar"] = title;
1626 map[
"minm"] = author;
1631 n->SetId(notificationID);
1634 n->SetFullScreen(
false);
static StreamList * getStreamList(void)
virtual bool IsPaused(void) const =0
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
MusicMetadata * getSongAt(int pos) const
void shuffleTracks(MusicPlayer::ShuffleMode mode)
void setPlayMode(PlayMode mode)
void sendVolumeChangedEvent(void)
virtual uint GetCurrentVolume(void) const
void setShuffleMode(ShuffleMode mode)
void changeCurrentTrack(int trackNo)
change the current track to the given track
uint getVolume(void) const
virtual void SetCurrentVolume(int value)
static Type MetadataChangedEvent
void sendTrackStatsChangedEvent(int trackID)
void sendCDChangedEvent(void)
MusicMetadata * getCurrentMetadata(void)
get the metadata for the current track in the playlist
Playlist * getCurrentPlaylist(void)
void playFile(const MusicMetadata &mdata)
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
RepeatMode toggleRepeatMode(void)
void removeListener(QObject *listener)
QHash< QString, QString > InfoMap
void removeVisual(MainVisual *visual)
static Type TrackAddedEvent
void SaveSetting(const QString &key, int newValue)
static Type MythEventMessage
static void error(const char *str,...)
virtual void SetTimecode(int64_t timecode)=0
void removeListener(QObject *listener)
Remove a listener to the observable.
virtual void unlock(void)
virtual void Reset(void)=0
static AudioOutput * OpenAudio(const QString &main_device, const QString &passthru_device, AudioFormat format, int channels, AVCodecID codec, int samplerate, AudioOutputSource source, bool set_initial_vol, bool passthru, int upmixer_startup=0, AudioOutputSettings *custom=nullptr)
bool openOutputDevice(void)
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
ResumeMode m_resumeModeRadio
virtual void ToggleMute(void)
QMap< QString, QString > DMAP
Playlist * getActive(void)
AllStream * m_all_streams
bool setCurrentTrackPos(int pos)
MusicMetadata & getMetadata()
void setupDecoderHandler(void)
void UnRegister(void *from, int id, bool closeimemdiately=false)
Unregister the client.
void setVolume(int volume)
MythScreenStack * GetStack(const QString &stackname)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
void addVisual(MainVisual *visual)
ResumeMode getResumeMode(void)
void addListener(QObject *listener)
Add a listener to the observable.
QMap< QString, int > m_notificationMap
void UnregisterForPlayback(QObject *sender)
Unregister sender from being called when TVPlaybackAboutToStart signal is emitted.
static Type CDChangedEvent
virtual void AdjustCurrentVolume(int change)
static Type TrackChangeEvent
void loadStreamPlaylist(void)
void setBufferSize(unsigned int sz)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
MuteState getMuteState(void) const
PlaylistContainer * m_all_playlists
This class is used as a container for messages.
static bool isRunning(const char *program)
Returns true if a program containing the specified string is running on this machine.
void removeTrack(MusicMetadata::IdType trackID)
void sendMetadataChangedEvent(int trackID)
DecoderHandler * getDecoderHandler(void)
MusicMetadata * getMetadata(int an_id)
virtual bool initialize()=0
void emitTVPlaybackStopped(void)
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
virtual void Pause(bool paused)=0
MusicPlayer(QObject *parent)
void addTrack(int trackID, bool updateUI)
QString GetSetting(const QString &key, const QString &defaultval="")
QList< MusicMetadata * > m_playedList
int getNotificationID(const QString &hostname)
void decoderHandlerReady(void)
void getBufferStatus(int *bufferAvailable, int *bufferSize)
QSet< QObject * > m_listeners
void moveTrackUpDown(bool flag, int where_its_at)
ResumeMode m_resumeModeEditor
static Type TrackRemovedEvent
QString GetMasterHostName(void)
bool Queue(const MythNotification ¬ification)
Queue a notification Queue() is thread-safe and can be called from anywhere.
void setSpeed(float speed)
static Type TrackUnavailableEvent
void disableSaves(void)
whether any changes should be saved to the DB
void updateVolatileMetadata(void)
DecoderHandler * m_decoderHandler
void setOutput(AudioOutput *)
void showMiniPlayer(void)
virtual void SetStretchFactor(float factor)
ShuffleMode toggleShuffleMode(void)
void addVisual(MythTV::Visual *)
StreamList * getStreams(void)
MythUIHelper * GetMythUI()
virtual void seek(double)=0
void stop(bool stopAll=false)
Playlist * getStreamPlaylist(void)
void addTrack(MusicMetadata::IdType trackID, bool update_display)
Given a tracks ID, add that track to this playlist.
void dispatch(const MythEvent &event)
MythMainWindow * GetMythMainWindow(void)
static MThreadPool * globalInstance(void)
void sendAlbumArtChangedEvent(int trackID)
MusicMetadata * m_oneshotMetadata
void PauseIdleTimer(bool pause)
void restorePosition(void)
int GetNumSetting(const QString &key, int defaultval=0)
#define LOG(_MASK_, _LEVEL_, _STRING_)
static Type AllTracksRemovedEvent
void start(QRunnable *runnable, const QString &debugName, int priority=0)
void addListener(QObject *listener)
bool GetBoolSetting(const QString &key, bool defaultval=false)
void updateLastplay(void)
void sendTrackUnavailableEvent(int trackID)
ShuffleMode m_shuffleMode
static Type PlaylistChangedEvent
void toMap(InfoMap &infoMap)
void removeVisual(MythTV::Visual *)
Class for starting stream decoding.
int getTrackPosition(MusicMetadata::IdType trackID)
QSet< QObject * > m_visualisers
void playlistChanged(int playlistID)
static Type PlayedTracksChangedEvent
void removeTrack(int trackID)
void sendNotification(int notificationID, const QString &title, const QString &author, const QString &desc)
void moveTrackUpDown(bool moveUp, int whichTrack)
void RegisterForPlayback(QObject *sender, const char *method)
Register sender for TVPlaybackAboutToStart signal.
Decoder * getDecoder(void)
AudioOutput * getOutput(void)
void removeAllTracks(void)
static Type VolumeChangeEvent
void WantingPlayback(QObject *sender)
All the objects that have registered using MythCoreContext::RegisterForPlayback but sender will be ca...
MusicMetadata * getNextMetadata(void)
get the metadata for the next track in the playlist
QString GetHostName(void)
static Type AlbumArtChangedEvent
void customEvent(QEvent *event) override
bool InWantingPlayback(void)
Returns true if a client has requested playback.
void activePlaylistChanged(int trackID, bool deleted)
send a message to the master BE without blocking the UI thread
QString GetError(void) const
static Type TrackStatsChangedEvent
void reloadMusic(void)
reload music after a scan, rip or import
void setRepeatMode(RepeatMode mode)
virtual MuteState GetMuteState(void) const
MythNotificationCenter * GetNotificationCenter(void)
void start(MusicMetadata *mdata)
ResumeMode m_resumeModePlayback