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")
618 else if (list[2] ==
"STOP")
622 else if (list[2] ==
"PAUSE")
626 else if (list[2] ==
"SET_VOLUME")
630 int volume = list[3].toInt();
631 if (volume >= 0 && volume <= 100)
635 else if (list[2] ==
"GET_VOLUME")
637 QString message = QString(
"MUSIC_CONTROL ANSWER %1 %2")
642 else if (list[2] ==
"PLAY_FILE")
644 int start = me->Message().indexOf(
"'");
645 int end = me->Message().lastIndexOf(
"'");
647 if (start != -1 && end != -1 && start != end)
649 QString
filename = me->Message().mid(start + 1, end - start - 1);
656 LOG(VB_GENERAL, LOG_ERR,
657 QString(
"MusicPlayer: got invalid MUSIC_COMMAND "
658 "PLAY_FILE - %1").arg(me->Message()));
661 else if (list[2] ==
"PLAY_URL")
663 if (list.size() == 4)
672 LOG(VB_GENERAL, LOG_ERR,
673 QString(
"MusicPlayer: got invalid MUSIC_COMMAND "
674 "PLAY_URL - %1").arg(me->Message()));
677 else if (list[2] ==
"PLAY_TRACK")
679 if (list.size() == 4)
681 int trackID = list[3].toInt();
688 LOG(VB_GENERAL, LOG_ERR,
689 QString(
"MusicPlayer: got invalid MUSIC_COMMAND "
690 "PLAY_TRACK - %1").arg(me->Message()));
693 else if (list[2] ==
"GET_METADATA")
698 mdataStr = QString(
"%1 by %2 from %3").arg(mdata->
Title(), mdata->
Artist(), mdata->
Album());
700 mdataStr =
"Unknown Track2";
702 QString message = QString(
"MUSIC_CONTROL ANSWER %1 %2")
707 else if (list[2] ==
"GET_STATUS")
709 QString statusStr =
"STOPPED";
712 statusStr =
"PLAYING";
714 statusStr =
"PAUSED";
716 QString message = QString(
"MUSIC_CONTROL ANSWER %1 %2")
724 LOG(VB_GENERAL, LOG_ERR,
725 QString(
"MusicPlayer: got unknown/invalid MUSIC_COMMAND "
726 "- %1").arg(me->Message()));
729 else if (me->Message().startsWith(
"MUSIC_SETTINGS_CHANGED"))
733 else if (me->Message().startsWith(
"MUSIC_METADATA_CHANGED"))
737 QStringList list = me->Message().simplified().split(
' ');
738 if (list.size() == 2)
740 int songID = list[1].toInt();
753 else if (me->Message().startsWith(
"MUSIC_SCANNER_STARTED"))
755 QStringList list = me->Message().simplified().split(
' ');
756 if (list.size() == 2)
758 const QString& host = list[1];
761 tr(
"A music file scan has started on %1").arg(host),
762 tr(
"Music File Scanner"),
763 tr(
"This may take a while I'll give a shout when finished"));
766 else if (me->Message().startsWith(
"MUSIC_SCANNER_FINISHED"))
768 QStringList list = me->Message().simplified().split(
' ');
769 if (list.size() == 6)
771 const QString& host = list[1];
773 int totalTracks = list[2].toInt();
774 int newTracks = list[3].toInt();
775 int totalCoverart = list[4].toInt();
776 int newCoverart = list[5].toInt();
778 QString summary = QString(
"Total Tracks: %2, new tracks: %3,\nTotal Coverart: %4, New CoverArt %5")
779 .arg(totalTracks).arg(newTracks).arg(totalCoverart).arg(newCoverart);
781 tr(
"A music file scan has finished on %1").arg(host),
782 tr(
"Music File Scanner"), summary);
787 else if (me->Message().startsWith(
"MUSIC_SCANNER_ERROR"))
789 QStringList list = me->Message().simplified().split(
' ');
790 if (list.size() == 3)
792 const QString& host = list[1];
793 const QString&
error = list[2];
796 if (
error ==
"Already_Running")
799 tr(
"Music File Scanner"),
800 tr(
"Can't run the music file scanner because it is already running on %1").arg(host));
802 else if (
error ==
"Stalled")
805 tr(
"Music File Scanner"),
806 tr(
"The music file scanner has been running for more than 60 minutes on %1.\nResetting and trying again")
813 if (event->type() == AudioOutput::Event::kError)
820 LOG(VB_GENERAL, LOG_ERR, QString(
"Audio Output Error: %1").arg(aoe->errorMessage()));
844 LOG(VB_GENERAL, LOG_ERR, QString(
"Decoder Error: %2").arg(*dxe->errorMessage()));
868 LOG(VB_GENERAL, LOG_ERR, QString(
"Decoder Handler Error - %1").arg(*dhe->getMessage()));
885 else if (event->type() == AudioOutput::Event::kInfo)
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)
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 SaveDurSetting(const QString &key, T newValue)
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.
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.
T GetDurSetting(const QString &key, T defaultval=T::zero())
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)
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()