diff --git a/code/ryzom/client/src/interface_v3/action_handler_phrase.cpp b/code/ryzom/client/src/interface_v3/action_handler_phrase.cpp index 8a94a4c16..0e5ba0c48 100644 --- a/code/ryzom/client/src/interface_v3/action_handler_phrase.cpp +++ b/code/ryzom/client/src/interface_v3/action_handler_phrase.cpp @@ -536,8 +536,10 @@ public: CInterfaceManager *pIM= CInterfaceManager::getInstance(); // Launch the modal to select the faber plan - extern void fillFaberPlanSelection(const std::string &brickDB, uint maxSelection); - fillFaberPlanSelection(CDBGroupBuildPhrase::BrickSelectionDB, CDBGroupBuildPhrase::MaxSelection); + extern void fillFaberPlanSelection(const std::string &brickDB, uint maxSelection, TOOL_TYPE::TCraftingToolType toolType); + // from sphrase_manager.cpp + extern TOOL_TYPE::TCraftingToolType getRightHandCraftToolType(); + fillFaberPlanSelection(CDBGroupBuildPhrase::BrickSelectionDB, CDBGroupBuildPhrase::MaxSelection, getRightHandCraftToolType()); // setup the validation CHandlerPhraseValidateBrick::BuildPhraseGroup= NULL; diff --git a/code/ryzom/client/src/interface_v3/action_phrase_faber.cpp b/code/ryzom/client/src/interface_v3/action_phrase_faber.cpp index 40efedac0..1c99983e3 100644 --- a/code/ryzom/client/src/interface_v3/action_phrase_faber.cpp +++ b/code/ryzom/client/src/interface_v3/action_phrase_faber.cpp @@ -257,7 +257,7 @@ void CActionPhraseFaber::onCloseFaberCastWindow() // *************************************************************************** -void CActionPhraseFaber::fillFaberPlanSelection(const std::string &brickDB, uint maxSelection) +void CActionPhraseFaber::fillFaberPlanSelection(const std::string &brickDB, uint maxSelection, TOOL_TYPE::TCraftingToolType toolType) { CInterfaceManager *pIM= CInterfaceManager::getInstance(); CSBrickManager *pBM= CSBrickManager::getInstance(); @@ -268,7 +268,21 @@ void CActionPhraseFaber::fillFaberPlanSelection(const std::string &brickDB, ui for(i=0;i<_FaberPlanBrickFamilies.size();i++) { const std::vector &famBricks= pBM->getFamilyBricks(_FaberPlanBrickFamilies[i]); - bricks.insert(bricks.end(), famBricks.begin(), famBricks.end()); + if (toolType == TOOL_TYPE::Unknown) + { + bricks.insert(bricks.end(), famBricks.begin(), famBricks.end()); + } + else + { + for(std::vector::const_iterator it = famBricks.begin(); it != famBricks.end(); ++it) + { + CSBrickSheet *brick= pBM->getBrick(*it); + if (brick && brick->FaberPlan.ToolType == toolType) + { + bricks.push_back(*it); + } + } + } } // get only ones known @@ -1257,10 +1271,10 @@ void launchFaberCastWindow(sint32 memoryLine, uint memoryIndex, CSBrickSheet *r } // *************************************************************************** -void fillFaberPlanSelection(const std::string &brickDB, uint maxSelection) +void fillFaberPlanSelection(const std::string &brickDB, uint maxSelection, TOOL_TYPE::TCraftingToolType toolType) { if (ActionPhraseFaber == NULL) ActionPhraseFaber = new CActionPhraseFaber; - ActionPhraseFaber->fillFaberPlanSelection(brickDB, maxSelection); + ActionPhraseFaber->fillFaberPlanSelection(brickDB, maxSelection, toolType); } // *************************************************************************** diff --git a/code/ryzom/client/src/interface_v3/action_phrase_faber.h b/code/ryzom/client/src/interface_v3/action_phrase_faber.h index ba74ef0aa..f406d0ee2 100644 --- a/code/ryzom/client/src/interface_v3/action_phrase_faber.h +++ b/code/ryzom/client/src/interface_v3/action_phrase_faber.h @@ -25,6 +25,7 @@ #include "nel/misc/types_nl.h" #include "inventory_manager.h" +#include "game_share/crafting_tool_type.h" #include "game_share/rm_family.h" #include "game_share/brick_families.h" #include "game_share/item_origin.h" @@ -62,7 +63,7 @@ public: /// Fill the Faber Plan selection DB (no window opened) - void fillFaberPlanSelection(const std::string &brickDB, uint maxSelection); + void fillFaberPlanSelection(const std::string &brickDB, uint maxSelection, TOOL_TYPE::TCraftingToolType toolType = TOOL_TYPE::Unknown); /// Called when the user has selected the Plan. copy bag. the itemPlanBrick must have "FaberPlan" good infos. void validateFaberPlanSelection(CSBrickSheet *itemPlanBrick); @@ -249,7 +250,7 @@ private: // Called when click a Faber phrase extern void launchFaberCastWindow(sint32 memoryLine, uint memoryIndex, CSBrickSheet *rootBrick); // Called when select a Faber plan -extern void fillFaberPlanSelection(const std::string &brickDB, uint maxSelection); +extern void fillFaberPlanSelection(const std::string &brickDB, uint maxSelection, TOOL_TYPE::TCraftingToolType toolType = TOOL_TYPE::Unknown); // Called when the Faber plan is selected extern void validateFaberPlanSelection(CSBrickSheet *itemPlanBrick); // Called when something needs to close the crafting window (does nothing if not open) diff --git a/code/ryzom/client/src/interface_v3/music_player.cpp b/code/ryzom/client/src/interface_v3/music_player.cpp index 717ea7e66..2f02352fb 100644 --- a/code/ryzom/client/src/interface_v3/music_player.cpp +++ b/code/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))); @@ -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(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 &filenames) +static void addFromPlaylist(const std::string &playlist, const std::vector &extensions, std::vector &filenames) { static uint8 utf8Header[] = { 0xefu, 0xbbu, 0xbfu }; @@ -362,15 +559,82 @@ static void addFromPlaylist(const std::string &playlist, std::vector 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 filesToProcess; + CPath::getPathContent (newPath, true, false, true, filesToProcess); + + uint i; + std::vector filenames; + std::vector 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 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 filesToProcess; - CPath::getPathContent (newPath, true, false, true, filesToProcess); - - uint i; - std::vector filenames; - std::vector 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 songs; - for (i=0; igetMixer()->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") { diff --git a/code/ryzom/client/src/interface_v3/music_player.h b/code/ryzom/client/src/interface_v3/music_player.h index 549fa169d..1932591e3 100644 --- a/code/ryzom/client/src/interface_v3/music_player.h +++ b/code/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,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 _Songs; + // updated info from worker thread + std::vector _SongUpdateQueue; // State enum TState { Stopped, Playing, Paused } _State; diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index ab5d07baf..5c4f99446 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -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); } } }