audio-clock-bsd 0.1.1

PTP/NTP clock synchronization and sample-index to timestamp conversion for real-time audio
Documentation
# audio-clock-bsd

[![License: BSD-2-Clause](https://img.shields.io/badge/license-BSD--2--Clause-blue.svg)](./LICENSE)
[![Rust](https://img.shields.io/badge/rust-1.85%2B-orange.svg)](https://www.rust-lang.org)

Clock synchronization and timestamp conversion for real-time audio. It provides
the `ClockSource` trait — the single contract a host application uses to map
between an audio **sample index** and a wall-clock **PTP timestamp**
(nanoseconds) — together with an anchor-based conversion and two backends.

> The doc comments on each public item are the primary reference. This README is
> an overview only.

## Overview

`audio-clock-bsd` lets an audio engine timestamp its samples for distributed
playback (e.g. PTP-aligned streaming) or for offline alignment. The
sample-index ↔ timestamp map is a pure linear function anchored at a known
reference point; [`ClockAnchor`] performs the checked arithmetic, and the two
conversions are allocation-free and safe to call from the real-time audio
thread. Reading the live time source (polling a daemon, reading a device) is a
**blocking** operation and runs on a separate worker thread.

### Backends

| Backend     | Source                                          | Availability           |
|-------------|-------------------------------------------------|------------------------|
| `NtpClock`  | `std::time::SystemTime` (wall clock, ns)        | Always (MVP default)   |
| `PtpClock`  | injectable `PtpTimeProvider` (PTPv2 daemon/NIC) | When PTP HW is present |

`NtpClock` is the always-available fallback. `PtpClock` reads PTP time from an
injectable provider (e.g. a daemon-polling thread reading `/dev/ptp*`); when no
provider is supplied it degrades to an `Unavailable` stub whose conversions
still work for offline mapping.

## Core types

| Type              | Description                                                              |
|-------------------|--------------------------------------------------------------------------|
| `ClockSource`     | The public trait: `ptp_now_ns` / `sample_to_ptp` / `ptp_to_sample`.      |
| `ClockAnchor`     | The linear sample↔timestamp map (checked, overflow-safe `i128` math).    |
| `NtpClock`        | System wall-clock backend (default fallback).                            |
| `PtpClock`        | PTPv2 backend with injectable `PtpTimeProvider`.                         |
| `PtpTimeProvider` | Trait for a live PTP timestamp source.                                   |
| `ClockError`      | Error enum (thiserror-backed).                                           |

## Threading model

`ClockSource` is `Send` but **not** real-time-safe in general: `ptp_now_ns` may
block and must run off the RT audio thread. The pure conversions
`sample_to_ptp` / `ptp_to_sample` are allocation-free, never panic (return
`i64::MIN` on overflow), and are safe to call from the RT thread.

## Dependencies

| Dependency       | Version | Purpose                          | License           |
|------------------|---------|----------------------------------|-------------------|
| `audio-core-bsd` | 0.1.0   | `AudioFrame` (example only)      | BSD-2-Clause      |
| `thiserror`      | 2.0     | `ClockError` derive              | MIT OR Apache-2.0 |
| `proptest` (dev) | 1.11.0  | property tests                   | MIT OR Apache-2.0 |

This crate links **no system library**, so it builds and tests anywhere
(docs.rs, CI hosts without PTP hardware).

## Status

**0.x — experimental.** The API is not yet frozen. The `ClockSource` contract,
the `ClockAnchor` conversion, and both backends are implemented and tested.
Live PTP hardware timestamping depends on a `PtpTimeProvider` supplied by the
host (FreeBSD NIC support for igb/ixl/mlx5en is hardware-dependent).

- edition: 2021
- MSRV: 1.85
- license: BSD-2-Clause

## Example

Read the NTP clock and timestamp a few sample windows:

```rust
use audio_clock_bsd::{ClockSource, NtpClock};

let clock = NtpClock::new(48_000)?;
println!("now: {} ns", clock.ptp_now_ns()?);
// 48000 samples forward is exactly 1 second.
let one_sec_later = clock.sample_to_ptp(48_000);
# Ok::<(), audio_clock_bsd::ClockError>(())
```

See `examples/ntp_clock.rs` for a runnable version.

## License

BSD-2-Clause. See [`LICENSE`](./LICENSE).

[`ClockAnchor`]: https://docs.rs/audio-clock-bsd/latest/audio_clock_bsd/clock/struct.ClockAnchor.html