diff --git a/ryzom/client/src/interface_v3/music_player.cpp b/ryzom/client/src/interface_v3/music_player.cpp index 717ea7e66..10b4cd288 100644 --- a/ryzom/client/src/interface_v3/music_player.cpp +++ b/ryzom/client/src/interface_v3/music_player.cpp @@ -28,6 +28,9 @@ #include "interface_manager.h" #include "../client_cfg.h" +#include "nel/misc/thread.h" +#include "nel/misc/mutex.h" + using namespace std; using namespace NLMISC; using namespace NL3D; @@ -48,6 +51,88 @@ extern UDriver *Driver; #define MP3_SAVE_REPEAT "UI:SAVE:MP3_REPEAT" CMusicPlayer MusicPlayer; +static NLMISC::CUnfairMutex MusicPlayerMutex; + +// *************************************************************************** +class CMusicPlayerWorker : public NLMISC::IRunnable +{ +private: + bool _Running; + IThread *_Thread; + + std::vector _Files; + +public: + CMusicPlayerWorker(): _Running(false), _Thread(NULL) + { + } + + ~CMusicPlayerWorker() + { + _Running = false; + if (_Thread) + { + _Thread->terminate(); + delete _Thread; + _Thread = NULL; + } + } + + bool isRunning() const { return _Running; } + + void run() + { + _Running = true; + + uint i = 0; + while(_Running && SoundMngr && i < _Files.size()) + { + // get copy incase _Files changes + std::string filename(_Files[i]); + + std::string title; + float length; + + if (SoundMngr->getMixer()->getSongTitle(filename, title, length)) + { + MusicPlayer.updateSong(filename, title, length); + } + + ++i; + } + + _Running = false; + _Files.clear(); + } + + // called from GUI + void getSongsInfo(const std::vector &filenames) + { + if (_Thread) + { + stopThread(); + } + + _Files = filenames; + + _Thread = IThread::create(this); + nlassert(_Thread != NULL); + _Thread->start(); + } + + void stopThread() + { + _Running = false; + if (_Thread) + { + _Thread->wait(); + delete _Thread; + _Thread = NULL; + } + } +}; + +static CMusicPlayerWorker MusicPlayerWorker; // *************************************************************************** @@ -69,11 +154,14 @@ bool CMusicPlayer::isShuffleEnabled() const return (NLGUI::CDBManager::getInstance()->getDbProp(MP3_SAVE_SHUFFLE)->getValue32() == 1); } - // *************************************************************************** -void CMusicPlayer::playSongs (const std::vector &songs) +void CMusicPlayer::playSongs (const std::vector &filenames) { - _Songs = songs; + _Songs.clear(); + for (uint i=0; i _Songs.size()) @@ -87,6 +175,9 @@ void CMusicPlayer::playSongs (const std::vector &songs) // If pause, stop, else play will resume if (_State == Paused || _Songs.empty()) _State = Stopped; + + // get song title/duration using worker thread + MusicPlayerWorker.getSongsInfo(filenames); } // *************************************************************************** @@ -107,6 +198,88 @@ void CMusicPlayer::updatePlaylist(sint prevIndex) if (pIE) pIE->setActive(true); } +// *************************************************************************** +// called from worker thread +void CMusicPlayer::updateSong(const std::string filename, const std::string title, float length) +{ + CAutoMutex mutex(MusicPlayerMutex); + + _SongUpdateQueue.push_back(CSongs(filename, title, length)); +} + +// *************************************************************************** +// called from GUI +void CMusicPlayer::updateSongs() +{ + CAutoMutex mutex(MusicPlayerMutex); + if (!_SongUpdateQueue.empty()) + { + for(uint i = 0; i < _SongUpdateQueue.size(); ++i) + { + updateSong(_SongUpdateQueue[i]); + } + _SongUpdateQueue.clear(); + } +} + +// *************************************************************************** +void CMusicPlayer::updateSong(const CSongs &song) +{ + uint index = 0; + while(index < _Songs.size()) + { + if (_Songs[index].Filename == song.Filename) + { + _Songs[index].Title = song.Title; + _Songs[index].Length = song.Length; + break; + } + + ++index; + } + if (index == _Songs.size()) + { + nlwarning("Unknown song file '%s'", song.Filename.c_str()); + return; + } + + std::string rowId(toString("%s:s%d", MP3_PLAYER_PLAYLIST_LIST, index)); + CInterfaceGroup *pIG = dynamic_cast(CWidgetManager::getInstance()->getElementFromId(rowId)); + if (!pIG) + { + nlwarning("Playlist row '%s' not found", rowId.c_str()); + return; + } + + CViewText *pVT; + pVT = dynamic_cast(pIG->getView(TEMPLATE_PLAYLIST_SONG_TITLE)); + if (pVT) + { + pVT->setHardText(song.Title); + } + else + { + nlwarning("title element '%s' not found", TEMPLATE_PLAYLIST_SONG_TITLE); + } + + pVT = dynamic_cast(pIG->getView(TEMPLATE_PLAYLIST_SONG_DURATION)); + if (pVT) + { + uint min = (sint32)(song.Length / 60) % 60; + uint sec = (sint32)(song.Length) % 60; + uint hour = song.Length / 3600; + std::string duration(toString("%02d:%02d", min, sec)); + if (hour > 0) + duration = toString("%02d:", hour) + duration; + + pVT->setHardText(duration); + } + else + { + nlwarning("duration element '%s' not found", TEMPLATE_PLAYLIST_SONG_DURATION); + } +} + // *************************************************************************** void CMusicPlayer::shuffleAndRebuildPlaylist() { @@ -131,12 +304,16 @@ void CMusicPlayer::rebuildPlaylist() _CurrentSongIndex = i; } - uint min = (sint32)(_Songs[i].Length / 60) % 60; - uint sec = (sint32)(_Songs[i].Length) % 60; - uint hour = _Songs[i].Length / 3600; - std::string duration(toString("%02d:%02d", min, sec)); - if (hour > 0) - duration = toString("%02d:", hour) + duration; + std::string duration("--:--"); + if (_Songs[i].Length > 0) + { + uint min = (sint32)(_Songs[i].Length / 60) % 60; + uint sec = (sint32)(_Songs[i].Length) % 60; + uint hour = _Songs[i].Length / 3600; + duration = toString("%02d:%02d", min, sec); + if (hour > 0) + duration = toString("%02d:", hour) + duration; + } vector< pair > vParams; vParams.push_back(pair("id", "s" + toString(i))); @@ -289,6 +466,12 @@ void CMusicPlayer::update () { if(!SoundMngr) return; + + if (MusicPlayerWorker.isRunning() || !_SongUpdateQueue.empty()) + { + updateSongs(); + } + if (_State == Playing) { CViewText *pVT = dynamic_cast(CWidgetManager::getInstance()->getElementFromId("ui:interface:mp3_player:screen:text")); @@ -331,7 +514,7 @@ void CMusicPlayer::update () } // *************************************************************************** -static void addFromPlaylist(const std::string &playlist, std::vector &filenames) +static void addFromPlaylist(const std::string &playlist, const std::vector &extensions, std::vector &filenames) { static uint8 utf8Header[] = { 0xefu, 0xbbu, 0xbfu }; @@ -362,9 +545,19 @@ static void addFromPlaylist(const std::string &playlist, std::vector songs; - for (i=0; igetMixer()->getSongTitle(filenames[i], song.Title, song.Length); - if (song.Length > 0) - songs.push_back (song); - } + // Sort songs by filename + sort(filenames.begin(), filenames.end()); - MusicPlayer.playSongs(songs); + MusicPlayer.playSongs(filenames); } else if (Params == "update_playlist") { diff --git a/ryzom/client/src/interface_v3/music_player.h b/ryzom/client/src/interface_v3/music_player.h index 549fa169d..3569d6439 100644 --- a/ryzom/client/src/interface_v3/music_player.h +++ b/ryzom/client/src/interface_v3/music_player.h @@ -42,12 +42,16 @@ public: class CSongs { public: + CSongs(std::string file = std::string(), std::string title = std::string(), float length = 0.f) + : Filename(file), Title(title), Length(length) + { } + std::string Filename; std::string Title; float Length; }; - void playSongs (const std::vector &songs); + void playSongs (const std::vector &filenames); void play (sint index = -1); // Play the song at current position, if playing, restart. If paused, resume. void pause (); void stop (); @@ -68,12 +72,23 @@ public: // Update playlist active row void updatePlaylist(sint prevIndex = -1); + // Update single song title/duration on _Songs and on playlist + void updateSong(const CSongs &song); + + // Update _Songs and playlist from _SongUpdateQueue + void updateSongs(); + + // update song from worker thread + void updateSong(const std::string filename, const std::string title, float length); + private: // The playlist CSongs _CurrentSong; uint _CurrentSongIndex; // If (!_Songs.empty()) must always be <_Songs.size() std::vector _Songs; + // updated info from worker thread + std::vector _SongUpdateQueue; // State enum TState { Stopped, Playing, Paused } _State;