mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 19:52:45 +00:00 
			
		
		
		
	 314b8a374b
			
		
	
	
		314b8a374b
		
	
	
	
	
		
			
			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).
		
			
				
	
	
		
			70 lines
		
	
	
	
		
			1.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			70 lines
		
	
	
	
		
			1.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2021, the SerenityOS developers.
 | |
|  * Copyright (c) 2021, Leandro A. F. Pereira <leandro@tia.mat.br>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #pragma once
 | |
| 
 | |
| #include "M3UParser.h"
 | |
| #include "PlaylistWidget.h"
 | |
| #include <AK/StringHash.h>
 | |
| #include <AK/StringView.h>
 | |
| #include <AK/Vector.h>
 | |
| 
 | |
| class Playlist {
 | |
| public:
 | |
|     Playlist()
 | |
|         : m_model(adopt_ref(*new PlaylistModel()))
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     bool load(StringView);
 | |
| 
 | |
|     RefPtr<PlaylistModel> model() { return m_model; }
 | |
|     int size() { return m_model->items().size(); }
 | |
| 
 | |
|     StringView next();
 | |
|     StringView previous();
 | |
| 
 | |
|     void set_looping(bool looping) { m_looping = looping; }
 | |
|     bool looping() const { return m_looping; }
 | |
| 
 | |
|     void set_shuffling(bool shuffling) { m_shuffling = shuffling; }
 | |
|     bool shuffling() const { return m_shuffling; }
 | |
| 
 | |
| private:
 | |
|     // This naïve bloom filter is used in the shuffling algorithm to
 | |
|     // provide random uniformity, avoiding playing items that were recently
 | |
|     // played.
 | |
|     class BloomFilter {
 | |
|     public:
 | |
|         void reset() { m_bitmap = 0; }
 | |
|         void add(const StringView key) { m_bitmap |= mask_for_key(key); }
 | |
|         bool maybe_contains(const StringView key) const
 | |
|         {
 | |
|             auto mask = mask_for_key(key);
 | |
|             return (m_bitmap & mask) == mask;
 | |
|         }
 | |
| 
 | |
|     private:
 | |
|         u64 mask_for_key(StringView key) const
 | |
|         {
 | |
|             auto key_chars = key.characters_without_null_termination();
 | |
|             auto hash1 = string_hash(key_chars, key.length(), 0xdeadbeef);
 | |
|             auto hash2 = string_hash(key_chars, key.length(), 0xbebacafe);
 | |
|             return 1ULL << (hash1 & 63) | 1ULL << (hash2 & 63);
 | |
|         }
 | |
|         u64 m_bitmap { 0 };
 | |
|     };
 | |
| 
 | |
|     void try_fill_missing_info(Vector<M3UEntry>&, StringView);
 | |
| 
 | |
|     RefPtr<PlaylistModel> m_model;
 | |
|     bool m_looping { false };
 | |
|     bool m_shuffling { false };
 | |
| 
 | |
|     BloomFilter m_previously_played_paths;
 | |
|     int m_next_index_to_play { 0 };
 | |
| };
 |