Manage them like we do on the client and reuse logic. Make a node
function to safely add and remove a target.
Activate the targets from the process loop when we can be sure that we
can resume them. This avoids incrementing the pending state when we are
not going to be able to resume the nodes (like when the cycle is ongoing
and we have already been scheduled) and avoids glitches and xruns.
When a node is added to the poll loop, it can activate its own targets.
This is mostly for driver so that they have something to schedule and
can then activate the other targets.
Try to resume the target when it is removed and we are supposed to be
scheduled.
Also add targets to the target_list when the node is remote to make sure
the profiler can see the targets as well.
Keep the node in the INACTIVE state as long as the eventfd of the node
is not added to the loop. Skip nodes in the INACTIVE state from going to
the NOT_TRIGGERED status, which avoids scheduling the node.
Make sure we remove any local targets we have in a node when we export
it, we will receive new targets from the server.
This should eliminate any glitches when adding and removing nodes from
the graph.
See #4026, #2468
Atomically change the node status from TRIGGERED to AWAKE. Only trigger
the peer nodes when the node was previously in the AWAKE state.
When we remove a node from the graph or when we destroy a link, we need
to manually resume the peers. We can do this now by atomically setting
the node to FINISHED and checking if it was previously != FINISHED.
This ensures that removing nodes/links never leaves some nodes (and also
the driver) untriggered and cause a xruns.
Fixes#4026
The larger events need to be copied into the target buffer at the same
offset as the source buffer or else we overwrite the header and make
a corrupt buffer.
Instead of doing (cycle+1) & 1 for output ports, simply swap the io
areas depending on the port direction (0 = input, 1 = output) and
just to cycle&1 for all ports.
When node.async is set, make the node async.
Advertize SPA_IO_AsyncBuffers on mixer ports when supported. Set a new
port flag when AsyncBuffer is supported on the port.
When making a link and if one of the nodes is async and the linked ports
support AsyncBuffer, make the link async and set this as a property on
the link. For async nodes we will use SPA_IO_AsyncBuffers on the mixer
ports.
Nodes that are async will not increment the peer required counters. This
ensures that the peer can start immediately before the async node is
ready.
On an async link, writers will write to the (cycle+1 & 1) async buffers
entry and readers will read from (cycle & 1). This makes the readers read
from the previously filled area.
We need to have two very controlled areas with specific rules for who
reads and who writes where because the two nodes will run concurrently
and no special synchronization is possible otherwise.
These async nodes can be paused and blocked without blocking or xrunning
the rest of graph. If the node didn't produce anything when the next
cycle starts, the graph will run with silence.
See #3509
Only activate the nodes when it was not-triggered, do this check with an
atomic compare-and-swap so that we only activate a node once.
We might be able to use this later to make sure that we resume the
untriggered peer nodes when we remove a node from the graph.
nframes in the midi buffer should be set to the current cycle
buffer_size and it should restrict the timestamps that can be set on the
midi events.
Keep the last max_frames around in a globals so that we can use it to
set the midi buffer to the default size.
Return NULL when we do jack_port_get_buffer() with larger nframes than
the current cycle buffer_size, just like JACK. Otherwise this could
result in a crash when we try to mix more than the available buffer
space.
Check and reset the midi buffer better. Check if the MAGIC is still ok.
jack_midi_reset_buffer() should restore the MAGIC and other values.
The output buffer size is always the current cycle buffer_size.
The size that is give by the JACK API is only used to restrict the
number of mixdown samples or midi offsets.
Fixes#3892
On the midi input ports, do the same trick as on the output ports:
first convert the midi to JACK and then copy the whole buffer to the
port specific storage.
This makes it possible to have a different midi buffer per port and
allow multiple threads to get the buffer concurrently.
Fixes#3901
node.sync-group can contain a list of strings. When a node in the graph
sets node.sync = true, it will be scheduled with all of the other nodes with
common node.sync-group strings. By default all nodes are placed in
group.sync.0 except the freewheel and dummy driver.
Use this to ensure that all nodes are grouped under the same driver
(that is not the freewheel and dummy driver) as soon as the transport is
started so that the transport is visible to all nodes from the same
sync-groups. We also don't deactive the sync-group anymore for the node,
even if the transport is stopped, to avoid driver changes and transport
jumps.
When the node that activated the sync/transport is destroyed, things are
restored to their original state. Note that this is different from JACK
where starting the transport outlives the application and always needs
to be explicitly stopped again. We can't really do this (by default) because
it leaves the graph in an unnatural state with all devices in sync.
The reason for the node.sync-group is that it is possible to still have N
different subgraphs with a separate transports by manually specifying
the node.sync-group.
It's also slightly different from the node.group, that is always active.
The sync-group is something you only want to enable in specific cases
because it groups drivers together and enables adaptive resampling etc.
It's also possible to place this option in the jack.conf file to
automatically sync all devices and apps as soon as a jack app is started.
Fixes#3850
Don't just overwite the node.group property when changing the freewheel
thate but append the frewheel group and remove it again to preserve
the original node.group.
Keep track of the active number of mixer ports and update the global mix
io in sync with the data thread because that is where we will check the
state of the global mix io.
This is mostly important for output ports. When removing all links from
an output port, we first will clear all the mixer io and then remove the
global mixer with client_node_port_set_mix_info(). If we don't clear the
io before that, the data thread will be using that buffers as they are
cleared.
See !1915
When we clear the port io, pause the core until the invoke call
completed. This way we don't start processing other messages until we
have safely removed the port io.
Normally, when clearing a link on a port, first the mix io will be set
to NULL and then the format will be cleared, which clears the buffers
as well. By delaying the processing of the format clear until the io
is removed from the data thread we avoid taking away the buffer memory
from the processing thread prematurely.
When creating a link, first the format and buffers are configured and
then the io is set, which should be safe in all cases.
See !1915
Make a rtprio-server and rtprio-client option. Leave the server
priority by default to 88 but lower client priority to 83. JACK
does something similar by setting clients to rtprio-server - 5.
Make module-rt use the client priority by default and bump the server
priority explicitly in the config file.
Leave the pulse-server to the default rtprio-client, there is no reason
to lower this any further because it is really just a regular client.
Bump the ffado packetizer thread to rtprio-server + 5 because that is
also what JACK does.
88 is still much higher than the value of 60 that JACK uses in
Fedora but now this is at least configurable.
Add a new property that controls how connections between other ports are
handled. This can be used to block jack apps from connecting or
disconnecting port they don't own.
We already checked if a node is_jack when it appeared in the registry
with the client.api property, so don't check again with ALWAYS_PROCESS
when the node properties changed.
When we get a node info about our own state, we can use the active state
of the node to decide if we are running or not.
See #3794
We should not use nsec from the clock as the current_usec because
current_usec is supposed to be an idealized time when the wakeup would
have happened and nsec is when it actually happened.
Instead use next_nsec and subtract the rate corrected period from it to
get the idealized current_usec.
We can still use nsec to calculate the elapsed time since the wakeup and
be sure that it is always in the past.
Some JACK clients place OSC messages in MIDI buffers. Try to detect the
OSC messages and mark the control as OSC.
Also allow OSC control to be converted to JACK MIDI.
This should make things work better with native PipeWire clients that
handle MIDI and OSC.
Ardour calls jack_port_get_buffer() from multiple threads. For audio
buffers this will result in mixing the input samples into the target
buffer and there is no window for having a corrupt buffer.
For MIDI, there is a problem because we need to convert from and to
PipeWire MIDI and while we do that from multiple threads, the midi
buffer can be incomplete or corrupt.
Fix this by building the intermediate POD to a thread-local scratch area
and then copy it to the target buffer. If this is done from multiple
threads there is no moment where incomplete data can be seen in the
target buffer.
Make the complete midi-scratch buffer thread_local so that we also avoid
a race when converting midi data from a foreign port.
When we write into the scratch buffer when mixing and converting input
midi data, we also avoid a race there.
Fixes#3632
Avoid reading from the activation directly to get cycle times but copy
the relevant fields of the clock when the cycle starts. Use the unique id
to get a consistent copy of the data.
This avoids some stay frames and values in jack_showtime.
Improve get_buffer debug.
Add some extra checks for the type before we try to use the object as a
port.
Set latency range to 0 when called with wrong port. Ardour seems to call
into us with ports from a previous jack_client..
When we get a midi buffer directly from one of our peer ports, we need
to convert it to a jack midi buffer.
Note that this previously returned NULL because of the size check of the
midi buffer, which was likely much smaller than the frames argument.
This fixes midi event recording preview in ardour.
PW_VERSION_NODE is 3 while PW_VERSION_NODE_EVENTS is 0, I am unsure if
it could have caused issues. Same thing with PW_VERSION_PORT &
PW_VERSION_PORT_EVENTS.
I have not seen code checking this version number.
Signed-off-by: Théo Lebrun <theo.lebrun@bootlin.com>
do a sync when starting up client and sync when creating a new proxy so
that we get all the proxy updates before the client is opened.
This ensures all ports are enumerated correctly.
Fixes#3618
Don't set the port io to NEED_DATA in prepare_output because this
might be called when we get the data from an input port that is linked
to out output port.
See #3514