mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 18:57:35 +00:00
SoundPlayer: Implement playlist shuffle mode
The shuffling algorithm uses a naïve bloom filter to provide random uniformity, avoiding items that were recently played. With 32 bits, double hashing, and an error rate of ~10%, this bloom filter should be able to hold around ~16 keys, which should be sufficient to give the illusion of fairness to the shuffling algorithm. This avoids having to shuffle the playlist itself (user might have spent quite a bit of time to sort them, so it's not a good idea to mess with it), or having to create a proxy model that shuffles (that could potentially use quite a bit of memory).
This commit is contained in:
parent
0812965f50
commit
314b8a374b
7 changed files with 106 additions and 11 deletions
|
@ -8,6 +8,7 @@
|
|||
#include "Playlist.h"
|
||||
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/Random.h>
|
||||
#include <LibAudio/Loader.h>
|
||||
#include <LibGUI/MessageBox.h>
|
||||
|
||||
|
@ -33,7 +34,7 @@ void Playlist::try_fill_missing_info(Vector<M3UEntry>& entries, StringView path)
|
|||
Vector<M3UEntry*> to_delete;
|
||||
|
||||
for (auto& entry : entries) {
|
||||
if (!LexicalPath(entry.path).is_absolute())
|
||||
if (!LexicalPath { entry.path }.is_absolute())
|
||||
entry.path = String::formatted("{}/{}", playlist_path.dirname(), entry.path);
|
||||
|
||||
if (!entry.extended_info->file_size_in_bytes.has_value()) {
|
||||
|
@ -69,8 +70,32 @@ StringView Playlist::next()
|
|||
return {};
|
||||
m_next_index_to_play = 0;
|
||||
}
|
||||
|
||||
auto next = m_model->items().at(m_next_index_to_play).path;
|
||||
m_next_index_to_play++;
|
||||
if (!shuffling()) {
|
||||
m_next_index_to_play++;
|
||||
return next;
|
||||
}
|
||||
|
||||
// Try a few times getting an item to play that has not been
|
||||
// recently played. But do not try too hard, as we don't want
|
||||
// to wait forever.
|
||||
int shuffle_try;
|
||||
int const max_times_to_try = min(4, size());
|
||||
for (shuffle_try = 0; shuffle_try < max_times_to_try; shuffle_try++) {
|
||||
if (!m_previously_played_paths.maybe_contains(next))
|
||||
break;
|
||||
|
||||
m_next_index_to_play = get_random_uniform(size());
|
||||
next = m_model->items().at(m_next_index_to_play).path;
|
||||
}
|
||||
if (shuffle_try == max_times_to_try) {
|
||||
// If we tried too much, maybe it's time to try resetting
|
||||
// the bloom filter and start over.
|
||||
m_previously_played_paths.reset();
|
||||
}
|
||||
|
||||
m_previously_played_paths.add(next);
|
||||
return next;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue