CPE 551 Final Project — Stevens Institute of Technology
- Michael Moschello (mmoschel@stevens.edu, 10479727)
- Michael Savo (msavo@stevens.edu, 20026290)
One of us has a multi-zone home audio setup in the basement: five speaker zones plus a subwoofer, all running off the same amplifier. The problem is that no single bass/treble setting sounds right for every song. A track like "Blinding Lights" that lives mostly in the mids needs very different EQ than something bass-heavy like a Daft Punk track, and the overhead speakers behave nothing like the subwoofer.
So we wrote a program that does the tuning for us. You type in a song name, it pulls the track's metadata from the Spotify Web API, looks up that song's audio features (loudness and energy, plus a few we precomputed), and feeds those features into a per-zone K-Nearest Neighbors model. Each zone gets its own predicted bass and treble gain based on training data we collected by hand while listening to reference tracks through that zone. The console prints the recommended EQ per zone and a matplotlib chart shows the result visually.
The point is: the speakers in each zone have different physical capabilities (frequency range, sensitivity, power handling), so it doesn't make sense to apply one global EQ. KNN lets us learn what "good" sounds like for each zone separately, without having to hand-write rules.
SpeakerBalence/
├── main.py # interactive console entry point
├── requirements.txt
├── pytest.ini
├── README.md
├── .env # your Spotify creds (you create this)
├── data/
│ ├── speakers.csv # hardware specs per speaker
│ ├── training_data.csv # hand-tuned (loudness, energy) -> (bass, treble) rows
│ └── song_features.csv # precomputed audio features for known tracks
├── src/
│ ├── speaker.py # Speaker class + Subwoofer subclass (inheritance)
│ ├── zone.py # SpeakerZone (composition of Speakers)
│ ├── knn_model.py # KNN training + prediction wrapper
│ ├── spotify_client.py # spotipy wrapper for track metadata
│ ├── balancer.py # ties zones + KNN + features together
│ └── visualizer.py # matplotlib bar chart of EQ recommendations
├── tests/
│ ├── test_balancer.py
│ └── unit/test_pipeline.py
└── planning/
├── PROPOSAL.txt
└── ARCHITECTURE.txt
Michael M — data + ML side
src/speaker.py,src/zone.py,src/knn_model.pydata/speakers.csv,data/training_data.csv(built from listening sessions on the basement testbed)- KNN + speaker/zone tests
Michael S — Spotify, orchestration, UI
src/spotify_client.py,src/balancer.py,src/visualizer.pymain.pyinteractive loop- Spotify dev app +
.envsetup - Spotify / I/O tests
From the repo root:
python -m pip install -r requirements.txtThat installs pandas, numpy, scikit-learn, matplotlib, spotipy, python-dotenv, and pytest.
The Spotify portion uses the Web API search endpoint to look up track metadata (title, artist, album, release date, duration, Spotify URL). We do not use the deprecated Audio Features endpoint — instead, the audio features used for KNN come from data/song_features.csv, which we precomputed for the songs we trained on.
To get the Spotify part working you need your own developer credentials:
- Go to https://developer.spotify.com/dashboard and log in.
- Click Create app. Name it whatever (e.g.
Audio Balancing System). - For API/SDK, pick Web API.
- If it asks for a redirect URI, enter
http://127.0.0.1:3000. - Open the app's settings and copy the Client ID and Client Secret.
- In the project root, make a file called
.env:
SPOTIPY_CLIENT_ID=your_client_id_here
SPOTIPY_CLIENT_SECRET=your_client_secret_here.env is in .gitignore so it won't get pushed.
To make the program recommend EQ for a new song, add one row to data/song_features.csv. You do not need to add that song to data/training_data.csv unless you also want the model to learn from your own hand-tuned EQ ratings for that song. In other words, song_features.csv controls which songs can be predicted, while training_data.csv controls what the KNN model learns from.
Use Chosic's song analyzer as the preferred source for feature values: https://www.chosic.com/music-genre-finder/. Chosic is useful because it reports Spotify-style audio features such as energy, danceability, acousticness, instrumentalness, valence, speechiness, and tempo. These values are based on Spotify's audio analysis data, which normal Spotify Web API developers no longer have direct access to through the old Audio Features endpoint.
Keep the CSV song title and artist as clean, canonical names. The program can handle common Spotify metadata variants like radio edits, fuller artist names, and classical catalog numbers.
Example row format:
song_title,artist,energy,danceability,tempo,acousticness,instrumentalness,valence,speechiness
Black Hole Sun,Soundgarden,0.83,0.35,105.0,0.00,0.00,0.15,0.04python main.pyYou'll get prompted for a song name (and optionally an artist). The program:
- Searches Spotify for the track and prints what it matched.
- Looks up that song's loudness/energy/etc. from
song_features.csv. - Runs each zone's trained KNN to predict bass and treble gains.
- Prints the recommendations and pops up a matplotlib bar chart.
Type quit (or q) when you're done.
To sanity-check Spotify auth without running the whole thing:
python -c "from dotenv import load_dotenv; load_dotenv(); from src.spotify_client import SpotifyClient; c=SpotifyClient(); print(c); print(c.get_song_metadata('Blinding Lights', 'The Weeknd'))"If your creds are good, you'll see SpotifyClient(authenticated=True) and a dict of track metadata.
We use pytest. From the repo root:
python -m pytestTest files live in tests/. They cover the Speaker/Zone classes, the KNN training/prediction wrapper, the balancer orchestration, and the Spotify client (with mocked responses so the tests don't hit the network).
Part 1 — fundamentals
- Classes:
Speaker,Subwoofer(Speaker)(inheritance),SpeakerZone(composition of Speakers),SpotifyClient. - Functions: every module is broken into small functions with docstrings — see
src/balancer.pyandmain.pyfor the orchestration ones. - Exception handling:
main.pycatchesFileNotFoundError,ImportError,ValueError, andConnectionErroraround startup and the user loop.src/spotify_client.pyraisesConnectionError/ValueErrorfor auth and lookup failures.src/knn_model.pyvalidates inputs and raisesValueErroron bad training data. - Data I/O: CSV loading via pandas in
main.py,src/balancer.py, andsrc/speaker.py. Environment variables loaded from.envvia python-dotenv. - Loops: the interactive
while Trueloop inmain.py, plus zone-iteration loops insrc/balancer.py. - Libraries: pandas, numpy, scikit-learn (
KNeighborsRegressor), matplotlib, spotipy, python-dotenv. - Docstrings: every public class, function, and module has one.
- README: this file.
Part 2 — advanced features (need at least four; we have more)
- List/dict comprehensions — used throughout
src/balancer.pyandsrc/zone.pyfor building zone lists and recommendation dicts. - Operator overloading —
Speaker.__str__andSpeaker.__eq__(andSubwooferoverrides) insrc/speaker.py. - Inheritance —
SubwooferextendsSpeakerand overridesbass_capability/treble_capability/__str__. - Built-in modules —
pathlib.Pathfor cross-platform file paths,__future__annotations,typinghints. - Third-party advanced libraries — scikit-learn for ML, matplotlib for visualization, spotipy for the Spotify Web API.
- Unit testing with pytest including mocking for the Spotify client.
Startup error: Training data file not found— make sure you're runningpython main.pyfrom the project root, not from insidesrc/.- Spotify auth fails — double-check
.envis in the project root and the variable names are exactlySPOTIPY_CLIENT_ID/SPOTIPY_CLIENT_SECRET. No quotes around the values. Song not found in features CSV—song_features.csvonly contains the songs we precomputed features for. Try one of those (see the file for the full list) or add your own row.- matplotlib window doesn't appear — on some systems you may need a GUI backend.
pip install pyqt5usually fixes it on Windows.