Previously, if "add_listener" was called on the monitor device, then it
did not necessarily emit events about all devices because it called
`enum_devices()` which does not emit events about already existing
devices. So the very first listener would get all existing devices,
but subsequent ones would not get events about the existence of devices
that have already been seen by the monitor. Fix that by simply emitting
events about all existing devices if the current listener is not the first.
libcamera's CameraManager runs the event handlers on its own thread,
thus synchronization is needed to ensure correct functionality.
Implement that by collecting hotplug events into a mutex protected queue
and signalling the main loop where they can be processed and the
"object_info" event can be safely emitted.
Using a shared_ptr removes the need for manually calling
`libcamera_manager_release()` to drop the reference as it is done
automatically whenever the shared_ptr is destroyed or reset.
Previously, the libcamera manager `impl` object was neither properly
constructed neither properly destructed. As a consequence, for example,
the shared pointers in the `devices` array weren't properly destructed,
although this has been somewhat mitigated by a previous change
that modifed `clear_devices()`.
Previously, the "impl" object was never properly constructed or
destructed, but it more or less worked out since the memory was initialized
to zero bytes and each member had trivial constructors. Except std::shared_ptr,
but an all zero storage happened to be equivalent to a default constructed
shared_ptr.
However, there was the still the problem that the shared_ptr was never
destructed, so it kept the referenced `Camera` object alive, which lead
to memory leaks.
An additional, somewhat unrelated change is that the "props" struct
is removed, and the device identifier is now stored in an `std::string`.
The reason is that `CameraManager::get()` already takes a const std::string reference,
so an std::string must be constructed in any case, so we might as well
take advantage of that and use `std::string` in the "impl" object as well.
Furthermore, wrap the `impl` struct in an anonymous namespace
to avoid name resolution problems.
Previously, in `remove_device()`, the last device would be copied into
the slot of the to-be-remove device. The problem with this is that it
left the shared_ptr untouched in the previously last slot, and hence
creating an extra reference. Fix this by moving instead of copying.
A similar problem is present in `clear_devices()` which also
did not properly dispose of the shared_ptrs. Fix that by
calling `reset()` on each device's camera pointer.
BlueZ adds the Endpoint property to the Properties dictionary of
SelectProperties.
This allows to know which remote Endpoint is an acceptor, and so which
local transport should be used as an initiator.
Multiple transport from the same device may share the same stream (CIS)
and group (CIG) but for different direction, e.g. a speaker and a
microphone. In this case they are linked.
In this case:
- On acquire, if another transport has already been acquired, the new
transport should not call Acquire or TryAcquire but re-use values from
the previously acquired transport,
- on release, the closing of transport fd and call to Release should be
done only for the last transport.
We can't determine which remote endpoint or device the
SelectConfiguration() call is associated with. For LE Audio BAP, as this
method is called only for the Initiator we set the whole instance as a
Central/Initiator.
This flag is unset on BAP media endpoint removal.
Add methods activate() that is called before first call to run() when
stream starts and deactivate() that is called after last call to run()
when stream stops. This makes it possible for aec-plugins to reset their
state between streams.
DSD64 would be a rate of 44100 * 64 / 8. When packed in U32_BE, we would
negotiate 44100 * 8 / 4 (88200) with the device, this means all rates
from 88200 and up are allowed for DSD64 in U32_BE.
Although pos will be set to 0 in the first iteration, the compiler does
not seem to figure this out, so help it a little:
../spa/include/spa/support/log.h:306:21: warning: ‘pos’ may be used uninitialized [-Wmaybe-uninitialized]
306 | pos += sprintf(str + pos, "%02x ", buf[i]); \
| ^~
../spa/include/spa/support/log.h:301:13: note: ‘pos’ was declared here
301 | int pos; \
| ^~~
When the device is not running but has a format, close/open the
device to get all the available formats again. Do the same when
setting a format.
Otherwise, the configuration space of the device is restricted to the
current negotiated format and we can't query the other possibilities
or change it.
Fixes#2625
A lot of code calls spa_hook_remove() from error paths where the hook
and therefore the list may not have been initialized.
This leads to null-derefences.
Only update the resampler rate when we ask for more data, when we have
more input data, use the previously configured rate to calculate how
many samples we will consume.
Fixes resync errors with multiple sources. One source would do rate
matching, audioconvert would ask it to produce X samples, the source is
scheduled to produce the samples, the rate match is updated with the new
rate correction, audioconvert is scheduled again. It should now use the
X samples it asked to produce and apply the new rate correction for the
next iteration.
If no packets have been received and spa_bt_decode_buffer_process is
called, this->packet_size.max == INT32_MIN, which can give overflows.
Guard against this condition, although it should be harmless.
At the moment, cross compilation may not work in certain cases because
checks are carried out against the build machine instead of the host machine.
Replace uses of `build_machine` with `host_machine` to fix that.
In native compilation, all three "machine objects" available in meson
are the same, so this change should have no effect in that case.
More: https://mesonbuild.com/Cross-compilation.html
React immediately to "bad" buffer level. Use smaller bitrate
increments/decrements. Fix ABR timer increment for fragmented packets.
Handle actual bitrate being smaller than target during silence.
Only simple upmixing would replicate the FC channel into REAR/SIDE.
The PSD method would take the diff between FL/FR (which would
be 0 if only FC is available) and not generate output.
Always use the DSP rate on DSP ports for format conversion, not the
previous used rate.
This avoids some resampler reconfiguration as it negotiates a non-passthrough
rate conversion and then switches to passthrough when the rate correction is
done to match the graph rate.
See #2614
libcamera commit 1c4d4801850559d6f919eef5c2ffbaf7675dbc46
changed the return type of libcamera::ControlList::get()
to be std::optional<T>. Adapt the code to this change.
Fixes#2575
By: Kevin Yin
R->A calculation removed: it wasn't valid anyway. No behavior change.
Placed existing A in there directly.
cosh window -> strangely tweaked exp window: remove the discontinuity
at the border, which is wrong for a window function. If A changes in the
future, this window will be better. With the current A, you will not be
able to tell the difference on any graph. (Of course, it's not a cosh
window anymore.)
Fixes#2574
Support Opus as A2DP vendor codec.
The specification for vendor A2DP codec is our Pipewire-specific one, so
it is compatible only with devices running Pipewire.
The device is not know at SelectConfiguration time, so the settings
argument in select_config is currently unused. Pass on a global settings
dict instead, so that codec parameters can be configured.
Also add settings argument to caps_preference_cmp.
Bump codec API version.
Add a flag A2DP_CODEC_FLAG_SINK to incidate a sink endpoint.
Also enum_config and caps_preference_cmp may need to know whether the
codec is being configured for SRC or SNK. Also add the flags argument to
init_props.
Bump codec API version.
a2dp-sink writing duplex data to the BT socket breaks a2dp-source source
polling, also in A2DP source role. Hence, use the timer-based polling
workaround always in duplex mode.
As currently implemented, input format convert channel remap is no-op.
This is because although the out_datas array is permuted, the original
pointer array is not referred to later on, so the only effect is that
the temporary data array is stored in permuted order.
Fix the permutation by permuting the pointers only for the conversion
step.
Make a new noise method called PATTERN and use it to add a slow (every
1024 samples) repeating pattern of -1, 0.
Only use this method when we don't already use triangular dither.
See #2540
Also clamp the amount of input samples we push when flushing. do several
rounds of zero pushing until we have flushed enough.
Handle the cases where no input is needed or no output is generated.
Fixes crashes when downsampling from 96000 to 1000 Hz or so.
The order of attribute changes is random, so it's possible that controlCX is
accessible before the other devices, which marks the device as available but it
actually fails to open. Only consider the device accessible if both control and
PCM devices can be accessed.
This requires reacting to ATTRIB changes of pcm devices as well now.
Fixes#2534
We need to check the last offset against the size of the buffer, not the
remaining size in the buffer.
When the writing is split, this could cause the buffer to be reused
wrongly.
See #2536
Add avx mixer to test and benchmark
Rework and unroll the avx mixer some more.
The SSE one is 10 times faster than the C one, The AVX is 20 times
faster. The SSE2 function is 5 times faster than the C one.
User changing volume via headset buttons should be treated on the same
level as changing from desktop UI. Also initial headset volume should
be considered saved (even though session managers currently ignore the
initial route values on route restore).
Mark route as saved on volume events.
When emitting node, get initial volumes from transport hardware volume,
if available.
The session manager usually overrides these immediately with saved
values, but it's better to show the HW volume when the node first
appears.
The A2DP and HFP profiles may have different volume curves, so trying to
convert volumes between the two can produce undesirable volume spikes.
For example, when one of them is using hardware volume and the other
software.
Fix by separating HFP and A2DP routes.
Let the mixer functions accumulate the intermediate results into a
larger size variable and then clamp to the final precission. This avoids
distortions because of intermediate clamping.
Although the access pattern of the reads are no longer sequential, the
writes are sequential and we don't need to read intermediate values.
Together with the avoided clamping this is probably faster overall.
Add a unit test for the various cases.
When the audioconverter needs more data, let it return NEED_DATA. This
can happen before the ports actually have consumed all the input data.
For example, then the next cycle would require 1024 samples but there
are currently only 16 samples queued, the next cycle will consume the
16 samples and then need another buffer to produce output.
For rt streams, this is not a problem because a new buffer will be
fetched in the next cycle synchronously.
When the stream is async, we can use this NEED_DATA to prefetch a
new buffer so that we have one in the next cycle.
This fixes hickups with async streams that provide random sized
buffers.
Move the setup of the output buffers first.
Then figure out how many samples we need to produce and consume.
Make sure we use the resampler to only convert the input samples that
are needed to produce the output samples.
Fixes some muddled sound with mpv when upmixing.
Remove the redundant remap array.
First set up the array with remapped output pointers in case we
can do passthrough output. Make sure stages write to the remapped
array in passthrough.
Remap the input array after unpack/convert.
This avoid an input and output memcpy in the common case of
remapping.
When we are actively driving the stream and the converter needs more
data, call the stream process function again to get it so that we
don't underrun.
Fixes#2494
a2dp-source as driver does not produce regularly spaced graph cycles,
because A2DP is not isochronous. This causes e.g. crackling for alsa
etc. that expect regular timings. It also does not rate match.
Change a2dp-source to trigger graph on regular intervals. Change recv to
only accumulate data to a buffer, and put data to buffers in process().
Rate match with DLL, keeping average buffer level constant. Keep track
of jitter to determine a safe target value.
Tweak the conversion constants a bit so that they handle the
extreme ranges a bit better.
Align the C and vector instructions.
Reactivate the unit test asserts when a conversion fails.
Do the channel remapping to the cannonical format when we
deinterleave/interleave instead. Otherwise we would completely skip
the remapping when we have interleaved input.
Fixes#2502, #2490
When we get something else that a drain status as input, bring us back
to the non-drained state.
When we are draining, don't remove the drained flag on the input
io status. This needs to be cleared by the host when the draining is
finished.
Fixes speaker-test
Ensure that our temporary buffers can hold at least quantum_limit
samples. When no output or input is connected, we can generate up
to a quantum_limit of silence, which requires all the buffers to
be scaled correctly.
Fixes a segfault in mpv.
Make a new uint42_t and int24_t type and use that to handle 24 bits
samples. This makes it easier because we can iterate and copy the
structs like other types.
Make dither noise as a value between -0.5 and 0.5 and add this
to the scaled samples.
For this, we first need to do the scaling and then the CLAMP to
the target depth. This optimizes to the same code but allows us
to avoid under and overflows when we add the dither noise.
Add more dithering methods.
Expose a dither.method property on audioconvert. Disable dither when
the target depth > 16.
We need to do dithering and noise when converting f32 to the
target format. This is more natural because we can work in 32 bits
integers instead of floats.
This will also make it possible to actually calculate the error between
source and target values and implement some sort of feedback and
noise shaping later.
Move the noise setting in the dither struct so that it can be
handled separately.
Setup dither separately.
Set used cpu_flags in structures after setup.
The quantize is the amount of bits we want to keep from the original
signal, subtract the amount of bits for noise. Clamp this to 0 (all
noise).
Calculate the scale factor better with powf() and avoid overflows.
Fixes#2479