diff --git a/Src/Music_Player_Application/Makefile b/Src/Music_Player_Application/Makefile new file mode 100644 index 0000000..787e555 --- /dev/null +++ b/Src/Music_Player_Application/Makefile @@ -0,0 +1,31 @@ +# Makefile for Lecture 18 C++ Music Player System + +CXX = g++ +CXXFLAGS = -std=c++11 -Wall -Wextra +TARGET = music_player +SOURCE = main.cpp + +# Default target +all: $(TARGET) + +# Build the executable +$(TARGET): $(SOURCE) + $(CXX) $(CXXFLAGS) -I. -o $(TARGET) $(SOURCE) + +# Run the application +run: $(TARGET) + ./$(TARGET) + +# Clean up compiled files +clean: + rm -f $(TARGET) $(TARGET).exe + +# Help target +help: + @echo "Available targets:" + @echo " all - Build the application (default)" + @echo " run - Build and run the application" + @echo " clean - Remove compiled files" + @echo " help - Show this help message" + +.PHONY: all run clean help diff --git a/Src/Music_Player_Application/MusicPlayerApplication.hpp b/Src/Music_Player_Application/MusicPlayerApplication.hpp new file mode 100644 index 0000000..56a6896 --- /dev/null +++ b/Src/Music_Player_Application/MusicPlayerApplication.hpp @@ -0,0 +1,94 @@ +#pragma once +#include "managers/PlaylistManager.hpp" +#include "MusicPlayerFacade.hpp" + +using namespace std; + +class MusicPlayerApplication { +private: + static MusicPlayerApplication* instance; + vector songLibrary; + MusicPlayerApplication() {} + +public: + static MusicPlayerApplication* getInstance() { + if (!instance) { + instance = new MusicPlayerApplication(); + } + return instance; + } + + void createSongInLibrary(const string& title, const string& artist, + const string& path) { + Song* newSong = new Song(title, artist, path); + songLibrary.push_back(newSong); + } + + Song* findSongByTitle(const string& title) { + for (Song* s : songLibrary) { + if (s->getTitle() == title) { + return s; + } + } + return nullptr; + } + void createPlaylist(const string& playlistName) { + PlaylistManager::getInstance()->createPlaylist(playlistName); + } + + void addSongToPlaylist(const string& playlistName, + const string& songTitle) { + Song* song = findSongByTitle(songTitle); + if (!song) { + throw runtime_error("Song \"" + songTitle + "\" not found in library."); + } + PlaylistManager::getInstance() + ->addSongToPlaylist(playlistName, song); + } + + void connectAudioDevice(DeviceType deviceType) { + MusicPlayerFacade::getInstance()->connectDevice(deviceType); + } + + void selectPlayStrategy(PlayStrategyType strategyType) { + MusicPlayerFacade::getInstance()->setPlayStrategy(strategyType); + } + + void loadPlaylist(const string& playlistName) { + MusicPlayerFacade::getInstance()->loadPlaylist(playlistName); + } + + void playSingleSong(const string& songTitle) { + Song* song = findSongByTitle(songTitle); + if (!song) { + throw runtime_error("Song \"" + songTitle + "\" not found."); + } + MusicPlayerFacade::getInstance()->playSong(song); + } + + void pauseCurrentSong(const string& songTitle) { + Song* song = findSongByTitle(songTitle); + if (!song) { + throw runtime_error("Song \"" + songTitle + "\" not found."); + } + MusicPlayerFacade::getInstance()->pauseSong(song); + } + + void playAllTracksInPlaylist() { + MusicPlayerFacade::getInstance()->playAllTracks(); + } + + void playPreviousTrackInPlaylist() { + MusicPlayerFacade::getInstance()->playPreviousTrack(); + } + + void queueSongNext(const string& songTitle) { + Song* song = findSongByTitle(songTitle); + if (!song) { + throw runtime_error("Song \"" + songTitle + "\" not found."); + } + MusicPlayerFacade::getInstance()->enqueueNext(song); + } +}; + +MusicPlayerApplication* MusicPlayerApplication::instance = nullptr; \ No newline at end of file diff --git a/Src/Music_Player_Application/MusicPlayerFacade.hpp b/Src/Music_Player_Application/MusicPlayerFacade.hpp new file mode 100644 index 0000000..f144da5 --- /dev/null +++ b/Src/Music_Player_Application/MusicPlayerFacade.hpp @@ -0,0 +1,112 @@ +#pragma once +#include "core/AudioEngine.hpp" +#include "models/Playlist.hpp" +#include "models/Song.hpp" +#include "strategies/PlayStrategy.hpp" +#include "enums/DeviceType.hpp" +#include "enums/PlayStrategyType.hpp" +#include "managers/DeviceManager.hpp" +#include "managers/PlaylistManager.hpp" +#include "managers/StrategyManager.hpp" + +using namespace std; + +class MusicPlayerFacade { +private: + static MusicPlayerFacade* instance; + AudioEngine* audioEngine; + Playlist* loadedPlaylist; + PlayStrategy* playStrategy; + + MusicPlayerFacade() { + loadedPlaylist = nullptr; + playStrategy = nullptr; + audioEngine = new AudioEngine(); + } + +public: + static MusicPlayerFacade* getInstance() { + if (!instance) { + instance = new MusicPlayerFacade(); + } + return instance; + } + + void connectDevice(DeviceType deviceType) { + DeviceManager::getInstance()->connect(deviceType); + } + + void setPlayStrategy(PlayStrategyType strategyType) { + playStrategy = StrategyManager::getInstance()->getStrategy(strategyType); + } + + void loadPlaylist(const string& name) { + loadedPlaylist = PlaylistManager::getInstance()->getPlaylist(name); + if (!playStrategy) { + throw runtime_error("Play strategy not set before loading."); + } + playStrategy->setPlaylist(loadedPlaylist); + } + + void playSong(Song* song) { + if (!DeviceManager::getInstance()->hasOutputDevice()) { + throw runtime_error("No audio device connected."); + } + IAudioOutputDevice* device = DeviceManager::getInstance()->getOutputDevice(); + audioEngine->play(device, song); + } + + void pauseSong(Song* song) { + if (audioEngine->getCurrentSongTitle() != song->getTitle()) { + throw runtime_error("Cannot pause \"" + song->getTitle() + "\"; not currently playing."); + } + audioEngine->pause(); + } + + void playAllTracks() { + if (!loadedPlaylist) { + throw runtime_error("No playlist loaded."); + } + while (playStrategy->hasNext()) { + Song* nextSong = playStrategy->next(); + IAudioOutputDevice* device = DeviceManager::getInstance()->getOutputDevice(); + audioEngine->play(device, nextSong); + } + cout << "Completed playlist: " << loadedPlaylist->getPlaylistName() << "\n"; + } + + void playNextTrack() { + if (!loadedPlaylist) { + throw runtime_error("No playlist loaded."); + } + if(playStrategy->hasNext()) { + Song* nextSong = playStrategy->next(); + IAudioOutputDevice* device = DeviceManager::getInstance()->getOutputDevice(); + audioEngine->play(device, nextSong); + } + else { + cout << "Completed playlist: " << loadedPlaylist->getPlaylistName() << "\n"; + } + } + + void playPreviousTrack() { + if (!loadedPlaylist) { + throw runtime_error("No playlist loaded."); + } + if(playStrategy->hasPrevious()) { + Song* prevSong = playStrategy->previous(); + IAudioOutputDevice* device = DeviceManager::getInstance()->getOutputDevice(); + audioEngine->play(device, prevSong); + } + else { + cout << "Completed playlist: " << loadedPlaylist->getPlaylistName() << "\n"; + } + } + + void enqueueNext(Song* song) { + playStrategy->addToNext(song); + } +}; + +MusicPlayerFacade* MusicPlayerFacade::instance = nullptr; + \ No newline at end of file diff --git a/Src/Music_Player_Application/compile.bat b/Src/Music_Player_Application/compile.bat new file mode 100644 index 0000000..ab91e5c --- /dev/null +++ b/Src/Music_Player_Application/compile.bat @@ -0,0 +1,17 @@ +@echo off +echo Compiling C++ Music Player System... + +REM Compile the main program +g++ -std=c++11 -I. -o music_player main.cpp + +if %errorlevel% equ 0 ( + echo Compilation successful! + echo Running the Music Player application... + echo. + music_player.exe +) else ( + echo Compilation failed! + echo Please check for any syntax errors. +) + +pause diff --git a/Src/Music_Player_Application/compile.sh b/Src/Music_Player_Application/compile.sh new file mode 100644 index 0000000..2a2aae2 --- /dev/null +++ b/Src/Music_Player_Application/compile.sh @@ -0,0 +1,15 @@ +#!/bin/bash +echo "Compiling Lecture 18 C++ Music Player System..." + +# Compile the main program +g++ -std=c++11 -I. -o music_player main.cpp + +if [ $? -eq 0 ]; then + echo "Compilation successful!" + echo "Running the Music Player application..." + echo + ./music_player +else + echo "Compilation failed!" + echo "Please check for any syntax errors." +fi diff --git a/Src/Music_Player_Application/core/AudioEngine.hpp b/Src/Music_Player_Application/core/AudioEngine.hpp new file mode 100644 index 0000000..c967633 --- /dev/null +++ b/Src/Music_Player_Application/core/AudioEngine.hpp @@ -0,0 +1,55 @@ +#pragma once +#include "../models/Song.hpp" +#include "../device/IAudioOutputDevice.hpp" +#include +#include + +using namespace std; + +class AudioEngine { +private: + Song* currentSong; + bool songIsPaused; +public: + AudioEngine() { + currentSong = nullptr; + songIsPaused = false; + } + string getCurrentSongTitle() const { + if (currentSong) { + return currentSong->getTitle(); + } + return ""; + } + bool isPaused() const { + return songIsPaused; + } + void play(IAudioOutputDevice* aod, Song* song) { + if (song == nullptr) { + throw runtime_error("Cannot play a null song."); + } + // Resume if same song was paused + if (songIsPaused && song == currentSong) { + songIsPaused = false; + cout << "Resuming song: " << song->getTitle() << "\n"; + aod->playAudio(song); + return; + } + + currentSong = song; + songIsPaused = false; + cout << "Playing song: " << song->getTitle() << "\n"; + aod->playAudio(song); + } + + void pause() { + if (currentSong == nullptr) { + throw runtime_error("No song is currently playing to pause."); + } + if (songIsPaused) { + throw runtime_error("Song is already paused."); + } + songIsPaused = true; + cout << "Pausing song: " << currentSong->getTitle() << "\n"; + } +}; \ No newline at end of file diff --git a/Src/Music_Player_Application/device/BluetoothSpeakerAdapter.hpp b/Src/Music_Player_Application/device/BluetoothSpeakerAdapter.hpp new file mode 100644 index 0000000..5879545 --- /dev/null +++ b/Src/Music_Player_Application/device/BluetoothSpeakerAdapter.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "../models/Song.hpp" +#include "IAudioOutputDevice.hpp" +#include "../external/BluetoothSpeakerAPI.hpp" + +using namespace std; + +class BluetoothSpeakerAdapter : public IAudioOutputDevice { +private: + BluetoothSpeakerAPI* bluetoothApi; +public: + BluetoothSpeakerAdapter(BluetoothSpeakerAPI* api) { + bluetoothApi = api; + } + + void playAudio(Song* song) override { + string payload = song->getTitle() + " by " + song->getArtist(); + bluetoothApi->playSoundViaBluetooth(payload); + } +}; \ No newline at end of file diff --git a/Src/Music_Player_Application/device/HeadphonesAdapter.hpp b/Src/Music_Player_Application/device/HeadphonesAdapter.hpp new file mode 100644 index 0000000..cdaaf1c --- /dev/null +++ b/Src/Music_Player_Application/device/HeadphonesAdapter.hpp @@ -0,0 +1,21 @@ +#pragma once +#include "../models/Song.hpp" +#include "IAudioOutputDevice.hpp" +#include "../external/HeadphonesAPI.hpp" + +using namespace std; + +class HeadphonesAdapter : public IAudioOutputDevice { +private: + HeadphonesAPI* headphonesApi; +public: + HeadphonesAdapter(HeadphonesAPI* api) { + headphonesApi = api; + } + + void playAudio(Song* song) override { + string payload = song->getTitle() + " by " + song->getArtist(); + headphonesApi->playSoundViaJack(payload); + } +}; + diff --git a/Src/Music_Player_Application/device/IAudioOutputDevice.hpp b/Src/Music_Player_Application/device/IAudioOutputDevice.hpp new file mode 100644 index 0000000..7b94c0d --- /dev/null +++ b/Src/Music_Player_Application/device/IAudioOutputDevice.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "../models/Song.hpp" + +class IAudioOutputDevice { +public: + virtual ~IAudioOutputDevice() {} + virtual void playAudio(Song* song) = 0; +}; \ No newline at end of file diff --git a/Src/Music_Player_Application/device/WiredSpeakerAdapter.hpp b/Src/Music_Player_Application/device/WiredSpeakerAdapter.hpp new file mode 100644 index 0000000..b486488 --- /dev/null +++ b/Src/Music_Player_Application/device/WiredSpeakerAdapter.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "../models/Song.hpp" +#include "IAudioOutputDevice.hpp" +#include "../external/WiredSpeakerAPI.hpp" + +using namespace std; + +class WiredSpeakerAdapter : public IAudioOutputDevice { +private: + WiredSpeakerAPI* wiredApi; +public: + WiredSpeakerAdapter(WiredSpeakerAPI* api) { + wiredApi = api; + } + + void playAudio(Song* song) override { + string payload = song->getTitle() + " by " + song->getArtist(); + wiredApi->playSoundViaCable(payload); + } +}; \ No newline at end of file diff --git a/Src/Music_Player_Application/enums/DeviceType.hpp b/Src/Music_Player_Application/enums/DeviceType.hpp new file mode 100644 index 0000000..614654a --- /dev/null +++ b/Src/Music_Player_Application/enums/DeviceType.hpp @@ -0,0 +1,7 @@ +#pragma once + +enum class DeviceType { + BLUETOOTH, + WIRED, + HEADPHONES +}; \ No newline at end of file diff --git a/Src/Music_Player_Application/enums/PlayStrategyType.hpp b/Src/Music_Player_Application/enums/PlayStrategyType.hpp new file mode 100644 index 0000000..ab2780a --- /dev/null +++ b/Src/Music_Player_Application/enums/PlayStrategyType.hpp @@ -0,0 +1,7 @@ +#pragma once + +enum class PlayStrategyType { + SEQUENTIAL, + RANDOM, + CUSTOM_QUEUE +}; \ No newline at end of file diff --git a/Src/Music_Player_Application/external/BluetoothSpeakerAPI.hpp b/Src/Music_Player_Application/external/BluetoothSpeakerAPI.hpp new file mode 100644 index 0000000..58c03d9 --- /dev/null +++ b/Src/Music_Player_Application/external/BluetoothSpeakerAPI.hpp @@ -0,0 +1,13 @@ +#pragma once +#include +#include + +using namespace std; + +class BluetoothSpeakerAPI { +public: + void playSoundViaBluetooth(const string& data) { + cout << "[BluetoothSpeaker] Playing: " << data << "\n"; + // mimics playing music + } +}; \ No newline at end of file diff --git a/Src/Music_Player_Application/external/HeadphonesAPI.hpp b/Src/Music_Player_Application/external/HeadphonesAPI.hpp new file mode 100644 index 0000000..1220821 --- /dev/null +++ b/Src/Music_Player_Application/external/HeadphonesAPI.hpp @@ -0,0 +1,13 @@ +#pragma once +#include +#include + +using namespace std; + +class HeadphonesAPI { +public: + void playSoundViaJack(const string& data) { + cout << "[Headphones] Playing: " << data << "\n"; + // mimics playing music + } +}; \ No newline at end of file diff --git a/Src/Music_Player_Application/external/WiredSpeakerAPI.hpp b/Src/Music_Player_Application/external/WiredSpeakerAPI.hpp new file mode 100644 index 0000000..c7b4efe --- /dev/null +++ b/Src/Music_Player_Application/external/WiredSpeakerAPI.hpp @@ -0,0 +1,13 @@ +#pragma once +#include +#include + +using namespace std; + +class WiredSpeakerAPI { +public: + void playSoundViaCable(const string& data) { + cout << "[WiredSpeaker] Playing: " << data << "\n"; + // mimics playing music + } +}; \ No newline at end of file diff --git a/Src/Music_Player_Application/factories/DeviceFactory.hpp b/Src/Music_Player_Application/factories/DeviceFactory.hpp new file mode 100644 index 0000000..6d4d01a --- /dev/null +++ b/Src/Music_Player_Application/factories/DeviceFactory.hpp @@ -0,0 +1,22 @@ +#pragma once +#include +#include "../device/IAudioOutputDevice.hpp" +#include "../device/BluetoothSpeakerAdapter.hpp" +#include "../device/WiredSpeakerAdapter.hpp" +#include "../device/HeadphonesAdapter.hpp" +#include "../enums/DeviceType.hpp" + +using namespace std; + +class DeviceFactory { +public: + static IAudioOutputDevice* createDevice(DeviceType deviceType) { + if (deviceType == DeviceType::BLUETOOTH) { + return new BluetoothSpeakerAdapter(new BluetoothSpeakerAPI()); + } else if (deviceType == DeviceType::WIRED) { + return new WiredSpeakerAdapter(new WiredSpeakerAPI()); + } else { // HEADPHONES + return new HeadphonesAdapter(new HeadphonesAPI()); + } + } +}; \ No newline at end of file diff --git a/Src/Music_Player_Application/folderstructure.txt b/Src/Music_Player_Application/folderstructure.txt new file mode 100644 index 0000000..eaa9dc6 --- /dev/null +++ b/Src/Music_Player_Application/folderstructure.txt @@ -0,0 +1,41 @@ +MusicPlayerApp/ +│ +├── main.cpp # Composition root and entry point +├── MusicPlayerFacade.hpp # Facade class (orchestrator) +├── MusicPlayerApplication.hpp # High-level application/demo runner +│ +├── core/ +│ └── AudioEngine.hpp # Playback engine +│ +├── enums/ # All shared enum types +│ ├── DeviceType.hpp # enum class DeviceType { BLUETOOTH, WIRED, HEADPHONES } +│ └── PlayStrategyType.hpp # enum class PlayStrategyType { SEQUENTIAL, RANDOM, CUSTOM_QUEUE } +│ +├── models/ +│ ├── Song.hpp +│ └── Playlist.hpp +│ +├── managers/ +│ ├── PlaylistManager.hpp +│ ├── DeviceManager.hpp +| └── StrategyManager.hpp +│ +├── strategies/ +│ ├── PlayStrategy.hpp +│ ├── SequentialPlayStrategy.hpp +│ ├── RandomPlayStrategy.hpp +│ └── CustomQueueStrategy.hpp +│ +├── device/ # Audio device interfaces & adapters +│ ├── IAudioOutputDevice.hpp +│ ├── BluetoothSpeakerAdapter.hpp +│ ├── WiredSpeakerAdapter.hpp +│ └── HeadphonesAdapter.hpp +| +├── external/ # External devices +│ ├── BluetoothSpeakerAPI.hpp +│ ├── HeadphonesAPI.hpp +│ └── WiredSpeakerAPI.hpp +│ +└── factories/ + └── DeviceFactory.hpp # Creates IAudioOutputDevice instances diff --git a/Src/Music_Player_Application/main.cpp b/Src/Music_Player_Application/main.cpp new file mode 100644 index 0000000..2580613 --- /dev/null +++ b/Src/Music_Player_Application/main.cpp @@ -0,0 +1,60 @@ +#include "MusicPlayerApplication.hpp" + +using namespace std; + +int main() { + try { + auto application = MusicPlayerApplication::getInstance(); + + // Populate library + application->createSongInLibrary("Kesariya", "Arijit Singh", "/music/kesariya.mp3"); + application->createSongInLibrary("Chaiyya Chaiyya", "Sukhwinder Singh", "/music/chaiyya_chaiyya.mp3"); + application->createSongInLibrary("Tum Hi Ho", "Arijit Singh", "/music/tum_hi_ho.mp3"); + application->createSongInLibrary("Jai Ho", "A. R. Rahman", "/music/jai_ho.mp3"); + application->createSongInLibrary("Zinda", "Siddharth Mahadevan", "/music/zinda.mp3"); + + // Create playlist and add songs + application->createPlaylist("Bollywood Vibes"); + application->addSongToPlaylist("Bollywood Vibes", "Kesariya"); + application->addSongToPlaylist("Bollywood Vibes", "Chaiyya Chaiyya"); + application->addSongToPlaylist("Bollywood Vibes", "Tum Hi Ho"); + application->addSongToPlaylist("Bollywood Vibes", "Jai Ho"); + + // Connect device + application->connectAudioDevice(DeviceType::BLUETOOTH); + + //Play/pause a single song + application->playSingleSong("Zinda"); + application->pauseCurrentSong("Zinda"); + application->playSingleSong("Zinda"); // resume + + cout << "\n-- Sequential Playback --\n"; + application->selectPlayStrategy(PlayStrategyType::SEQUENTIAL); + application->loadPlaylist("Bollywood Vibes"); + application->playAllTracksInPlaylist(); + + cout << "\n-- Random Playback --\n"; + application->selectPlayStrategy(PlayStrategyType::RANDOM); + application->loadPlaylist("Bollywood Vibes"); + application->playAllTracksInPlaylist(); + + cout << "\n-- Custom Queue Playback --\n"; + application->selectPlayStrategy(PlayStrategyType::CUSTOM_QUEUE); + application->loadPlaylist("Bollywood Vibes"); + application->queueSongNext("Kesariya"); + application->queueSongNext("Tum Hi Ho"); + application->playAllTracksInPlaylist(); + + cout << "\n-- Play Previous in Sequential --\n"; + application->selectPlayStrategy(PlayStrategyType::SEQUENTIAL); + application->loadPlaylist("Bollywood Vibes"); + application->playAllTracksInPlaylist(); + + application->playPreviousTrackInPlaylist(); + application->playPreviousTrackInPlaylist(); + + } catch (const exception& error) { + cerr << "Error: " << error.what() << endl; + } + return 0; +} \ No newline at end of file diff --git a/Src/Music_Player_Application/managers/DeviceManager.hpp b/Src/Music_Player_Application/managers/DeviceManager.hpp new file mode 100644 index 0000000..fa94699 --- /dev/null +++ b/Src/Music_Player_Application/managers/DeviceManager.hpp @@ -0,0 +1,54 @@ +#pragma once +#include +#include "../device/IAudioOutputDevice.hpp" +#include "../enums/DeviceType.hpp" +#include "../factories/DeviceFactory.hpp" + +using namespace std; + +class DeviceManager { +private: + static DeviceManager* instance; + IAudioOutputDevice* currentOutputDevice; + DeviceManager() { + currentOutputDevice = nullptr; + } +public: + static DeviceManager* getInstance() { + if (instance == nullptr) { + instance = new DeviceManager(); + } + return instance; + } + void connect(DeviceType deviceType) { + if (currentOutputDevice) { + delete currentOutputDevice; + } + + currentOutputDevice = DeviceFactory::createDevice(deviceType); + + switch(deviceType) { + case DeviceType::BLUETOOTH: + cout<< "Bluetooth device connected \n"; + break; + case DeviceType::WIRED: + cout<< "Wired device connected \n"; + break; + case DeviceType::HEADPHONES: + cout<< "Headphones connected \n"; + } + } + + IAudioOutputDevice* getOutputDevice() { + if (!currentOutputDevice) { + throw runtime_error("No output device is connected."); + } + return currentOutputDevice; + } + + bool hasOutputDevice() { + return currentOutputDevice != nullptr; + } +}; + +DeviceManager* DeviceManager::instance = nullptr; \ No newline at end of file diff --git a/Src/Music_Player_Application/managers/PlaylistManager.hpp b/Src/Music_Player_Application/managers/PlaylistManager.hpp new file mode 100644 index 0000000..1f25df7 --- /dev/null +++ b/Src/Music_Player_Application/managers/PlaylistManager.hpp @@ -0,0 +1,43 @@ +#pragma once +#include "../models/Playlist.hpp" +#include +#include +#include + +using namespace std; + +class PlaylistManager { +private: + static PlaylistManager* instance; + map playlists; + PlaylistManager() {} +public: + static PlaylistManager* getInstance() { + if (!instance) { + instance = new PlaylistManager(); + } + return instance; + } + + void createPlaylist(const string& name) { + if (playlists.count(name)) { + throw runtime_error("Playlist \"" + name + "\" already exists."); + } + playlists[name] = new Playlist(name); + } + + void addSongToPlaylist(const string& playlistName, Song* song) { + if (!playlists.count(playlistName)) { + throw runtime_error("Playlist \"" + playlistName + "\" not found."); + } + playlists[playlistName]->addSongToPlaylist(song); + } + + Playlist* getPlaylist(const string& name) { + if (!playlists.count(name)) { + throw runtime_error("Playlist \"" + name + "\" not found."); + } + return playlists[name]; + } +}; +PlaylistManager* PlaylistManager::instance = nullptr; \ No newline at end of file diff --git a/Src/Music_Player_Application/managers/StrategyManager.hpp b/Src/Music_Player_Application/managers/StrategyManager.hpp new file mode 100644 index 0000000..c972ad0 --- /dev/null +++ b/Src/Music_Player_Application/managers/StrategyManager.hpp @@ -0,0 +1,40 @@ +#pragma once +#include +#include "../strategies/SequentialPlayStrategy.hpp" +#include "../strategies/CustomQueueStrategy.hpp" +#include "../strategies/RandomPlayStrategy.hpp" +#include "../enums/PlayStrategyType.hpp" + +using namespace std; + +class StrategyManager { +private: + static StrategyManager* instance; + SequentialPlayStrategy* sequentialStrategy; + RandomPlayStrategy* randomStrategy; + CustomQueueStrategy* customQueueStrategy; + + StrategyManager() { + sequentialStrategy = new SequentialPlayStrategy(); + randomStrategy = new RandomPlayStrategy(); + customQueueStrategy = new CustomQueueStrategy(); + } +public: + static StrategyManager* getInstance() { + if (!instance) { + instance = new StrategyManager(); + } + return instance; + } + PlayStrategy* getStrategy(PlayStrategyType type) { + if (type == PlayStrategyType::SEQUENTIAL) { + return sequentialStrategy; + } else if (type == PlayStrategyType::RANDOM) { + return randomStrategy; + } else { + return customQueueStrategy; + } + } +}; + +StrategyManager* StrategyManager::instance = nullptr; \ No newline at end of file diff --git a/Src/Music_Player_Application/models/Playlist.hpp b/Src/Music_Player_Application/models/Playlist.hpp new file mode 100644 index 0000000..b1e1225 --- /dev/null +++ b/Src/Music_Player_Application/models/Playlist.hpp @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include +#include "Song.hpp" + +using namespace std; + +class Playlist { +private: + string playlistName; + vector songList; +public: + Playlist(string name) { + playlistName = name; + } + string getPlaylistName() { + return playlistName; + } + const vector getSongs() { + return songList; + } + int getSize() { + return (int)songList.size(); + } + void addSongToPlaylist(Song* song) { + if (song == nullptr) { + throw runtime_error("Cannot add null song to playlist."); + } + songList.push_back(song); + } +}; \ No newline at end of file diff --git a/Src/Music_Player_Application/models/Song.hpp b/Src/Music_Player_Application/models/Song.hpp new file mode 100644 index 0000000..6b546ef --- /dev/null +++ b/Src/Music_Player_Application/models/Song.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include + +using namespace std; + +class Song { +private: + string title; + string artist; + string filePath; +public: + Song(string t, string a, string f) { + title = t; + artist = a; + filePath = f; + } + string getTitle() { + return title; + } + string getArtist() { + return artist; + } + string getFilePath() { + return filePath; + } +}; \ No newline at end of file diff --git a/Src/Music_Player_Application/music_player.exe b/Src/Music_Player_Application/music_player.exe new file mode 100644 index 0000000..29347a7 Binary files /dev/null and b/Src/Music_Player_Application/music_player.exe differ diff --git a/Src/Music_Player_Application/strategies/CustomQueueStrategy.hpp b/Src/Music_Player_Application/strategies/CustomQueueStrategy.hpp new file mode 100644 index 0000000..0b4db7b --- /dev/null +++ b/Src/Music_Player_Application/strategies/CustomQueueStrategy.hpp @@ -0,0 +1,113 @@ +#pragma once +#include +#include +#include +#include +#include +#include "../models/Playlist.hpp" +#include "PlayStrategy.hpp" + +class CustomQueueStrategy : public PlayStrategy { +private: + Playlist* currentPlaylist; + int currentIndex; + queue nextQueue; + stack prevStack; + + Song* nextSequential() { + if (currentPlaylist->getSize() == 0) { + throw runtime_error("Playlist is empty."); + } + currentIndex = currentIndex + 1; + return currentPlaylist->getSongs()[currentIndex]; + } + + Song* previousSequential() { + if (currentPlaylist->getSize() == 0) { + throw runtime_error("Playlist is empty."); + } + currentIndex = currentIndex - 1; + return currentPlaylist->getSongs()[currentIndex]; + } + +public: + CustomQueueStrategy() { + currentPlaylist = nullptr; + currentIndex = -1; + } + + void setPlaylist(Playlist* playlist) override { + currentPlaylist = playlist; + currentIndex = -1; + while (!nextQueue.empty()) { + nextQueue.pop(); + } + while(!prevStack.empty()) { + prevStack.pop(); + } + } + + bool hasNext() override { + return ((currentIndex + 1) < currentPlaylist->getSize()); + } + + Song* next() override { + if (!currentPlaylist || currentPlaylist->getSize() == 0) { + throw runtime_error("No playlist loaded or playlist is empty."); + } + + if (!nextQueue.empty()) { + Song* s = nextQueue.front(); + nextQueue.pop(); + prevStack.push(s); + + // update index to match queued song + auto& list = currentPlaylist->getSongs(); + for (int i = 0; i < (int)list.size(); ++i) { + if (list[i] == s) { + currentIndex = i; + break; + } + } + return s; + } + + // Otherwise sequential + return nextSequential(); + } + + bool hasPrevious() override { + return (currentIndex - 1 > 0); + } + + Song* previous() override { + if (!currentPlaylist || currentPlaylist->getSize() == 0) { + throw runtime_error("No playlist loaded or playlist is empty."); + } + + if (!prevStack.empty()) { + Song* s = prevStack.top(); + prevStack.pop(); + + // update index to match stacked song + auto& list = currentPlaylist->getSongs(); + for (int i = 0; i < (int)list.size(); ++i) { + if (list[i] == s) { + currentIndex = i; + break; + } + } + return s; + } + + // Otherwise sequential + return previousSequential(); + } + + void addToNext(Song* song) override { + if (!song) { + throw runtime_error("Cannot enqueue null song."); + } + nextQueue.push(song); + } +}; \ No newline at end of file diff --git a/Src/Music_Player_Application/strategies/PlayStrategy.hpp b/Src/Music_Player_Application/strategies/PlayStrategy.hpp new file mode 100644 index 0000000..3d09455 --- /dev/null +++ b/Src/Music_Player_Application/strategies/PlayStrategy.hpp @@ -0,0 +1,17 @@ +#pragma once +#include +#include "../models/Song.hpp" +#include "../models/Playlist.hpp" + +using namespace std; + +class PlayStrategy { +public: + virtual ~PlayStrategy() {} + virtual void setPlaylist(Playlist* playlist) = 0; + virtual Song* next() = 0; + virtual bool hasNext() = 0; + virtual Song* previous() = 0; + virtual bool hasPrevious() = 0; + virtual void addToNext(Song* song) {} +}; \ No newline at end of file diff --git a/Src/Music_Player_Application/strategies/RandomPlayStrategy.hpp b/Src/Music_Player_Application/strategies/RandomPlayStrategy.hpp new file mode 100644 index 0000000..b3d5314 --- /dev/null +++ b/Src/Music_Player_Application/strategies/RandomPlayStrategy.hpp @@ -0,0 +1,72 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "../models/Playlist.hpp" +#include "PlayStrategy.hpp" + +using namespace std; + +class RandomPlayStrategy : public PlayStrategy { +private: + Playlist* currentPlaylist; + vector remainingSongs; + stack history; + +public: + RandomPlayStrategy() { + currentPlaylist = nullptr; + srand((unsigned)time(nullptr)); + } + + void setPlaylist(Playlist* playlist) override { + currentPlaylist = playlist; + if (!currentPlaylist || currentPlaylist->getSize() == 0) return; + + remainingSongs = currentPlaylist->getSongs(); + history = stack(); + } + + bool hasNext() override { + return currentPlaylist && !remainingSongs.empty(); + } + + // Next in Loop + Song* next() override { + if (!currentPlaylist || currentPlaylist->getSize() == 0) { + throw runtime_error("No playlist loaded or playlist is empty."); + } + if (remainingSongs.empty()) { + throw runtime_error("No songs left to play"); + } + + int idx = rand() % remainingSongs.size(); + Song* selectedSong = remainingSongs[idx]; + + // Remove the selectedSong from the list. (Swap and pop to remove in O(1)) + swap(remainingSongs[idx], remainingSongs.back()); + remainingSongs.pop_back(); + + history.push(selectedSong); + return selectedSong; + } + + bool hasPrevious() override { + return history.size() > 0; + } + + Song* previous() override { + if (history.empty()) { + throw std::runtime_error("No previous song available."); + } + + Song* song = history.top(); + history.pop(); + return song; + } +}; + \ No newline at end of file diff --git a/Src/Music_Player_Application/strategies/SequentialPlayStrategy.hpp b/Src/Music_Player_Application/strategies/SequentialPlayStrategy.hpp new file mode 100644 index 0000000..e221af8 --- /dev/null +++ b/Src/Music_Player_Application/strategies/SequentialPlayStrategy.hpp @@ -0,0 +1,48 @@ +#pragma once +#include +#include "../models/Playlist.hpp" +#include "PlayStrategy.hpp" + +using namespace std; + +class SequentialPlayStrategy : public PlayStrategy { +private: + Playlist* currentPlaylist; + int currentIndex; +public: + SequentialPlayStrategy() { + currentPlaylist = nullptr; + currentIndex = -1; + } + + void setPlaylist(Playlist* playlist) override { + currentPlaylist = playlist; + currentIndex = -1; + } + + bool hasNext() override { + return ((currentIndex + 1) < currentPlaylist->getSize()); + } + + // Next in Loop + Song* next() override { + if (!currentPlaylist || currentPlaylist->getSize() == 0) { + throw runtime_error("No playlist loaded or playlist is empty."); + } + currentIndex = currentIndex + 1; + return currentPlaylist->getSongs()[currentIndex]; + } + + bool hasPrevious() override { + return (currentIndex - 1 > 0); + } + + // previous in Loop + Song* previous() override { + if (!currentPlaylist || currentPlaylist->getSize() == 0) { + throw runtime_error("No playlist loaded or playlist is empty."); + } + currentIndex = currentIndex - 1; + return currentPlaylist->getSongs()[currentIndex]; + } +}; \ No newline at end of file