Combination port logic calculates some useful properties for device
groups that we could reuse while generating multiple profiles to support
conflicting devices. Split them into their own functions.
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
Right now this check is rejecting devices whose UCM config specifies
neither a conflicting device nor a supported device list, and accepting
devices which specify both. However, a device without neither list is
actually unrestricted, and a device with both lists is a configuration
error. Fix the check to accept the former.
Furthermore, this is missing another case where an already selected
device might have a supported devices list that doesn't have the
candidate device. Make this function also check against that, and also
make it accept devices already in the set.
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
The existing code meant to generate device groups for combination ports
is tightly coupled to port creation. Similar functionality would be
useful to generate nonconflicting device groups for multiple profiles as
well, so this tries to rewrite it into a more reusable state.
Several things (e.g devices, mapping contexts) use idxsets to store a
device selection. This also switches this conformance check and device
group generation to using idxsets to make it easier to work with those,
with the eventual aim to unify device group representations.
Also try to adjust users of these functions to use idxsets these will
need/return, without causing too much interference.
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
To support having multiple profiles per UCM verb, split the profile
creation into two parts based on whether they should run once for each
verb or for each profile (maybe multiple times per verb).
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
The ucm_create_mapping() function is not idempotent. It looks like it
was meant to be called once per device for the devices of a UCM verb
and takes a profile argument simply because a verb has generated a
single profile so far.
Make sure creating mappings per device and adding those mappings to the
profiles happens as separate steps to make it easier to split UCM verbs
and profiles as concepts.
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
So far each profile had the exact name as their associated UCM verb,
which caused the one to be used where the other should have been.
Explicitly get and use the verb name where that was intended, and make
sure things about profiles aren't named after verbs.
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
Currently each UCM verb generates one profile named the same as the
verb, meaning it's trivial to know which verb the profile belongs to.
This will be slightly harder to do when we generate multiple profiles
per UCM verb (e.g. to make use of conflicting devices).
It would still be possible to parse the profile name to get the UCM
verb, but instead let's keep track of the struct instance representing
the profile's associated verb. This also lets us remove a block of code
searching for the verb by its name.
Co-authored-by: Jaroslav Kysela <perex@perex.cz>
[Alper: Reused Jaroslav's UCM profile context changes for UCM verb
instead of combined devices.]
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
While switching profiles, it's possible that we will want to do more
work besides switching UCM verbs. The alsa-card module already has our
profiles as structs, but passes in only the names instead of the entire
struct. Make things work with the struct instead, so we can add other
things (like a UCM context) to it and use those here.
Co-authored-by: Tanu Kaskinen <tanuk@iki.fi>
[Alper: Split into its own commit and integrated Tanu's snippet.]
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
Right now manipulating device status is done inline once while setting a
port. However, we will need to reuse this code to disable conflicting
devices of a device we want to enable. Split it into enable and disable
helper functions.
There is another issue with the device enable logic, where trying to
disabling an already disabled device sometimes fails. To avoid that,
implement a status helper and check if the device we want to enable is
already enabled/disabled before trying to do so.
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
Modifiers currently keep their conflicting and supported devices's
names, and these names are resolved to devices every time we need to use
them. Instead, resolve these device names while creating the modifier
struct and keep track of the resulting device structs in idxsets, same
as how device structs keep track of their support relations.
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
This is intended to make the current and upcoming code a bit clearer, as
we won't need to constantly check for the existence of these idxsets
before using or operating on them.
It may be possible that the ALSA control element appears
again. Allow this combination by checking, if the pulseaudio
mixer element already exists. Do not create the duplicate
mixer element in this case.
Make BAP nodes align the first sample of their packets at multiples of
the ISO interval, counted in the shared graph sample position. This
skips a few samples (< 10ms) at the start of playback to ensure the
alignment.
Since the sinks align their flush timing to the graph time, this also
results to them sending packets corresponding to the same graph time at
the same real time instants.
Due to packet queues in kernel/controller, the playback may still be off
by multiples of packets. Kernel changes are needed to address that part.
This works towards making BAP left and right channels to be
synchronized in TWS headsets, where the two earpieces currently appear
as different devices.
If transport goes into error state too often, fail instead of trying to
acquire it again.
This avoids getting into a tight acquire->fail->reacquire loop.
We need to acquire and release all transports in the same CIG at the
same time.
Due to current kernel ISO socket limitations, this cannot be done one by
one.
Now that sinks/sources can do transport acquire asynchronously, remove
the workaround that made it synchronous. Do release still synchronously
however.
Change A2DP/BAP transport acquire and release to be async.
Since BlueZ acquiring ISO sockets blocks until all sockets in same CIG
are acquired, BAP transports must be acquired asynchronously.
Allow asynchronous changes in transport state in the sinks/sources.
Also allow transport acquire to be actually synchronous, in this case it
must set transport state during acquire call.
Separate driver start/stop from transport start/stop.
Emit any remove node events before resetting initial profile. It
indicates to the session manager that nodes if any went away before
device disconnected.
Usually the profile is removed first which removes the nodes. This
depends on ordering of events from bluez, which apparently can be
different depending on how remote device disconnects.
Add some guards against doing processing when there has been an error or
the node is not started. Set error status to IO. Continue driving on IO
errors.
In media-sink, there's no need to set RCVBUF.
In media-source, we don't need to set NONBLOCK, as reads are done with
DONTWAIT. Don't set SNDBUF as it's not needed there. Don't set RCVBUF,
but use the (big) kernel default value: decode-buffer will handle any
overruns. Small values of RCVBUF might cause problems if kernel is
sending packets in a burst faster than we wake up.
Single argument static_assert() is only available since
C++17 and C23. Some compilers accept it even in earlier language
versions, but some do not.
Single argument static assertions can be supported by using
a GCC extensions, namely that `, ## __VA_ARGS__` removes the
comma if the variadic argument list is empty. This enables a
construction which passes a pre-determined string as the second
argument of the underlying static_assert() when only a single
argument is used in the `SPA_STATIC_ASSERT()` macro.
Fixes#3050
Don't just limit the max delay of samples we keep in the ALSA ringbuffer
to the buffer_size but to half of it. Make this into a max_delay
variable.
If we have a buffer size of 8192 samples and a headroom of 8192 samples,
when capturing, we would wait for the ringbuffer to contain at least
8192 samples, which would always xrun. When we limit the size to
half, we can still read the data without xruns.
Fixes#2972
On underflow in sources, pad with explicit silence. This avoids the
audioadapter from getting off sync from the cycle. That causes problems
as driver when we want to produce a buffer only a the start of the
cycle.
In some cases, it's also possible that the io already has buffer at the
start of the cycle when rate matching as driver. Currently, we don't
produce buffer in this case, but we should. Fix that by doing things in
the exact same way as ALSA sources do.
On glibc, `pthread_t` is `unsigned long int` while on musl
it has a pointer type. To avoid format string warnings,
cast it to `void *` and use the `%p` format specifier.
Delay output by one packet, so that we never need to wait for
node_process to supply more data when a packet is due out, and can write
audio packets at exactly equal intervals (up to timer/io accuracy).
In principle, this should not be necessary. However, enable it for now,
in case this improves the various stutter/etc. bug reports.
After flushing a packet, encode the next one immediately if we already
have the data. This makes the flush timing more accurate (std ~4x
smaller) as we don't need to wait for the encode.
* Add support for running the sink as a driver
* Detect which compressed formats are actually supported
* Correctly open/close/start/stop device according to the node commands
* Shift away from tinycompress and use Compress-Offload ioctls directly
to be able to access various caps information (including fragment sizes)
which are unavailable in the tinycompress API
* Implement SPA_PARAM_PropInfo and SPA_PARAM_Props support