Refactor metadata and internal data structures
Replace Metadata.h with Containers.h, introducing a HashTable implementation with SIMD acceleration and specialized track tracking. Cleanup PlayerEngine logic by merging it into the playback flow and refactoring several internal API signatures.
This commit is contained in:
@@ -0,0 +1,88 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <taglib/tag.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct Metadata {
|
||||||
|
TagLib::String artist;
|
||||||
|
TagLib::String title;
|
||||||
|
TagLib::String album;
|
||||||
|
TagLib::String albumArtist;
|
||||||
|
unsigned int year;
|
||||||
|
TagLib::VariantList artwork;
|
||||||
|
TagLib::String genre;
|
||||||
|
unsigned int trackNr;
|
||||||
|
double length;
|
||||||
|
std::string format;
|
||||||
|
unsigned int sampleRate;
|
||||||
|
unsigned int bitRate;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Track {
|
||||||
|
TagLib::String artist;
|
||||||
|
TagLib::String title;
|
||||||
|
TagLib::String album;
|
||||||
|
unsigned int trackNr;
|
||||||
|
unsigned int length;
|
||||||
|
std::string filePath;
|
||||||
|
std::string_view artwork;
|
||||||
|
std::string_view genre;
|
||||||
|
std::string format;
|
||||||
|
};
|
||||||
|
|
||||||
|
using trackPtr = std::shared_ptr<Track>;
|
||||||
|
|
||||||
|
class HashTable {
|
||||||
|
public:
|
||||||
|
static constexpr uint8_t EMPTY = 0x80;
|
||||||
|
static constexpr uint8_t DELETED = 0xFE;
|
||||||
|
|
||||||
|
explicit HashTable ( size_t capacity = 64 );
|
||||||
|
|
||||||
|
// delete disabled - requires rehash
|
||||||
|
HashTable ( const HashTable & ) = delete;
|
||||||
|
HashTable &operator= ( const HashTable & ) = delete;
|
||||||
|
|
||||||
|
HashTable ( HashTable && ) noexcept = default;
|
||||||
|
HashTable &operator= ( HashTable && ) noexcept = default;
|
||||||
|
|
||||||
|
void ht_insert ( const std::string &key, trackPtr track );
|
||||||
|
trackPtr ht_lookup ( const std::string &key ) const;
|
||||||
|
bool ht_delete ( const std::string &key );
|
||||||
|
|
||||||
|
std::vector<std::string> listAll () const;
|
||||||
|
|
||||||
|
void save ( const std::string &filename ) const;
|
||||||
|
std::vector<std::unique_ptr<Track>> load ( const std::string &filename );
|
||||||
|
|
||||||
|
std::size_t size () const noexcept { return size_; }
|
||||||
|
std::size_t capacity () const noexcept { return ctrl_.size(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr double MAX_LOAD = 0.75;
|
||||||
|
static constexpr std::size_t GROUP_SIZE = 32;
|
||||||
|
|
||||||
|
struct Entry {
|
||||||
|
std::string key;
|
||||||
|
uint64_t hash = 0;
|
||||||
|
trackPtr track;
|
||||||
|
};
|
||||||
|
|
||||||
|
// helpers
|
||||||
|
static uint64_t hashString ( const std::string &key ) noexcept;
|
||||||
|
static uint8_t fingerprint ( uint64_t hash ) noexcept;
|
||||||
|
std::ptrdiff_t probe ( const std::string &key, uint64_t hash, bool insert ) const noexcept;
|
||||||
|
void rehash ( std::size_t newCapacity );
|
||||||
|
|
||||||
|
std::vector<uint8_t> ctrl_;
|
||||||
|
std::vector<Entry> entries_;
|
||||||
|
std::size_t size_ = 0;
|
||||||
|
double loadFactor_ = static_cast<double> ( size_ ) / static_cast<double> ( ctrl_.size());
|
||||||
|
|
||||||
|
|
||||||
|
}; // Class HashTable
|
||||||
+4
-3
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Metadata.h"
|
#include "Containers.h"
|
||||||
|
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
|
|
||||||
@@ -10,8 +10,9 @@ class Database {
|
|||||||
public:
|
public:
|
||||||
Database( const char* filename, sqlite3** db );
|
Database( const char* filename, sqlite3** db );
|
||||||
~Database();
|
~Database();
|
||||||
void fetch( const std::string& query );
|
void fetch( const std::string& query ) const;
|
||||||
void fetchAlbum( const std::string& album );
|
void fetchAlbum( const std::string& album ) const;
|
||||||
|
void fetchAll() const;
|
||||||
void addSong( );
|
void addSong( );
|
||||||
private:
|
private:
|
||||||
sqlite3* db_;
|
sqlite3* db_;
|
||||||
|
|||||||
+7
-7
@@ -1,22 +1,22 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Containers.h"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#define MUSIC_FOLDER = {"data/"};
|
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
class File {
|
class File {
|
||||||
public:
|
public:
|
||||||
void importFile( std::string fileName );
|
void importFile( std::string fileName );
|
||||||
void importFolder( std::string folderPath );
|
void importFolder( const std::string& folderPath, std::vector<Track>& tracks );
|
||||||
std::vector<std::string> filenames_;
|
void readFileTag( const char* fileName );
|
||||||
|
std::string parseFiletype( const std::string& path );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string path_;
|
std::string path_;
|
||||||
fs::path musicFolder_ MUSIC_FOLDER;
|
fs::path musicFolder_ = "data/";
|
||||||
std::string fileName_;
|
std::string fileName_;
|
||||||
fs::path fp_ = musicFolder_ / fileName_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string_view parseFiletype( std::string_view path );
|
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <taglib/tag.h>
|
|
||||||
|
|
||||||
struct Metadata {
|
|
||||||
TagLib::String artist;
|
|
||||||
TagLib::String title;
|
|
||||||
TagLib::String album;
|
|
||||||
TagLib::String albumArtist;
|
|
||||||
unsigned int year;
|
|
||||||
TagLib::VariantList artwork;
|
|
||||||
TagLib::String genre;
|
|
||||||
unsigned int trackNr;
|
|
||||||
double length;
|
|
||||||
std::string format;
|
|
||||||
unsigned int sampleRate;
|
|
||||||
unsigned int bitRate;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Track {
|
|
||||||
std::string_view artist;
|
|
||||||
std::string_view title;
|
|
||||||
std::string_view album;
|
|
||||||
unsigned int trackNr;
|
|
||||||
unsigned int length;
|
|
||||||
const char *filePath;
|
|
||||||
};
|
|
||||||
@@ -28,7 +28,7 @@ class PlaybackEngine {
|
|||||||
void seek ( ma_uint64 frame );
|
void seek ( ma_uint64 frame );
|
||||||
int isFinished () const;
|
int isFinished () const;
|
||||||
|
|
||||||
ma_uint64 getPosition ();
|
uint64_t getPosition ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ma_device device_;
|
ma_device device_;
|
||||||
|
|||||||
+14
-8
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../include/Metadata.h"
|
#include "../include/Containers.h"
|
||||||
#include "../include/PlaybackEngine.h"
|
#include "../include/PlaybackEngine.h"
|
||||||
#include "../include/miniaudio.h"
|
#include "../include/miniaudio.h"
|
||||||
|
|
||||||
@@ -8,7 +8,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
using Queue = std::vector<Track>;
|
using trackVec = std::vector<Track>;
|
||||||
|
using ht = HashTable;
|
||||||
|
|
||||||
class Player {
|
class Player {
|
||||||
public:
|
public:
|
||||||
@@ -33,16 +34,16 @@ class Player {
|
|||||||
virtual std::string &getAlbum () const;
|
virtual std::string &getAlbum () const;
|
||||||
virtual unsigned int getTrackNr () const;
|
virtual unsigned int getTrackNr () const;
|
||||||
|
|
||||||
// Player Queue
|
// Player trackVec
|
||||||
Queue getQueue () const;
|
trackVec getQueue () const;
|
||||||
Queue clearQueue ();
|
trackVec clearQueue ();
|
||||||
void addToQueue ( Track &track );
|
void addToQueue ( Track &track );
|
||||||
Queue rempveFromQueue ( Track &track );
|
trackVec rempveFromQueue ( Track &track );
|
||||||
Queue shuffleQueue ();
|
trackVec shuffleQueue ();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
PlaybackEngine PE_;
|
PlaybackEngine PE_;
|
||||||
Queue queue_;
|
trackVec queue_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string artist_;
|
std::string artist_;
|
||||||
@@ -53,4 +54,9 @@ class Player {
|
|||||||
|
|
||||||
unsigned int samplerate_ = 48000;
|
unsigned int samplerate_ = 48000;
|
||||||
unsigned int channels_ = 2;
|
unsigned int channels_ = 2;
|
||||||
|
|
||||||
|
trackVec tracks;
|
||||||
|
ht songs;
|
||||||
|
ht artists;
|
||||||
|
ht albums;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "miniaudio.h"
|
|
||||||
|
|
||||||
class PlayerEngine {
|
|
||||||
private:
|
|
||||||
ma_engine engine_;
|
|
||||||
ma_engine_config engineConfig_;
|
|
||||||
ma_device device_;
|
|
||||||
ma_device_config deviceConfig_;
|
|
||||||
unsigned int sampleRate_;
|
|
||||||
unsigned int channels_ = 2;
|
|
||||||
ma_format format_ = ma_format_unknown;
|
|
||||||
ma_context context_;
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
PlayerEngine();
|
|
||||||
PlayerEngine( unsigned int sampleRate, unsigned int channels, ma_format format );
|
|
||||||
~PlayerEngine();
|
|
||||||
ma_device getDevice() const;
|
|
||||||
unsigned int getChannels() const;
|
|
||||||
unsigned int getSampleRate() const;
|
|
||||||
ma_format getFormat() const;
|
|
||||||
|
|
||||||
void setDevice( ma_device device );
|
|
||||||
void setSampleRate( unsigned int sampleRate );
|
|
||||||
void setChannels( unsigned int channels );
|
|
||||||
void setFormat( ma_format format );
|
|
||||||
|
|
||||||
ma_device_data_proc data_callback();
|
|
||||||
void configureDevice( ma_context& context );
|
|
||||||
void initDevice ();
|
|
||||||
void initEngine();
|
|
||||||
|
|
||||||
};
|
|
||||||
+15
-9
@@ -1,20 +1,26 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Metadata.h"
|
#include "containers.h"
|
||||||
|
//#include "file.h"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <taglib/tstring.h>
|
#include <taglib/tstring.h>
|
||||||
#include <taglib/fileref.h>
|
#include <taglib/fileref.h>
|
||||||
#include <taglib/tvariant.h>
|
#include <taglib/tvariant.h>
|
||||||
|
|
||||||
|
|
||||||
class Tag {
|
namespace fs = std::filesystem;
|
||||||
public:
|
|
||||||
void readTag( TagLib::FileName fileName );
|
|
||||||
Metadata getMetadata() const;
|
|
||||||
|
|
||||||
const std::string& getFilePath() const;
|
class tag {
|
||||||
const auto getArtwork() const;
|
public:
|
||||||
|
void readtag( TagLib::filename filename );
|
||||||
|
metadata getmetadata() const;
|
||||||
|
|
||||||
|
const std::string& getfilepath() const;
|
||||||
|
const auto getartwork() const;
|
||||||
private:
|
private:
|
||||||
Metadata metadata_;
|
metadata metadata_;
|
||||||
std::string filePath_;
|
std::string filepath_;
|
||||||
|
fs::path musicdir = music_folder;
|
||||||
};
|
};
|
||||||
|
|||||||
Binary file not shown.
+37
-8
@@ -16,7 +16,7 @@ db::~Database() {
|
|||||||
sqlite3_close( db_ );
|
sqlite3_close( db_ );
|
||||||
}
|
}
|
||||||
|
|
||||||
void db::fetch( const std::string& query ) {
|
void db::fetch( const std::string& query ) const {
|
||||||
sqlite3_stmt* stmt;
|
sqlite3_stmt* stmt;
|
||||||
|
|
||||||
int rc = sqlite3_prepare_v2( db_, query.c_str(), -1, &stmt, nullptr );
|
int rc = sqlite3_prepare_v2( db_, query.c_str(), -1, &stmt, nullptr );
|
||||||
@@ -49,9 +49,11 @@ void db::fetch( const std::string& query ) {
|
|||||||
sqlite3_finalize( stmt );
|
sqlite3_finalize( stmt );
|
||||||
}
|
}
|
||||||
|
|
||||||
void db::fetchAlbum( const std::string& album ) {
|
|
||||||
|
|
||||||
|
void db::fetchAlbum( const std::string& album ) const {
|
||||||
sqlite3_stmt* stmt;
|
sqlite3_stmt* stmt;
|
||||||
const char* sql = "SELECT artist, title, album, trackNr from songs WHERE album = ? ORDER BY trackNr; ";
|
const char* sql = "SELECT artist, title, album, trackNr from music WHERE album = ? ORDER BY trackNr; ";
|
||||||
|
|
||||||
if ( sqlite3_prepare_v2( db_, sql, -1, &stmt, nullptr ) != SQLITE_OK ) {
|
if ( sqlite3_prepare_v2( db_, sql, -1, &stmt, nullptr ) != SQLITE_OK ) {
|
||||||
throw std::runtime_error( sqlite3_errmsg( db_ ) );
|
throw std::runtime_error( sqlite3_errmsg( db_ ) );
|
||||||
@@ -62,12 +64,39 @@ void db::fetchAlbum( const std::string& album ) {
|
|||||||
while ( sqlite3_step( stmt ) == SQLITE_ROW ) {
|
while ( sqlite3_step( stmt ) == SQLITE_ROW ) {
|
||||||
Track t;
|
Track t;
|
||||||
Player p;
|
Player p;
|
||||||
t.artist = reinterpret_cast<const char*> ( sqlite3_column_text( stmt, 0 ));
|
t.artist = sqlite3_column_text( stmt, 0 ) ? reinterpret_cast<const char*> ( sqlite3_column_text( stmt, 0 )) : "";
|
||||||
t.title = reinterpret_cast<const char*> ( sqlite3_column_text( stmt, 1 ));
|
t.title = sqlite3_column_text( stmt, 1 ) ? reinterpret_cast<const char*> ( sqlite3_column_text( stmt, 1 )) : "";
|
||||||
t.album = reinterpret_cast<const char*> ( sqlite3_column_text( stmt, 1 ));
|
t.album = sqlite3_column_text( stmt, 1 ) ? reinterpret_cast<const char*> ( sqlite3_column_text( stmt, 1 )) : "";
|
||||||
t.trackNr = sqlite3_column_int( stmt, 3 );
|
t.trackNr = sqlite3_column_int( stmt, 3 ) ? sqlite3_column_int( stmt, 3 ) : 0;
|
||||||
|
|
||||||
p.addToQueue( t );
|
p.addToQueue( t );
|
||||||
}
|
}
|
||||||
|
sqlite3_finalize( stmt );
|
||||||
|
}
|
||||||
|
|
||||||
|
void db::fetchAll() const {
|
||||||
|
sqlite3_stmt* stmt;
|
||||||
|
const char* sql = "SELECT * FROM music";
|
||||||
|
|
||||||
|
int rc = sqlite3_prepare_v2( db_, sql, -1, &stmt, nullptr );
|
||||||
|
|
||||||
|
if ( rc != SQLITE_OK ) {
|
||||||
|
throw std::runtime_error( sqlite3_errmsg( db_ ));
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( (rc = sqlite3_step( stmt )) == SQLITE_ROW ) {
|
||||||
|
Track t;
|
||||||
|
Player p;
|
||||||
|
|
||||||
|
t.artist = sqlite3_column_text( stmt, 0 ) ? reinterpret_cast<const char*> ( sqlite3_column_text( stmt, 0 )) : "";
|
||||||
|
t.title = sqlite3_column_text( stmt, 1 ) ? reinterpret_cast<const char*> ( sqlite3_column_text( stmt, 1 )) : "";
|
||||||
|
t.album = sqlite3_column_text( stmt, 1 ) ? reinterpret_cast<const char*> ( sqlite3_column_text( stmt, 1 )) : "";
|
||||||
|
t.trackNr = sqlite3_column_int( stmt, 3 ) ? sqlite3_column_int( stmt, 3 ) : 0;
|
||||||
|
t.filePath = sqlite3_column_text( stmt, 4 ) ? reinterpret_cast<const char*> ( sqlite3_column_text( stmt, 4 )) : "";
|
||||||
|
t.length = sqlite3_column_int( stmt, 5 ) ? sqlite3_column_int ( stmt, 5 ) : 0;
|
||||||
|
t.artwork = sqlite3_column_text( stmt, 6 ) ? reinterpret_cast<const char*> ( sqlite3_column_text( stmt, 6 )) : "";
|
||||||
|
t.genre = sqlite3_column_text( stmt, 7 ) ? reinterpret_cast<const char*> ( sqlite3_column_text( stmt, 7 )) : "";
|
||||||
|
t.format = sqlite3_column_text( stmt, 8 ) ? reinterpret_cast<const char*> ( sqlite3_column_text( stmt, 8 )) : "";
|
||||||
|
}
|
||||||
|
sqlite3_finalize( stmt );
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-7
@@ -3,24 +3,32 @@
|
|||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <taglib/fileref.h>
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
void File::importFolder( std::string folderPath ) {
|
void File::importFolder(const std::string& folderPath, std::vector<Track>& tracks ) {
|
||||||
|
|
||||||
for ( const auto& audioFile : fs::directory_iterator{ folderPath }) {
|
for ( const auto& audioFile : fs::directory_iterator{ folderPath }) {
|
||||||
filenames_.reserve( 20 );
|
Track t;
|
||||||
filenames_.push_back( audioFile.path() );
|
t.filePath = static_cast<std::string>(audioFile.path());
|
||||||
|
tracks.push_back( t );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void File::importFile( std::string fileName ) {
|
void File::readFileTag( const char* fileName, std::vector<Track>& tracks ) {
|
||||||
fileName_ = fileName;
|
TagLib::FileRef f( fileName );
|
||||||
fs::path fp = fp_;
|
Track t;
|
||||||
|
if ( !f.isNull() ) {
|
||||||
|
t.artist = ( f.tag()->artist().isEmpty() ) ? "" : ( f.tag()->artist());
|
||||||
|
t.title = ( f.tag()->title().isEmpty() ) ? "" : ( f.tag()->title());
|
||||||
|
t.album = ( f.tag()->album().isEmpty() ) ? "" : ( f.tag()->album());
|
||||||
|
t.trackNr = ( f.tag()->track() );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string_view parseFiletype( std::string_view path ){
|
std::string File::parseFiletype( const std::string& path ){
|
||||||
|
|
||||||
size_t length = path.size();
|
size_t length = path.size();
|
||||||
size_t dot = path.find_last_of( ".");
|
size_t dot = path.find_last_of( ".");
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
#include "../include/Containers.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#ifndef __AVX2__
|
||||||
|
#include <immintrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using ht = HashTable;
|
||||||
|
using trackPtr = std::shared_ptr<Track>;
|
||||||
|
|
||||||
|
|
||||||
|
ht::HashTable( std::size_t capacity ) :ctrl_ ( capacity, EMPTY ), entries_ ( capacity ), size_( 0 ) {
|
||||||
|
if ( !capacity ) throw std::invalid_argument( "Capacity must be > 0" );
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t ht::hashString( const std::string& key ) noexcept {
|
||||||
|
uint64_t hash = 14695981039346656037ULL;
|
||||||
|
for ( char c : key ) {
|
||||||
|
hash ^= static_cast<uint8_t> ( c );
|
||||||
|
hash *= 1099511628211ULL;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ht::fingerprint ( uint64_t hash ) noexcept {
|
||||||
|
return static_cast<uint8_t> ( hash & 0x7F ); // Use lower 7 bits for fingerprint
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ptrdiff_t ht::probe ( const std::string &key, uint64_t hash, bool insert ) const noexcept {
|
||||||
|
const std::size_t capacity = ctrl_.size();
|
||||||
|
const uint8_t fp = fingerprint ( hash );
|
||||||
|
|
||||||
|
#ifdef __AVX2__
|
||||||
|
std::size_t group = ( hash % capacity ) & ~( GROUP_SIZE - 1 ); // Align to group boundary
|
||||||
|
__m256i fpVec = _mm256_set1_epi8 ( fp );
|
||||||
|
__m256i emptyVec = _mm256_set1_epi8 ( static_cast<char> ( EMPTY ) );
|
||||||
|
|
||||||
|
std::ptrdiff_t firstTombstone = -1;
|
||||||
|
|
||||||
|
for ( ;; ) {
|
||||||
|
__m256i ctrlVec = _mm256_loadu_si256 ( reinterpret_cast<const __m256i *> ( &ctrl_[ group ] ) );
|
||||||
|
uint32_t matchMask = _mm256_movemask_epi8 ( _mm256_cmpeq_epi8 ( ctrlVec, fpVec ) );
|
||||||
|
uint32_t emptyMask = _mm256_movemask_epi8 ( _mm256_cmpeq_epi8 ( ctrlVec, emptyVec ) );
|
||||||
|
|
||||||
|
// check candidates
|
||||||
|
for ( uint32_t mask = matchMask; mask != 0; mask &= ( mask - 1 ) ) {
|
||||||
|
std::ptrdiff_t idx = ( group + static_cast<std::size_t> ( __builtin_ctz ( mask ) ) ) % capacity;
|
||||||
|
|
||||||
|
if ( entries_[ idx ].hash == hash && entries_[ idx ].key == key ) {
|
||||||
|
return static_cast<std::ptrdiff_t> ( idx ); // Found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// record first tombstone for this group
|
||||||
|
if ( insert && firstTombstone == -1 ) {
|
||||||
|
for ( std::size_t i = 0; i < GROUP_SIZE; ++i ) {
|
||||||
|
std::size_t idx = ( group + i ) % capacity;
|
||||||
|
if ( ctrl_[ idx ] == DELETED ) {
|
||||||
|
firstTombstone = static_cast<std::ptrdiff_t> ( idx );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( emptyMask ) {
|
||||||
|
if ( !insert )
|
||||||
|
return -1; // Not found
|
||||||
|
std::size_t slot = ( group + static_cast<std::size_t> ( __builtin_ctz ( emptyMask ) ) ) % capacity;
|
||||||
|
return firstTombstone != -1 ? firstTombstone : static_cast<std::ptrdiff_t> ( slot ); // Insert here
|
||||||
|
}
|
||||||
|
group = ( group + GROUP_SIZE ) % capacity; // Move to next group
|
||||||
|
}
|
||||||
|
#else // Fallback to scalar probing
|
||||||
|
std::size_t group = ( hash % capacity ) & ~( GROUP_SIZE - 1 ); // Align to group boundary
|
||||||
|
std::ptrdiff_t firstTombstone = -1;
|
||||||
|
|
||||||
|
for ( ;; ) {
|
||||||
|
bool foundEmpty = false;
|
||||||
|
std::size_t emptyIdx = 0;
|
||||||
|
|
||||||
|
for ( std::size_t i = 0; i < GROUP_SIZE; ++i ) {
|
||||||
|
std::size_t idx = ( group + i ) % capacity;
|
||||||
|
uint8_t c = ctrl_.at( idx );
|
||||||
|
|
||||||
|
if ( c == EMPTY ) {
|
||||||
|
if ( !foundEmpty ) {
|
||||||
|
foundEmpty = true;
|
||||||
|
emptyIdx = idx;
|
||||||
|
}
|
||||||
|
} else if ( c == DELETED ) {
|
||||||
|
if ( insert && firstTombstone == -1 ) {
|
||||||
|
firstTombstone = static_cast<std::ptrdiff_t> ( idx );
|
||||||
|
}
|
||||||
|
} else if ( c == fp && entries_.at( idx ).hash == hash && entries_.at( idx ).key == key ) {
|
||||||
|
return static_cast<std::ptrdiff_t> ( idx ); // Found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( foundEmpty ) {
|
||||||
|
if ( !insert )
|
||||||
|
return -1; // Not found
|
||||||
|
return firstTombstone != -1 ? firstTombstone : static_cast<std::ptrdiff_t> ( emptyIdx ); // Insert here
|
||||||
|
}
|
||||||
|
group = ( group + GROUP_SIZE ) % capacity; // Move to next group
|
||||||
|
} // end probeloop
|
||||||
|
#endif
|
||||||
|
} // probe
|
||||||
|
|
||||||
|
void ht::ht_insert( const std::string& key, trackPtr track ) {
|
||||||
|
//double loadFactor = static_cast<double> ( size_ ) / static_cast<double> ( ctrl_.size());
|
||||||
|
|
||||||
|
if ( loadFactor_ > MAX_LOAD ) rehash( ctrl_.size() * 2 );
|
||||||
|
|
||||||
|
uint64_t hash = hashString ( key );
|
||||||
|
auto idx = static_cast<std::size_t> ( probe ( key, hash, true ) );
|
||||||
|
|
||||||
|
if ( ctrl_.at( idx ) == EMPTY || ctrl_.at( idx ) == DELETED ) {
|
||||||
|
Entry tmp;
|
||||||
|
tmp.key = key;
|
||||||
|
tmp.hash = hash;
|
||||||
|
tmp.track = track;
|
||||||
|
entries_.at( idx ) = std::move( tmp );
|
||||||
|
++size_;
|
||||||
|
} else {
|
||||||
|
entries_.at( idx ).track = track;
|
||||||
|
}
|
||||||
|
ctrl_.at( idx ) = fingerprint( hash );
|
||||||
|
}
|
||||||
|
|
||||||
|
trackPtr ht::ht_lookup( const std::string& key ) const {
|
||||||
|
uint64_t hash = hashString( key );
|
||||||
|
auto idx = probe( key, hash, false );
|
||||||
|
if ( idx != -1 ) return entries_.at( static_cast<std::size_t> ( idx )).track;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ht::ht_delete( const std::string& key ) {
|
||||||
|
uint64_t hash = hashString( key );
|
||||||
|
auto raw = probe( key, hash, false );
|
||||||
|
|
||||||
|
if ( raw == -1 ) return false;
|
||||||
|
|
||||||
|
auto idx = static_cast<size_t>( raw );
|
||||||
|
ctrl_.at( idx ) = DELETED;
|
||||||
|
entries_.at( idx ) = Entry();
|
||||||
|
--size_;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ht::rehash( std::size_t newCapacity ) {
|
||||||
|
ht newTable( newCapacity );
|
||||||
|
for ( std::size_t i = 0; i < ctrl_.size(); ++i ) {
|
||||||
|
if ( ctrl_.at( i ) != EMPTY && ctrl_.at( i ) != DELETED ) {
|
||||||
|
const Entry& entry = entries_.at( i );
|
||||||
|
newTable.ht_insert( entry.key, entry.track );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctrl_ = std::move( newTable.ctrl_ );
|
||||||
|
entries_ = std::move( newTable.entries_ );
|
||||||
|
size_ = newTable.size_;
|
||||||
|
}
|
||||||
@@ -69,7 +69,7 @@ void PE::seek ( ma_uint64 frame ) {
|
|||||||
ma_decoder_seek_to_pcm_frame ( &state.decoder, frame );
|
ma_decoder_seek_to_pcm_frame ( &state.decoder, frame );
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PE::getPosition () {
|
uint64_t PE::getPosition () {
|
||||||
ma_decoder_get_cursor_in_pcm_frames ( &state.decoder, &cursor_ );
|
ma_decoder_get_cursor_in_pcm_frames ( &state.decoder, &cursor_ );
|
||||||
return cursor_;
|
return cursor_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
#include "../include/PlayerEngine.h"
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
using pe = PlayerEngine;
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
pe::PlayerEngine(){
|
|
||||||
configureDevice(context_ );
|
|
||||||
initDevice();
|
|
||||||
ma_device_start( &device_ );
|
|
||||||
}
|
|
||||||
pe::PlayerEngine( unsigned int sampleRate, unsigned int channels, ma_format format ) : sampleRate_{ sampleRate }, channels_{ channels }, format_( format ) {
|
|
||||||
configureDevice(context_ );
|
|
||||||
initDevice();
|
|
||||||
ma_device_start( &device_ );
|
|
||||||
}
|
|
||||||
|
|
||||||
pe::~PlayerEngine(){
|
|
||||||
ma_device_uninit( &device_ );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getters
|
|
||||||
ma_device pe::getDevice() const { return device_; }
|
|
||||||
ma_format pe::getFormat() const { return format_; }
|
|
||||||
unsigned int pe::getSampleRate() const { return sampleRate_; }
|
|
||||||
unsigned int pe::getChannels() const { return channels_; }
|
|
||||||
|
|
||||||
// Setters
|
|
||||||
void pe::setDevice( ma_device device ) { device_ = device; }
|
|
||||||
void pe::setSampleRate ( unsigned int sampleRate ) { sampleRate_ = sampleRate; }
|
|
||||||
void pe::setChannels( unsigned int channels ) { channels_ = channels; }
|
|
||||||
void pe::setFormat( ma_format format ) { format_ = format; }
|
|
||||||
|
|
||||||
void pe::configureDevice( ma_context& context ) {
|
|
||||||
ma_device_config conf = deviceConfig_;
|
|
||||||
conf = ma_device_config_init( ma_device_type_playback);
|
|
||||||
conf.playback.format = format_;
|
|
||||||
conf.playback.channels = channels_;
|
|
||||||
conf.sampleRate = sampleRate_;
|
|
||||||
conf.dataCallback = data_callback();
|
|
||||||
}
|
|
||||||
void pe::initDevice() {
|
|
||||||
if( ma_device_init( NULL, &deviceConfig_, &device_ ) != MA_SUCCESS) {
|
|
||||||
throw std::runtime_error{ "Device could not be initialized." };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void initEngine() {
|
|
||||||
ma_result result;
|
|
||||||
ma_engine_config conf;
|
|
||||||
|
|
||||||
conf = ma_engine_config_init();
|
|
||||||
// TODO resource Manager
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user