Kelick Tech Hub

Build, learn & keep up — tech, Linux & sports news included.

C++ Music Playlist Manager Project

Intermediate•1–2 days•linked lists, classes, pointers

Build playlists on a hand-rolled linked list: next, previous, shuffle, repeat.

Share on X

What you will build

Manage songs and playlists where 'next track' and 'previous track' are literal pointer hops through a doubly linked list you build yourself. It's the friendliest way to truly understand pointers and dynamic memory — with music as the reward.

What you will learn

Feature checklist

Starter code

#include <iostream>
#include <string>

struct Song {
    std::string title, artist;
    Song *prev = nullptr, *next = nullptr;
};

class Playlist {
    Song* head_ = nullptr; Song* tail_ = nullptr; Song* current_ = nullptr;
public:
    void add(std::string t, std::string a) {
        Song* s = new Song{std::move(t), std::move(a), tail_, nullptr};
        if (tail_) tail_->next = s; else head_ = s;
        tail_ = s;
        if (!current_) current_ = s;
    }
    void next() { if (current_ && current_->next) current_ = current_->next; }
    void prev() { if (current_ && current_->prev) current_ = current_->prev; }
    void removeCurrent() {
        if (!current_) return;
        Song* dead = current_;
        current_ = dead->next ? dead->next : dead->prev;
        if (dead->prev) dead->prev->next = dead->next; else head_ = dead->next;
        if (dead->next) dead->next->prev = dead->prev; else tail_ = dead->prev;
        delete dead;
    }
    void nowPlaying() const {
        if (current_) std::cout << "Now: " << current_->title
                                << " — " << current_->artist << "\n";
    }
    ~Playlist() { while (head_) { Song* n = head_->next; delete head_; head_ = n; } }
};

Full walkthrough

Step 1: Choose a structure that supports 'current song'

struct Song { title, artist, durationSeconds } and class Playlist built on std::list<Song> (or a vector plus a currentIndex). A playlist is not just a bag of songs: there is a CURSOR — the current track — and next/prev move it. The list + iterator design makes removing the current song survivable; the vector + index design is simpler. Either is defensible; pick one and make its edge cases your own.

Step 2: add() and a readable listing

add() appends a Song. The listing prints numbered songs with durations formatted m:ss (seconds / 60 and seconds % 60 with a zero-padded remainder — the printMs skill from the stopwatch, recycled), the total playlist duration the same way, and a marker (▶) on the current track.

Step 3: next() and prev() with wrap-around

Advance the cursor; at the end, wrap to the beginning (repeat-all behaviour) or stop, according to the repeat mode. prev() mirrors it. Wrap-around is a policy decision — state it in the UI so 'next' on the last song never surprises.

Step 4: removeCurrent(): the edge-case factory

Erase the current song. If songs remain, the cursor lands on the NEXT song (or the previous if you removed the last). If none remain, there is no current song and every playback command must handle that state gracefully. Removing from the middle, the end, and the only song are three different programs wearing one name — test all three.

// after erase, cursor points at the following song,
// or wraps to begin() if you removed the last one

Step 5: Shuffle by shuffling the order, not the cursor

Collect the songs into a vector, std::shuffle it with mt19937, and rebuild the play order, resetting the cursor to the first song. (std::list has no random access, so shuffling via a vector is the practical route — the container choice from step 1 sends its bill here.)

Step 6: Repeat modes as a tiny state machine

An enum Repeat { Off, All, One }: One makes next() return the same song, All wraps, Off stops at the end. One variable, three behaviours, zero duplicated navigation code — the mode is consulted at the boundaries only.

Step 7: Persist and test the cursor stories

Save songs (and ideally the current position) to a file. Test: next past the end under each repeat mode; remove the current song mid-list, at the end, and as the only song; total duration of 3:45 + 4:12 + 3:27 must print 11:24, proving your seconds arithmetic carries correctly.

Common mistakes

Compile and run

g++ -std=c++17 -o playlist playlist.cpp

Sample run

$ ./playlist
Loaded 3 songs.
1) Add  2) List  3) Play  4) Next  5) Prev  6) Remove current  7) Shuffle  0) Quit
> 2
1. Sunrise Avenue — Adaeze Obi (3:45)
2. ▶ Midnight Code — The Compilers (4:12)
3. Home Again — Yusuf Bello (3:27)
Total duration: 11:24
1) Add  2) List  3) Play  4) Next  5) Prev  6) Remove current  7) Shuffle  0) Quit
> 4
Now playing: Home Again — Yusuf Bello (3:27)
1) Add  2) List  3) Play  4) Next  5) Prev  6) Remove current  7) Shuffle  0) Quit
> 4
End of playlist — wrapped to the top (repeat: all)
Now playing: Sunrise Avenue — Adaeze Obi (3:45)
1) Add  2) List  3) Play  4) Next  5) Prev  6) Remove current  7) Shuffle  0) Quit
> 7
Shuffled. New order starts with: Midnight Code — The Compilers
1) Add  2) List  3) Play  4) Next  5) Prev  6) Remove current  7) Shuffle  0) Quit
> 0

Try it in your browser

No browser demo is included for this project. Build the C++ version below — that is the real program.

Related Intermediate projects

← All 60 projects · Classic site (Learn & Sandbox)

Download Fake GPS