Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 31 additions & 20 deletions akroasis/shared/akroasis-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,37 +1,48 @@
[package]
name = "akroasis-core"
version = "0.1.0"
edition = "2021"
authors = ["Akroasis Contributors"]
edition = "2024"
license = "GPL-3.0"
description = "High-fidelity audio playback core for Akroasis media player"
repository = "https://github.com/forkwright/akroasis"
description = "High-fidelity audio engine — decode, DSP, and output"
repository = "https://github.com/forkwright/harmonia"

[lib]
crate-type = ["lib", "cdylib", "staticlib"]

[dependencies]
# Audio decoding
claxon = "0.4" # Pure Rust FLAC decoder
# Audio decode
symphonia = { version = "0.5", features = [
"flac", "wav", "mp3", "aac", "isomp4", "vorbis", "alac", "aiff", "ogg",
"opt-simd-sse", "opt-simd-neon",
] }
opus = "0.3"
lofty = "0.22"

# Audio output — requires ALSA on Linux; enable via "native-output" feature
cpal = { version = "0.17", optional = true }

# Audio processing
rubato = "0.15" # Sample rate conversion (when needed)
dasp = "0.11" # Digital audio signal processing
rubato = "1.0"
ebur128 = "0.1"

# Utilities
thiserror = "2.0"
tracing = "0.1"
# Async runtime
tokio = { version = "1", features = ["rt", "sync", "time", "macros"] }

# JNI (Android bindings)
jni = { version = "0.21", optional = true }
# Error handling
snafu = "0.8"

[features]
android = ["jni"]
# Logging
tracing = "0.1"

# Random (TPDF dither)
rand = "0.9"

[dev-dependencies]
criterion = "0.5"
rstest = "0.25"
tokio = { version = "1", features = ["rt-multi-thread", "macros", "test-util"] }

[profile.release]
opt-level = 3
lto = true
codegen-units = 1
[features]
# UniFFI bindings for Android — Phase 6
android = []
# Native audio output via cpal (requires ALSA on Linux)
native-output = ["cpal"]
69 changes: 69 additions & 0 deletions akroasis/shared/akroasis-core/PROTOTYPE_NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Prototype Notes — akroasis-core

Documents findings from the pre-R6 prototype and decisions made during the P1-01 scaffold.

## Prototype Inventory (now in `legacy/akroasis-core/`)

| File | What it did | Why removed |
|------|-------------|-------------|
| `decoder.rs` | `FlacDecoder` via `claxon`, returned `Vec<i32>` | claxon is single-codec; i32 internal format is wrong for f64 pipeline |
| `buffer.rs` | `AudioBuffer` backed by `Vec<i16>`, position-tracking read/write | Not lock-free; i16 precision ceiling; no ring semantics |
| `replaygain.rs` | Applied dB gain to `&mut [i16]` slices | Integer sample math loses precision; replaced by dsp/replaygain.rs on f64 |
| `jni.rs` | Raw JNI bindings to `FlacDecoder` via pointer juggling | UniFFI replaces raw JNI in Phase 6; decoder API has changed entirely |
| `lib.rs` | Exposed `AudioConfig`, `FlacDecoder`, `AudioError` | All superseded by new module tree |

## Key Architecture Decisions

### f64 Internal Pipeline
All decoded samples are f64 in `[-1.0, 1.0]`. This avoids precision loss during chained DSP
(EQ + ReplayGain + compressor + volume). Quantisation to i16/i24/i32 happens only at the
output stage in `dsp/volume.rs` (TPDF dither) + `output/format.rs`.

### symphonia over claxon
symphonia handles FLAC, WAV, ALAC, AIFF, MP3, AAC, Vorbis, Opus-in-OGG, and MP4/M4A with a
single unified `FormatReader` + `Decoder` API. claxon is FLAC-only and unmaintained.

### snafu over thiserror
Per project rules. `Location` tracking via `#[snafu(implicit)]` aids debugging of async decode
tasks where stack traces are truncated.

### Native async fn in traits
Rust 1.75+ supports native `async fn` in traits without `async-trait`. The `AudioDecoder` and
`OutputBackend` traits use this feature. No `async-trait` crate dependency.

### cpal as optional feature
cpal requires ALSA headers on Linux. Gated behind `native-output` feature to allow the crate to
compile in CI environments without audio hardware. On-device builds enable the feature.

### Lock-free SPSC ring buffer
`ring_buffer.rs` uses `UnsafeCell<f64>` per slot with atomic read/write positions. No `Mutex`,
no allocation after `new()`. Required for the audio output callback which runs at real-time
priority and must not block.

## Dependency Notes

- `opus = "0.3"` links `libopus` via `audiopus_sys`. Requires opus C library on the build host.
Pure-Rust Opus support pending (no production-ready crate as of 2026-03).
- `lofty = "0.22"` is used for gapless metadata (LAME header, iTunSMPB). It also reads
ReplayGain tags but the actual gain computation uses `ebur128`.
- `rubato = "1.0"` — API changed significantly from `0.15` used in the prototype. Phase 1
stubs reference the 1.x API.

## Stub Map (P1-0X targets)

| Stub | Implementing prompt |
|------|---------------------|
| `decode/symphonia.rs` | P1-02 |
| `decode/opus.rs` | P1-03 |
| `decode/probe.rs` | P1-02 |
| `dsp/silence.rs` | P1-04 |
| `dsp/eq.rs` | P1-05 |
| `dsp/crossfeed.rs` | P1-06 |
| `dsp/replaygain.rs` | P1-07 |
| `dsp/compressor.rs` | P1-08 |
| `dsp/convolution.rs` | P1-09 |
| `dsp/volume.rs` | P1-10 |
| `output/cpal.rs` | P1-11 |
| `output/format.rs` | P1-11 |
| `gapless/prebuffer.rs` | P1-10 |
| `engine.rs` (body) | P1-10 |
Loading
Loading