Metadata-Version: 2.4
Name: resonator
Version: 1.1.0
Summary: Game-audio asset automation pipeline: silence strip, EBU R128 loudnorm, naming-convention parsing, Wwise/UE5 manifest emission, DSP audit, and WAAPI integration.
Author: Amir Pourtaghavy
License: MIT
Project-URL: Homepage, https://github.com/Bloody-Crow/Resonator
Project-URL: Documentation, https://github.com/Bloody-Crow/Resonator#readme
Project-URL: Repository, https://github.com/Bloody-Crow/Resonator
Project-URL: Issues, https://github.com/Bloody-Crow/Resonator/issues
Project-URL: Changelog, https://github.com/Bloody-Crow/Resonator/blob/main/CHANGELOG.md
Keywords: game-audio,wwise,unreal-engine,localization,loudness,ebu-r128,audio,dsp,automation,pipeline,ffmpeg,loudnorm,audio-programming,waapi
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Sound/Audio :: Analysis
Classifier: Topic :: Multimedia :: Sound/Audio :: Editors
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: watchdog>=4.0
Requires-Dist: click>=8.1
Requires-Dist: pydub>=0.25
Requires-Dist: soundfile>=0.12
Requires-Dist: numpy>=1.26
Requires-Dist: pyloudnorm>=0.1
Requires-Dist: python-dotenv>=1.0
Requires-Dist: rich>=13.7
Provides-Extra: wwise
Requires-Dist: waapi-python>=1.0; extra == "wwise"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: ruff>=0.5; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"
Requires-Dist: hypothesis>=6.0; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=4.0; extra == "dev"
Dynamic: license-file

# Resonator — Game-Audio Asset Automation Pipeline

> A daemon + CLI that watches an ingest directory for incoming WAV assets,
> strips silence, normalizes loudness to EBU R128, parses complex naming
> conventions, and emits engine-ready manifests and packages for **Wwise**
> and **Unreal Engine 5** bulk import. Includes DSP auditing, SQLite
> processing history, asset-class presets, and direct WAAPI integration.

**Author:** Amir Pourtaghavy — Senior Audio Engineer & Studio Systems
Technician (14+ yrs) transitioning to Technical Audio Programmer / Audio Lead.

