59 setObjectName(
"MusicPlayer");
62 if (playmode.toLower() ==
"random")
64 else if (playmode.toLower() ==
"intelligent")
66 else if (playmode.toLower() ==
"album")
68 else if (playmode.toLower() ==
"artist")
74 if (repeatmode.toLower() ==
"track")
76 else if (repeatmode.toLower() ==
"all")
97 QMap<QString, int>::Iterator i;
275 QMutexLocker locker(
m_lock);
311 decoder->
cond()->wakeAll();
331 ShowOkPopup(tr(
"Got too many track unavailable errors. Maybe the host with the music on is off-line?"));
379 if (adevice ==
"default" || adevice.isEmpty())
388 adevice, pdevice,
FORMAT_S16, 2, AV_CODEC_ID_NONE, 44100,
394 LOG(VB_GENERAL, LOG_ERR,
395 QString(
"MusicPlayer: Cannot open audio output device: %1").arg(adevice));
402 LOG(VB_GENERAL, LOG_ERR,
403 QString(
"MusicPlayer: Cannot open audio output device: %1").arg(adevice));
404 LOG(VB_GENERAL, LOG_ERR,
423 QMutexLocker locker(
m_lock);
436 if (
nullptr == playlist)
476 if (
nullptr == playlist)
489 if (currentTrack >= 0)
508 if (
nullptr == playlist)
533 auto *miniplayer =
new MiniPlayer(popupStack);
535 if (miniplayer->Create())
601 auto *miniplayer =
new MiniPlayer(popupStack);
603 if (miniplayer->Create())
616 auto *me =
dynamic_cast<MythEvent*
>(event);
620 if (me->Message().left(13) ==
"MUSIC_COMMAND")
622 QStringList list = me->
Message().simplified().split(
' ');
626 if (list[2] ==
"PLAY")
628 else if (list[2] ==
"STOP")
630 else if (list[2] ==
"PAUSE")
632 else if (list[2] ==
"SET_VOLUME")
636 int volume = list[3].toInt();
637 if (volume >= 0 && volume <= 100)
641 else if (list[2] ==
"GET_VOLUME")
643 QString message = QString(
"MUSIC_CONTROL ANSWER %1 %2")
648 else if (list[2] ==
"PLAY_FILE")
650 int start = me->Message().indexOf(
"'");
651 int end = me->Message().lastIndexOf(
"'");
653 if (start != -1 && end != -1 && start != end)
655 QString
filename = me->Message().mid(start + 1, end - start - 1);
662 LOG(VB_GENERAL, LOG_ERR,
663 QString(
"MusicPlayer: got invalid MUSIC_COMMAND "
664 "PLAY_FILE - %1").arg(me->Message()));
667 else if (list[2] ==
"PLAY_URL")
669 if (list.size() == 4)
678 LOG(VB_GENERAL, LOG_ERR,
679 QString(
"MusicPlayer: got invalid MUSIC_COMMAND "
680 "PLAY_URL - %1").arg(me->Message()));
683 else if (list[2] ==
"PLAY_TRACK")
685 if (list.size() == 4)
687 int trackID = list[3].toInt();
694 LOG(VB_GENERAL, LOG_ERR,
695 QString(
"MusicPlayer: got invalid MUSIC_COMMAND "
696 "PLAY_TRACK - %1").arg(me->Message()));
699 else if (list[2] ==
"GET_METADATA")
704 mdataStr = QString(
"%1 by %2 from %3").arg(mdata->
Title(), mdata->
Artist(), mdata->
Album());
706 mdataStr =
"Unknown Track2";
708 QString message = QString(
"MUSIC_CONTROL ANSWER %1 %2")
713 else if (list[2] ==
"GET_STATUS")
715 QString statusStr =
"STOPPED";
718 statusStr =
"PLAYING";
720 statusStr =
"PAUSED";
722 QString message = QString(
"MUSIC_CONTROL ANSWER %1 %2")
730 LOG(VB_GENERAL, LOG_ERR,
731 QString(
"MusicPlayer: got unknown/invalid MUSIC_COMMAND "
732 "- %1").arg(me->Message()));
735 else if (me->Message().startsWith(
"MUSIC_SETTINGS_CHANGED"))
739 else if (me->Message().startsWith(
"MUSIC_METADATA_CHANGED"))
743 QStringList list = me->Message().simplified().split(
' ');
744 if (list.size() == 2)
746 int songID = list[1].toInt();
759 else if (me->Message().startsWith(
"MUSIC_SCANNER_STARTED"))
761 QStringList list = me->Message().simplified().split(
' ');
762 if (list.size() == 2)
764 const QString& host = list[1];
767 tr(
"A music file scan has started on %1").arg(host),
768 tr(
"Music File Scanner"),
769 tr(
"This may take a while I'll give a shout when finished"));
772 else if (me->Message().startsWith(
"MUSIC_SCANNER_FINISHED"))
774 QStringList list = me->Message().simplified().split(
' ');
775 if (list.size() == 6)
777 const QString& host = list[1];
779 int totalTracks = list[2].toInt();
780 int newTracks = list[3].toInt();
781 int totalCoverart = list[4].toInt();
782 int newCoverart = list[5].toInt();
784 QString summary = QString(
"Total Tracks: %2, new tracks: %3,\nTotal Coverart: %4, New CoverArt %5")
785 .arg(totalTracks).arg(newTracks).arg(totalCoverart).arg(newCoverart);
787 tr(
"A music file scan has finished on %1").arg(host),
788 tr(
"Music File Scanner"), summary);
793 else if (me->Message().startsWith(
"MUSIC_SCANNER_ERROR"))
795 QStringList list = me->Message().simplified().split(
' ');
796 if (list.size() == 3)
798 const QString& host = list[1];
799 const QString&
error = list[2];
802 if (
error ==
"Already_Running")
805 tr(
"Music File Scanner"),
806 tr(
"Can't run the music file scanner because it is already running on %1").arg(host));
808 else if (
error ==
"Stalled")
811 tr(
"Music File Scanner"),
812 tr(
"The music file scanner has been running for more than 60 minutes on %1.\nResetting and trying again")
826 LOG(VB_GENERAL, LOG_ERR, QString(
"Audio Output Error: %1").arg(*aoe->errorMessage()));
848 LOG(VB_GENERAL, LOG_ERR, QString(
"Decoder Error: %2").arg(*dxe->errorMessage()));
870 LOG(VB_GENERAL, LOG_ERR, QString(
"Decoder Handler Error - %1").arg(*dhe->getMessage()));
930 auto metadataSecs = duration_cast<std::chrono::seconds>(
getCurrentMetadata()->Length());
934 LOG(VB_GENERAL, LOG_NOTICE, QString(
"MusicPlayer: Updating track length was %1s, should be %2s")
963 QObject::customEvent(event);
992 if ((bookmark < 0) ||
1011 if ((bookmark < 0) ||
1033 for (
int x = 0; x < list->count(); x++)
1038 if (mdata->
ID() ==
id)
1048 if (
nullptr == playlist)
1051 if (moveUp && whichTrack <= 0)
1143 decoder->
seek(pos.count());
1156 auto *miniplayer =
new MiniPlayer(popupStack);
1158 if (miniplayer->Create())
1169 if (
nullptr == playlist)
1178 if (m_currentTrack < 0 || m_currentTrack >= playlist->
getTrackCount())
1180 LOG(VB_GENERAL, LOG_ERR,
1181 QString(
"MusicPlayer: asked to set the current track to an invalid track no. %1")
1218 if (m_currentTrack < playlist->getTrackCount() - 1)
1276 int curTrackID = -1;
1285 if (
nullptr == playlist)
1290 if (curTrackID != -1)
1324 if (
GetMythDB()->GetNumSetting(
"AllowTagWriting", 0) == 1)
1326 QStringList strList;
1327 strList << QString(
"MUSIC_TAG_UPDATE_VOLATILE")
1451 map[
"volumemute"] =
isMuted() ? tr(
"%1% (Muted)",
"Zero Audio Volume").arg(
getVolume()) :
1453 map[
"volume"] = QString(
"%1").arg(
getVolume());
1454 map[
"volumepercent"] = QString(
"%1%").arg(
getVolume());
1455 map[
"mute"] =
isMuted() ? tr(
"Muted") :
"";
1535 QMutexLocker locker(
m_lock);
1548 LOG(VB_PLAYBACK, LOG_INFO, QString (
"decoder handler is ready, decoding %1")
1549 .arg(decoder->
getURL()));
1552 auto *cddecoder =
dynamic_cast<CdDecoder*
>(decoder);
1570 QMutexLocker locker(
m_lock);
1606 LOG(VB_PLAYBACK, LOG_ERR, QString(
"Cannot initialise decoder for %1")
1607 .arg(decoder->
getURL()));
1622 if (
nullptr == playlist)
1664 QString image =
"musicscanner.png";
1666 LOG(VB_GENERAL, LOG_ERR,
"MusicPlayer: sendNotification failed to find the 'musicscanner.png' image");
1669 map[
"asar"] = title;
1670 map[
"minm"] = author;
1675 n->SetId(notificationID);
1678 n->SetFullScreen(
false);
MusicMetadata * getMetadata(int an_id)
StreamList * getStreams(void)
virtual bool IsPaused(void) const =0
virtual void SetTimecode(std::chrono::milliseconds timecode)=0
static AudioOutput * OpenAudio(const QString &main_device, const QString &passthru_device, AudioFormat format, int channels, AVCodecID codec, int samplerate, AudioOutputSource source, bool set_initial_vol, bool passthru, int upmixer_startup=0, AudioOutputSettings *custom=nullptr)
virtual void PauseUntilBuffered(void)=0
virtual void Reset(void)=0
virtual void Pause(bool paused)=0
virtual void SetStretchFactor(float factor)
QString GetError(void) const
void setDevice(const QString &dev)
static const Type kStopped
static const Type kFinished
Events sent by the DecoderHandler and it's helper classes.
static const Type kBufferStatus
void getBufferStatus(int *available, int *maxSize) const
Class for starting stream decoding.
void start(MusicMetadata *mdata)
QString getURL(void) const
virtual void unlock(void)
virtual bool initialize()=0
virtual void seek(double)=0
void setOutput(AudioOutput *o)
static MThreadPool * globalInstance(void)
void start(QRunnable *runnable, const QString &debugName, int priority=0)
bool isRunning(void) const
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
void reloadMusic(void) const
reload music after a scan, rip or import
PlaylistContainer * m_all_playlists
AllStream * m_all_streams
static const Type kPlayedTracksChangedEvent
static const Type kCDChangedEvent
static const Type kMetadataChangedEvent
static const Type kTrackStatsChangedEvent
static const Type kTrackUnavailableEvent
static const Type kAlbumArtChangedEvent
static const Type kVolumeChangeEvent
static const Type kAllTracksRemovedEvent
static const Type kTrackAddedEvent
static const Type kTrackChangeEvent
static const Type kPlaylistChangedEvent
static const Type kTrackRemovedEvent
MusicMetadata * getCurrentMetadata(void)
get the metadata for the current track in the playlist
void sendMetadataChangedEvent(int trackID)
void setSpeed(float speed)
void sendVolumeChangedEvent(void)
ShuffleMode toggleShuffleMode(void)
QSet< QObject * > m_visualisers
void playlistChanged(int playlistID)
void showMiniPlayer(void) const
void sendNotification(int notificationID, const QString &title, const QString &author, const QString &desc)
std::chrono::seconds m_lastTrackStart
uint getVolume(void) const
void setPlayMode(PlayMode mode)
void setVolume(int volume)
void removeTrack(int trackID)
~MusicPlayer(void) override
MusicMetadata * m_oneshotMetadata
void updateVolatileMetadata(void)
void stop(bool stopAll=false)
void activePlaylistChanged(int trackID, bool deleted)
DecoderHandler * m_decoderHandler
void loadStreamPlaylist(void)
DecoderHandler * getDecoderHandler(void)
void sendCDChangedEvent(void)
QList< MusicMetadata * > m_playedList
void getBufferStatus(int *bufferAvailable, int *bufferSize) const
std::chrono::seconds m_lastplayDelay
QMap< QString, int > m_notificationMap
void customEvent(QEvent *event) override
int getNotificationID(const QString &hostname)
void setupDecoderHandler(void)
MusicPlayer(QObject *parent)
std::chrono::seconds m_currentTime
void moveTrackUpDown(bool moveUp, int whichTrack)
void restorePosition(void)
ResumeMode getResumeMode(void)
void updateLastplay(void)
void addTrack(int trackID, bool updateUI)
void removeListener(QObject *listener)
bool isPlaying(void) const
void removeVisual(MainVisual *visual)
bool openOutputDevice(void)
bool setCurrentTrackPos(int pos)
void playFile(const MusicMetadata &mdata)
AudioOutput * getOutput(void)
ResumeMode m_resumeModeRadio
void sendTrackStatsChangedEvent(int trackID)
void seek(std::chrono::seconds pos)
static StreamList * getStreamList(void)
void addListener(QObject *listener)
void decoderHandlerReady(void)
static bool getPlayNow(void)
void setShuffleMode(ShuffleMode mode)
Playlist * getCurrentPlaylist(void)
static void setPlayNow(bool PlayNow)
whether we prefer Play Now over Add Tracks
void sendTrackUnavailableEvent(int trackID)
void setRepeatMode(RepeatMode mode)
ResumeMode m_resumeModeEditor
ShuffleMode m_shuffleMode
Decoder * getDecoder(void)
MusicMetadata * getNextMetadata(void)
get the metadata for the next track in the playlist
MuteState getMuteState(void) const
RepeatMode toggleRepeatMode(void)
void changeCurrentTrack(int trackNo)
change the current track to the given track
ResumeMode m_resumeModePlayback
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.
const QString & Message() const
static const Type kMythEventMessage
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 ¬ification)
Queue a notification Queue() is thread-safe and can be called from anywhere.
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
void removeListener(QObject *listener)
Remove a listener to the observable.
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
static const Type kStopped
void removeVisual(MythTV::Visual *v)
void setBufferSize(unsigned int sz)
void addVisual(MythTV::Visual *v)
Playlist * getStreamPlaylist(void)
Playlist * getActive(void)
int getTrackPosition(MusicMetadata::IdType trackID)
void removeAllTracks(void)
void disableSaves(void)
whether any changes should be saved to the DB
void moveTrackUpDown(bool flag, int where_its_at)
void shuffleTracks(MusicPlayer::ShuffleMode mode)
MusicMetadata * getSongAt(int pos) const
void removeTrack(MusicMetadata::IdType trackID)
void addTrack(MusicMetadata::IdType trackID, bool update_display)
Given a tracks ID, add that track to this playlist.
send a message to the master BE without blocking the UI thread
virtual MuteState GetMuteState(void) const
virtual void SetCurrentVolume(int value)
virtual uint GetCurrentVolume(void) const
virtual void ToggleMute(void)
virtual void AdjustCurrentVolume(int change)
static constexpr std::chrono::seconds LASTPLAY_DELAY
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
MythNotificationCenter * GetNotificationCenter(void)
MythMainWindow * GetMythMainWindow(void)
QMap< QString, QString > DMAP
QHash< QString, QString > InfoMap
MythUIHelper * GetMythUI()