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));
411 QMutexLocker locker(
m_lock);
424 if (
nullptr == playlist)
464 if (
nullptr == playlist)
477 if (currentTrack >= 0)
496 if (
nullptr == playlist)
521 auto *miniplayer =
new MiniPlayer(popupStack);
523 if (miniplayer->Create())
589 auto *miniplayer =
new MiniPlayer(popupStack);
591 if (miniplayer->Create())
604 auto *me =
dynamic_cast<MythEvent*
>(event);
608 if (me->Message().left(13) ==
"MUSIC_COMMAND")
610 QStringList list = me->
Message().simplified().split(
' ');
614 if (list[2] ==
"PLAY")
616 else if (list[2] ==
"STOP")
618 else if (list[2] ==
"PAUSE")
620 else if (list[2] ==
"SET_VOLUME")
624 int volume = list[3].toInt();
625 if (volume >= 0 && volume <= 100)
629 else if (list[2] ==
"GET_VOLUME")
631 QString message = QString(
"MUSIC_CONTROL ANSWER %1 %2")
636 else if (list[2] ==
"PLAY_FILE")
638 int start = me->Message().indexOf(
"'");
639 int end = me->Message().lastIndexOf(
"'");
641 if (start != -1 && end != -1 && start != end)
643 QString
filename = me->Message().mid(start + 1, end - start - 1);
650 LOG(VB_GENERAL, LOG_ERR,
651 QString(
"MusicPlayer: got invalid MUSIC_COMMAND "
652 "PLAY_FILE - %1").arg(me->Message()));
655 else if (list[2] ==
"PLAY_URL")
657 if (list.size() == 4)
666 LOG(VB_GENERAL, LOG_ERR,
667 QString(
"MusicPlayer: got invalid MUSIC_COMMAND "
668 "PLAY_URL - %1").arg(me->Message()));
671 else if (list[2] ==
"PLAY_TRACK")
673 if (list.size() == 4)
675 int trackID = list[3].toInt();
682 LOG(VB_GENERAL, LOG_ERR,
683 QString(
"MusicPlayer: got invalid MUSIC_COMMAND "
684 "PLAY_TRACK - %1").arg(me->Message()));
687 else if (list[2] ==
"GET_METADATA")
692 mdataStr = QString(
"%1 by %2 from %3").arg(mdata->
Title(), mdata->
Artist(), mdata->
Album());
694 mdataStr =
"Unknown Track2";
696 QString message = QString(
"MUSIC_CONTROL ANSWER %1 %2")
701 else if (list[2] ==
"GET_STATUS")
703 QString statusStr =
"STOPPED";
706 statusStr =
"PLAYING";
708 statusStr =
"PAUSED";
710 QString message = QString(
"MUSIC_CONTROL ANSWER %1 %2")
718 LOG(VB_GENERAL, LOG_ERR,
719 QString(
"MusicPlayer: got unknown/invalid MUSIC_COMMAND "
720 "- %1").arg(me->Message()));
723 else if (me->Message().startsWith(
"MUSIC_SETTINGS_CHANGED"))
727 else if (me->Message().startsWith(
"MUSIC_METADATA_CHANGED"))
731 QStringList list = me->Message().simplified().split(
' ');
732 if (list.size() == 2)
734 int songID = list[1].toInt();
747 else if (me->Message().startsWith(
"MUSIC_SCANNER_STARTED"))
749 QStringList list = me->Message().simplified().split(
' ');
750 if (list.size() == 2)
752 const QString& host = list[1];
755 tr(
"A music file scan has started on %1").arg(host),
756 tr(
"Music File Scanner"),
757 tr(
"This may take a while I'll give a shout when finished"));
760 else if (me->Message().startsWith(
"MUSIC_SCANNER_FINISHED"))
762 QStringList list = me->Message().simplified().split(
' ');
763 if (list.size() == 6)
765 const QString& host = list[1];
767 int totalTracks = list[2].toInt();
768 int newTracks = list[3].toInt();
769 int totalCoverart = list[4].toInt();
770 int newCoverart = list[5].toInt();
772 QString summary = QString(
"Total Tracks: %2, new tracks: %3,\nTotal Coverart: %4, New CoverArt %5")
773 .arg(totalTracks).arg(newTracks).arg(totalCoverart).arg(newCoverart);
775 tr(
"A music file scan has finished on %1").arg(host),
776 tr(
"Music File Scanner"), summary);
781 else if (me->Message().startsWith(
"MUSIC_SCANNER_ERROR"))
783 QStringList list = me->Message().simplified().split(
' ');
784 if (list.size() == 3)
786 const QString& host = list[1];
787 const QString&
error = list[2];
790 if (
error ==
"Already_Running")
793 tr(
"Music File Scanner"),
794 tr(
"Can't run the music file scanner because it is already running on %1").arg(host));
796 else if (
error ==
"Stalled")
799 tr(
"Music File Scanner"),
800 tr(
"The music file scanner has been running for more than 60 minutes on %1.\nResetting and trying again")
807 if (event->type() == AudioOutput::Event::kError)
814 LOG(VB_GENERAL, LOG_ERR, QString(
"Audio Output Error: %1").arg(aoe->errorMessage()));
836 LOG(VB_GENERAL, LOG_ERR, QString(
"Decoder Error: %2").arg(*dxe->errorMessage()));
858 LOG(VB_GENERAL, LOG_ERR, QString(
"Decoder Handler Error - %1").arg(*dhe->getMessage()));
873 else if (event->type() == AudioOutput::Event::kInfo)
918 auto metadataSecs = duration_cast<std::chrono::seconds>(
getCurrentMetadata()->Length());
922 LOG(VB_GENERAL, LOG_NOTICE, QString(
"MusicPlayer: Updating track length was %1s, should be %2s")
951 QObject::customEvent(event);
980 if ((bookmark < 0) ||
999 if ((bookmark < 0) ||
1021 for (
int x = 0; x < list->count(); x++)
1026 if (mdata->
ID() ==
id)
1036 if (
nullptr == playlist)
1039 if (moveUp && whichTrack <= 0)
1131 decoder->
seek(pos.count());
1144 auto *miniplayer =
new MiniPlayer(popupStack);
1146 if (miniplayer->Create())
1157 if (
nullptr == playlist)
1166 if (m_currentTrack < 0 || m_currentTrack >= playlist->
getTrackCount())
1168 LOG(VB_GENERAL, LOG_ERR,
1169 QString(
"MusicPlayer: asked to set the current track to an invalid track no. %1")
1206 if (m_currentTrack < playlist->getTrackCount() - 1)
1264 int curTrackID = -1;
1273 if (
nullptr == playlist)
1278 if (curTrackID != -1)
1312 if (
GetMythDB()->GetNumSetting(
"AllowTagWriting", 0) == 1)
1314 QStringList strList;
1315 strList << QString(
"MUSIC_TAG_UPDATE_VOLATILE")
1439 map[
"volumemute"] =
isMuted() ? tr(
"%1% (Muted)",
"Zero Audio Volume").arg(
getVolume()) :
1441 map[
"volume"] = QString(
"%1").arg(
getVolume());
1442 map[
"volumepercent"] = QString(
"%1%").arg(
getVolume());
1443 map[
"mute"] =
isMuted() ? tr(
"Muted") :
"";
1523 QMutexLocker locker(
m_lock);
1536 LOG(VB_PLAYBACK, LOG_INFO, QString (
"decoder handler is ready, decoding %1")
1537 .arg(decoder->
getURL()));
1540 auto *cddecoder =
dynamic_cast<CdDecoder*
>(decoder);
1558 QMutexLocker locker(
m_lock);
1594 LOG(VB_PLAYBACK, LOG_ERR, QString(
"Cannot initialise decoder for %1")
1595 .arg(decoder->
getURL()));
1610 if (
nullptr == playlist)
1652 QString image =
"musicscanner.png";
1654 LOG(VB_GENERAL, LOG_ERR,
"MusicPlayer: sendNotification failed to find the 'musicscanner.png' image");
1657 map[
"asar"] = title;
1658 map[
"minm"] = author;
1663 n->SetId(notificationID);
1666 n->SetFullScreen(
false);
MusicMetadata * getMetadata(int an_id)
StreamList * getStreams(void)
void addVisual(Visualization *v)
bool isConfigured() const
virtual bool IsPaused(void) const =0
virtual void SetTimecode(std::chrono::milliseconds timecode)=0
void removeVisual(Visualization *v)
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)
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)
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()