Add a new pulse.zeroramp.gap property that will enable gap detection and
fade-in/fade-out on gaps on playback streams.
Make a rule to enable this on chrome, which does not do a cork/pause
when a stream is paused but sends out silence. With the gap detection
enabled, this allows the audioconvert to perform fades to avoid pops and
clocks from sudden DC changes at the gaps.
Fixes#4745
Make a new zeroramp.duration and zeroramp.gap property on audioconver.
It detects N samples of silence before triggering a fade-in or fade-out
of the given zeroramp.duration.
The zeroramp.duration is by default 5ms and zeroramp.gap is set to 0.
When the zeroramp.gap is set to 0, the audioconver will not do any gap
detection but it will only do fade-out from the last sample when the IO
Buffer area is removed from a port.
This by default makes the audio adapter perform a fade-out when the last
input of the port mixer was removed and the mixer is no longer scheduled
and the IO Area removed from the audioconverter input port.
Pass the zeroramp.duration property from the node to the port and then
to the mixer so that it can be configured.
Add the zeroramp.duration to the docs.
When the port IO_Buffers is set, do a fade-in of the next buffer data
into the final mixed output.
When the port IO Buffers in cleared, do a fade-out of that last sample.
Add a zeroramp.duration property that controls the length in seconds of
the fade-in/out curve. By default, set this to 5ms.
The fade-in and fade-outs avoid popping noise from sudden DC voltage
changes when ports a linked and unlinked.
It will also trigger when the upstream peer is paused, because that will
also remove the IO Buffers from the mixer ports.
Each port mix embedded its buffer table in struct mix as a fixed array
buffers[MAX_BUFFERS] (MAX_BUFFERS == 64), and every struct buffer in turn
embedded datas[MAX_DATAS] (MAX_DATAS == 256) and metas[MAX_METAS]. This
reserved ~674 KB per mix in create_mix() regardless of the actual buffer,
data and meta counts, multiplied by every port mix (one per link end).
Turn buffers, datas and metas into pointers and allocate the buffer table
together with the per-buffer data and meta pools in a single calloc in
do_port_use_buffers(), sized to the real number of buffers and their
n_datas/n_metas, and release it in clear_buffers(). create_mix() now starts
with an empty (NULL) table, so an idle mix costs only the struct header.
This mirrors the dynamic-allocation approach already used for the
audioconvert and audiomixer port buffers and removes the dominant heap and
mmap consumer in the client-node graph.
Co-authored-by: Copilot <copilot@github.com>
Fix incorrect assertion condition that compares fragment_size
with max_fragments instead of num_fragments.
The condition should validate num_fragments range, not mix
fragment_size with max_fragments limit.
Since we know DTS and AC3 devices don't provide the snd_pcm_info of the
real underlying device, let's look for them by name (doesn't seem to be
a better way to detect this case), and avoid overwriting any previously
detected h/w device index (from one of the PCM paths).
Always place the follower EnumFormat or Format as internalFormat on
the EnumPortConfig and PortConfig respectively.
This ways, internalFormat always refers to the follower formats and
the format is the external format after (optional) conversion by the
adapter.
The idea was to keep some sort of compatibility with older versions but
that doesn't really apply because the format was never used on
EnumPortConfig and interalFormat didn't exist.
Like the audioconvert node, the DSP mixer embedded a fixed
buffers[MAX_BUFFERS] array (MAX_BUFFERS == 64) in struct port, each
struct buffer holding datas[MAX_DATAS] (MAX_DATAS == 64), reserving
~37 KB per port regardless of the actual buffer/channel count. A DSP
mixer port is created for every link set that mixes into a port, so
this adds up.
Allocate the buffer table and the per-buffer data-pointer pool in a
single calloc in port_use_buffers(), sized to the real number of
buffers and their data blocks, and release it in clear_buffers().
remove_port() now clears the port before zeroing it so a pooled port
does not leak its allocation, and impl_clear() clears the input and
output ports on teardown. Mirrors the audioconvert dynamic-allocation
change.
Co-authored-by: Copilot <copilot@github.com>
The port buffer table was embedded in struct port as a fixed array
buffers[MAX_BUFFERS] of struct buffer, each holding datas[MAX_DATAS]
(MAX_DATAS == SPA_AUDIO_MAX_CHANNELS == 64). This reserved ~17 KB per
port regardless of the actual buffer/channel count, multiplied by every
active port.
Allocate the buffer table and the per-buffer data-pointer pool in a
single calloc in port_use_buffers(), sized to the real number of
buffers and blocks, and release it in clear_buffers(). free_dir() now
clears each port so the allocation (and any mmapped buffer data) is
released on node destruction. This mirrors the dynamic-allocation
approach already used for the channelmix matrices and preserves a lot
of memory in the common low-channel-count case.
Co-authored-by: Copilot <copilot@github.com>
In the past, it made no difference whether or not an RTP sink's
sess.ts-direct property was set to true. This changed in commit
6bf81ebe59 . Now, sess.ts-direct must be
set to true if an associated RTP source is using direct timestamp mode.
That's because when the RTP sink's direct timestamp mode is not enabled,
it will set the new internal ts_align offset to a nonzero value, which
then corrupts timestamps and causes out of sync playback in the RTP
sources.
Also, copy the sess.ts-direct property from the global RTP sink props
to the stream props to match the behavior of the RTP source module.
* Rename do_disconnect to do_disconnect_core, since the original name
is ambiguous (it can be interpreted as being about disconnecting
a network socket).
* impl->session_name is not used at all, so remove it.
This fixes code duplication, since the checks are the same regardless
of payload type. hlen is also calculated the same across payload types,
so it is moved as well. The impl->receive_rtp() function pointer then
solely addresses the actual payload processing.
Rename format_info to rtp_format_info to make the purpose of that struct
and the associated variable clearer.
Document what info and stream_info are there for, since their purpose
is not immediately obvious.
Using find_audio_format_info() for Opus and MIDI makes no sense, since
both of these only have one info each, which is always found, so one might
as well just use that single, always-matching RTP format info directly.
This means that the audio_format_info array remains there solely for PCM,
so rename it to rtp_pcm_audio_format_info.
Use the Format for all possible formats on the
EnumPortConfig and the configured external format on PortConfig.
Make a new internalFormat that contains the configured format of the
adapter follower.
Make pw-top use the PortConfig internalFormat in the FORMAT view and fall
back to Format, when not available (for older clients).
Enumerating the Format on a node does not make sense, this is a port or
stream only property.
Move the enum of the formats to the portconfig param, where it is used
to configure the port group.
This also makes it possible later to have multiple port groups on a node
and have the session manager configure and link them individually.
Some objects might aggregate properties from multiple other objects.
Make sure they are grouped together with the group property.
This makes it possible to get per port group properties, for example.
update_delay is called primarily when the stream format or latency changes,
and from playback thread, if stream reports different delay as before.
This function calculates the number of compensate samples for each stream
based on the latencies of other streams (which must be in a streaming state).
During the first playback on a new format, update_delay is called multiple times
due to format or latency changes. The delay is calculated only from streams
that are currently streaming. If some streams are not yet streaming, their
latencies are ignored, and the delay is updated later in the processing
thread. The processing thread also stores the stream delay in a local variable
(accessed only from that thread, thus requiring no locking).
On a subsequent playback using the same format, update_delay is still called a
few times, and the delay is updated based on the currently streaming streams.
If some streams are not streaming, their latencies are ignored.
However, this time, the processing thread fails to update the delay for the
previously non-streaming streams. Because the format didn't change, the streams
delay matches the last stored delay from the previous playback. As a
result, the compensate samples are not recalculated.
To properly update the compensate samples, update_delay must also be called
when a stream's state changes to streaming (avoiding the need to clear the
thread-buffered value, which would require locking in the processing
thread).
Target number of samples stored in buffer is calculated by
target = SPA_CLAMP(SPA_ROUND_UP(SPA_MAX(spike * 3/2, duration),
SPA_CLAMP((int)this->rate / 50, 1, INT32_MAX)),
duration, max_buf - 2*packet_size);
At beginning of the playback is spike == 0, so the target is set to
duration. Our BT is not able to maintain such low latency and drops
so spike is increased. This can happens multiple times until playback
is stable.
This strategy causes few dropouts at the beginning of a new playback,
in my case on each new track.
Add min-latency-ms property to reduces spikes and the likelihood of
dropouts at the beginning.
This property is useful for forcing tracking behavior even if the clock
can directly be used by timerfd.
This can be useful in cases where the clock is for example the realtime
clock and can shift and change during playback unexpectedly. The node
driver can handle this without a DLL. But, in some cases, graphs can
experience a more stable behavior if the DLL is used and it manages
the readjustment to the updated time.
This is also useful for testing and debugging the DLL's behavior.
module-rtp-sink is sending data timestampted with clock provided from
a driver. When driver jumps (for example due ptp master change) rtp sink
has to jumps too. Rtp sink is checking for difference between expected and
actual position and is big enough the jump is done. As resync value in
driver is not equal to diff between positions, sink could ends with
not precise timestamps up to 1 quantum.
Emit discont flag when driver jumps, so sink can be reinitialized without
its own heuristic.
If the error is very large (after a big discontinuity in the timestamps
for example), the DLL has a hard time compensating, and a full restart
of the DLL without the old measurement history is more appropriate to
get the synchronization back on track faster and with fewer oscillations.
When jack state changes cause port properties to be updated, mark EnumRoute and
Route params as changed and call emit_info() so that the device re-emits the
route parameters with the updated port properties.
Make a hard volume limit property that is configured with the config and
can never be changed.
Make a soft volume limit property that can be changed at runtime.
The used volume limit is a combination of the soft and hard limit.
Add 3 levels of volume limits.
1. Add api.acp.min-volume and api.acp.max-valume on the ACP devices that
is applied to all noded from this device
2. Add api.acp.device.<node-name>.min-volume and
api.acp.device.<node-name>.max-volume that is applied to all nodes
from the device with the given node-name.
3. Add api.acp.port.<port-name>.min-volume and
api.acp.port.<port-name>.max-volume that is applied to all ports
from the device with the given port-name.
The volume settings on an ALSA nodes can either go through the device on
the Routes (ports) to control the hardware mixer volumes and then the
remainder is performed on the nodes in software by the channel mixer.
We need to set the limits on the channel mixer when the hardware mixer
does not have routes.
This is not an easy way to set the volume limits but it provides a
static configuration option to enforce the limits. An easier
configuration option will also make it easier to change/bypass the
limits, which these options can guard against.
See #5266, #4323, #1517