Part of a larger restructure, added MusicPlayer which is the main controller and a structure for the song queue
This commit is contained in:
47
src/core/SongHistory/SongHistory.hpp
Normal file
47
src/core/SongHistory/SongHistory.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
template<typename T>
|
||||
class SongHistory {
|
||||
private:
|
||||
struct Node {
|
||||
T data;
|
||||
Node* next;
|
||||
};
|
||||
|
||||
Node* topNode = nullptr;
|
||||
|
||||
public:
|
||||
SongHistory( ) { }
|
||||
~SongHistory( ) {
|
||||
while (!isEmpty( ))
|
||||
pop( );
|
||||
}
|
||||
|
||||
void push(const T& value) {
|
||||
Node* newNode = new Node;
|
||||
newNode->data = value;
|
||||
newNode->next = topNode;
|
||||
topNode = newNode;
|
||||
}
|
||||
|
||||
void pop( ) {
|
||||
if (!isEmpty( )) {
|
||||
Node* tmp = topNode;
|
||||
topNode = topNode->next;
|
||||
delete tmp;
|
||||
}
|
||||
else
|
||||
std::cerr << "Stack empty, cannot pop" << std::endl;
|
||||
}
|
||||
|
||||
T top( ) {
|
||||
if (!isEmpty( ))
|
||||
return topNode->data;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool isEmpty( ) const { return topNode == nullptr; }
|
||||
};
|
||||
@@ -0,0 +1,186 @@
|
||||
#include "ConditionalCircularLinkedList.h"
|
||||
|
||||
ConditionalCircularLinkedList::ConditionalCircularLinkedList( ) { }
|
||||
|
||||
void ConditionalCircularLinkedList::Append(Song* song) {
|
||||
Node* newNode = new Node(song);
|
||||
if (head == nullptr) {
|
||||
head = newNode;
|
||||
|
||||
// Only link back when its supposed to
|
||||
if (isLinked)
|
||||
head->next = head;
|
||||
|
||||
current = head;
|
||||
}
|
||||
else {
|
||||
Node* tmp = head;
|
||||
|
||||
while (tmp->next != head)
|
||||
tmp = tmp->next;
|
||||
|
||||
tmp->next = newNode;
|
||||
|
||||
// Again make sure to only link when supposed to
|
||||
if (isLinked)
|
||||
newNode->next = head;
|
||||
}
|
||||
}
|
||||
|
||||
void ConditionalCircularLinkedList::SetListMode(bool isLooping) {
|
||||
if (head == nullptr) {
|
||||
std::cout << "WARN: List is empty, cannot set mode" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
Node* tmp = head;
|
||||
while (tmp->next != head)
|
||||
tmp = tmp->next;
|
||||
|
||||
if (!isLooping)
|
||||
tmp->next = nullptr;
|
||||
else
|
||||
tmp->next = head;
|
||||
}
|
||||
|
||||
void ConditionalCircularLinkedList::Clear( ) {
|
||||
while (head != nullptr) {
|
||||
Node* tmp = head;
|
||||
head = head->next;
|
||||
delete tmp;
|
||||
delete current;
|
||||
}
|
||||
}
|
||||
|
||||
void ConditionalCircularLinkedList::Advance( ) {
|
||||
if (current != nullptr)
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
Song* ConditionalCircularLinkedList::GetNext( ) {
|
||||
if (current != nullptr)
|
||||
return current->data;
|
||||
}
|
||||
|
||||
void ConditionalCircularLinkedList::AddSongBefore(Song* newSong, Song* addSongBefore) {
|
||||
if (head == nullptr) return;
|
||||
|
||||
Node* newNode = new Node(newSong);
|
||||
|
||||
Node* current = head;
|
||||
Node* prev = nullptr;
|
||||
|
||||
do {
|
||||
if (current->data == addSongBefore) {
|
||||
if (prev == nullptr) {
|
||||
newNode->next = head;
|
||||
head = newNode;
|
||||
|
||||
Node* lastNode = head;
|
||||
while (lastNode->next != head)
|
||||
lastNode = lastNode->next;
|
||||
|
||||
if (isLinked)
|
||||
lastNode->next = head;
|
||||
}
|
||||
else {
|
||||
prev->next = newNode;
|
||||
newNode->next = current;
|
||||
}
|
||||
return;
|
||||
}
|
||||
prev = current;
|
||||
current = current->next;
|
||||
|
||||
} while (current != head);
|
||||
}
|
||||
|
||||
void ConditionalCircularLinkedList::AddSongAfter(Song* newSong, Song* addSongAfter) {
|
||||
if (head == nullptr) return;
|
||||
|
||||
Node* newNode = new Node(newSong);
|
||||
|
||||
Node* current = head;
|
||||
|
||||
do {
|
||||
if (current->data == addSongAfter) {
|
||||
newNode->next = current->next;
|
||||
current->next = newNode;
|
||||
return;
|
||||
}
|
||||
current = current->next;
|
||||
} while (current != head);
|
||||
|
||||
}
|
||||
|
||||
Song* ConditionalCircularLinkedList::RemoveSong(Song* removeSong) {
|
||||
if (head == nullptr) return nullptr;
|
||||
|
||||
Node* current = head;
|
||||
Node* prev = nullptr;
|
||||
|
||||
do {
|
||||
if (current->data == removeSong) {
|
||||
if (prev == nullptr) {
|
||||
head = current->next;
|
||||
Node* lastNode = head;
|
||||
while (lastNode->next != current)
|
||||
lastNode = lastNode->next;
|
||||
|
||||
if (isLinked)
|
||||
lastNode->next = head;
|
||||
|
||||
Song* deletedSong = current->data;
|
||||
delete current;
|
||||
return deletedSong;
|
||||
}
|
||||
else {
|
||||
prev->next = current->next;
|
||||
|
||||
Song* deletedSong = current->data;
|
||||
delete current;
|
||||
return deletedSong;
|
||||
}
|
||||
}
|
||||
prev = current;
|
||||
current = current->next;
|
||||
} while (current != head);
|
||||
}
|
||||
|
||||
bool ConditionalCircularLinkedList::IsEmpty( ) {
|
||||
return current == nullptr;
|
||||
}
|
||||
|
||||
void ConditionalCircularLinkedList::Shuffle( ) {
|
||||
int cnt = 0;
|
||||
Node* tmp = head;
|
||||
|
||||
while (tmp != nullptr) {
|
||||
cnt++;
|
||||
tmp = tmp->next;
|
||||
}
|
||||
|
||||
Node** nodes = new Node * [cnt];
|
||||
|
||||
tmp = head;
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
nodes[i] = tmp;
|
||||
tmp = tmp->next;
|
||||
}
|
||||
|
||||
for (int i = cnt; i > 0; i--) {
|
||||
int j = rand( ) % (i + 1);
|
||||
|
||||
Node* tmp = nodes[i];
|
||||
nodes[i] = nodes[j];
|
||||
nodes[j] = tmp;
|
||||
}
|
||||
|
||||
head = nodes[0];
|
||||
for (int i = 0; i < cnt - 1; i++)
|
||||
nodes[i]->next = nodes[i + 1];
|
||||
|
||||
nodes[cnt - 1]->next = nullptr;
|
||||
|
||||
delete[] nodes;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "../../song/song.h"
|
||||
|
||||
class ConditionalCircularLinkedList {
|
||||
private:
|
||||
|
||||
struct Node {
|
||||
Node* next;
|
||||
Song* data;
|
||||
|
||||
Node(Song* song) : data(song), next(nullptr) { }
|
||||
};
|
||||
|
||||
Node* head;
|
||||
|
||||
// Variable to keep the current position that we seek
|
||||
Node* current;
|
||||
|
||||
// Variable to either link or unlink the list
|
||||
bool isLinked;
|
||||
public:
|
||||
ConditionalCircularLinkedList( );
|
||||
ConditionalCircularLinkedList(bool isLinked) : head(nullptr), isLinked(isLinked) { }
|
||||
|
||||
/**
|
||||
* @brief Append a new song to the queue
|
||||
*
|
||||
* @param song
|
||||
*/
|
||||
void Append(Song* song);
|
||||
|
||||
/**
|
||||
* @brief Clear the entire list so its brand spanking new.
|
||||
*
|
||||
*/
|
||||
void Clear( );
|
||||
|
||||
/**
|
||||
* @brief Depending on if the queue is supposed to be looping, this will link
|
||||
* up the end of the list, with its beginning to its a circular list.
|
||||
* If its not supposed to loop then we break the link and it will be a normal list.
|
||||
* @param isLooping Will set the list to circular or normal mode
|
||||
*/
|
||||
void SetListMode(bool isLooping);
|
||||
|
||||
/**
|
||||
* @brief Advance the circular list by one, this means setting the "current" pointer
|
||||
* to the next element
|
||||
*
|
||||
*/
|
||||
void Advance( );
|
||||
|
||||
/**
|
||||
* @brief Get the "current" pointer's data
|
||||
*
|
||||
* @return Song*
|
||||
*/
|
||||
Song* GetNext( );
|
||||
|
||||
void AddSongBefore(Song* newSong, Song* addSongBefore);
|
||||
|
||||
void AddSongAfter(Song* newSong, Song* addSongAfter);
|
||||
|
||||
Song* RemoveSong(Song* removeSong);
|
||||
|
||||
bool IsEmpty( );
|
||||
|
||||
void Shuffle( );
|
||||
};
|
||||
94
src/core/SongQueue/SongQueue.cpp
Normal file
94
src/core/SongQueue/SongQueue.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "SongQueue.h"
|
||||
|
||||
SongQueue::SongQueue( ) {
|
||||
queue = new ConditionalCircularLinkedList( );
|
||||
|
||||
// Its never going to be linked, I'm just too lazy to make a new implementation thats almost the same
|
||||
priorityQueue = new ConditionalCircularLinkedList(false);
|
||||
}
|
||||
|
||||
SongQueue::~SongQueue( ) { }
|
||||
|
||||
void SongQueue::PopulateQueue(const std::list<Song*>& songList) {
|
||||
for (Song* song : songList) {
|
||||
queue->Append(song);
|
||||
}
|
||||
}
|
||||
|
||||
void SongQueue::Next( ) {
|
||||
if (priorityQueue->IsEmpty( )) {
|
||||
topSong = queue->GetNext( );
|
||||
queue->Advance( );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
topSong = priorityQueue->GetNext( );
|
||||
priorityQueue->Advance( );
|
||||
}
|
||||
|
||||
void SongQueue::JumpToSong(Song* song, bool fromPrioQueue) {
|
||||
if (fromPrioQueue) {
|
||||
if (priorityQueue->IsEmpty( ))
|
||||
return;
|
||||
|
||||
while (priorityQueue->GetNext( ) != song) {
|
||||
priorityQueue->Advance( );
|
||||
topSong = song;
|
||||
}
|
||||
}
|
||||
|
||||
if (queue->IsEmpty( ))
|
||||
return;
|
||||
|
||||
while (queue->GetNext( ) != song) {
|
||||
queue->Advance( );
|
||||
topSong = song;
|
||||
}
|
||||
}
|
||||
|
||||
void SongQueue::AddToPriorityQueue(Song* song) {
|
||||
priorityQueue->Append(song);
|
||||
}
|
||||
|
||||
bool SongQueue::IsEmpty( ) {
|
||||
return queue->IsEmpty( ) && priorityQueue->IsEmpty( );
|
||||
}
|
||||
|
||||
Song* SongQueue::Top( ) { return topSong; }
|
||||
|
||||
void SongQueue::SetTop(Song* song) { topSong = song; }
|
||||
|
||||
void SongQueue::RemoveSongFromPriorityQueue(Song* song) {
|
||||
if (priorityQueue->IsEmpty( ))
|
||||
return;
|
||||
|
||||
priorityQueue->RemoveSong(song);
|
||||
}
|
||||
|
||||
void SongQueue::MoveSongInPriorityQueue(Song* songToMove, Song* otherSong, bool beforeElseAfter) {
|
||||
if (priorityQueue->IsEmpty( ))
|
||||
return;
|
||||
|
||||
if (beforeElseAfter)
|
||||
priorityQueue->AddSongBefore(songToMove, otherSong);
|
||||
else
|
||||
priorityQueue->AddSongAfter(songToMove, otherSong);
|
||||
}
|
||||
|
||||
void SongQueue::ShufflePlaylist( ) {
|
||||
//First backup the original list state
|
||||
queue_original = new ConditionalCircularLinkedList(queue);
|
||||
|
||||
queue->Shuffle( );
|
||||
}
|
||||
|
||||
void SongQueue::RestorePlaylist( ) {
|
||||
if (queue_original == nullptr) return;
|
||||
|
||||
queue = new ConditionalCircularLinkedList(queue_original);
|
||||
}
|
||||
|
||||
void SongQueue::LinkQueue(bool link) {
|
||||
queue->SetListMode(link);
|
||||
}
|
||||
61
src/core/SongQueue/SongQueue.h
Normal file
61
src/core/SongQueue/SongQueue.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
|
||||
#include "../song/song.h"
|
||||
#include "ConditionalCircularLinkedList/ConditionalCircularLinkedList.h"
|
||||
|
||||
class SongQueue {
|
||||
private:
|
||||
|
||||
// The currently playing song
|
||||
Song* topSong;
|
||||
|
||||
//Regular song queue, can be unlinked for non looping playlists
|
||||
ConditionalCircularLinkedList* queue;
|
||||
//Copy of queue to faster restore when unshuffling
|
||||
ConditionalCircularLinkedList* queue_original;
|
||||
|
||||
//The name is confusing, but its because the queue itself has priority over the normal one
|
||||
ConditionalCircularLinkedList* priorityQueue;
|
||||
|
||||
void setQueue( );
|
||||
public:
|
||||
SongQueue( );
|
||||
~SongQueue( );
|
||||
|
||||
void PopulateQueue(const std::list<Song*>& songlist);
|
||||
|
||||
/**
|
||||
* @brief Will put the next song from the correct sublist as the topSong.
|
||||
* This will also remove the song from the queue as its not needed anymore.
|
||||
*
|
||||
*/
|
||||
void Next( );
|
||||
|
||||
/**
|
||||
* @brief Will put the given song from the priority or normal queue as the top song and remove it
|
||||
* from the list
|
||||
*
|
||||
* @param song
|
||||
*/
|
||||
void JumpToSong(Song* song, bool fromPrioQueue);
|
||||
|
||||
void AddToPriorityQueue(Song* song);
|
||||
|
||||
bool IsEmpty( );
|
||||
|
||||
Song* Top( );
|
||||
|
||||
void SetTop(Song* song);
|
||||
|
||||
void RemoveSongFromPriorityQueue(Song* song);
|
||||
|
||||
void MoveSongInPriorityQueue(Song* songToMove, Song* otherSong, bool beforeElseAfter);
|
||||
|
||||
void ShufflePlaylist( );
|
||||
void RestorePlaylist( );
|
||||
|
||||
void LinkQueue(bool link);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "audio.h"
|
||||
|
||||
Audio::Audio(const std::string path) :path(path) {
|
||||
Audio::Audio( ) {
|
||||
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
|
||||
std::cerr << "SDL initialization failed: " << SDL_GetError( ) << std::endl;
|
||||
return;
|
||||
@@ -11,14 +11,6 @@ Audio::Audio(const std::string path) :path(path) {
|
||||
SDL_Quit( );
|
||||
return;
|
||||
}
|
||||
|
||||
music = Mix_LoadMUS(path.c_str( ));
|
||||
if (!music) {
|
||||
std::cerr << "Failed to load MP3 file: " << Mix_GetError( ) << std::endl;
|
||||
Mix_CloseAudio( );
|
||||
SDL_Quit( );
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
Audio::~Audio( ) {
|
||||
@@ -34,6 +26,22 @@ void Audio::StartMusic( ) {
|
||||
}
|
||||
}
|
||||
|
||||
void Audio::StopMusic( ) {
|
||||
Mix_HaltMusic( );
|
||||
}
|
||||
|
||||
void Audio::PlaySong(const std::string path) {
|
||||
this->path = path;
|
||||
|
||||
music = Mix_LoadMUS(path.c_str( ));
|
||||
if (!music) {
|
||||
std::cerr << "Failed to load MP3 file: " << Mix_GetError( ) << std::endl;
|
||||
Mix_CloseAudio( );
|
||||
SDL_Quit( );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Audio::PauseMusic( ) {
|
||||
if (Mix_PlayingMusic( ) == 1) {
|
||||
Mix_PauseMusic( );
|
||||
|
||||
@@ -14,16 +14,16 @@ extern "C" {
|
||||
|
||||
class Audio {
|
||||
public:
|
||||
static Audio& getInstance(const std::string path) {
|
||||
static Audio instance(path);
|
||||
static Audio& getInstance( ) {
|
||||
static Audio instance;
|
||||
return instance;
|
||||
}
|
||||
private:
|
||||
Audio(const std::string path);
|
||||
Audio( );
|
||||
|
||||
const std::string path;
|
||||
const std::string artist;
|
||||
const std::string album;
|
||||
std::string path;
|
||||
std::string artist;
|
||||
std::string album;
|
||||
|
||||
Mix_Music* music;
|
||||
|
||||
@@ -43,6 +43,9 @@ public:
|
||||
|
||||
QPixmap GetAlbumCover( );
|
||||
|
||||
void PlaySong(const std::string path);
|
||||
|
||||
void StopMusic( );
|
||||
void StartMusic( );
|
||||
void PauseMusic( );
|
||||
void ResumeMusic( );
|
||||
|
||||
36
src/core/song/song.cpp
Normal file
36
src/core/song/song.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "song.h"
|
||||
|
||||
Song::Song(
|
||||
std::string title,
|
||||
std::string album,
|
||||
std::string artist,
|
||||
std::string codec,
|
||||
std::string comment,
|
||||
std::string copyright,
|
||||
std::string publisher,
|
||||
std::string genre,
|
||||
std::string encoded_by,
|
||||
std::string date,
|
||||
std::string language,
|
||||
std::string albumCoverPath,
|
||||
std::string path,
|
||||
int length,
|
||||
std::string discoveredPath
|
||||
) : title(title),
|
||||
album(album),
|
||||
artist(artist),
|
||||
codec(codec),
|
||||
comment(comment),
|
||||
copyright(copyright),
|
||||
publisher(publisher),
|
||||
genre(genre),
|
||||
encoded_by(encoded_by),
|
||||
date(date),
|
||||
language(language),
|
||||
albumCoverPath(albumCoverPath),
|
||||
path(path),
|
||||
length(length) {
|
||||
|
||||
}
|
||||
|
||||
Song::~Song( ) { }
|
||||
54
src/core/song/song.h
Normal file
54
src/core/song/song.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
class Song {
|
||||
private:
|
||||
// Song information
|
||||
const std::string& title;
|
||||
const std::string& album;
|
||||
const std::string& artist;
|
||||
const std::string& codec;
|
||||
const std::string& comment;
|
||||
const std::string& copyright;
|
||||
const std::string& publisher;
|
||||
const std::string& genre;
|
||||
const std::string& encoded_by;
|
||||
const std::string& date;
|
||||
const std::string& language;
|
||||
const std::string& albumCoverPath;
|
||||
const std::string& path;
|
||||
const int& length;
|
||||
|
||||
// Our own Metadata
|
||||
bool favorite = false;
|
||||
int playCount = 0;
|
||||
std::string discovered;
|
||||
|
||||
public:
|
||||
Song(
|
||||
std::string title,
|
||||
std::string album,
|
||||
std::string artist,
|
||||
std::string codec,
|
||||
std::string comment,
|
||||
std::string copyright,
|
||||
std::string publisher,
|
||||
std::string genre,
|
||||
std::string encoded_by,
|
||||
std::string date,
|
||||
std::string language,
|
||||
std::string albumCoverPath,
|
||||
std::string path,
|
||||
int length,
|
||||
std::string discoveredPath
|
||||
);
|
||||
~Song( );
|
||||
|
||||
void SetFavorite(bool fav) { favorite = fav; }
|
||||
|
||||
void IncrementPlayCount( ) { playCount++; }
|
||||
|
||||
std::string GetPath( ) { return path; };
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user