[![PyPI version](https://img.shields.io/pypi/v/resonator.svg)](https://pypi.org/project/resonator/)
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![CI](https://github.com/Bloody-Crow/Resonator/actions/workflows/ci.yml/badge.svg)](https://github.com/Bloody-Crow/Resonator/actions/workflows/ci.yml)
[![Tests](https://img.shields.io/badge/tests-187%20passing-brightgreen.svg)](#)

---

## Install

```bash
# From PyPI (recommended)
pip install resonator

# With Wwise WAAPI support (optional)
pip install resonator[wwise]

# Or from source
git clone https://github.com/Bloody-Crow/Resonator.git
cd Resonator
pip install -e .
```

**System dependency:** ffmpeg + ffprobe must be on PATH.
- Linux: `sudo apt install ffmpeg`
- macOS: `brew install ffmpeg`
- Windows: `choco install ffmpeg`

Verify your setup:
```bash
resonator doctor
```

---

## Why this exists — the real-world bottleneck

Game localization at scale is a **pipeline** problem, not a craft problem.
A single AAA title ships 40,000+ voice lines across 12+ languages. The
bottleneck is never the recording — it's the **per-asset preparation** between
the booth and the engine:

1. Every WAV arrives with inconsistent silence, loudness, and sample rates.
2. Filenames encode the routing contract (`VO_Hero_Quest01_Line03_v1_EN`)
   but parsing that by hand into Wwise/Unreal is hours of clerical work.
3. A single corrupted or mis-named file breaks a bulk import.

Resonator removes all three. Drop files into `ingest/`; out come
**loudness-normalized, silence-trimmed, correctly-named assets** plus
engine-ready manifests — with zero manual tagging.

---

## Quick start

```bash
# Scaffold directories
resonator init

# Drop WAV files into the ingest directory
cp VO_Hero_Quest01_Line03_v1_EN.wav data/ingest/

# One-shot batch process
resonator process --ingest data/ingest

# Or start the long-running watcher daemon
resonator run
```

---

## CLI reference

| Command | Purpose |
|---|---|
| `resonator init` | Scaffold the directory layout + print a sample `.env` |
| `resonator run` | Long-running watcher daemon (blocks until SIGINT/SIGTERM) |
| `resonator process` | One-shot batch: scan → process → manifest → exit |
| `resonator validate` | Dry-run: naming-convention + dependency checks, no audio touched |
| `resonator analyze` | DSP audit: loudness, clipping, DC offset, format — no processing |
| `resonator doctor` | Health-check: dependencies, directories, config, end-to-end test |
| `resonator history list` | Query the processing history database |
| `resonator history failed` | List failed processing attempts |
| `resonator history report` | Loudness compliance report |
| `resonator export` | Re-export a manifest for a specific engine (wwise/ue5/generic) |
| `resonator wwise push` | Push a manifest into Wwise Authoring as Voice objects |
| `resonator wwise reconcile` | Compare a manifest against the current Wwise object tree |
| `resonator presets` | List available asset-class and naming-convention presets |

Global flags: `-v/--verbose` (DEBUG logs), `--plain-logs` (human-readable file logs).

---

## Feature overview

### 1. Directory watcher & ingestion
- `watchdog` (inotify/FSEvents) observation with polling fallback
- Write-completion detection: tracks mtime+size, waits for stability cooldown
- Thread-safe queue decouples ingest latency from DSP latency

### 2. Audio signal processing & normalization
- Two-pass EBU R128 loudnorm (measure → linear gain → encode)
- `silenceremove` strips head/tail silence, preserves interior pacing
- 48k/16-bit PCM canonicalization for Wwise/UE5
- Atomic writes (`.part` + `os.replace`) — crashes never leave corrupt files

### 3. Filename parsing & metadata extraction
- Regex-driven: `VO_Hero_Quest01_Line03_v2_EN.wav`
- Typed, frozen `AssetMetadata` dataclass
- Case-insensitive — `vo_hero_...` and `VO_Hero_...` group together

### 4. Asset-class presets
Different asset types have different loudness standards. Use `--preset`:

| Preset | LUFS | TP | Channels | Use case |
|---|---|---|---|---|
| `dialogue` | -23 | -1 | mono | Voice-over (EBU R128) |
| `music` | -14 | -1 | stereo | Streaming music |
| `broadcast` | -18 | -1 | stereo | Broadcast music |
| `sfx` | -18 | -3 | mono | Sound effects (peak-driven) |
| `cinematic` | -27 | -2 | stereo | Theatrical/cutscene |
| `ui` | -20 | -3 | mono | UI clicks/notifications |

### 5. Naming-convention presets
Ship presets for common studio schemas:

| Convention | Example | Separator |
|---|---|---|
| `default` | `VO_Hero_Quest01_Line03_v2_EN.wav` | underscore |
| `ubisoft` | `VO_Hero_Quest01_Line03_v2_EN.wav` | underscore |
| `naughty-dog` | `VO-Hero-Q01-L03-v2-EN.wav` | hyphen |
| `ea` | `vo.hero.q01.l03.v2.en.wav` | dot, lowercase |

### 6. DSP analysis & audit
`resonator analyze` produces a per-asset signal report:
- EBU R128 loudness (integrated, true peak, loudness range)
- DC offset detection
- Clipping detection (samples at 0 dBFS)
- Silence ratio estimation
- Format inspection (sample rate, bit depth, channels, codec)
- Warnings for non-standard formats, very loud input, high silence ratio

Outputs `audit.json` (structured) + `audit.md` (human-readable table).

### 7. Game engine integration manifests
- **manifest.json** — full audit trail with loudness measurements
- **localization.csv** — flat CSV for UE5/Wwise import
- **`--export-target wwise`** — Wwise external-sources CSV with language mapping
- **`--export-target ue5`** — UE5 Localization Dashboard CSV + StringTable JSON
- Per-group zips with embedded `_manifest.json` for traceability

### 8. Wwise Authoring API (WAAPI) integration
`pip install resonator[wwise]` enables direct Wwise Authoring control:
- `resonator wwise push --manifest manifest.json` — bulk-create Voice objects
- `resonator wwise reconcile` — compare manifest vs. Wwise object tree
- `--dry-run` uses mock transport for testing without Wwise running
- Language mapping: EN → English(US), FA → Farsi, etc.

### 9. SQLite processing history
Every processed asset is recorded with source hash, loudness, and timestamp:
- **Content-hash idempotency** — skip reprocessing unchanged files
- **Audit trails** — "when did we process this line, and what did we do?"
- **Loudness compliance reports** — `resonator history report`

### 10. System resilience & DevOps
- Quarantine on failure; daemon never crashes on one bad file
- Multi-stage Docker (non-root, `tini`, healthcheck) + docker-compose
- NixOS systemd module with `ProtectSystem=strict`
- Structured JSON logging with rotation + Rich console
- CI: ruff + mypy + pytest (187 tests) across Python 3.11/3.12 + Docker build

---

## Architecture

```
src/resonator/
├── __init__.py          # package marker, version
├── config.py            # frozen PipelineConfig, env loading, validation
├── exceptions.py        # typed exception hierarchy
├── logging_config.py    # structured JSON logging w/ rotation + Rich
├── watcher.py           # watchdog + write-completion detection
├── metadata.py          # regex filename parser → AssetMetadata
├── processor.py         # ffmpeg 2-pass EBU R128 loudnorm + silence strip
├── manifest.py          # manifest.json + localization.csv + zip packaging
├── analysis.py          # DSP audit: loudness, clipping, DC offset, format
├── history.py           # SQLite processing history + idempotency
├── presets.py           # asset-class + naming-convention presets
├── export.py            # engine-specific export (generic/wwise/ue5)
├── waapi_client.py      # Wwise Authoring API client (mock-first)
├── pipeline.py          # orchestrator: worker pool, signals, history
└── cli.py               # click CLI: 12 subcommands
```

### Resilience guarantees

| Failure mode | Behavior |
|---|---|
| File still being written | Stability tracker waits for unchanged mtime+size |
| Corrupted WAV | ProcessingError → quarantined, worker continues |
| Mis-named file | MetadataParseError → quarantined, pattern logged |
| ffmpeg/ffprobe missing | DependencyMissingError at startup, exit non-zero |
| Daemon killed mid-batch | Manifest flushed after every asset (atomic write) |
| Same file re-dropped | Output-exists + content-hash check → skipped (idempotent) |
| inotify unavailable | Automatic polling fallback for restricted mounts |
| Ultra-short / all-silence | Single-pass loudnorm fallback (no -inf crash) |

---

## Configuration

Every parameter is an environment variable (twelve-factor). See
[`.env.example`](.env.example) for the full list. Key parameters:

| Variable | Default | Notes |
|---|---|---|
| `RESONATOR_LUFS_TARGET` | `-23.0` | EBU R128 integrated loudness |
| `RESONATOR_TRUE_PEAK_DBTP` | `-1.0` | True-peak ceiling |
| `RESONATOR_SILENCE_THRESHOLD_DB` | `-50.0` | Below this = "silence" to strip |
| `RESONATOR_SAMPLE_RATE` | `48000` | UE5/Wwise native rate |
| `RESONATOR_CHANNELS` | `1` | Mono VO; set `2` for stereo |
| `RESONATOR_STABILITY_COOLDOWN` | `2.0` | Seconds a file must be unchanged |
| `RESONATOR_MAX_WORKERS` | `4` | Parallel DSP threads |
| `RESONATOR_PACKAGE_BY` | `character` | `character` / `quest` / `language` |
| `RESONATOR_NAMING_PATTERN` | (regex) | Override the naming convention |

---

## Engine integration

### Wwise
- `resonator export --target wwise` — external-sources CSV
- `resonator wwise push` — bulk-create Voice objects via WAAPI
- `resonator wwise reconcile` — compare manifest vs. Wwise tree
- Language mapping: EN → English(US), FA → Farsi, JA → Japanese, etc.

### Unreal Engine 5
- `resonator export --target ue5` — Localization Dashboard CSV + StringTable JSON
- SoundCue paths in `/Game/Audio/VO/<Character>/` convention
- Output WAVs are 48k/16-bit PCM — no resample on import

---

## Development

```bash
pip install -e ".[dev]"
ruff check src tests          # lint
mypy src/resonator            # type-check (strict)
pytest                        # 187 tests
```

---

## License

MIT — see [LICENSE](LICENSE).

---

## Author

**Amir Pourtaghavy** — Tehran, Iran

14+ years in studio infrastructure, FOH engineering, and massive localization
pipelines (1000+ recording sessions, 1000+ dubbed titles, FOH/Studio Lead for
post-rock band *Crows In The Rain*). Worked with Oscar-winning directors
(Asghar Farhadi) and classical masters (Homayoun Shajarian). Transitioning to
Technical Audio Programmer / Audio Lead.
