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

develop
kaetemi 5 years ago
commit 491d39ff08

@ -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<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);
}
// ***************************************************************************
void CMusicPlayer::playSongs (const std::vector<CSongs> &songs)
void CMusicPlayer::playSongs (const std::vector<std::string> &filenames)
{
_Songs = songs;
_Songs.clear();
for (uint i=0; i<filenames.size(); i++)
{
_Songs.push_back(CSongs(filenames[i], CFile::getFilename(filenames[i]), 0.f));
}
// reset song index if out of bounds
if (_CurrentSongIndex > _Songs.size())
@ -85,8 +173,11 @@ void CMusicPlayer::playSongs (const std::vector<CSongs> &songs)
rebuildPlaylist();
// If pause, stop, else play will resume
if (_State == Paused)
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<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()
{
@ -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<string, string> > vParams;
vParams.push_back(pair<string, string>("id", "s" + toString(i)));
@ -176,6 +353,17 @@ void CMusicPlayer::play (sint index)
if(!SoundMngr)
return;
if (_Songs.empty())
{
index = 0;
createPlaylistFromMusic();
}
if (_Songs.empty())
{
_State = Stopped;
return;
}
sint prevSongIndex = _CurrentSongIndex;
@ -288,7 +476,16 @@ void CMusicPlayer::next ()
void CMusicPlayer::update ()
{
if(!SoundMngr)
{
_State = Stopped;
return;
}
if (MusicPlayerWorker.isRunning() || !_SongUpdateQueue.empty())
{
updateSongs();
}
if (_State == Playing)
{
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 };
@ -362,15 +559,82 @@ static void addFromPlaylist(const std::string &playlist, std::vector<std::string
// Not a comment line
if (lineStr[0] != '#')
{
std::string filepath = CFile::getPath(lineStr);
std::string filename = CFile::getFilename(lineStr);
filenames.push_back (CPath::makePathAbsolute(filepath, basePlaylist)+filename);
std::string filename = CPath::makePathAbsolute(CFile::getPath(lineStr), basePlaylist) + CFile::getFilename(lineStr);
std::string ext = toLower(CFile::getExtension(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);
}
}
void CMusicPlayer::createPlaylistFromMusic()
{
std::vector<std::string> extensions;
SoundMngr->getMixer()->getMusicExtensions(extensions);
// no format supported
if (extensions.empty())
{
// in the very unlikely scenario
const ucstring message("Sound driver has no support for music.");
CInterfaceManager::getInstance()->displaySystemInfo(message, "SYS");
nlinfo("%s", message.toUtf8().c_str());
return;
}
std::string newPath = CPath::makePathAbsolute(CPath::standardizePath(ClientCfg.MediaPlayerDirectory), CPath::getCurrentPath(), true);
std::string extlist;
join(extensions, ", ", extlist);
extlist += ", m3u, m3u8";
std::string msg(CI18N::get("uiMk_system6").toUtf8());
msg += ": " + newPath + " (" + extlist + ")";
CInterfaceManager::getInstance()->displaySystemInfo(ucstring::makeFromUtf8(msg), "SYS");
nlinfo("%s", msg.c_str());
// Recursive scan for files from media directory
vector<string> filesToProcess;
CPath::getPathContent (newPath, true, false, true, filesToProcess);
uint i;
std::vector<std::string> filenames;
std::vector<std::string> playlists;
for (i = 0; i < filesToProcess.size(); ++i)
{
std::string ext = toLower(CFile::getExtension(filesToProcess[i]));
if (std::find(extensions.begin(), extensions.end(), ext) != extensions.end())
{
filenames.push_back(filesToProcess[i]);
}
else if (ext == "m3u" || ext == "m3u8")
{
playlists.push_back(filesToProcess[i]);
}
}
// Add songs from playlists
for (i = 0; i < playlists.size(); ++i)
{
addFromPlaylist(playlists[i], extensions, filenames);
}
// Sort songs by filename
sort(filenames.begin(), filenames.end());
playSongs(filenames);
}
// ***************************************************************************
class CMusicPlayerPlaySongs: public IActionHandler
{
@ -388,76 +652,7 @@ public:
if (Params == "play_songs")
{
std::vector<std::string> extensions;
SoundMngr->getMixer()->getMusicExtensions(extensions);
// no format supported
if (extensions.empty())
{
// in the very unlikely scenario
const ucstring message("Sound driver has no support for music.");
CInterfaceManager::getInstance()->displaySystemInfo(message, "SYS");
nlinfo("%s", message.toUtf8().c_str());
return;
}
std::string newPath = CPath::makePathAbsolute(CPath::standardizePath(ClientCfg.MediaPlayerDirectory), CPath::getCurrentPath(), true);
std::string extlist;
join(extensions, ", ", extlist);
extlist += ", m3u, m3u8";
std::string msg(CI18N::get("uiMk_system6").toUtf8());
msg += ": " + newPath + " (" + extlist + ")";
CInterfaceManager::getInstance()->displaySystemInfo(ucstring::makeFromUtf8(msg), "SYS");
nlinfo("%s", msg.c_str());
// Recursive scan for files from media directory
vector<string> filesToProcess;
CPath::getPathContent (newPath, true, false, true, filesToProcess);
uint i;
std::vector<std::string> filenames;
std::vector<std::string> playlists;
for (i = 0; i < filesToProcess.size(); ++i)
{
std::string ext = toLower(CFile::getExtension(filesToProcess[i]));
if (std::find(extensions.begin(), extensions.end(), ext) != extensions.end())
{
filenames.push_back(filesToProcess[i]);
}
else if (ext == "m3u" || ext == "m3u8")
{
playlists.push_back(filesToProcess[i]);
}
}
// Sort songs by filename
sort (filenames.begin(), filenames.end());
// Add songs from playlists
for (i = 0; i < playlists.size(); ++i)
{
addFromPlaylist(playlists[i], filenames);
}
// Build the songs array
std::vector<CMusicPlayer::CSongs> songs;
for (i=0; i<filenames.size(); i++)
{
if (!CFile::fileExists(filenames[i])) {
nlwarning("Ignore non-existing file '%s'", filenames[i].c_str());
continue;
}
CMusicPlayer::CSongs song;
song.Filename = filenames[i];
// TODO: cache the result for next refresh
SoundMngr->getMixer()->getSongTitle(filenames[i], song.Title, song.Length);
if (song.Length > 0)
songs.push_back (song);
}
MusicPlayer.playSongs(songs);
MusicPlayer.createPlaylistFromMusic();
}
else if (Params == "update_playlist")
{

@ -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<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 pause ();
void stop ();
@ -68,12 +72,26 @@ 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);
// scan music folder and rebuild playlist
void createPlaylistFromMusic();
private:
// The playlist
CSongs _CurrentSong;
uint _CurrentSongIndex; // If (!_Songs.empty()) must always be <_Songs.size()
std::vector<CSongs> _Songs;
// updated info from worker thread
std::vector<CSongs> _SongUpdateQueue;
// 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
if (Driver->getPolygonMode() != UDriver::Filled)
if (Driver->getPolygonMode() != UDriver::Filled || !Filter3D[FilterSky])
{
if (!Driver->isLost())
{
Driver->clearBuffers (CRGBA(127, 127, 127));
Driver->clearBuffers (ClientCfg.BGColor);
}
}
}

Loading…
Cancel
Save