diff --git a/rpcs3/Emu/Cell/Modules/cellSearch.cpp b/rpcs3/Emu/Cell/Modules/cellSearch.cpp index e1284df3b2..3a6922edf9 100644 --- a/rpcs3/Emu/Cell/Modules/cellSearch.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSearch.cpp @@ -1690,19 +1690,8 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr shared_ptr - { - if (searchObject->content_ids.size() == 1) - { - return first_content; - } - - std::vector result; - std::sample(searchObject->content_ids.begin(), searchObject->content_ids.end(), std::back_inserter(result), 1, std::mt19937{std::random_device{}()}); - ensure(result.size() == 1); - shared_ptr content = ensure(result[0].second); - return content; - }; + // Index of the track to start with, if the search contains single tracks + u32 first_track = umax; if (contentId) { @@ -1741,8 +1730,8 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptrsecond->type == CELL_SEARCH_CONTENTTYPE_MUSIC) { - context.playlist.push_back(content->second->infoPath.contentPath); - cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning found track: Type=0x%x, Path='%s'", content_hash, +content->second->type, context.playlist.back()); + first_track = ::narrow(std::distance(searchObject->content_ids.begin(), content)); + cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Found track %d: Type=0x%x, Path='%s'", content_hash, first_track, +content->second->type, content->second->infoPath.contentPath); } else { @@ -1754,47 +1743,40 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr content = get_random_content(); - context.playlist.push_back(content->infoPath.contentPath); - cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning random track: Type=0x%x, Path='%s'", content_hash, +content->type, context.playlist.back()); - } - else - { - // Select the first track by default - // TODO: whole playlist - context.playlist.push_back(first_content->infoPath.contentPath); - cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning first track: Type=0x%x, Path='%s'", content_hash, +first_content->type, context.playlist.back()); - } } else if (first_content->type == CELL_SEARCH_CONTENTTYPE_MUSICLIST) { // Abort if we don't have the necessary info to select a playlist. return { CELL_SEARCH_ERROR_NOT_SUPPORTED_CONTEXT, "Type: CELL_SEARCH_CONTENTTYPE_MUSICLIST" }; } - else if (option == CELL_SEARCH_CONTEXTOPTION_SHUFFLE) + + if (first_content->type != CELL_SEARCH_CONTENTTYPE_MUSICLIST) { - // Select random track - // TODO: whole playlist - shared_ptr content = get_random_content(); - context.playlist.push_back(content->infoPath.contentPath); - cellSearch.notice("cellSearchGetMusicSelectionContext(): Assigning random track: Type=0x%x, Path='%s'", +content->type, context.playlist.back()); - } - else - { - // Select the first track by default - // TODO: whole playlist - context.playlist.push_back(first_content->infoPath.contentPath); - cellSearch.notice("cellSearchGetMusicSelectionContext(): Assigning first track: Type=0x%x, Path='%s'", +first_content->type, context.playlist.back()); + if (first_track == umax) + { + // Start with a random track if shuffled, otherwise with the first track + first_track = 0; + + if (option == CELL_SEARCH_CONTEXTOPTION_SHUFFLE) + { + std::mt19937 engine{std::random_device{}()}; + first_track = ::narrow(std::uniform_int_distribution(0, searchObject->content_ids.size() - 1)(engine)); + } + } + + // The playlist contains all the tracks of the search + for (const content_id_type& cid : searchObject->content_ids) + { + context.playlist.push_back(ensure(cid.second)->infoPath.contentPath); + } + + context.first_track = context.current_track = first_track; + cellSearch.notice("cellSearchGetMusicSelectionContext(): Assigning %d tracks, starting with track %d: Path='%s'", context.playlist.size(), first_track, ::at32(context.playlist, first_track)); } context.content_type = first_content->type; context.repeat_mode = repeatMode; context.context_option = option; - // TODO: context.first_track = ?; // Resolve hashed paths for (std::string& track : context.playlist) @@ -2140,27 +2122,40 @@ error_code music_selection_context::find_content_id(vm::ptr if (!contents_id) return CELL_MUSIC_ERROR_PARAM; - // Search for the content that matches our current selection + // Search for the content that matches our current selection, starting with the selected track auto& content_map = g_fxo->get(); shared_ptr found_content; u64 hash = 0; - for (const std::string& track : playlist) + const usz start = first_track < playlist.size() ? first_track : 0; + u64 last_hash = 0; + + for (usz i = 0; i < playlist.size(); i++) { - if (content_type == CELL_SEARCH_CONTENTTYPE_MUSICLIST) + const std::string_view track = ::at32(playlist, (start + i) % playlist.size()); + + // The tracks of a music list share the hash of their directory + const u64 track_hash = std::hash()(content_type == CELL_SEARCH_CONTENTTYPE_MUSICLIST ? fs::get_parent_dir_view(track) : track); + + if (i > 0 && track_hash == last_hash) { - hash = std::hash()(fs::get_parent_dir(track)); - } - else - { - hash = std::hash()(track); + continue; } - if (auto found = content_map.map.find(hash); found != content_map.map.end()) + last_hash = track_hash; + + if (auto found = content_map.map.find(track_hash); found != content_map.map.end()) { + hash = track_hash; found_content = found->second; break; } + + if (i == 0) + { + // Look for the selected track below if no content is found + hash = track_hash; + } } if (found_content) diff --git a/rpcs3/util/media_utils.cpp b/rpcs3/util/media_utils.cpp index 6eecc1dba2..1ec64940bf 100644 --- a/rpcs3/util/media_utils.cpp +++ b/rpcs3/util/media_utils.cpp @@ -857,7 +857,11 @@ namespace utils media_log.notice("audio_decoder: shuffling initial playlist..."); std::random_device rd; auto engine = std::default_random_engine{rd()}; - std::shuffle(std::begin(m_context.playlist), std::end(m_context.playlist), engine); + + // Keep the selected track as the first one, so that the whole shuffled playlist is played before the next shuffle + std::swap(m_context.playlist.front(), ::at32(m_context.playlist, m_context.current_track)); + std::shuffle(std::begin(m_context.playlist) + 1, std::end(m_context.playlist), engine); + m_context.current_track = 0; } while (thread_ctrl::state() != thread_state::aborting)