Skip to content

Repository files navigation

VocalWell Backend

A Flask API that turns a .wav recording into a voice-pathology screening result: acoustic feature extraction, an LSTM classifier, a clinical-style PDF report, and a SQLite history of every analysis.

Not a diagnostic device. The model hasn't been clinically validated — treat the output as a screening signal, not medical advice.

How a request moves through it

  1. UploadPOST /analyze receives a .wav over multipart/form-data.
  2. Feature extractionmodel.py uses Librosa to pull F0 (YIN), Jitter, Shimmer, HNR, LPC formants, and 13 MFCCs, and builds the mel-spectrogram the model actually consumes.
  3. Inference — the mel-spectrogram goes into lsm_model3, a pre-trained bidirectional LSTM, loaded once at process start rather than per-request. Output is a 3-class softmax: Healthy / Laryngitis / Vocal Polyp. Full architecture and training details: MODEL_CARD.md.
  4. Reporting — ReportLab and Matplotlib turn the raw features and confidence scores into a 3-page PDF (waveform, spectrogram, formant chart) plus a standalone confidence bar chart.
  5. Persist and respond — the classification, risk level, and file paths are written to SQLite, and the same JSON goes back to the caller.

Stack

Framework Flask + Flask-CORS
Audio / DSP Librosa
Model TensorFlow / Keras (lsm_model3, bidirectional LSTM)
Reporting ReportLab, Matplotlib
Storage SQLite (stdlib sqlite3)
Runtime Docker, deployed on Hugging Face Spaces

Running it

Needs Python ≤3.11 (TensorFlow 2.15's ceiling) and system audio libs:

brew install libsndfile ffmpeg          # macOS
sudo apt-get install libsndfile1 ffmpeg # Ubuntu/Debian
git clone https://github.com/auraCodesKM/vocalB.git
cd vocalB
python3.11 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
python app.py

Listens on http://localhost:7860. Or via Docker: docker build -t vocalwell-backend . && docker run -p 7860:7860 vocalwell-backend.

Endpoints

Method Path Does
POST /analyze Upload a .wav, get a diagnosis back
GET /analyses List past analyses, newest first
GET /analyses/{id} One analysis by id
GET /plot/{name} The confidence bar chart (PNG)
GET /report/{name} The PDF report — add ?download=true to force a download
GET /health Liveness check for Railway/Render/HF Spaces

POST /analyze is the one that matters — send the file, get this back:

curl -X POST http://localhost:7860/analyze -F "audio=@your_voice.wav"
{
  "Prediction": "Your voice shows moderate indication of Vocal Polyp",
  "Class": "Vocal Polyp",
  "RiskLevel": "moderate",
  "PlotPath": "analysis_plot_1786688281.png",
  "ReportPath": "voice_analysis_report_1786688281.pdf"
}

Everything else — /analyses, /plot, /report — just hands back what /analyze already pointed at.

The model

lsm_model3 is a bidirectional LSTM — InputLayer(128, 128) → Bidirectional(LSTM(128)) → Dense(3, softmax), 263,939 parameters (~1MB) — trained separately from this repo and checked in as a finished artifact, the way most serving repos keep training code and datasets out of the production path. Full layer breakdown, the compiled optimizer/loss config, and known limitations: MODEL_CARD.md.

Layout

vocalB/
├── app.py            Flask routes, CORS, logging, DB wiring
├── db.py             SQLite: init / insert / list / get
├── model.py          feature extraction, inference, PDF generation
├── MODEL_CARD.md      architecture, training config, known issues
├── requirements.txt
├── Dockerfile
├── Procfile            gunicorn, for Heroku/Render
├── railway.toml
├── render.yaml
├── assets/             logo used in the PDF header
├── lsm_model3/          the trained LSTM weights
├── plots/                56 sample confidence charts, checked in for reference
└── reports/               56 sample PDF reports, checked in for reference

Why it's built this way

Flask, not FastAPI. There's exactly one endpoint doing real work, and it's CPU-bound on TensorFlow inference the whole time — nothing here is waiting on I/O that async would help with. Flask is the simpler tool for that shape of problem. What you lose: FastAPI's free OpenAPI docs.

SQLite, not Postgres. The history table is a single-instance log nobody else writes to concurrently — SQLite covers that with the Python standard library and no extra container. If a history write fails, it's logged and swallowed; the diagnosis already succeeded and that's the part that matters.

Model loaded once. model.py loads lsm_model3 at import time, not inside the request handler — retraining per-request isn't realistic, and reloading the weights on every call would dominate response time.

Deploying

Hugging Face Spaces (where this actually runs): git remote add hf https://huggingface.co/spaces/<user>/<space> && git push hf main. The Dockerfile handles system deps and starts Gunicorn on 7860.

Railway / Render: railway.toml / render.yaml are already set up — health check on /health, persistent volumes for reports/, plots/, data/.


Frontend: auraCodesKM/VocalF — Next.js client for recording, dashboard, and reports.

About

Flask backend for AI-assisted voice pathology screening using acoustic feature extraction, a bidirectional LSTM classifier, automated clinical-style PDF reports, and persistent analysis history.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages