Go to the documentation of this file.
5 #include <QApplication>
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);
void addVisual(MainVisual *visual)
QString GetError(void) const
void addVisual(MythTV::Visual *v)
virtual void SetCurrentVolume(int value)
bool openOutputDevice(void)
Decoder * getDecoder(void)
AudioOutput * getOutput(void)
MusicMetadata * getSongAt(int pos) const
QMap< QString, int > m_notificationMap
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
QString GetMasterHostName(void)
bool setCurrentTrackPos(int pos)
static const Type kMythEventMessage
void setVolume(int volume)
void toMap(InfoMap &infoMap) const
virtual void AdjustCurrentVolume(int change)
void sendNotification(int notificationID, const QString &title, const QString &author, const QString &desc)
virtual uint GetCurrentVolume(void) const
ResumeMode getResumeMode(void)
virtual void Reset(void)=0
void loadStreamPlaylist(void)
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
void emitTVPlaybackStopped(void)
void shuffleTracks(MusicPlayer::ShuffleMode mode)
This class is used as a container for messages.
static bool getPlayNow(void)
void setShuffleMode(ShuffleMode mode)
bool isPlaying(void) const
MusicPlayer(QObject *parent)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
std::chrono::seconds m_lastplayDelay
ResumeMode m_resumeModePlayback
static const Type kMetadataChangedEvent
static StreamList * getStreamList(void)
MuteState getMuteState(void) const
Playlist * getCurrentPlaylist(void)
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
std::enable_if_t< std::chrono::__is_duration< T >::value, void > SaveDurSetting(const QString &key, T newValue)
void removeListener(QObject *listener)
void removeVisual(MythTV::Visual *v)
void setPlayMode(PlayMode mode)
void updateVolatileMetadata(void)
DecoderHandler * m_decoderHandler
QSet< QObject * > m_listeners
const QString & Message() const
static const Type kAllTracksRemovedEvent
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)
void changeCurrentTrack(int trackNo)
change the current track to the given track
ResumeMode m_resumeModeRadio
Playlist * getActive(void)
QHash< QString, QString > InfoMap
void sendTrackStatsChangedEvent(int trackID)
void addListener(QObject *listener)
Add a listener to the observable.
static const Type kPlayedTracksChangedEvent
MusicMetadata * getCurrentMetadata(void)
get the metadata for the current track in the playlist
int getNotificationID(const QString &hostname)
Class for starting stream decoding.
void UnregisterForPlayback(QObject *sender)
static const Type kStopped
RepeatMode toggleRepeatMode(void)
Events sent by the DecoderHandler and it's helper classes.
std::chrono::seconds m_lastTrackStart
void removeVisual(MainVisual *visual)
void getBufferStatus(int *available, int *maxSize) const
virtual void Pause(bool paused)=0
virtual void unlock(void)
virtual void ToggleMute(void)
void reloadMusic(void) const
reload music after a scan, rip or import
ShuffleMode toggleShuffleMode(void)
static const Type kTrackChangeEvent
void removeTrack(MusicMetadata::IdType trackID)
void setBufferSize(unsigned int sz)
std::enable_if_t< std::chrono::__is_duration< T >::value, T > GetDurSetting(const QString &key, T defaultval=T::zero())
AllStream * m_all_streams
StreamList * getStreams(void)
Playlist * getStreamPlaylist(void)
void setupDecoderHandler(void)
static const Type kTrackStatsChangedEvent
virtual MuteState GetMuteState(void) const
void sendTrackUnavailableEvent(int trackID)
send a message to the master BE without blocking the UI thread
void addTrack(MusicMetadata::IdType trackID, bool update_display)
Given a tracks ID, add that track to this playlist.
ShuffleMode m_shuffleMode
void disableSaves(void)
whether any changes should be saved to the DB
void RegisterForPlayback(QObject *sender, PlaybackStartCb method)
QString getURL(void) const
void playlistChanged(int playlistID)
void sendMetadataChangedEvent(int trackID)
static const Type kStopped
MusicMetadata * getMetadata(int an_id)
void removeTrack(int trackID)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
PlaylistContainer * m_all_playlists
void setDevice(const QString &dev)
void updateLastplay(void)
void showMiniPlayer(void) const
int GetNumSetting(const QString &key, int defaultval=0)
void TVPlaybackAborted(void)
bool GetBoolSetting(const QString &key, bool defaultval=false)
void moveTrackUpDown(bool flag, int where_its_at)
void start(MusicMetadata *mdata)
static const Type kTrackUnavailableEvent
MusicMetadata * getNextMetadata(void)
get the metadata for the next track in the playlist
static const Type kCDChangedEvent
void decoderHandlerReady(void)
virtual bool initialize()=0
QSet< QObject * > m_visualisers
bool InWantingPlayback(void)
Returns true if a client has requested playback.
DecoderHandler * getDecoderHandler(void)
void activePlaylistChanged(int trackID, bool deleted)
void setSpeed(float speed)
static const Type kFinished
void setRepeatMode(RepeatMode mode)
void addTrack(int trackID, bool updateUI)
virtual void SetStretchFactor(float factor)
void setOutput(AudioOutput *o)
QList< MusicMetadata * > m_playedList
void customEvent(QEvent *event) override
static const Type kBufferStatus
MythNotificationCenter * GetNotificationCenter(void)
void UnRegister(void *from, int id, bool closeimemdiately=false)
Unregister the client.
ResumeMode m_resumeModeEditor
void sendVolumeChangedEvent(void)
~MusicPlayer(void) override
MythMainWindow * GetMythMainWindow(void)
bool isRunning(void) const
int getTrackPosition(MusicMetadata::IdType trackID)
static const Type kTrackRemovedEvent
void sendAlbumArtChangedEvent(int trackID)
MusicMetadata * m_oneshotMetadata
void restorePosition(void)
MythScreenStack * GetStack(const QString &Stackname)
void WantingPlayback(QObject *sender)
All the objects that have registered using MythCoreContext::RegisterForPlayback but sender will be ca...
void sendCDChangedEvent(void)
static const Type kTrackAddedEvent
void seek(std::chrono::seconds pos)
QString GetHostName(void)
void playFile(const MusicMetadata &mdata)
static const Type kVolumeChangeEvent
void removeAllTracks(void)
void stop(bool stopAll=false)
void TVPlaybackStopped(void)
void SaveSetting(const QString &key, int newValue)
QMap< QString, QString > DMAP
void getBufferStatus(int *bufferAvailable, int *bufferSize) const
static void setPlayNow(bool PlayNow)
whether we prefer Play Now over Add Tracks
MythUIHelper * GetMythUI()
virtual void seek(double)=0
static MThreadPool * globalInstance(void)
uint getVolume(void) const
static constexpr std::chrono::seconds LASTPLAY_DELAY
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
std::chrono::seconds m_currentTime
void dispatch(const MythEvent &event)
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
static const Type kPlaylistChangedEvent
void removeListener(QObject *listener)
Remove a listener to the observable.
virtual void PauseUntilBuffered(void)=0
void SaveBoolSetting(const QString &key, bool newValue)
virtual bool IsPaused(void) const =0
void start(QRunnable *runnable, const QString &debugName, int priority=0)
void addListener(QObject *listener)
void PauseIdleTimer(bool Pause)
Pause the idle timeout timer.
virtual void SetTimecode(std::chrono::milliseconds timecode)=0
QString GetSetting(const QString &key, const QString &defaultval="")
static const Type kAlbumArtChangedEvent
bool Queue(const MythNotification ¬ification)
Queue a notification Queue() is thread-safe and can be called from anywhere.
void moveTrackUpDown(bool moveUp, int whichTrack)