This release is a pre-release and may not be stable for production use.
omi_audio
Renderer-agnostic spatial audio for Python, built natively on the glTF
KHR_audio_emitter
data model — which is the Web Audio PannerNode model. Emitters have a distance
curve, an optional directional cone and a gain; a listener hears them panned
about its own forward axis. The mixing is a fixed voice pool summed in NumPy.
- NumPy is the only hard dependency. No graphics library, no scenegraph, no audio library is required to mix.
miniaudiois optional — it decodes files and reaches the sound card. Without it the library still mixes and simply stays silent.- Ogg Opus decodes too, through the optional
opusextra.miniaudiodoes not read it, so the container is demultiplexed here from the published RFCs and the codec islibopus. - Nothing here is copyleft, and neither is anything it depends on:
miniaudioand every decoder it bundles are MIT or public domain, and the Opus chain —libopus, theopuslibbindings, the wheel that carries them — is BSD-3-Clause throughout. The convenient wrappers are not:libsndfile, PyAV andpydubvia ffmpeg are LGPL or worse and cannot be a dependency here, whatever they would save. - Silence is a backend. A missing package, a device that will not open, or a
machine with no audio hardware all end in one warning and a
NullDevice.open_device()cannot raise. - The audio thread never allocates, blocks, decodes or logs. Pre-allocated
buffers, NumPy
out=everywhere, and a lock the mixing never takes. - A document's
uriis never resolved here. A glTF file comes from somebody you have never met; deciding what one of its strings is allowed to mean is the application's call, not a library's. SeeAudioLibrary.
⚠️ This code is largely LLM-written. It has a test suite, but it comes with no guarantees of correctness, accuracy, or fitness for any purpose (see
LICENSE, MIT). Review it before relying on it for anything that matters.
Install
pip install omi_audio # mixing only (NumPy)
pip install "omi_audio[playback]" # + miniaudio, for files and a sound card
pip install "omi_audio[opus]" # + Ogg Opus decoding
The opus extra carries a libopus inside its wheel for Linux, macOS and
Windows, about 330 KB. A libopus already on the system is used where the extra
is absent, which covers most Linux desktops — but it is not a component Windows
or macOS ships, so relying on the system alone would decode on one platform and
quietly fail on the others.
Quick start
from omi_audio import AudioEngine, model, synth
engine = AudioEngine() # opens a device, or silence
# A sound with no file behind it, so this runs anywhere.
engine.clips.put('ping', synth.impact(0.4, seed=1))
emitter = model.AudioEmitter(
gain=0.8,
positional=model.PositionalProperties(refDistance=2.0, rolloffFactor=1.0),
)
handle = engine.play('ping', emitter=emitter, position=(3.0, 0.0, -5.0))
Run it for real, with no assets and no sound card required:
python examples/orbit.py # a sound orbits the listener for ten seconds
python examples/orbit.py --silent # never opens a device, still prints levels
Each frame, move the listener and re-aim whatever is still playing. A moving
sound is re-aimed, never restarted: aim() writes two floats and the mixer
ramps to them across the next block.
engine.listen(camera) # anything with .position/.quaternion
engine.aim(handle, emitter, position=emitter_world_position)
Testing without a sound card
Everything below the device is arithmetic over arrays, so build an engine on a
NullDevice and assert on the mix:
from omi_audio import AudioEngine, NullDevice, synth
engine = AudioEngine(device=NullDevice(sample_rate=8000), voices=8)
engine.mixer.play(synth.tone(440.0, 1.0, sample_rate=8000), pan=1.0)
block = engine.mixer.mix(64) # (64, 2) float32
assert block[:, 0].max() < 1e-9 # nothing in the left ear
assert block[:, 1].max() > 0.1 # and plenty in the right
The pieces
In the order sound travels through them:
| Module | Answers |
|---|---|
model |
KHR_audio_emitter as typed records, with the extension's own field names and defaults; from_gltf/to_gltf round-trip, and the node/scene references that say where an emitter is |
formats |
The glTF codec extensions — OMI_audio_ogg_vorbis and OMI_audio_opus — and which of them this installation can actually decode |
library |
What a document's audio references have resolved to — the seam where your resolver, not this library, decides what a uri means |
spatial |
Every gain curve — three glTF distance models, the Web Audio cone, VRML97's two ellipsoids, equal-power panning — and the listener's pose |
clip |
Encoded audio → mono float32 at one rate, from a file or from bytes (.glb buffer views, data: URIs, downloads), decoded once; and a clip back out as a WAV file |
synth |
Tones, chirps, noise, impacts, rumbles and looping ambience (surf, birdsong) made out of arithmetic, so demos and tests need no assets and no licences |
mixer |
A fixed voice pool summed into stereo blocks: allocation-free, lock-free on the audio thread, priority stealing, gain ramping, an underwater low-pass |
reverb |
The sound of the place the listener is in: a comb-filter reverb over the whole mix |
vehicle |
A road vehicle's motor, tyres, wind and impacts, from its speed, tyre scrub and throttle, with a tuning of the vehicle's own figures |
device |
Where blocks go — miniaudio, or silence |
engine |
The one object an application holds |
Deeper documentation is in docs/ — start with
GAME-INTEGRATION.md if you are building
something, or SPATIALISATION.md for every gain curve
with a diagram of each, generated from the code that implements it.
What this does not do
- Stereo only, and the pan carries azimuth alone: a sound directly overhead and one dead ahead are indistinguishable. Height needs an HRTF and surround needs more than two channels; neither is here.
- No occlusion or doppler, and one reverb for the whole mix. The effects are
master-bus ones:
muffle, a low-pass, andreverb, the place the listener is in. A sound cannot be reverberated on its own, and two places cannot be heard at once. - No streaming. Clips are decoded whole into memory.
- No scheduling. Nothing here has a clock;
autoplaystarts when your application says the scene has begun.
Using it from a scenegraph
omi_audio is deliberately ignorant of scenegraphs: it is handed world
positions, a listener pose and clips. OpenGLContext
is the reference integration — AudioEmitter, AudioSource and VRML97's
Sound nodes, a per-context engine driven once a frame from the render pass,
and a glTF loader that reads KHR_audio_emitter blocks straight into this
model.
Contributing, changes, security
- CONTRIBUTING.md — how to run the gates, and the two rules with teeth (the audio thread, and the untrusted document).
- CHANGELOG.md
- SECURITY.md — the threat model, and why your resolver is the boundary.
Licence
MIT. Note that some jurisdictions do not allow for LLM generated code to have a copyright, as such this library may be in the Public Domain in your jurisdiction. There is NO warranty of any kind on the software.
See LICENSE.
Metadata
Release files for omi-audio 0.4.0a3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| omi_audio-0.4.0a3.tar.gz | 259.9 kB | With provenance | Details for omi_audio-0.4.0a3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| omi_audio-0.4.0a3-py3-none-any.whl | Python 3 | none | any | Details for omi_audio-0.4.0a3-py3-none-any.whl |
Total release size: 372.7 kB
Release files / omi_audio-0.4.0a3.tar.gz
| Download URL | omi_audio-0.4.0a3.tar.gz |
|---|---|
| Size | 259.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f281e132222e86ad5e01bc9402b9e36feed7b718e04e20235fdf1d1f5f0d8d57
|
|
BLAKE2b-256 checksum How to use checksums |
13d2a3eaa2a0e86cb4795e9f9369a6d6e17fa6d0bd8b4ef20553580762a0bd49
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logRelease files / omi_audio-0.4.0a3-py3-none-any.whl
| Download URL | omi_audio-0.4.0a3-py3-none-any.whl |
|---|---|
| Size | 112.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b541ae76ccc043ceecd276cfaf594ee7d51ad301e81a79a5361949fef90e685a
|
|
BLAKE2b-256 checksum How to use checksums |
83bdc50a13903c252f763f92051d0802beb52d625e646993fea27727ae2599ae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logSecurity for omi-audio 0.4.0a3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for omi_audio-0.4.0a3.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.