# Audio generation for Fusion game projects

The Fusion dev VM (`10.3.0.26`, VLAN3) can reach two self-hosted audio boxes.
Use them to generate **voice-over** and **music/SFX** at **dev time**, then
**commit the resulting audio files into the game repo** (`assets/audio/...`),
exactly like we do with sprites. **Do not call these services at game runtime** —
ship the baked audio files.

## Helper: `fusion-audio`

Installed at `/usr/local/bin/fusion-audio`. Wraps both services, decodes the
response, and writes a real audio file (transcodes to `.wav` via ffmpeg when the
service returns MP3).

```bash
# Text-described voice (Qwen3-TTS)
fusion-audio voice "Welcome to Exoforge, commander." out.wav ["calm male mission-control voice"]

# Voice clone from a 3s reference clip
fusion-audio clone "This line in the cloned voice." out.wav reference.wav

# Music / sound effect (ACE-Step)
fusion-audio sfx "short retro laser blast, 8-bit sci-fi zap" laser.wav 4

# Service health
fusion-audio health
```

Each successful call prints `wrote <path> (<bytes>)`. Files under ~1KB are
flagged as probably-invalid.

## Endpoints

### 1. ACE-Step 1.5 — music + SFX  (`http://10.0.0.119:8000`)  ✅ working
Model: `acestep/acestep-v15-turbo`. Text (and optional audio) -> music/SFX.

**Use the synchronous OpenRouter-compatible endpoint** (reliable):

```
POST /v1/chat/completions
{ "model":"acestep/acestep-v15-turbo",
  "messages":[{"role":"user","content":"<prompt>"}],
  "stream": false,
  "audio_duration": 4 }
```
Returns a normal chat.completion; the audio is inline as a **base64 data-URL**:
`choices[0].message.audio[0].audio_url.url` = `data:audio/mpeg;base64,...` (MP3).
~13-16s per clip. `GET /health`, `GET /v1/stats`, `GET /v1/models` also work.

> There is also an async pair `POST /release_task` + `POST /query_result` and
> `GET /v1/audio?path=...`. **`release_task` queues fine and the job succeeds,
> but `query_result` returns an empty `data:[]` for every task-id / body shape,
> so the async result-retrieval path is currently broken.** Use
> `/v1/chat/completions` instead — it blocks until done and hands back the audio.

### 2. Qwen3-TTS — voices  (`http://10.0.0.104:8000`)  ⚠️ currently DOWN
Model: Qwen3-TTS-12Hz-1.7B (Base + VoiceDesign).

```
POST /design {"text": "...", "voice_description": "..."}   -> wav   (text-described voice)
POST /clone  {"text": "...", "reference_audio": <wav>}      -> wav   (3s zero-shot clone)
```
**As of 2026-07-06 this box is not answering:** TCP :8000 is open (DNAT), but the
backend resets every HTTP request with zero bytes (not a wrong path/shape — even
`/health` and a raw socket get `connection reset`; not TLS; no alternate port).
The earlier `/design` "HTTP 000" was this outage, not a bad request. The
`fusion-audio voice`/`clone` calls are coded to the correct shape above and will
work once the Qwen3-TTS container is restarted (check `osprey /services` →
Qwen3-TTS, then restart the `defjob-proxy`/container on host `3930b`/10.0.0.104).

## Convention (important)
- Generate at dev time; **commit** files into the project:
  - voice-over → `assets/audio/<feature>/` (e.g. `assets/audio/tutorial/`)
  - sound effects → `assets/audio/sfx/`
- Reference the committed files from the game like any other static asset.
- Never hit `10.0.0.104` / `10.0.0.119` from shipped game code.
- After adding/wiring audio, re-run the project's `test.mjs` and confirm it still passes.
