Moodra is a Python-based music mood detection app that analyzes an uploaded audio file and predicts the most likely emotion label for the clip. It combines a Flask web app, a PyTorch classifier, and Librosa-based audio feature extraction to turn a song snippet into a mood prediction plus feature-level analysis.
The repository also includes utilities for preparing training data, retraining the model, and validating that the packaged artifacts load correctly. In practice, this project is organized around three flows:
- Run the web app and upload audio for prediction.
- Prepare and retrain the model from labeled WAV files.
- Verify the model, encoder, and import paths before deployment.
The main Flask app accepts an audio file on the /predict endpoint, saves it under static/uploads/, calls the prediction pipeline, and returns JSON containing:
- the predicted mood
- prediction confidence
- a probability breakdown for every known label
- derived audio features such as tempo, key, energy, valence, spectral centroid, spectral rolloff, and zero-crossing rate
- a small set of recommended track placeholders derived from the predicted mood
The browser UI in templates/homepage.html is a single-page upload experience that lets users add multiple audio files, play previews, and analyze each file individually or in bulk.
.
|-- app.py # Flask web app and /predict endpoint
|-- predict_mood.py # Model loading, feature extraction, prediction
|-- music_mood_therapy_prototype.py # Dataset, classifier, and training loop
|-- retrain_model.py # Retrain the classifier from data/metadata.csv
|-- convert_mp3_to_wav.py # Convert MP3 training audio to WAV and generate metadata
|-- debug_app.py # Local debugging helper for import/path checks
|-- sanity-check.py # Lightweight environment sanity checks
|-- test_imports.py # Import verification script
|-- test_model_loading.py # Validates model.pth and label_encoder.pkl loading
|-- test_paths.py # Confirms absolute path resolution for artifacts
|-- FIX_502_ERROR.md # Notes for troubleshooting Python/runtime startup issues
|-- Procfile # Gunicorn entrypoint for hosted environments
|-- requirements.txt # Python dependencies
|-- model.pth # Trained PyTorch model weights
|-- label_encoder.pkl # Saved label encoder for class mapping
|-- data/
| |-- metadata.csv # Training metadata: file_path,mood
| |-- mp3audio/ # Source MP3 training files
| `-- wavaudio/ # Converted WAV training files
|-- static/
| `-- uploads/ # Uploaded audio files for prediction
`-- templates/
`-- homepage.html # Frontend UI
- The user uploads an audio file in the browser.
- Flask stores the file in
static/uploads/. predict_mood.pyloadsmodel.pthandlabel_encoder.pklwith a cached artifact loader.- The file is converted into MFCC features via
extract_features()inmusic_mood_therapy_prototype.py. - The classifier returns a label distribution and the top prediction.
- Additional descriptive audio features are extracted with Librosa and returned alongside the prediction.
The classifier is a small feed-forward neural network defined in music_mood_therapy_prototype.py:
- input layer: 40 MFCC features
- hidden layer 1: 128 units with ReLU and dropout
- hidden layer 2: 64 units with ReLU
- output layer: one neuron per emotion class
The current artifact loader in predict_mood.py is defensive: it checks the saved encoder first, then the checkpoint, and can partially load the model if the final layer size does not match exactly.
The app combines two views of the input audio:
- MFCC features for the classifier input
- analysis metrics for display and debugging
The analysis metrics currently include tempo, key, energy, valence, spectral centroid, spectral rolloff, and zero-crossing rate.
The training set currently contains these mood labels:
- Angry
- Calm
- Happy
- Romantic
- Sad
- Zoned
If you retrain the model with a different label set, the saved label_encoder.pkl becomes the source of truth for label ordering.
The project targets Python 3 and depends on the packages listed in requirements.txt, including:
- Flask
- PyTorch and Torchaudio
- Librosa
- NumPy, Pandas, SciPy
- scikit-learn
- Pydub
- Gunicorn for hosted deployment
cd C:\External_Files\GitHub\Hz
py -3.11 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pippip install -r requirements.txtThe app expects these files in the repository root:
model.pthlabel_encoder.pkl
If either file is missing, prediction will fail until the model is retrained or the artifact is restored.
Start the Flask server with:
python app.pyThen open the local Flask address printed in the terminal, usually http://127.0.0.1:5000/.
The browser UI lets you:
- choose audio files through a file picker
- drag and drop audio into the upload zone
- preview the uploaded audio before analysis
- analyze one file or all files in the queue
- see the predicted mood and supporting probabilities
Send multipart form data with an audio field.
Example using curl:
curl -X POST http://127.0.0.1:5000/predict \
-F "audio=@path/to/song.wav"Success response fields include:
successmoodconfidenceprobabilitiesdetailsaudio_url
If no file is provided, the endpoint returns a 400 error. If prediction fails, the app returns a 500 error with a helpful message and, in debug mode, a traceback.
Use retrain_model.py when you have updated labeled training data in data/metadata.csv.
python retrain_model.pyThis script:
- loads the dataset from
data/metadata.csv - builds a
MusicDataset - trains a
MoodClassifier - saves updated artifacts to
model.pthandlabel_encoder.pkl - prints a quick prediction sanity check after training
The default training loop in music_mood_therapy_prototype.py uses CrossEntropyLoss and Adam with a learning rate of 0.001.
convert_mp3_to_wav.py is a helper for turning MP3 source audio into WAV files and generating metadata.
It:
- scans a source directory for MP3 files
- converts each file to WAV with
pydub - infers a mood label from the filename when possible
- writes
data/metadata.csvin the format expected by the dataset loader
The script is intended as a dataset preparation utility. In the current repository, the source files live under data/mp3audio/ and the converted WAV files live under data/wavaudio/.
The repository includes a few lightweight validation scripts:
python test_imports.pychecks that the major dependencies and internal modules import correctly.python test_model_loading.pyverifies thatmodel.pthandlabel_encoder.pklcan be loaded.python test_paths.pyprints the resolved artifact locations for path debugging.python sanity-check.pyis a lightweight environment check.
These scripts are useful before deploying or after changing environments because the app depends on a working Python stack, valid model artifacts, and correct file paths.
The repository includes a Procfile configured for Gunicorn:
web: gunicorn app:app
That means the app can be deployed to a hosting platform that understands Procfiles and runs a WSGI app. Before deployment, make sure:
requirements.txtis installed cleanlymodel.pthandlabel_encoder.pklare included in the deployed package- the runtime can write to
static/uploads/
If prediction fails with a missing artifact error, check that model.pth and label_encoder.pkl are in the repo root. predict_mood.py searches multiple locations, but the root directory is the expected default.
The Flask app resolves static/uploads/ relative to app.py and creates the directory if needed. If uploads fail in a hosted environment, verify the process has write permissions.
FIX_502_ERROR.md documents a known class of environment problems where Python itself is broken or partially corrupted. If Flask crashes before requests reach /predict, follow the cleanup and virtual environment guidance in that file.
If the app stops importing correctly after environment changes, run test_imports.py first. It is the fastest way to identify a broken package installation.
- Keep
metadata.csvaligned with the actual labels used to train the model. - Update
label_encoder.pklwhenever the label set changes. - Do not assume the saved classifier output size is fixed;
predict_mood.pyalready handles some checkpoint/encoder mismatches. - Treat
static/uploads/as a runtime scratch directory, not as source data.