Files
oak-editor/crates/oak-audio/README.md
T
Mike-Solar 4babbf5de8
CI / Build & test (Linux) (push) Successful in 24m6s
CI / Build & test (Windows) (push) Successful in 31m14s
core: merge oak-common into oak-core
oak-common is gone; its modules (configstore, xmlutils, ocioutils,
oiioutils, colormath, colortransform, videoparams, ffmpegutils, ...)
now live in oak-core alongside the value types. The render value/GPU
types moved too: backend (wgpu context + DisplayRenderer), color
(ColorProcessor over ocio-rs), texture, frame, and the commonutil
config helpers.

Fix-ups to make the merged tree build and pass tests:

- oak-core Cargo.toml: wgpu back to 25 (the moved backend code is
  written against that API generation); add the toml/quick-xml/image
  deps oak-common carried.
- lib.rs: drop the duplicate 'pub mod error;'.
- error.rs: unified OAKCORE_* codes; restore Error::new() and
  From<OcioError> from oak-common's error type.
- backend.rs/color.rs: oak_core::/oak_render:: self-references
  rewritten to crate::; the shaderfx-dependent GPU effect test moved
  to oak-render's shaderfx tests (shaderfx depends on oak-node and
  cannot live in oak-core).
- oak-render's error module re-exports oak_core::error::{Error,
  Result}; the OAKRENDER_* codes stay as the public-code contract.
- oak-node jobs.rs: ColorProcessor imported from oak_core::color.
- Integration tests repointed at oak_core::{texture, frame, backend,
  color, colormath}.
- the display-ICC regression test treats an empty OAK_DISPLAY_ICC as
  unset, matching displayicc::env_override_icc.
2026-09-03 17:42:20 +08:00

103 lines
4.9 KiB
Markdown

# oakaudio Rust crate
> Status: **implemented**. The C ABI (`include/audio/*.h`) is implemented
> by `src/ffi.rs`; the contract suite lives in `tests/` (all green,
> ~88% line coverage under tarpaulin). The architecture below mirrors the
> oaknode/oakrender crate template (FFI discipline, testing layers) from
> `crates/oaknode/README.md` and `crates/oakrender/README.md`.
## Scope
Replaces the C++ oakaudio module (`src/audio/src`, ~50k lines): the
PortAudio output/input manager (`AudioManager`), the real-time
resampler/format converter (`AudioProcessor`), timeline synchronization
helpers (`AudioSynchronizer`, `AudioWaveformSync`), the level meter
(`AudioLevelMeter`), the visual waveform store (`AudioVisualWaveform`),
the header-only pull buffer (`PreviewAudioDevice`), and the config
bridge (`audio_config` namespace).
Public contract: `include/audio/*.h` (5 headers plus `error.h`, ~45
functions) — frozen, implemented verbatim by `src/ffi.rs`.
## Key architectural decisions (C++ → Rust mapping)
1. **Singleton manager.** `AudioManager` is a process-wide PortAudio
singleton. Rust keeps the singleton behind a `OnceLock<Mutex<...>>`
with borrow-only handles: `addref`/`release` are no-ops exactly as on
the C++ side, and an empty handle reports `OAKAUDIO_E_STATE`. No
destruction ever happens through the handle.
2. **Processor is the only heavy FFI consumer.** `AudioProcessor` wraps
the ffmpeg_bridge audio filter graph (`fb_audio_graph_*`,
`fb_frame_*`); every call funnels through `bridge::ffmpeg`. The
resampler/format-conversion semantics and the always-planar-f32
output (`OAKAUDIO_PROCESSOR_OUTPUT_FORMAT = 4`) are preserved.
3. **Sync helpers are stateless.** `AudioSynchronizer` and
`AudioWaveformSync` have only static methods in C++; they become
plain functions in `synchronizer.rs` / `waveformsync.rs`. No handles
are involved on the sync headers except by-value arguments.
4. **Value types are local.** `params.rs` defines `AudioParams` (a
plain POD) and a **planar-first** `SampleFormat` enum mirroring
`olive::core::SampleFormat::Format` exactly, because these values
cross the C ABI as `int`. See the note in `params.rs` about why the
crate does not reuse `oakcore-rs`'s `SampleFormat`.
5. **Rational reuses oakcore-rs.** `core::Rational` (used by
synchronizer and waveform) comes from `oakcore-rs`; there is no
local copy.
6. **Record path through oakcodec.** `AudioManager` records through the
oakcodec encoder C ABI (`bridge::codec`) and waveform extraction
decodes through the oakcodec decoder C ABI — exactly as the C++
does. No direct ffmpeg_bridge use in the record path.
7. **Config via oak_core.** Device names and the output buffer size
read through `bridge::common` (`oak_core_config_*`), preserving the
`audio_config` namespace semantics as a `config.rs` free-function
module.
## Layout
`COVERAGE.md` maps every C++ audio class/method to its Rust home.
Review that first.
```
src/
lib.rs crate doc + module map
error.rs error codes (mirrors include/audio/error.h)
handle.rs refcounted-handle scaffolding (same pattern as node)
params.rs AudioParams + planar-first SampleFormat value types
config.rs audio_config namespace (bridge::common)
manager.rs AudioManager singleton (PortAudio I/O, recording)
processor.rs AudioProcessor (resampler/converter, bridge::ffmpeg)
synchronizer.rs AudioSynchronizer placement helpers
levelmeter.rs AudioLevelMeter peak/RMS/VU/LUFS analysis
waveform.rs AudioVisualWaveform mipmapped store + extraction
waveformsync.rs AudioWaveformSync envelope offset estimation
previewdevice.rs PreviewAudioDevice pull buffer
bridge/ C ABI imports: common.rs, codec.rs, ffmpeg.rs
ffi.rs include/audio/*.h export layer
tests/ contract + golden tests (see README test section)
```
## Hard rules for the implementer
1. Every `extern "C"` body goes through `handle::guard*`; no panic
crosses FFI. The manager's borrow-only singleton is the one place
`guard_handle`/`guard_void` are used with no refcount semantics.
2. `SampleFormat` and `AudioParams` integer values MUST match the C++
enums bit-for-bit; `// CPP-PARITY:` comments mark every load-bearing
layout decision.
3. Behavior parity with C++ is proven by the C ABI test-suite
(`src/audio/tests`, unchanged) plus the golden tests in `tests/`
(waveform mipmap/channel-interleaved layout, RMS/LUFS thresholds,
sync placement).
4. Where C++ behavior is genuinely load-bearing but ugly, port the
behavior, not the aesthetics; leave a `// CPP-PARITY:` comment with
the C++ file:line.
5. `src/plugin/` is frozen and out of scope; no oakaudio code reaches
into it.
## Dependency policy
Prefer mature third-party crates (MIT/Apache-2.0/BSD, GPL-compatible)
over hand-rolling; register each addition (name + reason) here. Large
existing C++ libraries (OTIO, OCIO, OIIO, FFmpeg) are NEVER rewritten
— they are consumed through their C ABI / bridge layers.