C++ Music Playlist Manager Project
Build playlists on a hand-rolled linked list: next, previous, shuffle, repeat.
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
- Building a doubly linked list by hand
- Inserting and deleting nodes safely
- Traversal in both directions
- Shuffle by collecting and randomising nodes
Feature checklist
- Add songs (title, artist, duration)
- Play order with next / previous
- Remove a song from anywhere in the list
- Shuffle play
- Repeat one / repeat all modes
- Total playlist duration
- Save playlist to a file
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 oneStep 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
- Removing the current song and leaving the cursor/index pointing past the end — the next 'next' crashes or skips.
- Formatting durations as raw seconds (224) instead of m:ss, or forgetting zero-padding (3:5 instead of 3:05).
- Shuffling by swapping the current song randomly, which scrambles nothing and confuses the cursor.
Compile and run
g++ -std=c++17 -o playlist playlist.cppSample 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
> 0Try it in your browser
No browser demo is included for this project. Build the C++ version below — that is the real program.