Prosodiff
Same words. Different delivery—made visible.
Prosodiff is a small, training-free CLI for comparing two to four recordings of the same speaker saying the same text in different ways. It exports a publication-grade 4:5 figure and a versioned JSON record of explicit delivery-attribute deltas—without predicting emotions or collapsing the comparison into a single score.
Built as a side tool for retrieval-augmented expressive TTS research (ProsodyRAG) at Universität Osnabrück.
Try the complete no-audio demonstration without cloning or installing:
uvx prosodiff demo --output prosodiff-card.png
Or install the command permanently:
pip install prosodiff
What it reports
- utterance duration;
- median F0 and robust pitch span (interquartile range in semitones);
- recorded RMS level and within-take energy dynamics;
- energy-defined internal pause count, time, and share;
- pYIN coverage, clipping, level, pitch-boundary, and pause-sensitivity warnings;
- every unordered pair as an explicit B − A attribute delta.
Prosodiff does not infer emotion, speaking rate, linguistic boundaries, listener response, naturalness, vocal effort, or an overall prosody score.
Local browser interface
Start the private, loopback-only interface:
uv run prosodiff ui
Prosodiff opens a browser page where you can record two to four takes directly from the microphone, replay or redo each take, edit labels, and generate the same 4:5 card and JSON as the CLI. Each take gets a nonblocking level/clipping check before analysis, and the page recommends a redo when the recorded level is below −35 dBFS, clipping is detected, the device changes, or a take differs from the reference by more than 6 dB. Allow microphone access when prompted. Selecting existing WAV files remains available as a secondary option.
The server binds only to 127.0.0.1; it has no telemetry, CDN assets, or external API calls. Live captures are encoded as mono PCM WAVs in the browser. Recordings use random temporary filenames on the server and are deleted immediately after the analysis succeeds or fails. Generated PNG/JSON results are deleted when the server stops. Do not modify the code to expose this development interface on 0.0.0.0 or deploy it publicly.
If a browser does not open automatically, copy the local URL printed in the terminal. Use uv run prosodiff ui --no-open-browser --port 7860 when you want a fixed port.
Fresh-clone quickstart
Requires Python 3.10+ and uv.
Install in one command:
uv sync --extra dev
Generate the complete synthetic demonstration in one command:
uv run prosodiff demo --output docs/prosodiff-card.png
The command creates docs/prosodiff-card.png and docs/prosodiff-card.json. No real audio is committed; the demo signals are deterministic synthetic fixtures and are labelled accordingly in the figure and JSON.
The first run can take roughly one to two minutes while librosa/Numba and Matplotlib build local caches; subsequent short-utterance runs are faster.
Exact usage example
Record the same sentence back-to-back, then run:
uv run prosodiff compare calm.wav encouraging.wav --label Calm --label Encouraging --text "Heute schaffen wir das gemeinsam." --output prosodiff-card.png
This writes prosodiff-card.png plus the adjacent prosodiff-card.json explicit delivery-attribute delta schema. Pass two, three, or four WAV files; repeat --label once per file.
Recording protocol
Recorded-level comparisons are only interpretable when every take uses:
- the same speaker and exact text;
- the same device, room, microphone direction, gain, and distance;
- one back-to-back recording session;
- disabled auto-gain, noise suppression, “voice enhancement,” and other automatic processing where possible;
- unclipped, unnormalized PCM WAV files.
The tool cannot verify these assumptions. It records them in the JSON and keeps microphone-sensitive measures visibly marked in the figure.
Method
Prosodiff resamples analysis copies to 22.05 kHz but never peak-normalizes, denoises, pre-emphasizes, repairs pitch, or time-warps audio.
- Pitch:
librosa.pyin, 50–600 Hz by default. F0 selection follows pYIN's Viterbi-decoded voiced flag inside the detected utterance and outside pauses; voiced probability is retained as a diagnostic and controls only pale/solid contour emphasis. Summaries require at least 20 decoded frames and 25% eligible-frame coverage. Median F0 is compared on a take-balanced shared semitone reference. Pitch span is the IQR, reducing sensitivity to isolated extremes without silently deleting octave jumps. - Recorded energy: time-domain RMS in dBFS. The absolute active-frame median is retained for between-take deltas; contour display is relative to the pooled take median. This is recorded level—not calibrated SPL, loudness, intensity, or vocal effort.
- Energy-defined pauses: an adaptive RMS threshold followed by removal of sub-50 ms energy islands and filling of sub-150 ms gaps. Internal gaps use a nominal 150 ms cutoff with one-hop (11.6 ms) boundary quantization. The detector repeats at ±3 dB and warns when the result changes materially.
- Time: contours use raw seconds from the detected utterance onset. They are explicitly not phonetic alignment. Normalized coordinates are stored in JSON only as a coarse linear-time view and must not be treated as phone correspondence.
All calculations are descriptive. A difference between two recordings is not evidence that listeners perceived a different intention.
JSON contract
The machine-readable output uses schema prosodiff.explicit-delivery-attribute-delta, version 0.2.0. It contains:
- fixed analysis parameters and assumptions;
- input metadata, utterance spans, pitch/energy/pause attributes, and quality warnings;
- auditable raw-time and normalized-time contours, including candidate F0, decoded voiced flags, eligibility masks, and probability diagnostics, with unavailable values encoded as JSON
null; - all unordered input pairs in order, always identified as
b_minus_a; - pair-level reliability fields and confound warnings.
The schema is intentionally multidimensional. There is no aggregate “expressivity” or quality score.
Honest approximations
- The live recorder first requests disabled echo cancellation, noise suppression, and auto-gain as exact constraints, then falls back when a browser cannot satisfy them. Reported track settings and privacy-preserving capture provenance are stored in JSON, but the browser, operating system, or audio device can still omit or misreport settings; use the WAV upload route for tightly controlled recording chains.
- pYIN probabilities are an engineering confidence signal, not calibrated certainty; tracker errors can remain.
- Energy measurements are dominated by recording chain changes if the protocol is not controlled.
- Energy-defined gaps are not syntactic, pragmatic, or linguistic pause labels.
- Same-speaker and same-text assumptions are supplied by the user and are not verified.
- Short-utterance acoustic deltas do not establish population effects or listener cognition.
Development
Run the synthetic test suite:
uv run pytest
The tests generate audio inside temporary directories. No recordings or research datasets are stored in the repository.
Roadmap
- Add optional transcript-aware phone alignment while retaining raw-time views.
- Add batch manifests for ProsodyRAG reference-versus-synthesis evaluation and Flowent tutor-voice QA.
- Validate acoustic deltas against preregistered listener judgments rather than assuming perceptual relevance.
Author and license
Created by Abdalla Sera (asera-1). Released under the MIT License.
Prosodiff’s authored source is MIT-licensed. Installed dependencies retain their own licenses; the locked librosa audio stack includes dynamically used or installed LGPL components such as libsndfile and python-soxr. They are not vendored or redistributed by this repository.
Metadata
Release files for prosodiff 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| prosodiff-0.2.0.tar.gz | 846.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| prosodiff-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 900.4 kB
Release files / prosodiff-0.2.0.tar.gz
| Download URL | prosodiff-0.2.0.tar.gz |
|---|---|
| Size | 846.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e1e9682595fed93feddb1b09474e1f131dd519e57553845f6d1e062af3056dde
|
|
BLAKE2b-256 checksum How to use checksums |
0057205ab6697122190e2b2b19da483c33d4f384f7cb4e131967d103f4665e67
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.6.2
|
Release files / prosodiff-0.2.0-py3-none-any.whl
| Download URL | prosodiff-0.2.0-py3-none-any.whl |
|---|---|
| Size | 53.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ec774658bcbf993da2773f933fa2e57c7193d2ba1849d2148a54ab39ae1f2433
|
|
BLAKE2b-256 checksum How to use checksums |
bea08a47fe9001309129f89f05ad4e72bdad4229b039f223d02042d0eb847954
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.6.2
|