Go to the documentation of this file.
5 #include <QApplication>
58 setObjectName(
"MusicPlayer");
61 if (playmode.toLower() ==
"random")
63 else if (playmode.toLower() ==
"intelligent")
65 else if (playmode.toLower() ==
"album")
67 else if (playmode.toLower() ==
"artist")
73 if (repeatmode.toLower() ==
"track")
75 else if (repeatmode.toLower() ==
"all")
96 QMap<QString, int>::Iterator i;
274 QMutexLocker locker(
m_lock);
310 decoder->
cond()->wakeAll();
330 ShowOkPopup(tr(
"Got too many track unavailable errors. Maybe the host with the music on is off-line?"));
378 if (adevice ==
"default" || adevice.isEmpty())
387 adevice, pdevice,
FORMAT_S16, 2, AV_CODEC_ID_NONE, 44100,
393 LOG(VB_GENERAL, LOG_ERR,
394 QString(
"MusicPlayer: Cannot open audio output device: %1").arg(adevice));
401 LOG(VB_GENERAL, LOG_ERR,
402 QString(
"MusicPlayer: Cannot open audio output device: %1").arg(adevice));
403 LOG(VB_GENERAL, LOG_ERR,
422 QMutexLocker locker(
m_lock);
482 if (currentTrack >= 0)
525 auto *miniplayer =
new MiniPlayer(popupStack);
527 if (miniplayer->Create())
593 auto *miniplayer =
new MiniPlayer(popupStack);
595 if (miniplayer->Create())
608 auto *me =
dynamic_cast<MythEvent*
>(event);
612 if (me->Message().left(13) ==
"MUSIC_COMMAND")
614 QStringList list = me->
Message().simplified().split(
' ');
618 if (list[2] ==
"PLAY")
620 else if (list[2] ==
"STOP")
622 else if (list[2] ==
"PAUSE")
624 else if (list[2] ==
"SET_VOLUME")
628 int volume = list[3].toInt();
629 if (volume >= 0 && volume <= 100)
633 else if (list[2] ==
"GET_VOLUME")
635 QString message = QString(
"MUSIC_CONTROL ANSWER %1 %2")
640 else if (list[2] ==
"PLAY_FILE")
642 int start = me->Message().indexOf(
"'");
643 int end = me->Message().lastIndexOf(
"'");
645 if (start != -1 && end != -1 && start != end)
647 QString
filename = me->Message().mid(start + 1, end - start - 1);
654 LOG(VB_GENERAL, LOG_ERR,
655 QString(
"MusicPlayer: got invalid MUSIC_COMMAND "
656 "PLAY_FILE - %1").arg(me->Message()));
659 else if (list[2] ==
"PLAY_URL")
661 if (list.size() == 4)
670 LOG(VB_GENERAL, LOG_ERR,
671 QString(
"MusicPlayer: got invalid MUSIC_COMMAND "
672 "PLAY_URL - %1").arg(me->Message()));
675 else if (list[2] ==
"PLAY_TRACK")
677 if (list.size() == 4)
679 int trackID = list[3].toInt();
686 LOG(VB_GENERAL, LOG_ERR,
687 QString(
"MusicPlayer: got invalid MUSIC_COMMAND "
688 "PLAY_TRACK - %1").arg(me->Message()));
691 else if (list[2] ==
"GET_METADATA")
696 mdataStr = QString(
"%1 by %2 from %3").arg(mdata->
Title(), mdata->
Artist(), mdata->
Album());
698 mdataStr =
"Unknown Track2";
700 QString message = QString(
"MUSIC_CONTROL ANSWER %1 %2")
705 else if (list[2] ==
"GET_STATUS")
707 QString statusStr =
"STOPPED";
710 statusStr =
"PLAYING";
712 statusStr =
"PAUSED";
714 QString message = QString(
"MUSIC_CONTROL ANSWER %1 %2")
722 LOG(VB_GENERAL, LOG_ERR,
723 QString(
"MusicPlayer: got unknown/invalid MUSIC_COMMAND "
724 "- %1").arg(me->Message()));
727 else if (me->Message().startsWith(
"MUSIC_SETTINGS_CHANGED"))
731 else if (me->Message().startsWith(
"MUSIC_METADATA_CHANGED"))
735 QStringList list = me->Message().simplified().split(
' ');
736 if (list.size() == 2)
738 int songID = list[1].toInt();
751 else if (me->Message().startsWith(
"MUSIC_SCANNER_STARTED"))
753 QStringList list = me->Message().simplified().split(
' ');
754 if (list.size() == 2)
756 QString host = list[1];
759 tr(
"A music file scan has started on %1").arg(host),
760 tr(
"Music File Scanner"),
761 tr(
"This may take a while I'll give a shout when finished"));
764 else if (me->Message().startsWith(
"MUSIC_SCANNER_FINISHED"))
766 QStringList list = me->Message().simplified().split(
' ');
767 if (list.size() == 6)
769 QString host = list[1];
771 int totalTracks = list[2].toInt();
772 int newTracks = list[3].toInt();
773 int totalCoverart = list[4].toInt();
774 int newCoverart = list[5].toInt();
776 QString summary = QString(
"Total Tracks: %2, new tracks: %3,\nTotal Coverart: %4, New CoverArt %5")
777 .arg(totalTracks).arg(newTracks).arg(totalCoverart).arg(newCoverart);
779 tr(
"A music file scan has finished on %1").arg(host),
780 tr(
"Music File Scanner"), summary);
785 else if (me->Message().startsWith(
"MUSIC_SCANNER_ERROR"))
787 QStringList list = me->Message().simplified().split(
' ');
788 if (list.size() == 3)
790 QString host = list[1];
791 QString
error = list[2];
794 if (
error ==
"Already_Running")
797 tr(
"Music File Scanner"),
798 tr(
"Can't run the music file scanner because it is already running on %1").arg(host));
800 else if (
error ==
"Stalled")
803 tr(
"Music File Scanner"),
804 tr(
"The music file scanner has been running for more than 60 minutes on %1.\nResetting and trying again")
818 LOG(VB_GENERAL, LOG_ERR, QString(
"Audio Output Error: %1").arg(*aoe->errorMessage()));
840 LOG(VB_GENERAL, LOG_ERR, QString(
"Decoder Error: %2").arg(*dxe->errorMessage()));
862 LOG(VB_GENERAL, LOG_ERR, QString(
"Decoder Handler Error - %1").arg(*dhe->getMessage()));
922 auto metadataSecs = duration_cast<std::chrono::seconds>(
getCurrentMetadata()->Length());
926 LOG(VB_GENERAL, LOG_NOTICE, QString(
"MusicPlayer: Updating track length was %1s, should be %2s")
955 QObject::customEvent(event);
1017 for (
int x = 0; x < list->count(); x++)
1022 if (mdata->
ID() ==
id)
1034 if (moveUp && whichTrack <= 0)
1125 decoder->
seek(pos.count());
1138 auto *miniplayer =
new MiniPlayer(popupStack);
1140 if (miniplayer->Create())
1161 LOG(VB_GENERAL, LOG_ERR,
1162 QString(
"MusicPlayer: asked to set the current track to an invalid track no. %1")
1255 int curTrackID = -1;
1268 if (curTrackID != -1)
1302 if (
GetMythDB()->GetNumSetting(
"AllowTagWriting", 0) == 1)
1304 QStringList strList;
1305 strList << QString(
"MUSIC_TAG_UPDATE_VOLATILE")
1429 map[
"volumemute"] =
isMuted() ? tr(
"%1% (Muted)",
"Zero Audio Volume").arg(
getVolume()) :
1431 map[
"volume"] = QString(
"%1").arg(
getVolume());
1432 map[
"volumepercent"] = QString(
"%1%").arg(
getVolume());
1433 map[
"mute"] =
isMuted() ? tr(
"Muted") :
"";
1512 QMutexLocker locker(
m_lock);
1525 LOG(VB_PLAYBACK, LOG_INFO, QString (
"decoder handler is ready, decoding %1")
1526 .arg(decoder->
getURL()));
1529 auto *cddecoder =
dynamic_cast<CdDecoder*
>(decoder);
1547 QMutexLocker locker(
m_lock);
1583 LOG(VB_PLAYBACK, LOG_ERR, QString(
"Cannot initialise decoder for %1")
1584 .arg(decoder->
getURL()));
1638 QString image =
"musicscanner.png";
1640 LOG(VB_GENERAL, LOG_ERR,
"MusicPlayer: sendNotification failed to find the 'musicscanner.png' image");
1643 map[
"asar"] = title;
1644 map[
"minm"] = author;
1649 n->SetId(notificationID);
1652 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
static void error(const char *str,...)
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
MusicMetadata & getMetadata()
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)