# Binaural 360° post-processing Places finished speech around the listener's head for headphones: circling, sweeping from ear to ear, or held at one spot. [中文](README_zh.md) This is a separate post-processing step, not a feature of the Iroha-TTS model. It works on any speech recording and needs no GPU or model weights. ## Quick start ```bash python3.12 -m venv .venv-spatial .venv-spatial/bin/pip install -r spatial/requirements.txt # Start at the left ear and circle the head twice, at 0.5 m .venv-spatial/bin/python spatial/binaural.py --in a.wav b.wav c.wav --gap 0.6 --start -90 --turns 2 --near 0.8 --out orbit.wav # Sweep from the left ear through the front to the right ear .venv-spatial/bin/python spatial/binaural.py --in a.wav b.wav --start -90 --turns 0.5 --near 0.6 --out sweep.wav # Hold at the right ear, 0.25 m .venv-spatial/bin/python spatial/binaural.py --in a.wav --start 90 --turns 0 --near 1 --out right.wav ``` ## Parameters | Parameter | Default | Meaning | |---|---|---| | `--in` | Required | Clips in playing order. Any sample rate; stereo is mixed to mono. | | `--gap` | `0.6` | Silence before each clip after the first, in seconds. | | `--gaps` | Unset | One silence per clip after the first. Overrides `--gap`. | | `--lead` | `0.5` | Silence before the first clip, in seconds. | | `--keep-level` | Off | Keep the clips' relative levels (pieces cut from one take). Otherwise each clip is set to -20 dBFS RMS first. | | `--start` | `-90` | Azimuth where the first clip starts: 0° front, 90° right, 180° behind, 270° (or -90°) left. | | `--turns` | `1` | Turns from the first clip's start to the last clip's end. Positive is clockwise seen from above, negative the other way, `0` holds still. | | `--near` | `0.8` | Closeness from 0 to 1. Picks the measured distance: ≥ .9 is 0.25 m, ≥ .7 is 0.5 m, ≥ .55 is 0.75 m, otherwise 1 m. Each 0.25 also adds 1 dB. | | `--ceil` | `0.99` | Peak limit. | | `--hrir` | `spatial/hrir` | Folder with the four SOFA files. | | `--out` | Required | Output WAV. A log with the same name and `.json` is written next to it. | The output is a 48 kHz, stereo, 24-bit WAV with voiced parts near -20 dBFS RMS (limiting can lower this by about 1 dB). The path moves at constant speed and keeps moving during pauses, so each clip starts wherever the path has reached. The log lists each clip's start, duration, azimuths, and `ild_db`, the level of the right ear minus the left. Use it to check the side: positive means the right ear is louder. ## How it works Each position is rendered by convolving the voice with the left- and right-ear impulse responses measured for that direction on a KU100 dummy head. The level difference, time difference, and spectral coloring between the ears come from the measurements. On a moving clip the response changes every 20 ms with a triangular crossfade. ## Limitations - **Headphones only.** Over loudspeakers, or after a mono downmix, the effect is lost. Do not apply further panning or stereo processing to the output. - **Front and back are easily confused.** The KU100 is a generic head, not fitted to each listener, and some listeners hear the voice inside the head. Left, right, and close-to-ear positions are the clearest. - **Horizontal circle only.** There is no elevation control, and only the four measured distances are available. - **No perceptual study was run on these settings.** Listen before choosing a distance. ## License and credit `binaural.py` is under the MIT License ([LICENSE-MIT](../LICENSE-MIT)). The SOFA files are by TH Köln under CC BY-SA 3.0 ([hrir/LICENSE](hrir/LICENSE)). If the voice comes from Iroha-TTS, that audio remains subject to the [Fish Audio Research License](../LICENSE.md), and the speaker's consent is still required. When you publish rendered audio, credit the measurements. We do not take a position on whether rendered audio counts as an adaptation of the data, so crediting is the cautious choice: > Binaural rendering uses KU100 near-field HRIRs by TH Köln (CC BY-SA 3.0).