Merge remote-tracking branch 'ryzomcore/develop' into develop

develop
kaetemi 5 years ago
commit 491d39ff08

@ -28,6 +28,9 @@
#include "interface_manager.h" #include "interface_manager.h"
#include "../client_cfg.h" #include "../client_cfg.h"
#include "nel/misc/thread.h"
#include "nel/misc/mutex.h"
using namespace std; using namespace std;
using namespace NLMISC; using namespace NLMISC;
using namespace NL3D; using namespace NL3D;
@ -48,6 +51,88 @@ extern UDriver *Driver;
#define MP3_SAVE_REPEAT "UI:SAVE:MP3_REPEAT" #define MP3_SAVE_REPEAT "UI:SAVE:MP3_REPEAT"
CMusicPlayer MusicPlayer; CMusicPlayer MusicPlayer;
static NLMISC::CUnfairMutex MusicPlayerMutex;
// ***************************************************************************
class CMusicPlayerWorker : public NLMISC::IRunnable
{
private:
bool _Running;
IThread *_Thread;
std::vector<std::string> _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<std::string> &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); return (NLGUI::CDBManager::getInstance()->getDbProp(MP3_SAVE_SHUFFLE)->getValue32() == 1);
} }
// *************************************************************************** // ***************************************************************************
void CMusicPlayer::playSongs (const std::vector<CSongs> &songs) void CMusicPlayer::playSongs (const std::vector<std::string> &filenames)
{
_Songs.clear();
for (uint i=0; i<filenames.size(); i++)
{ {
_Songs = songs; _Songs.push_back(CSongs(filenames[i], CFile::getFilename(filenames[i]), 0.f));
}
// reset song index if out of bounds // reset song index if out of bounds
if (_CurrentSongIndex > _Songs.size()) if (_CurrentSongIndex > _Songs.size())
@ -85,8 +173,11 @@ void CMusicPlayer::playSongs (const std::vector<CSongs> &songs)
rebuildPlaylist(); rebuildPlaylist();
// If pause, stop, else play will resume // If pause, stop, else play will resume
if (_State == Paused) if (_State == Paused || _Songs.empty())
_State = Stopped; _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); if (pIE) pIE->setActive(true);
} }
// ***************************************************************************
// called from worker thread
void CMusicPlayer::updateSong(const std::string filename, const std::string title, float length)
{
CAutoMutex<CUnfairMutex> mutex(MusicPlayerMutex);
_SongUpdateQueue.push_back(CSongs(filename, title, length));
}
// ***************************************************************************
// called from GUI
void CMusicPlayer::updateSongs()
{
CAutoMutex<CUnfairMutex> 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<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId(rowId));
if (!pIG)
{
nlwarning("Playlist row '%s' not found", rowId.c_str());
return;
}
CViewText *pVT;
pVT = dynamic_cast<CViewText *>(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<CViewText *>(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() void CMusicPlayer::shuffleAndRebuildPlaylist()
{ {
@ -131,12 +304,16 @@ void CMusicPlayer::rebuildPlaylist()
_CurrentSongIndex = i; _CurrentSongIndex = i;
} }
std::string duration("--:--");
if (_Songs[i].Length > 0)
{
uint min = (sint32)(_Songs[i].Length / 60) % 60; uint min = (sint32)(_Songs[i].Length / 60) % 60;
uint sec = (sint32)(_Songs[i].Length) % 60; uint sec = (sint32)(_Songs[i].Length) % 60;
uint hour = _Songs[i].Length / 3600; uint hour = _Songs[i].Length / 3600;
std::string duration(toString("%02d:%02d", min, sec)); duration = toString("%02d:%02d", min, sec);
if (hour > 0) if (hour > 0)
duration = toString("%02d:", hour) + duration; duration = toString("%02d:", hour) + duration;
}
vector< pair<string, string> > vParams; vector< pair<string, string> > vParams;
vParams.push_back(pair<string, string>("id", "s" + toString(i))); vParams.push_back(pair<string, string>("id", "s" + toString(i)));
@ -176,6 +353,17 @@ void CMusicPlayer::play (sint index)
if(!SoundMngr) if(!SoundMngr)
return; return;
if (_Songs.empty())
{
index = 0;
createPlaylistFromMusic();
}
if (_Songs.empty())
{
_State = Stopped;
return;
}
sint prevSongIndex = _CurrentSongIndex; sint prevSongIndex = _CurrentSongIndex;
@ -288,7 +476,16 @@ void CMusicPlayer::next ()
void CMusicPlayer::update () void CMusicPlayer::update ()
{ {
if(!SoundMngr) if(!SoundMngr)
{
_State = Stopped;
return; return;
}
if (MusicPlayerWorker.isRunning() || !_SongUpdateQueue.empty())
{
updateSongs();
}
if (_State == Playing) if (_State == Playing)
{ {
CViewText *pVT = dynamic_cast<CViewText*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:mp3_player:screen:text")); CViewText *pVT = dynamic_cast<CViewText*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:mp3_player:screen:text"));
@ -331,7 +528,7 @@ void CMusicPlayer::update ()
} }
// *************************************************************************** // ***************************************************************************
static void addFromPlaylist(const std::string &playlist, std::vector<std::string> &filenames) static void addFromPlaylist(const std::string &playlist, const std::vector<std::string> &extensions, std::vector<std::string> &filenames)
{ {
static uint8 utf8Header[] = { 0xefu, 0xbbu, 0xbfu }; static uint8 utf8Header[] = { 0xefu, 0xbbu, 0xbfu };
@ -362,31 +559,26 @@ static void addFromPlaylist(const std::string &playlist, std::vector<std::string
// Not a comment line // Not a comment line
if (lineStr[0] != '#') if (lineStr[0] != '#')
{ {
std::string filepath = CFile::getPath(lineStr); std::string filename = CPath::makePathAbsolute(CFile::getPath(lineStr), basePlaylist) + CFile::getFilename(lineStr);
std::string filename = CFile::getFilename(lineStr); std::string ext = toLower(CFile::getExtension(filename));
filenames.push_back (CPath::makePathAbsolute(filepath, basePlaylist)+filename); if (std::find(extensions.begin(), extensions.end(), ext) != extensions.end())
{
if (CFile::fileExists(filename))
filenames.push_back(filename);
else
nlwarning("Ignore non-existing file '%s'", filename.c_str());
} }
else
{
nlwarning("Ingnore invalid extension '%s'", filename.c_str());
} }
fclose (file);
} }
} }
fclose (file);
// *************************************************************************** }
class CMusicPlayerPlaySongs: public IActionHandler
{
public:
virtual void execute(CCtrlBase * /* pCaller */, const string &Params)
{
if(!SoundMngr)
{
// Do not show warning on volume change as its restored at startup
if (Params.find("volume") == std::string::npos)
CInterfaceManager::getInstance()->messageBox (CI18N::get ("uiMP3SoundDisabled"));
return;
} }
if (Params == "play_songs") void CMusicPlayer::createPlaylistFromMusic()
{ {
std::vector<std::string> extensions; std::vector<std::string> extensions;
SoundMngr->getMixer()->getMusicExtensions(extensions); SoundMngr->getMixer()->getMusicExtensions(extensions);
@ -431,33 +623,36 @@ public:
} }
} }
// Sort songs by filename
sort (filenames.begin(), filenames.end());
// Add songs from playlists // Add songs from playlists
for (i = 0; i < playlists.size(); ++i) for (i = 0; i < playlists.size(); ++i)
{ {
addFromPlaylist(playlists[i], filenames); addFromPlaylist(playlists[i], extensions, filenames);
} }
// Build the songs array // Sort songs by filename
std::vector<CMusicPlayer::CSongs> songs; sort(filenames.begin(), filenames.end());
for (i=0; i<filenames.size(); i++)
{ playSongs(filenames);
if (!CFile::fileExists(filenames[i])) {
nlwarning("Ignore non-existing file '%s'", filenames[i].c_str());
continue;
} }
CMusicPlayer::CSongs song; // ***************************************************************************
song.Filename = filenames[i]; class CMusicPlayerPlaySongs: public IActionHandler
// TODO: cache the result for next refresh {
SoundMngr->getMixer()->getSongTitle(filenames[i], song.Title, song.Length); public:
if (song.Length > 0) virtual void execute(CCtrlBase * /* pCaller */, const string &Params)
songs.push_back (song); {
if(!SoundMngr)
{
// Do not show warning on volume change as its restored at startup
if (Params.find("volume") == std::string::npos)
CInterfaceManager::getInstance()->messageBox (CI18N::get ("uiMP3SoundDisabled"));
return;
} }
MusicPlayer.playSongs(songs); if (Params == "play_songs")
{
MusicPlayer.createPlaylistFromMusic();
} }
else if (Params == "update_playlist") else if (Params == "update_playlist")
{ {

@ -42,12 +42,16 @@ public:
class CSongs class CSongs
{ {
public: 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 Filename;
std::string Title; std::string Title;
float Length; float Length;
}; };
void playSongs (const std::vector<CSongs> &songs); void playSongs (const std::vector<std::string> &filenames);
void play (sint index = -1); // Play the song at current position, if playing, restart. If paused, resume. void play (sint index = -1); // Play the song at current position, if playing, restart. If paused, resume.
void pause (); void pause ();
void stop (); void stop ();
@ -68,12 +72,26 @@ public:
// Update playlist active row // Update playlist active row
void updatePlaylist(sint prevIndex = -1); 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);
// scan music folder and rebuild playlist
void createPlaylistFromMusic();
private: private:
// The playlist // The playlist
CSongs _CurrentSong; CSongs _CurrentSong;
uint _CurrentSongIndex; // If (!_Songs.empty()) must always be <_Songs.size() uint _CurrentSongIndex; // If (!_Songs.empty()) must always be <_Songs.size()
std::vector<CSongs> _Songs; std::vector<CSongs> _Songs;
// updated info from worker thread
std::vector<CSongs> _SongUpdateQueue;
// State // State
enum TState { Stopped, Playing, Paused } _State; enum TState { Stopped, Playing, Paused } _State;

@ -556,11 +556,11 @@ void clearBuffers()
} }
// Sky is used to clear the frame buffer now, but if in line or point polygon mode, we should draw it // Sky is used to clear the frame buffer now, but if in line or point polygon mode, we should draw it
if (Driver->getPolygonMode() != UDriver::Filled) if (Driver->getPolygonMode() != UDriver::Filled || !Filter3D[FilterSky])
{ {
if (!Driver->isLost()) if (!Driver->isLost())
{ {
Driver->clearBuffers (CRGBA(127, 127, 127)); Driver->clearBuffers (ClientCfg.BGColor);
} }
} }
} }

Loading…
Cancel
Save