jessy8051/acapella-arranger
Aca-Adapt
Convert any song into an editable four-part SATB (Soprano, Alto, Tenor, Bass) a cappella arrangement. Upload an audio file and download a MIDI or MusicXML score that opens in any notation software for further editing.
What it Does
Aca-Adapt is a multi-stage machine-learning pipeline that takes an audio file (MP3 / WAV / MP4) and produces an editable four-part vocal arrangement as MIDI and MusicXML. The system isolates the lead vocal with HT-Demucs, transcribes it to a monophonic lead line with CREPE, and then uses a custom Transformer + LSTM hybrid model — pretrained on Bach chorales and fine-tuned on the jaCappella corpus — to predict harmonizing soprano, alto, tenor, and bass lines. A rule-based voice-leading post-processor ensures notes stay in human vocal ranges and filters impossible intervals. The arrangement is delivered through a deployed Streamlit web application where users upload audio and download a .mid or .musicxml file ready to open in MuseScore, Finale, Sibelius, or any notation software.
Quick Start
# 1. Clone and enter
git clone <repo-url> && cd "Acapella Arranger"
# 2. Create env and install
python3.10 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# 3. Download data
python scripts/download_data.py
# 4. Run the Streamlit app locally
streamlit run src/app/main.pyFull setup instructions (GPU, training, reproducing evaluation): see SETUP.md.
Video Links
- Demo video (non-technical): TBD — will be linked before 2026-04-26
- Technical walkthrough: TBD — will be linked before 2026-04-26
Evaluation
Metrics reported on the jaCappella held-out test set (15% of songs, song-level split).
Ablation axes, error analysis, and qualitative examples are written up in reports/ and walked through on video. See notebooks/04_evaluation.ipynb for the full reproduction pipeline.
Individual Contributions
- [Partner 1 name]: TBD — e.g., custom hybrid model architecture, training pipeline, data augmentation + tokenization, evaluation metrics, error analysis
- [Partner 2 name]: TBD — e.g., Streamlit UI, production hardening (caching, rate limiting, logging), deployment, Demucs + CREPE integration, UX
Both partners contributed equally to project design, PRD, ablation design, and the final write-up.
Production Hardening
The deployed Streamlit app is hardened for a public Hugging Face Space rather than treated as a one-off demo. Concrete measures, each with a file:line pointer:
- Caching at three scopes. Process-wide checkpoint caching via
@st.cache_resource(src/app/main.py:_ensure_checkpoint) downloads the 90 MB model weights once per container; upload probing via@st.cache_data(src/app/main.py:_probe_duration) avoids re-decoding an audio file on every Streamlit rerun; per-session result caching viast.session_state(src/app/main.py— the_resultkey) returns instantly when Generate is clicked on an unchanged(file, options)pair. - Per-session rate limiting. Sliding-window limiter in
src/app/main.py:_rate_limit_checkblocks a single visitor from saturating the 2-vCPU free tier. Defaults to 3 generations per 10 minutes; override withACA_ADAPT_RATE_LIMIT_MAX/ACA_ADAPT_RATE_LIMIT_WINDOW_SECenv vars. - Structured logging.
_configure_logginginsrc/app/main.pyinstalls a key=value handler on the root logger. Every event (upload,generate_start,generate_ok,generate_fail,rate_limited,probe_error) is tagged with a short session id and emitted as onegrep-able line. Running tallies of uploads / successes / failures are included on eachgenerate_okso the container log contains a rolling view of service health. - Explicit error handling on every failure mode.
src/app/main.py:maincatchesFileNotFoundError(checkpoint missing),RuntimeError(pipeline-internal), and a catch-allExceptionpath — each surfaces a distinct UI message and logs agenerate_failevent with a classified reason. Module-loadImportErrors are caught separately and rendered as a structured diagnostic banner (_render_env_error) instead of a stack trace. - Monitoring via log-scrapeable events. Because every event line is key=value, grader-friendly queries work with plain shell tools, e.g.
grep 'event=generate_ok' logs | awk -F'elapsed_sec=' '{print $2}' | awk '{print $1}'yields a distribution of end-to-end generation latencies. The container log on HF is the monitoring backend — no external dashboard required for a demo-tier deploy. - Environment-driven config. Model repo (
ACA_ADAPT_HF_REPO_ID), checkpoint filename (ACA_ADAPT_HF_FILENAME), Demucs model (ACA_ADAPT_DEMUCS_MODEL), and rate-limit parameters are all env vars with documented defaults insrc/app/main.py. Swapping environments needs no code change. - Reproducible container image. The
Dockerfilepinstorch==2.3.1 torchaudio==2.3.1from the CPU wheel index with a post-install force-reinstall + version assertion so a broken resolver fails the build loudly rather than producing a mismatched runtime. Deployment is driven byscripts/deploy_to_hf_space.sh, which pushes an orphan snapshot that excludes binary rubric evidence HF would otherwise reject.
Project Structure
.
├── src/
│ ├── data/ # jaCappella + JSB loading, tokenization, augmentation
│ ├── models/ # Hybrid Transformer+LSTM, baseline Transformer
│ ├── pipeline/ # Demucs + CREPE end-to-end inference
│ ├── training/ # Train loop, optimizer, scheduling
│ ├── postprocess/ # Voice-leading rules, music21 export
│ ├── eval/ # Metrics, ablation, error analysis
│ └── app/ # Streamlit web UI
├── notebooks/ # Exploration, training, ablation, evaluation
├── configs/ # Training hyperparameter YAMLs
├── scripts/ # Data download and utility scripts
├── reports/ # Error analysis, evaluation write-ups
├── data/ # (git-ignored) raw + processed data
├── models/ # (git-ignored) checkpoints
└── outputs/ # (git-ignored) generated MIDI + logsDocumentation
- docs/PRD.md — product requirements, architecture, scoring plan
- SETUP.md — installation, training, reproduction
- ATTRIBUTION.md — pretrained models, datasets, libraries, AI-tool usage
License
MIT for project code. Dataset and model licenses listed in ATTRIBUTION.md.
