This makes it so clients have to opt into the VPIO forcelist.
It is currently mainly needed by gecko, since sites may create multiple
streams to a builtin device with different prefs and processing params,
and HAL audio units to builtin device are not working properly when
there is a VPIO audio unit to the same builtin device on (anywhere) on
the system.
With the cfg flag vpio-forcelist a client can avoid VPIO altogether, by
not compiling with the flag, and by never creating input streams with
the VOICE pref.
Should a client need to mix VOICE and non-VOICE input streams to a
single device, it should enable vpio-forcelist.
Fixes#242.
This works around https://github.com/rust-lang/cargo/issues/6745 and
allows cubeb-coreaudio-rs to appear as a subdirectory of a workspace
member in another project (for instance, cubeb-rs).
The current HOST_TIME_TO_NS_RATIO implemented with lazy_static is
causing what appears to be false-positive TSAN errors. OnceLock and
LazyLock exhibit similar errors under TSAN.
It seemed easier to rewrite host_time_to_ns_ratio to be a init-once
context member, rather than making the unstable `no_sanitize` attribute
work. Being unstable it also needed to be included conditionally only
when compiled with the nightly compiler. And trying the rustversion
crate for this resulted in compilation failure due to TSAN reporting it
doesn't work...
An Apple bug breaks, at least, the bypass property if it is set prior to
starting the VPIO unit. Broken here means that the property can be set
to any value later while running, without having an effect.
The VPIO defaults is bypass OFF and AGC ON. Starting like that and then
turning on bypass as requested is not going to cause an unexpected
glitch. We reset the VPIO to defaults before stopping so that on reuse
it doesn't risk causing a glitch either.
The builtin mic is always mono. Except when VPIO is live somewhere on
the system, as it is then 3, at least on modern MacBooks (with a mic
array). For this reason, and that mixing those 3 channels does not
result in good audio, we already force VPIO with the builtin mic. To
properly pass tests on a local machine with a builtin mic, and to have
clients not be fooled in a way resulting in us having to upmix input
audio, we override the builtin mic's reported channel count and always
report 1.
This could be useful if a caller needs to filter something based on the
owning device's properties, and is not possible to put outside of
get_device_streams since it could get the streams from a sub device, in
case streams were requested on an aggregate device.
Empirical testing has shown that stream IDs lie between their owning
device's device ID and the next higher device ID. This commit uses this
knowledge to filter out VPIO input tap streams (really they're loopbacks
of some output stream) from devices because the VPIO tap streams have
IDs between the VPIO device's ID and the next higher device ID.
Regular aggregate devices are an exception as the tap streams appear to
take the place of the real input streams (by judging at their channel
count) and the real input streams get IDs that seem to belong to the
VPIO device. To solve this we enumerate the aggregate's sub devices and
return all their input streams, filtered per above. This should be
enough for every stream property except StartingChannel (it needs to be
in the context of the right device for obvious reasons) which we don't
use.
The stream IDs on an aggregate device behave differently than a on a regular
device when creating a VPIO unit. This adds a test case for the specific
case with an aggregate devices.
This names it get_sub_devices_or_self to better reflect its semantics.
It is useful in some cases but not in others, so a new get_sub_devices
that does not fall back to return itself is also added. This simplifies
some tests.
Reinit is inherently racy. It can be triggered async by the input
callback, and the output callback can, between the input callback and
reinit(), set the `stopped` flag. In this case, there is no need to
continue with the reinit.
The order of events we want to mimic is:
- input callback
- reinit_async
- output callback
- data callback error -> state_callback(error)
- reinit
- setup error -> state_callback(error)
For a particular stream these events should result in only one
state_callback(error).
We observed a deadlock on macOS 14 that involved main thread (freezing
the parent process UI) and a CoreAudio getter on a platform callback
thread. This patch mitigates that by moving the getter to the serial
queue.