Metadata-Version: 2.4
Name: pyalsoft
Version: 0.2.0
Summary: Functional Python audio API and typed OpenAL Soft bindings with bundled runtimes
Project-URL: Homepage, https://github.com/BDraves/pyalsoft
Project-URL: Documentation, https://github.com/BDraves/pyalsoft/blob/master/docs/reference.md
Project-URL: Source, https://github.com/BDraves/pyalsoft
Project-URL: Issues, https://github.com/BDraves/pyalsoft/issues
Project-URL: Release notes, https://github.com/BDraves/pyalsoft/releases
Author-email: Brennan Draves <dravesbrennan@gmail.com>
License-Expression: MIT AND LGPL-2.0-or-later AND BSD-3-Clause AND LicenseRef-OpenAL-Registry
License-File: LICENSE
License-File: LICENSES/Microsoft-GSL.txt
License-File: LICENSES/OpenAL-Registry.txt
License-File: LICENSES/THIRD-PARTY.md
License-File: LICENSES/fmt.txt
License-File: vendor/openal-soft/COPYING
License-File: vendor/openal-soft/LICENSE-pffft
Keywords: 3d-audio,audio,bindings,ctypes,efx,game-audio,hrtf,openal,openal-soft,sound,spatial-audio
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.12
Description-Content-Type: text/markdown

# PyALSoft

[![CI status](https://github.com/BDraves/pyalsoft/actions/workflows/ci.yml/badge.svg?branch=development)](https://github.com/BDraves/pyalsoft/actions/workflows/ci.yml)
[![PyPI version](https://img.shields.io/pypi/v/pyalsoft.svg)](https://pypi.org/project/pyalsoft/)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/pyalsoft.svg)](https://pypi.org/project/pyalsoft/)
<!-- openal-soft-version-badge:start -->
[![OpenAL Soft 1.25.2](https://img.shields.io/badge/OpenAL_Soft-1.25.2-557C94)](https://github.com/kcat/openal-soft/releases/tag/1.25.2)
<!-- openal-soft-version-badge:end -->

PyALSoft provides a functional, managed playback API and typed bindings for
OpenAL Soft, including core OpenAL, ALC, EFX, and supported extensions. The
managed API lives at the package root; the complete low-level interface remains
available through `pyalsoft.bindings`.

> PyALSoft is an independent project and is not affiliated with or endorsed by the OpenAL Soft project.

## Installation

PyALSoft requires Python 3.12 or later.

```console
python3 -m pip install pyalsoft
```

## Quick start

This example generates a 440 Hz sine wave, plays it, and releases every native
resource when the `with` block exits:

```python
import math
import time
from array import array

from pyalsoft import (
    PCM,
    VoiceState,
    get_voice_status,
    open_playback,
    play,
    release,
    upload,
)

sample_rate = 44_100
duration = 0.5
pcm = array(
    "h",
    (
        round(((1 << 14) - 1) * math.sin(math.tau * 440 * frame / sample_rate))
        for frame in range(round(sample_rate * duration))
    ),
).tobytes()

audio = PCM(samples=pcm, channels=1, sample_rate=sample_rate)

with open_playback() as playback:
    clip = upload(playback, audio)
    voice = play(playback, clip)
    while get_voice_status(playback, voice).state is VoiceState.PLAYING:
        time.sleep(0.01)
    release(playback, voice)
    release(playback, clip)
```

The same example is available as [`examples/play_sine.py`](examples/play_sine.py)
and can be run from a source checkout with:

```console
uv run python examples/play_sine.py
```

[`examples/move_sine.py`](examples/move_sine.py) shows a playing voice moving
from left to right by creating updated `VoiceConfig` values with
`dataclasses.replace` and passing them to `set_voice_config`.

## Platforms

PyALSoft supports Windows x86-64, macOS x86-64 and ARM64, and Linux x86-64
and ARM64. Platform wheels bundle OpenAL Soft. `pyalsoft.bindings.load()` uses
the bundled library when available, then falls back to a system installation.
Pass an explicit library path to `pyalsoft.bindings.load(path)` to override
discovery.

## API layers

The package root is a functional interface for static buffered playback.
`PCM`, `VoiceConfig`, and `Listener` are immutable data. `Clip` and `Voice` are
opaque identities owned by a `Playback` session. Functions including `upload`,
`play`, `set_voice_config`, `set_listener`, `pause`, `resume`, `stop`, and
`release` make state changes explicit. A stopped or completed voice retains its
identity until it is passed to `release`; `release_finished` collects all such
voices in a long-lived session. Closing the session releases any resources that
remain. `resume` accepts paused voices only.

`pyalsoft.bindings` is the supported low-level escape hatch for streaming,
capture, EFX, extensions, or direct control. Its recommended `library.al` and
`library.alc` namespaces use snake-case names, accept Python strings and
sequences, infer array lengths, allocate output parameters, and return normal
Python values. Generated object handles such as `library.al.source(identifier)`
expose typed properties including `gain`, `position`, `buffer`, and `state`.

Exact C entry points remain available when direct control is needed. For
example, `library.alGenSources` is the generated `ctypes` binding for
`alGenSources`, while `library.al.gen_sources()` is its Python-value wrapper.
Constants, enums, and C types are available through `bindings.constants`,
`bindings.enums`, and `bindings.types`.

Extensions are discoverable by registry name or generated attribute. Check an
extension against its device or current context before using its commands:

```python
efx = library.extensions.alc_ext_efx
if efx.is_present(device):
    print(efx.commands)
```

At the bindings layer, unavailable libraries, missing contexts, and unsupported
extensions raise `LibraryNotFoundError`, `ContextRequiredError`, and
`ExtensionUnavailableError`, respectively. `open_playback` translates library
discovery failures into `PlaybackOpenError`. See the
[`bindings API reference`](docs/reference.md) for the complete command,
property, and extension surface.

## Contributing

Create the locked development environment with [uv](https://docs.astral.sh/uv/):

```console
uv sync --locked --python 3.12
```

Before submitting a change, run the same core checks as CI:

```console
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run mypy
uv run python tools/generate_bindings.py --check
uv run python tools/sync_openal_soft.py --check
```

Bindings and [`docs/reference.md`](docs/reference.md) are generated from the
vendored OpenAL registry plus reviewed corrections in
[`tools/semantic_overrides.toml`](tools/semantic_overrides.toml). After changing
the generator, registry, or overrides, regenerate them with:

```console
uv run python tools/generate_bindings.py
```

See the [repository tool guide](tools/README.md) for the purpose and structure
of each development and release command.

## License

PyALSoft's original Python code is available under the MIT License. Bundled
OpenAL Soft and other third-party components remain under their respective
licenses. The distribution includes the complete license texts and a
third-party notice.
