Skip to content

feat: add duplex stream API (interface only; backends to follow)#1229

Open
gulbrand wants to merge 1 commit into
RustAudio:developfrom
gulbrand:duplex-api-interface
Open

feat: add duplex stream API (interface only; backends to follow)#1229
gulbrand wants to merge 1 commit into
RustAudio:developfrom
gulbrand:duplex-api-interface

Conversation

@gulbrand

@gulbrand gulbrand commented Jun 1, 2026

Copy link
Copy Markdown

Introduce the public duplex stream contract without any backend implementation. Hosts inherit trait defaults: supports_duplex() returns false and build_duplex_stream_raw returns UnsupportedOperation.

I have tested this with coreaudio/macos for a few months now as part of: #1096 (which I want to close in favor of this PR and a follow-up to add the coreaudio/macos support).

I am just now starting to test with ASIO on windows (still setting up my lab for this). The interface in this PR appears to be a good match for ASIO and coreaudio/macos. You can see the draft changes here for ASIO -- but these are totally untested and not ready for review: gulbrand#5

My plan is to get this API PR merged, close PR 1096, finish the coreaudio/macos rebase/merge conflict resolve and open a new PR for the coreaudio/macos implementation.

As for ASIO support, I can't commit to that yet, but the interface looks ok to me from a high level.

Introduce the public duplex stream contract without any backend
implementation. Hosts inherit trait defaults: supports_duplex() returns
false and build_duplex_stream_raw returns UnsupportedOperation.
@gulbrand gulbrand mentioned this pull request Jun 1, 2026
@gulbrand gulbrand changed the base branch from master to develop June 1, 2026 21:45

@StantonMatt StantonMatt left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took a pass over this as an API-only PR, not as backend validation.

This looks like a reasonable interface step to me. Existing hosts keep the default supports_duplex() == false, and build_duplex_stream_raw() defaults to UnsupportedOperation, so the PR is not accidentally making any backend claim support. The dynamic platform::Device forwarding also matches the existing input/output dispatch pattern, and DuplexCallbackInfo follows the current callback-info accessor style while keeping input/output timestamps separate.

The one API tradeoff I would call out before merge is that both build_duplex_stream<T> and build_duplex_stream_raw() use a single SampleFormat for input and output. That seems consistent with the synchronized ASIO/CoreAudio direction described here, but if a future backend needs different native sample formats for capture and playback, this API would need a follow-up shape rather than just a backend implementation.

I also checked the visible PR jobs individually: formatting, docs-Linux, Linux/macOS/Windows/iOS/tvOS/Android/WASM/WASI/PipeWire, sanitizer, and clippy jobs are green; only the release/publish docs jobs are skipped. No blocker from me on the interface/backward-compatibility side.

@roderickvd roderickvd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good approach to take it step-by-step.

Comment thread src/duplex.rs
/// The sample rate driving both directions.
pub sample_rate: SampleRate,
/// The desired buffer size, in frames per callback.
pub buffer_size: crate::BufferSize,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: for consistency, you could import BufferSize into scope.

Comment thread src/traits.rs
/// rendered output share a single clock.
///
/// Returning `true` is a contract that input and output sides will run from one device-level
/// callback, or an OS driver aggregate (such as an Aggregate Device on MacOS).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: macOS (note capitalization). Several occurrences in this file.

Comment thread CHANGELOG.md
- `realtime` feature for real-time audio thread scheduling without a D-Bus build dependency.
- `StreamTrait::now()` to query the current instant on the stream's clock.
- `StreamTrait::buffer_size()` to query the stream's current buffer size in frames per callback.
- Added duplex stream API (interface only; backends to follow).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For style, we're not starting the line with "Added" for the other entries. Consider something like:

DeviceTrait::build_duplex_stream() and supports_duplex() for synchronized capture and playback on a shared clock (no backend support yet).

Comment thread src/traits.rs
fn build_duplex_stream_raw<D, E>(
&self,
_config: DuplexStreamConfig,
_sample_format: SampleFormat,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible for a duplex stream to have its halves with different sample formats? If so, how likely would that be, and would we then want to support that or document it as a limitation?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants