-
Notifications
You must be signed in to change notification settings - Fork 363
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Duplex API #553
Proposal: Duplex API #553
Conversation
Stubbing hosts is up next.
This makes the duplex API somewhat asymmetric compared to standalone input / output. However, something more robust than providing all possible channel combinations is necessary for the duplex mode. Rationale: - Hosts might provide e.g. 32 different channel count options for both input and output, and even this could be limiting for some high-end devices or esoteric setups. 32*32 = 1024 is too much to provide as separate `SupportedDuplexStreamConfigRange` items. - The duplex mode is meant for "pro audio" use-cases, where static channel semantics (mono, stereo, surround, etc.) are less prevalent compared to binding a bunch of channels, and leaving semantics to the user. This makes for a vague justification for the asymmetry. - min/max is not quite sufficient, and neither is defaulting to stereo or mono. This is why I've added a `default` field for the channel ranges in duplex mode. On some hosts, like jack, there is no meaningful maximum channel count, since channels can be patched arbitrarily. There is however, often a physical device available where default counts can potentially be inferred from. Alternatively the default could be stereo. On CoreAudio, audio streams are always bound to concrete devices (either physical or statically configured). There it makes sense to default to the maximum channel count for the device in question. The host can opt to report no defaults, in which case stereo is preferred in `default_with_sample_rate` and `default_with_max_sample_rate`. Before a more thorough redesign of the configuration API as a whole, this seems like a reasonable middle-ground.
Why is JACK support in a separate branch (https://github.com/rustydaw/cpal/tree/duplex-jack) not included in this PR? Right now, this PR doesn't compile when I enable the JACK feature. |
Rebased with help from nyanpasu64. --- It almost compiles. Original (outdated) commit message: The way the temporary interlacing buffers are handled currently is not great. The root of the issue seems to be that JACK actually provides a callback that allows performing allocations on the realtime thread when buffer size changes, but this is not exposed as such in rust-jack. See: RustAudio/rust-jack#137 The duplex implementation now mirrors input, and this panicks if buffer size becomes greater than the original. Co-authored-by: nyanpasu64 <[email protected]>
My thought was that it wouldn't make sense to collect all host implementations into this PR, so it's easier to review the design. Having one implementation as an example isn't a bad idea so I've incorporated it here now. Apologies for leaving this PR hanging for so long – I was sort of hoping for some comments before continuing work on it. It should now be good to merge as-is, and other host implementations can be added in later. Edit: Looks like the default heuristic comparator still needs an implementation. |
(copied from Discord) When running under pipewire-jack, Latency is unaffected when I chain 3 cpal jack duplex clients: I assume latency should be deterministic, since the JACK server calls clients with input, and waits for it to return output synchronously. Startup seems glitch-free most of the time? |
We're working on an audio editor / DAW, and full duplex audio IO is a fairly important feature for such software that is currently missing from CPAL.
For reference, duplex (or more accurately, full duplex) means that both input and output are processed at the same time, on the same stream, implicitly synchronized. Hosts have different levels of support for this: some provide it natively (JACK, CoreAudio), while on others this API merely allows input and output to run on the same loop to simplify synchronization. Without it, a CPAL user needs to handle synchronization manually and buffer an indeterminate amount to avoid over/underruns, as is done in the
feedback
example. This proposal does not address combining input and output streams from separate devices since in general that opens up further responsibilities such as clock drift compensation.The API I have laid out in this PR is based on the one previously discussed here, though some things in CPAL have since changed. It is mostly a mirror of the existing input / output APIs, forming a third type of stream called "duplex". In particular,
input/output_devices
->duplex_devices
,default_input/output_device
->default_duplex_device
supported_input/output_configs
->supported_duplex_configs
,default_input/output_config
->default_duplex_config
build_input/output_stream
->build_duplex_stream
FnMut(&Data, &mut Data, &DuplexCallbackInfo)
. In general the order of parameters should always be input, then output.The largest difference is with how channels are configured. I determined it doesn't make sense to list out every possible combination of the amount of input and output channels as separate
SupportedDuplexStreamConfigRange
instances for the user to iterate over. Therefore I instead incorporate ranges of possible channel configurations (min, max, default) for both input and output in theSupportedDuplexStreamConfigRange
structs. Hopefully this doesn't make the API too illogical. I wrote up a more in-depth rationale in this commit.In addition, it should be straightforward to add a conversion layer to convert input/output
SupportedStreamConfig
intoSupportedDuplexStreamConfig
with zero channels of the other type. This would allow users of CPAL to only write one type of callback handler, but have their code still work even if duplex devices aren't available. Internally,build_duplex_stream
would delegate to the respectiveraw
methods so the host implementation doesn't have to be aware of it or try to open devices with zero channels.Feedback and improvement ideas are welcome. If this is deemed the way to go, I and anyone interested can start implementing support for it in more hosts.
Host implementation status