diff --git a/code/nel/include/nel/gui/interface_group.h b/code/nel/include/nel/gui/interface_group.h index 01f2b9701..5f864e9e9 100644 --- a/code/nel/include/nel/gui/interface_group.h +++ b/code/nel/include/nel/gui/interface_group.h @@ -101,6 +101,9 @@ namespace NLGUI // test is a group is a direct child of this interface group bool isChildGroup(const CInterfaceGroup *group) const; + // test is x,y is inside last draw clip aread + bool isInViewport(sint32 x, sint32 y) const; + virtual bool isWindowUnder (sint32 x, sint32 y); // Virtual for menu that is not square CInterfaceGroup *getGroupUnder (sint32 x, sint32 y); virtual bool getViewsUnder (sint32 x, sint32 y, sint32 clipX, sint32 clipY, sint32 clipW, sint32 clipH, std::vector &vVB); // Return true if x,y under the group @@ -341,8 +344,13 @@ namespace NLGUI void alignElements(); protected: + /// Last clip area cached from draw call + sint32 _LastClipX; + sint32 _LastClipY; + sint32 _LastClipW; + sint32 _LastClipH; - void makeNewClip (sint32 &oldClipX, sint32 &oldClipY, sint32 &oldClipW, sint32 &oldClipH); + void makeNewClip (sint32 &oldClipX, sint32 &oldClipY, sint32 &oldClipW, sint32 &oldClipH, bool drawing = false); void restoreClip (sint32 oldSciX, sint32 oldSciY, sint32 oldSciW, sint32 oldSciH); // Compute clip contribution for current window, and a previous clipping rectangle. This doesn't change the clip window in the driver. diff --git a/code/nel/include/nel/sound/stream_file_source.h b/code/nel/include/nel/sound/stream_file_source.h index 79151f171..b7c48f96a 100644 --- a/code/nel/include/nel/sound/stream_file_source.h +++ b/code/nel/include/nel/sound/stream_file_source.h @@ -98,8 +98,9 @@ private: NLMISC::IThread *m_Thread; IAudioDecoder *m_AudioDecoder; - + bool m_Paused; + bool m_DecodingEnded; }; /* class CStreamFileSource */ diff --git a/code/nel/src/gui/ctrl_text_button.cpp b/code/nel/src/gui/ctrl_text_button.cpp index 81dfcc953..6e1fab26e 100644 --- a/code/nel/src/gui/ctrl_text_button.cpp +++ b/code/nel/src/gui/ctrl_text_button.cpp @@ -899,7 +899,7 @@ namespace NLGUI } if (!(_SizeRef & 2)) { - _H= _BmpH; + _H= max(_BmpH, _ViewText->getH()); } CViewBase::updateCoords(); diff --git a/code/nel/src/gui/interface_group.cpp b/code/nel/src/gui/interface_group.cpp index 789030a4a..3da60c9f2 100644 --- a/code/nel/src/gui/interface_group.cpp +++ b/code/nel/src/gui/interface_group.cpp @@ -77,6 +77,11 @@ namespace NLGUI _LUAEnvTableCreated= false; _DepthForZSort= 0.f; + _LastClipX = 0; + _LastClipY = 0; + _LastClipW = 0; + _LastClipH = 0; + #ifdef AJM_DEBUG_TRACK_INTERFACE_GROUPS CInterfaceManager::getInstance()->DebugTrackGroupsCreated( this ); #endif @@ -1254,7 +1259,8 @@ namespace NLGUI { const NLGUI::CEventDescriptorMouse &eventDesc = (const NLGUI::CEventDescriptorMouse &)event; - if (!isIn(eventDesc.getX(), eventDesc.getY())) + // group might be partially hidden (scolling) so test against last visible area + if (!isInViewport(eventDesc.getX(), eventDesc.getY())) return false; bool taken = false; @@ -1299,7 +1305,6 @@ namespace NLGUI } if (eventDesc.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mousewheel) { - // handle the Mouse Wheel only if interesting if (_H>_MaxH) { CInterfaceGroup *currParent = _Parent; @@ -1329,7 +1334,7 @@ namespace NLGUI void CInterfaceGroup::draw () { sint32 oldSciX, oldSciY, oldSciW, oldSciH; - makeNewClip (oldSciX, oldSciY, oldSciW, oldSciH); + makeNewClip (oldSciX, oldSciY, oldSciW, oldSciH, true); // Display sons only if not total clipped CViewRenderer &rVR = *CViewRenderer::getInstance(); @@ -1718,6 +1723,16 @@ namespace NLGUI (y <= (_YReal + _HReal))); } + // ------------------------------------------------------------------------------------------------ + bool CInterfaceGroup::isInViewport(sint32 x, sint32 y) const + { + return ( + (x > _LastClipX) && + (x < (_LastClipX + _LastClipW))&& + (y > _LastClipY) && + (y < (_LastClipY + _LastClipH))); + } + // ------------------------------------------------------------------------------------------------ CInterfaceGroup* CInterfaceGroup::getGroupUnder (sint32 x, sint32 y) { @@ -1976,11 +1991,10 @@ namespace NLGUI newSciYDest = newSciY; newSciWDest = newSciW/* - _MarginLeft*/; newSciHDest = newSciH; - } // ------------------------------------------------------------------------------------------------ - void CInterfaceGroup::makeNewClip (sint32 &oldSciX, sint32 &oldSciY, sint32 &oldSciW, sint32 &oldSciH) + void CInterfaceGroup::makeNewClip (sint32 &oldSciX, sint32 &oldSciY, sint32 &oldSciW, sint32 &oldSciH, bool drawing) { CViewRenderer &rVR = *CViewRenderer::getInstance(); rVR.getClipWindow (oldSciX, oldSciY, oldSciW, oldSciH); @@ -1988,6 +2002,14 @@ namespace NLGUI sint32 newSciX, newSciY, newSciW, newSciH; computeCurrentClipContribution(oldSciX, oldSciY, oldSciW, oldSciH, newSciX, newSciY, newSciW, newSciH); rVR.setClipWindow (newSciX, newSciY, newSciW, newSciH); + + if (drawing) + { + _LastClipX = newSciX; + _LastClipY = newSciY; + _LastClipW = newSciW; + _LastClipH = newSciH; + } } // ------------------------------------------------------------------------------------------------ diff --git a/code/nel/src/sound/stream_file_source.cpp b/code/nel/src/sound/stream_file_source.cpp index 185b7514f..9a03ba2d8 100644 --- a/code/nel/src/sound/stream_file_source.cpp +++ b/code/nel/src/sound/stream_file_source.cpp @@ -45,7 +45,7 @@ using namespace std; namespace NLSOUND { CStreamFileSource::CStreamFileSource(CStreamFileSound *streamFileSound, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster, CGroupController *groupController) -: CStreamSource(streamFileSound, spawn, cb, cbUserParam, cluster, groupController), m_AudioDecoder(NULL), m_Paused(false) +: CStreamSource(streamFileSound, spawn, cb, cbUserParam, cluster, groupController), m_AudioDecoder(NULL), m_Paused(false), m_DecodingEnded(false) { m_Thread = NLMISC::IThread::create(this); } @@ -244,7 +244,7 @@ void CStreamFileSource::resume() bool CStreamFileSource::isEnded() { - return (!m_Thread->isRunning() && !_Playing && !m_WaitingForPlay && !m_Paused); + return m_DecodingEnded || (!m_Thread->isRunning() && !_Playing && !m_WaitingForPlay && !m_Paused); } float CStreamFileSource::getLength() @@ -319,6 +319,7 @@ void CStreamFileSource::run() this->getRecommendedBufferSize(samples, bytes); uint32 recSleep = 40; uint32 doSleep = 10; + m_DecodingEnded = false; while (_Playing || m_WaitingForPlay) { if (!m_AudioDecoder->isMusicEnded()) @@ -369,6 +370,9 @@ void CStreamFileSource::run() { delete m_AudioDecoder; m_AudioDecoder = NULL; + // _Playing cannot be used to detect play state because its required in cleanup + // Using m_AudioDecoder in isEnded() may result race condition (decoder is only created after thread is started) + m_DecodingEnded = true; } // drop buffers m_FreeBuffers = 3; diff --git a/code/ryzom/client/src/interface_v3/music_player.cpp b/code/ryzom/client/src/interface_v3/music_player.cpp index b9cadad5c..358189d42 100644 --- a/code/ryzom/client/src/interface_v3/music_player.cpp +++ b/code/ryzom/client/src/interface_v3/music_player.cpp @@ -39,6 +39,9 @@ extern UDriver *Driver; #define TEMPLATE_PLAYLIST_SONG "playlist_song" #define TEMPLATE_PLAYLIST_SONG_TITLE "title" #define TEMPLATE_PLAYLIST_SONG_DURATION "duration" +// ui state +#define MP3_SAVE_SHUFFLE "UI:SAVE:MP3_SHUFFLE" +#define MP3_SAVE_REPEAT "UI:SAVE:MP3_REPEAT" static const std::string MediaPlayerDirectory("music/"); @@ -48,8 +51,20 @@ CMusicPlayer MusicPlayer; CMusicPlayer::CMusicPlayer () { - _CurrentSong = 0; + _CurrentSongIndex = 0; _State = Stopped; + _PlayStart = 0; + _PauseTime = 0; +} + +bool CMusicPlayer::isRepeatEnabled() const +{ + return (NLGUI::CDBManager::getInstance()->getDbProp(MP3_SAVE_REPEAT)->getValue32() == 1); +} + +bool CMusicPlayer::isShuffleEnabled() const +{ + return (NLGUI::CDBManager::getInstance()->getDbProp(MP3_SAVE_SHUFFLE)->getValue32() == 1); } @@ -59,16 +74,61 @@ void CMusicPlayer::playSongs (const std::vector &songs) _Songs = songs; // reset song index if out of bounds - if (_CurrentSong > _Songs.size()) - _CurrentSong = 0; + if (_CurrentSongIndex > _Songs.size()) + _CurrentSongIndex = 0; + + if (isShuffleEnabled()) + shuffleAndRebuildPlaylist(); + else + rebuildPlaylist(); + + // If pause, stop, else play will resume + if (_State == Paused) + _State = Stopped; +} + +// *************************************************************************** +void CMusicPlayer::updatePlaylist(sint prevIndex) +{ + CInterfaceElement *pIE; + std::string rowId; + if (prevIndex >= 0 && prevIndex < _Songs.size()) + { + rowId = toString("%s:s%d:bg", MP3_PLAYER_PLAYLIST_LIST, prevIndex); + pIE = dynamic_cast(CWidgetManager::getInstance()->getElementFromId(rowId)); + if (pIE) pIE->setActive(false); + } + + rowId = toString("%s:s%d:bg", MP3_PLAYER_PLAYLIST_LIST, _CurrentSongIndex); + pIE = dynamic_cast(CWidgetManager::getInstance()->getElementFromId(rowId)); + if (pIE) pIE->setActive(true); +} + +// *************************************************************************** +void CMusicPlayer::shuffleAndRebuildPlaylist() +{ + std::random_shuffle(_Songs.begin(), _Songs.end()); + rebuildPlaylist(); +} + +// *************************************************************************** +void CMusicPlayer::rebuildPlaylist() +{ CGroupList *pList = dynamic_cast(CWidgetManager::getInstance()->getElementFromId(MP3_PLAYER_PLAYLIST_LIST)); if (pList) { pList->clearGroups(); pList->setDynamicDisplaySize(true); + bool found = _CurrentSong.Filename.empty(); for (uint i=0; i < _Songs.size(); ++i) { + if (!found && _CurrentSong.Filename == _Songs[i].Filename) + { + found = true; + _CurrentSongIndex = i; + } + uint min = (sint32)(_Songs[i].Length / 60) % 60; uint sec = (sint32)(_Songs[i].Length) % 60; uint hour = _Songs[i].Length / 3600; @@ -103,9 +163,7 @@ void CMusicPlayer::playSongs (const std::vector &songs) pList->invalidateCoords(); } - // If pause, stop, else play will resume - if (_State == Paused) - _State = Stopped; + updatePlaylist(); } @@ -116,31 +174,40 @@ void CMusicPlayer::play (sint index) if(!SoundMngr) return; + sint prevSongIndex = _CurrentSongIndex; + if (index >= 0 && index < (sint)_Songs.size()) { if (_State == Paused) + { stop(); + } - _CurrentSong = index; + _CurrentSongIndex = index; + _PauseTime = 0; } if (!_Songs.empty()) { - nlassert (_CurrentSong<_Songs.size()); + nlassert (_CurrentSongIndex<_Songs.size()); /* If the player is paused, resume, else, play the current song */ if (_State == Paused) + { SoundMngr->resumeMusic(); + } else - SoundMngr->playMusic(_Songs[_CurrentSong].Filename, 0, true, false, false); + { + SoundMngr->playMusic(_Songs[_CurrentSongIndex].Filename, 0, true, false, false); + _PauseTime = 0; + } _State = Playing; + _PlayStart = CTime::getLocalTime() - _PauseTime; - /* Show the song title */ - CInterfaceManager *pIM = CInterfaceManager::getInstance(); - CViewText *pVT = dynamic_cast(CWidgetManager::getInstance()->getElementFromId("ui:interface:mp3_player:screen:text")); - if (pVT) - pVT->setText (ucstring::makeFromUtf8(_Songs[_CurrentSong].Title)); + _CurrentSong = _Songs[_CurrentSongIndex]; + + updatePlaylist(prevSongIndex); } } @@ -155,6 +222,9 @@ void CMusicPlayer::pause () { SoundMngr->pauseMusic(); _State = Paused; + + if (_PlayStart > 0) + _PauseTime = CTime::getLocalTime() - _PlayStart; } } @@ -167,6 +237,8 @@ void CMusicPlayer::stop () // stop the music only if we are really playing (else risk to stop a background music!) SoundMngr->stopMusic(0); _State = Stopped; + _PlayStart = 0; + _PauseTime = 0; } // *************************************************************************** @@ -176,12 +248,13 @@ void CMusicPlayer::previous () if (!_Songs.empty()) { // Point the previous song - if (_CurrentSong == 0) - _CurrentSong = (uint)_Songs.size()-1; + sint index; + if (_CurrentSongIndex == 0) + index = (uint)_Songs.size()-1; else - _CurrentSong--; + index = _CurrentSongIndex-1; - play (); + play(index); } } @@ -191,9 +264,11 @@ void CMusicPlayer::next () { if (!_Songs.empty()) { - _CurrentSong++; - _CurrentSong%=_Songs.size(); - play (); + sint index = _CurrentSongIndex+1; + if (index == _Songs.size()) + index = 0; + + play(index); } } @@ -205,17 +280,30 @@ void CMusicPlayer::update () return; if (_State == Playing) { - if (SoundMngr->isMusicEnded ()) + CViewText *pVT = dynamic_cast(CWidgetManager::getInstance()->getElementFromId("ui:interface:mp3_player:screen:text")); + if (pVT) { - // Point the next song - _CurrentSong++; - _CurrentSong%=_Songs.size(); + TTime dur = (CTime::getLocalTime() - _PlayStart) / 1000; + std::string title; + title = toString("%02d:%02d %s", dur / 60, dur % 60, _CurrentSong.Title.c_str()); + pVT->setText(ucstring::makeFromUtf8(title)); + } - // End of the playlist ? - if (_CurrentSong != 0) + if (SoundMngr->isMusicEnded ()) + { + // select next song from playlist + sint index = _CurrentSongIndex + 1; + if (isRepeatEnabled() || index < _Songs.size()) { - // No, play the next song - play (); + if (index == _Songs.size()) + { + index = 0; + + if (isShuffleEnabled()) + shuffleAndRebuildPlaylist(); + } + + play(index); } else { @@ -361,11 +449,19 @@ public: CMusicPlayer::CSongs song; song.Filename = filenames[i]; SoundMngr->getMixer()->getSongTitle(filenames[i], song.Title, song.Length); - songs.push_back (song); + if (song.Length > 0) + songs.push_back (song); } MusicPlayer.playSongs(songs); } + else if (Params == "update_playlist") + { + if (MusicPlayer.isShuffleEnabled()) + MusicPlayer.shuffleAndRebuildPlaylist(); + + MusicPlayer.rebuildPlaylist(); + } else if (Params == "previous") MusicPlayer.previous(); else if (Params == "play") diff --git a/code/ryzom/client/src/interface_v3/music_player.h b/code/ryzom/client/src/interface_v3/music_player.h index 33de33c74..7170934d4 100644 --- a/code/ryzom/client/src/interface_v3/music_player.h +++ b/code/ryzom/client/src/interface_v3/music_player.h @@ -55,14 +55,28 @@ public: void update (); + bool isRepeatEnabled() const; + bool isShuffleEnabled() const; + + // Build playlist UI from songs + void rebuildPlaylist(); + // Randomize playlist and rebuild the ui + void shuffleAndRebuildPlaylist(); + // Update playlist active row + void updatePlaylist(sint prevIndex = -1); + private: // The playlist - uint _CurrentSong; // If (!_Songs.empty()) must always be <_Songs.size() + CSongs _CurrentSong; + uint _CurrentSongIndex; // If (!_Songs.empty()) must always be <_Songs.size() std::vector _Songs; // State enum TState { Stopped, Playing, Paused } _State; + + TTime _PlayStart; + TTime _PauseTime; }; extern CMusicPlayer MusicPlayer;