Compare commits

...

235 commits

Author SHA1 Message Date
Wim Taymans
5075f27ea0 filter-graph: small convolver optimizations
Use FMA when we can, make sure FMA compilation is supported and the CPU
also supports it at runtime.

Avoid divisions by doing the modulo increment more explicitly.
2026-04-10 12:17:25 +02:00
Wim Taymans
c2f85ffc51 filter-chain: improve docs
Add the default values to the docs for some of the convolver config
variables.
2026-04-09 16:35:17 +02:00
Wim Taymans
b1a9bc966b tools: fix compiler error on older compilers
COLS could be very small and the statusbar array might overflow with
strcpy and strcat. Also initializing the variable array seems to cause
problems on older compilers.

Instead use a fixed array that is big enough to hold all possible
values we write into it.
2026-04-09 16:32:16 +02:00
Wim Taymans
1ed1349e7c test: fix some compiler warnings
be64toh returns a uint64_t so there is no need for casts in the test.
2026-04-09 13:04:35 +02:00
Martin Geier
42415eadd9 bluez5: iso-io: don't use streams without tx_latency enabled for fill level calculation
When there is a stream without tx_latency enabled, the fill_count ends
with MIN_FILL value. This causes one buffer of silence to be written to
every stream before the actual data in each iteration.
Consequently, more data is written than consumed in each iteration.
After several iterations, spa_bt_send fails, triggering a
group_latency_check failure in few next iterations and leading to
dropped data.

Skip streams without tx_latency enabled in fill level calculations
to prevent these audio glitches.
2026-04-09 08:00:37 +00:00
lumingzh
eeaddbb385 update Chinese translation 2026-04-09 07:45:56 +00:00
Christian F.K. Schaller
f5259828b6 tools: add pw-avb-virtual for virtual AVB graph nodes
Add a standalone tool that creates virtual AVB talker/listener endpoints
visible in the PipeWire graph (e.g. Helvum). Uses the loopback transport
so no AVB hardware or network access is needed.

The sink node consumes audio silently, the source produces silence.
Supports --milan flag for Milan v1.2 mode and --name for custom node
name prefix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
14310e66fe module-avb: extend transport abstraction to stream data path
Add stream_setup_socket and stream_send ops to avb_transport_ops so the
stream data plane can use the same pluggable transport backend as the
control plane. Move the raw AF_PACKET socket setup from stream.c into
avdecc.c as raw_stream_setup_socket(), and add a raw_stream_send()
wrapper around sendmsg().

Add a stream list (spa_list) to struct server so streams can be iterated
after creation, and add stream_activate_virtual() for lightweight
activation without MRP/MAAP network operations.

Implement loopback stream ops: eventfd-based dummy sockets and no-op
send that discards audio data. This enables virtual AVB nodes that work
without network hardware or privileges.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
ffa855d76e test: add additional AVB protocol coverage tests (phases 7-8)
Add 26 new tests covering protocol areas not yet exercised:

Phase 7 (12 tests):
- MAAP conflict detection: probe/announce conflicts, defend logic
- ACMP disconnect: RX forwarding, TX without stream, pending timeout
- AECP GET_AVB_INFO: success path and wrong descriptor type
- MRP timers: leave-all and periodic timer verification
- MSRP talker-failed: attribute processing with failure info

Phase 8 (14 tests):
- MVRP: attribute lifecycle, VID packet encoding
- MMRP: attribute type verification (MAC + service requirement)
- ADP: duplicate entity, targeted discover, readvertise, departure
- Descriptor lookup: edge cases, data integrity after add
- AECP commands: GET_CONFIGURATION, GET_SAMPLING_RATE, GET_NAME

Total test count: 72 tests across 8 phases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
cec53759dd test: add AVTP audio data path tests
Add 10 Phase 6 tests for the AVTP audio data path:
- IEC61883 and AAF packet structure layout validation
- 802.1Q frame header construction
- PDU size calculations for various audio configurations
- Ringbuffer audio data round-trip integrity
- Ringbuffer wrap-around with multiple PDU-sized writes
- IEC61883 receive simulation (packet → ringbuffer)
- IEC61883 transmit PDU construction and field verification
- Ringbuffer overrun detection
- Sequence number and DBC counter wrapping

These tests validate the AVTP packet formats and audio data path
logic without requiring hardware, AF_PACKET sockets, or CLOCK_TAI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
fdfede8b96 test: add AECP/AEM entity model tests and document new bugs
Add 12 Phase 5 tests for the AECP/AEM entity model:
- READ_DESCRIPTOR for existing and non-existent descriptors
- AECP packet filtering (wrong EtherType, wrong subtype)
- Unsupported AECP message types (ADDRESS_ACCESS, etc.)
- Unimplemented AEM commands (REBOOT, etc.)
- ACQUIRE_ENTITY and LOCK_ENTITY for legacy mode
- Milan ENTITY_AVAILABLE, LOCK_ENTITY (lock/contention/unlock)
- Milan LOCK_ENTITY for non-entity descriptors
- Milan ACQUIRE_ENTITY returns NOT_SUPPORTED
- Milan READ_DESCRIPTOR

Also adds Milan test server helper with properly sized entity
descriptor for lock state, and AECP/AEM packet builder utility.

Updates avb-bugs.md with 3 new bugs found (bugs #6-#8).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
e661c72272 test: add ACMP integration tests and bug documentation
Add Phase 4 ACMP integration tests:
- NOT_SUPPORTED response for unimplemented commands
- CONNECT_TX_COMMAND with no streams (error response)
- Entity ID filtering (wrong GUID ignored)
- CONNECT_RX_COMMAND forwarding to talker
- Pending request timeout and retry
- Packet filtering (wrong EtherType/subtype)

Also add avb-bugs.md documenting all bugs found by the test suite.

Total: 24 tests, all passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
f5c5c9d7a3 test: add MRP state machine, MSRP, and packet parsing tests
Extend the AVB test suite with Phase 3 tests:

MRP state machine tests:
- Begin/join/TX cycle with NEW attribute
- Join then leave lifecycle
- RX_NEW registrar notification callback
- Registrar leave timer (LV -> MT after timeout)
- Multiple coexisting attributes

MSRP protocol tests:
- All four attribute types (talker, talker-failed, listener, domain)
- Domain and talker transmit via loopback capture
- Talker-failed notification (validates NULL deref fix)

MRP packet parsing tests:
- Single domain value parse
- Leave-all (LVA) flag detection
- Three-value base-6 event decoding

Total: 18 tests, all passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
ef4ff8cfd0 test: add AVB protocol test suite with loopback transport
Add a test suite for the AVB (Audio Video Bridging) protocol stack that
runs entirely in software, requiring no hardware, root privileges, or
running PipeWire daemon.

The loopback transport (avb-transport-loopback.h) replaces raw AF_PACKET
sockets with in-memory packet capture, using a synthetic MAC address and
eventfd for protocol handlers that need a valid fd.

Test utilities (test-avb-utils.h) provide helpers for creating test
servers, injecting packets, advancing time, and building ADP packets.

Tests cover:
- ADP entity available/departing/discover/timeout
- MRP attribute lifecycle (create, begin, join)
- Milan v1.2 mode server creation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
a73988d38d module-avb: add transport abstraction for pluggable network backends
Introduce struct avb_transport_ops vtable with setup/send_packet/
make_socket/destroy callbacks. The existing raw AF_PACKET socket code
becomes the default "raw" transport. avdecc_server_new() defaults to
avb_transport_raw if no transport is set, and avdecc_server_free()
delegates cleanup through the transport ops.

This enables alternative transports (e.g. loopback for testing) without
modifying protocol handler code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
d9821d09c7 module-avb: fix Milan lock entity error response and re-lock timeout
Fix two bugs in handle_cmd_lock_entity_milan_v12():

1. When server_find_descriptor() returns NULL, reply_status() was called
   with the AEM packet pointer instead of the full ethernet frame,
   corrupting the response ethernet header.

2. When refreshing an existing lock, the expire timeout was extended by
   raw seconds (60) instead of nanoseconds (60 * SPA_NSEC_PER_SEC),
   causing the lock to expire almost immediately after re-lock.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
4e62826e01 module-avb: fix legacy AECP handlers reading payload at wrong offset
handle_acquire_entity_avb_legacy() and handle_lock_entity_avb_legacy()
incorrectly treated the full ethernet frame pointer as the AEM packet
pointer, causing p->payload to read descriptor_type and descriptor_id
from the wrong offset. Fix by properly skipping the ethernet header,
matching the pattern used by all other AEM command handlers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
3f386ecd34 module-avb: fix ACMP error responses sent with wrong message type
In handle_connect_tx_command() and handle_disconnect_tx_command(),
AVB_PACKET_ACMP_SET_MESSAGE_TYPE() is called after the goto done
target. When find_stream() fails and jumps to done, the response
is sent with the original command message type (e.g., CONNECT_TX_COMMAND)
instead of the correct response type (CONNECT_TX_RESPONSE).

Move the SET_MESSAGE_TYPE call before find_stream() so error responses
are always sent with the correct response message type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
1d0c51f057 module-avb: fix MRP NEW messages never being transmitted
AVB_MRP_SEND_NEW was defined as 0, making it indistinguishable from
"no pending send" in the MSRP and MVRP event handlers which check
`if (!pending_send)`. This meant that when an attribute was first
declared (applicant state VN or AN), the NEW message was silently
dropped instead of being transmitted on the network.

Fix by shifting all AVB_MRP_SEND_* values to start at 1, so that 0
unambiguously means "no send pending". Update the MSRP and MVRP
encoders to subtract 1 when encoding to the IEEE 802.1Q wire format
(which uses 0-based event values).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
ef8f820d4a module-avb: fix potential NULL pointer dereference in MSRP/MVRP notify
The msrp_notify() and mvrp_notify() functions call dispatch table
notify callbacks without checking for NULL. In MSRP, the
TALKER_FAILED attribute type has a NULL notify callback, which would
crash if a talker-failed attribute received a registrar state change
notification (e.g. RX_NEW triggering NOTIFY_NEW).

Add NULL checks before calling the dispatch notify callbacks, matching
the defensive pattern used in the encode path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Christian F.K. Schaller
bdaecfebb8 module-avb: fix heap corruption in server_destroy_descriptors
server_add_descriptor() allocates the descriptor and its data in a
single calloc (d->ptr = SPA_PTROFF(d, sizeof(struct descriptor))),
so d->ptr points inside the same allocation as d. Calling free(d->ptr)
frees an interior pointer, corrupting the heap. Only free(d) is needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 07:43:19 +00:00
Wim Taymans
20d648aaad filter-chain: don't corrupt the enumerated properties
When we add a Format property after we dereffed all the other params in
the builder, we might relocate the builder memory and invalidate all
previously dereffed params, causing corruption.

Instead, first add all the params to the builder and then deref the
params.

There is a special case when we have both a capture and playback
stream. The capture stream will receive all filter params and the
playback stream will just receive its Format param.

Fixes #5202
2026-04-08 17:49:41 +02:00
Wim Taymans
dc47f9ea45 filter-graph: return current control value correctly
The control values are only set in the port control_data after the
filter has been activated and the instances are created.

Property enumerations might happen before that and then we can either
return the current_value (when set in a control section or later with a
param property) or the default value.
2026-04-08 17:45:28 +02:00
Wim Taymans
0f00ad19cb tools: clean up the preset filter code 2026-04-08 15:01:11 +02:00
pallaswept
bf7f2a5d88 pw-top: filter by status 2026-04-08 12:47:08 +00:00
Wim Taymans
49073cf527 port: improve parsing of passive modes
Make a function to parse the passive mode and use that in ports and
nodes. Improve the node passive mode parsing a little.

Also make Duplex nodes follow-suspend.
2026-04-08 14:07:57 +02:00
Wim Taymans
446e36807f port: debug passive modes better 2026-04-08 13:21:24 +02:00
Wim Taymans
7012594926 dlopen: improve prefix check some more
If we pass a path /usr/libevil/mycode.so, it might have a prefix of
/usr/lib but we should still reject it. Do thi by checking that after
the prefix match, we start a new directory.
2026-04-08 12:00:04 +02:00
Wim Taymans
fbbc4271a3 doc: improve formatting 2026-04-08 11:35:52 +02:00
Wim Taymans
0e0c325194 fix some uninitialized variables warnings 2026-04-08 11:29:36 +02:00
Wim Taymans
abd8c8f666 test: fix pod size
It's at least 24 bytes, 8 for struct header, 8 for int header and 8 for
int data.
2026-04-08 11:28:04 +02:00
Wim Taymans
5474c3c3a5 doc: add running.dox 2026-04-08 10:30:46 +02:00
Wim Taymans
1a3df16e27 mem: handle overflow in pw_map_range_init()
Integer overflows can result in map_range_init() to return wrong offset
or size that can result in access to invalid or unmapped memory.

Check for the overflows and return an EOVERFLOW error.

Found by Claude Code.
2026-04-08 09:47:57 +02:00
Wim Taymans
d7be4353ad tools: avoid strcat in pw-cat
We might overflow the path buffer when we strcat the provided filename
into it, which might crash or cause unexpected behaviour.

Instead use spa_scnprintf which avoids overflow and properly truncates
and null-terminates the string.

Found by Claude Code.
2026-04-07 18:44:43 +02:00
Wim Taymans
337801717e test: add unit test for fds mismatch 2026-04-07 18:31:56 +02:00
Wim Taymans
c9ecbf9fab protocol-native: check msg fds against available fds
Check that the number of fds for the message does not exceed the number
of received fds with SCM_RIGHTS.

The check was simply doing an array bounds check. This could still lead
to out-of-sync fds or usage of uninitialized/invalid fds when the
message header claims more fds than there were passed with SCM_RIGHTS.

Found by Claude Code.
2026-04-07 18:20:26 +02:00
Wim Taymans
247918339e doc: add docs about the runnable calculations 2026-04-07 16:20:46 +02:00
Wim Taymans
50aacea749 plugins: allow some absolute paths
Allow abosulte paths as long as they start with one of the search paths.
2026-04-07 09:44:22 +02:00
Wim Taymans
57299da899 filter-graph: improve debug when loading fails
List the path name and the plugin search path when we can't load an
error.
2026-04-06 15:01:43 +02:00
Wim Taymans
785bf36b9b docs: remove support for absolute paths from docs 2026-04-06 14:47:21 +02:00
Wim Taymans
8fd7982087 only dlopen from the defined search paths
Don't accept absolute library paths and skip the ../ in paths to avoid
opening arbitrary libraries from unexpected places.
2026-04-06 14:20:56 +02:00
Pauli Virtanen
ad0bab69a1 spa: system: make spa_poll_event compatible with epoll_events
spa_poll_event should have exactly same layout as epoll_events to be
compatible across platforms. The structure is packed only on x86-64.

Fix packing and replace the data member with similar union as
epoll_data, to fix compatibility on 32-bit etc.
2026-04-06 10:24:32 +00:00
Wim Taymans
6b0248d68c Revert "impl-node: Don't suspend when links are busy"
This reverts commit bb0efd777f.

It is unclear what the problem was before this commit. If there are any
pending operations, the suspend should simply cancel them.

See #5207
2026-04-06 10:39:15 +02:00
Alexander Sarmanow
54a4515b09 bluez5: bap: add support for manual BIS config 2026-04-05 20:18:22 +00:00
ValdikSS
ee1b429441 bluez5: aac: Use VBR encoding with Mode 5 by default 2026-04-05 20:24:47 +03:00
ValdikSS
a35b6b0c4b bluez5: aac: use higher band limit for CBR mode
FDK-AAC encoder uses band pass filter, which is automatically
applied at all bitrates.
For CBR encoding mode, its values are as follows (for stereo):

* 0-12 kb/s:   5   kHz
* 12-20 kb/s:  6.4 kHz
* 20-28 kb/s:  9.6 kHz
* 40-56 kb/s:  13  kHz
* 56-72 kb/s:  16  kHz
* 72-576 kb/s: 17  kHz

VBR uses the following table (stereo):

* Mode 1: 13   kHz
* Mode 2: 13   kHz
* Mode 3: 15.7 kHz
* Mode 4: 16.5 kHz
* Mode 5: 19.3 kHz

17 kHz for CBR is a limiting value for high bitrate.

Assume >110 kbit/s as a "high bitrate" CBR and increase the
band pass cutout up to 19.3 kHz (as in mode 5 VBR).

Link: d8e6b1a3aa/libAACenc/src/bandwidth.cpp (L114-L160)
2026-04-05 20:24:47 +03:00
ValdikSS
49d5f4f236 bluez5: aac: use maximum possible peak bitrate according to MTU
Android 11 and newer, in both CBR and VBR modes,
* Sets bitrate (AACENC_BITRATE) to the max_bitrate value of A2DP
* Sets peak bitrate (AACENC_PEAK_BITRATE) according to the maximum data
   which could fit into single audio packet based on MTU

AACENC_BITRATE is used only in CBR mode. For VBR mode, the only limiting
factor is AACENC_PEAK_BITRATE.

Do the same in Pipewire.

Link: https://gitlab.freedesktop.org/pipewire/pipewire/-/work_items/1482#note_2949680
Link: https://cs.android.com/android/platform/superproject/+/android16-qpr2-release:packages/modules/Bluetooth/system/stack/a2dp/a2dp_aac_encoder.cc;drc=37d7b4549f7b8740df1a290f04c20c591a2d3391;l=269
2026-04-05 20:24:47 +03:00
Wim Taymans
67dd3549a7 system: use attribute packed for the spa_poll_event
This makes it the same size as epoll_event and we don't need to copy the
results over.

It however technically causes an ABI break, in case someone was using
the system interface directly.
2026-04-01 16:28:47 +02:00
Martin Geier
54c517b2d9 module-rtp: Add more logging for debugging timer related issues 2026-03-30 23:45:34 +02:00
Carlos Rafael Giani
0121bdc475 module-rtp: Lower missing timeout log line from warn to trace
A warning is not warranted in this case, and this log line can spam
the logs, so set it to trace.
2026-03-30 23:45:34 +02:00
Carlos Rafael Giani
5b86e3d418 module-rtp-source: Only enable IGMP recovery when using multicast
IGMP recovery makes no sense with unicast IP addresses.
2026-03-30 23:45:34 +02:00
Carlos Rafael Giani
3080bca85a module-rtp-source: Fix unicast by-address packet filtering
Using connect() on a UDP receiver creates a strict filter based on
the sender's _source_ port, not the sender's destination port. The
source port specifies at what sender port the packet exits the sender.
The destination port specifies at what receiver port the packet enters
the receiver. But, the RTP sink uses an ephemeral (= random) port as the
source port. Consequently, connect() at the receiver will cause a
comparison of that ephemeral port with the fixated one (which is actually
the number of the _destination_ port). This incorrect filtering causes
all packets to be dropped.

Use bind() to filter for the local destination port, and use recvmsg()
with manual IP comparison to filter for the sender's identity.
2026-03-30 23:45:34 +02:00
Carlos Rafael Giani
41b5bc662e network-utils: pw_net_are_addresses_equal() function 2026-03-30 23:45:33 +02:00
Carlos Rafael Giani
9ba92bd728 spa: Do not perform upper range check on 32-bit platforms 2026-03-30 23:31:36 +02:00
Wim Taymans
18c97222c4 channelmix: make up/downmix levels configurable
Add channelmix.center-level, channelmix.surround-level and
channelmix.lfe-level to control how center, surround and LFE is
up/downmixed.

Fixes #5198
2026-03-30 17:50:48 +02:00
Sriman Achanta
03f894bab0 alsa-udev: Add wireless device status monitoring 2026-03-30 07:43:27 +00:00
Barnabás Pőcze
7f4baba41c spa: do not use SPA_PROP_live
Remove support for changing `SPA_PROP_live` in node implementations
that supported it, and hard-code `SPA_PROP_live = true`. If a mode
of operation is desired where the data is processed as fast as possible,
it can be achieved by implementing non-driver operation and using the
freewheel driver in pipewire.
2026-03-27 18:03:24 +01:00
Barnabás Pőcze
00b4717c6e spa: remove async members
Some node implementations have an `async` member, but these members
are not written anywhere. So remove them.
2026-03-27 11:32:29 +01:00
Barnabás Pőcze
84e7b744a6 spa: libcamera: use std::span
libcamera is planning to move to C++20 and drop the custom `libcamera::Span`
type at some point in the future. Since pipewire already uses C++20, remove
all uses of it and instead use `std::span` so that things will compile
after the removal.
2026-03-27 09:57:56 +01:00
Barnabás Pőcze
29b221671f spa: remove timerspec members
These are only used when the timer is set, so convert them to local variables.
No functional changes intended.
2026-03-26 14:43:54 +01:00
Wim Taymans
7599079d61 jack: increase notify buffer and items
Make the notify buffer larger, it was 8K but we can make it 64K. Also
reorder the notify struct fields to make it smaller.

This should avoid "notify queue full" warnings. Ideally we should
dynamically size this queue and not lose any messages.
2026-03-26 11:59:28 +01:00
Wim Taymans
60062432b8 module-rtp: handle the send_packet/feedback as callbacks
They are emited from the streaming thread and therefore can be emitted
concurrently with the events on the main thread. This can cause crashes
when the hook list is iterated.

Instead, make those events into callbacks that are more efficient,
and threadsafe.
2026-03-26 09:34:45 +01:00
Wim Taymans
50fcf64058 tools: add -C | --monitor to pw-cat
It sets the stream.capture.sink property which makes a record stream
capture from a sink monitor instead.
2026-03-25 18:06:17 +01:00
Wim Taymans
9eeb2f1930 mixer: handle control.ump property
Add a control.ump port property. When true, the port wants UMP and the
mixer will convert to it. When false, the port supports both UMP and
Midi1 and no conversions will happen. When unset, the mixer will always
convert UMP to midi1.

Remove the CONTROL_types property from the filter. This causes problems
because this is the format negotiated with peers, which might not
support the types but can still be linked because the mixer will
convert.

The control.ump port property is supposed to be a temporary fix until we
can negotiate the mixer ports properly with the CONTROL_types.

Remove UMP handling from bluetooth midi, just use the raw Midi1 events
now that the mixer will give those and we are supposed to output our
unconverted format.

Fix midi events in-place in netjack because we can.

Update docs and pw-mididump to note that we are back to midi1 as the
default format.

With this, most of the midi<->UMP conversion should be gone again and we
should be able to avoid conversion problems in ALSA and PipeWire.

Fixes #5183
2026-03-25 11:59:43 +01:00
Wim Taymans
7fe191bc10 buffers: handle 0 result from Buffers param enumeration
Since abf37dbdde the param enumeration in
the client-node can return 0 when the parameter is supported but there
are no params uploaded.

When negotiating buffers we need to assume a 0 result as a NULL filter
as well or else we will error.
2026-03-25 11:59:43 +01:00
Wim Taymans
ea28343166 midi: don't convert Midi in nodes
Avoid doing conversions in the nodes between Midi formats, just assume
the imput is what we expect and output what we naturally produce.

For ALSA this means we produce and consume Midi1 or Midi2 depending on the
configurtation.

All of the other modules (ffado, RTP, netjack and VBAN) really only
produce and consume MIDI1.

Set the default MIDI format to MIDI1 in ALSA.

Whith this change, almost everything now produces and consumes MIDI1
again (previously the buffer format was forced to MIDI2).

The problem is that MIDI2 to and from MIDI1 conversion has problems in
some cases in PipeWire and ALSA and breaks compatibility with some
hardware.

The idea is to let elements produce their prefered format and that the
control mixer also negotiates and converts to the node prefered format.
There is then a mix of MIDI2 and MIDI1 on ports but with the control
port adapting, this should not be a problem.

There is one remaining problem to make this work, the port format is
taken from the node port and not the mixer port, which would then expose
the prefered format on the port and force negotiation to it with the
peer instead of in the mixer.

See #5183
2026-03-25 11:59:43 +01:00
Wim Taymans
67070762d0 mixer: only add input ports to mixer 2026-03-25 11:59:43 +01:00
George Kiagiadakis
0d1280a5b2 bluez5: parse the broadcast adapter value from the correct iterator 2026-03-24 16:55:00 +00:00
Barnabás Pőcze
22c1a16dce spa: audioconvert: benchmark-fmt-ops: initialize convert
Since c02cdcb5ce ("audioconvert: add avx2 optimized s32_to f32d")
`conv_s32_to_f32d_avx2()` reads `convert::cpu_flags`, which was
previously unitiailized, fix that by setting it to 0.
2026-03-24 17:50:51 +01:00
Barnabás Pőcze
39f4cbfc98 spa: audioconvert: test-fmt-ops: initialize convert
Since c02cdcb5ce ("audioconvert: add avx2 optimized s32_to f32d")
`conv_s32_to_f32d_avx2()` reads `convert::cpu_flags`, which was
previously unitiailized, fix that by setting it to 0.
2026-03-24 17:38:26 +01:00
lumingzh
bc87bc8588 update Chinese translation 2026-03-24 07:29:02 +00:00
Julian Bouzas
4f56442249 bluez: Discard latency and quality codecs worse than SBC for A2DP auto profiles.
Since SBC is mandatory in all devices that support A2DP, we dont need to inclide
them in the priority tables.

This change also increases the priority of OPUS_G codec as it has better latency
and quality than SBC.
2026-03-24 07:28:14 +00:00
Julian Bouzas
e1f53b7f39 bluez: Set initial profile to quality A2DP 2026-03-24 07:28:14 +00:00
Julian Bouzas
2e7aee3573 bluez: Increase priority of A2DP quality and latency profiles
This improves the UX as the highest A2DP profile will be selected by default.
2026-03-24 07:28:14 +00:00
Vitaliy Fadeev
01b9abc5ef Edit overview.dox
five flags
Node
2026-03-24 07:26:15 +00:00
Wim Taymans
d47b4974ce impl-node: add () around &&
To fix a compiler warning.
2026-03-23 11:55:05 +01:00
Martin Geier
52b23384e0 impl-node: correctly synchronize suspend state with remote node
Previously, if a remote node was set to running and immediately reverted
to suspended state, the remote node stayed in running state. This occurred
because suspend_node sent suspend command only when the locally cached
state was "idle" or "running."

Modified to send suspend to a node whenever its pending state is not
"suspended," ensuring the command is sent during state transitions.

Fixes #5026

Signed-off-by: Martin Geier <martin.geier@streamunlimited.com>
2026-03-23 11:41:09 +01:00
Wim Taymans
9ba0c3cfd3 audioconver: add avx channelmix copy function 2026-03-20 18:23:43 +01:00
Wim Taymans
c02cdcb5ce audioconvert: add avx2 optimized s32_to f32d
Add an alternative avx2 s32_to_f32d implementation that doesn't use the
gather function for when gather is slow.

Don't overwrite the orinal cpu_flags but store the selected flags in a
new variable. Use this to debug the selected function cpu flags.

Build libraries with defines from previous libraries so that we can
reuse functions from them.

We can then remove the SSE2 | SLOW_GATHER function selection from the
list. We will now select avx2 and it will then switch implementations
based on the CPU flags.
2026-03-20 17:57:59 +01:00
Pauli Virtanen
3dff64364f bluez5: media-source: don't crash if BAP streams doesn't have iso_io
Don't crash in update_target_latency() if a BAP stream doesn't have
iso_io for some reason.
2026-03-19 15:39:18 +00:00
Julian Bouzas
1dd8729d13 bluez: Add A2DP auto quality and latency profiles
These 2 new profiles will select the highest quality and lowest latency A2DP
codecs respectively, making it easier for users to know which codec is the best
based on their needs.

The priority for these 2 new profiles is 0, so the default behavior should not
change.
2026-03-19 15:38:43 +00:00
Wim Taymans
115525d000 fmt-ops: make function static 2026-03-19 16:36:11 +01:00
Wim Taymans
3b422e31a2 cpu: add SLOW_GATHER flag
Intel Skylake (level 0x16) is the first model with fast gather
opcodes. Mark lower versions with the SLOW_GATHER flag.

Prefer the SSE2 version of the format conversion without gather when
SLOW_GATHER is set. Makes the conversion much faster on my Ivy
Bridge.
2026-03-19 16:21:35 +01:00
Wim Taymans
5ade045654 pipewire-jack: only call timebase on 0 status
If the status passed to cycle_signal != 0 we should not call the
timebase callback. We already do this but we can use this fact to also
not call the timebase_callback when there was no buffer_frames
configured yet.

Do this by setting the fallback return value of process_callback to != 0.
When there is no buffer_frames yet, this will be used and we can then
avoid the buffer_frames check in cycle_signal.
2026-03-19 13:18:58 +01:00
Wim Taymans
244c3b597f pipewire-jack: call timebase_callback with valid buffer_frames
Check if we have a valid buffer_frames before we call the timebase
callback.

Also set the buffer_frames to the invalid value of -1 in jack_activate
instead of 0. -1 is the invalid value we check everywhere else.
2026-03-19 13:00:55 +01:00
Wim Taymans
41d8ce7fff audioconvert: optimize some fallback paths
We can use vector operations in some parts.

Also Make a macro for common store multiple operations.
2026-03-19 09:12:58 +01:00
Wim Taymans
b16a2e41e8 scheduler: remove sync group from runnable calculations
The sync group is only to group nodes when sync is enabled.
2026-03-18 13:19:04 +01:00
Wim Taymans
e4693ebc83 audioconvert: avoid OOB mix matrix read
Patch by David Nowotny

In make_matrix (channelmix-ops.c), the matrix-filling loop at the done:
label allows the index i (and j in the inner loop) to grow beyond MAX_CHANNELS
when dst_paired/src_paired has sparse bits set. In that case the continue
fires for most values of i < CHANNEL_BITS, so i advances much faster than ic,
and matrix[i][j] reads off the end of the stack-allocated array.

Add bounds guards to both loop conditions so i and j cannot exceed
MAX_CHANNELS.

Fixes #5176
2026-03-17 14:13:01 +01:00
Wim Taymans
6bf81ebe59 modules: align rtp_timestamps for sender
When the driver changes, the clock position can also change and there
would be a discont in the rtp_timestamp.

This is not usually a problem except in RAOP mode where the base rtp
timestamp is negotiated and anything that deviates too much is to be
discarded.

If we are not using direct_timestamp for the sender, make sure we always
keep the rtp_time aligned to avoid this problem.

See #5167
2026-03-17 14:05:09 +01:00
Wim Taymans
f4587ea434 modules: improve debug in RTP 2026-03-17 14:05:09 +01:00
Wim Taymans
8db1153519 modules: remove some unused fields 2026-03-17 14:05:09 +01:00
Arun Raghavan
36740e0100 spa: aec: Add some channel config validation
This is checked in AudioProcessing, but will fail at processing time
rather than in initialisation.
2026-03-17 12:06:25 +00:00
Arun Raghavan
edb074f438 spa: aec: Fix log topic name 2026-03-17 12:06:25 +00:00
Baurzhan Muftakhidinov
f846879399 po: update Kazakh translation 2026-03-16 19:07:54 +00:00
Robert Rosengren
e5809ee052 pipewiresrc: fix wait_negotiated from change_state
Going from PLAYING to PAUSED will have basesrc calling unlock and
setting flushing to false. Going back to PLAYING may then fail in
wait_negotiated, as unlock_stop will be called after change_state.

Fix by remove the flushing check, since already in that "state".
2026-03-16 17:08:44 +00:00
Rudi Heitbaum
1a37f445a2 spa/plugins/alsa/acp/compat.h: p is already const do not recast 2026-03-17 03:17:15 +11:00
Wim Taymans
653b8703bc filter-graph: fix up def/min/max values for lv2
lv2 filters can return NAN for the min/max and default values when not
specified. Handle them so that we don't end up clamping NAN numbers.

Fixes #5166
2026-03-16 12:19:57 +01:00
Wim Taymans
90fd6fbc65 filter-graph: only use min/max when defined in LADSPA
Fixes #5170
2026-03-16 09:59:14 +01:00
Wim Taymans
11d1f3653a doc: try to add 1.6 docs 2026-03-13 13:15:16 +01:00
Wim Taymans
b1b5653393 sendspin: negotiate the first raw format
Flac and Opus are not supported yet.
2026-03-13 12:03:11 +01:00
Nedko Arnaudov
6544996a33 pipewire-jack: emit foreign port registration callbacks on jack_activate
The jack_activate loop was only queuing NOTIFY_TYPE_PORTREGISTRATION
for the activating client's own ports. Ports belonging to other clients
— including all WirePlumber-managed ports and MIDI ports — were silently
skipped due to the o->port.port->client != c condition.

This caused two observable bugs for clients using libjackserver (e.g.
jackdbus):
- JackPortRegistrationCallback was not fired for any pre-existing
  foreign ports at activate time, leaving the patchbay empty unless
  the session manager happened to start after the client.
- JACK MIDI ports were never announced via callback, even though they
  are correctly returned by jack_get_ports().

The graph_order_callback fallback (used by jackdbus for initial port
enumeration) is also ineffective here because pipewire-jack only fires
it on connection events, not on activate.

Fix by iterating all non-removed foreign ports in the object list and
queuing registration callbacks for those whose node is active, matching
the semantics already implemented in node_info() for ports of nodes
that transition to running state after activate.

The change is libjackserver.so only. libjack.so behaviour is left
unmodifed, as carla is showing ports of each client twice.
2026-03-13 09:21:20 +01:00
Jonas Ådahl
3c1b8dcdcc spa/util/json-core: Fix condition checking for container end
Fixes the following warning:

    /usr/include/spa-0.2/spa/utils/json-core.h: In function ‘spa_json_is_container_end’:
    /usr/include/spa-0.2/spa/utils/json-core.h:404:41: warning: logical ‘or’ of equal expressions [-Wlogical-op]
      404 |         return len > 0 && (*val == '}'  || *val == '}');
          |                                         ^~
2026-03-12 22:21:41 +01:00
Barnabás Pőcze
3273ba6333 spa: support: logger: print thread id for each message
The thread id is very useful for debugging, add it to the every log
message so that it does not have to be inferred from the content itself.

This is already done in the systemd journal logger, so implement
it here as well.

Before:

  [I][00:13:11.120303] pw.context   | [      pipewire.c:  585 pw_init()] version 1.7.0

After:

  [I][365073][00:13:11.120303] pw.context   | [      pipewire.c:  585 pw_init()] version 1.7.0
2026-03-12 18:31:46 +01:00
Wim Taymans
95eac7b4e5 scheduler: add comment about the FOLLOW_SUSPEND special case 2026-03-12 18:12:42 +01:00
Barnabás Pőcze
9098996b87 spa: support: logger: use spa_strbuf
Using `spa_strbuf` simplifies a code a great deal by removing the manual
tracking of buffer sizes, and it also removes the need for the separate
strings for the timestamp, topic, etc.
2026-03-12 16:53:16 +00:00
Barnabás Pőcze
00148467ef spa: utils: string: add spa_strbuf_appendv()
Add a function that makes it possible to pass a `va_list` when
printing into a `spa_strbuf`.
2026-03-12 16:53:16 +00:00
Wim Taymans
e4e5f62d44 impl-node: accept more node.passive values
So that we can set separate values for in and out ports.

The node.passive=follow mode is a good idea for a filter.
2026-03-12 17:25:36 +01:00
Wim Taymans
16bde0c61d filter-graph: set some more def/min/max for control input
So that the values don't get clamped to 0.0
2026-03-12 16:01:52 +01:00
Jonas Holmberg
1344bec7bb filter-graph: set min, max and def values for ramp
Set min, max and def values for the control ports of ramp plugin to
avoid clamping all control values to 0.
2026-03-12 15:34:35 +01:00
Wim Taymans
f4558472df Revert "pipewire-jack: emit foreign port registration callbacks on jack_activate"
This reverts commit b0065bfe9a.

This causes double ports in carla.
2026-03-12 15:00:59 +01:00
Wim Taymans
3e209f6d20 modules: try to improve code readability some more 2026-03-12 14:57:23 +01:00
Wim Taymans
41520f1022 modules: remove a warning 2026-03-12 14:44:34 +01:00
Wim Taymans
ca91b368c1 impl-port: make a follow-suspend port mode
It's like the follow mode but when you link 2 follow-suspend ports
together, they will activate eachother.

This is to make Source -> Sink links work.
2026-03-12 14:40:04 +01:00
Wim Taymans
45e3af5cdc impl-port: make passive mode as an enum
Hopefully easier to understand.
2026-03-12 13:46:49 +01:00
Wim Taymans
ad195b289a modules: try to document the passive ports better 2026-03-12 12:13:43 +01:00
Arun Raghavan
f11ab0da3e spa: loop: Mark cancellation fields as volatile
Cancellation handlers use setjmp/longjmp, for which the C99
specification has the following note:

> 17.3.2.1 (3)
> All accessible objects have values, and all other components of the
> abstract machine) have state, as of the time the longjmp function was
> called, except that the values of objects of automatic storage
> duration that are local to the function containing the invocation of
> the corresponding setjmp macro that do not have volatile-qualified
> type and have been changed between the setjmp invocation and longjmp
> call are indeterminate.

While everything works fine with GCC, with Clang we see that the
cancellation handler doesn't seem to have an effect (loop-test fails
when it notices that its spa_source's priv and mask have not been
cleaned up).

The underlying cause is that the compiler can assume data.ep_count is
only used in loop_iterate_cancel(), and so can be cached in a register.
When we access that field in the cancellation handler, it was never
actually written to the memory on the stack, so the read in
cancellation_handler() does not see the current value.

We fix this by marking all fields on the stack that we expect to be
modified in loop_iterate_cancel() as volatile, forcing the memory to be
updated and correctly available to the cancellation handler.
2026-03-12 09:24:53 +00:00
Siva Mahadevan
0393fd8a72 spa/plugins: include <stdlib.h> for alloca() on non-linux 2026-03-12 09:21:40 +00:00
Siva Mahadevan
357f27658e spa/utils: remove FreeBSD workaround for bswap*
Both <endian.h> and <byteswap.h> have the things we need.
2026-03-12 09:20:50 +00:00
Siva Mahadevan
a671625637 spa/plugins: revert "Disable alsa plugin on !Linux platforms."
This reverts commit 01096bf669.

The linux-specific headers are gone, it builds fine now with alsa enabled.
2026-03-12 09:20:05 +00:00
Siva Mahadevan
9495e2b8a9 pipewire/thread.c: only handle reset_on_fork if SCHED_RESET_ON_FORK is defined
This fixes a missing definition error in thread.c:

../src/pipewire/thread.c:129:30: error: use of undeclared identifier 'SCHED_RESET_ON_FORK'
  129 |         SPA_FLAG_UPDATE(new_policy, SCHED_RESET_ON_FORK, reset_on_fork);
2026-03-12 09:18:41 +00:00
Nedko Arnaudov
7c5b5d12ed pipewire-jack: fix jack_port_type_id() to return jack1/jack2 compatible values
pipewire-jack defines TYPE_ID_VIDEO=1 between audio and MIDI, shifting
TYPE_ID_MIDI to 2. This caused jack_port_type_id() to return 2 for MIDI
ports, breaking compatibility with jack1/jack2 which return 1.

The jack_port_type_id() return value is part of the public JACK API and
consumers such as jackdbus rely on the conventional values established
by jack1/jack2: 0 for audio, 1 for MIDI.

Map internal TYPE_ID_* values to their jack1/jack2 compatible equivalents
before returning. All MIDI variants (MIDI, OSC, UMP) map to 1. Video has
no jack1/jack2 equivalent so maps to 3, beyond the conventional range.
2026-03-12 09:55:15 +01:00
Nedko Arnaudov
b0065bfe9a pipewire-jack: emit foreign port registration callbacks on jack_activate
The jack_activate loop was only queuing NOTIFY_TYPE_PORTREGISTRATION
for the activating client's own ports. Ports belonging to other clients
— including all WirePlumber-managed ports and MIDI ports — were silently
skipped due to the o->port.port->client != c condition.

This caused two observable bugs for clients using libjackserver (e.g.
jackdbus):
- JackPortRegistrationCallback was not fired for any pre-existing
  foreign ports at activate time, leaving the patchbay empty unless
  the session manager happened to start after the client.
- JACK MIDI ports were never announced via callback, even though they
  are correctly returned by jack_get_ports().

The graph_order_callback fallback (used by jackdbus for initial port
enumeration) is also ineffective here because pipewire-jack only fires
it on connection events, not on activate.

Fix by iterating all non-removed foreign ports in the object list and
queuing registration callbacks for those whose node is active, matching
the semantics already implemented in node_info() for ports of nodes
that transition to running state after activate.
2026-03-12 09:55:07 +01:00
Siva Mahadevan
e447b46d36 spa/tests: remove unused #include <linux/limits.h> 2026-03-11 21:50:21 +00:00
Siva Mahadevan
283052c15a examples: fix includes for makedev() macro
On non-linux, the makedev() macro comes from <sys/types.h>.
2026-03-11 21:49:48 +00:00
Siva Mahadevan
d17e68c43f network-utils: add missing #include <netinet/in.h>
As stated in https://pubs.opengroup.org/onlinepubs/9799919799//basedefs/netinet_in.h.html,

> The <netinet/in.h> header shall define the sockaddr_in structure [...]

This fixes the following build error:

In file included from ../src/modules/module-protocol-native.c:44:
../src/modules/network-utils.h:96:35: error: incomplete definition of type 'struct sockaddr_in'
   96 |                 if (inet_ntop(sa->ss_family, &in->sin_addr, ip, len) == NULL)
      |                                               ~~^
2026-03-11 17:20:12 -04:00
Siva Mahadevan
cd84d007cd spa: replace ECHRNG with EINVAL
ECHRNG is linux-specific and does not exist on all OSes.
On the other hand, EINVAL is specified in POSIX[0] (and is thus cross-platform)
and is commonly used to signify an index out of bounds error.

https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/errno.h.html
2026-03-11 20:40:24 +00:00
Siva Mahadevan
469929d4f6 pipewire: guard linux-specific RLIMIT_* inside #ifdef __linux__ 2026-03-11 14:20:48 -04:00
Wim Taymans
9c7aa4d423 impl-port: don't send NULL tag to clear
NULL tags are never handled and so tags just keeps configured on the
ports. It's also hard to know the direction from the NULL data.

Instead, send an empty Tag with the direction to clear everything.
2026-03-11 18:18:49 +01:00
Wim Taymans
2f65cf7124 modules: return the error of getsockopt instead of -EPIPE 2026-03-11 18:18:49 +01:00
Kisaragi Hiu
6e332a5d32 po: update Chinese (Taiwan) translation 2026-03-11 22:05:24 +09:00
Wim Taymans
00f1d6c3d8 modules: improve debug in profiler
Also log the node that we are inspecting.
2026-03-11 12:42:06 +01:00
Wim Taymans
f45e619559 filter-graph: emit control change after loading
Always do a control change to the instances when they are created. This
is to make sure the internal state is synced to the control values.

The sofa filter and biquads need this to correctly configure themselves
after a suspend.

Fixes #5152
2026-03-11 12:39:46 +01:00
Wim Taymans
5cc63c1b34 audiomixer: only add the input port to mix_list
Adding the output port is not a problem because there is never a buffer
to consume and mix but it wastes cycles.
2026-03-11 12:36:39 +01:00
Wim Taymans
5f4b422ab1 loop: improve cancellation handling
Now that the loop_leave function will assert when the unlock fails we
need to be extra careful with the cancellable loop. If it cancels inside
the poll or one of the before/after callbacks we need to make sure that
we lock the loop correctly again or we will create an assert later on.

Do this by pushing the cleanup earlier and then record all the things we
managed to do before we get canceled. If we ever get canceled and the
lock was unlocked but not locked again, fix this up.

Fix fixes issues when using the JACK API causing assertions when the
data loop is stopped/cancelled.
2026-03-10 16:46:29 +01:00
Wim Taymans
7ecd51dc80 client-node: avoid using invalid fd or mem in clear_data
Don't close an -1 fd in clear_data.

If we let the client allocate buffer, set our fd and data to invalid
values. If the client decides to renegotiate before we get the buffer
data we might otherwise try to clear the mem_id (default 0) or
close the fd (also default 0).

Fixes #5162
2026-03-10 14:25:07 +01:00
Wim Taymans
6a3ac7f7b0 examples: support REQUEST like video-src 2026-03-10 12:30:03 +01:00
Wim Taymans
8f22785cf0 json-core: use table to classify chars for number check 2026-03-10 12:30:03 +01:00
Barnabás Pőcze
5c67ab2a7a pipewire: mem: log if close() fails
If a file descriptor is expected to be closed by the associated `pw_memblock`,
and closing fails, then that usually signals a more serious issue in the memory
accounting. So add a log message when that happens.
2026-03-10 12:27:44 +01:00
Barnabás Pőcze
d33066cdd7 pipewire: module-client-node: use pw_log_*()
Use the `pw_log_*()` macros instead of the `spa_log_*()` ones since
this is a pipewire module, and otherwise not the expected log topic
would be used.
2026-03-10 12:09:36 +01:00
Barnabás Pőcze
810617997b spa: libcamera: source: SPA_PARAM_Props is write-only
There are no readable `SPA_PARAM_Props` on the node, so mark it write-only.
2026-03-09 21:19:38 +00:00
Barnabás Pőcze
a661f14d2c spa: support: loop: check enter_count before iterating
Calling "iterate()" on a loop that has not been entered by the
calling thread is invalid. So try to diagnose misbehaving applications
on a "best effort" basis by checking `impl::enter_count`.

This is not a foolproof check, and can also technically cause data
races while reading the variable.

See #5148
2026-03-09 22:08:23 +01:00
Barnabás Pőcze
dc1738ce57 spa: support: loop: more mutex lock checks
Check the return values of `pthread_mutex_{,un}lock()` in more places
to diagnose misbehaving applications.

See #5148
2026-03-09 22:08:22 +01:00
Barnabás Pőcze
abf37dbdde pipewire: module-client-node: use pw_param_info_find() to check validity
Previously when parameters were enumarated, it was checked if at least one
param was known for `id`. If not, `-ENOENT` was returned to signal that
this param id is not supported.

This is not necessarily true, since a param id might be supported, but it
might have zero params at the moment, in which case an unexpected error
would be returned.

Fix that by using `pw_param_info_find()` with the underlying impl object
to check if the param id is actually supported.
2026-03-09 21:34:19 +01:00
Barnabás Pőcze
5e37d43881 pipewire: impl-{node,port}: do not cache failed param enumerations
If the param enumeration fails, do not set `spa_param_info::user` to 1
indicating that the result is cached. Doing so can lead to the first
(uncached) call failing, while the rest will succeed (with 0 params).
2026-03-09 20:04:22 +00:00
Pauli Virtanen
22a5fad902 bluez5: cleanup get_codec_profile()
Check codec kinds for each direction properly when mapping to profiles
corresponding to it. Being sloppy here masked another bug, so best fix
it.
2026-03-09 21:35:22 +02:00
Pauli Virtanen
75c3d3ecf8 bluez5: fix spa_bt_device_supports_media_codec() for HFP codecs
HFP codecs don't have a direction dependent "target" profile, and this
function was returning false if A2DP is disabled.

Don't check target profile for HFP, leave checks to backend.

Fixes HFP-only configurations, which were missing profiles.
2026-03-09 21:33:28 +02:00
Wim Taymans
2548fcad25 spa: update lib.c 2026-03-09 18:33:32 +01:00
Wim Taymans
f6939a1cf0 json: Use state machine and fix 1E10 parsing
Use a state machine to check for valid numbers and fix the 1E10
parsing case.

With help from Claude.
2026-03-09 18:17:04 +01:00
Jonas Ådahl
c745582ef5 pipewire/capabilities: Remove left-over macro defiition
This was used for a previous iteration, that used tags, and was replaced
with PW_CAPABILITY_DEVICE_ID_NEGOTIATION.
2026-03-09 15:54:08 +00:00
Jonas Ådahl
f7bb4c95ed pipewire/capabilities: Update device ID negotation according to API docs
It was was documenting a previous iteration of the protocol which used a
boolean and a base64 encoded list.
2026-03-09 15:54:08 +00:00
Jonas Ådahl
99079dd955 docs/dma-buf: Document the correct device ID negotation key
The correct key is PW_CAPABILITY_DEVICE_ID_NEGOTIATION which contains a
number.
2026-03-09 15:54:08 +00:00
Pauli Virtanen
d42646e91f bluez5: sbc: clean up codec_enum_config
Non-spec compliant devices may set multiple bits in code config, which
we currently reject in validate_config().

enum_config() does work to deal with multiple bits set, but this is
never used, so write the code in a simpler way to return a single
configuration.
2026-03-09 15:53:35 +00:00
Pauli Virtanen
67b4732c26 bluez5: aac: for multiple bits in aot, normalize to mandatory
Non-spec compliant devices may set multiple bits in AAC AOT, which is
invalid.

In this case, we should normalize to MPEG-2 AAC LC which is the
mandatory value in spec, not to MPEG-4 AAC LC. In select_config() we
also prefer MPEG-2 over MPEG-4.
2026-03-09 15:53:35 +00:00
Pauli Virtanen
5f8ece7017 bluez5: aac: coerce non-spec compliant freq/channels to single choice
Some non-spec compliant devices (Sony XB100) set multiple bits
in all AAC field, including the frequency & channels.

Although they set multiple bits, these devices appear to intend that the
sender picks some specific format and uses it, and don't work correctly
with the others.

validate_config() already picks one configuration, so use the result in
enum_config(), instead of allowing also other settings.

Assume devices generally want preferably 44.1 kHz stereo.

Note we cannot reject the configuration, as BlueZ does not necessarily
retry, leaving the device connected but with no audio.
2026-03-09 15:53:35 +00:00
Pauli Virtanen
f9e2b1d8b9 bluez5: backend-native: don't crash without dbus session bus
When there is no DBus session bus, creation of the telephony backend
fails, and we later crash on null ptr deref.

In this case, avoid crash trying to create telephony_ag or iterate its
call list.
2026-03-09 15:52:05 +00:00
Wim Taymans
ddf63e0863 json: relax float parsing some more
We already support more float variants than standard JSON in the relaxed
format, adding extra restrictions does not actually help much. If you
need to know if this is a value JSON number, there is now a function to
check that instead.
2026-03-09 16:48:05 +01:00
Wim Taymans
231a41a22f impl-link: fix shared mem test
We can only use non-shared memory when both nodes live in the same
process _and_ we can be sure the output port is never going to be linked
to a remote node because it is exclusive.

This fixes the case where a null-sink is loaded inside the process space
of the server and linked to the ALSA sink. This would create a link
without shared mem and then as soon as something else (out of process)
wants to link to the null-sink output, it would get a -22 EINVAL
negotiation error because the memory can't be shared.

Fixes #5159
2026-03-09 16:18:58 +01:00
Wim Taymans
6f73e42d05 v4l2: use 0x as the prefix for hex values
fixes #5161
2026-03-09 13:50:38 +01:00
Wim Taymans
3a2d16a3bc json-builder: do better json number check
If we are going to produce valid JSON we need to do a better JSON number
check because our own float and int parser can accept more variants
and will let through numbers that are not valid JSON.

See #5161
2026-03-09 13:33:20 +01:00
Wim Taymans
48c22e2aa7 pw-dump: place key before None choice values
See #5161
2026-03-09 13:19:43 +01:00
qaqland
d1c372f5ee alsa-udev: support alsa.ignore-dB
Some sound cards are only adapted for Android/macOS and other
systems, without considering Linux. The hardware-reported dB
volume is incorrect (while the percentage volume is normal).
Add support for the ignore-dB option to simplify compatibility.

For example, the 3206:0798 HP SIMGOT GEW1 Sound Card reports:

numid=4,iface=MIXER,name='PCM Playback Volume'
  ; type=INTEGER,access=rw---R--,values=2,min=0,max=100,step=0
  : values=100,100
  | dBminmax-min=0.00dB,max=0.39dB

This dB value does not match actual audio perception, and the
vendor attributed this issue to non-target system compatibility.
2026-03-07 13:53:38 +08:00
Wim Taymans
70b7b42f5d pw-cat: fix encoded format playback
We keep a mapping between the sndfile formats and the format we would
like to decode them to for encoded formats. Make sure we don't mix up
the sample widths between them.

Make sure we don't send encoded formats as raw.

Debug the uncompressed format name correctly.

Fixes #5155
2026-03-06 17:03:20 +01:00
Wim Taymans
a179e8c695 sendspin: implement parsing of client state 2026-03-06 11:24:21 +01:00
Wim Taymans
784ad934a4 impl-node: remove the can_suspend boolean
Sink and Sources can now be implemented as the follow passive mode,
they won't activate anything themselves but follow the state of the
peer.
2026-03-06 10:42:00 +01:00
Wim Taymans
118d8574c8 impl-port: support mode port.passive values
Add a port.passive = follow mode and the node.passive equivalents
out-follow, in-follow, follow.

This makes it possible to control how a port influences the state of
the peer and how the peer influences the state of the node independently.

In passive mode, the port will not make the peer runnable and will also
not become runnable when the peer activates.

In the follow mode, the port will not make the peer runnable but it will
become runnable when the peer is active.

This makes it possible to do new things like (f for follow):

  Source -> (f)loopback1-in|loopback1-out(f) -> Sink

It will not make the source and sink run but when one of them start, all
will become runnable.

Or you can now better do the leak node hack that was previously used:

  Source -> (f)pw-record

That will only start running when the source is activated by something
else.

With port.passive = true|false|follow there is a potential 4th case
which would activate the peer but not be activated by the peer, which is
not something that makes sense.
2026-03-06 09:35:17 +01:00
Wim Taymans
437a8d32f2 impl-node: remove the leaf node check
This was a hack around some scheduler issues that we should be able to
handle better now.

One of the cases is:

  Source -> (p)pw-record

Which would never work otherwise because pw-record does not activate the
Source and when the Source is activated in any other way, pw-record
would not follow.

This will still fail with the current scheduler, but the leaf check is
not so great because it doesn't work in this case:

  Source -> (p)loopback-in|loopback-out(p) -> Sink

What is probably required is a passive mode that doesn't activate but
does follows the peer.
2026-03-05 15:03:30 +01:00
Wim Taymans
106b4a37d4 node: remove node.link-group from drivers
Sink/Source pairs should not have the same link-group otherwise the
session manager will not be able to autoconnect them with a loopback or
some other internally linked stream.
2026-03-05 14:32:41 +01:00
Wim Taymans
e6a5951a47 gst: fix compilation on older GStreamer
These functions are since 1.28, which is a little too new.
2026-03-04 17:55:53 +01:00
Robert Mader
efd1526423 pipewiresrc: Use clock time difference to update last_buffer time
Setting the current clock time when resending buffers is often wrong.
Especially for pseudo-live sources - the default mode - it discards
the original buffer time, which again is used by the base-class to
adjust the timestamps further, ultimately resulting in very wrong
timestamps.

Instead, try to calculate the delta between when we originally got the
buffer and now.
2026-03-03 22:52:52 +01:00
Robert Mader
49300d8ee0 pipewiresrc: Take a copy instead of a reference for last_buffer
Buffer timestamps get adjusted by the base class, GstBaseSrc, even if we
take an additional ref. Arguably the base class should check if buffers
are writable (gst_buffer_make_writable()), which would trigger a buffer
copy. That is currently not the case, though, thus do so on our side.

Notes:
1. Usually a buffer copy doesn't copy the underlying memory, i.e.
copying is cheap.
2. The copy holds a reference to the copied buffer, preventing the
buffer from getting recycled as before.
2026-03-03 22:51:04 +01:00
Wim Taymans
a3853c2c3d scheduler: activate links also in make_runnable
Also make sure we unset the preparing flag when the port state
changes.
2026-03-03 11:56:35 +01:00
Elliot Chen
95e89f786a pipewiresrc: update per-plane stride and offset according to chunk info 2026-03-03 09:16:51 +00:00
qaqland
182f52603c meson.build: bump sndfile version to 1.1.0
This commit follows 35817c0d85

Bump required sndfile version to 1.1.0 for SF_FORMAT_MPEG_LAYER
and other formats added in commit 35817c0d8c.
2026-03-03 09:14:25 +00:00
Wim Taymans
ea21281f38 scheduler: skip checking runnable from suspendable nodes
We used to skip the runnable state from driver nodes because we assume
that they will be activated from other nodes. We however need to make
this more general to all suspendable nodes.

This makes pw-play -> loopback1-sink loopback1-out -> loopback2-sink
loopback-out -> sink also work correctly because the loopback2-sink does
not activate loopback1-out then.
2026-03-02 17:22:05 +01:00
Wim Taymans
797cdbc72f impl-link: link.passive is no longer used 2026-03-02 17:19:41 +01:00
Wim Taymans
b8e27cc02b modules: IO_HUP also has an error 2026-03-02 12:22:30 +01:00
Wim Taymans
6e2522b657 modules: improve error reporting
Instead of reporting -EPIPE, get the error from the socket and report
that instead.
2026-03-02 12:13:22 +01:00
Wim Taymans
87087a4629 jack: never return NULL from jack_port_by_id()
JACK will never return NULL from jack_port_by_id() because the id
and the port_t are the same for JACK.

In PipeWire however we use the serial number as the id and so it can
be removed and become invalid. In this case, return a dummy port
from the client that can be used for some of the basic operations
you can do on a port_t, like get the name etc.

Also make sure that port_name() doesn't return NULL in case we use the
dummy port (which has the client set to NULL).

Fixes #3512
2026-03-02 11:41:21 +01:00
Jonas Holmberg
f4e174870e module-protocol-native: Fix socket activation
Fix path comparison in is_socket_unix() and don't unset LISTEN_FDS since
the function that uses it is called more than once and it was not unset
when sd_listen_fds() was used.

Fixes #5140
2026-03-02 10:28:26 +01:00
Wim Taymans
d206b06c70 sendspin: cleanup receive sync and logging
don't check the same thing twice.

Make sure values are set before we log them.
2026-03-01 12:49:24 +01:00
Wim Taymans
06f336a581 json-builder: handle allocation failures in vasprintf 2026-03-01 12:40:34 +01:00
thewrz
8ceb671cc8 module-vban: derive write position from frame counter
Instead of writing packets sequentially and losing sync on any
frame gap, compute the write position from the VBAN header's
n_frames field. Out-of-order packets land at the correct
ringbuffer offset, matching how module-rtp handles this.

Only advance the writeindex when a packet extends the frontier
so that late arrivals fill gaps without moving the pointer
backwards.

Fixes: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/5145
2026-02-27 22:48:12 -08:00
Wim Taymans
354ec08b9b doc: add sendspin modules to doc 2026-02-27 20:43:56 +01:00
Wim Taymans
b05fcdbfbd modules: fix compilation without avahi 2026-02-27 20:36:37 +01:00
Wim Taymans
8eed27820c meson: try to fix the doc build 2026-02-27 18:23:45 +01:00
Frédéric Danis
ee18160c4e bluez5: bap: Fix typos 2026-02-27 17:19:01 +00:00
Wim Taymans
9ad5ca2e5a websocket: fix some overflows
Fix some integer and buffer overflows as suggested by Sami Farin.
2026-02-27 17:58:51 +01:00
Wim Taymans
dee2d5ee06 zeroconf: sanitize the properties
Use some constants for the zeroconf properties. Make the right ones are
used in all places.
2026-02-27 17:31:42 +01:00
Wim Taymans
3a1b790588 modules: port snapcast to zeroconf helper 2026-02-27 17:31:38 +01:00
Wim Taymans
a065cff8c1 zeroconf: pass user data as const 2026-02-27 16:06:33 +01:00
Wim Taymans
db713c8264 modules: port rtp-session to new zeroconf helper 2026-02-27 13:50:49 +01:00
Wim Taymans
a1db2b8d35 pulse-server: port zeroconf publish to helper 2026-02-27 12:22:52 +01:00
Wim Taymans
c09bcfdc97 zeroconf: support proto, ifindex and subtypes
Also improve debug a little.
2026-02-27 12:22:24 +01:00
Wim Taymans
cd77dd0e62 zeroconf: add some more error checking
Also publish domain and host when we can and fix a memleak.
2026-02-26 23:19:53 +01:00
Wim Taymans
79d8e7f61d modules: port zeroconf-discover to new helper 2026-02-26 23:19:49 +01:00
Wim Taymans
b67937f247 module-raop: port to new zeroconf helper 2026-02-26 18:16:08 +01:00
Wim Taymans
fa04146cfb modules: move zeroconf code to zeroconf-utils 2026-02-26 17:09:45 +01:00
Wim Taymans
4a399172b6 sendspin: implement receiver zeroconf connect
Let a receiver connect to the zeroconf server. Add option to connect to
multiple serves with rules.

Fix some leaks.

Fixes #5095
2026-02-26 16:48:06 +01:00
Wim Taymans
d6654e84a7 sendspin: add sendspin sender and receiver
The sender makes an input stream for each connected client. This makes
it easier to do the per client conversion using the adapter and send
different channels to clients.

The receiver uses linear regression to map ringbuffer indexes to server
timestamps and server timestamps to client timestamps. It can then
schedule playback against its own clock.
2026-02-26 12:17:17 +01:00
Wim Taymans
6daa8ccc0d json-builder: zero the struct 2026-02-26 12:08:26 +01:00
Wim Taymans
be3c63d55e module-raop: move base64 to separate file 2026-02-26 12:00:31 +01:00
Wim Taymans
03662b3dfe json-builder: avoid indent on the first item
Avoids putting a \n or (now also) space as the first item.
2026-02-26 11:17:17 +01:00
Wim Taymans
bdbb5f6d27 json-builder: add raw mode that leaves keys/strings like they are 2026-02-26 11:09:13 +01:00
Wim Taymans
7dd924797b audioconver: reformat the graph description for properties 2026-02-26 10:59:57 +01:00
Wim Taymans
ed361a856f tools: port various tools to the new json-builder
Remove custom json serialization code and use the new json-builder
from various tools.

spa-json-dump now has color and raw mode and can probably be simplified
a bit more.

The properties can now serialize arbitrarily large keys, which makes
a unit test work. It also has a new 'simple' option for SPA output,
which is added and used in pw-config.
2026-02-26 10:51:17 +01:00
Wim Taymans
6753c51ab8 spa: add a new json-builder helper
It keeps track of the json bits like commas and indentation, supports
colors and recursive reformatting. It also supports a simple mode that
implements the SPA syntax.
2026-02-26 10:48:53 +01:00
Wim Taymans
692590b30a json: a container start also ends a bare string
This stops the parser from seeing foo{bar as a single string.

This also makes a valid test work, add another small test.
2026-02-26 10:42:14 +01:00
Barnabás Pőcze
848ac24490 spa: libcamera: source: fix stop sequence
Currently it is possible for the request completion handler (`impl::requestComplete`)
to observe `impl::source.fd` while it is being modified in `impl::stop()`.

Fix that by closing the eventfd after the camera has been stopped.

Fixes: 3e28f3e859 ("spa: libcamera: source: rework startup sequence")
2026-02-24 18:29:51 +01:00
Wim Taymans
6961bfeaa1 examples: adapt samplerate in audio-dsp-src
Follow the graph rate to generate the sine wav instead of using a
hardcoded value.

See #5135
2026-02-24 15:12:43 +01:00
Wim Taymans
5a285602e2 scheduler: sync-groups don't define the node runnable state
The sync-groups are only to group nodes with the same driver but don't
make them runnable.

This avoid making v4l2 runnable (without a link) when running ardour
because ardour uses the transport, which activates the sync group.
2026-02-23 11:32:40 +01:00
Anders Jonsson
426a5be235 po: Update Swedish translation 2026-02-22 21:50:02 +01:00
Ripley Tom
ff04b47942 meson.build: Add -Werror=discarded-qualifiers 2026-02-22 15:59:56 +00:00
Ripley Tom
c847b81629 spa/plugins/alsa/acp/compat.h: Fix missed -Wdiscarded-qualifiers warning 2026-02-22 15:59:56 +00:00
Ripley Tom
367ce4626c
src/modules/module-rtp-source.c: Fix alignment requirement for 32 bit build 2026-02-21 20:55:17 +01:00
Wim Taymans
846096d435 scheduler: prepare link before usage
A link might become unprepared because a node suspended. When we want
to make the nodes runnable, make sure the link is prepared again.
2026-02-21 17:25:54 +01:00
Wim Taymans
476220c18b scheduler: don't take active state into account for grouping
The grouping of the node does not depend on the active state.
2026-02-21 16:33:21 +01:00
Wim Taymans
973f48dde7 scheduler: make always_process nodes runnable
Don't just set the flag but call make_runnable for always_process node
so that the peers also get activated.
2026-02-21 16:32:30 +01:00
Wim Taymans
9e82e49446 scheduler: rework the runnable state calculation
Move the runnable state calculation out of the collect_nodes function.
They are really two different steps that doin't overlap much.

The runnable state of a node is very easy to calculate. A node is
runnable if it is linked to another node without a passive port. When we
find two runnable nodes, make them runnable, which makes all nodes
linked to them runnable, stopping at passive ports.

We don't have to check the active state of the nodes or links to group
them together. This ensures we don't swap nodes around too much when the
node or link state changes.
2026-02-21 16:19:34 +01:00
Wim Taymans
ce18660127 tests: fix test 2026-02-20 10:35:31 +01:00
Wim Taymans
8f3d8d77ab impl-link: prepare a link right after creating it
There is no reason to delay preparing the link (by the scheduler) when
both nodes are active, we can do that right from the start.

This makes things a bit more symetrical because deactivating a node does
not unprepare a link.

This however changes things a bit because you can no longer delay link
prepare until you activate the node. I don't know if this is actually in
use and it would probably be to delay format negotiation. The right way
do delay format negotiation is to wait until an EnumFormat is set but
that is something to improve later.
2026-02-20 10:35:31 +01:00
Arun Raghavan
5bd93b97ad Revert "spa: json: Add a helper method to shrink an object string"
Drop this until we have better API for building/visiting JSON.

This reverts commit c244fbf945.
2026-02-19 20:02:51 +00:00
Barnabás Pőcze
e46bfe67b6 treewide: fix some -Wdiscarded-qualifiers
Newer glibc versions have made certain `str*()` functions into macros
that ensure that the const-ness of the argument is propagated to the
return type.
2026-02-19 21:00:10 +01:00
Barnabás Pőcze
d7c3e8c2bc pipewire: module-roc-{sink,source}: fix log format string issues
Passing an unknown string as the format string is unsafe, so don't do it.

Fixes: b9922d8ed5 ("module-roc: forward roc-toolkit logs to pipewire logs")
2026-02-19 19:37:15 +00:00
Barnabás Pőcze
d8b06f94ee pipewire: module-roc-{sink,source}: remove logging related unused code
!2699 has been merged a bit prematurely and it contained things that are
not used. So remove the unused member variables, functions, fix module
usage strings, and move some functions from headers.
2026-02-19 19:37:15 +00:00
Arun Raghavan
d4329600d1 audioconvert: Report loaded filter graphs in props
Makes it easier to know what filters have been loaded.
2026-02-19 19:29:40 +00:00
Arun Raghavan
c244fbf945 spa: json: Add a helper method to shrink an object string 2026-02-19 19:29:40 +00:00
Arun Raghavan
f5107f3e83 ci: Fix doccheck error message 2026-02-19 11:04:23 -08:00
Arun Raghavan
569c2dce55 doc: Add module scheduler subpage 2026-02-19 11:04:23 -08:00
Arun Raghavan
987579b7b7 tests: Update context events test
New field and version, update the test.
2026-02-19 10:45:45 -08:00
Wim Taymans
2fb38af3e0 modules: move the scheduler to a module
Mostly because we can but also because there are more ways of doing
the scheduling and this opens the door for some experimentation.
2026-02-19 17:44:27 +01:00
Wim Taymans
7887c365d1 filter-graph: Make a new control_sync function
This function is run for all the nodes with the data loop locked. It can
be used to atomically update multiple node controls.

We can't use the control_changed function because this one runs without
the lock and might do slow things, like what the sofa plugin currently
does.

See #5019
2026-02-19 13:15:40 +01:00
Wim Taymans
e7ca02c4d8 filter-graph: sync control updates with data thread.
don't read the control ports from the processing thread and check for
updates. Use the control_changed signal to check and update the
parameters of the biquad atimically.

See #5019
2026-02-19 13:15:39 +01:00
Wim Taymans
be0e037809 pulse-server: avoid doing allocations and string copies
We can just concatenate the stream and client dict on the stack, reusing
all the strings without having to do allocations or copies.

Also filter out the object.id and object.serial from the client, we want
to keep the ones from the streams.
2026-02-19 13:03:50 +01:00
Wim Taymans
6eb4483069 pulse-server: add client props to sink_input/source_output
Instead of adding the client props to the stream props when we create
it, add them when we enumerate the streams.

This makes it possible to also have the client props in the stream props
for streams that are not created with the pulse API.

Fixes #5090
2026-02-19 12:08:23 +01:00
Wim Taymans
b8b2c58cda Development continues as 1.7.0 2026-02-19 11:09:50 +01:00
211 changed files with 17098 additions and 5262 deletions

View file

@ -410,13 +410,15 @@ build_on_fedora_html_docs:
-Dsndfile=enabled
-Dsession-managers=[]
before_script:
- git fetch origin 1.0 1.2 1.4 master
- git fetch origin 1.0 1.2 1.4 1.6 master
- git branch -f 1.0 origin/1.0
- git clone -b 1.0 . branch-1.0
- git branch -f 1.2 origin/1.2
- git clone -b 1.2 . branch-1.2
- git branch -f 1.4 origin/1.4
- git clone -b 1.4 . branch-1.4
- git branch -f 1.6 origin/1.6
- git clone -b 1.6 . branch-1.6
- git branch -f master origin/master
- git clone -b master . branch-master
- !reference [.build, before_script]
@ -433,6 +435,10 @@ build_on_fedora_html_docs:
- meson setup builddir $MESON_OPTIONS
- meson compile -C builddir doc/pipewire-docs
- cd ..
- cd branch-1.6
- meson setup builddir $MESON_OPTIONS
- meson compile -C builddir doc/pipewire-docs
- cd ..
- cd branch-master
- meson setup builddir $MESON_OPTIONS
- meson compile -C builddir doc/pipewire-docs
@ -658,7 +664,7 @@ doccheck:
- cat pipewire_module_pages
- |
for page in $(cat pipewire_module_pages); do
git grep -q -e "\\\subpage $page" || (echo "\\page $page is missing \\subpage entry in doc/pipewire-modules.dox" && false)
git grep -q -e "\\\subpage $page" || (echo "\\page $page is missing \\subpage entry in doc/dox/modules.dox" && false)
done
check_missing_headers:
@ -682,12 +688,13 @@ pages:
- job: build_on_fedora_html_docs
artifacts: true
script:
- mkdir public public/1.0 public/1.2 public/1.4 public/devel
- mkdir public public/1.0 public/1.2 public/1.4 public/1.6 public/devel
- cp -R branch-1.0/builddir/doc/html/* public/1.0/
- cp -R branch-1.2/builddir/doc/html/* public/1.2/
- cp -R branch-1.4/builddir/doc/html/* public/1.4/
- cp -R branch-1.6/builddir/doc/html/* public/1.6/
- cp -R branch-master/builddir/doc/html/* public/devel/
- (cd public && ln -s 1.4/* .)
- (cd public && ln -s 1.6/* .)
artifacts:
paths:
- public

View file

@ -44,6 +44,7 @@
<tab type="usergroup" title="PipeWire Versions">
<tab type="user" url="https://docs.pipewire.org/1.2/" title="1.2.x"/>
<tab type="user" url="https://docs.pipewire.org/1.4/" title="1.4.x"/>
<tab type="user" url="https://docs.pipewire.org/1.6/" title="1.6.x"/>
<tab type="user" url="https://docs.pipewire.org/devel/" title="Development"/>
</tab>
</navindex>

View file

@ -80,6 +80,9 @@ stream.properties = {
#channelmix.fc-cutoff = 12000.0
#channelmix.rear-delay = 12.0
#channelmix.stereo-widen = 0.0
#channelmix.center-level = 0.707106781
#channelmix.surround-level = 0.707106781
#channelmix.lfe-level = 0.5
#channelmix.hilbert-taps = 0
#dither.noise = 0
#dither.method = none # rectangular, triangular, triangular-hf, wannamaker3, shaped5

View file

@ -450,11 +450,25 @@ Whether the node target may be changed using metadata.
@PAR@ node-prop node.passive = false
\parblock
This is a passive node and so it should not keep sinks/sources busy. This property makes the session manager create passive links to the sink/sources. If the node is not otherwise linked (via a non-passive link), the node and the sink it is linked to are idle (and eventually suspended).
This can be used to configure the port.passive property for all ports of this node.
Possible values are:
* "out": output ports are passive, They will not make the peers active and active peers will
not make this node active.
* "in": input ports are passive, They will not make the peers active and active peers will
not make this node active.
* "true": A combination in "in" and "out", both input and output ports are passive.
* "out-follow": output ports will not make the peer active but when the peer is activated via
some other way, this node will also become active.
* "in-follow": input ports will not make the peer active but when the peer is activated via
some other way, this node will also become active.
* "follow": A combination of "in-follow" and "out-follow".
This is used for filter nodes that sit in front of sinks/sources and need to suspend together with the sink/source.
\endparblock
@PAR@ node-prop node.link-group = ID
Add the node to a certain link group. Nodes from the same link group are not automatically linked to each other by the session manager. And example is a coupled stream where you don't want the output to link to the input streams, making a useless loop.
@ -769,6 +783,15 @@ more to the center speaker and leaves the ambient sound in the stereo channels.
This is only active when up-mix is enabled and a Front Center channel is mixed.
\endparblock
@PAR@ node-prop channelmix.center-level = 0.707106781
The level of the center channel when up/downmixing.
@PAR@ node-prop channelmix.surround-level = 0.707106781
The level of the surround channels when up/downmixing.
@PAR@ node-prop channelmix.lfe-level = 0.5
The level of the LFE channel when up/downmixing.
@PAR@ node-prop channelmix.hilbert-taps = 0
\parblock
This option will apply a 90 degree phase shift to the rear channels to improve specialization.
@ -1375,9 +1398,9 @@ Default: as per QoS preset.
@PAR@ device-prop bluez5.bap.force-target-latency = "balanced" # string
BAP QoS target latency profile forced for QoS configuration selection.
If not set or set to "balanced", both low-latency and high-reliabilty QoS configuration table are used.
If not set or set to "balanced", both low-latency and high-reliability QoS configuration table are used.
This property is experimental.
Available: low-latency, high-reliabilty, balanced
Available: low-latency, high-reliability, balanced
## Node properties
@ -1420,6 +1443,11 @@ them. Below are some port properties may interesting for users:
\copydoc PW_KEY_PORT_ALIAS
\endparblock
@PAR@ port-prop port.passive # string
\parblock
\copydoc PW_KEY_PORT_PASSIVE
\endparblock
\see pw_keys in the API documentation for a full list.
# LINK PROPERTIES @IDX@ props

View file

@ -93,6 +93,9 @@ stream.properties = {
#channelmix.fc-cutoff = 12000.0
#channelmix.rear-delay = 12.0
#channelmix.stereo-widen = 0.0
#channelmix.center-level = 0.707106781
#channelmix.surround-level = 0.707106781
#channelmix.lfe-level = 0.5
#channelmix.hilbert-taps = 0
#dither.noise = 0
#dither.method = none # rectangular, triangular, triangular-hf, wannamaker3, shaped5

View file

@ -349,10 +349,10 @@ rectangles. For example
params[n_params++] = spa_pod_builder_pop(&b, &f);
```
After having received the first \ref SPA_PARAM_PeerCapability param, if it contained the \ref
PW_CAPABILITY_DEVICE_ID set to `true`, the full set of formats can be sent using \ref
pw_stream_update_params following by activating the stream using
`pw_stream_set_active(stream, true)`.
After having received the first \ref SPA_PARAM_PeerCapability param, if it contained the
\ref PW_CAPABILITY_DEVICE_ID_NEGOTIATION set to a supported API version number, the full
set of formats can be sent using \ref pw_stream_update_params following by activating the
stream usina supported API version numberstream_set_active(stream, true)`.
Note that the first \ref SPA_PARAM_Format received may be the result of the initial format
negotian with bare minimum parameters, and will be superseded by the result of the format

View file

@ -10,6 +10,7 @@
- \subpage page_objects_design
- \subpage page_library
- \subpage page_dma_buf
- \subpage page_running
- \subpage page_scheduling
- \subpage page_driver
- \subpage page_latency

View file

@ -103,7 +103,7 @@ down and upstream.
# Async nodes
When a node has the node.async property set to true, it will be considered an async
node and will be scheduled differently, see scheduling.dox.
node and will be scheduled differently, see \ref page_scheduling .
A link between a port of an async node and another port (async or not) is called an
async link and will have the link.async=true property.

View file

@ -62,6 +62,13 @@ As of 1.4, SPA_CONTROL_UMP (Universal Midi Packet) is the prefered format
for the MIDI 1.0 and 2.0 messages in the \ref spa_pod_sequence. Conversion
to SPA_CONTROL_Midi is performed for legacy applications.
As of 1.7 the prefered format is Midi1 again because most devices and
applications are still Midi1 and conversions between Midi1 and UMP are not
completely transparent in ALSA and PipeWire. UMP in the ALSA sequencer
and consumers must be enabled explicitly. UMP in producers is supported
still and will be converted to Midi1 by all consumers that did not explicitly
enable UMP support.
## The PipeWire Daemon
Nothing special is implemented for MIDI. Negotiation of formats
@ -104,13 +111,14 @@ filtering out the \ref SPA_CONTROL_Midi, \ref SPA_CONTROL_OSC and
\ref SPA_CONTROL_UMP types. On output ports, the JACK event stream is
converted to control messages in a similar way.
Normally, all MIDI and UMP messages are converted to MIDI1 jack events unless
the JACK port was created with an explcit "32 bit raw UMP" format or with
the JackPortIsMIDI2 flag, in which case the raw UMP is passed to the JACK
application directly. For output ports,
the JACK events are assumed to be MIDI1 and converted to UMP unless the port
has the "32 bit raw UMP" format or the JackPortIsMIDI2 flag, in which case
the UMP messages are simply passed on.
Normally, all MIDI and UMP input messages are converted to MIDI1 jack
events unless the JACK port was created with an explcit "32 bit raw UMP"
format or with the JackPortIsMIDI2 flag, in which case the messages are
converted to UMP or passed on directly.
For output ports, the JACK events are assumed to be
MIDI1 unless the port has the "32 bit raw UMP" format or the JackPortIsMIDI2
flag, in which case the control messages are assumed to be UMP.
There is a 1 to 1 mapping between the JACK events and control
messages so there is no information loss or need for complicated

View file

@ -0,0 +1,393 @@
/** \page page_running Node running
This document tries to explain how the Nodes in a PipeWire graph become
runnable so that they can be scheduled later.
It also describes how nodes are grouped together and scheduled together.
# Runnable Nodes
A runnable node is a node that will participate in the dataflow in the
PipeWire graph.
Not all nodes should participate by default. For example, filters or device
nodes that are not linked to any runnable nodes should not do useless
processing.
Nodes form one or more groups depending on properties and how they are
linked. For each group, one driver is selected to run the group. Inside the
group, all runnable nodes are scheduled by the group driver. If there are no
runnable nodes in a group, the driver is not started.
PipeWire provides mechanisms to precisely describe how and when nodes should
be scheduled and grouped using:
- port properties to control how port links control runnable state
- node properties to control processing
- grouping of nodes and internal links between nodes
# Port passive modes
A Port has 4 passive modes, this depends on the value of the `port.passive` property:
- `false`, the port will make the peer active and an active peer will make this port
active.
- `true`, the port will not make the peer active and an active peer will not make this
port active.
- `follow`, the port will not make the peer active but an active peer will make this
port active.
- `follow-suspend`, the port will only make another follow-suspend peer active but any
active peer will make this port active.
The combination of these 4 modes on the output and input ports of a link results in a
wide range of use cases.
# Node passive modes
A Node can have 10 passive modes, `node.passive` can be set to a comma separated list
of the following values:
- `false`, both input and output ports have `port.passive = false`
- `in`, input ports have `port.passive = true`
- `out`, output ports have `port.passive = true`
- `true`, both input and output ports have `port.passive = true`. This is the same
as `in,out`.
- `in-follow`, input ports have `port.passive = follow`
- `out-follow`, output ports have `port.passive = follow`
- `follow`, input and output ports have `port.passive = follow`. This is the same
as `in-follow,out-follow`.
- `in-follow-suspend`, input ports have `port.passive = follow-suspend`
- `out-follow-suspend`, output ports have `port.passive = follow-suspend`
- `follow-suspend`, input and output ports have `port.passive = follow-suspend`.
This is the same as `in-follow-suspend,out-follow-suspend`.
Nodes by default have the `false` mode but nodes with the `media.class` property
containing `Sink`, `Source` or `Duplex` receive the `follow-suspend` mode by default.
Unless explicitly configured, ports inherit the mode from their parent node.
# Updating the node runnable state
We iterate all nodes A in the graph and look at its peers B.
Based on the port passive modes of the port links we can decide if the nodes are
runnable or not. A link will always make both nodes runnable or none.
The following table decides the runnability of the 2 nodes based on the port.passive
mode of the link between the 2 ports:
```
B-false B-true B-follow B-follow-suspend
A-false X X X X
A-true
A-follow
A-follow-suspend X
Table 1
```
When a node is made runnable, the port passive mode will then decide if the peer ports
should become active as well with the following table.
```
B-false B-true B-follow B-follow-suspend
A-false X X X
A-true X X X
A-follow X X X
A-follow-suspend X X X
Table 2
```
So when A is runnable, all peers are activated except those with `port.passive=true`.
When A is runnable, all the nodes that share the same group or link-group will also
be made runnable.
# Use cases
Let's check some cases that we want to solve with these node and port properties.
## Device nodes
```
+--------+ +--------+
| ALSA | | ALSA |
| Source | | Sink |
| FL FL |
| FR FR |
+--------+ +--------+
```
Unlinked device nodes are supposed to stay suspended when nothing is linked to
them.
```
+----------+ +--------+
| playback | | ALSA |
| | | Sink |
| FL ------ FL |
| FR ------ FR |
+----------+ +--------+
```
An (active) player node linked to a device node should make both nodes runnable.
Device nodes have the `port.passive = follow-suspend` property by default. The
playback node has the `port.passive = false` by default.
If we look at the playback node as A and the sink as B, both nodes will be made
runnable according to Table 1.
The two runnable nodes form a group and will be scheduled together. One of the
nodes of a group with the `node.driver = true` property is selected as the
driver. In the above case, that will be the ALSA Sink.
Likewise, a capture node linked to an ALSA Source should make both nodes runnable.
```
+--------+ +---------+
| ALSA | | capture |
| Source | | |
| FL ------ FL |
| FR ------ FR |
+--------+ +---------+
```
The ALSA Source is now the driver.
Also, linking 2 device nodes together should make them runnable:
```
+--------+ +--------+
| ALSA | | ALSA |
| Source | | Sink |
| FL ----------------------- FL |
| FR ----------------------- FR |
+--------+ +--------+
```
This is the case because in Table 1, the two `port.passive = follow-suspend` ports
from the Source and Sink activate each other.
## Filter nodes
When there is a filter in front of the ALSA Sink, it should not make the filter and
sink runnable.
```
+--------+ +--------+
| filter | | ALSA |
| | | Sink |
FL FL ------ FL |
FR FR ------ FR |
+--------+ +--------+
```
The links between the filter and ALSA Sink are `port.passive = true` and don't make
the nodes runnable.
The filter needs to be made runnable via some other means to also make the ALSA
Sink runnable, for example by linking a playback node:
```
+----------+ +--------+ +--------+
| playback | | filter | | ALSA |
| | | | | Sink |
| FL ------ FL FL ------ FL |
| FR ------ FR FR ------ FR |
+----------+ +--------+ +--------+
```
The input port of the filter is `port.passive = follow-suspend' and so it can be
activated by the playback node.
Likewise, if the ALSA Sink is runnable, it should not automatically make the
filter runnable. For example:
```
+--------+ +--------+
| filter | | ALSA |
| | | Sink |
FL FL ---+-- FL |
FR FR ---|+- FR |
+--------+ || +--------+
||
+----------+ ||
| playback | ||
| | ||
| FL ---+|
| FR ----+
+----------+
```
Here the playback node makes the ALSA Sink runnable but the filter
stays not-runnable because the output port is `port.passive = true`.
## Device node monitor
Consider the case where we have an ALSA Sink and a monitor stream
connected to the sink monitor ports.
```
+-------+ +--------++
| ALSA | | monitor |
| Sink | | |
FL FL ------ FL |
FR FR ------ FR |
+-------+ +---------+
```
We would like to keep the monitor stream and the ALSA sink suspended
unless something else activates the ALSA Sink:
```
+----------+ +-------+ +---------+
| playback | | ALSA | | monitor |
| | | Sink | | |
| FL ------ FL FL ------ FL |
| FR ------ FR FR ------ FR |
+----------+ +-------+ +---------+
```
We can do this by making the monitor stream input ports `port.passive = follow`
and leave the ALSA Sink monitor output ports as `port.passive = follow-suspend`.
According to Table 1, both nodes will not activate each other but when ALSA Sink
becomes runnable because of playback, according to Table 2, the monitor will
become runnable as well.
Note how we need the distinction between `follow` and `follow-suspend` for this
use case.
## Node groups
Normally when an application makes a capture and playback node, both nodes will
be scheduled in different groups, consider:
```
+--------+ +---------+
| ALSA | | capture |
| Source | | |
| FL ------ FL |
| FR ------ FR |
+--------+ +---------+
+----------+ +--------+
| playback | | ALSA |
| | | Sink |
| FL ------ FL |
| FR ------ FR |
+----------+ +--------+
```
Here we see 2 groups with the ALSA Source and ALSA Sink respectively as the
drivers. Depending on the clocks of the nodes, the capture and playback will not
be in sync. They will each run in their own time domain depending on the rate of
the drivers.
When we place a node.group property with the same value on the capture and playback
nodes, they will be grouped together and this whole graph becomes one single group.
Because there are 2 potential drivers in the group, the one with the highest
`priority.driver` property is selected as the driver in the group. The other nodes
in the group (including the other driver) become followers in the group.
When a node becomes runnable, all other nodes with the same node.group property
become runnable as well.
## Node link groups
When we have a filter that is constructed from two nodes, an input and an output
node, we could use the `node.group` property to make sure they are both scheduled
and made runnable together.
```
+--------+ +-------+ +--------+ +-------+
| ALSA | | input | | output | | ALSA |
| Source | | | | | | Sink |
| FL ------ FL -- processing-- FL ------ FL |
| FR ------ FR | | FR ------ FR |
+--------+ +-------+ +--------+ +-------+
```
This would work fine but it does not describe that there is an implicit internal
link between the input and output node. This information is important for the
session manager to avoid linking the output node to the input node and make a
loop.
The `node.link-group` property can be used to both group the nodes together and
descibe that they are internally linked together.
When a node becomes runnable, all other nodes with the same node.link-group property
become runnable as well.
For the 2 node filters, like loopback and filter-chain, the same `port.passive`
property rules apply as for the filter nodes. Note that for the virtual devices,
the Source/Sink nodes will be `follow-suspend` by default and the other node should
be set to `node.passive = true` to make the ports passive.
## Want driver
When there is no driver node in the group, nothing should be scheduled. This can
happen when a playback node is linked to a capture node:
```
+--------+ +---------+
| player | | capture |
| | | |
| FL ----------- FL |
| FR ----------- FR |
+--------+ +---------+
```
None of these nodes is a driver so there is no driver in the group and nothing
will be scheduled.
When one of the nodes has `node.want-driver = true` they are grouped and
scheduled with a random driver node. This is often the driver node with the
highest priority (usually the Dummy-Driver) or otherwise a driver that is already
scheduling some other nodes.
## Always process nodes
A simple node, unlinked to anything should normally not run.
```
+--------+
| player |
| |
| FL
| FR
+--------+
```
When the `node.always-process = true` property is set, the node will however be
made runnable even if unlinked. This is done by adding the node to a random driver.
`node.always-process = true` implies the `node.want-driver = true` property.
## Sync groups
In some cases, you only want to group nodes together depending on some condition.
For example, when the JACK transport is activated, all nodes in the graph should share
the same driver node, regardless of the grouping or linking of the nodes.
This is done by setting the same node.sync-group property on all nodes (by default all
nodes have `node.sync-group = group.sync.0`). When a node sets `node.sync = true` all
the other nodes with the same `node.sync-group` property are grouped together.
This can be used to implement the JACK transport. When the transport is started, the
`node.sync=true` property is set and all nodes join one group with a shared driver
and timing information.
*/

View file

@ -23,6 +23,9 @@ node is scheduled to run.
This document describes the processing that happens in the data processing
thread after the main thread has configured it.
Before scheduling of the node happens, the scheduler will collect a list of
nodes that are runnable, see \ref page_running
# Nodes
Nodes are objects with 0 or more input and output ports.

View file

@ -81,6 +81,7 @@ List of known modules:
- \subpage page_module_raop_discover
- \subpage page_module_roc_sink
- \subpage page_module_roc_source
- \subpage page_module_scheduler_v1
- \subpage page_module_rtp_sap
- \subpage page_module_rtp_sink
- \subpage page_module_rtp_source
@ -90,6 +91,8 @@ List of known modules:
- \subpage page_module_spa_node_factory
- \subpage page_module_spa_device
- \subpage page_module_spa_device_factory
- \subpage page_module_sendspin_recv
- \subpage page_module_sendspin_send
- \subpage page_module_session_manager
- \subpage page_module_snapcast_discover
- \subpage page_module_vban_recv

View file

@ -77,7 +77,7 @@ Certain properties are, by convention, expected for specific object types.
Each object type has a list of methods that it needs to implement.
The session manager is responsible for defining the list of permissions each client has. Each permission entry is an object ID and four flags. The four flags are:
The session manager is responsible for defining the list of permissions each client has. Each permission entry is an object ID and five flags. The five flags are:
- Read: the object can be seen and events can be received;
- Write: the object can be modified, usually through methods (which requires the execute flag)
@ -109,7 +109,7 @@ Modules in PipeWire can only be loaded in their own process. A client, for examp
Nodes are the core data processing entities in PipeWire.
They may produce data (capture devices, signal generators, ...), consume data (playback devices, network endpoints, ...) or both (filters).
Notes have a method `process`, which eats up data from input ports and provides data for each output port.
Nodes have a method `process`, which eats up data from input ports and provides data for each output port.
#### Ports

View file

@ -124,6 +124,9 @@ Set a node target (default auto). The value can be:
- <b>\<id\></b>: The object.serial or the node.name of a target node
\endparblock
\par -C | \--monitor
In recording mode, record from monitor ports.
\par \--latency=VALUE\[*units*\]
\parblock
Set the node latency (default 100ms)

View file

@ -188,6 +188,11 @@ Quit
Clear the ERR counters. This does *not* clear the counters globally,
it will only reset the counters in this instance of *pw-top*.
\par [f|F]
Cycle through filter presets. If any nodes are filtered from view,
the current preset will be indicated in the header bar. Only nodes
with the indicated state or higher, will be printed.
# OPTIONS
\par -h | \--help
@ -199,6 +204,9 @@ Run in non-interactive batch mode, similar to top\'s batch mode.
\par -n | \--iterations=NUMBER
Exit after NUMBER of batch iterations. Only used in batch mode.
\par -f | \--filter=NUMBER
Start with filter preset NUMBER selected.
\par -r | \--remote=NAME
The name the *remote* instance to monitor. If left unspecified, a
connection is made to the default PipeWire instance.

View file

@ -67,6 +67,7 @@ extra_docs = [
'dox/internals/session-manager.dox',
'dox/internals/objects.dox',
'dox/internals/audio.dox',
'dox/internals/running.dox',
'dox/internals/scheduling.dox',
'dox/internals/driver.dox',
'dox/internals/protocol.dox',

View file

@ -1,5 +1,5 @@
project('pipewire', ['c' ],
version : '1.6.0',
version : '1.7.0',
license : [ 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ],
meson_version : '>= 0.61.1',
default_options : [ 'warning_level=3',
@ -116,6 +116,7 @@ cc_flags = common_flags + [
'-Werror=old-style-definition',
'-Werror=missing-parameter-type',
'-Werror=strict-prototypes',
'-Werror=discarded-qualifiers',
]
add_project_arguments(cc.get_supported_arguments(cc_flags), language: 'c')
add_project_arguments(cc_native.get_supported_arguments(cc_flags),
@ -367,7 +368,7 @@ cdata.set('HAVE_OPUS', opus_dep.found())
summary({'readline (for pw-cli)': readline_dep.found()}, bool_yn: true, section: 'Misc dependencies')
cdata.set('HAVE_READLINE', readline_dep.found())
ncurses_dep = dependency('ncursesw', required : false)
sndfile_dep = dependency('sndfile', version : '>= 1.0.20', required : get_option('sndfile'))
sndfile_dep = dependency('sndfile', version : '>= 1.1.0', required : get_option('sndfile'))
summary({'sndfile': sndfile_dep.found()}, bool_yn: true, section: 'pw-cat/pw-play/pw-dump/filter-chain')
cdata.set('HAVE_SNDFILE', sndfile_dep.found())
pulseaudio_dep = dependency('libpulse', required : get_option('libpulse'))
@ -411,7 +412,7 @@ gst_deps_def = {
'gio-unix-2.0': {},
'gstreamer-1.0': {'version': '>= 1.10.0'},
'gstreamer-base-1.0': {},
'gstreamer-video-1.0': {},
'gstreamer-video-1.0': {'version': '>= 1.22.0'},
'gstreamer-audio-1.0': {},
'gstreamer-allocators-1.0': {},
}

View file

@ -55,7 +55,7 @@ pipewire_jackserver = shared_library('jackserver',
pipewire_jackserver_sources,
soversion : soversion,
version : libjackversion,
c_args : pipewire_jack_c_args,
c_args : pipewire_jack_c_args + '-DLIBJACKSERVER',
include_directories : [configinc, jack_inc],
dependencies : [pipewire_dep, mathlib],
install : true,

View file

@ -86,7 +86,7 @@ PW_LOG_TOPIC_STATIC(jack_log_topic, "jack");
#define OTHER_CONNECT_FAIL -1
#define OTHER_CONNECT_IGNORE 0
#define NOTIFY_BUFFER_SIZE (1u<<13)
#define NOTIFY_BUFFER_SIZE (1u<<16)
#define NOTIFY_BUFFER_MASK (NOTIFY_BUFFER_SIZE-1)
struct notify {
@ -104,8 +104,8 @@ struct notify {
#define NOTIFY_TYPE_TOTAL_LATENCY ((9<<4)|NOTIFY_ACTIVE_FLAG)
#define NOTIFY_TYPE_PORT_RENAME ((10<<4)|NOTIFY_ACTIVE_FLAG)
int type;
struct object *object;
int arg1;
struct object *object;
const char *msg;
};
@ -492,6 +492,8 @@ struct client {
jack_position_t jack_position;
jack_transport_state_t jack_state;
struct frame_times jack_times;
struct object dummy_port;
};
#define return_val_if_fail(expr, val) \
@ -1446,8 +1448,9 @@ static size_t convert_from_event(void *midi, void *buffer, size_t size, uint32_t
switch (type) {
case TYPE_ID_MIDI:
event_type = SPA_CONTROL_Midi;
break;
case TYPE_ID_OSC:
/* we handle MIDI as OSC, check below */
event_type = SPA_CONTROL_OSC;
break;
case TYPE_ID_UMP:
@ -1464,27 +1467,15 @@ static size_t convert_from_event(void *midi, void *buffer, size_t size, uint32_t
for (i = 0; i < count; i++) {
jack_midi_event_t ev;
jack_midi_event_get(&ev, midi, i);
uint32_t ev_type;
if (type != TYPE_ID_MIDI || is_osc(&ev)) {
/* no midi port or it's OSC */
spa_pod_builder_control(&b, ev.time, event_type);
spa_pod_builder_bytes(&b, ev.buffer, ev.size);
} else {
/* midi port and it's not OSC, convert to UMP */
uint8_t *data = ev.buffer;
size_t size = ev.size;
uint64_t state = 0;
if (type == TYPE_ID_MIDI && is_osc(&ev))
ev_type = SPA_CONTROL_OSC;
else
ev_type = event_type;
while (size > 0) {
uint32_t ump[4];
int ump_size = spa_ump_from_midi(&data, &size,
ump, sizeof(ump), 0, &state);
if (ump_size <= 0)
break;
spa_pod_builder_control(&b, ev.time, SPA_CONTROL_UMP);
spa_pod_builder_bytes(&b, ump, ump_size);
}
}
spa_pod_builder_control(&b, ev.time, ev_type);
spa_pod_builder_bytes(&b, ev.buffer, ev.size);
}
spa_pod_builder_pop(&b, &f);
return b.state.offset;
@ -2208,7 +2199,7 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask)
}
} else if (SPA_LIKELY(mask & SPA_IO_IN)) {
uint32_t buffer_frames;
int status = 0;
int status = -EBUSY;
buffer_frames = cycle_run(c);
@ -4468,6 +4459,11 @@ jack_client_t * jack_client_open (const char *client_name,
0, NULL, &client->info);
client->info.change_mask = 0;
client->dummy_port.type = INTERFACE_Port;
snprintf(client->dummy_port.port.name, sizeof(client->dummy_port.port.name), "%s:dummy", client_name);
snprintf(client->dummy_port.port.alias1, sizeof(client->dummy_port.port.alias1), "%s:dummy", client_name);
snprintf(client->dummy_port.port.alias2, sizeof(client->dummy_port.port.alias2), "%s:dummy", client_name);
client->show_monitor = pw_properties_get_bool(client->props, "jack.show-monitor", true);
client->show_midi = pw_properties_get_bool(client->props, "jack.show-midi", true);
client->merge_monitor = pw_properties_get_bool(client->props, "jack.merge-monitor", true);
@ -4868,7 +4864,7 @@ int jack_activate (jack_client_t *client)
freeze_callbacks(c);
/* reemit buffer_frames */
c->buffer_frames = 0;
c->buffer_frames = (uint32_t)-1;
pw_data_loop_start(c->loop);
c->active = true;
@ -4880,9 +4876,21 @@ int jack_activate (jack_client_t *client)
c->activation->pending_sync = true;
spa_list_for_each(o, &c->context.objects, link) {
#if !defined(LIBJACKSERVER)
if (o->type != INTERFACE_Port || o->port.port == NULL ||
o->port.port->client != c || !o->port.port->valid)
continue;
#else
/* emits all foreign active ports, skips own (already announced via jack_port_register) */
if (o->type != INTERFACE_Port || o->removed)
continue;
/* own ports are handled by jack_port_register */
if (o->port.port != NULL && o->port.port->client == c)
continue;
/* only announce ports whose node is active */
if (o->port.node != NULL && !node_is_active(c, o->port.node))
continue;
#endif
o->registered = 0;
queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, o, 1, NULL);
}
@ -5318,7 +5326,7 @@ int jack_set_freewheel(jack_client_t* client, int onoff)
pw_thread_loop_lock(c->context.loop);
str = pw_properties_get(c->props, PW_KEY_NODE_GROUP);
if (str != NULL) {
char *p = strstr(str, ",pipewire.freewheel");
const char *p = strstr(str, ",pipewire.freewheel");
if (p == NULL)
p = strstr(str, "pipewire.freewheel");
if (p == NULL && onoff)
@ -5437,7 +5445,7 @@ SPA_EXPORT
jack_nframes_t jack_get_buffer_size (jack_client_t *client)
{
struct client *c = (struct client *) client;
jack_nframes_t res = -1;
uint32_t res = -1;
return_val_if_fail(c != NULL, 0);
@ -5454,7 +5462,7 @@ jack_nframes_t jack_get_buffer_size (jack_client_t *client)
}
c->buffer_frames = res;
pw_log_debug("buffer_frames: %u", res);
return res;
return (jack_nframes_t)res;
}
SPA_EXPORT
@ -5951,9 +5959,7 @@ static const char *port_name(struct object *o)
{
const char *name;
struct client *c = o->client;
if (c == NULL)
return NULL;
if (c->default_as_system && is_port_default(c, o))
if (c != NULL && c->default_as_system && is_port_default(c, o))
name = o->port.system;
else
name = o->port.name;
@ -6007,7 +6013,16 @@ jack_port_type_id_t jack_port_type_id (const jack_port_t *port)
return_val_if_fail(o != NULL, 0);
if (o->type != INTERFACE_Port)
return TYPE_ID_OTHER;
return o->port.type_id;
/* map internal type IDs to jack1/jack2 compatible public values */
switch (o->port.type_id) {
case TYPE_ID_AUDIO: return 0;
case TYPE_ID_MIDI:
case TYPE_ID_OSC:
case TYPE_ID_UMP: return 1; /* all MIDI variants map to 1 */
case TYPE_ID_VIDEO: return 3; /* video maps to 3 */
default: return o->port.type_id;
}
}
SPA_EXPORT
@ -6999,13 +7014,11 @@ jack_port_t * jack_port_by_id (jack_client_t *client,
pthread_mutex_lock(&c->context.lock);
res = find_by_serial(c, port_id);
if (res && res->type != INTERFACE_Port)
res = NULL;
pw_log_debug("%p: port %d -> %p", c, port_id, res);
pthread_mutex_unlock(&c->context.lock);
if (res == NULL || res->type != INTERFACE_Port)
res = &c->dummy_port;
if (res == NULL)
pw_log_info("%p: port %d not found", c, port_id);
pw_log_debug("%p: port %d -> %p", c, port_id, res);
return object_to_port(res);
}

View file

@ -2570,7 +2570,10 @@ static void *v4l2_mmap(void *addr, size_t length, int prot,
buf = &file->buffers[id];
data = &buf->buf->buffer->datas[0];
pw_map_range_init(&range, data->mapoffset, data->maxsize, 1024);
if (pw_map_range_init(&range, data->mapoffset, data->maxsize, 1024) < 0) {
res = MAP_FAILED;
goto error_unlock;
}
if (!SPA_FLAG_IS_SET(data->flags, SPA_DATA_FLAG_READABLE))
prot &= ~PROT_READ;

573
po/kk.po
View file

@ -1,15 +1,14 @@
# Kazakh translation of pipewire.
# Copyright (C) 2020 The pipewire authors.
# This file is distributed under the same license as the pipewire package.
# Baurzhan Muftakhidinov <baurthefirst@gmail.com>, 2020.
# Baurzhan Muftakhidinov <baurthefirst@gmail.com>, 2020-2026.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/"
"issues/new\n"
"POT-Creation-Date: 2021-04-18 16:54+0800\n"
"PO-Revision-Date: 2020-06-30 08:04+0500\n"
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues\n"
"POT-Creation-Date: 2026-03-09 12:19+0000\n"
"PO-Revision-Date: 2026-03-17 00:04+0500\n"
"Last-Translator: Baurzhan Muftakhidinov <baurthefirst@gmail.com>\n"
"Language-Team: \n"
"Language: kk\n"
@ -17,96 +16,199 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.3.1\n"
"X-Generator: Poedit 3.9\n"
#: src/daemon/pipewire.c:43
#: src/daemon/pipewire.c:29
#, c-format
msgid ""
"%s [options]\n"
" -h, --help Show this help\n"
" -v, --verbose Increase verbosity by one level\n"
" --version Show version\n"
" -c, --config Load config (Default %s)\n"
" -P --properties Set context properties\n"
msgstr ""
"%s [опциялар]\n"
" -h, --help Осы көмекті көрсету\n"
" -v, --verbose Ақпараттылығын бір деңгейге арттыру\n"
" --version Нұсқасын көрсету\n"
" -c, --config Конфигурацияны жүктеу (Бастапқы %s)\n"
" -P --properties Контекст қасиеттерін орнату\n"
#: src/daemon/pipewire.desktop.in:3
msgid "PipeWire Media System"
msgstr "PipeWire медиа жүйесі"
#: src/daemon/pipewire.desktop.in:4
msgid "PipeWire Media System"
msgstr ""
#: src/daemon/pipewire.desktop.in:5
msgid "Start the PipeWire Media System"
msgstr ""
msgstr "PipeWire медиа жүйесін іске қосу"
#: src/examples/media-session/alsa-monitor.c:526
#: spa/plugins/alsa/acp/compat.c:187
msgid "Built-in Audio"
msgstr "Құрамындағы аудио"
#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159
#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159
#, c-format
msgid "Tunnel to %s%s%s"
msgstr "%s%s%s бағытына туннель"
#: src/examples/media-session/alsa-monitor.c:530
#: spa/plugins/alsa/acp/compat.c:192
msgid "Modem"
msgstr "Модем"
#: src/modules/module-fallback-sink.c:40
msgid "Dummy Output"
msgstr "Жалған шығыс"
#: src/examples/media-session/alsa-monitor.c:539
#: src/modules/module-pulse-tunnel.c:761
#, c-format
msgid "Tunnel for %s@%s"
msgstr "%s@%s үшін туннель"
#: src/modules/module-zeroconf-discover.c:290
msgid "Unknown device"
msgstr ""
msgstr "Белгісіз құрылғы"
#: src/tools/pw-cat.c:991
#: src/modules/module-zeroconf-discover.c:302
#, c-format
msgid "%s on %s@%s"
msgstr "%s, %s@%s ішінде"
#: src/modules/module-zeroconf-discover.c:306
#, c-format
msgid "%s on %s"
msgstr "%s, %s ішінде"
#: src/tools/pw-cat.c:269
#, c-format
msgid "Supported formats:\n"
msgstr "Қолдау көрсетілетін пішімдер:\n"
#: src/tools/pw-cat.c:754
#, c-format
msgid "Supported channel layouts:\n"
msgstr "Қолдау көрсетілетін арна жаймалары:\n"
#: src/tools/pw-cat.c:764
#, c-format
msgid "Supported channel layout aliases:\n"
msgstr "Қолдау көрсетілетін арна жаймаларының алиастары:\n"
#: src/tools/pw-cat.c:766
#, c-format
msgid " %s -> %s\n"
msgstr " %s -> %s\n"
#: src/tools/pw-cat.c:771
#, c-format
msgid "Supported channel names:\n"
msgstr "Қолдау көрсетілетін арна атаулары:\n"
#: src/tools/pw-cat.c:1182
#, c-format
msgid ""
"%s [options] <file>\n"
"%s [options] [<file>|-]\n"
" -h, --help Show this help\n"
" --version Show version\n"
" -v, --verbose Enable verbose operations\n"
"\n"
msgstr ""
"%s [опциялар] [<файл>|-]\n"
" -h, --help Осы көмекті көрсету\n"
" --version Нұсқасын көрсету\n"
" -v, --verbose Толық ақпаратты іске қосу\n"
"\n"
#: src/tools/pw-cat.c:998
#: src/tools/pw-cat.c:1189
#, c-format
msgid ""
" -R, --remote Remote daemon name\n"
" --media-type Set media type (default %s)\n"
" --media-category Set media category (default %s)\n"
" --media-role Set media role (default %s)\n"
" --target Set node target (default %s)\n"
" --target Set node target serial or name (default %s)\n"
" 0 means don't link\n"
" --latency Set node latency (default %s)\n"
" Xunit (unit = s, ms, us, ns)\n"
" or direct samples (256)\n"
" the rate is the one of the source "
"file\n"
" --list-targets List available targets for --target\n"
" the rate is the one of the source file\n"
" -P --properties Set node properties\n"
"\n"
msgstr ""
" -R, --remote Қашықтағы қызмет атауы\n"
" --media-type Медиа түрін орнату (бастапқы %s)\n"
" --media-category Медиа категориясын орнату (бастапқы %s)\n"
" --media-role Медиа рөлін орнату (бастапқы %s)\n"
" --target Түйін мақсатының сериялық нөмірін немесе атын орнату (бастапқы "
"%s)\n"
" 0 байланыстырмауды білдіреді\n"
" --latency Түйін кідірісін орнату (бастапқы %s)\n"
" Xюнит (юнит = с, мс, мкс, нс)\n"
" немесе тікелей үлгілер (256)\n"
" жиілік бастапқы файлдың жиілігі болып табылады\n"
" -P --properties Түйін қасиеттерін орнату\n"
"\n"
#: src/tools/pw-cat.c:1016
#: src/tools/pw-cat.c:1207
#, c-format
msgid ""
" --rate Sample rate (req. for rec) (default "
"%u)\n"
" --channels Number of channels (req. for rec) "
"(default %u)\n"
" --rate Sample rate (default %u)\n"
" --channels Number of channels (default %u)\n"
" --channel-map Channel map\n"
" one of: \"stereo\", "
"\"surround-51\",... or\n"
" comma separated list of channel "
"names: eg. \"FL,FR\"\n"
" --format Sample format %s (req. for rec) "
"(default %s)\n"
" a channel layout: \"Stereo\", \"5.1\",... or\n"
" comma separated list of channel names: eg. \"FL,FR\"\n"
" --list-layouts List supported channel layouts\n"
" --list-channel-names List supported channel maps\n"
" --format Sample format (default %s)\n"
" --list-formats List supported sample formats\n"
" --container Container format\n"
" --list-containers List supported containers and extensions\n"
" --volume Stream volume 0-1.0 (default %.3f)\n"
" -q --quality Resampler quality (0 - 15) (default "
"%d)\n"
" -q --quality Resampler quality (0 - 15) (default %d)\n"
" -a, --raw RAW mode\n"
" -M, --force-midi Force midi format, one of \"midi\" or \"ump\", (default ump)\n"
" -n, --sample-count COUNT Stop after COUNT samples\n"
"\n"
msgstr ""
" --rate Дискреттеу жиілігі (бастапқы %u)\n"
" --channels Арналар саны (бастапқы %u)\n"
" --channel-map Арналар картасы\n"
" арна жаймасы: «Stereo», «5.1»,... немесе\n"
" үтірмен ажыратылған арна атауларының тізімі: мысалы, "
"«FL,FR»\n"
" --list-layouts Қолдау көрсетілетін арна жаймаларын тізімдеу\n"
" --list-channel-names Қолдау көрсетілетін арналар картасын тізімдеу\n"
" --format Дискреттеу пішімі (бастапқы %s)\n"
" --list-formats Қолдау көрсетілетін дискреттеу пішімдерін тізімдеу\n"
" --container Контейнер пішімі\n"
" --list-containers Қолдау көрсетілетін контейнерлер мен кеңейтулерді тізімдеу\n"
" --volume Ағын дыбыс деңгейі 0-1.0 (бастапқы %.3f)\n"
" -q --quality Қайта дискреттеу сапасы (0 - 15) (бастапқы %d)\n"
" -a, --raw RAW режимі\n"
" -M, --force-midi Midi пішімін мәжбүрлеу, «midi» немесе «ump» біреуі, (бастапқы "
"ump)\n"
" -n, --sample-count COUNT COUNT үлгісінен кейін тоқтату\n"
"\n"
#: src/tools/pw-cat.c:1033
#: src/tools/pw-cat.c:1232
msgid ""
" -p, --playback Playback mode\n"
" -r, --record Recording mode\n"
" -m, --midi Midi mode\n"
" -d, --dsd DSD mode\n"
" -o, --encoded Encoded mode\n"
" -s, --sysex SysEx mode\n"
" -c, --midi-clip MIDI clip mode\n"
"\n"
msgstr ""
" -p, --playback Ойнату режимі\n"
" -r, --record Жазу режимі\n"
" -m, --midi Midi режимі\n"
" -d, --dsd DSD режимі\n"
" -o, --encoded Шифрленген режим\n"
" -s, --sysex SysEx режимі\n"
" -c, --midi-clip MIDI clip режимі\n"
"\n"
#: src/tools/pw-cli.c:2932
#: src/tools/pw-cat.c:1837
#, c-format
msgid "Supported containers and extensions:\n"
msgstr "Қолдау көрсетілетін контейнерлер мен кеңейтулер:\n"
#: src/tools/pw-cli.c:2386
#, c-format
msgid ""
"%s [options] [command]\n"
@ -114,465 +216,506 @@ msgid ""
" --version Show version\n"
" -d, --daemon Start as daemon (Default false)\n"
" -r, --remote Remote daemon name\n"
" -m, --monitor Monitor activity\n"
"\n"
msgstr ""
"%s [опциялар] [команда]\n"
" -h, --help Осы көмекті көрсету\n"
" --version Нұсқасын көрсету\n"
" -d, --daemon Қызмет ретінде іске қосу (Бастапқы false)\n"
" -r, --remote Қашықтағы қызмет атауы\n"
" -m, --monitor Белсенділікті бақылау\n"
"\n"
#: spa/plugins/alsa/acp/acp.c:290
#: spa/plugins/alsa/acp/acp.c:361
msgid "Pro Audio"
msgstr ""
msgstr "Кәсіби аудио"
#: spa/plugins/alsa/acp/acp.c:411 spa/plugins/alsa/acp/alsa-mixer.c:4704
#: spa/plugins/bluez5/bluez5-device.c:1000
#: spa/plugins/alsa/acp/acp.c:535 spa/plugins/alsa/acp/alsa-mixer.c:4699
#: spa/plugins/bluez5/bluez5-device.c:2021
msgid "Off"
msgstr "Сөнд."
#: spa/plugins/alsa/acp/channelmap.h:466
msgid "(invalid)"
msgstr "(жарамсыз)"
#: spa/plugins/alsa/acp/acp.c:618
#, c-format
msgid "%s [ALSA UCM error]"
msgstr "%s [ALSA UCM қатесі]"
#: spa/plugins/alsa/acp/alsa-mixer.c:2709
#: spa/plugins/alsa/acp/alsa-mixer.c:2721
msgid "Input"
msgstr "Кіріс"
#: spa/plugins/alsa/acp/alsa-mixer.c:2710
#: spa/plugins/alsa/acp/alsa-mixer.c:2722
msgid "Docking Station Input"
msgstr "Док-станция кірісі"
#: spa/plugins/alsa/acp/alsa-mixer.c:2711
#: spa/plugins/alsa/acp/alsa-mixer.c:2723
msgid "Docking Station Microphone"
msgstr "Док-станция микрофоны"
#: spa/plugins/alsa/acp/alsa-mixer.c:2712
#: spa/plugins/alsa/acp/alsa-mixer.c:2724
msgid "Docking Station Line In"
msgstr "Док-станцияның сызықтық кірісі"
#: spa/plugins/alsa/acp/alsa-mixer.c:2713
#: spa/plugins/alsa/acp/alsa-mixer.c:2804
#: spa/plugins/alsa/acp/alsa-mixer.c:2725 spa/plugins/alsa/acp/alsa-mixer.c:2816
msgid "Line In"
msgstr "Сызықтық кіріс"
#: spa/plugins/alsa/acp/alsa-mixer.c:2714
#: spa/plugins/alsa/acp/alsa-mixer.c:2798
#: spa/plugins/bluez5/bluez5-device.c:1145
#: spa/plugins/alsa/acp/alsa-mixer.c:2726 spa/plugins/alsa/acp/alsa-mixer.c:2810
#: spa/plugins/bluez5/bluez5-device.c:2422
msgid "Microphone"
msgstr "Микрофон"
#: spa/plugins/alsa/acp/alsa-mixer.c:2715
#: spa/plugins/alsa/acp/alsa-mixer.c:2799
#: spa/plugins/alsa/acp/alsa-mixer.c:2727 spa/plugins/alsa/acp/alsa-mixer.c:2811
msgid "Front Microphone"
msgstr "Алдыңғы микрофон"
#: spa/plugins/alsa/acp/alsa-mixer.c:2716
#: spa/plugins/alsa/acp/alsa-mixer.c:2800
#: spa/plugins/alsa/acp/alsa-mixer.c:2728 spa/plugins/alsa/acp/alsa-mixer.c:2812
msgid "Rear Microphone"
msgstr "Артқы микрофон"
#: spa/plugins/alsa/acp/alsa-mixer.c:2717
#: spa/plugins/alsa/acp/alsa-mixer.c:2729
msgid "External Microphone"
msgstr "Сыртқы микрофон"
#: spa/plugins/alsa/acp/alsa-mixer.c:2718
#: spa/plugins/alsa/acp/alsa-mixer.c:2802
#: spa/plugins/alsa/acp/alsa-mixer.c:2730 spa/plugins/alsa/acp/alsa-mixer.c:2814
msgid "Internal Microphone"
msgstr "Ішкі микрофон"
#: spa/plugins/alsa/acp/alsa-mixer.c:2719
#: spa/plugins/alsa/acp/alsa-mixer.c:2805
#: spa/plugins/alsa/acp/alsa-mixer.c:2731 spa/plugins/alsa/acp/alsa-mixer.c:2817
msgid "Radio"
msgstr "Радио"
#: spa/plugins/alsa/acp/alsa-mixer.c:2720
#: spa/plugins/alsa/acp/alsa-mixer.c:2806
#: spa/plugins/alsa/acp/alsa-mixer.c:2732 spa/plugins/alsa/acp/alsa-mixer.c:2818
msgid "Video"
msgstr "Видео"
#: spa/plugins/alsa/acp/alsa-mixer.c:2721
#: spa/plugins/alsa/acp/alsa-mixer.c:2733
msgid "Automatic Gain Control"
msgstr "Күшейтуді автореттеу"
#: spa/plugins/alsa/acp/alsa-mixer.c:2722
#: spa/plugins/alsa/acp/alsa-mixer.c:2734
msgid "No Automatic Gain Control"
msgstr "Күшейтуді автореттеу жоқ"
#: spa/plugins/alsa/acp/alsa-mixer.c:2723
#: spa/plugins/alsa/acp/alsa-mixer.c:2735
msgid "Boost"
msgstr "Күшейту"
#: spa/plugins/alsa/acp/alsa-mixer.c:2724
#: spa/plugins/alsa/acp/alsa-mixer.c:2736
msgid "No Boost"
msgstr "Күшейту жоқ"
#: spa/plugins/alsa/acp/alsa-mixer.c:2725
#: spa/plugins/alsa/acp/alsa-mixer.c:2737
msgid "Amplifier"
msgstr "Күшейткіш"
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
#: spa/plugins/alsa/acp/alsa-mixer.c:2738
msgid "No Amplifier"
msgstr "Күшейткіш жоқ"
#: spa/plugins/alsa/acp/alsa-mixer.c:2727
#: spa/plugins/alsa/acp/alsa-mixer.c:2739
msgid "Bass Boost"
msgstr "Бас күшейту"
#: spa/plugins/alsa/acp/alsa-mixer.c:2728
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
msgid "No Bass Boost"
msgstr "Бас күшейту жоқ"
#: spa/plugins/alsa/acp/alsa-mixer.c:2729
#: spa/plugins/bluez5/bluez5-device.c:1150
#: spa/plugins/alsa/acp/alsa-mixer.c:2741 spa/plugins/bluez5/bluez5-device.c:2428
msgid "Speaker"
msgstr "Динамик"
#: spa/plugins/alsa/acp/alsa-mixer.c:2730
#: spa/plugins/alsa/acp/alsa-mixer.c:2808
#. Don't call it "headset", the HF one has the mic
#: spa/plugins/alsa/acp/alsa-mixer.c:2742 spa/plugins/alsa/acp/alsa-mixer.c:2820
#: spa/plugins/bluez5/bluez5-device.c:2434 spa/plugins/bluez5/bluez5-device.c:2501
msgid "Headphones"
msgstr "Құлаққаптар"
#: spa/plugins/alsa/acp/alsa-mixer.c:2797
#: spa/plugins/alsa/acp/alsa-mixer.c:2809
msgid "Analog Input"
msgstr "Аналогтық кіріс"
#: spa/plugins/alsa/acp/alsa-mixer.c:2801
#: spa/plugins/alsa/acp/alsa-mixer.c:2813
msgid "Dock Microphone"
msgstr "Док-станция микрофоны"
#: spa/plugins/alsa/acp/alsa-mixer.c:2803
#: spa/plugins/alsa/acp/alsa-mixer.c:2815
msgid "Headset Microphone"
msgstr "Гарнитура микрофоны"
#: spa/plugins/alsa/acp/alsa-mixer.c:2807
#: spa/plugins/alsa/acp/alsa-mixer.c:2819
msgid "Analog Output"
msgstr "Аналогтық шығыс"
#: spa/plugins/alsa/acp/alsa-mixer.c:2809
#, fuzzy
#: spa/plugins/alsa/acp/alsa-mixer.c:2821
msgid "Headphones 2"
msgstr "Құлаққаптар"
msgstr "Құлаққап 2"
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
#: spa/plugins/alsa/acp/alsa-mixer.c:2822
msgid "Headphones Mono Output"
msgstr "Құлаққаптардың моно шығысы"
#: spa/plugins/alsa/acp/alsa-mixer.c:2811
#: spa/plugins/alsa/acp/alsa-mixer.c:2823
msgid "Line Out"
msgstr "Сызықтық шығыс"
#: spa/plugins/alsa/acp/alsa-mixer.c:2812
#: spa/plugins/alsa/acp/alsa-mixer.c:2824
msgid "Analog Mono Output"
msgstr "Аналогтық моно шығысы"
#: spa/plugins/alsa/acp/alsa-mixer.c:2813
#: spa/plugins/alsa/acp/alsa-mixer.c:2825
msgid "Speakers"
msgstr "Динамиктер"
#: spa/plugins/alsa/acp/alsa-mixer.c:2814
#: spa/plugins/alsa/acp/alsa-mixer.c:2826
msgid "HDMI / DisplayPort"
msgstr "HDMI / DisplayPort"
#: spa/plugins/alsa/acp/alsa-mixer.c:2815
#: spa/plugins/alsa/acp/alsa-mixer.c:2827
msgid "Digital Output (S/PDIF)"
msgstr "Цифрлық шығыс (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2816
#: spa/plugins/alsa/acp/alsa-mixer.c:2828
msgid "Digital Input (S/PDIF)"
msgstr "Цифрлық кіріс (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2817
#: spa/plugins/alsa/acp/alsa-mixer.c:2829
msgid "Multichannel Input"
msgstr "Көпарналы кіріс"
#: spa/plugins/alsa/acp/alsa-mixer.c:2818
#: spa/plugins/alsa/acp/alsa-mixer.c:2830
msgid "Multichannel Output"
msgstr "Көпарналы шығыс"
#: spa/plugins/alsa/acp/alsa-mixer.c:2819
#: spa/plugins/alsa/acp/alsa-mixer.c:2831
msgid "Game Output"
msgstr "Ойын шығысы"
#: spa/plugins/alsa/acp/alsa-mixer.c:2820
#: spa/plugins/alsa/acp/alsa-mixer.c:2821
#: spa/plugins/alsa/acp/alsa-mixer.c:2832 spa/plugins/alsa/acp/alsa-mixer.c:2833
msgid "Chat Output"
msgstr "Чат шығысы"
#: spa/plugins/alsa/acp/alsa-mixer.c:2822
#, fuzzy
#: spa/plugins/alsa/acp/alsa-mixer.c:2834
msgid "Chat Input"
msgstr "Чат шығысы"
msgstr "Чат кірісі"
#: spa/plugins/alsa/acp/alsa-mixer.c:2823
#, fuzzy
#: spa/plugins/alsa/acp/alsa-mixer.c:2835
msgid "Virtual Surround 7.1"
msgstr "Виртуалды көлемді аудиоқабылдағыш"
msgstr "Виртуалды көлемді дыбыс 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4527
#: spa/plugins/alsa/acp/alsa-mixer.c:4522
msgid "Analog Mono"
msgstr "Аналогтық моно"
#: spa/plugins/alsa/acp/alsa-mixer.c:4528
#, fuzzy
#: spa/plugins/alsa/acp/alsa-mixer.c:4523
msgid "Analog Mono (Left)"
msgstr "Аналогтық моно"
msgstr "Аналогты моно (Сол жақ)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4529
#, fuzzy
#: spa/plugins/alsa/acp/alsa-mixer.c:4524
msgid "Analog Mono (Right)"
msgstr "Аналогтық моно"
msgstr "Аналогты моно (Оң жақ)"
#. Note: Not translated to "Analog Stereo Input", because the source
#. * name gets "Input" appended to it automatically, so adding "Input"
#. * here would lead to the source name to become "Analog Stereo Input
#. * Input". The same logic applies to analog-stereo-output,
#. * multichannel-input and multichannel-output.
#: spa/plugins/alsa/acp/alsa-mixer.c:4530
#: spa/plugins/alsa/acp/alsa-mixer.c:4538
#: spa/plugins/alsa/acp/alsa-mixer.c:4539
#: spa/plugins/alsa/acp/alsa-mixer.c:4525 spa/plugins/alsa/acp/alsa-mixer.c:4533
#: spa/plugins/alsa/acp/alsa-mixer.c:4534
msgid "Analog Stereo"
msgstr "Аналогтық стерео"
#: spa/plugins/alsa/acp/alsa-mixer.c:4531
#: spa/plugins/alsa/acp/alsa-mixer.c:4526
msgid "Mono"
msgstr "Моно"
#: spa/plugins/alsa/acp/alsa-mixer.c:4532
#: spa/plugins/alsa/acp/alsa-mixer.c:4527
msgid "Stereo"
msgstr "Стерео"
#: spa/plugins/alsa/acp/alsa-mixer.c:4540
#: spa/plugins/alsa/acp/alsa-mixer.c:4698
#: spa/plugins/bluez5/bluez5-device.c:1135
#: spa/plugins/alsa/acp/alsa-mixer.c:4535 spa/plugins/alsa/acp/alsa-mixer.c:4693
#: spa/plugins/bluez5/bluez5-device.c:2410
msgid "Headset"
msgstr "Гарнитура"
#: spa/plugins/alsa/acp/alsa-mixer.c:4541
#: spa/plugins/alsa/acp/alsa-mixer.c:4699
#, fuzzy
#: spa/plugins/alsa/acp/alsa-mixer.c:4536 spa/plugins/alsa/acp/alsa-mixer.c:4694
msgid "Speakerphone"
msgstr "Динамик"
#: spa/plugins/alsa/acp/alsa-mixer.c:4542
#: spa/plugins/alsa/acp/alsa-mixer.c:4543
#: spa/plugins/alsa/acp/alsa-mixer.c:4537 spa/plugins/alsa/acp/alsa-mixer.c:4538
msgid "Multichannel"
msgstr "Көпарналы"
#: spa/plugins/alsa/acp/alsa-mixer.c:4544
#: spa/plugins/alsa/acp/alsa-mixer.c:4539
msgid "Analog Surround 2.1"
msgstr "Аналогтық көлемді 2.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4545
#: spa/plugins/alsa/acp/alsa-mixer.c:4540
msgid "Analog Surround 3.0"
msgstr "Аналогтық көлемді 3.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4546
#: spa/plugins/alsa/acp/alsa-mixer.c:4541
msgid "Analog Surround 3.1"
msgstr "Аналогтық көлемді 3.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4547
#: spa/plugins/alsa/acp/alsa-mixer.c:4542
msgid "Analog Surround 4.0"
msgstr "Аналогтық көлемді 4.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4548
#: spa/plugins/alsa/acp/alsa-mixer.c:4543
msgid "Analog Surround 4.1"
msgstr "Аналогтық көлемді 4.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4549
#: spa/plugins/alsa/acp/alsa-mixer.c:4544
msgid "Analog Surround 5.0"
msgstr "Аналогтық көлемді 5.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4550
#: spa/plugins/alsa/acp/alsa-mixer.c:4545
msgid "Analog Surround 5.1"
msgstr "Аналогтық көлемді 5.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4551
#: spa/plugins/alsa/acp/alsa-mixer.c:4546
msgid "Analog Surround 6.0"
msgstr "Аналогтық көлемді 6.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4552
#: spa/plugins/alsa/acp/alsa-mixer.c:4547
msgid "Analog Surround 6.1"
msgstr "Аналогтық көлемді 6.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4553
#: spa/plugins/alsa/acp/alsa-mixer.c:4548
msgid "Analog Surround 7.0"
msgstr "Аналогтық көлемді 7.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4554
#: spa/plugins/alsa/acp/alsa-mixer.c:4549
msgid "Analog Surround 7.1"
msgstr "Аналогтық көлемді 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4555
#: spa/plugins/alsa/acp/alsa-mixer.c:4550
msgid "Digital Stereo (IEC958)"
msgstr "Цифрлық стерео (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4556
#: spa/plugins/alsa/acp/alsa-mixer.c:4551
msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr "Цифрлық көлемді 4.0 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4557
#: spa/plugins/alsa/acp/alsa-mixer.c:4552
msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr "Цифрлық көлемді 5.1 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4558
#: spa/plugins/alsa/acp/alsa-mixer.c:4553
msgid "Digital Surround 5.1 (IEC958/DTS)"
msgstr "Цифрлық көлемді 5.1 (IEC958/DTS)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4559
#: spa/plugins/alsa/acp/alsa-mixer.c:4554
msgid "Digital Stereo (HDMI)"
msgstr "Цифрлық стерео (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4560
#: spa/plugins/alsa/acp/alsa-mixer.c:4555
msgid "Digital Surround 5.1 (HDMI)"
msgstr "Цифрлық көлемді 5.1 (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4561
#: spa/plugins/alsa/acp/alsa-mixer.c:4556
msgid "Chat"
msgstr ""
msgstr "Чат"
#: spa/plugins/alsa/acp/alsa-mixer.c:4562
#: spa/plugins/alsa/acp/alsa-mixer.c:4557
msgid "Game"
msgstr ""
msgstr "Ойын"
#: spa/plugins/alsa/acp/alsa-mixer.c:4696
#: spa/plugins/alsa/acp/alsa-mixer.c:4691
msgid "Analog Mono Duplex"
msgstr "Аналогтық моно дуплекс"
#: spa/plugins/alsa/acp/alsa-mixer.c:4697
#: spa/plugins/alsa/acp/alsa-mixer.c:4692
msgid "Analog Stereo Duplex"
msgstr "Аналогтық стерео дуплекс"
#: spa/plugins/alsa/acp/alsa-mixer.c:4700
#: spa/plugins/alsa/acp/alsa-mixer.c:4695
msgid "Digital Stereo Duplex (IEC958)"
msgstr "Цифрлық стерео дуплекс (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4701
#: spa/plugins/alsa/acp/alsa-mixer.c:4696
msgid "Multichannel Duplex"
msgstr "Көпарналы дуплекс"
#: spa/plugins/alsa/acp/alsa-mixer.c:4702
#: spa/plugins/alsa/acp/alsa-mixer.c:4697
msgid "Stereo Duplex"
msgstr "Стерео дуплекс"
#: spa/plugins/alsa/acp/alsa-mixer.c:4703
#: spa/plugins/alsa/acp/alsa-mixer.c:4698
msgid "Mono Chat + 7.1 Surround"
msgstr ""
msgstr "Моно чат + 7.1 көлемді дыбыс"
#: spa/plugins/alsa/acp/alsa-mixer.c:4806
#: spa/plugins/alsa/acp/alsa-mixer.c:4799
#, c-format
msgid "%s Output"
msgstr "%s шығысы"
#: spa/plugins/alsa/acp/alsa-mixer.c:4813
#: spa/plugins/alsa/acp/alsa-mixer.c:4807
#, c-format
msgid "%s Input"
msgstr "%s кірісі"
#: spa/plugins/alsa/acp/alsa-util.c:1175 spa/plugins/alsa/acp/alsa-util.c:1269
#: spa/plugins/alsa/acp/alsa-util.c:1233 spa/plugins/alsa/acp/alsa-util.c:1327
#, c-format
msgid ""
"snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
"ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
"snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
msgid_plural ""
"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu "
"ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
"snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
msgstr[0] ""
"snd_pcm_avail() өте үлкен мән қайтарды: %lu байт (%lu мс).\n"
"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
msgstr[1] ""
"snd_pcm_avail() өте үлкен мән қайтарды: %lu байт (%lu мс).\n"
"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
#: spa/plugins/alsa/acp/alsa-util.c:1241
#: spa/plugins/alsa/acp/alsa-util.c:1299
#, c-format
msgid ""
"snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s"
"%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
"snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
msgid_plural ""
"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
"%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
msgstr[0] ""
"snd_pcm_delay() өте үлкен мән қайтарды: %li байт (%s%lu мс).\n"
"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
msgstr[1] ""
"snd_pcm_delay() өте үлкен мән қайтарды: %li байт (%s%lu мс).\n"
"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
#: spa/plugins/alsa/acp/alsa-util.c:1288
#: spa/plugins/alsa/acp/alsa-util.c:1346
#, c-format
msgid ""
"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
"%lu.\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail %lu.\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
msgstr ""
"snd_pcm_avail_delay() оғаш мәндер қайтарды: кідіріс %lu мәні қолжетімді %lu мәнінен аз.\n"
"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
#: spa/plugins/alsa/acp/alsa-util.c:1331
#: spa/plugins/alsa/acp/alsa-util.c:1389
#, c-format
msgid ""
"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
"(%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte (%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
msgid_plural ""
"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes "
"(%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."
msgstr[0] ""
"snd_pcm_mmap_begin() өте үлкен мән қайтарды: %lu байт (%lu мс).\n"
"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
msgstr[1] ""
"snd_pcm_mmap_begin() өте үлкен мән қайтарды: %lu байт (%lu мс).\n"
"Бұл ALSA драйверіндегі («%s») қате болуы әбден мүмкін. Бұл мәселе туралы ALSA әзірлеушілеріне хабарлаңыз."
#: spa/plugins/bluez5/bluez5-device.c:1010
#: spa/plugins/alsa/acp/channelmap.h:460
msgid "(invalid)"
msgstr "(жарамсыз)"
#: spa/plugins/alsa/acp/compat.c:194
msgid "Built-in Audio"
msgstr "Құрамындағы аудио"
#: spa/plugins/alsa/acp/compat.c:199
msgid "Modem"
msgstr "Модем"
#: spa/plugins/bluez5/bluez5-device.c:2032
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
msgstr ""
msgstr "Аудио шлюзі (A2DP бастапқы көзі және HSP/HFP AG)"
#: spa/plugins/bluez5/bluez5-device.c:1033
#: spa/plugins/bluez5/bluez5-device.c:2061
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
msgstr "Есту аппараттарына арналған аудио ағыны (ASHA қабылдағышы)"
#: spa/plugins/bluez5/bluez5-device.c:2104
#, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
msgstr ""
msgstr "Жоғары сапалы ойнату (A2DP қабылдағышы, кодек %s)"
#: spa/plugins/bluez5/bluez5-device.c:1035
#: spa/plugins/bluez5/bluez5-device.c:2107
#, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
msgstr ""
msgstr "Жоғары сапалы дуплекс (A2DP бастапқы көзі/қабылдағышы, кодек %s)"
#: spa/plugins/bluez5/bluez5-device.c:1041
#: spa/plugins/bluez5/bluez5-device.c:2115
msgid "High Fidelity Playback (A2DP Sink)"
msgstr ""
msgstr "Жоғары сапалы ойнату (A2DP қабылдағышы)"
#: spa/plugins/bluez5/bluez5-device.c:1043
#: spa/plugins/bluez5/bluez5-device.c:2117
msgid "High Fidelity Duplex (A2DP Source/Sink)"
msgstr ""
msgstr "Жоғары сапалы дуплекс (A2DP бастапқы көзі/қабылдағышы)"
#: spa/plugins/bluez5/bluez5-device.c:1070
#: spa/plugins/bluez5/bluez5-device.c:2194
#, c-format
msgid "High Fidelity Playback (BAP Sink, codec %s)"
msgstr "Жоғары сапалы ойнату (BAP қабылдағышы, кодек %s)"
#: spa/plugins/bluez5/bluez5-device.c:2199
#, c-format
msgid "High Fidelity Input (BAP Source, codec %s)"
msgstr "Жоғары сапалы кіріс (BAP бастапқы көзі, кодек %s)"
#: spa/plugins/bluez5/bluez5-device.c:2203
#, c-format
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
msgstr "Жоғары сапалы дуплекс (BAP бастапқы көзі/қабылдағышы, кодек %s)"
#: spa/plugins/bluez5/bluez5-device.c:2212
msgid "High Fidelity Playback (BAP Sink)"
msgstr "Жоғары сапалы ойнату (BAP қабылдағышы)"
#: spa/plugins/bluez5/bluez5-device.c:2216
msgid "High Fidelity Input (BAP Source)"
msgstr "Жоғары сапалы кіріс (BAP бастапқы көзі)"
#: spa/plugins/bluez5/bluez5-device.c:2219
msgid "High Fidelity Duplex (BAP Source/Sink)"
msgstr "Жоғары сапалы дуплекс (BAP бастапқы көзі/қабылдағышы)"
#: spa/plugins/bluez5/bluez5-device.c:2259
#, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)"
msgstr ""
msgstr "Гарнитура (HSP/HFP, кодек %s)"
#: spa/plugins/bluez5/bluez5-device.c:1074
msgid "Headset Head Unit (HSP/HFP)"
msgstr ""
#: spa/plugins/bluez5/bluez5-device.c:1140
#: spa/plugins/bluez5/bluez5-device.c:2411 spa/plugins/bluez5/bluez5-device.c:2416
#: spa/plugins/bluez5/bluez5-device.c:2423 spa/plugins/bluez5/bluez5-device.c:2429
#: spa/plugins/bluez5/bluez5-device.c:2435 spa/plugins/bluez5/bluez5-device.c:2441
#: spa/plugins/bluez5/bluez5-device.c:2447 spa/plugins/bluez5/bluez5-device.c:2453
#: spa/plugins/bluez5/bluez5-device.c:2459
msgid "Handsfree"
msgstr "Хендс-фри"
#: spa/plugins/bluez5/bluez5-device.c:1155
msgid "Headphone"
msgstr "Құлаққап"
#: spa/plugins/bluez5/bluez5-device.c:2417
msgid "Handsfree (HFP)"
msgstr "Гарнитура (HFP)"
#: spa/plugins/bluez5/bluez5-device.c:1160
#: spa/plugins/bluez5/bluez5-device.c:2440
msgid "Portable"
msgstr "Портативті динамик"
#: spa/plugins/bluez5/bluez5-device.c:1165
#: spa/plugins/bluez5/bluez5-device.c:2446
msgid "Car"
msgstr "Автомобильдік динамик"
#: spa/plugins/bluez5/bluez5-device.c:1170
#: spa/plugins/bluez5/bluez5-device.c:2452
msgid "HiFi"
msgstr "HiFi"
#: spa/plugins/bluez5/bluez5-device.c:1175
#: spa/plugins/bluez5/bluez5-device.c:2458
msgid "Phone"
msgstr "Телефон"
#: spa/plugins/bluez5/bluez5-device.c:1181
#: spa/plugins/bluez5/bluez5-device.c:2465
msgid "Bluetooth"
msgstr "Bluetooth"
#: spa/plugins/bluez5/bluez5-device.c:2466
msgid "Bluetooth Handsfree"
msgstr "Bluetooth гарнитурасы"
#~ msgid "Headphone"
#~ msgstr "Құлаққап"

382
po/sv.po
View file

@ -1,9 +1,9 @@
# Swedish translation for pipewire.
# Copyright © 2008-2025 Free Software Foundation, Inc.
# Copyright © 2008-2026 Free Software Foundation, Inc.
# This file is distributed under the same license as the pipewire package.
# Daniel Nylander <po@danielnylander.se>, 2008, 2012.
# Josef Andersson <josef.andersson@fripost.org>, 2014, 2017.
# Anders Jonsson <anders.jonsson@norsjovallen.se>, 2021, 2022, 2023, 2024, 2025.
# Anders Jonsson <anders.jonsson@norsjovallen.se>, 2021, 2022, 2023, 2024, 2025, 2026.
#
# Termer:
# input/output: ingång/utgång (det handlar om ljud)
@ -19,8 +19,8 @@ msgstr ""
"Project-Id-Version: pipewire\n"
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
"issues\n"
"POT-Creation-Date: 2025-04-16 15:31+0000\n"
"PO-Revision-Date: 2025-04-22 10:43+0200\n"
"POT-Creation-Date: 2026-02-09 12:55+0000\n"
"PO-Revision-Date: 2026-02-22 21:48+0100\n"
"Last-Translator: Anders Jonsson <anders.jonsson@norsjovallen.se>\n"
"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
"Language: sv\n"
@ -28,7 +28,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Poedit 3.5\n"
"X-Generator: Poedit 3.8\n"
#: src/daemon/pipewire.c:29
#, c-format
@ -47,11 +47,11 @@ msgstr ""
" -c, --config Läs in konfig (standard %s)\n"
" -P --properties Ställ in kontextegenskaper\n"
#: src/daemon/pipewire.desktop.in:4
#: src/daemon/pipewire.desktop.in:3
msgid "PipeWire Media System"
msgstr "PipeWire mediasystem"
#: src/daemon/pipewire.desktop.in:5
#: src/daemon/pipewire.desktop.in:4
msgid "Start the PipeWire Media System"
msgstr "Starta mediasystemet PipeWire"
@ -65,26 +65,51 @@ msgstr "Tunnel till %s%s%s"
msgid "Dummy Output"
msgstr "Attrapputgång"
#: src/modules/module-pulse-tunnel.c:760
#: src/modules/module-pulse-tunnel.c:761
#, c-format
msgid "Tunnel for %s@%s"
msgstr "Tunnel för %s@%s"
#: src/modules/module-zeroconf-discover.c:320
#: src/modules/module-zeroconf-discover.c:326
msgid "Unknown device"
msgstr "Okänd enhet"
#: src/modules/module-zeroconf-discover.c:332
#: src/modules/module-zeroconf-discover.c:338
#, c-format
msgid "%s on %s@%s"
msgstr "%s på %s@%s"
#: src/modules/module-zeroconf-discover.c:336
#: src/modules/module-zeroconf-discover.c:342
#, c-format
msgid "%s on %s"
msgstr "%s på %s"
#: src/tools/pw-cat.c:973
#: src/tools/pw-cat.c:264
#, c-format
msgid "Supported formats:\n"
msgstr "Format som stöds:\n"
#: src/tools/pw-cat.c:749
#, c-format
msgid "Supported channel layouts:\n"
msgstr "Kanallayouter som stöds:\n"
#: src/tools/pw-cat.c:759
#, c-format
msgid "Supported channel layout aliases:\n"
msgstr "Kanallayoutalias som stöds:\n"
#: src/tools/pw-cat.c:761
#, c-format
msgid " %s -> %s\n"
msgstr " %s -> %s\n"
#: src/tools/pw-cat.c:766
#, c-format
msgid "Supported channel names:\n"
msgstr "Kanalnamn som stöds:\n"
#: src/tools/pw-cat.c:1177
#, c-format
msgid ""
"%s [options] [<file>|-]\n"
@ -99,7 +124,7 @@ msgstr ""
" -v, --verbose Aktivera utförliga operationer\n"
"\n"
#: src/tools/pw-cat.c:980
#: src/tools/pw-cat.c:1184
#, c-format
msgid ""
" -R, --remote Remote daemon name\n"
@ -131,50 +156,64 @@ msgstr ""
" -P --properties Sätt nodegenskaper\n"
"\n"
#: src/tools/pw-cat.c:998
#: src/tools/pw-cat.c:1202
#, c-format
msgid ""
" --rate Sample rate (req. for rec) (default "
"%u)\n"
" --channels Number of channels (req. for rec) "
"(default %u)\n"
" --rate Sample rate (default %u)\n"
" --channels Number of channels (default %u)\n"
" --channel-map Channel map\n"
" one of: \"stereo\", "
"\"surround-51\",... or\n"
" a channel layout: \"Stereo\", "
"\"5.1\",... or\n"
" comma separated list of channel "
"names: eg. \"FL,FR\"\n"
" --format Sample format %s (req. for rec) "
"(default %s)\n"
" --list-layouts List supported channel layouts\n"
" --list-channel-names List supported channel maps\n"
" --format Sample format (default %s)\n"
" --list-formats List supported sample formats\n"
" --container Container format\n"
" --list-containers List supported containers and "
"extensions\n"
" --volume Stream volume 0-1.0 (default %.3f)\n"
" -q --quality Resampler quality (0 - 15) (default "
"%d)\n"
" -a, --raw RAW mode\n"
" -M, --force-midi Force midi format, one of \"midi\" "
"or \"ump\", (default ump)\n"
" -n, --sample-count COUNT Stop after COUNT samples\n"
"\n"
msgstr ""
" --rate Samplingsfrekvens (krävs för insp.) "
"(standard %u)\n"
" --channels Antal kanaler (krävs för insp.) "
"(standard %u)\n"
" --rate Samplingsfrekvens (standard %u)\n"
" --channels Antal kanaler (standard %u)\n"
" --channel-map Kanalmappning\n"
" en av: \"stereo\", "
"\"surround-51\",... eller\n"
" en kanallayout: \"Stereo\", "
"\"5.1\",... eller\n"
" kommaseparerad lista av "
"kanalnamn: t.ex. \"FL,FR\"\n"
" --format Samplingsformat %s (krävs för insp.) "
"(standard %s)\n"
" --list-layouts Lista kanallayouter som stöds\n"
" --list-channel-names Lista kanalmappningar som stöds\n"
" --format Samplingsformat (standard %s)\n"
" --list-formats Lista samplingsformat som stöds\n"
" --container Behållarformat\n"
" --list-containers Lista behållare och tillägg som "
"stöds\n"
" --volume Strömvolym 0-1.0 (standard %.3f)\n"
" -q --quality Omsamplarkvalitet (0 - 15) (standard "
"%d)\n"
" -a, --raw RAW-läge\n"
" -M, --force-midi Tvinga midi-format, en av \"midi\" "
"eller \"ump\", (standard ump)\n"
" -n, --sample-count ANTAL Stoppa efter ANTAL samplar\n"
"\n"
#: src/tools/pw-cat.c:1016
#: src/tools/pw-cat.c:1227
msgid ""
" -p, --playback Playback mode\n"
" -r, --record Recording mode\n"
" -m, --midi Midi mode\n"
" -d, --dsd DSD mode\n"
" -o, --encoded Encoded mode\n"
" -s, --sysex SysEx mode\n"
" -c, --midi-clip MIDI clip mode\n"
"\n"
msgstr ""
" -p, --playback Uppspelningsläge\n"
@ -182,9 +221,16 @@ msgstr ""
" -m, --midi Midiläge\n"
" -d, --dsd DSD-läge\n"
" -o, --encoded Kodat läge\n"
" -s, --sysex SysEx-läge\n"
" -c, --midi-clip MIDI-klippläge\n"
"\n"
#: src/tools/pw-cli.c:2306
#: src/tools/pw-cat.c:1827
#, c-format
msgid "Supported containers and extensions:\n"
msgstr "Behållare och tillägg som stöds:\n"
#: src/tools/pw-cli.c:2386
#, c-format
msgid ""
"%s [options] [command]\n"
@ -203,200 +249,203 @@ msgstr ""
" -m, --monitor Övervaka aktivitet\n"
"\n"
#: spa/plugins/alsa/acp/acp.c:350
#: spa/plugins/alsa/acp/acp.c:361
msgid "Pro Audio"
msgstr "Professionellt ljud"
#: spa/plugins/alsa/acp/acp.c:511 spa/plugins/alsa/acp/alsa-mixer.c:4635
#: spa/plugins/bluez5/bluez5-device.c:1802
#: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
#: spa/plugins/bluez5/bluez5-device.c:2021
msgid "Off"
msgstr "Av"
#: spa/plugins/alsa/acp/acp.c:593
#: spa/plugins/alsa/acp/acp.c:620
#, c-format
msgid "%s [ALSA UCM error]"
msgstr "%s [ALSA UCM-fel]"
#: spa/plugins/alsa/acp/alsa-mixer.c:2652
#: spa/plugins/alsa/acp/alsa-mixer.c:2721
msgid "Input"
msgstr "Ingång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2653
#: spa/plugins/alsa/acp/alsa-mixer.c:2722
msgid "Docking Station Input"
msgstr "Ingång för dockningsstation"
#: spa/plugins/alsa/acp/alsa-mixer.c:2654
#: spa/plugins/alsa/acp/alsa-mixer.c:2723
msgid "Docking Station Microphone"
msgstr "Mikrofon för dockningsstation"
#: spa/plugins/alsa/acp/alsa-mixer.c:2655
#: spa/plugins/alsa/acp/alsa-mixer.c:2724
msgid "Docking Station Line In"
msgstr "Linje in för dockningsstation"
#: spa/plugins/alsa/acp/alsa-mixer.c:2656
#: spa/plugins/alsa/acp/alsa-mixer.c:2747
#: spa/plugins/alsa/acp/alsa-mixer.c:2725
#: spa/plugins/alsa/acp/alsa-mixer.c:2816
msgid "Line In"
msgstr "Linje in"
#: spa/plugins/alsa/acp/alsa-mixer.c:2657
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
#: spa/plugins/bluez5/bluez5-device.c:2146
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
#: spa/plugins/bluez5/bluez5-device.c:2422
msgid "Microphone"
msgstr "Mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2658
#: spa/plugins/alsa/acp/alsa-mixer.c:2742
#: spa/plugins/alsa/acp/alsa-mixer.c:2727
#: spa/plugins/alsa/acp/alsa-mixer.c:2811
msgid "Front Microphone"
msgstr "Frontmikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2659
#: spa/plugins/alsa/acp/alsa-mixer.c:2743
#: spa/plugins/alsa/acp/alsa-mixer.c:2728
#: spa/plugins/alsa/acp/alsa-mixer.c:2812
msgid "Rear Microphone"
msgstr "Bakre mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2660
#: spa/plugins/alsa/acp/alsa-mixer.c:2729
msgid "External Microphone"
msgstr "Extern mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2661
#: spa/plugins/alsa/acp/alsa-mixer.c:2745
#: spa/plugins/alsa/acp/alsa-mixer.c:2730
#: spa/plugins/alsa/acp/alsa-mixer.c:2814
msgid "Internal Microphone"
msgstr "Intern mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2662
#: spa/plugins/alsa/acp/alsa-mixer.c:2748
#: spa/plugins/alsa/acp/alsa-mixer.c:2731
#: spa/plugins/alsa/acp/alsa-mixer.c:2817
msgid "Radio"
msgstr "Radio"
#: spa/plugins/alsa/acp/alsa-mixer.c:2663
#: spa/plugins/alsa/acp/alsa-mixer.c:2749
#: spa/plugins/alsa/acp/alsa-mixer.c:2732
#: spa/plugins/alsa/acp/alsa-mixer.c:2818
msgid "Video"
msgstr "Video"
#: spa/plugins/alsa/acp/alsa-mixer.c:2664
#: spa/plugins/alsa/acp/alsa-mixer.c:2733
msgid "Automatic Gain Control"
msgstr "Automatisk förstärkningskontroll"
#: spa/plugins/alsa/acp/alsa-mixer.c:2665
#: spa/plugins/alsa/acp/alsa-mixer.c:2734
msgid "No Automatic Gain Control"
msgstr "Ingen automatisk förstärkningskontroll"
#: spa/plugins/alsa/acp/alsa-mixer.c:2666
#: spa/plugins/alsa/acp/alsa-mixer.c:2735
msgid "Boost"
msgstr "Ökning"
#: spa/plugins/alsa/acp/alsa-mixer.c:2667
#: spa/plugins/alsa/acp/alsa-mixer.c:2736
msgid "No Boost"
msgstr "Ingen ökning"
#: spa/plugins/alsa/acp/alsa-mixer.c:2668
#: spa/plugins/alsa/acp/alsa-mixer.c:2737
msgid "Amplifier"
msgstr "Förstärkare"
#: spa/plugins/alsa/acp/alsa-mixer.c:2669
#: spa/plugins/alsa/acp/alsa-mixer.c:2738
msgid "No Amplifier"
msgstr "Ingen förstärkare"
#: spa/plugins/alsa/acp/alsa-mixer.c:2670
#: spa/plugins/alsa/acp/alsa-mixer.c:2739
msgid "Bass Boost"
msgstr "Basökning"
#: spa/plugins/alsa/acp/alsa-mixer.c:2671
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
msgid "No Bass Boost"
msgstr "Ingen basökning"
#: spa/plugins/alsa/acp/alsa-mixer.c:2672
#: spa/plugins/bluez5/bluez5-device.c:2152
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
#: spa/plugins/bluez5/bluez5-device.c:2428
msgid "Speaker"
msgstr "Högtalare"
#: spa/plugins/alsa/acp/alsa-mixer.c:2673
#: spa/plugins/alsa/acp/alsa-mixer.c:2751
#. Don't call it "headset", the HF one has the mic
#: spa/plugins/alsa/acp/alsa-mixer.c:2742
#: spa/plugins/alsa/acp/alsa-mixer.c:2820
#: spa/plugins/bluez5/bluez5-device.c:2434
#: spa/plugins/bluez5/bluez5-device.c:2501
msgid "Headphones"
msgstr "Hörlurar"
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
#: spa/plugins/alsa/acp/alsa-mixer.c:2809
msgid "Analog Input"
msgstr "Analog ingång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2744
#: spa/plugins/alsa/acp/alsa-mixer.c:2813
msgid "Dock Microphone"
msgstr "Dockmikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2746
#: spa/plugins/alsa/acp/alsa-mixer.c:2815
msgid "Headset Microphone"
msgstr "Headset-mikrofon"
#: spa/plugins/alsa/acp/alsa-mixer.c:2750
#: spa/plugins/alsa/acp/alsa-mixer.c:2819
msgid "Analog Output"
msgstr "Analog utgång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2752
#: spa/plugins/alsa/acp/alsa-mixer.c:2821
msgid "Headphones 2"
msgstr "Hörlurar 2"
#: spa/plugins/alsa/acp/alsa-mixer.c:2753
#: spa/plugins/alsa/acp/alsa-mixer.c:2822
msgid "Headphones Mono Output"
msgstr "Monoutgång för hörlurar"
#: spa/plugins/alsa/acp/alsa-mixer.c:2754
#: spa/plugins/alsa/acp/alsa-mixer.c:2823
msgid "Line Out"
msgstr "Linje ut"
#: spa/plugins/alsa/acp/alsa-mixer.c:2755
#: spa/plugins/alsa/acp/alsa-mixer.c:2824
msgid "Analog Mono Output"
msgstr "Analog monoutgång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2756
#: spa/plugins/alsa/acp/alsa-mixer.c:2825
msgid "Speakers"
msgstr "Högtalare"
#: spa/plugins/alsa/acp/alsa-mixer.c:2757
#: spa/plugins/alsa/acp/alsa-mixer.c:2826
msgid "HDMI / DisplayPort"
msgstr "HDMI / DisplayPort"
#: spa/plugins/alsa/acp/alsa-mixer.c:2758
#: spa/plugins/alsa/acp/alsa-mixer.c:2827
msgid "Digital Output (S/PDIF)"
msgstr "Digital utgång (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2759
#: spa/plugins/alsa/acp/alsa-mixer.c:2828
msgid "Digital Input (S/PDIF)"
msgstr "Digital ingång (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2760
#: spa/plugins/alsa/acp/alsa-mixer.c:2829
msgid "Multichannel Input"
msgstr "Multikanalingång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2761
#: spa/plugins/alsa/acp/alsa-mixer.c:2830
msgid "Multichannel Output"
msgstr "Multikanalutgång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2762
#: spa/plugins/alsa/acp/alsa-mixer.c:2831
msgid "Game Output"
msgstr "Spelutgång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2763
#: spa/plugins/alsa/acp/alsa-mixer.c:2764
#: spa/plugins/alsa/acp/alsa-mixer.c:2832
#: spa/plugins/alsa/acp/alsa-mixer.c:2833
msgid "Chat Output"
msgstr "Chatt-utgång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2765
#: spa/plugins/alsa/acp/alsa-mixer.c:2834
msgid "Chat Input"
msgstr "Chatt-ingång"
#: spa/plugins/alsa/acp/alsa-mixer.c:2766
#: spa/plugins/alsa/acp/alsa-mixer.c:2835
msgid "Virtual Surround 7.1"
msgstr "Virtual surround 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4458
#: spa/plugins/alsa/acp/alsa-mixer.c:4522
msgid "Analog Mono"
msgstr "Analog mono"
#: spa/plugins/alsa/acp/alsa-mixer.c:4459
#: spa/plugins/alsa/acp/alsa-mixer.c:4523
msgid "Analog Mono (Left)"
msgstr "Analog mono (vänster)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4460
#: spa/plugins/alsa/acp/alsa-mixer.c:4524
msgid "Analog Mono (Right)"
msgstr "Analog mono (höger)"
@ -405,142 +454,142 @@ msgstr "Analog mono (höger)"
#. * here would lead to the source name to become "Analog Stereo Input
#. * Input". The same logic applies to analog-stereo-output,
#. * multichannel-input and multichannel-output.
#: spa/plugins/alsa/acp/alsa-mixer.c:4461
#: spa/plugins/alsa/acp/alsa-mixer.c:4469
#: spa/plugins/alsa/acp/alsa-mixer.c:4470
#: spa/plugins/alsa/acp/alsa-mixer.c:4525
#: spa/plugins/alsa/acp/alsa-mixer.c:4533
#: spa/plugins/alsa/acp/alsa-mixer.c:4534
msgid "Analog Stereo"
msgstr "Analog stereo"
#: spa/plugins/alsa/acp/alsa-mixer.c:4462
#: spa/plugins/alsa/acp/alsa-mixer.c:4526
msgid "Mono"
msgstr "Mono"
#: spa/plugins/alsa/acp/alsa-mixer.c:4463
#: spa/plugins/alsa/acp/alsa-mixer.c:4527
msgid "Stereo"
msgstr "Stereo"
#: spa/plugins/alsa/acp/alsa-mixer.c:4471
#: spa/plugins/alsa/acp/alsa-mixer.c:4629
#: spa/plugins/bluez5/bluez5-device.c:2134
#: spa/plugins/alsa/acp/alsa-mixer.c:4535
#: spa/plugins/alsa/acp/alsa-mixer.c:4693
#: spa/plugins/bluez5/bluez5-device.c:2410
msgid "Headset"
msgstr "Headset"
#: spa/plugins/alsa/acp/alsa-mixer.c:4472
#: spa/plugins/alsa/acp/alsa-mixer.c:4630
#: spa/plugins/alsa/acp/alsa-mixer.c:4536
#: spa/plugins/alsa/acp/alsa-mixer.c:4694
msgid "Speakerphone"
msgstr "Högtalartelefon"
#: spa/plugins/alsa/acp/alsa-mixer.c:4473
#: spa/plugins/alsa/acp/alsa-mixer.c:4474
#: spa/plugins/alsa/acp/alsa-mixer.c:4537
#: spa/plugins/alsa/acp/alsa-mixer.c:4538
msgid "Multichannel"
msgstr "Multikanal"
#: spa/plugins/alsa/acp/alsa-mixer.c:4475
#: spa/plugins/alsa/acp/alsa-mixer.c:4539
msgid "Analog Surround 2.1"
msgstr "Analog surround 2.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4476
#: spa/plugins/alsa/acp/alsa-mixer.c:4540
msgid "Analog Surround 3.0"
msgstr "Analog surround 3.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4477
#: spa/plugins/alsa/acp/alsa-mixer.c:4541
msgid "Analog Surround 3.1"
msgstr "Analog surround 3.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4478
#: spa/plugins/alsa/acp/alsa-mixer.c:4542
msgid "Analog Surround 4.0"
msgstr "Analog surround 4.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4479
#: spa/plugins/alsa/acp/alsa-mixer.c:4543
msgid "Analog Surround 4.1"
msgstr "Analog surround 4.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4480
#: spa/plugins/alsa/acp/alsa-mixer.c:4544
msgid "Analog Surround 5.0"
msgstr "Analog surround 5.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4481
#: spa/plugins/alsa/acp/alsa-mixer.c:4545
msgid "Analog Surround 5.1"
msgstr "Analog surround 5.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4482
#: spa/plugins/alsa/acp/alsa-mixer.c:4546
msgid "Analog Surround 6.0"
msgstr "Analog surround 6.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4483
#: spa/plugins/alsa/acp/alsa-mixer.c:4547
msgid "Analog Surround 6.1"
msgstr "Analog surround 6.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4484
#: spa/plugins/alsa/acp/alsa-mixer.c:4548
msgid "Analog Surround 7.0"
msgstr "Analog surround 7.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4485
#: spa/plugins/alsa/acp/alsa-mixer.c:4549
msgid "Analog Surround 7.1"
msgstr "Analog surround 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4486
#: spa/plugins/alsa/acp/alsa-mixer.c:4550
msgid "Digital Stereo (IEC958)"
msgstr "Digital stereo (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4487
#: spa/plugins/alsa/acp/alsa-mixer.c:4551
msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr "Digital surround 4.0 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4488
#: spa/plugins/alsa/acp/alsa-mixer.c:4552
msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr "Digital surround 5.1 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4489
#: spa/plugins/alsa/acp/alsa-mixer.c:4553
msgid "Digital Surround 5.1 (IEC958/DTS)"
msgstr "Digital surround 5.1 (IEC958/DTS)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4490
#: spa/plugins/alsa/acp/alsa-mixer.c:4554
msgid "Digital Stereo (HDMI)"
msgstr "Digital stereo (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4491
#: spa/plugins/alsa/acp/alsa-mixer.c:4555
msgid "Digital Surround 5.1 (HDMI)"
msgstr "Digital surround 5.1 (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4492
#: spa/plugins/alsa/acp/alsa-mixer.c:4556
msgid "Chat"
msgstr "Chatt"
#: spa/plugins/alsa/acp/alsa-mixer.c:4493
#: spa/plugins/alsa/acp/alsa-mixer.c:4557
msgid "Game"
msgstr "Spel"
#: spa/plugins/alsa/acp/alsa-mixer.c:4627
#: spa/plugins/alsa/acp/alsa-mixer.c:4691
msgid "Analog Mono Duplex"
msgstr "Analog mono duplex"
#: spa/plugins/alsa/acp/alsa-mixer.c:4628
#: spa/plugins/alsa/acp/alsa-mixer.c:4692
msgid "Analog Stereo Duplex"
msgstr "Analog stereo duplex"
#: spa/plugins/alsa/acp/alsa-mixer.c:4631
#: spa/plugins/alsa/acp/alsa-mixer.c:4695
msgid "Digital Stereo Duplex (IEC958)"
msgstr "Digital stereo duplex (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4632
#: spa/plugins/alsa/acp/alsa-mixer.c:4696
msgid "Multichannel Duplex"
msgstr "Multikanalduplex"
#: spa/plugins/alsa/acp/alsa-mixer.c:4633
#: spa/plugins/alsa/acp/alsa-mixer.c:4697
msgid "Stereo Duplex"
msgstr "Stereo duplex"
#: spa/plugins/alsa/acp/alsa-mixer.c:4634
#: spa/plugins/alsa/acp/alsa-mixer.c:4698
msgid "Mono Chat + 7.1 Surround"
msgstr "Mono Chatt + 7.1 Surround"
#: spa/plugins/alsa/acp/alsa-mixer.c:4735
#: spa/plugins/alsa/acp/alsa-mixer.c:4799
#, c-format
msgid "%s Output"
msgstr "%s-utgång"
#: spa/plugins/alsa/acp/alsa-mixer.c:4743
#: spa/plugins/alsa/acp/alsa-mixer.c:4807
#, c-format
msgid "%s Input"
msgstr "%s-ingång"
@ -627,116 +676,115 @@ msgstr[1] ""
"Förmodligen är detta ett fel i ALSA-drivrutinen ”%s”. Vänligen rapportera "
"problemet till ALSA-utvecklarna."
#: spa/plugins/alsa/acp/channelmap.h:457
#: spa/plugins/alsa/acp/channelmap.h:460
msgid "(invalid)"
msgstr "(ogiltig)"
#: spa/plugins/alsa/acp/compat.c:193
#: spa/plugins/alsa/acp/compat.c:194
msgid "Built-in Audio"
msgstr "Inbyggt ljud"
#: spa/plugins/alsa/acp/compat.c:198
#: spa/plugins/alsa/acp/compat.c:199
msgid "Modem"
msgstr "Modem"
#: spa/plugins/bluez5/bluez5-device.c:1813
#: spa/plugins/bluez5/bluez5-device.c:2032
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
msgstr "Audio gateway (A2DP-källa & HSP/HFP AG)"
#: spa/plugins/bluez5/bluez5-device.c:1841
#: spa/plugins/bluez5/bluez5-device.c:2061
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
msgstr "Ljudströmning för hörhjälpmedel (ASHA-utgång)"
#: spa/plugins/bluez5/bluez5-device.c:1881
#: spa/plugins/bluez5/bluez5-device.c:2104
#, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
msgstr "High fidelity-uppspelning (A2DP-utgång, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:1884
#: spa/plugins/bluez5/bluez5-device.c:2107
#, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
msgstr "High fidelity duplex (A2DP-källa/utgång, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:1892
#: spa/plugins/bluez5/bluez5-device.c:2115
msgid "High Fidelity Playback (A2DP Sink)"
msgstr "High fidelity-uppspelning (A2DP-utgång)"
#: spa/plugins/bluez5/bluez5-device.c:1894
#: spa/plugins/bluez5/bluez5-device.c:2117
msgid "High Fidelity Duplex (A2DP Source/Sink)"
msgstr "High fidelity duplex (A2DP-källa/utgång)"
#: spa/plugins/bluez5/bluez5-device.c:1944
#: spa/plugins/bluez5/bluez5-device.c:2194
#, c-format
msgid "High Fidelity Playback (BAP Sink, codec %s)"
msgstr "High fidelity-uppspelning (BAP-utgång, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:1949
#: spa/plugins/bluez5/bluez5-device.c:2199
#, c-format
msgid "High Fidelity Input (BAP Source, codec %s)"
msgstr "High fidelity-ingång (BAP-källa, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:1953
#: spa/plugins/bluez5/bluez5-device.c:2203
#, c-format
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
msgstr "High fidelity duplex (BAP-källa/utgång, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:1962
#: spa/plugins/bluez5/bluez5-device.c:2212
msgid "High Fidelity Playback (BAP Sink)"
msgstr "High fidelity-uppspelning (BAP-utgång)"
#: spa/plugins/bluez5/bluez5-device.c:1966
#: spa/plugins/bluez5/bluez5-device.c:2216
msgid "High Fidelity Input (BAP Source)"
msgstr "High fidelity-ingång (BAP-källa)"
#: spa/plugins/bluez5/bluez5-device.c:1969
#: spa/plugins/bluez5/bluez5-device.c:2219
msgid "High Fidelity Duplex (BAP Source/Sink)"
msgstr "High fidelity duplex (BAP-källa/utgång)"
#: spa/plugins/bluez5/bluez5-device.c:2015
#: spa/plugins/bluez5/bluez5-device.c:2259
#, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)"
msgstr "Headset-huvudenhet (HSP/HFP, kodek %s)"
#: spa/plugins/bluez5/bluez5-device.c:2135
#: spa/plugins/bluez5/bluez5-device.c:2140
#: spa/plugins/bluez5/bluez5-device.c:2147
#: spa/plugins/bluez5/bluez5-device.c:2153
#: spa/plugins/bluez5/bluez5-device.c:2159
#: spa/plugins/bluez5/bluez5-device.c:2165
#: spa/plugins/bluez5/bluez5-device.c:2171
#: spa/plugins/bluez5/bluez5-device.c:2177
#: spa/plugins/bluez5/bluez5-device.c:2183
#: spa/plugins/bluez5/bluez5-device.c:2411
#: spa/plugins/bluez5/bluez5-device.c:2416
#: spa/plugins/bluez5/bluez5-device.c:2423
#: spa/plugins/bluez5/bluez5-device.c:2429
#: spa/plugins/bluez5/bluez5-device.c:2435
#: spa/plugins/bluez5/bluez5-device.c:2441
#: spa/plugins/bluez5/bluez5-device.c:2447
#: spa/plugins/bluez5/bluez5-device.c:2453
#: spa/plugins/bluez5/bluez5-device.c:2459
msgid "Handsfree"
msgstr "Handsfree"
#: spa/plugins/bluez5/bluez5-device.c:2141
#: spa/plugins/bluez5/bluez5-device.c:2417
msgid "Handsfree (HFP)"
msgstr "Handsfree (HFP)"
#: spa/plugins/bluez5/bluez5-device.c:2158
msgid "Headphone"
msgstr "Hörlurar"
#: spa/plugins/bluez5/bluez5-device.c:2164
#: spa/plugins/bluez5/bluez5-device.c:2440
msgid "Portable"
msgstr "Bärbar"
#: spa/plugins/bluez5/bluez5-device.c:2170
#: spa/plugins/bluez5/bluez5-device.c:2446
msgid "Car"
msgstr "Bil"
#: spa/plugins/bluez5/bluez5-device.c:2176
#: spa/plugins/bluez5/bluez5-device.c:2452
msgid "HiFi"
msgstr "HiFi"
#: spa/plugins/bluez5/bluez5-device.c:2182
#: spa/plugins/bluez5/bluez5-device.c:2458
msgid "Phone"
msgstr "Telefon"
#: spa/plugins/bluez5/bluez5-device.c:2189
#: spa/plugins/bluez5/bluez5-device.c:2465
msgid "Bluetooth"
msgstr "Bluetooth"
#: spa/plugins/bluez5/bluez5-device.c:2190
msgid "Bluetooth (HFP)"
msgstr "Bluetooth (HFP)"
#: spa/plugins/bluez5/bluez5-device.c:2466
msgid "Bluetooth Handsfree"
msgstr "Bluetooth-handsfree"
#~ msgid "Headphone"
#~ msgstr "Hörlurar"

View file

@ -13,8 +13,8 @@ msgstr ""
"Project-Id-Version: pipewire.master-tx\n"
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/"
"issues\n"
"POT-Creation-Date: 2026-02-11 16:53+0000\n"
"PO-Revision-Date: 2026-02-13 09:36+0800\n"
"POT-Creation-Date: 2026-04-08 13:01+0000\n"
"PO-Revision-Date: 2026-04-09 08:06+0800\n"
"Last-Translator: lumingzh <lumingzh@qq.com>\n"
"Language-Team: Chinese (China) <i18n-zh@googlegroups.com>\n"
"Language: zh_CN\n"
@ -22,7 +22,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2016-03-22 13:23+0000\n"
"X-Generator: Gtranslator 49.0\n"
"X-Generator: Gtranslator 50.0\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: src/daemon/pipewire.c:29
@ -65,46 +65,46 @@ msgstr "虚拟输出"
msgid "Tunnel for %s@%s"
msgstr "用于 %s@%s 的隧道"
#: src/modules/module-zeroconf-discover.c:326
#: src/modules/module-zeroconf-discover.c:290
msgid "Unknown device"
msgstr "未知设备"
#: src/modules/module-zeroconf-discover.c:338
#: src/modules/module-zeroconf-discover.c:302
#, c-format
msgid "%s on %s@%s"
msgstr "%2$s@%3$s 上的 %1$s"
#: src/modules/module-zeroconf-discover.c:342
#: src/modules/module-zeroconf-discover.c:306
#, c-format
msgid "%s on %s"
msgstr "%2$s 上的 %1$s"
#: src/tools/pw-cat.c:264
#: src/tools/pw-cat.c:269
#, c-format
msgid "Supported formats:\n"
msgstr "支持的格式:\n"
#: src/tools/pw-cat.c:749
#: src/tools/pw-cat.c:754
#, c-format
msgid "Supported channel layouts:\n"
msgstr "支持的声道布局:\n"
#: src/tools/pw-cat.c:759
#: src/tools/pw-cat.c:764
#, c-format
msgid "Supported channel layout aliases:\n"
msgstr "支持的声道布局别名:\n"
#: src/tools/pw-cat.c:761
#: src/tools/pw-cat.c:766
#, c-format
msgid " %s -> %s\n"
msgstr " %s -> %s\n"
#: src/tools/pw-cat.c:766
#: src/tools/pw-cat.c:771
#, c-format
msgid "Supported channel names:\n"
msgstr "支持的声道名称:\n"
#: src/tools/pw-cat.c:1177
#: src/tools/pw-cat.c:1183
#, c-format
msgid ""
"%s [options] [<file>|-]\n"
@ -119,7 +119,7 @@ msgstr ""
" -v, --verbose 输出详细操作\n"
"\n"
#: src/tools/pw-cat.c:1184
#: src/tools/pw-cat.c:1190
#, c-format
msgid ""
" -R, --remote Remote daemon name\n"
@ -129,6 +129,8 @@ msgid ""
" --target Set node target serial or name "
"(default %s)\n"
" 0 means don't link\n"
" -C --monitor Capture monitor ports (in recording "
"mode)\n"
" --latency Set node latency (default %s)\n"
" Xunit (unit = s, ms, us, ns)\n"
" or direct samples (256)\n"
@ -143,6 +145,7 @@ msgstr ""
" --media-role 设置媒体角色 (默认 %s)\n"
" --target 设置节点目标序列或名称 (默认 %s)\n"
" 设为 0 则不链接节点\n"
" -C --monitor 捕获监视器端口 (录制模式)\n"
" --latency 设置节点延迟 (默认 %s)\n"
" 时间 (单位可为 s, ms, us, ns)\n"
" 或样本数 (如256)\n"
@ -151,7 +154,7 @@ msgstr ""
" -P --properties 设置节点属性\n"
"\n"
#: src/tools/pw-cat.c:1202
#: src/tools/pw-cat.c:1209
#, c-format
msgid ""
" --rate Sample rate (default %u)\n"
@ -198,7 +201,7 @@ msgstr ""
" -n, --sample-count COUNT 计数采样后停止\n"
"\n"
#: src/tools/pw-cat.c:1227
#: src/tools/pw-cat.c:1234
msgid ""
" -p, --playback Playback mode\n"
" -r, --record Recording mode\n"
@ -218,7 +221,7 @@ msgstr ""
" -c, --midi-clip MIDI 剪辑模式\n"
"\n"
#: src/tools/pw-cat.c:1827
#: src/tools/pw-cat.c:1839
#, c-format
msgid "Supported containers and extensions:\n"
msgstr "支持的容器和扩展:\n"
@ -245,12 +248,12 @@ msgstr ""
msgid "Pro Audio"
msgstr "专业音频"
#: spa/plugins/alsa/acp/acp.c:537 spa/plugins/alsa/acp/alsa-mixer.c:4699
#: spa/plugins/bluez5/bluez5-device.c:2021
#: spa/plugins/alsa/acp/acp.c:535 spa/plugins/alsa/acp/alsa-mixer.c:4699
#: spa/plugins/bluez5/bluez5-device.c:2163
msgid "Off"
msgstr "关"
#: spa/plugins/alsa/acp/acp.c:620
#: spa/plugins/alsa/acp/acp.c:618
#, c-format
msgid "%s [ALSA UCM error]"
msgstr "%s [ALSA UCM 错误]"
@ -278,7 +281,7 @@ msgstr "输入插孔"
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
#: spa/plugins/bluez5/bluez5-device.c:2422
#: spa/plugins/bluez5/bluez5-device.c:2596
msgid "Microphone"
msgstr "话筒"
@ -344,15 +347,15 @@ msgid "No Bass Boost"
msgstr "无重低音增强"
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
#: spa/plugins/bluez5/bluez5-device.c:2428
#: spa/plugins/bluez5/bluez5-device.c:2602
msgid "Speaker"
msgstr "扬声器"
#. Don't call it "headset", the HF one has the mic
#: spa/plugins/alsa/acp/alsa-mixer.c:2742
#: spa/plugins/alsa/acp/alsa-mixer.c:2820
#: spa/plugins/bluez5/bluez5-device.c:2434
#: spa/plugins/bluez5/bluez5-device.c:2501
#: spa/plugins/bluez5/bluez5-device.c:2608
#: spa/plugins/bluez5/bluez5-device.c:2675
msgid "Headphones"
msgstr "模拟耳机"
@ -462,7 +465,7 @@ msgstr "立体声"
#: spa/plugins/alsa/acp/alsa-mixer.c:4535
#: spa/plugins/alsa/acp/alsa-mixer.c:4693
#: spa/plugins/bluez5/bluez5-device.c:2410
#: spa/plugins/bluez5/bluez5-device.c:2584
msgid "Headset"
msgstr "耳机"
@ -657,101 +660,109 @@ msgstr "内置音频"
msgid "Modem"
msgstr "调制解调器"
#: spa/plugins/bluez5/bluez5-device.c:2032
#: spa/plugins/bluez5/bluez5-device.c:2174
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
msgstr "音频网关 (A2DP 信源 或 HSP/HFP 网关)"
#: spa/plugins/bluez5/bluez5-device.c:2061
#: spa/plugins/bluez5/bluez5-device.c:2203
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
msgstr "助听器音频流 (ASHA 信宿)"
#: spa/plugins/bluez5/bluez5-device.c:2104
#: spa/plugins/bluez5/bluez5-device.c:2246
#, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
msgstr "高保真回放 (A2DP 信宿, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2107
#: spa/plugins/bluez5/bluez5-device.c:2249
#, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
msgstr "高保真双工 (A2DP 信源/信宿, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2115
#: spa/plugins/bluez5/bluez5-device.c:2257
msgid "High Fidelity Playback (A2DP Sink)"
msgstr "高保真回放 (A2DP 信宿)"
#: spa/plugins/bluez5/bluez5-device.c:2117
#: spa/plugins/bluez5/bluez5-device.c:2259
msgid "High Fidelity Duplex (A2DP Source/Sink)"
msgstr "高保真双工 (A2DP 信源/信宿)"
#: spa/plugins/bluez5/bluez5-device.c:2194
#: spa/plugins/bluez5/bluez5-device.c:2281
msgid "Auto: Prefer Quality (A2DP)"
msgstr "自动:质量优先 (A2DP)"
#: spa/plugins/bluez5/bluez5-device.c:2286
msgid "Auto: Prefer Latency (A2DP)"
msgstr "自动:延迟优先 (A2DP)"
#: spa/plugins/bluez5/bluez5-device.c:2366
#, c-format
msgid "High Fidelity Playback (BAP Sink, codec %s)"
msgstr "高保真回放 (BAP 信宿, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2199
#: spa/plugins/bluez5/bluez5-device.c:2371
#, c-format
msgid "High Fidelity Input (BAP Source, codec %s)"
msgstr "高保真输入 (BAP 信源, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2203
#: spa/plugins/bluez5/bluez5-device.c:2375
#, c-format
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
msgstr "高保真双工 (BAP 信源/信宿, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2212
#: spa/plugins/bluez5/bluez5-device.c:2384
msgid "High Fidelity Playback (BAP Sink)"
msgstr "高保真回放 (BAP 信宿)"
#: spa/plugins/bluez5/bluez5-device.c:2216
#: spa/plugins/bluez5/bluez5-device.c:2388
msgid "High Fidelity Input (BAP Source)"
msgstr "高保真输入 (BAP 信源)"
#: spa/plugins/bluez5/bluez5-device.c:2219
#: spa/plugins/bluez5/bluez5-device.c:2391
msgid "High Fidelity Duplex (BAP Source/Sink)"
msgstr "高保真双工 (BAP 信源/信宿)"
#: spa/plugins/bluez5/bluez5-device.c:2259
#: spa/plugins/bluez5/bluez5-device.c:2431
#, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)"
msgstr "头戴式耳机单元 (HSP/HFP, 编码 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2411
#: spa/plugins/bluez5/bluez5-device.c:2416
#: spa/plugins/bluez5/bluez5-device.c:2423
#: spa/plugins/bluez5/bluez5-device.c:2429
#: spa/plugins/bluez5/bluez5-device.c:2435
#: spa/plugins/bluez5/bluez5-device.c:2441
#: spa/plugins/bluez5/bluez5-device.c:2447
#: spa/plugins/bluez5/bluez5-device.c:2453
#: spa/plugins/bluez5/bluez5-device.c:2459
#: spa/plugins/bluez5/bluez5-device.c:2585
#: spa/plugins/bluez5/bluez5-device.c:2590
#: spa/plugins/bluez5/bluez5-device.c:2597
#: spa/plugins/bluez5/bluez5-device.c:2603
#: spa/plugins/bluez5/bluez5-device.c:2609
#: spa/plugins/bluez5/bluez5-device.c:2615
#: spa/plugins/bluez5/bluez5-device.c:2621
#: spa/plugins/bluez5/bluez5-device.c:2627
#: spa/plugins/bluez5/bluez5-device.c:2633
msgid "Handsfree"
msgstr "免提"
#: spa/plugins/bluez5/bluez5-device.c:2417
#: spa/plugins/bluez5/bluez5-device.c:2591
msgid "Handsfree (HFP)"
msgstr "免提HFP"
#: spa/plugins/bluez5/bluez5-device.c:2440
#: spa/plugins/bluez5/bluez5-device.c:2614
msgid "Portable"
msgstr "便携式"
#: spa/plugins/bluez5/bluez5-device.c:2446
#: spa/plugins/bluez5/bluez5-device.c:2620
msgid "Car"
msgstr "车内"
#: spa/plugins/bluez5/bluez5-device.c:2452
#: spa/plugins/bluez5/bluez5-device.c:2626
msgid "HiFi"
msgstr "高保真"
#: spa/plugins/bluez5/bluez5-device.c:2458
#: spa/plugins/bluez5/bluez5-device.c:2632
msgid "Phone"
msgstr "电话"
#: spa/plugins/bluez5/bluez5-device.c:2465
#: spa/plugins/bluez5/bluez5-device.c:2639
msgid "Bluetooth"
msgstr "蓝牙"
#: spa/plugins/bluez5/bluez5-device.c:2466
#: spa/plugins/bluez5/bluez5-device.c:2640
msgid "Bluetooth Handsfree"
msgstr "蓝牙免提"

View file

@ -4,110 +4,221 @@
#
# Cheng-Chia Tseng <pswo10680@gmail.com>, 2010, 2012.
# pan93412 <pan93412@gmail.com>, 2020.
# SPDX-FileCopyrightText: 2026 Kisaragi Hiu <mail@kisaragi-hiu.com>
msgid ""
msgstr ""
"Project-Id-Version: PipeWire Volume Control\n"
"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/"
"issues/new\n"
"POT-Creation-Date: 2021-04-18 16:54+0800\n"
"PO-Revision-Date: 2020-01-11 13:49+0800\n"
"Last-Translator: pan93412 <pan93412@gmail.com>\n"
"Language-Team: Chinese <zh-l10n@lists.linux.org.tw>\n"
"POT-Creation-Date: 2026-03-11 22:03+0900\n"
"PO-Revision-Date: 2026-03-11 21:24+0900\n"
"Last-Translator: Kisaragi Hiu <mail@kisaragi-hiu.com>\n"
"Language-Team: Chinese (Taiwan) <zh-l10n@lists.slat.org>\n"
"Language: zh_TW\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Lokalize 19.12.0\n"
"X-Generator: Lokalize 26.03.70\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: src/daemon/pipewire.c:43
#: src/daemon/pipewire.c:29
#, c-format
msgid ""
"%s [options]\n"
" -h, --help Show this help\n"
" -v, --verbose Increase verbosity by one level\n"
" --version Show version\n"
" -c, --config Load config (Default %s)\n"
" -P --properties Set context properties\n"
msgstr ""
"%s [選項]\n"
" -h, --help 顯示此說明\n"
" -v, --verbose 提高訊息詳細程度一階\n"
" --version 顯示版本\n"
" -c, --config 載入設定檔案(預設 %s\n"
" -P --properties 設定前後文屬性\n"
#: src/daemon/pipewire.desktop.in:4
msgid "PipeWire Media System"
msgstr ""
msgstr "PipeWire 媒體系統"
#: src/daemon/pipewire.desktop.in:5
msgid "Start the PipeWire Media System"
msgstr ""
msgstr "啟動 PipeWire 媒體系統"
#: src/examples/media-session/alsa-monitor.c:526
#: spa/plugins/alsa/acp/compat.c:187
msgid "Built-in Audio"
msgstr "內部音效"
#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:159
#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:159
#, c-format
msgid "Tunnel to %s%s%s"
msgstr "穿隧到 %s%s%s"
#: src/examples/media-session/alsa-monitor.c:530
#: spa/plugins/alsa/acp/compat.c:192
msgid "Modem"
msgstr "數據機"
#: src/modules/module-fallback-sink.c:40
msgid "Dummy Output"
msgstr "虛擬輸出"
#: src/examples/media-session/alsa-monitor.c:539
#: src/modules/module-pulse-tunnel.c:761
#, c-format
msgid "Tunnel for %s@%s"
msgstr "%s@%s 的穿隧道"
#: src/modules/module-zeroconf-discover.c:290
msgid "Unknown device"
msgstr ""
msgstr "未知裝置"
#: src/tools/pw-cat.c:991
#: src/modules/module-zeroconf-discover.c:302
#, c-format
msgid "%s on %s@%s"
msgstr "%s 於 %s@%s"
#: src/modules/module-zeroconf-discover.c:306
#, c-format
msgid "%s on %s"
msgstr "%s 於 %s"
#: src/tools/pw-cat.c:269
#, c-format
msgid "Supported formats:\n"
msgstr "支援的格式:\n"
#: src/tools/pw-cat.c:754
#, c-format
msgid "Supported channel layouts:\n"
msgstr "支援的頻道配置:\n"
#: src/tools/pw-cat.c:764
#, c-format
msgid "Supported channel layout aliases:\n"
msgstr "支援的頻道配置別名:\n"
#: src/tools/pw-cat.c:766
#, c-format
msgid " %s -> %s\n"
msgstr " %s -> %s\n"
#: src/tools/pw-cat.c:771
#, c-format
msgid "Supported channel names:\n"
msgstr "支援的頻道名稱:\n"
#: src/tools/pw-cat.c:1182
#, c-format
msgid ""
"%s [options] <file>\n"
"%s [options] [<file>|-]\n"
" -h, --help Show this help\n"
" --version Show version\n"
" -v, --verbose Enable verbose operations\n"
"\n"
msgstr ""
"%s [選項] [<檔案>|-]\n"
" -h, --help 顯示此說明\n"
" --version 顯示版本\n"
" -v, --verbose 操作進行時顯示詳細訊息\n"
"\n"
#: src/tools/pw-cat.c:998
#: src/tools/pw-cat.c:1189
#, c-format
msgid ""
" -R, --remote Remote daemon name\n"
" --media-type Set media type (default %s)\n"
" --media-category Set media category (default %s)\n"
" --media-role Set media role (default %s)\n"
" --target Set node target (default %s)\n"
" --target Set node target serial or name "
"(default %s)\n"
" 0 means don't link\n"
" --latency Set node latency (default %s)\n"
" Xunit (unit = s, ms, us, ns)\n"
" or direct samples (256)\n"
" the rate is the one of the source "
"file\n"
" --list-targets List available targets for --target\n"
" -P --properties Set node properties\n"
"\n"
msgstr ""
" -R, --remote 遠端守護程式名稱\n"
" --media-type 設定媒體型態(預設 %s\n"
" --media-category 設定媒體分類(預設 %s\n"
" --media-role 設定媒體角色(預設 %s\n"
" --target 設定節點目標序號或名稱(預設 %s\n"
" 0 代表不要連結\n"
" --latency 設定節點延遲(預設 %s\n"
" X單位單位為 s, ms, us 或 ns\n"
" 或直接指定樣本數 (256)\n"
" 取樣率取自來源檔案\n"
" -P --properties 設定節點屬性\n"
"\n"
#: src/tools/pw-cat.c:1016
#: src/tools/pw-cat.c:1207
#, c-format
msgid ""
" --rate Sample rate (req. for rec) (default "
"%u)\n"
" --channels Number of channels (req. for rec) "
"(default %u)\n"
" --rate Sample rate (default %u)\n"
" --channels Number of channels (default %u)\n"
" --channel-map Channel map\n"
" one of: \"stereo\", "
"\"surround-51\",... or\n"
" a channel layout: \"Stereo\", "
"\"5.1\",... or\n"
" comma separated list of channel "
"names: eg. \"FL,FR\"\n"
" --format Sample format %s (req. for rec) "
"(default %s)\n"
" --list-layouts List supported channel layouts\n"
" --list-channel-names List supported channel maps\n"
" --format Sample format (default %s)\n"
" --list-formats List supported sample formats\n"
" --container Container format\n"
" --list-containers List supported containers and "
"extensions\n"
" --volume Stream volume 0-1.0 (default %.3f)\n"
" -q --quality Resampler quality (0 - 15) (default "
"%d)\n"
" -a, --raw RAW mode\n"
" -M, --force-midi Force midi format, one of \"midi\" "
"or \"ump\", (default ump)\n"
" -n, --sample-count COUNT Stop after COUNT samples\n"
"\n"
msgstr ""
" --rate 取樣率(預設 %u\n"
" --channels 頻道數量(預設 %u\n"
" --channel-map 頻道映射\n"
" 可以是頻道配置名稱,像是 "
"\"Stereo\"、\"5.1\" 等等,或是\n"
" 以逗號分隔的頻道名稱清單,像是 "
"\"FL,FR\"\n"
" --list-layouts 列出支援的頻道配置\n"
" --list-channel-names 列出支援的頻道映射\n"
" --format 取樣格式(預設 %s\n"
" --list-formats 列出支援的取樣格式\n"
" --container 容器格式\n"
" --list-containers 列出支援的容器與副檔名\n"
" --volume 串流音量 0-1.0(預設 %.3f\n"
" -q --quality 重新取樣器品質 0-15預設 %d\n"
" -a, --raw 原始模式\n"
" -M, --force-midi 強制使用 midi 格式,可選擇 \"midi\" "
"或 \"ump\"(預設 ump\n"
" -n, --sample-count 樣本數 在「樣本數」個樣本之後停止\n"
"\n"
#: src/tools/pw-cat.c:1033
#: src/tools/pw-cat.c:1232
msgid ""
" -p, --playback Playback mode\n"
" -r, --record Recording mode\n"
" -m, --midi Midi mode\n"
" -d, --dsd DSD mode\n"
" -o, --encoded Encoded mode\n"
" -s, --sysex SysEx mode\n"
" -c, --midi-clip MIDI clip mode\n"
"\n"
msgstr ""
" -p, --playback 播放模式\n"
" -r, --record 錄製模式\n"
" -m, --midi Midi 模式\n"
" -d, --dsd DSD 模式\n"
" -o, --encoded 已編碼模式\n"
" -s, --sysex SysEx 模式\n"
" -c, --midi-clip MIDI 素材模式\n"
"\n"
#: src/tools/pw-cli.c:2932
#: src/tools/pw-cat.c:1837
#, c-format
msgid "Supported containers and extensions:\n"
msgstr "支援的容器與副檔名:\n"
#: src/tools/pw-cli.c:2386
#, c-format
msgid ""
"%s [options] [command]\n"
@ -115,357 +226,363 @@ msgid ""
" --version Show version\n"
" -d, --daemon Start as daemon (Default false)\n"
" -r, --remote Remote daemon name\n"
" -m, --monitor Monitor activity\n"
"\n"
msgstr ""
"%s [選項] [指令]\n"
" -h, --help 顯示此說明\n"
" --version 顯示版本\n"
" -d, --daemon 作為守護程式啟動(預設為否)\n"
" -r, --remote 遠端守護程式名稱\n"
" -m, --monitor 監控活動\n"
"\n"
#: spa/plugins/alsa/acp/acp.c:290
#: spa/plugins/alsa/acp/acp.c:361
msgid "Pro Audio"
msgstr ""
msgstr "Pro Audio"
#: spa/plugins/alsa/acp/acp.c:411 spa/plugins/alsa/acp/alsa-mixer.c:4704
#: spa/plugins/bluez5/bluez5-device.c:1000
#: spa/plugins/alsa/acp/acp.c:535 spa/plugins/alsa/acp/alsa-mixer.c:4699
#: spa/plugins/bluez5/bluez5-device.c:2021
msgid "Off"
msgstr "關閉"
#: spa/plugins/alsa/acp/channelmap.h:466
msgid "(invalid)"
msgstr "(無效)"
#: spa/plugins/alsa/acp/acp.c:618
#, c-format
msgid "%s [ALSA UCM error]"
msgstr "%s [ALSA UCM 錯誤]"
#: spa/plugins/alsa/acp/alsa-mixer.c:2709
#: spa/plugins/alsa/acp/alsa-mixer.c:2721
msgid "Input"
msgstr "輸入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2710
#: spa/plugins/alsa/acp/alsa-mixer.c:2722
msgid "Docking Station Input"
msgstr "Docking Station 輸入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2711
#: spa/plugins/alsa/acp/alsa-mixer.c:2723
msgid "Docking Station Microphone"
msgstr "Docking Station 麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2712
#: spa/plugins/alsa/acp/alsa-mixer.c:2724
msgid "Docking Station Line In"
msgstr "Docking Station 線路輸入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2713
#: spa/plugins/alsa/acp/alsa-mixer.c:2804
#: spa/plugins/alsa/acp/alsa-mixer.c:2725
#: spa/plugins/alsa/acp/alsa-mixer.c:2816
msgid "Line In"
msgstr "線路輸入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2714
#: spa/plugins/alsa/acp/alsa-mixer.c:2798
#: spa/plugins/bluez5/bluez5-device.c:1145
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
#: spa/plugins/bluez5/bluez5-device.c:2422
msgid "Microphone"
msgstr "麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2715
#: spa/plugins/alsa/acp/alsa-mixer.c:2799
#: spa/plugins/alsa/acp/alsa-mixer.c:2727
#: spa/plugins/alsa/acp/alsa-mixer.c:2811
msgid "Front Microphone"
msgstr "前方麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2716
#: spa/plugins/alsa/acp/alsa-mixer.c:2800
#: spa/plugins/alsa/acp/alsa-mixer.c:2728
#: spa/plugins/alsa/acp/alsa-mixer.c:2812
msgid "Rear Microphone"
msgstr "後方麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2717
#: spa/plugins/alsa/acp/alsa-mixer.c:2729
msgid "External Microphone"
msgstr "外接麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2718
#: spa/plugins/alsa/acp/alsa-mixer.c:2802
#: spa/plugins/alsa/acp/alsa-mixer.c:2730
#: spa/plugins/alsa/acp/alsa-mixer.c:2814
msgid "Internal Microphone"
msgstr "內建麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2719
#: spa/plugins/alsa/acp/alsa-mixer.c:2805
#: spa/plugins/alsa/acp/alsa-mixer.c:2731
#: spa/plugins/alsa/acp/alsa-mixer.c:2817
msgid "Radio"
msgstr "無線電"
#: spa/plugins/alsa/acp/alsa-mixer.c:2720
#: spa/plugins/alsa/acp/alsa-mixer.c:2806
#: spa/plugins/alsa/acp/alsa-mixer.c:2732
#: spa/plugins/alsa/acp/alsa-mixer.c:2818
msgid "Video"
msgstr "視訊"
#: spa/plugins/alsa/acp/alsa-mixer.c:2721
#: spa/plugins/alsa/acp/alsa-mixer.c:2733
msgid "Automatic Gain Control"
msgstr "自動增益控制"
#: spa/plugins/alsa/acp/alsa-mixer.c:2722
#: spa/plugins/alsa/acp/alsa-mixer.c:2734
msgid "No Automatic Gain Control"
msgstr "無自動增益控制"
#: spa/plugins/alsa/acp/alsa-mixer.c:2723
#: spa/plugins/alsa/acp/alsa-mixer.c:2735
msgid "Boost"
msgstr "增強"
#: spa/plugins/alsa/acp/alsa-mixer.c:2724
#: spa/plugins/alsa/acp/alsa-mixer.c:2736
msgid "No Boost"
msgstr "無增強"
#: spa/plugins/alsa/acp/alsa-mixer.c:2725
#: spa/plugins/alsa/acp/alsa-mixer.c:2737
msgid "Amplifier"
msgstr "擴大器"
#: spa/plugins/alsa/acp/alsa-mixer.c:2726
#: spa/plugins/alsa/acp/alsa-mixer.c:2738
msgid "No Amplifier"
msgstr "無擴大器"
#: spa/plugins/alsa/acp/alsa-mixer.c:2727
#: spa/plugins/alsa/acp/alsa-mixer.c:2739
msgid "Bass Boost"
msgstr "低音增強"
#: spa/plugins/alsa/acp/alsa-mixer.c:2728
#: spa/plugins/alsa/acp/alsa-mixer.c:2740
msgid "No Bass Boost"
msgstr "無低音增強"
#: spa/plugins/alsa/acp/alsa-mixer.c:2729
#: spa/plugins/bluez5/bluez5-device.c:1150
#: spa/plugins/alsa/acp/alsa-mixer.c:2741
#: spa/plugins/bluez5/bluez5-device.c:2428
msgid "Speaker"
msgstr "喇叭"
#: spa/plugins/alsa/acp/alsa-mixer.c:2730
#: spa/plugins/alsa/acp/alsa-mixer.c:2808
#. Don't call it "headset", the HF one has the mic
#: spa/plugins/alsa/acp/alsa-mixer.c:2742
#: spa/plugins/alsa/acp/alsa-mixer.c:2820
#: spa/plugins/bluez5/bluez5-device.c:2434
#: spa/plugins/bluez5/bluez5-device.c:2501
msgid "Headphones"
msgstr "頭戴式耳機"
#: spa/plugins/alsa/acp/alsa-mixer.c:2797
#: spa/plugins/alsa/acp/alsa-mixer.c:2809
msgid "Analog Input"
msgstr "類比輸入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2801
#: spa/plugins/alsa/acp/alsa-mixer.c:2813
msgid "Dock Microphone"
msgstr "臺座麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2803
#: spa/plugins/alsa/acp/alsa-mixer.c:2815
msgid "Headset Microphone"
msgstr "耳麥麥克風"
#: spa/plugins/alsa/acp/alsa-mixer.c:2807
#: spa/plugins/alsa/acp/alsa-mixer.c:2819
msgid "Analog Output"
msgstr "類比輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2809
#, fuzzy
#: spa/plugins/alsa/acp/alsa-mixer.c:2821
msgid "Headphones 2"
msgstr "頭戴式耳機"
msgstr "頭戴式耳機 2"
#: spa/plugins/alsa/acp/alsa-mixer.c:2810
#: spa/plugins/alsa/acp/alsa-mixer.c:2822
msgid "Headphones Mono Output"
msgstr "頭戴式耳機單聲道輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2811
#: spa/plugins/alsa/acp/alsa-mixer.c:2823
msgid "Line Out"
msgstr "線路輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2812
#: spa/plugins/alsa/acp/alsa-mixer.c:2824
msgid "Analog Mono Output"
msgstr "類比單聲道輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2813
#: spa/plugins/alsa/acp/alsa-mixer.c:2825
msgid "Speakers"
msgstr "喇叭"
#: spa/plugins/alsa/acp/alsa-mixer.c:2814
#: spa/plugins/alsa/acp/alsa-mixer.c:2826
msgid "HDMI / DisplayPort"
msgstr "HDMI / DisplayPort"
#: spa/plugins/alsa/acp/alsa-mixer.c:2815
#: spa/plugins/alsa/acp/alsa-mixer.c:2827
msgid "Digital Output (S/PDIF)"
msgstr "數位輸出 (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2816
#: spa/plugins/alsa/acp/alsa-mixer.c:2828
msgid "Digital Input (S/PDIF)"
msgstr "數位輸入 (S/PDIF)"
#: spa/plugins/alsa/acp/alsa-mixer.c:2817
#: spa/plugins/alsa/acp/alsa-mixer.c:2829
msgid "Multichannel Input"
msgstr "多聲道輸入"
#: spa/plugins/alsa/acp/alsa-mixer.c:2818
#: spa/plugins/alsa/acp/alsa-mixer.c:2830
msgid "Multichannel Output"
msgstr "多聲道輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2819
#: spa/plugins/alsa/acp/alsa-mixer.c:2831
msgid "Game Output"
msgstr "遊戲輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2820
#: spa/plugins/alsa/acp/alsa-mixer.c:2821
#: spa/plugins/alsa/acp/alsa-mixer.c:2832
#: spa/plugins/alsa/acp/alsa-mixer.c:2833
msgid "Chat Output"
msgstr "聊天輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:2822
#, fuzzy
#: spa/plugins/alsa/acp/alsa-mixer.c:2834
msgid "Chat Input"
msgstr "聊天輸"
msgstr "聊天輸"
#: spa/plugins/alsa/acp/alsa-mixer.c:2823
#, fuzzy
#: spa/plugins/alsa/acp/alsa-mixer.c:2835
msgid "Virtual Surround 7.1"
msgstr "虛擬環繞聲 sink"
msgstr "虛擬環繞聲 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4527
#: spa/plugins/alsa/acp/alsa-mixer.c:4522
msgid "Analog Mono"
msgstr "類比單聲道"
#: spa/plugins/alsa/acp/alsa-mixer.c:4528
#, fuzzy
#: spa/plugins/alsa/acp/alsa-mixer.c:4523
msgid "Analog Mono (Left)"
msgstr "類比單聲道"
msgstr "類比單聲道(左)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4529
#, fuzzy
#: spa/plugins/alsa/acp/alsa-mixer.c:4524
msgid "Analog Mono (Right)"
msgstr "類比單聲道"
msgstr "類比單聲道(右)"
#. Note: Not translated to "Analog Stereo Input", because the source
#. * name gets "Input" appended to it automatically, so adding "Input"
#. * here would lead to the source name to become "Analog Stereo Input
#. * Input". The same logic applies to analog-stereo-output,
#. * multichannel-input and multichannel-output.
#: spa/plugins/alsa/acp/alsa-mixer.c:4530
#: spa/plugins/alsa/acp/alsa-mixer.c:4538
#: spa/plugins/alsa/acp/alsa-mixer.c:4539
#: spa/plugins/alsa/acp/alsa-mixer.c:4525
#: spa/plugins/alsa/acp/alsa-mixer.c:4533
#: spa/plugins/alsa/acp/alsa-mixer.c:4534
msgid "Analog Stereo"
msgstr "類比立體聲"
#: spa/plugins/alsa/acp/alsa-mixer.c:4531
#: spa/plugins/alsa/acp/alsa-mixer.c:4526
msgid "Mono"
msgstr "單聲道"
#: spa/plugins/alsa/acp/alsa-mixer.c:4532
#: spa/plugins/alsa/acp/alsa-mixer.c:4527
msgid "Stereo"
msgstr "立體聲"
#: spa/plugins/alsa/acp/alsa-mixer.c:4540
#: spa/plugins/alsa/acp/alsa-mixer.c:4698
#: spa/plugins/bluez5/bluez5-device.c:1135
#: spa/plugins/alsa/acp/alsa-mixer.c:4535
#: spa/plugins/alsa/acp/alsa-mixer.c:4693
#: spa/plugins/bluez5/bluez5-device.c:2410
msgid "Headset"
msgstr "耳麥"
#: spa/plugins/alsa/acp/alsa-mixer.c:4541
#: spa/plugins/alsa/acp/alsa-mixer.c:4699
#, fuzzy
#: spa/plugins/alsa/acp/alsa-mixer.c:4536
#: spa/plugins/alsa/acp/alsa-mixer.c:4694
msgid "Speakerphone"
msgstr "喇叭"
msgstr "會議揚聲器"
#: spa/plugins/alsa/acp/alsa-mixer.c:4542
#: spa/plugins/alsa/acp/alsa-mixer.c:4543
#: spa/plugins/alsa/acp/alsa-mixer.c:4537
#: spa/plugins/alsa/acp/alsa-mixer.c:4538
msgid "Multichannel"
msgstr "多聲道"
#: spa/plugins/alsa/acp/alsa-mixer.c:4544
#: spa/plugins/alsa/acp/alsa-mixer.c:4539
msgid "Analog Surround 2.1"
msgstr "類比環繞聲 2.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4545
#: spa/plugins/alsa/acp/alsa-mixer.c:4540
msgid "Analog Surround 3.0"
msgstr "類比環繞聲 3.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4546
#: spa/plugins/alsa/acp/alsa-mixer.c:4541
msgid "Analog Surround 3.1"
msgstr "類比環繞聲 3.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4547
#: spa/plugins/alsa/acp/alsa-mixer.c:4542
msgid "Analog Surround 4.0"
msgstr "類比環繞聲 4.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4548
#: spa/plugins/alsa/acp/alsa-mixer.c:4543
msgid "Analog Surround 4.1"
msgstr "類比環繞聲 4.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4549
#: spa/plugins/alsa/acp/alsa-mixer.c:4544
msgid "Analog Surround 5.0"
msgstr "類比環繞聲 5.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4550
#: spa/plugins/alsa/acp/alsa-mixer.c:4545
msgid "Analog Surround 5.1"
msgstr "類比環繞聲 5.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4551
#: spa/plugins/alsa/acp/alsa-mixer.c:4546
msgid "Analog Surround 6.0"
msgstr "類比環繞聲 6.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4552
#: spa/plugins/alsa/acp/alsa-mixer.c:4547
msgid "Analog Surround 6.1"
msgstr "類比環繞聲 6.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4553
#: spa/plugins/alsa/acp/alsa-mixer.c:4548
msgid "Analog Surround 7.0"
msgstr "類比環繞聲 7.0"
#: spa/plugins/alsa/acp/alsa-mixer.c:4554
#: spa/plugins/alsa/acp/alsa-mixer.c:4549
msgid "Analog Surround 7.1"
msgstr "類比環繞聲 7.1"
#: spa/plugins/alsa/acp/alsa-mixer.c:4555
#: spa/plugins/alsa/acp/alsa-mixer.c:4550
msgid "Digital Stereo (IEC958)"
msgstr "數位立體聲 (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4556
#: spa/plugins/alsa/acp/alsa-mixer.c:4551
msgid "Digital Surround 4.0 (IEC958/AC3)"
msgstr "數位環繞聲 4.0 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4557
#: spa/plugins/alsa/acp/alsa-mixer.c:4552
msgid "Digital Surround 5.1 (IEC958/AC3)"
msgstr "數位環繞聲 5.1 (IEC958/AC3)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4558
#: spa/plugins/alsa/acp/alsa-mixer.c:4553
msgid "Digital Surround 5.1 (IEC958/DTS)"
msgstr "數位環繞聲 5.1 (IEC958/DTS)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4559
#: spa/plugins/alsa/acp/alsa-mixer.c:4554
msgid "Digital Stereo (HDMI)"
msgstr "數位立體聲 (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4560
#: spa/plugins/alsa/acp/alsa-mixer.c:4555
msgid "Digital Surround 5.1 (HDMI)"
msgstr "數位環繞聲 5.1 (HDMI)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4561
#: spa/plugins/alsa/acp/alsa-mixer.c:4556
msgid "Chat"
msgstr ""
msgstr "聊天"
#: spa/plugins/alsa/acp/alsa-mixer.c:4562
#: spa/plugins/alsa/acp/alsa-mixer.c:4557
msgid "Game"
msgstr ""
msgstr "遊戲"
#: spa/plugins/alsa/acp/alsa-mixer.c:4696
#: spa/plugins/alsa/acp/alsa-mixer.c:4691
msgid "Analog Mono Duplex"
msgstr "類比單聲道雙工"
#: spa/plugins/alsa/acp/alsa-mixer.c:4697
#: spa/plugins/alsa/acp/alsa-mixer.c:4692
msgid "Analog Stereo Duplex"
msgstr "類比立體聲雙工"
#: spa/plugins/alsa/acp/alsa-mixer.c:4700
#: spa/plugins/alsa/acp/alsa-mixer.c:4695
msgid "Digital Stereo Duplex (IEC958)"
msgstr "數位立體聲雙工 (IEC958)"
#: spa/plugins/alsa/acp/alsa-mixer.c:4701
#: spa/plugins/alsa/acp/alsa-mixer.c:4696
msgid "Multichannel Duplex"
msgstr "多聲道雙工"
#: spa/plugins/alsa/acp/alsa-mixer.c:4702
#: spa/plugins/alsa/acp/alsa-mixer.c:4697
msgid "Stereo Duplex"
msgstr "立體聲雙工"
#: spa/plugins/alsa/acp/alsa-mixer.c:4703
#: spa/plugins/alsa/acp/alsa-mixer.c:4698
msgid "Mono Chat + 7.1 Surround"
msgstr ""
msgstr "單聲道聊天 + 7.1 立體聲"
#: spa/plugins/alsa/acp/alsa-mixer.c:4806
#: spa/plugins/alsa/acp/alsa-mixer.c:4799
#, c-format
msgid "%s Output"
msgstr "%s 輸出"
#: spa/plugins/alsa/acp/alsa-mixer.c:4813
#: spa/plugins/alsa/acp/alsa-mixer.c:4807
#, c-format
msgid "%s Input"
msgstr "%s 輸入"
#: spa/plugins/alsa/acp/alsa-util.c:1175 spa/plugins/alsa/acp/alsa-util.c:1269
#: spa/plugins/alsa/acp/alsa-util.c:1233 spa/plugins/alsa/acp/alsa-util.c:1327
#, c-format
msgid ""
"snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu "
@ -481,23 +598,23 @@ msgstr[0] ""
"snd_pcm_avail() 傳回超出預期的大值:%lu bytes (%lu ms)。\n"
"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
#: spa/plugins/alsa/acp/alsa-util.c:1241
#: spa/plugins/alsa/acp/alsa-util.c:1299
#, c-format
msgid ""
"snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s"
"%lu ms).\n"
"snd_pcm_delay() returned a value that is exceptionally large: %li byte "
"(%s%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgid_plural ""
"snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s"
"%lu ms).\n"
"snd_pcm_delay() returned a value that is exceptionally large: %li bytes "
"(%s%lu ms).\n"
"Most likely this is a bug in the ALSA driver '%s'. Please report this issue "
"to the ALSA developers."
msgstr[0] ""
"snd_pcm_delay() 傳回超出預期的大值:%li bytes (%s%lu ms)。\n"
"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
#: spa/plugins/alsa/acp/alsa-util.c:1288
#: spa/plugins/alsa/acp/alsa-util.c:1346
#, c-format
msgid ""
"snd_pcm_avail_delay() returned strange values: delay %lu is less than avail "
@ -508,7 +625,7 @@ msgstr ""
"snd_pcm_avail_delay() 傳回超出預期的大值:延遲 %lu 少於可用的 %lu。\n"
"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
#: spa/plugins/alsa/acp/alsa-util.c:1331
#: spa/plugins/alsa/acp/alsa-util.c:1389
#, c-format
msgid ""
"snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte "
@ -524,62 +641,115 @@ msgstr[0] ""
"snd_pcm_mmap_begin() 傳回超出預期的大值:%lu bytes (%lu ms)。\n"
"這很能是 ALSA 驅動程式「%s」的臭蟲。請回報這個問題給 ALSA 開發者。"
#: spa/plugins/bluez5/bluez5-device.c:1010
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
msgstr ""
#: spa/plugins/alsa/acp/channelmap.h:460
msgid "(invalid)"
msgstr "(無效)"
#: spa/plugins/bluez5/bluez5-device.c:1033
#: spa/plugins/alsa/acp/compat.c:194
msgid "Built-in Audio"
msgstr "內部音效"
#: spa/plugins/alsa/acp/compat.c:199
msgid "Modem"
msgstr "數據機"
#: spa/plugins/bluez5/bluez5-device.c:2032
msgid "Audio Gateway (A2DP Source & HSP/HFP AG)"
msgstr "音訊閘道 (A2DP Source & HSP/HFP AG)"
#: spa/plugins/bluez5/bluez5-device.c:2061
msgid "Audio Streaming for Hearing Aids (ASHA Sink)"
msgstr "助聽器的音訊串流 (ASHA Sink)"
#: spa/plugins/bluez5/bluez5-device.c:2104
#, c-format
msgid "High Fidelity Playback (A2DP Sink, codec %s)"
msgstr ""
msgstr "高傳真播放裝置 (A2DP Sink編碼器 %s)"
#: spa/plugins/bluez5/bluez5-device.c:1035
#: spa/plugins/bluez5/bluez5-device.c:2107
#, c-format
msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)"
msgstr ""
msgstr "高傳真雙工裝置 (A2DP Source/Sink編碼器 %s)"
#: spa/plugins/bluez5/bluez5-device.c:1041
#: spa/plugins/bluez5/bluez5-device.c:2115
msgid "High Fidelity Playback (A2DP Sink)"
msgstr ""
msgstr "高傳真播放裝置 (A2DP Sink)"
#: spa/plugins/bluez5/bluez5-device.c:1043
#: spa/plugins/bluez5/bluez5-device.c:2117
msgid "High Fidelity Duplex (A2DP Source/Sink)"
msgstr ""
msgstr "高傳真雙工裝置 (A2DP Source/Sink)"
#: spa/plugins/bluez5/bluez5-device.c:1070
#: spa/plugins/bluez5/bluez5-device.c:2194
#, c-format
msgid "High Fidelity Playback (BAP Sink, codec %s)"
msgstr "高傳真播放裝置 (BAP Sink編碼器 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2199
#, c-format
msgid "High Fidelity Input (BAP Source, codec %s)"
msgstr "高傳真輸入裝置 (BAP Source編碼器 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2203
#, c-format
msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)"
msgstr "高傳真雙工裝置 (BAP Source/Sink編碼器 %s)"
#: spa/plugins/bluez5/bluez5-device.c:2212
msgid "High Fidelity Playback (BAP Sink)"
msgstr "高傳真播放裝置 (BAP Sink)"
#: spa/plugins/bluez5/bluez5-device.c:2216
msgid "High Fidelity Input (BAP Source)"
msgstr "高傳真輸入裝置 (BAP Source)"
#: spa/plugins/bluez5/bluez5-device.c:2219
msgid "High Fidelity Duplex (BAP Source/Sink)"
msgstr "高傳真雙工裝置 (BAP Source/Sink)"
#: spa/plugins/bluez5/bluez5-device.c:2259
#, c-format
msgid "Headset Head Unit (HSP/HFP, codec %s)"
msgstr ""
msgstr "耳麥頭戴單元 (HSP/HFP編碼器 %s)"
#: spa/plugins/bluez5/bluez5-device.c:1074
msgid "Headset Head Unit (HSP/HFP)"
msgstr ""
#: spa/plugins/bluez5/bluez5-device.c:1140
#: spa/plugins/bluez5/bluez5-device.c:2411
#: spa/plugins/bluez5/bluez5-device.c:2416
#: spa/plugins/bluez5/bluez5-device.c:2423
#: spa/plugins/bluez5/bluez5-device.c:2429
#: spa/plugins/bluez5/bluez5-device.c:2435
#: spa/plugins/bluez5/bluez5-device.c:2441
#: spa/plugins/bluez5/bluez5-device.c:2447
#: spa/plugins/bluez5/bluez5-device.c:2453
#: spa/plugins/bluez5/bluez5-device.c:2459
msgid "Handsfree"
msgstr "免持裝置"
#: spa/plugins/bluez5/bluez5-device.c:1155
msgid "Headphone"
msgstr "頭戴式耳機"
#: spa/plugins/bluez5/bluez5-device.c:2417
msgid "Handsfree (HFP)"
msgstr "免持裝置 (HFP)"
#: spa/plugins/bluez5/bluez5-device.c:1160
#: spa/plugins/bluez5/bluez5-device.c:2440
msgid "Portable"
msgstr "可攜裝置"
#: spa/plugins/bluez5/bluez5-device.c:1165
#: spa/plugins/bluez5/bluez5-device.c:2446
msgid "Car"
msgstr "汽車"
#: spa/plugins/bluez5/bluez5-device.c:1170
#: spa/plugins/bluez5/bluez5-device.c:2452
msgid "HiFi"
msgstr "HiFi"
#: spa/plugins/bluez5/bluez5-device.c:1175
#: spa/plugins/bluez5/bluez5-device.c:2458
msgid "Phone"
msgstr "手機"
#: spa/plugins/bluez5/bluez5-device.c:1181
#, fuzzy
#: spa/plugins/bluez5/bluez5-device.c:2465
msgid "Bluetooth"
msgstr "藍牙輸入"
msgstr "藍牙"
#: spa/plugins/bluez5/bluez5-device.c:2466
msgid "Bluetooth Handsfree"
msgstr "藍牙免持裝置"
#~ msgid "Headphone"
#~ msgstr "頭戴式耳機"

View file

@ -44,7 +44,7 @@ spa_format_audio_dsd_parse(const struct spa_pod *format, struct spa_audio_info_d
SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels),
SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position));
if (info->channels > max_position)
return -ECHRNG;
return -EINVAL;
if (position == NULL ||
spa_pod_copy_array(position, SPA_TYPE_Id, info->position, max_position) != info->channels) {
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);

View file

@ -87,7 +87,7 @@ spa_audio_layout_info_parse_name(struct spa_audio_layout_info *layout, size_t si
uint32_t i, n_pos;
if (spa_atou32(name+3, &n_pos, 10)) {
if (n_pos > max_position)
return -ECHRNG;
return -EINVAL;
for (i = 0; i < 0x1000 && i < n_pos; i++)
layout->position[i] = SPA_AUDIO_CHANNEL_AUX0 + i;
for (; i < n_pos; i++)
@ -99,7 +99,7 @@ spa_audio_layout_info_parse_name(struct spa_audio_layout_info *layout, size_t si
SPA_FOR_EACH_ELEMENT_VAR(spa_type_audio_layout_info, i) {
if (spa_streq(name, i->name)) {
if (i->layout.n_channels > max_position)
return -ECHRNG;
return -EINVAL;
*layout = i->layout;
return i->layout.n_channels;
}

View file

@ -88,14 +88,14 @@ spa_audio_info_raw_ext_update(struct spa_audio_info_raw *info, size_t size,
} else if (spa_streq(key, SPA_KEY_AUDIO_CHANNELS)) {
if (spa_atou32(val, &v, 0) && (force || info->channels == 0)) {
if (v > max_position)
return -ECHRNG;
return -EINVAL;
info->channels = v;
}
} else if (spa_streq(key, SPA_KEY_AUDIO_LAYOUT)) {
if (force || info->channels == 0) {
if (spa_audio_parse_layout(val, info->position, max_position, &v) > 0) {
if (v > max_position)
return -ECHRNG;
return -EINVAL;
info->channels = v;
SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
}
@ -105,7 +105,7 @@ spa_audio_info_raw_ext_update(struct spa_audio_info_raw *info, size_t size,
if (spa_audio_parse_position_n(val, strlen(val), info->position,
max_position, &v) > 0) {
if (v > max_position)
return -ECHRNG;
return -EINVAL;
info->channels = v;
SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
}

View file

@ -46,7 +46,7 @@ spa_format_audio_raw_ext_parse(const struct spa_pod *format, struct spa_audio_in
SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels),
SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position));
if (info->channels > max_position)
return -ECHRNG;
return -EINVAL;
if (position == NULL ||
spa_pod_copy_array(position, SPA_TYPE_Id, info->position, max_position) != info->channels) {
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);

View file

@ -111,8 +111,15 @@ SPA_API_POD_BODY int spa_pod_choice_n_values(uint32_t choice_type, uint32_t *min
SPA_API_POD_BODY int spa_pod_body_from_data(void *data, size_t maxsize, off_t offset, size_t size,
struct spa_pod *pod, const void **body)
{
if (offset < 0 || offset > (int64_t)UINT32_MAX)
if (offset < 0)
return -EINVAL;
/* On 32-bit platforms, off_t is a signed 32-bit type (since it tracks pointer
* width), and consequently can never exceed UINT32_MAX. Skip the upper-bound
* check on 32-bit platforms to avoid a compiler warning. */
#if __SIZEOF_POINTER__ > 4
if (offset > (int64_t)UINT32_MAX)
return -EINVAL;
#endif
if (size < sizeof(struct spa_pod) ||
size > maxsize ||
maxsize - size < (uint32_t)offset)

View file

@ -62,6 +62,7 @@ struct spa_cpu { struct spa_interface iface; };
#define SPA_CPU_FLAG_BMI2 (1<<18) /**< Bit Manipulation Instruction Set 2 */
#define SPA_CPU_FLAG_AVX512 (1<<19) /**< AVX-512 */
#define SPA_CPU_FLAG_SLOW_UNALIGNED (1<<20) /**< unaligned loads/stores are slow */
#define SPA_CPU_FLAG_SLOW_GATHER (1<<21) /**< gather functions are slow */
/* PPC specific */
#define SPA_CPU_FLAG_ALTIVEC (1<<0) /**< standard */

View file

@ -41,7 +41,7 @@ struct itimerspec;
#define SPA_TYPE_INTERFACE_System SPA_TYPE_INFO_INTERFACE_BASE "System"
#define SPA_TYPE_INTERFACE_DataSystem SPA_TYPE_INFO_INTERFACE_BASE "DataSystem"
#define SPA_VERSION_SYSTEM 0
#define SPA_VERSION_SYSTEM 1
struct spa_system { struct spa_interface iface; };
/* IO events */
@ -59,11 +59,18 @@ struct spa_system { struct spa_interface iface; };
struct spa_poll_event {
uint32_t events;
void *data;
union {
void *data;
uint64_t data_u64;
};
#ifdef __x86_64__
} __attribute__((packed));
#else
};
#endif
struct spa_system_methods {
#define SPA_VERSION_SYSTEM_METHODS 0
#define SPA_VERSION_SYSTEM_METHODS 1
uint32_t version;
/* read/write/ioctl */
@ -151,7 +158,7 @@ SPA_API_SYSTEM int spa_system_pollfd_del(struct spa_system *object, int pfd, int
SPA_API_SYSTEM int spa_system_pollfd_wait(struct spa_system *object, int pfd,
struct spa_poll_event *ev, int n_ev, int timeout)
{
return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, pollfd_wait, 0, pfd, ev, n_ev, timeout);
return spa_api_method_fast_r(int, -ENOTSUP, spa_system, &object->iface, pollfd_wait, 1, pfd, ev, n_ev, timeout);
}
SPA_API_SYSTEM int spa_system_timerfd_create(struct spa_system *object, int clockid, int flags)

View file

@ -5,7 +5,7 @@
#ifndef SPA_ENDIAN_H
#define SPA_ENDIAN_H
#if defined(__FreeBSD__) || defined(__MidnightBSD__)
#if defined(__MidnightBSD__)
#include <sys/endian.h>
#define bswap_16 bswap16
#define bswap_32 bswap32

View file

@ -0,0 +1,445 @@
/* Simple Plugin API */
/* SPDX-FileCopyrightText: Copyright © 2026 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#ifndef SPA_UTILS_JSON_BUILDER_H
#define SPA_UTILS_JSON_BUILDER_H
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <float.h>
#include <inttypes.h>
#include <limits.h>
#include <spa/utils/defs.h>
#include <spa/utils/ansi.h>
#include <spa/utils/json.h>
#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif
#ifndef SPA_API_JSON_BUILDER
#ifdef SPA_API_IMPL
#define SPA_API_JSON_BUILDER SPA_API_IMPL
#else
#define SPA_API_JSON_BUILDER static inline
#endif
#endif
/** \defgroup spa_json_builder JSON builder
* JSON builder functions
*/
/**
* \addtogroup spa_json_builder
* \{
*/
struct spa_json_builder {
FILE *f;
#define SPA_JSON_BUILDER_FLAG_CLOSE (1<<0)
#define SPA_JSON_BUILDER_FLAG_INDENT (1<<1)
#define SPA_JSON_BUILDER_FLAG_SPACE (1<<2)
#define SPA_JSON_BUILDER_FLAG_PRETTY (SPA_JSON_BUILDER_FLAG_INDENT|SPA_JSON_BUILDER_FLAG_SPACE)
#define SPA_JSON_BUILDER_FLAG_COLOR (1<<3)
#define SPA_JSON_BUILDER_FLAG_SIMPLE (1<<4)
#define SPA_JSON_BUILDER_FLAG_RAW (1<<5)
uint32_t flags;
uint32_t indent_off;
uint32_t level;
uint32_t indent;
uint32_t count;
const char *delim;
const char *comma;
const char *key_sep;
#define SPA_JSON_BUILDER_COLOR_NORMAL 0
#define SPA_JSON_BUILDER_COLOR_KEY 1
#define SPA_JSON_BUILDER_COLOR_LITERAL 2
#define SPA_JSON_BUILDER_COLOR_NUMBER 3
#define SPA_JSON_BUILDER_COLOR_STRING 4
#define SPA_JSON_BUILDER_COLOR_CONTAINER 5
const char *color[8];
};
SPA_API_JSON_BUILDER int spa_json_builder_file(struct spa_json_builder *b, FILE *f, uint32_t flags)
{
bool color = flags & SPA_JSON_BUILDER_FLAG_COLOR;
bool simple = flags & SPA_JSON_BUILDER_FLAG_SIMPLE;
bool space = flags & SPA_JSON_BUILDER_FLAG_SPACE;
spa_zero(*b);
b->f = f;
b->flags = flags;
b->indent = 2;
b->delim = "";
b->comma = simple ? space ? "" : " " : ",";
b->key_sep = simple ? space ? " =" : "=" : ":";
b->color[0] = (color ? SPA_ANSI_RESET : "");
b->color[1] = (color ? SPA_ANSI_BRIGHT_BLUE : "");
b->color[2] = (color ? SPA_ANSI_BRIGHT_MAGENTA : "");
b->color[3] = (color ? SPA_ANSI_BRIGHT_CYAN : "");
b->color[4] = (color ? SPA_ANSI_BRIGHT_GREEN : "");
b->color[5] = (color ? SPA_ANSI_BRIGHT_YELLOW : "");
return 0;
}
SPA_API_JSON_BUILDER int spa_json_builder_memstream(struct spa_json_builder *b,
char **mem, size_t *size, uint32_t flags)
{
FILE *f;
spa_zero(*b);
if ((f = open_memstream(mem, size)) == NULL)
return -errno;
return spa_json_builder_file(b, f, flags | SPA_JSON_BUILDER_FLAG_CLOSE);
}
SPA_API_JSON_BUILDER int spa_json_builder_membuf(struct spa_json_builder *b,
char *mem, size_t size, uint32_t flags)
{
FILE *f;
spa_zero(*b);
if ((f = fmemopen(mem, size, "w")) == NULL)
return -errno;
return spa_json_builder_file(b, f, flags | SPA_JSON_BUILDER_FLAG_CLOSE);
}
SPA_API_JSON_BUILDER void spa_json_builder_close(struct spa_json_builder *b)
{
if (b->flags & SPA_JSON_BUILDER_FLAG_CLOSE)
fclose(b->f);
}
SPA_API_JSON_BUILDER int spa_json_builder_encode_string(struct spa_json_builder *b,
bool raw, const char *before, const char *val, int size, const char *after)
{
FILE *f = b->f;
int i, len;
if (raw) {
len = fprintf(f, "%s%.*s%s", before, size, val, after) - 1;
} else {
len = fprintf(f, "%s\"", before);
for (i = 0; i < size && val[i]; i++) {
char v = val[i];
switch (v) {
case '\n': len += fprintf(f, "\\n"); break;
case '\r': len += fprintf(f, "\\r"); break;
case '\b': len += fprintf(f, "\\b"); break;
case '\t': len += fprintf(f, "\\t"); break;
case '\f': len += fprintf(f, "\\f"); break;
case '\\':
case '"': len += fprintf(f, "\\%c", v); break;
default:
if (v > 0 && v < 0x20)
len += fprintf(f, "\\u%04x", v);
else
len += fprintf(f, "%c", v);
break;
}
}
len += fprintf(f, "\"%s", after);
}
return len-1;
}
SPA_API_JSON_BUILDER
void spa_json_builder_add_simple(struct spa_json_builder *b, const char *key, int key_len,
char type, const char *val, int val_len)
{
bool indent = b->indent_off == 0 && (b->flags & SPA_JSON_BUILDER_FLAG_INDENT);
bool space = b->flags & SPA_JSON_BUILDER_FLAG_SPACE;
bool force_raw = b->flags & SPA_JSON_BUILDER_FLAG_RAW;
bool raw = true, simple = b->flags & SPA_JSON_BUILDER_FLAG_SIMPLE;
int color;
if (val == NULL || val_len == 0) {
val = "null";
val_len = 4;
type = 'l';
}
if (type == 0) {
if (spa_json_is_container(val, val_len))
type = simple ? 'C' : 'S';
else if (val_len > 0 && (*val == '}' || *val == ']'))
type = 'e';
else if (spa_json_is_null(val, val_len) ||
spa_json_is_bool(val, val_len))
type = 'l';
else if (spa_json_is_string(val, val_len))
type = 's';
else if (spa_json_is_json_number(val, val_len))
type = 'd';
else if (simple && (spa_json_is_float(val, val_len) ||
spa_json_is_int(val, val_len)))
type = 'd';
else
type = 'S';
}
switch (type) {
case 'e':
b->level -= b->indent;
b->delim = "";
break;
}
fprintf(b->f, "%s%s%*s", b->delim, b->count == 0 ? "" : indent ? "\n" : space ? " " : "",
indent ? b->level : 0, "");
if (key) {
bool key_raw = force_raw || (simple && spa_json_make_simple_string(&key, &key_len)) ||
spa_json_is_string(key, key_len);
spa_json_builder_encode_string(b, key_raw,
b->color[1], key, key_len, b->color[0]);
fprintf(b->f, "%s%s", b->key_sep, space ? " " : "");
}
b->delim = b->comma;
switch (type) {
case 'c':
color = SPA_JSON_BUILDER_COLOR_NORMAL;
val_len = 1;
b->delim = "";
b->level += b->indent;
if (val[1] == '-') b->indent_off++;
break;
case 'e':
color = SPA_JSON_BUILDER_COLOR_NORMAL;
val_len = 1;
if (val[1] == '-') b->indent_off--;
break;
case 'l':
color = SPA_JSON_BUILDER_COLOR_LITERAL;
break;
case 'd':
color = SPA_JSON_BUILDER_COLOR_NUMBER;
break;
case 's':
color = SPA_JSON_BUILDER_COLOR_STRING;
break;
case 'C':
color = SPA_JSON_BUILDER_COLOR_CONTAINER;
break;
default:
color = SPA_JSON_BUILDER_COLOR_STRING;
raw = force_raw || (simple && spa_json_make_simple_string(&val, &val_len));
break;
}
spa_json_builder_encode_string(b, raw, b->color[color], val, val_len, b->color[0]);
b->count++;
}
SPA_API_JSON_BUILDER void spa_json_builder_object_push(struct spa_json_builder *b,
const char *key, const char *val)
{
spa_json_builder_add_simple(b, key, INT_MAX, 'c', val, INT_MAX);
}
SPA_API_JSON_BUILDER void spa_json_builder_pop(struct spa_json_builder *b,
const char *val)
{
spa_json_builder_add_simple(b, NULL, 0, 'e', val, INT_MAX);
}
SPA_API_JSON_BUILDER void spa_json_builder_object_null(struct spa_json_builder *b,
const char *key)
{
spa_json_builder_add_simple(b, key, INT_MAX, 'l', "null", 4);
}
SPA_API_JSON_BUILDER void spa_json_builder_object_bool(struct spa_json_builder *b,
const char *key, bool val)
{
spa_json_builder_add_simple(b, key, INT_MAX, 'l', val ? "true" : "false", INT_MAX);
}
SPA_API_JSON_BUILDER void spa_json_builder_object_int(struct spa_json_builder *b,
const char *key, int64_t val)
{
char str[128];
snprintf(str, sizeof(str), "%" PRIi64, val);
spa_json_builder_add_simple(b, key, INT_MAX, 'd', str, INT_MAX);
}
SPA_API_JSON_BUILDER void spa_json_builder_object_uint(struct spa_json_builder *b,
const char *key, uint64_t val)
{
char str[128];
snprintf(str, sizeof(str), "%" PRIu64, val);
spa_json_builder_add_simple(b, key, INT_MAX, 'd', str, INT_MAX);
}
SPA_API_JSON_BUILDER void spa_json_builder_object_double(struct spa_json_builder *b,
const char *key, double val)
{
char str[64];
spa_json_format_float(str, sizeof(str), (float)val);
spa_json_builder_add_simple(b, key, INT_MAX, 'd', str, INT_MAX);
}
SPA_API_JSON_BUILDER void spa_json_builder_object_string(struct spa_json_builder *b,
const char *key, const char *val)
{
spa_json_builder_add_simple(b, key, INT_MAX, 'S', val, INT_MAX);
}
SPA_API_JSON_BUILDER SPA_PRINTF_FUNC(3,0)
void spa_json_builder_object_stringv(struct spa_json_builder *b,
const char *key, const char *fmt, va_list va)
{
char *val;
if (vasprintf(&val, fmt, va) > 0) {
spa_json_builder_object_string(b, key, val);
free(val);
}
}
SPA_API_JSON_BUILDER SPA_PRINTF_FUNC(3,4)
void spa_json_builder_object_stringf(struct spa_json_builder *b,
const char *key, const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
spa_json_builder_object_stringv(b, key, fmt, va);
va_end(va);
}
SPA_API_JSON_BUILDER void spa_json_builder_object_value_iter(struct spa_json_builder *b,
struct spa_json *it, const char *key, int key_len, const char *val, int len)
{
struct spa_json sub;
if (spa_json_is_array(val, len)) {
spa_json_builder_add_simple(b, key, key_len, 'c', "[", 1);
spa_json_enter(it, &sub);
while ((len = spa_json_next(&sub, &val)) > 0)
spa_json_builder_object_value_iter(b, &sub, NULL, 0, val, len);
spa_json_builder_pop(b, "]");
}
else if (spa_json_is_object(val, len)) {
const char *k;
int kl;
spa_json_builder_add_simple(b, key, key_len, 'c', "{", 1);
spa_json_enter(it, &sub);
while ((kl = spa_json_next(&sub, &k)) > 0) {
if ((len = spa_json_next(&sub, &val)) < 0)
break;
spa_json_builder_object_value_iter(b, &sub, k, kl, val, len);
}
spa_json_builder_pop(b, "}");
}
else {
spa_json_builder_add_simple(b, key, key_len, 0, val, len);
}
}
SPA_API_JSON_BUILDER void spa_json_builder_object_value_full(struct spa_json_builder *b,
bool recurse, const char *key, int key_len, const char *val, int val_len)
{
if (!recurse || val == NULL) {
spa_json_builder_add_simple(b, key, key_len, 0, val, val_len);
} else {
struct spa_json it[1];
const char *v;
if (spa_json_begin(&it[0], val, val_len, &v) >= 0)
spa_json_builder_object_value_iter(b, &it[0], key, key_len, val, val_len);
}
}
SPA_API_JSON_BUILDER void spa_json_builder_object_value(struct spa_json_builder *b,
bool recurse, const char *key, const char *val)
{
spa_json_builder_object_value_full(b, recurse, key, key ? strlen(key) : 0,
val, val ? strlen(val) : 0);
}
SPA_API_JSON_BUILDER SPA_PRINTF_FUNC(4,5)
void spa_json_builder_object_valuef(struct spa_json_builder *b,
bool recurse, const char *key, const char *fmt, ...)
{
va_list va;
char *val;
va_start(va, fmt);
if (vasprintf(&val, fmt, va) > 0) {
spa_json_builder_object_value(b, recurse, key, val);
free(val);
}
va_end(va);
}
/* array functions */
SPA_API_JSON_BUILDER void spa_json_builder_array_push(struct spa_json_builder *b,
const char *val)
{
spa_json_builder_object_push(b, NULL, val);
}
SPA_API_JSON_BUILDER void spa_json_builder_array_null(struct spa_json_builder *b)
{
spa_json_builder_object_null(b, NULL);
}
SPA_API_JSON_BUILDER void spa_json_builder_array_bool(struct spa_json_builder *b,
bool val)
{
spa_json_builder_object_bool(b, NULL, val);
}
SPA_API_JSON_BUILDER void spa_json_builder_array_int(struct spa_json_builder *b,
int64_t val)
{
spa_json_builder_object_int(b, NULL, val);
}
SPA_API_JSON_BUILDER void spa_json_builder_array_uint(struct spa_json_builder *b,
uint64_t val)
{
spa_json_builder_object_uint(b, NULL, val);
}
SPA_API_JSON_BUILDER void spa_json_builder_array_double(struct spa_json_builder *b,
double val)
{
spa_json_builder_object_double(b, NULL, val);
}
SPA_API_JSON_BUILDER void spa_json_builder_array_string(struct spa_json_builder *b,
const char *val)
{
spa_json_builder_object_string(b, NULL, val);
}
SPA_API_JSON_BUILDER SPA_PRINTF_FUNC(2,3)
void spa_json_builder_array_stringf(struct spa_json_builder *b,
const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
spa_json_builder_object_stringv(b, NULL, fmt, va);
va_end(va);
}
SPA_API_JSON_BUILDER void spa_json_builder_array_value(struct spa_json_builder *b,
bool recurse, const char *val)
{
spa_json_builder_object_value(b, recurse, NULL, val);
}
SPA_API_JSON_BUILDER SPA_PRINTF_FUNC(3,4)
void spa_json_builder_array_valuef(struct spa_json_builder *b, bool recurse, const char *fmt, ...)
{
va_list va;
char *val;
va_start(va, fmt);
if (vasprintf(&val, fmt, va) > 0) {
spa_json_builder_object_value(b, recurse, NULL, val);
free(val);
}
va_end(va);
}
SPA_API_JSON_BUILDER char *spa_json_builder_reformat(const char *json, uint32_t flags)
{
struct spa_json_builder b;
char *mem;
size_t size;
int res;
if ((res = spa_json_builder_memstream(&b, &mem, &size, flags)) < 0) {
errno = -res;
return NULL;
}
spa_json_builder_array_value(&b, true, json);
spa_json_builder_close(&b);
return mem;
}
/**
* \}
*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* SPA_UTILS_JSON_BUILDER_H */

View file

@ -232,7 +232,7 @@ SPA_API_JSON int spa_json_next(struct spa_json * iter, const char **value)
switch (cur) {
case '\0':
case '\t': case ' ': case '\r': case '\n':
case '"': case '#':
case '"': case '#': case '{': case '[':
case ':': case ',': case '=': case ']': case '}':
iter->state = __STRUCT | flag;
if (iter->depth > 0)
@ -399,6 +399,10 @@ SPA_API_JSON int spa_json_is_container(const char *val, int len)
{
return len > 0 && (*val == '{' || *val == '[');
}
SPA_API_JSON int spa_json_is_container_end(const char *val, int len)
{
return len > 0 && (*val == '}' || *val == ']');
}
/* object */
SPA_API_JSON int spa_json_is_object(const char *val, int len)
@ -421,20 +425,11 @@ SPA_API_JSON bool spa_json_is_null(const char *val, int len)
/* float */
SPA_API_JSON int spa_json_parse_float(const char *val, int len, float *result)
{
char buf[96];
char *end;
int pos;
char buf[96], *end;
if (len <= 0 || len >= (int)sizeof(buf))
return 0;
for (pos = 0; pos < len; ++pos) {
switch (val[pos]) {
case '+': case '-': case '0' ... '9': case '.': case 'e': case 'E': break;
default: return 0;
}
}
memcpy(buf, val, len);
buf[len] = '\0';
@ -462,8 +457,7 @@ SPA_API_JSON char *spa_json_format_float(char *str, int size, float val)
/* int */
SPA_API_JSON int spa_json_parse_int(const char *val, int len, int *result)
{
char buf[64];
char *end;
char buf[64], *end;
if (len <= 0 || len >= (int)sizeof(buf))
return 0;
@ -480,6 +474,39 @@ SPA_API_JSON bool spa_json_is_int(const char *val, int len)
return spa_json_parse_int(val, len, &dummy);
}
SPA_API_JSON bool spa_json_is_json_number(const char *val, int len)
{
static const int8_t trans[9][7] = {
/* '1-9' '0' '-' '+' '.' 'eE' other */
/* 0 */ {-1, -1, -1, -1, 6, 7, -1 }, /* after '0' */
/* 1 */ { 1, 1, -1, -1, 6, 7, -1 }, /* in integer */
/* 2 */ { 2, 2, -1, -1, -1, 7, -1 }, /* in fraction */
/* 3 */ { 3, 3, -1, -1, -1, -1, -1 }, /* in exponent */
/* 4 */ { 1, 0, 5, -1, -1, -1, -1 }, /* start */
/* 5 */ { 1, 0, -1, -1, -1, -1, -1 }, /* after '-' */
/* 6 */ { 2, 2, -1, -1, -1, -1, -1 }, /* after '.' */
/* 7 */ { 3, 3, 8, 8, -1, -1, -1 }, /* after 'e'/'E' */
/* 8 */ { 3, 3, -1, -1, -1, -1, -1 }, /* after exp sign */
};
static const int8_t char_class[128] = {
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, /* 0-15 */
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, /* 16-31 */
6,6,6,6,6,6,6,6,6,6,6,3,6,2,4,6, /* 32-47: + - . */
1,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6, /* 48-63: 0-9 */
6,6,6,6,6,5,6,6,6,6,6,6,6,6,6,6, /* 64-79: E */
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, /* 80-95 */
6,6,6,6,6,5,6,6,6,6,6,6,6,6,6,6, /* 96-111: e */
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, /* 112-127 */
};
int i, state = 4;
for (i = 0; i < len; i++) {
if ((state = trans[state][char_class[val[i]&0x7f]]) < 0)
return false;
}
return state < 4;
}
/* bool */
SPA_API_JSON bool spa_json_is_true(const char *val, int len)
{
@ -510,6 +537,46 @@ SPA_API_JSON bool spa_json_is_string(const char *val, int len)
{
return len > 1 && *val == '"';
}
SPA_API_JSON bool spa_json_is_simple_string(const char *val, int size)
{
int i;
static const char *REJECT = "\"\\'=:,{}[]()#";
for (i = 0; i < size && val[i]; i++) {
if (val[i] <= 0x20 || strchr(REJECT, val[i]) != NULL)
return false;
}
return true;
}
SPA_API_JSON bool spa_json_make_simple_string(const char **val, int *len)
{
int i, l = *len;
const char *v = *val;
static const char *REJECT = "\"\\'=:,{}[]()#";
int trimmed = 0, bad = 0;
for (i = 0; i < l && v[i]; i++) {
if (i == 0 && v[0] == '\"')
trimmed++;
else if ((i+1 == l || !v[i+1]) && v[i] == '\"')
trimmed++;
else if (v[i] <= 0x20 || strchr(REJECT, v[i]) != NULL)
bad++;
}
if (trimmed == 0 && bad == 0 && i > 0)
return true;
else if (trimmed == 2) {
if (bad == 0 && i > 2 &&
!spa_json_is_null(&v[1], i-2) &&
!spa_json_is_bool(&v[1], i-2) &&
!spa_json_is_float(&v[1], i-2) &&
!spa_json_is_container(&v[1], i-2) &&
!spa_json_is_container_end(&v[1], i-2)) {
(*len) = i-2;
(*val)++;
}
return true;
}
return false;
}
SPA_API_JSON int spa_json_parse_hex(const char *p, int num, uint32_t *res)
{

View file

@ -380,17 +380,26 @@ SPA_API_STRING void spa_strbuf_init(struct spa_strbuf *buf, char *buffer, size_t
buf->buffer[0] = '\0';
}
SPA_PRINTF_FUNC(2, 0)
SPA_API_STRING int spa_strbuf_appendv(struct spa_strbuf *buf, const char *fmt, va_list args)
{
size_t remain = buf->maxsize - buf->pos;
int written = vsnprintf(&buf->buffer[buf->pos], remain, fmt, args);
if (written > 0)
buf->pos += SPA_MIN(remain, (size_t)written);
return written;
}
SPA_PRINTF_FUNC(2, 3)
SPA_API_STRING int spa_strbuf_append(struct spa_strbuf *buf, const char *fmt, ...)
{
size_t remain = buf->maxsize - buf->pos;
ssize_t written;
va_list args;
int written;
va_start(args, fmt);
written = vsnprintf(&buf->buffer[buf->pos], remain, fmt, args);
written = spa_strbuf_appendv(buf, fmt, args);
va_end(args);
if (written > 0)
buf->pos += SPA_MIN(remain, (size_t)written);
return written;
}

View file

@ -2,7 +2,6 @@
#undef SPA_AUDIO_MAX_CHANNELS
#define SPA_API_IMPL SPA_EXPORT
#include <spa/utils/defs.h>
#include <spa/buffer/alloc.h>
#include <spa/buffer/buffer.h>
#include <spa/buffer/type-info.h>
@ -126,16 +125,17 @@
#include <spa/param/video/raw-types.h>
#include <spa/param/video/raw-utils.h>
#include <spa/param/video/type-info.h>
#include <spa/pod/body.h>
#include <spa/pod/builder.h>
#include <spa/pod/command.h>
#include <spa/pod/compare.h>
#include <spa/pod/dynamic.h>
#include <spa/pod/event.h>
#include <spa/pod/filter.h>
#include <spa/pod/body.h>
#include <spa/pod/iter.h>
#include <spa/pod/parser.h>
#include <spa/pod/pod.h>
#include <spa/pod/simplify.h>
#include <spa/pod/vararg.h>
#include <spa/support/cpu.h>
#include <spa/support/dbus.h>
@ -158,6 +158,7 @@
#include <spa/utils/hook.h>
#include <spa/utils/json-core.h>
#include <spa/utils/json.h>
#include <spa/utils/json-builder.h>
#include <spa/utils/json-pod.h>
#include <spa/utils/keys.h>
#include <spa/utils/list.h>

View file

@ -39,7 +39,7 @@ struct impl_data {
std::unique_ptr<float *[]> play_buffer, rec_buffer, out_buffer;
};
SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.eac.webrtc");
SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.aec.webrtc");
#undef SPA_LOG_TOPIC_DEFAULT
#define SPA_LOG_TOPIC_DEFAULT &log_topic
@ -221,6 +221,11 @@ static int webrtc_init2(void *object, const struct spa_dict *args,
}};
#endif
if (out_info->channels != 1 && rec_info->channels != out_info->channels) {
spa_log_error(impl->log, "Source channels must be equal to capture channels or 1");
return -EINVAL;
}
#if defined(HAVE_WEBRTC)
auto apm = std::unique_ptr<webrtc::AudioProcessing>(webrtc::AudioProcessing::Create(config));
#elif defined(HAVE_WEBRTC1)

View file

@ -10,7 +10,9 @@
#include <time.h>
#include <stdbool.h>
#include <getopt.h>
#ifdef __linux__
#include <alloca.h>
#endif
#include <spa/debug/context.h>
#include <spa/utils/string.h>

View file

@ -485,13 +485,11 @@ static int add_pro_profile(pa_card *impl, uint32_t index)
if ((n_capture == 1 && n_playback == 1) || is_firewire) {
PA_IDXSET_FOREACH(m, ap->output_mappings, idx) {
pa_proplist_setf(m->output_proplist, "node.group", "pro-audio-%u", index);
pa_proplist_setf(m->output_proplist, "node.link-group", "pro-audio-%u", index);
pa_proplist_setf(m->output_proplist, "api.alsa.auto-link", "true");
pa_proplist_setf(m->output_proplist, "api.alsa.disable-tsched", "true");
}
PA_IDXSET_FOREACH(m, ap->input_mappings, idx) {
pa_proplist_setf(m->input_proplist, "node.group", "pro-audio-%u", index);
pa_proplist_setf(m->input_proplist, "node.link-group", "pro-audio-%u", index);
pa_proplist_setf(m->input_proplist, "api.alsa.auto-link", "true");
pa_proplist_setf(m->input_proplist, "api.alsa.disable-tsched", "true");
}

View file

@ -429,14 +429,14 @@ static PA_PRINTF_FUNC(1,0) inline char *pa_vsprintf_malloc(const char *fmt, va_l
#define pa_fopen_cloexec(f,m) fopen(f,m"e")
static inline char *pa_path_get_filename(const char *p)
static inline const char *pa_path_get_filename(const char *p)
{
char *fn;
const char *fn;
if (!p)
return NULL;
if ((fn = strrchr(p, PA_PATH_SEP_CHAR)))
return fn+1;
return (char*) p;
return p;
}
static inline bool pa_is_path_absolute(const char *fn)

View file

@ -227,7 +227,7 @@ static void emit_port_info(struct seq_state *this, struct seq_port *port, bool f
if (full)
port->info.change_mask = port->info_all;
if (port->info.change_mask) {
struct spa_dict_item items[6];
struct spa_dict_item items[7];
uint32_t n_items = 0;
int card_id;
snd_seq_port_info_t *info;
@ -284,6 +284,9 @@ static void emit_port_info(struct seq_state *this, struct seq_port *port, bool f
snprintf(card, sizeof(card), "%d", card_id);
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD, card);
}
if (this->ump)
items[n_items++] = SPA_DICT_ITEM_INIT("control.ump", "true");
port->info.props = &SPA_DICT_INIT(items, n_items);
spa_node_emit_port_info(&this->hooks,
@ -501,6 +504,7 @@ impl_node_port_enum_params(void *object, int seq,
struct seq_state *this = object;
struct seq_port *port;
struct spa_pod *param;
struct spa_pod_frame f[1];
struct spa_pod_builder b = { 0 };
uint8_t buffer[1024];
struct spa_result_node_params result;
@ -524,10 +528,18 @@ impl_node_port_enum_params(void *object, int seq,
case SPA_PARAM_EnumFormat:
if (result.index > 0)
return 0;
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
spa_pod_builder_push_object(&b, &f[0],
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
spa_pod_builder_add(&b,
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control));
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control),
0);
if (port->control_types != 0) {
spa_pod_builder_add(&b,
SPA_FORMAT_CONTROL_types, SPA_POD_Int(port->control_types),
0);
}
param = spa_pod_builder_pop(&b, &f[0]);
break;
case SPA_PARAM_Format:
@ -535,10 +547,18 @@ impl_node_port_enum_params(void *object, int seq,
return -EIO;
if (result.index > 0)
return 0;
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Format, SPA_PARAM_Format,
spa_pod_builder_push_object(&b, &f[0],
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
spa_pod_builder_add(&b,
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control));
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control),
0);
if (port->control_types != 0) {
spa_pod_builder_add(&b,
SPA_FORMAT_CONTROL_types, SPA_POD_Int(port->control_types),
0);
}
param = spa_pod_builder_pop(&b, &f[0]);
break;
case SPA_PARAM_Buffers:
@ -955,7 +975,7 @@ impl_init(const struct spa_handle_factory *factory,
this->quantum_limit = 8192;
this->min_pool_size = 500;
this->max_pool_size = 2000;
this->ump = true;
this->ump = false;
for (i = 0; info && i < info->n_items; i++) {
const char *k = info->items[i].key;

View file

@ -620,9 +620,8 @@ static int process_read(struct seq_state *state)
{
struct seq_stream *stream = &state->streams[SPA_DIRECTION_OUTPUT];
const bool ump = state->ump;
uint32_t *data;
void *data;
uint8_t midi1_data[MAX_EVENT_SIZE];
uint32_t ump_data[MAX_EVENT_SIZE];
long size;
int res = -1;
struct seq_port *port;
@ -633,9 +632,6 @@ static int process_read(struct seq_state *state)
uint64_t ev_time, diff;
uint32_t offset;
void *event;
uint8_t *midi1_ptr;
size_t midi1_size = 0;
uint64_t ump_state = 0;
snd_seq_event_type_t SPA_UNUSED type;
if (ump) {
@ -702,7 +698,7 @@ static int process_read(struct seq_state *state)
#ifdef HAVE_ALSA_UMP
snd_seq_ump_event_t *ev = event;
data = (uint32_t*)&ev->ump[0];
data = &ev->ump[0];
size = spa_ump_message_size(snd_ump_msg_hdr_type(ev->ump[0])) * 4;
#else
spa_assert_not_reached();
@ -715,34 +711,21 @@ static int process_read(struct seq_state *state)
spa_log_warn(state->log, "decode failed: %s", snd_strerror(size));
continue;
}
midi1_ptr = midi1_data;
midi1_size = size;
data = midi1_data;
}
do {
if (!ump) {
data = ump_data;
size = spa_ump_from_midi(&midi1_ptr, &midi1_size,
ump_data, sizeof(ump_data), 0, &ump_state);
if (size <= 0)
break;
}
spa_log_trace_fp(state->log, "event %d time:%"PRIu64" offset:%d size:%ld port:%d.%d",
type, ev_time, offset, size, addr->client, addr->port);
spa_log_trace_fp(state->log, "event %d time:%"PRIu64" offset:%d size:%ld port:%d.%d",
type, ev_time, offset, size, addr->client, addr->port);
spa_pod_builder_control(&port->builder, offset, ump ? SPA_CONTROL_UMP : SPA_CONTROL_Midi );
spa_pod_builder_bytes(&port->builder, data, size);
spa_pod_builder_control(&port->builder, offset, SPA_CONTROL_UMP);
spa_pod_builder_bytes(&port->builder, data, size);
/* make sure we can fit at least one control event of max size otherwise
* we keep the event in the queue and try to copy it in the next cycle */
if (port->builder.state.offset +
sizeof(struct spa_pod_control) +
MAX_EVENT_SIZE > port->buffer->buf->datas[0].maxsize)
goto done;
} while (!ump);
/* make sure we can fit at least one control event of max size otherwise
* we keep the event in the queue and try to copy it in the next cycle */
if (port->builder.state.offset +
sizeof(struct spa_pod_control) +
MAX_EVENT_SIZE > port->buffer->buf->datas[0].maxsize)
goto done;
}
done:
@ -819,7 +802,6 @@ static int process_write(struct seq_state *state)
const void *c_body;
uint64_t out_time;
snd_seq_real_time_t out_rt;
bool first = true;
if (io->status != SPA_STATUS_HAVE_DATA ||
io->buffer_id >= port->n_buffers)
@ -844,9 +826,6 @@ static int process_write(struct seq_state *state)
size_t body_size;
uint8_t *body;
if (c.type != SPA_CONTROL_UMP)
continue;
body = (uint8_t*)c_body;
body_size = c.value.size;
@ -861,6 +840,9 @@ static int process_write(struct seq_state *state)
#ifdef HAVE_ALSA_UMP
snd_seq_ump_event_t ev;
if (c.type != SPA_CONTROL_UMP)
continue;
snd_seq_ump_ev_clear(&ev);
snd_seq_ev_set_ump_data(&ev, body, SPA_MIN(sizeof(ev.ump), (size_t)body_size));
snd_seq_ev_set_source(&ev, state->event.addr.port);
@ -878,26 +860,26 @@ static int process_write(struct seq_state *state)
#endif
} else {
snd_seq_event_t ev;
uint8_t data[MAX_EVENT_SIZE];
int size;
uint64_t st = 0;
int size = 0;
long s;
if (c.type != SPA_CONTROL_Midi)
continue;
while (body_size > 0) {
if ((size = spa_ump_to_midi((const uint32_t **)&body, &body_size,
data, sizeof(data), &st)) <= 0)
break;
if (first)
if (size == 0)
snd_seq_ev_clear(&ev);
if ((size = snd_midi_event_encode(stream->codec, data, size, &ev)) < 0) {
if ((s = snd_midi_event_encode(stream->codec, body, body_size, &ev)) < 0) {
spa_log_warn(state->log, "failed to encode event: %s",
snd_strerror(size));
snd_midi_event_reset_encode(stream->codec);
first = true;
size = 0;
continue;
}
first = false;
body += s;
body_size -= s;
size += s;
if (ev.type == SND_SEQ_EVENT_NONE)
/* this can happen when the event is not complete yet, like
* a sysex message and we need to encode some more data. */
@ -913,7 +895,7 @@ static int process_write(struct seq_state *state)
spa_log_warn(state->log, "failed to output event: %s",
snd_strerror(err));
}
first = true;
size = 0;
}
}
}

View file

@ -82,6 +82,8 @@ struct seq_port {
struct spa_pod_builder builder;
struct spa_pod_frame frame;
uint32_t control_types;
struct spa_audio_info current_format;
unsigned int have_format:1;
unsigned int active:1;

View file

@ -48,6 +48,7 @@ struct card {
unsigned int accessible:1;
unsigned int ignored:1;
unsigned int emitted:1;
unsigned int wireless_disconnected:1;
/* Local SPA object IDs. (Global IDs are produced by PipeWire
* out of this using its registry.) Compress-Offload or PCM
@ -59,6 +60,10 @@ struct card {
* is used because 0 is a valid ALSA card number. */
uint32_t pcm_device_id;
uint32_t compress_offload_device_id;
/* Syspath of the USB interface that has wireless_status (e.g.
* /sys/devices/.../1-5:1.3). Empty string when not applicable. */
char wireless_status_syspath[256];
};
static uint32_t calc_pcm_device_id(struct card *card)
@ -92,6 +97,8 @@ struct impl {
struct spa_source source;
struct spa_source notify;
struct udev_monitor *usb_umonitor;
struct spa_source usb_source;
unsigned int use_acp:1;
unsigned int expose_busy:1;
int use_ucm;
@ -353,6 +360,87 @@ static int check_udev_environment(struct udev *udev, const char *devname)
return ret;
}
static void check_wireless_status(struct impl *this, struct card *card)
{
char path[PATH_MAX];
char buf[32];
size_t sz;
bool was_disconnected;
if (card->wireless_status_syspath[0] == '\0')
return;
was_disconnected = card->wireless_disconnected;
spa_scnprintf(path, sizeof(path), "%s/wireless_status", card->wireless_status_syspath);
spa_autoptr(FILE) f = fopen(path, "re");
if (f == NULL)
return;
sz = fread(buf, 1, sizeof(buf) - 1, f);
buf[sz] = '\0';
card->wireless_disconnected = spa_strstartswith(buf, "disconnected");
if (card->wireless_disconnected != was_disconnected)
spa_log_info(this->log, "card %u: wireless headset %s",
card->card_nr,
card->wireless_disconnected ? "disconnected" : "connected");
}
static void find_wireless_status(struct impl *this, struct card *card)
{
const char *bus, *parent_syspath, *parent_sysname;
struct udev_device *parent;
struct dirent *entry;
char path[PATH_MAX];
size_t sysname_len;
bus = udev_device_get_property_value(card->udev_device, "ID_BUS");
if (!spa_streq(bus, "usb"))
return;
/* udev_device_get_parent_* returns a borrowed reference owned by the child; do not unref it. */
parent = udev_device_get_parent_with_subsystem_devtype(card->udev_device, "usb", "usb_device");
if (parent == NULL)
return;
parent_syspath = udev_device_get_syspath(parent);
parent_sysname = udev_device_get_sysname(parent);
if (parent_syspath == NULL || parent_sysname == NULL)
return;
sysname_len = strlen(parent_sysname);
spa_autoptr(DIR) dir = opendir(parent_syspath);
if (dir == NULL)
return;
while ((entry = readdir(dir)) != NULL) {
/* USB interface directories are named "<sysname>:<config>.<intf>" */
if (strncmp(entry->d_name, parent_sysname, sysname_len) != 0 ||
entry->d_name[sysname_len] != ':')
continue;
spa_scnprintf(path, sizeof(path), "%s/%s/wireless_status",
parent_syspath, entry->d_name);
if (access(path, R_OK) < 0)
continue;
spa_scnprintf(card->wireless_status_syspath,
sizeof(card->wireless_status_syspath),
"%s/%s", parent_syspath, entry->d_name);
check_wireless_status(this, card);
spa_log_debug(this->log, "card %u: found wireless_status at %s (%s)",
card->card_nr, card->wireless_status_syspath,
card->wireless_disconnected ? "disconnected" : "connected");
return;
}
}
static int check_pcm_device_availability(struct impl *this, struct card *card,
int *num_pcm_devices)
{
@ -538,6 +626,9 @@ static int emit_added_object_info(struct impl *this, struct card *card)
if ((str = udev_device_get_property_value(udev_device, "ACP_PROFILE_SET")) && *str)
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PROFILE_SET, str);
if ((str = udev_device_get_property_value(udev_device, "ACP_IGNORE_DB")) && *str)
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_IGNORE_DB, str);
if ((str = udev_device_get_property_value(udev_device, "SOUND_CLASS")) && *str)
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_CLASS, str);
@ -731,7 +822,11 @@ static void process_card(struct impl *this, enum action action, struct card *car
return;
check_access(this, card);
if (card->accessible && !card->emitted) {
check_wireless_status(this, card);
bool effective_accessible = card->accessible && !card->wireless_disconnected;
if (effective_accessible && !card->emitted) {
int res = emit_added_object_info(this, card);
if (res < 0) {
if (card->ignored)
@ -750,7 +845,7 @@ static void process_card(struct impl *this, enum action action, struct card *car
card->card_nr);
card->unavailable = false;
}
} else if (!card->accessible && card->emitted) {
} else if (!effective_accessible && card->emitted) {
card->emitted = false;
if (card->pcm_device_id != ID_DEVICE_NOT_SUPPORTED)
@ -787,8 +882,11 @@ static void process_udev_device(struct impl *this, enum action action, struct ud
return;
card = find_card(this, card_nr);
if (action == ACTION_CHANGE && !card)
if (action == ACTION_CHANGE && !card) {
card = add_card(this, card_nr, udev_device);
if (card)
find_wireless_status(this, card);
}
if (!card)
return;
@ -915,6 +1013,41 @@ static void impl_on_fd_events(struct spa_source *source)
udev_device_unref(udev_device);
}
static void impl_on_usb_events(struct spa_source *source)
{
struct impl *this = source->data;
struct udev_device *udev_device;
const char *action, *syspath;
unsigned int i;
udev_device = udev_monitor_receive_device(this->usb_umonitor);
if (udev_device == NULL)
return;
if ((action = udev_device_get_action(udev_device)) == NULL)
action = "change";
if (!spa_streq(action, "change"))
goto done;
syspath = udev_device_get_syspath(udev_device);
if (syspath == NULL)
goto done;
for (i = 0; i < this->n_cards; i++) {
struct card *card = &this->cards[i];
if (card->wireless_status_syspath[0] == '\0')
continue;
if (!spa_streq(card->wireless_status_syspath, syspath))
continue;
spa_log_debug(this->log, "wireless_status change for card %u", card->card_nr);
process_card(this, ACTION_CHANGE, card);
}
done:
udev_device_unref(udev_device);
}
static int start_monitor(struct impl *this)
{
int res;
@ -938,6 +1071,20 @@ static int start_monitor(struct impl *this)
spa_log_debug(this->log, "monitor %p", this->umonitor);
spa_loop_add_source(this->main_loop, &this->source);
this->usb_umonitor = udev_monitor_new_from_netlink(this->udev, "udev");
if (this->usb_umonitor != NULL) {
udev_monitor_filter_add_match_subsystem_devtype(this->usb_umonitor,
"usb", "usb_interface");
udev_monitor_enable_receiving(this->usb_umonitor);
this->usb_source.func = impl_on_usb_events;
this->usb_source.data = this;
this->usb_source.fd = udev_monitor_get_fd(this->usb_umonitor);
this->usb_source.mask = SPA_IO_IN | SPA_IO_ERR;
spa_loop_add_source(this->main_loop, &this->usb_source);
}
if ((res = start_inotify(this)) < 0)
return res;
@ -955,6 +1102,12 @@ static int stop_monitor(struct impl *this)
udev_monitor_unref(this->umonitor);
this->umonitor = NULL;
if (this->usb_umonitor != NULL) {
spa_loop_remove_source(this->main_loop, &this->usb_source);
udev_monitor_unref(this->usb_umonitor);
this->usb_umonitor = NULL;
}
stop_inotify(this);
return 0;

View file

@ -16,6 +16,7 @@
#include <spa/utils/result.h>
#include <spa/utils/list.h>
#include <spa/utils/json.h>
#include <spa/utils/json-builder.h>
#include <spa/utils/names.h>
#include <spa/utils/string.h>
#include <spa/utils/ratelimit.h>
@ -268,6 +269,7 @@ struct impl {
struct spa_list active_graphs;
struct filter_graph graphs[MAX_GRAPH];
struct spa_process_latency_info latency;
char *graph_descs[MAX_GRAPH];
int in_filter_props;
int filter_props_count;
@ -722,6 +724,34 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 19:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.center-level"),
SPA_PROP_INFO_description, SPA_POD_String("Center up/downmix level"),
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(
this->mix.center_level, 0.0, 10.0),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 20:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.surround-level"),
SPA_PROP_INFO_description, SPA_POD_String("Surround up/downmix level"),
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(
this->mix.surround_level, 0.0, 10.0),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 21:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.lfe-level"),
SPA_PROP_INFO_description, SPA_POD_String("LFE up/downmix level"),
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(
this->mix.lfe_level, 0.0, 10.0),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 22:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.hilbert-taps"),
@ -730,7 +760,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
this->mix.hilbert_taps, 0, MAX_TAPS),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 20:
case 23:
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_PropInfo, id);
spa_pod_builder_add(b,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.upmix-method"),
@ -749,14 +779,14 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
spa_pod_builder_pop(b, &f[1]);
*param = spa_pod_builder_pop(b, &f[0]);
break;
case 21:
case 24:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_rate),
SPA_PROP_INFO_description, SPA_POD_String("Rate scaler"),
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Double(p->rate, 0.0, 10.0));
break;
case 22:
case 25:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_quality),
@ -765,7 +795,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->resample_quality, 0, 14),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 23:
case 26:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("resample.disable"),
@ -773,7 +803,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->resample_disabled),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 24:
case 27:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("dither.noise"),
@ -781,7 +811,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(this->dir[1].conv.noise_bits, 0, 16),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 25:
case 28:
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_PropInfo, id);
spa_pod_builder_add(b,
SPA_PROP_INFO_name, SPA_POD_String("dither.method"),
@ -799,7 +829,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
spa_pod_builder_pop(b, &f[1]);
*param = spa_pod_builder_pop(b, &f[0]);
break;
case 26:
case 29:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("debug.wav-path"),
@ -807,7 +837,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_String(p->wav_path),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 27:
case 30:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.lock-volumes"),
@ -815,7 +845,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->lock_volumes),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 28:
case 31:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("audioconvert.filter-graph.disable"),
@ -823,7 +853,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->filter_graph_disabled),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 29:
case 32:
*param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("audioconvert.filter-graph.N"),
@ -834,7 +864,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
default:
if (this->filter_graph[0] && this->filter_graph[0]->graph) {
return spa_filter_graph_enum_prop_info(this->filter_graph[0]->graph,
index - 30, b, param);
index - 33, b, param);
}
return 0;
}
@ -846,6 +876,7 @@ static int node_param_props(struct impl *this, uint32_t id, uint32_t index,
{
struct props *p = &this->props;
struct spa_pod_frame f[2];
struct filter_graph *g;
switch (index) {
case 0:
@ -900,6 +931,12 @@ static int node_param_props(struct impl *this, uint32_t id, uint32_t index,
spa_pod_builder_float(b, this->mix.rear_delay);
spa_pod_builder_string(b, "channelmix.stereo-widen");
spa_pod_builder_float(b, this->mix.widen);
spa_pod_builder_string(b, "channelmix.center-level");
spa_pod_builder_float(b, this->mix.center_level);
spa_pod_builder_string(b, "channelmix.surround-level");
spa_pod_builder_float(b, this->mix.surround_level);
spa_pod_builder_string(b, "channelmix.lfe-level");
spa_pod_builder_float(b, this->mix.lfe_level);
spa_pod_builder_string(b, "channelmix.hilbert-taps");
spa_pod_builder_int(b, this->mix.hilbert_taps);
spa_pod_builder_string(b, "channelmix.upmix-method");
@ -918,8 +955,12 @@ static int node_param_props(struct impl *this, uint32_t id, uint32_t index,
spa_pod_builder_bool(b, p->lock_volumes);
spa_pod_builder_string(b, "audioconvert.filter-graph.disable");
spa_pod_builder_bool(b, p->filter_graph_disabled);
spa_pod_builder_string(b, "audioconvert.filter-graph");
spa_pod_builder_string(b, "");
spa_list_for_each(g, &this->active_graphs, link) {
char key[64];
snprintf(key, sizeof(key), "audioconvert.filter-graph.%d", g->order);
spa_pod_builder_string(b, key);
spa_pod_builder_string(b, this->graph_descs[g->order]);
}
spa_pod_builder_pop(b, &f[1]);
*param = spa_pod_builder_pop(b, &f[0]);
break;
@ -953,7 +994,7 @@ static int impl_node_enum_params(void *object, int seq,
struct impl *this = object;
struct spa_pod *param;
struct spa_pod_builder b = { 0 };
uint8_t buffer[4096];
uint8_t buffer[16384];
struct spa_result_node_params result;
uint32_t count = 0;
int res = 0;
@ -1409,6 +1450,7 @@ static int load_filter_graph(struct impl *impl, const char *graph, int order)
g->removing = true;
spa_log_info(impl->log, "removing filter-graph order:%d", order);
}
free(impl->graph_descs[order]);
}
if (graph != NULL && graph[0] != '\0') {
@ -1434,6 +1476,8 @@ static int load_filter_graph(struct impl *impl, const char *graph, int order)
spa_list_remove(&pending->link);
insert_graph(&impl->active_graphs, pending);
impl->graph_descs[order] = spa_json_builder_reformat(graph, 0);
spa_log_info(impl->log, "loading filter-graph order:%d", order);
}
if (impl->setup)
@ -1480,6 +1524,12 @@ static int audioconvert_set_param(struct impl *this, const char *k, const char *
spa_atof(s, &this->mix.rear_delay);
else if (spa_streq(k, "channelmix.stereo-widen"))
spa_atof(s, &this->mix.widen);
else if (spa_streq(k, "channelmix.center-level"))
spa_atof(s, &this->mix.center_level);
else if (spa_streq(k, "channelmix.surround-level"))
spa_atof(s, &this->mix.surround_level);
else if (spa_streq(k, "channelmix.lfe-level"))
spa_atof(s, &this->mix.lfe_level);
else if (spa_streq(k, "channelmix.hilbert-taps"))
spa_atou32(s, &this->mix.hilbert_taps, 0);
else if (spa_streq(k, "channelmix.upmix-method"))
@ -2115,7 +2165,7 @@ static int setup_in_convert(struct impl *this)
return res;
spa_log_debug(this->log, "%p: got converter features %08x:%08x passthrough:%d remap:%d %s", this,
this->cpu_flags, in->conv.cpu_flags, in->conv.is_passthrough,
this->cpu_flags, in->conv.func_cpu_flags, in->conv.is_passthrough,
remap, in->conv.func_name);
return 0;
@ -2272,7 +2322,7 @@ static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *posi
set_volume(this);
spa_log_debug(this->log, "%p: got channelmix features %08x:%08x flags:%08x %s",
this, this->cpu_flags, this->mix.cpu_flags,
this, this->cpu_flags, this->mix.func_cpu_flags,
this->mix.flags, this->mix.func_name);
return 0;
}
@ -2320,7 +2370,7 @@ static int setup_resample(struct impl *this)
res = resample_native_init(&this->resample);
spa_log_debug(this->log, "%p: got resample features %08x:%08x %s",
this, this->cpu_flags, this->resample.cpu_flags,
this, this->cpu_flags, this->resample.func_cpu_flags,
this->resample.func_name);
return res;
}
@ -2412,7 +2462,7 @@ static int setup_out_convert(struct impl *this)
spa_log_debug(this->log, "%p: got converter features %08x:%08x quant:%d:%d"
" passthrough:%d remap:%d %s", this,
this->cpu_flags, out->conv.cpu_flags, out->conv.method,
this->cpu_flags, out->conv.func_cpu_flags, out->conv.method,
out->conv.noise_bits, out->conv.is_passthrough, remap, out->conv.func_name);
return 0;
@ -4234,6 +4284,7 @@ static void free_dir(struct dir *dir)
static int impl_clear(struct spa_handle *handle)
{
struct impl *this;
int i;
spa_return_val_if_fail(handle != NULL, -EINVAL);
@ -4245,6 +4296,10 @@ static int impl_clear(struct spa_handle *handle)
free_tmp(this);
clean_filter_handles(this, true);
for (i = 0; i < MAX_GRAPH; i++) {
if (this->graph_descs[i])
free(this->graph_descs[i]);
}
if (this->resample.free)
resample_free(&this->resample);
@ -4299,18 +4354,13 @@ impl_init(const struct spa_handle_factory *factory,
struct filter_graph *g = &this->graphs[i];
g->impl = this;
spa_list_append(&this->free_graphs, &g->link);
this->graph_descs[i] = NULL;
}
this->rate_limit.interval = 2 * SPA_NSEC_PER_SEC;
this->rate_limit.burst = 1;
this->mix.options = CHANNELMIX_OPTION_UPMIX | CHANNELMIX_OPTION_MIX_LFE;
this->mix.upmix = CHANNELMIX_UPMIX_NONE;
this->mix.log = this->log;
this->mix.lfe_cutoff = 0.0f;
this->mix.fc_cutoff = 0.0f;
this->mix.rear_delay = 0.0f;
this->mix.widen = 0.0f;
channelmix_reset(&this->mix);
for (i = 0; info && i < info->n_items; i++) {
const char *k = info->items[i].key;

View file

@ -51,9 +51,9 @@ static void run_test1(const char *name, const char *impl, bool in_packed, bool o
void *op[n_channels];
struct timespec ts;
uint64_t count, t1, t2;
struct convert conv;
conv.n_channels = n_channels;
struct convert conv = {
.n_channels = n_channels,
};
for (j = 0; j < n_channels; j++) {
ip[j] = &samp_in[j * n_samples * 4];

View file

@ -0,0 +1,63 @@
/* Spa */
/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#include "channelmix-ops.h"
#include <immintrin.h>
#include <float.h>
#include <math.h>
static inline void clear_avx(float *d, uint32_t n_samples)
{
memset(d, 0, n_samples * sizeof(float));
}
static inline void copy_avx(float *d, const float *s, uint32_t n_samples)
{
spa_memcpy(d, s, n_samples * sizeof(float));
}
static inline void vol_avx(float *d, const float *s, float vol, uint32_t n_samples)
{
uint32_t n, unrolled;
if (vol == 0.0f) {
clear_avx(d, n_samples);
} else if (vol == 1.0f) {
copy_avx(d, s, n_samples);
} else {
__m256 t[4];
const __m256 v = _mm256_set1_ps(vol);
if (SPA_IS_ALIGNED(d, 32) &&
SPA_IS_ALIGNED(s, 32))
unrolled = n_samples & ~31;
else
unrolled = 0;
for(n = 0; n < unrolled; n += 32) {
t[0] = _mm256_load_ps(&s[n]);
t[1] = _mm256_load_ps(&s[n+8]);
t[2] = _mm256_load_ps(&s[n+16]);
t[3] = _mm256_load_ps(&s[n+24]);
_mm256_store_ps(&d[n], _mm256_mul_ps(t[0], v));
_mm256_store_ps(&d[n+8], _mm256_mul_ps(t[1], v));
_mm256_store_ps(&d[n+16], _mm256_mul_ps(t[2], v));
_mm256_store_ps(&d[n+24], _mm256_mul_ps(t[3], v));
}
for(; n < n_samples; n++) {
__m128 v = _mm_set1_ps(vol);
_mm_store_ss(&d[n], _mm_mul_ss(_mm_load_ss(&s[n]), v));
}
}
}
void channelmix_copy_avx(struct channelmix *mix, void * SPA_RESTRICT dst[],
const void * SPA_RESTRICT src[], uint32_t n_samples)
{
uint32_t i, n_dst = mix->dst_chan;
float **d = (float **)dst;
const float **s = (const float **)src;
for (i = 0; i < n_dst; i++)
vol_avx(d[i], s[i], mix->matrix[i][i], n_samples);
}

View file

@ -36,6 +36,11 @@ static const struct channelmix_info {
uint32_t cpu_flags;
} channelmix_table[] =
{
#if defined (HAVE_AVX)
MAKE(2, MASK_MONO, 2, MASK_MONO, channelmix_copy_avx, SPA_CPU_FLAG_AVX),
MAKE(2, MASK_STEREO, 2, MASK_STEREO, channelmix_copy_avx, SPA_CPU_FLAG_AVX),
MAKE(EQ, 0, EQ, 0, channelmix_copy_avx, SPA_CPU_FLAG_AVX),
#endif
#if defined (HAVE_SSE)
MAKE(2, MASK_MONO, 2, MASK_MONO, channelmix_copy_sse, SPA_CPU_FLAG_SSE),
MAKE(2, MASK_STEREO, 2, MASK_STEREO, channelmix_copy_sse, SPA_CPU_FLAG_SSE),
@ -193,9 +198,9 @@ static int make_matrix(struct channelmix *mix)
uint32_t dst_chan = mix->dst_chan;
uint64_t unassigned, keep;
uint32_t i, j, ic, jc, matrix_encoding = MATRIX_NORMAL;
float clev = SQRT1_2;
float slev = SQRT1_2;
float llev = 0.5f;
float clev = mix->center_level;
float slev = mix->surround_level;
float llev = mix->lfe_level;
float maxsum = 0.0f;
bool filter_fc = false, filter_lfe = false, matched = false, normalize;
#define _MATRIX(s,d) matrix[_CH(s)][_CH(d)]
@ -720,7 +725,7 @@ done:
if (src_paired == 0)
src_paired = ~0LU;
for (jc = 0, ic = 0, i = 0; ic < dst_chan; i++) {
for (jc = 0, ic = 0, i = 0; ic < dst_chan && i < MAX_CHANNELS; i++) {
float sum = 0.0f;
char str1[1024], str2[1024];
struct spa_strbuf sb1, sb2;
@ -730,7 +735,7 @@ done:
if (i < CHANNEL_BITS && (dst_paired & (1UL << i)) == 0)
continue;
for (jc = 0, j = 0; jc < src_chan; j++) {
for (jc = 0, j = 0; jc < src_chan && j < MAX_CHANNELS; j++) {
if (j < CHANNEL_BITS && (src_paired & (1UL << j)) == 0)
continue;
@ -869,6 +874,21 @@ static void impl_channelmix_free(struct channelmix *mix)
mix->process = NULL;
}
void channelmix_reset(struct channelmix *mix)
{
spa_zero(*mix);
mix->options = CHANNELMIX_DEFAULT_OPTIONS;
mix->upmix = CHANNELMIX_DEFAULT_UPMIX;
mix->lfe_cutoff = CHANNELMIX_DEFAULT_LFE_CUTOFF;
mix->fc_cutoff = CHANNELMIX_DEFAULT_FC_CUTOFF;
mix->rear_delay = CHANNELMIX_DEFAULT_REAR_DELAY;
mix->center_level = CHANNELMIX_DEFAULT_CENTER_LEVEL;
mix->surround_level = CHANNELMIX_DEFAULT_SURROUND_LEVEL;
mix->lfe_level = CHANNELMIX_DEFAULT_LFE_LEVEL;
mix->widen = CHANNELMIX_DEFAULT_WIDEN;
mix->hilbert_taps = CHANNELMIX_DEFAULT_HILBERT_TAPS;
}
int channelmix_init(struct channelmix *mix)
{
const struct channelmix_info *info;
@ -885,8 +905,8 @@ int channelmix_init(struct channelmix *mix)
mix->free = impl_channelmix_free;
mix->process = info->process;
mix->set_volume = impl_channelmix_set_volume;
mix->cpu_flags = info->cpu_flags;
mix->delay = (uint32_t)(mix->rear_delay * mix->freq / 1000.0f);
mix->func_cpu_flags = info->cpu_flags;
mix->func_name = info->name;
spa_zero(mix->taps_mem);

View file

@ -28,6 +28,17 @@
#define CHANNELMIX_OPS_MAX_ALIGN 16
#define CHANNELMIX_DEFAULT_OPTIONS (CHANNELMIX_OPTION_UPMIX | CHANNELMIX_OPTION_MIX_LFE)
#define CHANNELMIX_DEFAULT_UPMIX CHANNELMIX_UPMIX_NONE
#define CHANNELMIX_DEFAULT_LFE_CUTOFF 0.0f
#define CHANNELMIX_DEFAULT_FC_CUTOFF 0.0f
#define CHANNELMIX_DEFAULT_REAR_DELAY 0.0f
#define CHANNELMIX_DEFAULT_CENTER_LEVEL 0.707106781f
#define CHANNELMIX_DEFAULT_SURROUND_LEVEL 0.707106781f
#define CHANNELMIX_DEFAULT_LFE_LEVEL 0.5f
#define CHANNELMIX_DEFAULT_WIDEN 0.0f
#define CHANNELMIX_DEFAULT_HILBERT_TAPS 0
struct channelmix {
uint32_t src_chan;
uint32_t dst_chan;
@ -44,6 +55,7 @@ struct channelmix {
uint32_t upmix;
struct spa_log *log;
uint32_t func_cpu_flags;
const char *func_name;
#define CHANNELMIX_FLAG_ZERO (1<<0) /**< all zero components */
@ -59,6 +71,9 @@ struct channelmix {
float fc_cutoff; /* in Hz, 0 is disabled */
float rear_delay; /* in ms, 0 is disabled */
float widen; /* stereo widen. 0 is disabled */
float center_level; /* center down/upmix level, sqrt(1/2) */
float lfe_level; /* lfe down/upmix level, 1/2 */
float surround_level; /* surround down/upmix level, sqrt(1/2) */
uint32_t hilbert_taps; /* to phase shift, 0 disabled */
struct lr4 lr4[MAX_CHANNELS];
@ -79,6 +94,7 @@ struct channelmix {
void *data;
};
void channelmix_reset(struct channelmix *mix);
int channelmix_init(struct channelmix *mix);
static const struct channelmix_upmix_info {
@ -139,4 +155,8 @@ DEFINE_FUNCTION(f32_5p1_4, sse);
DEFINE_FUNCTION(f32_7p1_4, sse);
#endif
#if defined (HAVE_AVX)
DEFINE_FUNCTION(copy, avx);
#endif
#undef DEFINE_FUNCTION

View file

@ -4,6 +4,8 @@
#include "fmt-ops.h"
#include <spa/support/cpu.h>
#include <immintrin.h>
// GCC: workaround for missing AVX intrinsic: "_mm256_setr_m128()"
// (see https://stackoverflow.com/questions/32630458/setting-m256i-to-the-value-of-two-m128i-values)
@ -30,6 +32,38 @@
_mm256_srli_epi16(x, 8)); \
})
#define _MM_TRANS_1x4_PS(v0,v1,v2,v3) \
({ \
v1 = _mm_shuffle_ps(v0, v0, _MM_SHUFFLE(0, 3, 2, 1)); \
v2 = _mm_shuffle_ps(v0, v0, _MM_SHUFFLE(1, 0, 3, 2)); \
v3 = _mm_shuffle_ps(v0, v0, _MM_SHUFFLE(2, 1, 0, 3)); \
})
#define _MM_TRANS_1x4_EPI32(v0,v1,v2,v3) \
({ \
v1 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(0, 3, 2, 1)); \
v2 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(1, 0, 3, 2)); \
v3 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(2, 1, 0, 3)); \
})
#define _MM_STOREM_PS(d0,d1,d2,d3,v) \
({ \
__m128 o[3]; \
_MM_TRANS_1x4_PS(v, o[0], o[1], o[2]); \
_mm_store_ss(d0, v); \
_mm_store_ss(d1, o[0]); \
_mm_store_ss(d2, o[1]); \
_mm_store_ss(d3, o[2]); \
})
#define _MM_STOREM_EPI32(d0,d1,d2,d3,v) \
({ \
__m128i o[3]; \
_MM_TRANS_1x4_EPI32(v, o[0], o[1], o[2]); \
*d0 = _mm_cvtsi128_si32(v); \
*d1 = _mm_cvtsi128_si32(o[0]); \
*d2 = _mm_cvtsi128_si32(o[1]); \
*d3 = _mm_cvtsi128_si32(o[2]); \
})
static void
conv_s16_to_f32d_1s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
@ -253,7 +287,7 @@ conv_s16s_to_f32d_2_avx2(struct convert *conv, void * SPA_RESTRICT dst[], const
}
static void
conv_s24_to_f32d_1s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
conv_s24_to_f32d_1s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int8_t *s = src;
@ -289,7 +323,7 @@ conv_s24_to_f32d_1s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA
}
static void
conv_s24_to_f32d_2s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
conv_s24_to_f32d_2s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int8_t *s = src;
@ -341,7 +375,7 @@ conv_s24_to_f32d_2s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA
}
}
static void
conv_s24_to_f32d_4s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
conv_s24_to_f32d_4s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int8_t *s = src;
@ -397,18 +431,13 @@ conv_s24_to_f32d_4s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA
s += 12 * n_channels;
}
for(; n < n_samples; n++) {
out[0] = _mm_cvtsi32_ss(factor, s24_to_s32(*((int24_t*)s+0)));
out[1] = _mm_cvtsi32_ss(factor, s24_to_s32(*((int24_t*)s+1)));
out[2] = _mm_cvtsi32_ss(factor, s24_to_s32(*((int24_t*)s+2)));
out[3] = _mm_cvtsi32_ss(factor, s24_to_s32(*((int24_t*)s+3)));
out[0] = _mm_mul_ss(out[0], factor);
out[1] = _mm_mul_ss(out[1], factor);
out[2] = _mm_mul_ss(out[2], factor);
out[3] = _mm_mul_ss(out[3], factor);
_mm_store_ss(&d0[n], out[0]);
_mm_store_ss(&d1[n], out[1]);
_mm_store_ss(&d2[n], out[2]);
_mm_store_ss(&d3[n], out[3]);
in[0] = _mm_setr_epi32(s24_to_s32(*((int24_t*)s+0)),
s24_to_s32(*((int24_t*)s+1)),
s24_to_s32(*((int24_t*)s+2)),
s24_to_s32(*((int24_t*)s+3)));
out[0] = _mm_cvtepi32_ps(in[0]);
out[0] = _mm_mul_ps(out[0], factor);
_MM_STOREM_PS(&d0[n], &d1[n], &d2[n], &d3[n], out[0]);
s += 3 * n_channels;
}
}
@ -420,16 +449,22 @@ conv_s24_to_f32d_avx2(struct convert *conv, void * SPA_RESTRICT dst[], const voi
const int8_t *s = src[0];
uint32_t i = 0, n_channels = conv->n_channels;
for(; i + 3 < n_channels; i += 4)
conv_s24_to_f32d_4s_avx2(conv, &dst[i], &s[3*i], n_channels, n_samples);
for(; i + 1 < n_channels; i += 2)
conv_s24_to_f32d_2s_avx2(conv, &dst[i], &s[3*i], n_channels, n_samples);
for(; i < n_channels; i++)
conv_s24_to_f32d_1s_avx2(conv, &dst[i], &s[3*i], n_channels, n_samples);
if (conv->cpu_flags & SPA_CPU_FLAG_SLOW_GATHER) {
#if defined (HAVE_SSE2)
conv_s24_to_f32d_sse2(conv, dst, src, n_samples);
#endif
} else {
for(; i + 3 < n_channels; i += 4)
conv_s24_to_f32d_4s_gather_avx2(conv, &dst[i], &s[3*i], n_channels, n_samples);
for(; i + 1 < n_channels; i += 2)
conv_s24_to_f32d_2s_gather_avx2(conv, &dst[i], &s[3*i], n_channels, n_samples);
for(; i < n_channels; i++)
conv_s24_to_f32d_1s_gather_avx2(conv, &dst[i], &s[3*i], n_channels, n_samples);
}
}
static void
conv_s32_to_f32d_4s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
conv_s32_to_f32d_4s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int32_t *s = src;
@ -473,24 +508,17 @@ conv_s32_to_f32d_4s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA
}
for(; n < n_samples; n++) {
__m128 out[4], factor = _mm_set1_ps(1.0f / S32_SCALE_I2F);
out[0] = _mm_cvtsi32_ss(factor, s[0]);
out[1] = _mm_cvtsi32_ss(factor, s[1]);
out[2] = _mm_cvtsi32_ss(factor, s[2]);
out[3] = _mm_cvtsi32_ss(factor, s[3]);
out[0] = _mm_mul_ss(out[0], factor);
out[1] = _mm_mul_ss(out[1], factor);
out[2] = _mm_mul_ss(out[2], factor);
out[3] = _mm_mul_ss(out[3], factor);
_mm_store_ss(&d0[n], out[0]);
_mm_store_ss(&d1[n], out[1]);
_mm_store_ss(&d2[n], out[2]);
_mm_store_ss(&d3[n], out[3]);
__m128i in[1];
in[0] = _mm_setr_epi32(s[0], s[1], s[2], s[3]);
out[0] = _mm_cvtepi32_ps(in[0]);
out[0] = _mm_mul_ps(out[0], factor);
_MM_STOREM_PS(&d0[n], &d1[n], &d2[n], &d3[n], out[0]);
s += n_channels;
}
}
static void
conv_s32_to_f32d_2s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
conv_s32_to_f32d_2s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int32_t *s = src;
@ -535,7 +563,7 @@ conv_s32_to_f32d_2s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA
}
static void
conv_s32_to_f32d_1s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
conv_s32_to_f32d_1s_gather_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int32_t *s = src;
@ -575,6 +603,169 @@ conv_s32_to_f32d_1s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA
}
}
static void
conv_s32_to_f32d_2s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int32_t *s = src;
float *d0 = dst[0], *d1 = dst[1];
uint32_t n, unrolled;
__m256i in[4];
__m256 out[4], t[4], factor = _mm256_set1_ps(1.0f / S32_SCALE_I2F);
if (SPA_IS_ALIGNED(d0, 32) &&
SPA_IS_ALIGNED(d1, 32))
unrolled = n_samples & ~7;
else
unrolled = 0;
for(n = 0; n < unrolled; n += 8) {
in[0] = _mm256_setr_epi64x(
*((uint64_t*)&s[0*n_channels]),
*((uint64_t*)&s[1*n_channels]),
*((uint64_t*)&s[4*n_channels]),
*((uint64_t*)&s[5*n_channels]));
in[1] = _mm256_setr_epi64x(
*((uint64_t*)&s[2*n_channels]),
*((uint64_t*)&s[3*n_channels]),
*((uint64_t*)&s[6*n_channels]),
*((uint64_t*)&s[7*n_channels]));
out[0] = _mm256_cvtepi32_ps(in[0]);
out[1] = _mm256_cvtepi32_ps(in[1]);
out[0] = _mm256_mul_ps(out[0], factor); /* a0 b0 a1 b1 a4 b4 a5 b5 */
out[1] = _mm256_mul_ps(out[1], factor); /* a2 b2 a3 b3 a6 b6 a7 b7 */
t[0] = _mm256_unpacklo_ps(out[0], out[1]); /* a0 a2 b0 b2 a4 a6 b4 b6 */
t[1] = _mm256_unpackhi_ps(out[0], out[1]); /* a1 a3 b1 b3 a5 a7 b5 b7 */
out[0] = _mm256_unpacklo_ps(t[0], t[1]); /* a0 a1 a2 a3 a4 a5 a6 a7 */
out[1] = _mm256_unpackhi_ps(t[0], t[1]); /* b0 b1 b2 b3 b4 b5 b6 b7 */
_mm256_store_ps(&d0[n], out[0]);
_mm256_store_ps(&d1[n], out[1]);
s += 8*n_channels;
}
for(; n < n_samples; n++) {
__m128 out[2], factor = _mm_set1_ps(1.0f / S32_SCALE_I2F);
out[0] = _mm_cvtsi32_ss(factor, s[0]);
out[1] = _mm_cvtsi32_ss(factor, s[1]);
out[0] = _mm_mul_ss(out[0], factor);
out[1] = _mm_mul_ss(out[1], factor);
_mm_store_ss(&d0[n], out[0]);
_mm_store_ss(&d1[n], out[1]);
s += n_channels;
}
}
static void
conv_s32_to_f32d_1s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int32_t *s = src;
float *d0 = dst[0];
uint32_t n, unrolled;
__m256i in[2];
__m256 out[2], factor = _mm256_set1_ps(1.0f / S32_SCALE_I2F);
if (SPA_IS_ALIGNED(d0, 32))
unrolled = n_samples & ~7;
else
unrolled = 0;
for(n = 0; n < unrolled; n += 8) {
in[0] = _mm256_setr_epi32(
s[0*n_channels], s[1*n_channels],
s[2*n_channels], s[3*n_channels],
s[4*n_channels], s[5*n_channels],
s[6*n_channels], s[7*n_channels]);
out[0] = _mm256_cvtepi32_ps(in[0]);
out[0] = _mm256_mul_ps(out[0], factor);
_mm256_store_ps(&d0[n+0], out[0]);
s += 8*n_channels;
}
for(; n < n_samples; n++) {
__m128 out, factor = _mm_set1_ps(1.0f / S32_SCALE_I2F);
out = _mm_cvtsi32_ss(factor, s[0]);
out = _mm_mul_ss(out, factor);
_mm_store_ss(&d0[n], out);
s += n_channels;
}
}
static void
conv_s32_to_f32d_4s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int32_t *s = src;
float *d0 = dst[0], *d1 = dst[1], *d2 = dst[2], *d3 = dst[3];
uint32_t n, unrolled;
__m256i in[4];
__m256 out[4], t[4], factor = _mm256_set1_ps(1.0f / S32_SCALE_I2F);
if (SPA_IS_ALIGNED(d0, 32) &&
SPA_IS_ALIGNED(d1, 32) &&
SPA_IS_ALIGNED(d2, 32) &&
SPA_IS_ALIGNED(d3, 32))
unrolled = n_samples & ~7;
else
unrolled = 0;
for(n = 0; n < unrolled; n += 8) {
in[0] = _mm256_setr_m128i(
_mm_loadu_si128((__m128i*)&s[0*n_channels]),
_mm_loadu_si128((__m128i*)&s[4*n_channels]));
in[1] = _mm256_setr_m128i(
_mm_loadu_si128((__m128i*)&s[1*n_channels]),
_mm_loadu_si128((__m128i*)&s[5*n_channels]));
in[2] = _mm256_setr_m128i(
_mm_loadu_si128((__m128i*)&s[2*n_channels]),
_mm_loadu_si128((__m128i*)&s[6*n_channels]));
in[3] = _mm256_setr_m128i(
_mm_loadu_si128((__m128i*)&s[3*n_channels]),
_mm_loadu_si128((__m128i*)&s[7*n_channels]));
out[0] = _mm256_cvtepi32_ps(in[0]); /* a0 b0 c0 d0 a4 b4 c4 d4 */
out[1] = _mm256_cvtepi32_ps(in[1]); /* a1 b1 c1 d1 a5 b5 c5 d5 */
out[2] = _mm256_cvtepi32_ps(in[2]); /* a2 b2 c2 d2 a6 b6 c6 d6 */
out[3] = _mm256_cvtepi32_ps(in[3]); /* a3 b3 c3 d3 a7 b7 c7 d7 */
out[0] = _mm256_mul_ps(out[0], factor);
out[1] = _mm256_mul_ps(out[1], factor);
out[2] = _mm256_mul_ps(out[2], factor);
out[3] = _mm256_mul_ps(out[3], factor);
t[0] = _mm256_unpacklo_ps(out[0], out[2]); /* a0 a2 b0 b2 a4 a6 b4 b6 */
t[1] = _mm256_unpackhi_ps(out[0], out[2]); /* c0 c2 d0 d2 c4 c6 d4 d6 */
t[2] = _mm256_unpacklo_ps(out[1], out[3]); /* a1 a3 b1 b3 a5 a7 b5 b7 */
t[3] = _mm256_unpackhi_ps(out[1], out[3]); /* c1 c3 d1 d3 c5 c7 d5 d7 */
out[0] = _mm256_unpacklo_ps(t[0], t[2]); /* a0 a1 a2 a3 a4 a5 a6 a7 */
out[1] = _mm256_unpackhi_ps(t[0], t[2]); /* b0 b1 b2 b3 b4 b5 b6 b7 */
out[2] = _mm256_unpacklo_ps(t[1], t[3]); /* c0 c1 c2 c3 c4 c5 c6 c7 */
out[3] = _mm256_unpackhi_ps(t[1], t[3]); /* d0 d1 d2 d3 d4 d5 d6 d7 */
_mm256_store_ps(&d0[n], out[0]);
_mm256_store_ps(&d1[n], out[1]);
_mm256_store_ps(&d2[n], out[2]);
_mm256_store_ps(&d3[n], out[3]);
s += 8*n_channels;
}
for(; n < n_samples; n++) {
__m128 out[4], factor = _mm_set1_ps(1.0f / S32_SCALE_I2F);
__m128i in[1];
in[0] = _mm_setr_epi32(s[0], s[1], s[2], s[3]);
out[0] = _mm_cvtepi32_ps(in[0]);
out[0] = _mm_mul_ps(out[0], factor);
_MM_STOREM_PS(&d0[n], &d1[n], &d2[n], &d3[n], out[0]);
s += n_channels;
}
}
void
conv_s32_to_f32d_avx2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[],
uint32_t n_samples)
@ -582,12 +773,21 @@ conv_s32_to_f32d_avx2(struct convert *conv, void * SPA_RESTRICT dst[], const voi
const int32_t *s = src[0];
uint32_t i = 0, n_channels = conv->n_channels;
for(; i + 3 < n_channels; i += 4)
conv_s32_to_f32d_4s_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
for(; i + 1 < n_channels; i += 2)
conv_s32_to_f32d_2s_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
for(; i < n_channels; i++)
conv_s32_to_f32d_1s_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
if (conv->cpu_flags & SPA_CPU_FLAG_SLOW_GATHER) {
for(; i + 3 < n_channels; i += 4)
conv_s32_to_f32d_4s_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
for(; i + 1 < n_channels; i += 2)
conv_s32_to_f32d_2s_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
for(; i < n_channels; i++)
conv_s32_to_f32d_1s_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
} else {
for(; i + 3 < n_channels; i += 4)
conv_s32_to_f32d_4s_gather_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
for(; i + 1 < n_channels; i += 2)
conv_s32_to_f32d_2s_gather_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
for(; i < n_channels; i++)
conv_s32_to_f32d_1s_gather_avx2(conv, &dst[i], &s[i], n_channels, n_samples);
}
}
static void
@ -612,14 +812,10 @@ conv_f32d_to_s32_1s_avx2(void *data, void * SPA_RESTRICT dst, const void * SPA_R
in[0] = _mm_mul_ps(_mm_load_ps(&s0[n]), scale);
in[0] = _MM_CLAMP_PS(in[0], int_min, int_max);
out[0] = _mm_cvtps_epi32(in[0]);
out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1));
out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2));
out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3));
d[0*n_channels] = _mm_cvtsi128_si32(out[0]);
d[1*n_channels] = _mm_cvtsi128_si32(out[1]);
d[2*n_channels] = _mm_cvtsi128_si32(out[2]);
d[3*n_channels] = _mm_cvtsi128_si32(out[3]);
_MM_STOREM_EPI32(&d[0*n_channels],
&d[1*n_channels],
&d[2*n_channels],
&d[3*n_channels], out[0]);
d += 4*n_channels;
}
for(; n < n_samples; n++) {
@ -774,15 +970,7 @@ conv_f32d_to_s32_4s_avx2(void *data, void * SPA_RESTRICT dst, const void * SPA_R
__m128 int_min = _mm_set1_ps(S32_MIN_F2I);
__m128 int_max = _mm_set1_ps(S32_MAX_F2I);
in[0] = _mm_load_ss(&s0[n]);
in[1] = _mm_load_ss(&s1[n]);
in[2] = _mm_load_ss(&s2[n]);
in[3] = _mm_load_ss(&s3[n]);
in[0] = _mm_unpacklo_ps(in[0], in[2]);
in[1] = _mm_unpacklo_ps(in[1], in[3]);
in[0] = _mm_unpacklo_ps(in[0], in[1]);
in[0] = _mm_setr_ps(s0[n], s1[n], s2[n], s3[n]);
in[0] = _mm_mul_ps(in[0], scale);
in[0] = _MM_CLAMP_PS(in[0], int_min, int_max);
out[0] = _mm_cvtps_epi32(in[0]);
@ -972,18 +1160,16 @@ conv_f32d_to_s16_4s_avx2(void *data, void * SPA_RESTRICT dst, const void * SPA_R
__m128 int_max = _mm_set1_ps(S16_MAX);
__m128 int_min = _mm_set1_ps(S16_MIN);
in[0] = _mm_mul_ss(_mm_load_ss(&s0[n]), int_scale);
in[1] = _mm_mul_ss(_mm_load_ss(&s1[n]), int_scale);
in[2] = _mm_mul_ss(_mm_load_ss(&s2[n]), int_scale);
in[3] = _mm_mul_ss(_mm_load_ss(&s3[n]), int_scale);
in[0] = _MM_CLAMP_SS(in[0], int_min, int_max);
in[1] = _MM_CLAMP_SS(in[1], int_min, int_max);
in[2] = _MM_CLAMP_SS(in[2], int_min, int_max);
in[3] = _MM_CLAMP_SS(in[3], int_min, int_max);
in[0] = _mm_setr_ps(s0[n], s1[n], s2[n], s3[n]);
in[0] = _mm_mul_ps(in[0], int_scale);
in[0] = _MM_CLAMP_PS(in[0], int_min, int_max);
_MM_TRANS_1x4_PS(in[0], in[1], in[2], in[3]);
d[0] = _mm_cvtss_si32(in[0]);
d[1] = _mm_cvtss_si32(in[1]);
d[2] = _mm_cvtss_si32(in[2]);
d[3] = _mm_cvtss_si32(in[3]);
d += n_channels;
}
}
@ -1055,14 +1241,10 @@ conv_f32d_to_s16_4_avx2(struct convert *conv, void * SPA_RESTRICT dst[], const v
__m128 int_max = _mm_set1_ps(S16_MAX);
__m128 int_min = _mm_set1_ps(S16_MIN);
in[0] = _mm_mul_ss(_mm_load_ss(&s0[n]), int_scale);
in[1] = _mm_mul_ss(_mm_load_ss(&s1[n]), int_scale);
in[2] = _mm_mul_ss(_mm_load_ss(&s2[n]), int_scale);
in[3] = _mm_mul_ss(_mm_load_ss(&s3[n]), int_scale);
in[0] = _MM_CLAMP_SS(in[0], int_min, int_max);
in[1] = _MM_CLAMP_SS(in[1], int_min, int_max);
in[2] = _MM_CLAMP_SS(in[2], int_min, int_max);
in[3] = _MM_CLAMP_SS(in[3], int_min, int_max);
in[0] = _mm_setr_ps(s0[n], s1[n], s2[n], s3[n]);
in[0] = _mm_mul_ps(in[0], int_scale);
in[0] = _MM_CLAMP_PS(in[0], int_min, int_max);
_MM_TRANS_1x4_PS(in[0], in[1], in[2], in[3]);
d[0] = _mm_cvtss_si32(in[0]);
d[1] = _mm_cvtss_si32(in[1]);
d[2] = _mm_cvtss_si32(in[2]);
@ -1185,3 +1367,4 @@ conv_f32d_to_s16s_2_avx2(struct convert *conv, void * SPA_RESTRICT dst[], const
d += 2;
}
}

View file

@ -26,6 +26,72 @@
a = _mm_shufflehi_epi16(a, _MM_SHUFFLE(2, 3, 0, 1)); \
})
#define spa_read_unaligned(ptr, type) \
__extension__ ({ \
__typeof__(type) _val; \
memcpy(&_val, (ptr), sizeof(_val)); \
_val; \
})
#define spa_write_unaligned(ptr, type, val) \
__extension__ ({ \
__typeof__(type) _val = (val); \
memcpy((ptr), &_val, sizeof(_val)); \
})
#define _MM_TRANS_1x4_PS(v0,v1,v2,v3) \
({ \
v1 = _mm_shuffle_ps(v0, v0, _MM_SHUFFLE(0, 3, 2, 1)); \
v2 = _mm_shuffle_ps(v0, v0, _MM_SHUFFLE(1, 0, 3, 2)); \
v3 = _mm_shuffle_ps(v0, v0, _MM_SHUFFLE(2, 1, 0, 3)); \
})
#define _MM_TRANS_1x4_EPI32(v0,v1,v2,v3) \
({ \
v1 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(0, 3, 2, 1)); \
v2 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(1, 0, 3, 2)); \
v3 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(2, 1, 0, 3)); \
})
#if 0
#define _MM_STOREM_PS(d0,d1,d2,d3,v) \
({ \
*d0 = v[0]; \
*d1 = v[1]; \
*d2 = v[2]; \
*d3 = v[3]; \
})
#else
#define _MM_STOREM_PS(d0,d1,d2,d3,v) \
({ \
__m128 o[3]; \
_MM_TRANS_1x4_PS(v, o[0], o[1], o[2]); \
_mm_store_ss(d0, v); \
_mm_store_ss(d1, o[0]); \
_mm_store_ss(d2, o[1]); \
_mm_store_ss(d3, o[2]); \
})
#endif
#define _MM_STOREM_EPI32(d0,d1,d2,d3,v) \
({ \
__m128i o[3]; \
_MM_TRANS_1x4_EPI32(v, o[0], o[1], o[2]); \
*d0 = _mm_cvtsi128_si32(v); \
*d1 = _mm_cvtsi128_si32(o[0]); \
*d2 = _mm_cvtsi128_si32(o[1]); \
*d3 = _mm_cvtsi128_si32(o[2]); \
})
#define _MM_STOREUM_EPI32(d0,d1,d2,d3,v) \
({ \
__m128i o[3]; \
_MM_TRANS_1x4_EPI32(v, o[0], o[1], o[2]); \
spa_write_unaligned(d0, uint32_t, _mm_cvtsi128_si32(v)); \
spa_write_unaligned(d1, uint32_t, _mm_cvtsi128_si32(o[0])); \
spa_write_unaligned(d2, uint32_t, _mm_cvtsi128_si32(o[1])); \
spa_write_unaligned(d3, uint32_t, _mm_cvtsi128_si32(o[2])); \
})
static void
conv_s16_to_f32d_1s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
@ -233,18 +299,6 @@ conv_s16s_to_f32d_2_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const
}
}
#define spa_read_unaligned(ptr, type) \
__extension__ ({ \
__typeof__(type) _val; \
memcpy(&_val, (ptr), sizeof(_val)); \
_val; \
})
#define spa_write_unaligned(ptr, type, val) \
__extension__ ({ \
__typeof__(type) _val = (val); \
memcpy((ptr), &_val, sizeof(_val)); \
})
void
conv_s24_to_f32d_1s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
@ -416,18 +470,13 @@ conv_s24_to_f32d_4s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA
s += 4 * n_channels;
}
for(; n < n_samples; n++) {
out[0] = _mm_cvtsi32_ss(factor, s24_to_s32(*s));
out[1] = _mm_cvtsi32_ss(factor, s24_to_s32(*(s+1)));
out[2] = _mm_cvtsi32_ss(factor, s24_to_s32(*(s+2)));
out[3] = _mm_cvtsi32_ss(factor, s24_to_s32(*(s+3)));
out[0] = _mm_mul_ss(out[0], factor);
out[1] = _mm_mul_ss(out[1], factor);
out[2] = _mm_mul_ss(out[2], factor);
out[3] = _mm_mul_ss(out[3], factor);
_mm_store_ss(&d0[n], out[0]);
_mm_store_ss(&d1[n], out[1]);
_mm_store_ss(&d2[n], out[2]);
_mm_store_ss(&d3[n], out[3]);
in[0] = _mm_setr_epi32(s24_to_s32(*s),
s24_to_s32(*(s+1)),
s24_to_s32(*(s+2)),
s24_to_s32(*(s+3)));
out[0] = _mm_cvtepi32_ps(in[0]);
out[0] = _mm_mul_ps(out[0], factor);
_MM_STOREM_PS(&d0[n], &d1[n], &d2[n], &d3[n], out[0]);
s += n_channels;
}
}
@ -447,6 +496,59 @@ conv_s24_to_f32d_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const voi
conv_s24_to_f32d_1s_sse2(conv, &dst[i], &s[3*i], n_channels, n_samples);
}
static void
conv_s32_to_f32d_4s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
{
const int32_t *s = src;
float *d0 = dst[0], *d1 = dst[1], *d2 = dst[2], *d3 = dst[3];
uint32_t n, unrolled;
__m128i in[4];
__m128 out[4], factor = _mm_set1_ps(1.0f / S32_SCALE_I2F);
if (SPA_IS_ALIGNED(d0, 16) &&
SPA_IS_ALIGNED(d1, 16) &&
SPA_IS_ALIGNED(d2, 16) &&
SPA_IS_ALIGNED(d3, 16) &&
SPA_IS_ALIGNED(s, 16) && (n_channels & 3) == 0)
unrolled = n_samples & ~3;
else
unrolled = 0;
for(n = 0; n < unrolled; n += 4) {
in[0] = _mm_load_si128((__m128i*)(s + 0*n_channels));
in[1] = _mm_load_si128((__m128i*)(s + 1*n_channels));
in[2] = _mm_load_si128((__m128i*)(s + 2*n_channels));
in[3] = _mm_load_si128((__m128i*)(s + 3*n_channels));
out[0] = _mm_cvtepi32_ps(in[0]);
out[1] = _mm_cvtepi32_ps(in[1]);
out[2] = _mm_cvtepi32_ps(in[2]);
out[3] = _mm_cvtepi32_ps(in[3]);
out[0] = _mm_mul_ps(out[0], factor);
out[1] = _mm_mul_ps(out[1], factor);
out[2] = _mm_mul_ps(out[2], factor);
out[3] = _mm_mul_ps(out[3], factor);
_MM_TRANSPOSE4_PS(out[0], out[1], out[2], out[3]);
_mm_store_ps(&d0[n], out[0]);
_mm_store_ps(&d1[n], out[1]);
_mm_store_ps(&d2[n], out[2]);
_mm_store_ps(&d3[n], out[3]);
s += 4*n_channels;
}
for(; n < n_samples; n++) {
in[0] = _mm_setr_epi32(s[0], s[1], s[2], s[3]);
out[0] = _mm_cvtepi32_ps(in[0]);
out[0] = _mm_mul_ps(out[0], factor);
_MM_STOREM_PS(&d0[n], &d1[n], &d2[n], &d3[n], out[0]);
s += n_channels;
}
}
static void
conv_s32_to_f32d_1s_sse2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples)
@ -487,6 +589,8 @@ conv_s32_to_f32d_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const voi
const int32_t *s = src[0];
uint32_t i = 0, n_channels = conv->n_channels;
for(; i + 3 < n_channels; i += 4)
conv_s32_to_f32d_4s_sse2(conv, &dst[i], &s[i], n_channels, n_samples);
for(; i < n_channels; i++)
conv_s32_to_f32d_1s_sse2(conv, &dst[i], &s[i], n_channels, n_samples);
}
@ -513,14 +617,10 @@ conv_f32d_to_s32_1s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_R
in[0] = _mm_mul_ps(_mm_load_ps(&s0[n]), scale);
in[0] = _MM_CLAMP_PS(in[0], int_min, int_max);
out[0] = _mm_cvtps_epi32(in[0]);
out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1));
out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2));
out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3));
d[0*n_channels] = _mm_cvtsi128_si32(out[0]);
d[1*n_channels] = _mm_cvtsi128_si32(out[1]);
d[2*n_channels] = _mm_cvtsi128_si32(out[2]);
d[3*n_channels] = _mm_cvtsi128_si32(out[3]);
_MM_STOREM_EPI32(&d[0*n_channels],
&d[1*n_channels],
&d[2*n_channels],
&d[3*n_channels], out[0]);
d += 4*n_channels;
}
for(; n < n_samples; n++) {
@ -630,15 +730,7 @@ conv_f32d_to_s32_4s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_R
d += 4*n_channels;
}
for(; n < n_samples; n++) {
in[0] = _mm_load_ss(&s0[n]);
in[1] = _mm_load_ss(&s1[n]);
in[2] = _mm_load_ss(&s2[n]);
in[3] = _mm_load_ss(&s3[n]);
in[0] = _mm_unpacklo_ps(in[0], in[2]);
in[1] = _mm_unpacklo_ps(in[1], in[3]);
in[0] = _mm_unpacklo_ps(in[0], in[1]);
in[0] = _mm_setr_ps(s0[n], s1[n], s2[n], s3[n]);
in[0] = _mm_mul_ps(in[0], scale);
in[0] = _MM_CLAMP_PS(in[0], int_min, int_max);
out[0] = _mm_cvtps_epi32(in[0]);
@ -754,14 +846,10 @@ conv_f32d_to_s32_1s_noise_sse2(struct convert *conv, void * SPA_RESTRICT dst, co
in[0] = _mm_add_ps(in[0], _mm_load_ps(&noise[n]));
in[0] = _MM_CLAMP_PS(in[0], int_min, int_max);
out[0] = _mm_cvtps_epi32(in[0]);
out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1));
out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2));
out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3));
d[0*n_channels] = _mm_cvtsi128_si32(out[0]);
d[1*n_channels] = _mm_cvtsi128_si32(out[1]);
d[2*n_channels] = _mm_cvtsi128_si32(out[2]);
d[3*n_channels] = _mm_cvtsi128_si32(out[3]);
_MM_STOREM_EPI32(&d[0*n_channels],
&d[1*n_channels],
&d[2*n_channels],
&d[3*n_channels], out[0]);
d += 4*n_channels;
}
for(; n < n_samples; n++) {
@ -810,14 +898,10 @@ conv_interleave_32_1s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA
for(n = 0; n < unrolled; n += 4) {
out[0] = _mm_load_si128((__m128i*)&s0[n]);
out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1));
out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2));
out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3));
d[0*n_channels] = _mm_cvtsi128_si32(out[0]);
d[1*n_channels] = _mm_cvtsi128_si32(out[1]);
d[2*n_channels] = _mm_cvtsi128_si32(out[2]);
d[3*n_channels] = _mm_cvtsi128_si32(out[3]);
_MM_STOREM_EPI32(&d[0*n_channels],
&d[1*n_channels],
&d[2*n_channels],
&d[3*n_channels], out[0]);
d += 4*n_channels;
}
for(; n < n_samples; n++) {
@ -893,14 +977,10 @@ conv_interleave_32s_1s_sse2(void *data, void * SPA_RESTRICT dst, const void * SP
for(n = 0; n < unrolled; n += 4) {
out[0] = _mm_load_si128((__m128i*)&s0[n]);
out[0] = _MM_BSWAP_EPI32(out[0]);
out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1));
out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2));
out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3));
d[0*n_channels] = _mm_cvtsi128_si32(out[0]);
d[1*n_channels] = _mm_cvtsi128_si32(out[1]);
d[2*n_channels] = _mm_cvtsi128_si32(out[2]);
d[3*n_channels] = _mm_cvtsi128_si32(out[3]);
_MM_STOREM_EPI32(&d[0*n_channels],
&d[1*n_channels],
&d[2*n_channels],
&d[3*n_channels], out[0]);
d += 4*n_channels;
}
for(; n < n_samples; n++) {
@ -1257,14 +1337,10 @@ conv_f32d_to_s16_2s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_R
t[1] = _mm_packs_epi32(t[1], t[1]);
out[0] = _mm_unpacklo_epi16(t[0], t[1]);
out[1] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(0, 3, 2, 1));
out[2] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(1, 0, 3, 2));
out[3] = _mm_shuffle_epi32(out[0], _MM_SHUFFLE(2, 1, 0, 3));
spa_write_unaligned(d + 0*n_channels, uint32_t, _mm_cvtsi128_si32(out[0]));
spa_write_unaligned(d + 1*n_channels, uint32_t, _mm_cvtsi128_si32(out[1]));
spa_write_unaligned(d + 2*n_channels, uint32_t, _mm_cvtsi128_si32(out[2]));
spa_write_unaligned(d + 3*n_channels, uint32_t, _mm_cvtsi128_si32(out[3]));
_MM_STOREUM_EPI32(&d[0*n_channels],
&d[1*n_channels],
&d[2*n_channels],
&d[3*n_channels], out[0]);
d += 4*n_channels;
}
for(; n < n_samples; n++) {

View file

@ -631,7 +631,7 @@ int convert_init(struct convert *conv)
conv->random[i] = random();
conv->is_passthrough = conv->src_fmt == conv->dst_fmt;
conv->cpu_flags = info->cpu_flags;
conv->func_cpu_flags = info->cpu_flags;
conv->update_noise = ninfo->noise;
conv->process = info->process;
conv->clear = cinfo ? cinfo->clear : NULL;

View file

@ -219,6 +219,7 @@ struct convert {
uint32_t n_channels;
uint32_t rate;
uint32_t cpu_flags;
uint32_t func_cpu_flags;
const char *func_name;
unsigned int is_passthrough:1;

View file

@ -44,7 +44,7 @@ endif
if have_sse2
audioconvert_sse2 = static_library('audioconvert_sse2',
['fmt-ops-sse2.c' ],
c_args : [sse2_args, '-O3', '-DHAVE_SSE2'],
c_args : [sse2_args, '-O3', '-DHAVE_SSE2', simd_cargs],
dependencies : [ spa_dep ],
install : false
)
@ -55,7 +55,7 @@ if have_ssse3
audioconvert_ssse3 = static_library('audioconvert_ssse3',
['fmt-ops-ssse3.c',
'resample-native-ssse3.c' ],
c_args : [ssse3_args, '-O3', '-DHAVE_SSSE3'],
c_args : [ssse3_args, '-O3', '-DHAVE_SSSE3', simd_cargs],
dependencies : [ spa_dep ],
install : false
)
@ -65,17 +65,27 @@ endif
if have_sse41
audioconvert_sse41 = static_library('audioconvert_sse41',
['fmt-ops-sse41.c'],
c_args : [sse41_args, '-O3', '-DHAVE_SSE41'],
c_args : [sse41_args, '-O3', '-DHAVE_SSE41', simd_cargs],
dependencies : [ spa_dep ],
install : false
)
simd_cargs += ['-DHAVE_SSE41']
simd_dependencies += audioconvert_sse41
endif
if have_avx
audioconvert_avx = static_library('audioconvert_avx',
['channelmix-ops-avx.c'],
c_args : [avx_args, '-O3', '-DHAVE_AVX', simd_cargs],
dependencies : [ spa_dep ],
install : false
)
simd_cargs += ['-DHAVE_AVX']
simd_dependencies += audioconvert_avx
endif
if have_avx2 and have_fma
audioconvert_avx2_fma = static_library('audioconvert_avx2_fma',
['resample-native-avx2.c'],
c_args : [avx2_args, fma_args, '-O3', '-DHAVE_AVX2', '-DHAVE_FMA'],
c_args : [avx2_args, fma_args, '-O3', '-DHAVE_AVX2', '-DHAVE_FMA', simd_cargs],
dependencies : [ spa_dep ],
install : false
)
@ -85,7 +95,7 @@ endif
if have_avx2
audioconvert_avx2 = static_library('audioconvert_avx2',
['fmt-ops-avx2.c'],
c_args : [avx2_args, '-O3', '-DHAVE_AVX2'],
c_args : [avx2_args, '-O3', '-DHAVE_AVX2', simd_cargs],
dependencies : [ spa_dep ],
install : false
)

View file

@ -60,7 +60,7 @@ int peaks_init(struct peaks *peaks)
if (info == NULL)
return -ENOTSUP;
peaks->cpu_flags = info->cpu_flags;
peaks->func_cpu_flags = info->cpu_flags;
peaks->func_name = info->name;
peaks->free = impl_peaks_free;
peaks->min_max = info->min_max;

View file

@ -14,6 +14,7 @@ extern struct spa_log_topic resample_log_topic;
struct peaks {
uint32_t cpu_flags;
uint32_t func_cpu_flags;
const char *func_name;
struct spa_log *log;

View file

@ -576,7 +576,7 @@ int resample_native_init(struct resample *r)
r, c->cutoff, r->quality, c->window, r->i_rate, r->o_rate, gcd, n_taps, n_phases,
r->cpu_flags, d->info->cpu_flags);
r->cpu_flags = d->info->cpu_flags;
r->func_cpu_flags = d->info->cpu_flags;
impl_native_reset(r);
impl_native_update_rate(r, 1.0);

View file

@ -38,6 +38,7 @@ struct resample {
#define RESAMPLE_OPTION_PREFILL (1<<0)
uint32_t options;
uint32_t cpu_flags;
uint32_t func_cpu_flags;
const char *func_name;
uint32_t channels;

View file

@ -54,7 +54,7 @@ static int setup_context(struct context *ctx)
size_t size;
int res;
struct spa_support support[1];
struct spa_dict_item items[6];
struct spa_dict_item items[9];
const struct spa_handle_factory *factory;
void *iface;
@ -76,10 +76,13 @@ static int setup_context(struct context *ctx)
items[3] = SPA_DICT_ITEM_INIT("channelmix.lfe-cutoff", "150");
items[4] = SPA_DICT_ITEM_INIT("channelmix.fc-cutoff", "12000");
items[5] = SPA_DICT_ITEM_INIT("channelmix.rear-delay", "12.0");
items[6] = SPA_DICT_ITEM_INIT("channelmix.center-level", "0.707106781");
items[7] = SPA_DICT_ITEM_INIT("channelmix.surround-level", "0.707106781");
items[8] = SPA_DICT_ITEM_INIT("channelmix.lfe-level", "0.5");
res = spa_handle_factory_init(factory,
ctx->convert_handle,
&SPA_DICT_INIT(items, 6),
&SPA_DICT_INIT(items, 9),
support, 1);
spa_assert_se(res >= 0);

View file

@ -45,7 +45,7 @@ static void test_mix(uint32_t src_chan, uint32_t src_mask, uint32_t dst_chan, ui
spa_log_debug(&logger.log, "start %d->%d (%08x -> %08x)", src_chan, dst_chan, src_mask, dst_mask);
spa_zero(mix);
channelmix_reset(&mix);
mix.options = options;
mix.src_chan = src_chan;
mix.dst_chan = dst_chan;
@ -340,7 +340,7 @@ static void test_n_m_impl(void)
src[i] = src_data[i];
}
spa_zero(mix);
channelmix_reset(&mix);
mix.src_chan = 16;
mix.dst_chan = 12;
mix.log = &logger.log;

View file

@ -45,9 +45,9 @@ static void run_test(const char *name,
void *tp[N_CHANNELS];
int i, j;
const uint8_t *in8 = in, *out8 = out;
struct convert conv;
conv.n_channels = N_CHANNELS;
struct convert conv = {
.n_channels = N_CHANNELS,
};
for (j = 0; j < N_SAMPLES; j++) {
memcpy(&samp_in[j * in_size], &in8[(j % n_samples) * in_size], in_size);

View file

@ -56,7 +56,7 @@ int volume_init(struct volume *vol)
if (info == NULL)
return -ENOTSUP;
vol->cpu_flags = info->cpu_flags;
vol->func_cpu_flags = info->cpu_flags;
vol->func_name = info->name;
vol->free = impl_volume_free;
vol->process = info->process;

View file

@ -13,6 +13,7 @@
struct volume {
uint32_t cpu_flags;
uint32_t func_cpu_flags;
const char *func_name;
struct spa_log *log;

View file

@ -725,7 +725,7 @@ static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq,
port->io[0] = info->data;
port->io[1] = info->data;
}
if (!port->active) {
if (port->direction == SPA_DIRECTION_INPUT && !port->active) {
spa_list_append(&info->impl->mix_list, &port->mix_link);
port->active = true;
}

View file

@ -718,7 +718,7 @@ static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq,
port->io[0] = info->data;
port->io[1] = info->data;
}
if (!port->active) {
if (port->direction == SPA_DIRECTION_INPUT && !port->active) {
spa_list_append(&info->impl->mix_list, &port->mix_link);
port->active = true;
}

View file

@ -41,13 +41,11 @@ enum wave_type {
#define DEFAULT_RATE 48000
#define DEFAULT_CHANNELS 2
#define DEFAULT_LIVE true
#define DEFAULT_WAVE WAVE_SINE
#define DEFAULT_FREQ 440.0
#define DEFAULT_VOLUME 1.0
struct props {
bool live;
uint32_t wave;
float freq;
float volume;
@ -55,7 +53,6 @@ struct props {
static void reset_props(struct props *props)
{
props->live = DEFAULT_LIVE;
props->wave = DEFAULT_WAVE;
props->freq = DEFAULT_FREQ;
props->volume = DEFAULT_VOLUME;
@ -118,9 +115,7 @@ struct impl {
struct spa_hook_list hooks;
struct spa_callbacks callbacks;
bool async;
struct spa_source timer_source;
struct itimerspec timerspec;
bool started;
uint64_t start_time;
@ -162,13 +157,6 @@ static int impl_node_enum_params(void *object, int seq,
switch (result.index) {
case 0:
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_live),
SPA_PROP_INFO_description, SPA_POD_String("Configure live mode of the source"),
SPA_PROP_INFO_type, SPA_POD_Bool(p->live));
break;
case 1:
spa_pod_builder_push_object(&b, &f[0], SPA_TYPE_OBJECT_PropInfo, id);
spa_pod_builder_add(&b,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_waveType),
@ -184,14 +172,14 @@ static int impl_node_enum_params(void *object, int seq,
spa_pod_builder_pop(&b, &f[1]);
param = spa_pod_builder_pop(&b, &f[0]);
break;
case 2:
case 1:
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_frequency),
SPA_PROP_INFO_description, SPA_POD_String("Select the frequency"),
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->freq, 0.0, 50000000.0));
break;
case 3:
case 2:
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_volume),
@ -211,7 +199,6 @@ static int impl_node_enum_params(void *object, int seq,
case 0:
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Props, id,
SPA_PROP_live, SPA_POD_Bool(p->live),
SPA_PROP_waveType, SPA_POD_Int(p->wave),
SPA_PROP_frequency, SPA_POD_Float(p->freq),
SPA_PROP_volume, SPA_POD_Float(p->volume));
@ -265,7 +252,6 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
if (id == SPA_PARAM_Props) {
struct props *p = &this->props;
struct port *port = &this->port;
if (param == NULL) {
reset_props(p);
@ -273,15 +259,9 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
}
spa_pod_parse_object(param,
SPA_TYPE_OBJECT_Props, NULL,
SPA_PROP_live, SPA_POD_OPT_Bool(&p->live),
SPA_PROP_waveType, SPA_POD_OPT_Int(&p->wave),
SPA_PROP_frequency, SPA_POD_OPT_Float(&p->freq),
SPA_PROP_volume, SPA_POD_OPT_Float(&p->volume));
if (p->live)
port->info.flags |= SPA_PORT_FLAG_LIVE;
else
port->info.flags &= ~SPA_PORT_FLAG_LIVE;
}
else
return -ENOENT;
@ -316,23 +296,15 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
static void set_timer(struct impl *this, bool enabled)
{
if (this->async || this->props.live) {
if (enabled) {
if (this->props.live) {
uint64_t next_time = this->start_time + this->elapsed_time;
this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
} else {
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 1;
}
} else {
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 0;
}
spa_system_timerfd_settime(this->data_system,
this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &this->timerspec, NULL);
struct itimerspec ts = {0};
if (enabled) {
uint64_t next_time = this->start_time + this->elapsed_time;
ts.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
ts.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
}
spa_system_timerfd_settime(this->data_system, this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &ts, NULL);
}
static int read_timer(struct impl *this)
@ -340,14 +312,12 @@ static int read_timer(struct impl *this)
uint64_t expirations;
int res = 0;
if (this->async || this->props.live) {
if ((res = spa_system_timerfd_read(this->data_system,
this->timer_source.fd, &expirations)) < 0) {
if (res != -EAGAIN)
spa_log_error(this->log, "%p: timerfd error: %s",
this, spa_strerror(res));
}
if ((res = spa_system_timerfd_read(this->data_system, this->timer_source.fd, &expirations)) < 0) {
if (res != -EAGAIN)
spa_log_error(this->log, "%p: timerfd error: %s",
this, spa_strerror(res));
}
return 0;
}
@ -471,10 +441,7 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
return 0;
clock_gettime(CLOCK_MONOTONIC, &now);
if (this->props.live)
this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
else
this->start_time = 0;
this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
this->sample_count = 0;
this->elapsed_time = 0;
@ -895,9 +862,6 @@ static inline void reuse_buffer(struct impl *this, struct port *port, uint32_t i
b->outstanding = false;
spa_list_append(&port->empty, &b->link);
if (!this->props.live && !this->following)
set_timer(this, true);
}
static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
@ -971,7 +935,7 @@ static int impl_node_process(void *object)
io->buffer_id = SPA_ID_INVALID;
}
if (!this->props.live || this->following)
if (this->following)
return make_buffer(this);
else
return SPA_STATUS_OK;
@ -1105,10 +1069,6 @@ impl_init(const struct spa_handle_factory *factory,
CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
this->timer_source.mask = SPA_IO_IN;
this->timer_source.rmask = 0;
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 0;
this->timerspec.it_interval.tv_sec = 0;
this->timerspec.it_interval.tv_nsec = 0;
if (this->data_loop)
spa_loop_add_source(this->data_loop, &this->timer_source);
@ -1117,9 +1077,7 @@ impl_init(const struct spa_handle_factory *factory,
port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
SPA_PORT_CHANGE_MASK_PARAMS;
port->info = SPA_PORT_INFO_INIT();
port->info.flags = SPA_PORT_FLAG_NO_REF;
if (this->props.live)
port->info.flags |= SPA_PORT_FLAG_LIVE;
port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_LIVE;
port->params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
port->params[1] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
port->params[2] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);

View file

@ -106,8 +106,8 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
static const struct media_codec_config
aac_frequencies[] = {
{ AAC_SAMPLING_FREQ_48000, 48000, 11 },
{ AAC_SAMPLING_FREQ_44100, 44100, 10 },
{ AAC_SAMPLING_FREQ_48000, 48000, 11 },
{ AAC_SAMPLING_FREQ_96000, 96000, 9 },
{ AAC_SAMPLING_FREQ_88200, 88200, 8 },
{ AAC_SAMPLING_FREQ_64000, 64000, 7 },
@ -194,75 +194,6 @@ static int codec_select_config(const struct media_codec *codec, uint32_t flags,
return sizeof(conf);
}
static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
struct spa_pod_builder *b, struct spa_pod **param)
{
a2dp_aac_t conf;
struct spa_pod_frame f[2];
struct spa_pod_choice *choice;
uint32_t position[2];
uint32_t i = 0;
if (caps_size < sizeof(conf))
return -EINVAL;
memcpy(&conf, caps, sizeof(conf));
if (idx > 0)
return 0;
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, id);
spa_pod_builder_add(b,
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_S16),
0);
spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0);
spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0);
choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]);
i = 0;
SPA_FOR_EACH_ELEMENT_VAR(aac_frequencies, f) {
if (AAC_GET_FREQUENCY(conf) & f->config) {
if (i++ == 0)
spa_pod_builder_int(b, f->value);
spa_pod_builder_int(b, f->value);
}
}
if (i > 1)
choice->body.type = SPA_CHOICE_Enum;
spa_pod_builder_pop(b, &f[1]);
if (i == 0)
return -EINVAL;
if (SPA_FLAG_IS_SET(conf.channels, AAC_CHANNELS_1 | AAC_CHANNELS_2)) {
spa_pod_builder_add(b,
SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int(2, 1, 2),
0);
} else if (conf.channels & AAC_CHANNELS_1) {
position[0] = SPA_AUDIO_CHANNEL_MONO;
spa_pod_builder_add(b,
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(1),
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
SPA_TYPE_Id, 1, position),
0);
} else if (conf.channels & AAC_CHANNELS_2) {
position[0] = SPA_AUDIO_CHANNEL_FL;
position[1] = SPA_AUDIO_CHANNEL_FR;
spa_pod_builder_add(b,
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(2),
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
SPA_TYPE_Id, 2, position),
0);
} else
return -EINVAL;
*param = spa_pod_builder_pop(b, &f[0]);
return *param == NULL ? -EIO : 1;
}
static int codec_validate_config(const struct media_codec *codec, uint32_t flags,
const void *caps, size_t caps_size,
struct spa_audio_info *info)
@ -283,8 +214,10 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags
/*
* A2DP v1.3.2, 4.5.2: only one bit shall be set in bitfields.
* However, there is a report (#1342) of device setting multiple
* bits for AAC object type. It's not clear if this was due to
* a BlueZ bug, but we can be lax here and below in codec_init.
* bits for AAC object type. In addition AirPods set multiple bits.
*
* Some devices also set multiple bits in frequencies & channels.
* For these, pick a "preferred" choice.
*/
if (!(conf.object_type & (AAC_OBJECT_TYPE_MPEG2_AAC_LC |
AAC_OBJECT_TYPE_MPEG4_AAC_LC |
@ -315,6 +248,35 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags
return 0;
}
static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
struct spa_pod_builder *b, struct spa_pod **param)
{
struct spa_audio_info info;
struct spa_pod_frame f[1];
int res;
if ((res = codec_validate_config(codec, flags, caps, caps_size, &info)) < 0)
return res;
if (idx > 0)
return 0;
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, id);
spa_pod_builder_add(b,
SPA_FORMAT_mediaType, SPA_POD_Id(info.media_type),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(info.media_subtype),
SPA_FORMAT_AUDIO_format, SPA_POD_Id(info.info.raw.format),
SPA_FORMAT_AUDIO_rate, SPA_POD_Int(info.info.raw.rate),
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info.info.raw.channels),
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
SPA_TYPE_Id, info.info.raw.channels, info.info.raw.position),
0);
*param = spa_pod_builder_pop(b, &f[0]);
return *param == NULL ? -EIO : 1;
}
static void *codec_init_props(const struct media_codec *codec, uint32_t flags, const struct spa_dict *settings)
{
struct props *p = calloc(1, sizeof(struct props));
@ -324,7 +286,7 @@ static void *codec_init_props(const struct media_codec *codec, uint32_t flags, c
return NULL;
if (settings == NULL || (str = spa_dict_lookup(settings, "bluez5.a2dp.aac.bitratemode")) == NULL)
str = "0";
str = "5";
p->bitratemode = SPA_CLAMP(atoi(str), 0, 5);
return p;
@ -369,14 +331,14 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags,
goto error;
/* If object type has multiple bits set (invalid per spec, see above),
* assume the device usually means AAC-LC.
* assume the device usually means MPEG2 AAC LC which is mandatory.
*/
if (conf->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC) {
res = aacEncoder_SetParam(this->aacenc, AACENC_AOT, AOT_AAC_LC);
if (conf->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC) {
res = aacEncoder_SetParam(this->aacenc, AACENC_AOT, AOT_MP2_AAC_LC);
if (res != AACENC_OK)
goto error;
} else if (conf->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC) {
res = aacEncoder_SetParam(this->aacenc, AACENC_AOT, AOT_MP2_AAC_LC);
} else if (conf->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC) {
res = aacEncoder_SetParam(this->aacenc, AACENC_AOT, AOT_AAC_LC);
if (res != AACENC_OK)
goto error;
} else if (conf->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_ELD) {
@ -418,8 +380,12 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags,
// Fragmentation is not implemented yet,
// so make sure every encoded AAC frame fits in (mtu - header)
this->max_bitrate = ((this->mtu - sizeof(struct rtp_header)) * 8 * this->rate) / 1024;
this->max_bitrate = SPA_MIN(this->max_bitrate, get_valid_aac_bitrate(conf));
this->cur_bitrate = this->max_bitrate;
this->cur_bitrate = SPA_MIN(this->max_bitrate, get_valid_aac_bitrate(conf));
spa_log_debug(log, "AAC: max (peak) bitrate: %d, cur bitrate: %d, mode: %d (vbr: %d)",
this->max_bitrate,
this->cur_bitrate,
bitratemode,
conf->vbr);
res = aacEncoder_SetParam(this->aacenc, AACENC_BITRATE, this->cur_bitrate);
if (res != AACENC_OK)
@ -429,6 +395,15 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags,
if (res != AACENC_OK)
goto error;
// Assume >110 kbit/s as a "high bitrate" CBR and increase the
// band pass cutout up to 19.3 kHz (as in mode 5 VBR).
if (!conf->vbr && this->cur_bitrate > 110000) {
res = aacEncoder_SetParam(this->aacenc, AACENC_BANDWIDTH,
19293);
if (res != AACENC_OK)
goto error;
}
res = aacEncoder_SetParam(this->aacenc, AACENC_TRANSMUX, TT_MP4_LATM_MCP1);
if (res != AACENC_OK)
goto error;

View file

@ -343,77 +343,27 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
struct spa_pod_builder *b, struct spa_pod **param)
{
a2dp_sbc_t conf;
struct spa_pod_frame f[2];
struct spa_pod_choice *choice;
uint32_t i = 0;
uint32_t position[2];
struct spa_audio_info info;
struct spa_pod_frame f[1];
int res;
if (caps_size < sizeof(conf))
return -EINVAL;
memcpy(&conf, caps, sizeof(conf));
if ((res = codec_validate_config(codec, flags, caps, caps_size, &info)) < 0)
return res;
if (idx > 0)
return 0;
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, id);
spa_pod_builder_add(b,
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_S16),
SPA_FORMAT_mediaType, SPA_POD_Id(info.media_type),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(info.media_subtype),
SPA_FORMAT_AUDIO_format, SPA_POD_Id(info.info.raw.format),
SPA_FORMAT_AUDIO_rate, SPA_POD_Int(info.info.raw.rate),
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info.info.raw.channels),
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
SPA_TYPE_Id, info.info.raw.channels, info.info.raw.position),
0);
spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0);
spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0);
choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]);
i = 0;
if (conf.frequency & SBC_SAMPLING_FREQ_48000) {
if (i++ == 0)
spa_pod_builder_int(b, 48000);
spa_pod_builder_int(b, 48000);
}
if (conf.frequency & SBC_SAMPLING_FREQ_44100) {
if (i++ == 0)
spa_pod_builder_int(b, 44100);
spa_pod_builder_int(b, 44100);
}
if (conf.frequency & SBC_SAMPLING_FREQ_32000) {
if (i++ == 0)
spa_pod_builder_int(b, 32000);
spa_pod_builder_int(b, 32000);
}
if (conf.frequency & SBC_SAMPLING_FREQ_16000) {
if (i++ == 0)
spa_pod_builder_int(b, 16000);
spa_pod_builder_int(b, 16000);
}
if (i > 1)
choice->body.type = SPA_CHOICE_Enum;
spa_pod_builder_pop(b, &f[1]);
if (conf.channel_mode & SBC_CHANNEL_MODE_MONO &&
conf.channel_mode & (SBC_CHANNEL_MODE_JOINT_STEREO |
SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_DUAL_CHANNEL)) {
spa_pod_builder_add(b,
SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int(2, 1, 2),
0);
} else if (conf.channel_mode & SBC_CHANNEL_MODE_MONO) {
position[0] = SPA_AUDIO_CHANNEL_MONO;
spa_pod_builder_add(b,
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(1),
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
SPA_TYPE_Id, 1, position),
0);
} else {
position[0] = SPA_AUDIO_CHANNEL_FL;
position[1] = SPA_AUDIO_CHANNEL_FR;
spa_pod_builder_add(b,
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(2),
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
SPA_TYPE_Id, 2, position),
0);
}
*param = spa_pod_builder_pop(b, &f[0]);
return *param == NULL ? -EIO : 1;
}

View file

@ -1969,6 +1969,9 @@ static void hfp_hf_remove_disconnected_calls(struct rfcomm *rfcomm)
struct updated_call *updated_call;
bool found;
if (!rfcomm->telephony_ag)
return;
spa_list_for_each_safe(call, call_tmp, &rfcomm->telephony_ag->call_list, link) {
found = false;
spa_list_for_each(updated_call, &rfcomm->updated_call_list, link) {
@ -2097,6 +2100,8 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
if (spa_streq(rfcomm->hf_indicators[indicator], "battchg")) {
spa_bt_device_report_battery_level(rfcomm->device, value * 100 / 5);
} else if (!rfcomm->telephony_ag) {
/* noop */
} else if (spa_streq(rfcomm->hf_indicators[indicator], "callsetup")) {
if (rfcomm->hfp_hf_clcc) {
rfcomm_send_cmd(rfcomm, hfp_hf_clcc_update, NULL, "AT+CLCC");
@ -2245,7 +2250,8 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
rfcomm->hfp_hf_in_progress = false;
}
}
} else if (sscanf(token, "+CLIP: \"%16[^\"]\",%u", number, &type) == 2) {
} else if (sscanf(token, "+CLIP: \"%16[^\"]\",%u", number, &type) == 2
&& rfcomm->telephony_ag) {
struct spa_bt_telephony_call *call;
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
if (call->state == CALL_STATE_INCOMING && !spa_streq(number, call->line_identification)) {
@ -2256,7 +2262,8 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
break;
}
}
} else if (sscanf(token, "+CCWA: \"%16[^\"]\",%u", number, &type) == 2) {
} else if (sscanf(token, "+CCWA: \"%16[^\"]\",%u", number, &type) == 2
&& rfcomm->telephony_ag) {
struct spa_bt_telephony_call *call;
bool found = false;
@ -2273,7 +2280,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
if (call == NULL)
spa_log_warn(backend->log, "failed to create waiting call");
}
} else if (spa_strstartswith(token, "+CLCC:")) {
} else if (spa_strstartswith(token, "+CLCC:") && rfcomm->telephony_ag) {
struct spa_bt_telephony_call *call;
size_t pos;
char *token_end;
@ -2421,17 +2428,19 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
}
}
rfcomm->telephony_ag = telephony_ag_new(backend->telephony, 0);
rfcomm->telephony_ag->address = strdup(rfcomm->device->address);
rfcomm->telephony_ag->volume[SPA_BT_VOLUME_ID_RX] = rfcomm->volumes[SPA_BT_VOLUME_ID_RX].hw_volume = backend->hfp_default_speaker_volume;
rfcomm->telephony_ag->volume[SPA_BT_VOLUME_ID_TX] = rfcomm->volumes[SPA_BT_VOLUME_ID_TX].hw_volume = backend->hfp_default_mic_volume;
telephony_ag_set_callbacks(rfcomm->telephony_ag,
if (backend->telephony) {
rfcomm->telephony_ag = telephony_ag_new(backend->telephony, 0);
rfcomm->telephony_ag->address = strdup(rfcomm->device->address);
rfcomm->telephony_ag->volume[SPA_BT_VOLUME_ID_RX] = rfcomm->volumes[SPA_BT_VOLUME_ID_RX].hw_volume = backend->hfp_default_speaker_volume;
rfcomm->telephony_ag->volume[SPA_BT_VOLUME_ID_TX] = rfcomm->volumes[SPA_BT_VOLUME_ID_TX].hw_volume = backend->hfp_default_mic_volume;
telephony_ag_set_callbacks(rfcomm->telephony_ag,
&telephony_ag_callbacks, rfcomm);
if (rfcomm->transport) {
rfcomm->telephony_ag->transport.codec = rfcomm->transport->media_codec->codec_id;
rfcomm->telephony_ag->transport.state = rfcomm->transport->state;
if (rfcomm->transport) {
rfcomm->telephony_ag->transport.codec = rfcomm->transport->media_codec->codec_id;
rfcomm->telephony_ag->transport.state = rfcomm->transport->state;
}
telephony_ag_register(rfcomm->telephony_ag);
}
telephony_ag_register(rfcomm->telephony_ag);
rfcomm_send_cmd(rfcomm, hfp_hf_clip, NULL, "AT+CLIP=1");
break;
@ -2478,7 +2487,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
break;
case hfp_hf_chld1_hangup:
/* For HFP/HF/TWC/BV-03-C - see 0e92ab9307e05758b3f70b4c0648e29c1d1e50be */
if (!rfcomm->hfp_hf_clcc) {
if (!rfcomm->hfp_hf_clcc && rfcomm->telephony_ag) {
struct spa_bt_telephony_call *call, *tcall;
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
if (call->state == CALL_STATE_ACTIVE) {

View file

@ -126,22 +126,22 @@ static const struct bap_qos bap_qos_configs[] = {
BAP_QOS("48_6_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 5, 20, 40000, 27, "low-latency"), /* 48_6_1 */
/* BAP v1.0.1 Table 5.2; high-reliability */
BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 13, 75, 40000, 10, "high-reliabilty"), /* 8_1_2 */
BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 13, 95, 40000, 0, "high-reliabilty"), /* 8_2_2 */
BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 13, 75, 40000, 11, "high-reliabilty"), /* 16_1_2 */
BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 13, 95, 40000, 1, "high-reliabilty"), /* 16_2_2 */
BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 13, 75, 40000, 12, "high-reliabilty"), /* 24_1_2 */
BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 13, 95, 40000, 2, "high-reliabilty"), /* 24_2_2 */
BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 13, 75, 40000, 13, "high-reliabilty"), /* 32_1_2 */
BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 13, 95, 40000, 3, "high-reliabilty"), /* 32_2_2 */
BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 13, 80, 40000, 54, "high-reliabilty"), /* 441_1_2 */
BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 13, 85, 40000, 44, "high-reliabilty"), /* 441_2_2 */
BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 13, 75, 40000, 55, "high-reliabilty"), /* 48_1_2 */
BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 13, 95, 40000, 45, "high-reliabilty"), /* 48_2_2 */
BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 13, 75, 40000, 56, "high-reliabilty"), /* 48_3_2 */
BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 13, 100, 40000, 46, "high-reliabilty"), /* 48_4_2 */
BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 13, 75, 40000, 57, "high-reliabilty"), /* 48_5_2 */
BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 13, 100, 40000, 47, "high-reliabilty"), /* 48_6_2 */
BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 13, 75, 40000, 10, "high-reliability"), /* 8_1_2 */
BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 13, 95, 40000, 0, "high-reliability"), /* 8_2_2 */
BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 13, 75, 40000, 11, "high-reliability"), /* 16_1_2 */
BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 13, 95, 40000, 1, "high-reliability"), /* 16_2_2 */
BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 13, 75, 40000, 12, "high-reliability"), /* 24_1_2 */
BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 13, 95, 40000, 2, "high-reliability"), /* 24_2_2 */
BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 13, 75, 40000, 13, "high-reliability"), /* 32_1_2 */
BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 13, 95, 40000, 3, "high-reliability"), /* 32_2_2 */
BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 13, 80, 40000, 54, "high-reliability"), /* 441_1_2 */
BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 13, 85, 40000, 44, "high-reliability"), /* 441_2_2 */
BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 13, 75, 40000, 55, "high-reliability"), /* 48_1_2 */
BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 13, 95, 40000, 45, "high-reliability"), /* 48_2_2 */
BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 13, 75, 40000, 56, "high-reliability"), /* 48_3_2 */
BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 13, 100, 40000, 46, "high-reliability"), /* 48_4_2 */
BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 13, 75, 40000, 57, "high-reliability"), /* 48_5_2 */
BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 13, 100, 40000, 47, "high-reliability"), /* 48_6_2 */
};
static const struct bap_qos bap_bcast_qos_configs[] = {
@ -167,22 +167,22 @@ static const struct bap_qos bap_bcast_qos_configs[] = {
BAP_QOS("48_6_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 20, 40000, 27, "low-latency"), /* 48_6_1 */
/* BAP v1.0.1 Table 6.4; high-reliability */
BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 4, 45, 40000, 10, "high-reliabilty"), /* 8_1_2 */
BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 4, 60, 40000, 0, "high-reliabilty"), /* 8_2_2 */
BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 4, 45, 40000, 11, "high-reliabilty"), /* 16_1_2 */
BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 4, 60, 40000, 1, "high-reliabilty"), /* 16_2_2 */
BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 4, 45, 40000, 12, "high-reliabilty"), /* 24_1_2 */
BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 4, 60, 40000, 2, "high-reliabilty"), /* 24_2_2 */
BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 4, 45, 40000, 13, "high-reliabilty"), /* 32_1_2 */
BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 4, 60, 40000, 3, "high-reliabilty"), /* 32_2_2 */
BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 4, 54, 40000, 14, "high-reliabilty"), /* 441_1_2 */
BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 4, 60, 40000, 4, "high-reliabilty"), /* 441_2_2 */
BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 4, 50, 40000, 15, "high-reliabilty"), /* 48_1_2 */
BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 4, 65, 40000, 5, "high-reliabilty"), /* 48_2_2 */
BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 4, 50, 40000, 16, "high-reliabilty"), /* 48_3_2 */
BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 4, 65, 40000, 6, "high-reliabilty"), /* 48_4_2 */
BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 50, 40000, 17, "high-reliabilty"), /* 48_5_2 */
BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 65, 40000, 7, "high-reliabilty"), /* 48_6_2 */
BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 4, 45, 40000, 10, "high-reliability"), /* 8_1_2 */
BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 4, 60, 40000, 0, "high-reliability"), /* 8_2_2 */
BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 4, 45, 40000, 11, "high-reliability"), /* 16_1_2 */
BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 4, 60, 40000, 1, "high-reliability"), /* 16_2_2 */
BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 4, 45, 40000, 12, "high-reliability"), /* 24_1_2 */
BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 4, 60, 40000, 2, "high-reliability"), /* 24_2_2 */
BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 4, 45, 40000, 13, "high-reliability"), /* 32_1_2 */
BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 4, 60, 40000, 3, "high-reliability"), /* 32_2_2 */
BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 4, 54, 40000, 14, "high-reliability"), /* 441_1_2 */
BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 4, 60, 40000, 4, "high-reliability"), /* 441_2_2 */
BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 4, 50, 40000, 15, "high-reliability"), /* 48_1_2 */
BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 4, 65, 40000, 5, "high-reliability"), /* 48_2_2 */
BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 4, 50, 40000, 16, "high-reliability"), /* 48_3_2 */
BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 4, 65, 40000, 6, "high-reliability"), /* 48_4_2 */
BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 50, 40000, 17, "high-reliability"), /* 48_5_2 */
BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 65, 40000, 7, "high-reliability"), /* 48_6_2 */
};
static unsigned int get_rate_mask(uint8_t rate) {
@ -1503,6 +1503,10 @@ static int codec_get_bis_config(const struct media_codec *codec, uint8_t *caps,
struct ltv_writer writer = LTV_WRITER(caps, *caps_size);
const struct bap_qos *preset = NULL;
uint32_t retransmissions = 0;
uint8_t rtn_manual_set = 0;
uint32_t max_transport_latency = 0;
uint32_t presentation_delay = 0;
*caps_size = 0;
if (settings) {
@ -1511,6 +1515,14 @@ static int codec_get_bis_config(const struct media_codec *codec, uint8_t *caps,
sscanf(settings->items[i].value, "%"PRIu32, &channel_allocation);
if (spa_streq(settings->items[i].key, "preset"))
preset_name = settings->items[i].value;
if (spa_streq(settings->items[i].key, "max_transport_latency"))
spa_atou32(settings->items[i].value, &max_transport_latency, 0);
if (spa_streq(settings->items[i].key, "presentation_delay"))
spa_atou32(settings->items[i].value, &presentation_delay, 0);
if (spa_streq(settings->items[i].key, "retransmissions")) {
spa_atou32(settings->items[i].value, &retransmissions, 0);
rtn_manual_set = 1;
}
}
}
@ -1537,9 +1549,9 @@ static int codec_get_bis_config(const struct media_codec *codec, uint8_t *caps,
else
qos->framing = 0;
qos->sdu = preset->framelen * get_channel_count(channel_allocation);
qos->retransmission = preset->retransmission;
qos->latency = preset->latency;
qos->delay = preset->delay;
qos->retransmission = rtn_manual_set ? retransmissions : preset->retransmission;
qos->latency = max_transport_latency ? max_transport_latency : preset->latency;
qos->delay = presentation_delay ? presentation_delay : preset->delay;
qos->phy = 2;
qos->interval = (preset->frame_duration == LC3_CONFIG_DURATION_7_5 ? 7500 : 10000);

View file

@ -188,9 +188,16 @@ struct spa_bt_metadata {
uint8_t value[METADATA_MAX_LEN - 1];
};
#define RTN_MAX 0x1E
#define MAX_TRANSPORT_LATENCY_MIN 0x5
#define MAX_TRANSPORT_LATENCY_MAX 0x0FA0
struct spa_bt_bis {
struct spa_list link;
char qos_preset[255];
int retransmissions;
int rtn_manual_set;
int max_transport_latency;
int channel_allocation;
struct spa_list metadata_list;
};
@ -587,18 +594,35 @@ static enum spa_bt_profile get_codec_profile(const struct media_codec *codec,
{
switch (direction) {
case SPA_BT_MEDIA_SOURCE:
return codec->kind == MEDIA_CODEC_BAP ? SPA_BT_PROFILE_BAP_SOURCE : SPA_BT_PROFILE_A2DP_SOURCE;
if (codec->kind == MEDIA_CODEC_A2DP)
return SPA_BT_PROFILE_A2DP_SOURCE;
else if (codec->kind == MEDIA_CODEC_BAP)
return SPA_BT_PROFILE_BAP_SOURCE;
else if (codec->kind == MEDIA_CODEC_HFP)
return SPA_BT_PROFILE_HEADSET_AUDIO;
else
return SPA_BT_PROFILE_NULL;
case SPA_BT_MEDIA_SINK:
if (codec->kind == MEDIA_CODEC_ASHA)
if (codec->kind == MEDIA_CODEC_A2DP)
return SPA_BT_PROFILE_A2DP_SINK;
else if (codec->kind == MEDIA_CODEC_ASHA)
return SPA_BT_PROFILE_ASHA_SINK;
else if (codec->kind == MEDIA_CODEC_BAP)
return SPA_BT_PROFILE_BAP_SINK;
else if (codec->kind == MEDIA_CODEC_HFP)
return SPA_BT_PROFILE_HEADSET_AUDIO;
else
return SPA_BT_PROFILE_A2DP_SINK;
return SPA_BT_PROFILE_NULL;
case SPA_BT_MEDIA_SOURCE_BROADCAST:
return SPA_BT_PROFILE_BAP_BROADCAST_SOURCE;
if (codec->kind == MEDIA_CODEC_BAP)
return SPA_BT_PROFILE_BAP_BROADCAST_SOURCE;
else
return SPA_BT_PROFILE_NULL;
case SPA_BT_MEDIA_SINK_BROADCAST:
return SPA_BT_PROFILE_BAP_BROADCAST_SINK;
if (codec->kind == MEDIA_CODEC_BAP)
return SPA_BT_PROFILE_BAP_BROADCAST_SINK;
else
return SPA_BT_PROFILE_NULL;
default:
spa_assert_not_reached();
}
@ -720,14 +744,12 @@ static const char *bap_features_get_uuid(struct bap_features *feat, size_t i)
/** Get feature name at \a i, or NULL if uuid doesn't match */
static const char *bap_features_get_name(struct bap_features *feat, size_t i, const char *uuid)
{
char *pos;
if (i >= feat->dict.n_items)
return NULL;
if (!spa_streq(feat->dict.items[i].value, uuid))
return NULL;
pos = strchr(feat->dict.items[i].key, ':');
const char *pos = strchr(feat->dict.items[i].key, ':');
if (!pos)
return NULL;
return pos + 1;
@ -1336,7 +1358,6 @@ static struct spa_bt_adapter *adapter_find(struct spa_bt_monitor *monitor, const
static int parse_modalias(const char *modalias, uint16_t *source, uint16_t *vendor,
uint16_t *product, uint16_t *version)
{
char *pos;
unsigned int src, i, j, k;
if (spa_strstartswith(modalias, "bluetooth:"))
@ -1346,7 +1367,7 @@ static int parse_modalias(const char *modalias, uint16_t *source, uint16_t *vend
else
return -EINVAL;
pos = strchr(modalias, ':');
const char *pos = strchr(modalias, ':');
if (pos == NULL)
return -EINVAL;
@ -2780,16 +2801,18 @@ bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const stru
bool is_bap = codec->kind == MEDIA_CODEC_BAP;
size_t i;
codec_target_profile = get_codec_target_profile(monitor, codec);
if (!codec_target_profile)
return false;
if (codec->kind == MEDIA_CODEC_HFP) {
if (!(profile & SPA_BT_PROFILE_HEADSET_AUDIO))
return false;
if (!is_media_codec_enabled(monitor, codec))
return false;
return spa_bt_backend_supports_codec(monitor->backend, device, codec->codec_id) == 1;
}
codec_target_profile = get_codec_target_profile(monitor, codec);
if (!codec_target_profile)
return false;
if (!device->adapter->a2dp_application_registered && is_a2dp) {
/* Codec switching not supported: only plain SBC allowed */
return (codec->codec_id == A2DP_CODEC_SBC && spa_streq(codec->name, "sbc") &&
@ -6176,8 +6199,11 @@ static void configure_bis(struct spa_bt_monitor *monitor,
struct bap_codec_qos qos;
struct spa_bt_metadata *metadata_entry;
struct spa_dict settings;
struct spa_dict_item setting_items[2];
struct spa_dict_item setting_items[4];
uint32_t n_items = 0;
char channel_allocation[64] = {0};
char retransmissions[3] = {0};
char max_transport_latency[5] = {0};
int mse = 0;
int options = 0;
@ -6202,12 +6228,27 @@ static void configure_bis(struct spa_bt_monitor *monitor,
metadata_size += metadata_entry->length - 1;
}
spa_log_debug(monitor->log, "bis->channel_allocation %d", bis->channel_allocation);
if (bis->channel_allocation)
if (bis->channel_allocation) {
spa_scnprintf(channel_allocation, sizeof(channel_allocation), "%"PRIu32, bis->channel_allocation);
setting_items[0] = SPA_DICT_ITEM_INIT("channel_allocation", channel_allocation);
setting_items[1] = SPA_DICT_ITEM_INIT("preset", bis->qos_preset);
settings = SPA_DICT_INIT(setting_items, 2);
}
spa_log_debug(monitor->log, "bis->rtn_manual_set %d", bis->rtn_manual_set);
spa_log_debug(monitor->log, "bis->retransmissions %d", bis->retransmissions);
if (bis->rtn_manual_set) {
spa_scnprintf(retransmissions, sizeof(retransmissions), "%"PRIu8, bis->retransmissions);
setting_items[n_items++] = SPA_DICT_ITEM_INIT("retransmissions", retransmissions);
}
spa_log_debug(monitor->log, "bis->max_transport_latency %d", bis->max_transport_latency);
if (bis->max_transport_latency) {
spa_scnprintf(max_transport_latency, sizeof(max_transport_latency), "%"PRIu32, bis->max_transport_latency);
setting_items[n_items++] = SPA_DICT_ITEM_INIT("max_transport_latency", max_transport_latency);
}
setting_items[n_items++] = SPA_DICT_ITEM_INIT("preset", bis->qos_preset);
setting_items[n_items++] = SPA_DICT_ITEM_INIT("channel_allocation", channel_allocation);
settings = SPA_DICT_INIT(setting_items, n_items);
caps_size = sizeof(caps);
ret = codec->get_bis_config(codec, caps, &caps_size, &settings, &qos);
@ -7081,7 +7122,7 @@ static void parse_broadcast_source_config(struct spa_bt_monitor *monitor, const
memcpy(big_entry->broadcast_code, bcode, strlen(bcode));
spa_log_debug(monitor->log, "big_entry->broadcast_code %s", big_entry->broadcast_code);
} else if (spa_streq(key, "adapter")) {
if (spa_json_get_string(&it[1], big_entry->adapter, sizeof(big_entry->adapter)) <= 0)
if (spa_json_get_string(&it[0], big_entry->adapter, sizeof(big_entry->adapter)) <= 0)
goto parse_failed;
spa_log_debug(monitor->log, "big_entry->adapter %s", big_entry->adapter);
} else if (spa_streq(key, "encryption")) {
@ -7110,6 +7151,20 @@ static void parse_broadcast_source_config(struct spa_bt_monitor *monitor, const
if (spa_json_get_string(&it[1], bis_entry->qos_preset, sizeof(bis_entry->qos_preset)) <= 0)
goto parse_failed;
spa_log_debug(monitor->log, "bis_entry->qos_preset %s", bis_entry->qos_preset);
} else if (spa_streq(bis_key, "retransmissions")) {
if (spa_json_get_int(&it[2], &bis_entry->retransmissions) <= 0)
goto parse_failed;
if (bis_entry->retransmissions > RTN_MAX)
goto parse_failed;
bis_entry->rtn_manual_set = 1;
spa_log_debug(monitor->log, "bis_entry->retransmissions %d", bis_entry->retransmissions);
} else if (spa_streq(bis_key, "max_transport_latency")) {
if (spa_json_get_int(&it[2], &bis_entry->max_transport_latency) <= 0)
goto parse_failed;
if (bis_entry->max_transport_latency < MAX_TRANSPORT_LATENCY_MIN &&
bis_entry->max_transport_latency > MAX_TRANSPORT_LATENCY_MAX)
goto parse_failed;
spa_log_debug(monitor->log, "bis_entry->max_transport_latency %d", bis_entry->max_transport_latency);
} else if (spa_streq(bis_key, "audio_channel_allocation")) {
if (spa_json_get_int(&it[1], &bis_entry->channel_allocation) <= 0)
goto parse_failed;

View file

@ -59,6 +59,8 @@ enum device_profile {
DEVICE_PROFILE_OFF = 0,
DEVICE_PROFILE_AG,
DEVICE_PROFILE_A2DP,
DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY,
DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY,
DEVICE_PROFILE_HSP_HFP,
DEVICE_PROFILE_BAP,
DEVICE_PROFILE_BAP_SINK,
@ -67,6 +69,12 @@ enum device_profile {
DEVICE_PROFILE_LAST,
};
enum codec_order {
CODEC_ORDER_NONE = 0,
CODEC_ORDER_QUALITY,
CODEC_ORDER_LATENCY,
};
enum {
ROUTE_INPUT = 0,
ROUTE_OUTPUT,
@ -204,9 +212,89 @@ static bool profile_is_bap(enum device_profile profile)
return false;
}
static void get_media_codecs(struct impl *this, enum spa_bluetooth_audio_codec id, const struct media_codec **codecs, size_t size)
static bool profile_is_a2dp(enum device_profile profile)
{
switch (profile) {
case DEVICE_PROFILE_A2DP:
case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
return true;
default:
break;
}
return false;
}
static size_t get_media_codec_quality_priority (const struct media_codec *mc)
{
/* From lowest quality to highest quality */
static const enum spa_bluetooth_audio_codec quality_priorities[] = {
SPA_BLUETOOTH_AUDIO_CODEC_START,
SPA_BLUETOOTH_AUDIO_CODEC_SBC,
SPA_BLUETOOTH_AUDIO_CODEC_APTX,
SPA_BLUETOOTH_AUDIO_CODEC_AAC,
SPA_BLUETOOTH_AUDIO_CODEC_OPUS_G,
SPA_BLUETOOTH_AUDIO_CODEC_LC3PLUS_HR,
SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD,
SPA_BLUETOOTH_AUDIO_CODEC_LDAC,
};
size_t i;
for (i = 0; i < SPA_N_ELEMENTS(quality_priorities); ++i) {
if (quality_priorities[i] == mc->id)
return i;
}
return 0;
}
static size_t get_media_codec_latency_priority (const struct media_codec *mc)
{
/* From highest latency to lowest latency */
static const enum spa_bluetooth_audio_codec latency_priorities[] = {
SPA_BLUETOOTH_AUDIO_CODEC_START,
SPA_BLUETOOTH_AUDIO_CODEC_SBC,
SPA_BLUETOOTH_AUDIO_CODEC_APTX,
SPA_BLUETOOTH_AUDIO_CODEC_OPUS_G,
SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM_DUPLEX,
SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM,
SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL_DUPLEX,
SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL,
};
size_t i;
for (i = 0; i < SPA_N_ELEMENTS(latency_priorities); ++i) {
if (latency_priorities[i] == mc->id)
return i;
}
return 0;
}
static int media_codec_quality_cmp(const void *a, const void *b) {
const struct media_codec *ca = *(const struct media_codec **)a;
const struct media_codec *cb = *(const struct media_codec **)b;
size_t ca_prio = get_media_codec_quality_priority (ca);
size_t cb_prio = get_media_codec_quality_priority (cb);
if (ca_prio > cb_prio) return -1;
if (ca_prio < cb_prio) return 1;
return 0;
}
static int media_codec_latency_cmp(const void *a, const void *b) {
const struct media_codec *ca = *(const struct media_codec **)a;
const struct media_codec *cb = *(const struct media_codec **)b;
size_t ca_prio = get_media_codec_latency_priority (ca);
size_t cb_prio = get_media_codec_latency_priority (cb);
if (ca_prio > cb_prio) return -1;
if (ca_prio < cb_prio) return 1;
return 0;
}
static void get_media_codecs(struct impl *this, enum codec_order order, enum spa_bluetooth_audio_codec id, const struct media_codec **codecs, size_t size)
{
const struct media_codec * const *c;
size_t n = 0;
spa_assert(size > 0);
spa_assert(this->supported_codecs);
@ -216,12 +304,24 @@ static void get_media_codecs(struct impl *this, enum spa_bluetooth_audio_codec i
continue;
if ((*c)->id == id || id == 0) {
*codecs++ = *c;
codecs[n++] = *c;
--size;
}
}
*codecs = NULL;
codecs[n] = NULL;
switch (order) {
case CODEC_ORDER_QUALITY:
qsort(codecs, n, sizeof(struct media_codec *), media_codec_quality_cmp);
break;
case CODEC_ORDER_LATENCY:
qsort(codecs, n, sizeof(struct media_codec *), media_codec_latency_cmp);
break;
case CODEC_ORDER_NONE:
default:
break;
}
}
static const struct media_codec *get_supported_media_codec(struct impl *this, enum spa_bluetooth_audio_codec id,
@ -380,6 +480,8 @@ static bool node_update_volume_from_transport(struct node *node, bool reset)
/* PW is the controller for remote device. */
if (impl->profile != DEVICE_PROFILE_A2DP
&& impl->profile != DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY
&& impl->profile != DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY
&& impl->profile != DEVICE_PROFILE_BAP
&& impl->profile != DEVICE_PROFILE_BAP_SINK
&& impl->profile != DEVICE_PROFILE_BAP_SOURCE
@ -1262,6 +1364,8 @@ static int emit_nodes(struct impl *this)
}
break;
case DEVICE_PROFILE_A2DP:
case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE) {
t = find_transport(this, SPA_BT_PROFILE_A2DP_SOURCE);
if (t) {
@ -1464,13 +1568,13 @@ static int set_profile(struct impl *this, uint32_t profile, enum spa_bluetooth_a
* XXX: source-only case, as it will only switch the sink, and we only
* XXX: list the sink codecs here. TODO: fix this
*/
if ((profile == DEVICE_PROFILE_A2DP || (profile_is_bap(profile) && is_bap_client(this)))
if ((profile_is_a2dp (profile) || (profile_is_bap(profile) && is_bap_client(this)))
&& !(this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE)) {
int ret;
const struct media_codec *codecs[64];
uint32_t profiles;
get_media_codecs(this, codec, codecs, SPA_N_ELEMENTS(codecs));
get_media_codecs(this, CODEC_ORDER_NONE, codec, codecs, SPA_N_ELEMENTS(codecs));
this->switching_codec = true;
@ -1487,6 +1591,14 @@ static int set_profile(struct impl *this, uint32_t profile, enum spa_bluetooth_a
case DEVICE_PROFILE_A2DP:
profiles = this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_DUPLEX;
break;
case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
get_media_codecs(this, CODEC_ORDER_QUALITY, 0, codecs, SPA_N_ELEMENTS(codecs));
profiles = this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_DUPLEX;
break;
case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
get_media_codecs(this, CODEC_ORDER_LATENCY, 0, codecs, SPA_N_ELEMENTS(codecs));
profiles = this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_DUPLEX;
break;
default:
profiles = 0;
break;
@ -1646,6 +1758,8 @@ static void profiles_changed(void *userdata, uint32_t connected_change)
nodes_changed);
break;
case DEVICE_PROFILE_A2DP:
case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
nodes_changed = (connected_change & SPA_BT_PROFILE_A2DP_DUPLEX);
spa_log_debug(this->log, "profiles changed: A2DP nodes changed: %d",
nodes_changed);
@ -1801,6 +1915,8 @@ static uint32_t profile_direction_mask(struct impl *this, uint32_t index, enum s
switch (index) {
case DEVICE_PROFILE_A2DP:
case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
if (device->connected_profiles & SPA_BT_PROFILE_A2DP_SINK)
have_output = true;
@ -1850,6 +1966,8 @@ static uint32_t get_profile_from_index(struct impl *this, uint32_t index, uint32
switch (profile) {
case DEVICE_PROFILE_OFF:
case DEVICE_PROFILE_AG:
case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
*codec = 0;
*next = (profile + 1) << 16;
return profile;
@ -1884,6 +2002,8 @@ static uint32_t get_index_from_profile(struct impl *this, uint32_t profile, enum
switch (profile) {
case DEVICE_PROFILE_OFF:
case DEVICE_PROFILE_AG:
case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
return (profile << 16);
case DEVICE_PROFILE_ASHA:
@ -1977,15 +2097,37 @@ static void set_initial_profile(struct impl *this)
t = find_transport(this, i);
if (t) {
if (i == SPA_BT_PROFILE_A2DP_SOURCE || i == SPA_BT_PROFILE_BAP_SOURCE)
if (i == SPA_BT_PROFILE_A2DP_SOURCE || i == SPA_BT_PROFILE_BAP_SOURCE) {
this->profile = DEVICE_PROFILE_AG;
else if (i == SPA_BT_PROFILE_BAP_SINK)
this->props.codec = t->media_codec->id;
} else if (i == SPA_BT_PROFILE_BAP_SINK) {
this->profile = DEVICE_PROFILE_BAP;
else if (i == SPA_BT_PROFILE_ASHA_SINK)
this->props.codec = t->media_codec->id;
} else if (i == SPA_BT_PROFILE_ASHA_SINK) {
this->profile = DEVICE_PROFILE_ASHA;
else
this->profile = DEVICE_PROFILE_A2DP;
this->props.codec = t->media_codec->id;
this->props.codec = t->media_codec->id;
} else {
const struct media_codec *codecs[64];
const struct media_codec *quality_codec = NULL;
int j;
get_media_codecs(this, CODEC_ORDER_QUALITY, 0, codecs, SPA_N_ELEMENTS(codecs));
for (j = 0; codecs[j] != NULL; ++j) {
if (codecs[j]->kind == MEDIA_CODEC_A2DP) {
quality_codec = codecs[j];
break;
}
}
if (quality_codec) {
this->profile = DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY;
this->props.codec = quality_codec->id;
} else {
this->profile = DEVICE_PROFILE_A2DP;
this->props.codec = t->media_codec->id;
}
}
spa_log_debug(this->log, "initial profile media profile:%d codec:%d",
this->profile, this->props.codec);
return;
@ -2123,6 +2265,36 @@ static struct spa_pod *build_profile(struct impl *this, struct spa_pod_builder *
n_source++;
break;
}
case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
{
uint32_t profile;
/* make this device profile visible only if there is an A2DP sink */
profile = device->connected_profiles & (SPA_BT_PROFILE_A2DP_SINK | SPA_BT_PROFILE_A2DP_SOURCE);
if (!(profile & SPA_BT_PROFILE_A2DP_SINK))
return NULL;
switch (profile_index) {
case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
name = "a2dp-auto-prefer-quality";
desc = _("Auto: Prefer Quality (A2DP)");
priority = 255;
break;
case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
name = "a2dp-auto-prefer-latency";
desc = _("Auto: Prefer Latency (A2DP)");
priority = 254;
break;
default:
return NULL;
}
n_sink++;
if (this->autoswitch_routes && (device->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT))
n_source++;
break;
}
case DEVICE_PROFILE_BAP_SINK:
case DEVICE_PROFILE_BAP_SOURCE:
/* These are client-only */
@ -2324,6 +2496,8 @@ static bool profile_has_route(uint32_t profile, uint32_t route)
case DEVICE_PROFILE_AG:
break;
case DEVICE_PROFILE_A2DP:
case DEVICE_PROFILE_A2DP_AUTO_PREFER_QUALITY:
case DEVICE_PROFILE_A2DP_AUTO_PREFER_LATENCY:
switch (route) {
case ROUTE_INPUT:
case ROUTE_OUTPUT:
@ -2623,7 +2797,7 @@ static struct spa_pod *build_route(struct impl *this, struct spa_pod_builder *b,
spa_pod_builder_array(b, sizeof(uint32_t), SPA_TYPE_Id,
node->n_channels, node->channels);
if ((this->profile == DEVICE_PROFILE_A2DP || profile_is_bap(this->profile)) &&
if ((profile_is_a2dp (this->profile) || profile_is_bap(this->profile)) &&
(dev & SINK_ID_FLAG)) {
spa_pod_builder_prop(b, SPA_PROP_latencyOffsetNsec, 0);
spa_pod_builder_long(b, node->latency_offset);
@ -2659,7 +2833,7 @@ next:
c = this->supported_codecs[*j];
if (!(this->profile == DEVICE_PROFILE_A2DP && c->kind == MEDIA_CODEC_A2DP) &&
if (!(profile_is_a2dp (this->profile) && c->kind == MEDIA_CODEC_A2DP) &&
!(profile_is_bap(this->profile) && c->kind == MEDIA_CODEC_BAP) &&
!(this->profile == DEVICE_PROFILE_HSP_HFP && c->kind == MEDIA_CODEC_HFP) &&
!(this->profile == DEVICE_PROFILE_ASHA && c->kind == MEDIA_CODEC_ASHA))
@ -3240,7 +3414,7 @@ static int impl_set_param(void *object,
return 0;
}
if (this->profile == DEVICE_PROFILE_A2DP || profile_is_bap(this->profile) ||
if (profile_is_a2dp (this->profile) || profile_is_bap(this->profile) ||
this->profile == DEVICE_PROFILE_ASHA || this->profile == DEVICE_PROFILE_HSP_HFP) {
size_t j;
for (j = 0; j < this->supported_codec_count; ++j) {

View file

@ -411,7 +411,7 @@ static void group_on_timeout(struct spa_source *source)
/* Ensure controller fill level */
fill_count = UINT_MAX;
spa_list_for_each(stream, &group->streams, link) {
if (!stream->sink || !group->started)
if (!stream->sink || !group->started || !stream->tx_latency.enabled)
continue;
if (stream->tx_latency.queue < MIN_FILL)
fill_count = SPA_MIN(fill_count, MIN_FILL - stream->tx_latency.queue);

View file

@ -1784,7 +1784,7 @@ static uint32_t get_samples(struct impl *this, int64_t *duration_ns)
static void update_target_latency(struct impl *this)
{
struct port *port = &this->port;
int32_t target;
int32_t target = 0;
int samples;
if (this->transport == NULL || !port->have_format)
@ -1803,7 +1803,7 @@ static void update_target_latency(struct impl *this)
*/
if (this->decode_buffer_target)
target = this->decode_buffer_target;
else
else if (this->transport->iso_io)
target = spa_bt_iso_io_get_source_target_latency(this->transport->iso_io);
spa_bt_decode_buffer_set_target_latency(&port->buffer, target);

View file

@ -23,7 +23,6 @@
#include <spa/utils/ringbuffer.h>
#include <spa/monitor/device.h>
#include <spa/control/control.h>
#include <spa/control/ump-utils.h>
#include <spa/node/node.h>
#include <spa/node/utils.h>
@ -450,7 +449,7 @@ static void midi_event_recv(void *user_data, uint16_t timestamp, uint8_t *data,
struct impl *this = user_data;
struct port *port = &this->ports[PORT_OUT];
struct time_sync *sync = &port->sync;
uint64_t time, state = 0;
uint64_t time;
int res;
spa_assert(size > 0);
@ -460,19 +459,11 @@ static void midi_event_recv(void *user_data, uint16_t timestamp, uint8_t *data,
spa_log_trace(this->log, "%p: event:0x%x size:%d timestamp:%d time:%"PRIu64"",
this, (int)data[0], (int)size, (int)timestamp, (uint64_t)time);
while (size > 0) {
uint32_t ump[4];
int ump_size = spa_ump_from_midi(&data, &size,
ump, sizeof(ump), 0, &state);
if (ump_size <= 0)
break;
res = midi_event_ringbuffer_push(&this->event_rbuf, time, (uint8_t*)ump, ump_size);
if (res < 0) {
midi_event_ringbuffer_init(&this->event_rbuf);
spa_log_warn(this->log, "%p: MIDI receive buffer overflow: %s",
this, spa_strerror(res));
}
res = midi_event_ringbuffer_push(&this->event_rbuf, time, data, size);
if (res < 0) {
midi_event_ringbuffer_init(&this->event_rbuf);
spa_log_warn(this->log, "%p: MIDI receive buffer overflow: %s",
this, spa_strerror(res));
}
}
@ -713,7 +704,7 @@ static int process_output(struct impl *this)
offset = time * this->rate / SPA_NSEC_PER_SEC;
offset = SPA_CLAMP(offset, 0u, this->duration - 1);
spa_pod_builder_control(&port->builder, offset, SPA_CONTROL_UMP);
spa_pod_builder_control(&port->builder, offset, SPA_CONTROL_Midi);
buf = spa_pod_builder_reserve_bytes(&port->builder, size);
if (buf) {
midi_event_ringbuffer_pop(&this->event_rbuf, buf, size);
@ -786,37 +777,28 @@ static int write_data(struct impl *this, struct spa_data *d)
time = 0;
while (spa_pod_parser_get_control_body(&parser, &c, &c_body) >= 0) {
int size;
uint8_t event[32];
const uint32_t *ump = c_body;
size_t ump_size = c.value.size;
uint64_t state = 0;
const uint8_t *event = c_body;
uint32_t size = c.value.size;
if (c.type != SPA_CONTROL_UMP)
if (c.type != SPA_CONTROL_Midi)
continue;
time = SPA_MAX(time, this->current_time + c.offset * SPA_NSEC_PER_SEC / this->rate);
while (ump_size > 0) {
size = spa_ump_to_midi(&ump, &ump_size, event, sizeof(event), &state);
if (size <= 0)
break;
spa_log_trace(this->log, "%p: output event:0x%x time:%"PRIu64, this,
(size > 0) ? event[0] : 0, time);
spa_log_trace(this->log, "%p: output event:0x%x time:%"PRIu64, this,
(size > 0) ? event[0] : 0, time);
do {
res = spa_bt_midi_writer_write(&this->writer,
time, event, size);
if (res < 0) {
return res;
} else if (res) {
int res2;
if ((res2 = flush_packet(this)) < 0)
return res2;
}
} while (res);
}
do {
res = spa_bt_midi_writer_write(&this->writer,
time, event, size);
if (res < 0) {
return res;
} else if (res) {
int res2;
if ((res2 = flush_packet(this)) < 0)
return res2;
}
} while (res);
}
if ((res = flush_packet(this)) < 0)

View file

@ -72,6 +72,7 @@ struct impl {
struct spa_node node;
uint32_t quantum_limit;
uint32_t control_types;
struct spa_log *log;
@ -473,9 +474,9 @@ static int port_set_format(void *object,
if (!port->have_format) {
this->n_formats++;
port->have_format = true;
port->types = types;
spa_log_debug(this->log, "%p: set format on port %d:%d",
this, direction, port_id);
port->types = types == 0 ? this->control_types : types;
spa_log_debug(this->log, "%p: set format on port %d:%d types:%08x %08x",
this, direction, port_id, port->types, this->control_types);
}
}
port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
@ -589,7 +590,7 @@ static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq,
port->io[0] = info->data;
port->io[1] = info->data;
}
if (!port->active) {
if (port->direction == SPA_DIRECTION_INPUT && !port->active) {
spa_list_append(&info->impl->mix_list, &port->mix_link);
port->active = true;
}
@ -955,6 +956,8 @@ impl_init(const struct spa_handle_factory *factory,
}
this->quantum_limit = 8192;
/* by default we convert to midi1 */
this->control_types = 1u<<SPA_CONTROL_Midi;
for (i = 0; info && i < info->n_items; i++) {
const char *k = info->items[i].key;
@ -962,6 +965,14 @@ impl_init(const struct spa_handle_factory *factory,
if (spa_streq(k, "clock.quantum-limit")) {
spa_atou32(s, &this->quantum_limit, 0);
}
else if (spa_streq(k, "control.ump")) {
if (spa_atob(s))
/* we convert to UMP when forced */
this->control_types = 1u<<SPA_CONTROL_UMP;
else
/* when unforced we let both midi1 and UMP through */
this->control_types = 0;
}
}
spa_hook_list_init(&this->hooks);

View file

@ -140,10 +140,10 @@ static void dsp_add_n_gain_avx2(void *obj, float *dst,
for (i = 1; i < n_src; i++) {
g = _mm256_set1_ps(gain[i]);
in[0] = _mm256_add_ps(in[0], _mm256_mul_ps(g, _mm256_load_ps(&s[i][n+ 0])));
in[1] = _mm256_add_ps(in[1], _mm256_mul_ps(g, _mm256_load_ps(&s[i][n+ 8])));
in[2] = _mm256_add_ps(in[2], _mm256_mul_ps(g, _mm256_load_ps(&s[i][n+16])));
in[3] = _mm256_add_ps(in[3], _mm256_mul_ps(g, _mm256_load_ps(&s[i][n+24])));
in[0] = _mm256_fmadd_ps(g, _mm256_load_ps(&s[i][n+ 0]), in[0]);
in[1] = _mm256_fmadd_ps(g, _mm256_load_ps(&s[i][n+ 8]), in[1]);
in[2] = _mm256_fmadd_ps(g, _mm256_load_ps(&s[i][n+16]), in[2]);
in[3] = _mm256_fmadd_ps(g, _mm256_load_ps(&s[i][n+24]), in[3]);
}
_mm256_store_ps(&d[n+ 0], in[0]);
_mm256_store_ps(&d[n+ 8], in[1]);
@ -237,13 +237,12 @@ void dsp_sum_avx2(void *obj, float *r, const float *a, const float *b, uint32_t
inline static __m256 _mm256_mul_pz(__m256 ab, __m256 cd)
{
__m256 aa, bb, dc, x0, x1;
__m256 aa, bb, dc, x1;
aa = _mm256_moveldup_ps(ab);
bb = _mm256_movehdup_ps(ab);
x0 = _mm256_mul_ps(aa, cd);
dc = _mm256_shuffle_ps(cd, cd, _MM_SHUFFLE(2,3,0,1));
x1 = _mm256_mul_ps(bb, dc);
return _mm256_addsub_ps(x0, x1);
return _mm256_fmaddsub_ps(aa, cd, x1);
}
void dsp_fft_cmul_avx2(void *obj, void *fft,
@ -308,12 +307,10 @@ void dsp_fft_cmuladd_avx2(void *obj, void *fft,
bb[1] = _mm256_load_ps(&b[2*i+8]); /* br2 bi2 br3 bi3 */
dd[0] = _mm256_mul_pz(aa[0], bb[0]);
dd[1] = _mm256_mul_pz(aa[1], bb[1]);
dd[0] = _mm256_mul_ps(dd[0], s);
dd[1] = _mm256_mul_ps(dd[1], s);
t[0] = _mm256_load_ps(&src[2*i]);
t[1] = _mm256_load_ps(&src[2*i+8]);
t[0] = _mm256_add_ps(t[0], dd[0]);
t[1] = _mm256_add_ps(t[1], dd[1]);
t[0] = _mm256_fmadd_ps(dd[0], s, t[0]);
t[1] = _mm256_fmadd_ps(dd[1], s, t[1]);
_mm256_store_ps(&dst[2*i], t[0]);
_mm256_store_ps(&dst[2*i+8], t[1]);
}

View file

@ -24,7 +24,7 @@ struct dsp_info {
static const struct dsp_info dsp_table[] =
{
#if defined (HAVE_AVX2)
{ SPA_CPU_FLAG_AVX2,
{ SPA_CPU_FLAG_AVX2 | SPA_CPU_FLAG_FMA3,
.funcs.clear = dsp_clear_c,
.funcs.copy = dsp_copy_c,
.funcs.mix_gain = dsp_mix_gain_avx2,

View file

@ -69,6 +69,7 @@ struct spa_fga_descriptor {
void (*connect_port) (void *instance, unsigned long port, void *data);
void (*control_changed) (void *instance);
void (*control_sync) (void *instance);
void (*activate) (void *instance);
void (*deactivate) (void *instance);

View file

@ -171,7 +171,10 @@ static int convolver1_run(struct spa_fga_dsp *dsp, struct convolver1 *conv, cons
if (conv->segCount > 1) {
if (inputBufferFill == 0) {
int indexAudio = (conv->current + 1) % conv->segCount;
int indexAudio = conv->current;
if (++indexAudio == conv->segCount)
indexAudio = 0;
spa_fga_dsp_fft_cmul(dsp, conv->fft, conv->pre_mult,
conv->segmentsIr[1],
@ -179,7 +182,8 @@ static int convolver1_run(struct spa_fga_dsp *dsp, struct convolver1 *conv, cons
conv->fftComplexSize, conv->scale);
for (i = 2; i < conv->segCount; i++) {
indexAudio = (conv->current + i) % conv->segCount;
if (++indexAudio == conv->segCount)
indexAudio = 0;
spa_fga_dsp_fft_cmuladd(dsp, conv->fft,
conv->pre_mult,
@ -214,9 +218,10 @@ static int convolver1_run(struct spa_fga_dsp *dsp, struct convolver1 *conv, cons
SPA_SWAP(conv->fft_buffer[0], conv->fft_buffer[1]);
conv->current = (conv->current > 0) ? (conv->current - 1) : (conv->segCount - 1);
if (conv->current == 0)
conv->current = conv->segCount;
conv->current--;
}
processed += processing;
}
conv->inputBufferFill = inputBufferFill;

View file

@ -19,6 +19,7 @@
#include <spa/utils/string.h>
#include <spa/utils/json.h>
#include <spa/support/cpu.h>
#include <spa/support/loop.h>
#include <spa/support/plugin-loader.h>
#include <spa/param/latency-utils.h>
#include <spa/param/tag-utils.h>
@ -221,6 +222,7 @@ struct impl {
struct spa_cpu *cpu;
struct spa_fga_dsp *dsp;
struct spa_plugin_loader *loader;
struct spa_loop *data_loop;
uint64_t info_all;
struct spa_filter_graph_info info;
@ -552,19 +554,25 @@ static int impl_get_props(void *object, struct spa_pod_builder *b, struct spa_po
struct descriptor *desc = node->desc;
const struct spa_fga_descriptor *d = desc->desc;
struct spa_fga_port *p = &d->ports[port->p];
float v, min, max;
if (node->name[0] != '\0')
snprintf(name, sizeof(name), "%s:%s", node->name, p->name);
else
snprintf(name, sizeof(name), "%s", p->name);
if (port->control_initialized)
v = port->control_current;
else
get_ranges(impl, p, &v, &min, &max);
spa_pod_builder_string(b, name);
if (p->hint & SPA_FGA_HINT_BOOLEAN) {
spa_pod_builder_bool(b, port->control_data[0] <= 0.0f ? false : true);
spa_pod_builder_bool(b, v <= 0.0f ? false : true);
} else if (p->hint & SPA_FGA_HINT_INTEGER) {
spa_pod_builder_int(b, (int32_t)port->control_data[0]);
spa_pod_builder_int(b, (int32_t)v);
} else {
spa_pod_builder_float(b, port->control_data[0]);
spa_pod_builder_float(b, v);
}
}
spa_pod_builder_pop(b, &f[1]);
@ -698,21 +706,46 @@ static int impl_reset(void *object)
return 0;
}
static void node_control_changed(struct node *node)
static int
do_emit_node_control_sync(struct spa_loop *loop, bool async, uint32_t seq, const void *data,
size_t size, void *user_data)
{
const struct spa_fga_descriptor *d = node->desc->desc;
struct impl *impl = user_data;
struct graph *graph = &impl->graph;
struct node *node;
uint32_t i;
spa_list_for_each(node, &graph->node_list, link) {
const struct spa_fga_descriptor *d = node->desc->desc;
if (!node->control_changed || d->control_sync == NULL)
continue;
for (i = 0; i < node->n_hndl; i++) {
if (node->hndl[i] != NULL)
d->control_sync(node->hndl[i]);
}
}
return 0;
}
static void emit_node_control_changed(struct impl *impl)
{
struct graph *graph = &impl->graph;
struct node *node;
uint32_t i;
if (!node->control_changed)
return;
spa_loop_locked(impl->data_loop, do_emit_node_control_sync, 1, NULL, 0, impl);
for (i = 0; i < node->n_hndl; i++) {
if (node->hndl[i] == NULL)
spa_list_for_each(node, &graph->node_list, link) {
const struct spa_fga_descriptor *d = node->desc->desc;
if (!node->control_changed)
continue;
if (d->control_changed)
d->control_changed(node->hndl[i]);
if (d->control_changed != NULL) {
for (i = 0; i < node->n_hndl; i++) {
if (node->hndl[i] != NULL)
d->control_changed(node->hndl[i]);
}
}
node->control_changed = false;
}
node->control_changed = false;
}
static int sync_volume(struct graph *graph, struct volume *vol)
@ -826,11 +859,7 @@ static int impl_set_props(void *object, enum spa_direction direction, const stru
spa_pod_dynamic_builder_clean(&b);
if (changed > 0) {
struct node *node;
spa_list_for_each(node, &graph->node_list, link)
node_control_changed(node);
emit_node_control_changed(impl);
spa_filter_graph_emit_props_changed(&impl->hooks, SPA_DIRECTION_INPUT);
}
return 0;
@ -1035,8 +1064,8 @@ static struct descriptor *descriptor_load(struct impl *impl, const char *type,
}
} else if (SPA_FGA_IS_PORT_CONTROL(fp->flags)) {
if (SPA_FGA_IS_PORT_INPUT(fp->flags)) {
spa_log_info(impl->log, "using port %lu ('%s') as control %d", p,
fp->name, desc->n_control);
spa_log_info(impl->log, "using port %lu ('%s') as control %d %f/%f/%f", p,
fp->name, desc->n_control, fp->def, fp->min, fp->max);
desc->control[desc->n_control++] = p;
}
else if (SPA_FGA_IS_PORT_OUTPUT(fp->flags)) {
@ -1622,6 +1651,7 @@ static int impl_activate(void *object, const struct spa_dict *props)
goto error;
}
}
node->control_changed = true;
}
/* then link ports */
@ -1695,10 +1725,10 @@ static int impl_activate(void *object, const struct spa_dict *props)
for (i = 0; i < node->n_hndl; i++) {
if (d->activate)
d->activate(node->hndl[i]);
if (node->control_changed && d->control_changed)
d->control_changed(node->hndl[i]);
}
}
emit_node_control_changed(impl);
/* calculate latency */
sort_reset(graph);
while ((node = sort_next_node(graph)) != NULL) {
@ -2346,6 +2376,7 @@ impl_init(const struct spa_handle_factory *factory,
spa_log_topic_init(impl->log, &log_topic);
impl->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU);
impl->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop);
impl->max_align = spa_cpu_get_max_align(impl->cpu);
impl->dsp = spa_fga_dsp_new(impl->cpu ? spa_cpu_get_flags(impl->cpu) : 0);

View file

@ -18,16 +18,16 @@ if have_sse
simd_cargs += ['-DHAVE_SSE']
simd_dependencies += filter_graph_sse
endif
if have_avx2
filter_graph_avx2 = static_library('filter_graph_avx2',
if have_avx2 and have_fma
filter_graph_avx2_fma = static_library('filter_graph_avx2_fma',
['audio-dsp-avx2.c' ],
include_directories : [configinc],
c_args : [avx2_args, fma_args,'-O3', '-DHAVE_AVX2'],
c_args : [avx2_args, fma_args, '-O3', '-DHAVE_AVX2', '-DHAVE_FMA'],
dependencies : [ spa_dep ],
install : false
)
simd_cargs += ['-DHAVE_AVX2']
simd_dependencies += filter_graph_avx2
simd_cargs += ['-DHAVE_AVX2', '-DHAVE_FMA']
simd_dependencies += filter_graph_avx2_fma
endif
if have_neon
filter_graph_neon = static_library('filter_graph_neon',

View file

@ -21,6 +21,7 @@
#include <spa/utils/cleanup.h>
#include <spa/support/cpu.h>
#include <spa/support/log.h>
#include <spa/support/loop.h>
#include <spa/plugins/audioconvert/resample.h>
#include <spa/debug/log.h>
@ -542,7 +543,12 @@ static void bq_run(void *Instance, unsigned long samples)
struct biquad *bq = &impl->bq;
float *out = impl->port[0];
float *in = impl->port[1];
spa_fga_dsp_biquad_run(impl->dsp, bq, 1, 0, &out, (const float **)&in, 1, samples);
}
static void bq_control_sync(void * Instance)
{
struct builtin *impl = Instance;
if (impl->type == BQ_NONE) {
float b0, b1, b2, a0, a1, a2;
b0 = impl->port[5][0];
@ -562,7 +568,6 @@ static void bq_run(void *Instance, unsigned long samples)
if (impl->freq != freq || impl->Q != Q || impl->gain != gain)
bq_freq_update(impl, impl->type, freq, Q, gain);
}
spa_fga_dsp_biquad_run(impl->dsp, bq, 1, 0, &out, (const float **)&in, 1, samples);
}
/** bq_lowpass */
@ -574,6 +579,7 @@ static const struct spa_fga_descriptor bq_lowpass_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -588,6 +594,7 @@ static const struct spa_fga_descriptor bq_highpass_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -602,6 +609,7 @@ static const struct spa_fga_descriptor bq_bandpass_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -616,6 +624,7 @@ static const struct spa_fga_descriptor bq_lowshelf_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -630,6 +639,7 @@ static const struct spa_fga_descriptor bq_highshelf_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -644,6 +654,7 @@ static const struct spa_fga_descriptor bq_peaking_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -658,6 +669,7 @@ static const struct spa_fga_descriptor bq_notch_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -673,6 +685,7 @@ static const struct spa_fga_descriptor bq_allpass_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -687,6 +700,7 @@ static const struct spa_fga_descriptor bq_raw_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -1453,6 +1467,7 @@ static struct spa_fga_port clamp_ports[] = {
{ .index = 3,
.name = "Control",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
.def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
{ .index = 4,
.name = "Min",
@ -1510,6 +1525,7 @@ static struct spa_fga_port linear_ports[] = {
{ .index = 3,
.name = "Control",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
.def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
{ .index = 4,
.name = "Mult",
@ -1577,6 +1593,7 @@ static struct spa_fga_port recip_ports[] = {
{ .index = 3,
.name = "Control",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
.def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
};
@ -1626,6 +1643,7 @@ static struct spa_fga_port exp_ports[] = {
{ .index = 3,
.name = "Control",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
.def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
{ .index = 4,
.name = "Base",
@ -1683,6 +1701,7 @@ static struct spa_fga_port log_ports[] = {
{ .index = 3,
.name = "Control",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
.def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
{ .index = 4,
.name = "Base",
@ -2472,10 +2491,12 @@ static struct spa_fga_port ramp_ports[] = {
{ .index = 1,
.name = "Start",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
.def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
{ .index = 2,
.name = "Stop",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
.def = 1.0f, .min = -FLT_MAX, .max = FLT_MAX
},
{ .index = 3,
.name = "Current",
@ -2484,6 +2505,7 @@ static struct spa_fga_port ramp_ports[] = {
{ .index = 4,
.name = "Duration (s)",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
.def = 1.0f, .min = 0.0f, .max = FLT_MAX
},
};
@ -2643,6 +2665,7 @@ static struct spa_fga_port debug_ports[] = {
{ .index = 2,
.name = "Control",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
.def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
{ .index = 3,
.name = "Notify",
@ -2989,7 +3012,7 @@ static struct spa_fga_port noisegate_ports[] = {
{ .index = 2,
.name = "Level",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
.def = NAN
.def = NAN, .min = -FLT_MAX, .max = FLT_MAX
},
{ .index = 3,
.name = "Open Threshold",
@ -3230,6 +3253,7 @@ static struct spa_fga_port null_ports[] = {
{ .index = 1,
.name = "Control",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
.def = 0.0f, .min = -FLT_MAX, .max = FLT_MAX
},
};

View file

@ -399,6 +399,7 @@ static struct spa_fga_port lufs2gain_ports[] = {
{ .index = 0,
.name = "LUFS",
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
.def = 0.0f, .min = 0.0f, .max = FLT_MAX
},
{ .index = 1,
.name = "Gain",

View file

@ -7,6 +7,7 @@
#include <dlfcn.h>
#include <math.h>
#include <limits.h>
#include <float.h>
#include <spa/utils/result.h>
#include <spa/utils/defs.h>
@ -113,8 +114,14 @@ static void ladspa_port_update_ranges(struct descriptor *dd, struct spa_fga_port
LADSPA_PortRangeHintDescriptor hint = d->PortRangeHints[p].HintDescriptor;
LADSPA_Data lower, upper;
lower = d->PortRangeHints[p].LowerBound;
upper = d->PortRangeHints[p].UpperBound;
if (hint & LADSPA_HINT_BOUNDED_BELOW)
lower = d->PortRangeHints[p].LowerBound;
else
lower = -FLT_MAX;
if (hint & LADSPA_HINT_BOUNDED_ABOVE)
upper = d->PortRangeHints[p].UpperBound;
else
upper = FLT_MAX;
port->hint = 0;
if (hint & LADSPA_HINT_TOGGLED)
@ -226,43 +233,49 @@ static inline const char *split_walk(const char *str, const char *delimiter, siz
return s;
}
static int load_ladspa_plugin(struct plugin *impl, const char *path)
static void make_search_paths(const char **path, const char **search_dirs)
{
const char *p;
while ((p = strstr(*path, "../")) != NULL)
*path = p + 3;
*search_dirs = getenv("LADSPA_PATH");
if (!*search_dirs)
*search_dirs = "/usr/lib64/ladspa:/usr/lib/ladspa:" LIBDIR;
}
static int load_ladspa_plugin(struct plugin *impl, const char *path, const char *search_dirs)
{
int res = -ENOENT;
const char *p, *state = NULL;
char filename[PATH_MAX];
size_t len;
if (path[0] != '/') {
const char *search_dirs, *p, *state = NULL;
char filename[PATH_MAX];
size_t len;
/*
* set the errno for the case when `ladspa_handle_load_by_path()`
* is never called, which can only happen if the supplied
* LADSPA_PATH contains too long paths
*/
res = -ENAMETOOLONG;
search_dirs = getenv("LADSPA_PATH");
if (!search_dirs)
search_dirs = "/usr/lib64/ladspa:/usr/lib/ladspa:" LIBDIR;
while ((p = split_walk(search_dirs, ":", &len, &state))) {
int namelen;
/*
* set the errno for the case when `ladspa_handle_load_by_path()`
* is never called, which can only happen if the supplied
* LADSPA_PATH contains too long paths
*/
res = -ENAMETOOLONG;
while ((p = split_walk(search_dirs, ":", &len, &state))) {
int namelen;
if (len >= sizeof(filename))
continue;
if (len == 0 || len >= sizeof(filename))
continue;
if (strncmp(path, p, len) == 0 && path[len] == '/')
namelen = snprintf(filename, sizeof(filename), "%s", path);
else
namelen = snprintf(filename, sizeof(filename), "%.*s/%s.so", (int) len, p, path);
if (namelen < 0 || (size_t) namelen >= sizeof(filename))
continue;
res = ladspa_handle_load_by_path(impl, filename);
if (res >= 0)
break;
}
}
else {
res = ladspa_handle_load_by_path(impl, path);
if (namelen < 0 || (size_t) namelen >= sizeof(filename))
continue;
res = ladspa_handle_load_by_path(impl, filename);
if (res >= 0)
break;
}
return res;
}
@ -310,7 +323,7 @@ impl_init(const struct spa_handle_factory *factory,
struct plugin *impl;
uint32_t i;
int res;
const char *path = NULL;
const char *path = NULL, *search_dirs;
handle->get_interface = impl_get_interface;
handle->clear = impl_clear;
@ -328,9 +341,11 @@ impl_init(const struct spa_handle_factory *factory,
if (path == NULL)
return -EINVAL;
if ((res = load_ladspa_plugin(impl, path)) < 0) {
spa_log_error(impl->log, "failed to load plugin '%s': %s",
path, spa_strerror(res));
make_search_paths(&path, &search_dirs);
if ((res = load_ladspa_plugin(impl, path, search_dirs)) < 0) {
spa_log_error(impl->log, "failed to load plugin '%s' in '%s': %s",
path, search_dirs, spa_strerror(res));
return res;
}

View file

@ -560,6 +560,17 @@ static const struct spa_fga_descriptor *lv2_plugin_make_desc(void *plugin, const
fp->min = mins[i];
fp->max = maxes[i];
fp->def = controls[i];
if (isnan(fp->min))
fp->min = -FLT_MAX;
if (isnan(fp->max))
fp->max = FLT_MAX;
if (isnan(fp->def))
fp->def = 0.0f;
if (fp->max <= fp->min)
fp->max = FLT_MAX;
if (fp->def <= fp->min)
fp->min = -FLT_MAX;
}
return &desc->desc;
}

View file

@ -593,6 +593,10 @@ static const struct spa_fga_descriptor *onnx_plugin_make_desc(void *plugin, cons
fp->flags |= SPA_FGA_PORT_OUTPUT;
fp->name = ti->data_name;
fp->min = -FLT_MAX;
fp->max = FLT_MAX;
fp->def = 0.0f;
ti->data_index = desc->desc.n_ports;
desc->desc.n_ports++;

View file

@ -5,6 +5,7 @@
/* SPDX-License-Identifier: MIT */
#include <cstddef>
#include <span>
#include <sstream>
#include <spa/support/plugin.h>
@ -25,7 +26,6 @@
#include <libcamera/camera.h>
#include <libcamera/property_ids.h>
#include <libcamera/base/span.h>
using namespace libcamera;
@ -50,7 +50,7 @@ struct impl {
std::string device_id);
};
const libcamera::Span<const int64_t> cameraDevice(const Camera& camera)
std::span<const int64_t> cameraDevice(const Camera& camera)
{
if (auto devices = camera.properties().get(properties::SystemDevices))
return devices.value();

View file

@ -184,9 +184,6 @@ struct impl {
0, nullptr, 0, this
);
if (source.fd >= 0)
spa_system_close(system, std::exchange(source.fd, -1));
camera->requestCompleted.disconnect(this, &impl::requestComplete);
if (int res = camera->stop(); res < 0) {
@ -194,6 +191,9 @@ struct impl {
camera->id().c_str(), spa_strerror(res));
}
if (source.fd >= 0)
spa_system_close(system, std::exchange(source.fd, -1));
completed_requests_rb = SPA_RINGBUFFER_INIT();
active = false;
@ -2163,7 +2163,7 @@ impl::impl(spa_log *log, spa_loop *data_loop, spa_system *system,
&impl_node, this);
params[NODE_PropInfo] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ);
params[NODE_Props] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
params[NODE_Props] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_WRITE);
params[NODE_EnumFormat] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
params[NODE_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);

View file

@ -1,4 +1,4 @@
if alsa_dep.found() and host_machine.system() == 'linux'
if alsa_dep.found()
subdir('alsa')
endif
if get_option('avb').require(host_machine.system() == 'linux', error_message: 'AVB support is only available on Linux').allowed()

View file

@ -78,6 +78,8 @@ x86_init(struct impl *impl)
if ((ebx & AVX512_BITS) == AVX512_BITS)
flags |= SPA_CPU_FLAG_AVX512;
}
if (max_level < 0x16)
flags |= SPA_CPU_FLAG_SLOW_GATHER;
/* Check cpuid level of extended features. */
__cpuid (0x80000000, ext_level, ebx, ecx, edx);

View file

@ -2,12 +2,16 @@
/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#include "config.h"
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <threads.h>
#include <fnmatch.h>
#include <spa/support/log.h>
@ -67,16 +71,10 @@ impl_log_logtv(void *object,
const char *fmt,
va_list args)
{
#define RESERVED_LENGTH 24
struct impl *impl = object;
char timestamp[18] = {0};
char topicstr[32] = {0};
char filename[64] = {0};
char location[1000 + RESERVED_LENGTH], *p, *s;
char location[1024];
static const char * const levels[] = { "-", "E", "W", "I", "D", "T", "*T*" };
const char *prefix = "", *suffix = "";
int size, len;
bool do_trace;
if ((do_trace = (level == SPA_LOG_LEVEL_TRACE && impl->have_source)))
@ -93,8 +91,18 @@ impl_log_logtv(void *object,
suffix = SPA_ANSI_RESET;
}
p = location;
len = sizeof(location) - RESERVED_LENGTH;
struct spa_strbuf msg;
spa_strbuf_init(&msg, location, sizeof(location));
spa_strbuf_append(&msg, "%s[%s]", prefix, levels[level]);
#ifdef HAVE_GETTID
static thread_local pid_t tid;
if (SPA_UNLIKELY(tid == 0))
tid = gettid();
spa_strbuf_append(&msg, "[%jd]", (intmax_t) tid);
#endif
if (impl->local_timestamp) {
char buf[64];
@ -104,67 +112,52 @@ impl_log_logtv(void *object,
clock_gettime(impl->clock_id, &now);
localtime_r(&now.tv_sec, &now_tm);
strftime(buf, sizeof(buf), "%H:%M:%S", &now_tm);
spa_scnprintf(timestamp, sizeof(timestamp), "[%s.%06d]", buf,
spa_strbuf_append(&msg, "[%s.%06d]", buf,
(int)(now.tv_nsec / SPA_NSEC_PER_USEC));
} else if (impl->timestamp) {
struct timespec now;
clock_gettime(impl->clock_id, &now);
spa_scnprintf(timestamp, sizeof(timestamp), "[%05jd.%06jd]",
spa_strbuf_append(&msg, "[%05jd.%06jd]",
(intmax_t) (now.tv_sec & 0x1FFFFFFF) % 100000, (intmax_t) now.tv_nsec / 1000);
}
if (topic && topic->topic)
spa_scnprintf(topicstr, sizeof(topicstr), " %-12s | ", topic->topic);
spa_strbuf_append(&msg, " %-12s | ", topic->topic);
if (impl->line && line != 0) {
s = strrchr(file, '/');
spa_scnprintf(filename, sizeof(filename), "[%16.16s:%5i %s()]",
const char *s = strrchr(file, '/');
spa_strbuf_append(&msg, "[%16.16s:%5i %s()]",
s ? s + 1 : file, line, func);
}
size = spa_scnprintf(p, len, "%s[%s]%s%s%s ", prefix, levels[level],
timestamp, topicstr, filename);
/*
* it is assumed that at this point `size` <= `len`,
* which is reasonable as long as file names and function names
* don't become very long
*/
size += spa_vscnprintf(p + size, len - size, fmt, args);
spa_strbuf_append(&msg, " ");
spa_strbuf_appendv(&msg, fmt, args);
spa_strbuf_append(&msg, "%s\n", suffix);
/*
* `RESERVED_LENGTH` bytes are reserved for printing the suffix
* (at the moment it's "... (truncated)\x1B[0m\n" at its longest - 21 bytes),
* its length must be less than `RESERVED_LENGTH` (including the null byte),
* otherwise a stack buffer overrun could ensue
*/
if (SPA_UNLIKELY(msg.pos >= msg.maxsize)) {
static const char truncated_text[] = "... (truncated)";
size_t suffix_length = strlen(suffix) + strlen(truncated_text) + 1 + 1;
/* if the message could not fit entirely... */
if (size >= len - 1) {
size = len - 1; /* index of the null byte */
len = sizeof(location);
size += spa_scnprintf(p + size, len - size, "... (truncated)");
spa_assert(msg.maxsize >= suffix_length);
msg.pos = msg.maxsize - suffix_length;
spa_strbuf_append(&msg, "%s%s\n", truncated_text, suffix);
spa_assert(msg.pos < msg.maxsize);
}
else {
len = sizeof(location);
}
size += spa_scnprintf(p + size, len - size, "%s\n", suffix);
if (SPA_UNLIKELY(do_trace)) {
uint32_t index;
spa_ringbuffer_get_write_index(&impl->trace_rb, &index);
spa_ringbuffer_write_data(&impl->trace_rb, impl->trace_data, TRACE_BUFFER,
index & (TRACE_BUFFER - 1), location, size);
spa_ringbuffer_write_update(&impl->trace_rb, index + size);
index & (TRACE_BUFFER - 1), msg.buffer, msg.pos);
spa_ringbuffer_write_update(&impl->trace_rb, index + msg.pos);
if (spa_system_eventfd_write(impl->system, impl->source.fd, 1) < 0)
fprintf(impl->file, "error signaling eventfd: %s\n", strerror(errno));
} else
fputs(location, impl->file);
#undef RESERVED_LENGTH
fputs(msg.buffer, impl->file);
}
static SPA_PRINTF_FUNC(6,0) void

View file

@ -462,12 +462,12 @@ again:
* this invoking thread but we need to serialize the flushing here with
* a mutex */
if (loop_thread == 0)
pthread_mutex_lock(&impl->lock);
spa_assert_se(pthread_mutex_lock(&impl->lock) == 0);
flush_all_queues(impl);
if (loop_thread == 0)
pthread_mutex_unlock(&impl->lock);
spa_assert_se(pthread_mutex_unlock(&impl->lock) == 0);
res = item->res;
} else {
@ -482,9 +482,9 @@ again:
recurse = impl->recurse;
while (impl->recurse > 0) {
impl->recurse--;
pthread_mutex_unlock(&impl->lock);
spa_assert_se(pthread_mutex_unlock(&impl->lock) == 0);
}
pthread_mutex_unlock(&impl->lock);
spa_assert_se(pthread_mutex_unlock(&impl->lock) == 0);
}
if ((res = spa_system_eventfd_read(impl->system, queue->ack_fd, &count)) < 0)
@ -492,7 +492,7 @@ again:
queue, queue->ack_fd, spa_strerror(res));
for (i = 0; i < recurse; i++) {
pthread_mutex_lock(&impl->lock);
spa_assert_se(pthread_mutex_lock(&impl->lock) == 0);
impl->recurse++;
}
@ -569,9 +569,14 @@ static int loop_locked(void *object, spa_invoke_func_t func, uint32_t seq,
{
struct impl *impl = object;
int res;
pthread_mutex_lock(&impl->lock);
res = pthread_mutex_lock(&impl->lock);
if (res)
return -res;
res = func(&impl->loop, false, seq, data, size, user_data);
pthread_mutex_unlock(&impl->lock);
spa_assert_se(pthread_mutex_unlock(&impl->lock) == 0);
return res;
}
@ -598,7 +603,7 @@ static void loop_enter(void *object)
struct impl *impl = object;
pthread_t thread_id = pthread_self();
pthread_mutex_lock(&impl->lock);
spa_assert_se(pthread_mutex_lock(&impl->lock) == 0);
if (impl->enter_count == 0) {
spa_return_if_fail(impl->thread == 0);
impl->thread = thread_id;
@ -625,7 +630,7 @@ static void loop_leave(void *object)
impl->thread = 0;
flush_all_queues(impl);
}
pthread_mutex_unlock(&impl->lock);
spa_assert_se(pthread_mutex_unlock(&impl->lock) == 0);
}
static int loop_check(void *object)
@ -644,7 +649,7 @@ static int loop_check(void *object)
/* we could take the lock, check if we actually locked it somewhere */
res = impl->recurse > 0 ? 1 : -EPERM;
pthread_mutex_unlock(&impl->lock);
spa_assert_se(pthread_mutex_unlock(&impl->lock) == 0);
return res;
}
static int loop_lock(void *object)
@ -722,13 +727,20 @@ static int loop_accept(void *object)
}
struct cancellation_handler_data {
struct spa_poll_event *ep;
int ep_count;
struct impl *impl;
const struct spa_poll_event *ep;
volatile int ep_count;
volatile int unlocked;
volatile int locked;
};
static void cancellation_handler(void *closure)
{
const struct cancellation_handler_data *data = closure;
struct impl *impl = data->impl;
if (data->unlocked && !data->locked)
spa_assert_se(pthread_mutex_lock(&impl->lock) == 0);
for (int i = 0; i < data->ep_count; i++) {
struct spa_source *s = data->ep[i].data;
@ -745,20 +757,24 @@ static int loop_iterate_cancel(void *object, int timeout)
struct spa_poll_event ep[MAX_EP], *e;
int i, nfds;
uint32_t remove_count;
struct cancellation_handler_data cdata = { impl, ep, 0, 0, 0 };
spa_return_val_if_fail(impl->enter_count > 0, -EPERM);
pthread_cleanup_push(cancellation_handler, &cdata);
remove_count = impl->remove_count;
spa_loop_control_hook_before(&impl->hooks_list);
pthread_mutex_unlock(&impl->lock);
spa_assert_se((cdata.unlocked = (pthread_mutex_unlock(&impl->lock) == 0)));
nfds = spa_system_pollfd_wait(impl->system, impl->poll_fd, ep, SPA_N_ELEMENTS(ep), timeout);
pthread_mutex_lock(&impl->lock);
spa_assert_se((cdata.locked = (pthread_mutex_lock(&impl->lock) == 0)));
spa_loop_control_hook_after(&impl->hooks_list);
if (remove_count != impl->remove_count)
nfds = 0;
struct cancellation_handler_data cdata = { ep, nfds };
pthread_cleanup_push(cancellation_handler, &cdata);
cdata.ep_count = nfds;
/* first we set all the rmasks, then call the callbacks. The reason is that
* some callback might also want to look at other sources it manages and
@ -794,13 +810,15 @@ static int loop_iterate(void *object, int timeout)
int i, nfds;
uint32_t remove_count;
spa_return_val_if_fail(impl->enter_count > 0, -EPERM);
remove_count = impl->remove_count;
spa_loop_control_hook_before(&impl->hooks_list);
pthread_mutex_unlock(&impl->lock);
spa_assert_se(pthread_mutex_unlock(&impl->lock) == 0);
nfds = spa_system_pollfd_wait(impl->system, impl->poll_fd, ep, SPA_N_ELEMENTS(ep), timeout);
pthread_mutex_lock(&impl->lock);
spa_assert_se(pthread_mutex_lock(&impl->lock) == 0);
spa_loop_control_hook_after(&impl->hooks_list);
if (remove_count != impl->remove_count)
return 0;

View file

@ -91,7 +91,6 @@ struct impl {
struct spa_io_clock *clock;
struct spa_source timer_source;
struct itimerspec timerspec;
int clock_fd;
bool started;
@ -182,13 +181,16 @@ static void set_timeout(struct impl *this, uint64_t next_time)
* returning -ECANCELED.)
* If timerfd is used with a non-realtime clock, the flag is ignored.
* (Note that the flag only works in combination with SPA_FD_TIMER_ABSTIME.) */
struct itimerspec ts;
spa_log_trace(this->log, "set timeout %"PRIu64, next_time);
this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
ts.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
ts.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = 0;
spa_system_timerfd_settime(this->data_system,
this->timer_source.fd, SPA_FD_TIMER_ABSTIME |
SPA_FD_TIMER_CANCEL_ON_SET, &this->timerspec, NULL);
SPA_FD_TIMER_CANCEL_ON_SET, &ts, NULL);
}
static inline uint64_t gettime_nsec(struct impl *this, clockid_t clock_id)
@ -1043,10 +1045,6 @@ impl_init(const struct spa_handle_factory *factory,
this->timer_source.mask = SPA_IO_IN;
this->timer_source.rmask = 0;
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 0;
this->timerspec.it_interval.tv_sec = 0;
this->timerspec.it_interval.tv_nsec = 0;
spa_loop_add_source(this->data_loop, &this->timer_source);

View file

@ -114,7 +114,6 @@ struct impl {
unsigned int started:1;
unsigned int following:1;
struct spa_source timer_source;
struct itimerspec timerspec;
uint64_t next_time;
};
@ -179,11 +178,15 @@ static int impl_node_enum_params(void *object, int seq,
static void set_timeout(struct impl *this, uint64_t next_time)
{
struct itimerspec ts;
spa_log_trace(this->log, "set timeout %"PRIu64, next_time);
this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
ts.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
ts.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = 0;
spa_system_timerfd_settime(this->data_system,
this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &this->timerspec, NULL);
this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &ts, NULL);
}
static int set_timers(struct impl *this)
@ -929,10 +932,6 @@ impl_init(const struct spa_handle_factory *factory,
SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
this->timer_source.mask = SPA_IO_IN;
this->timer_source.rmask = 0;
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 0;
this->timerspec.it_interval.tv_sec = 0;
this->timerspec.it_interval.tv_nsec = 0;
spa_loop_add_source(this->data_loop, &this->timer_source);

View file

@ -30,6 +30,10 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.system");
# define TFD_TIMER_CANCEL_ON_SET (1 << 1)
#endif
SPA_STATIC_ASSERT(sizeof(struct spa_poll_event) == sizeof(struct epoll_event));
SPA_STATIC_ASSERT(offsetof(struct spa_poll_event, events) == offsetof(struct epoll_event, events));
SPA_STATIC_ASSERT(offsetof(struct spa_poll_event, data) == offsetof(struct epoll_event, data.ptr));
struct impl {
struct spa_handle handle;
struct spa_system system;
@ -132,16 +136,9 @@ static int impl_pollfd_del(void *object, int pfd, int fd)
static int impl_pollfd_wait(void *object, int pfd,
struct spa_poll_event *ev, int n_ev, int timeout)
{
struct epoll_event ep[n_ev];
int i, nfds;
if (SPA_UNLIKELY((nfds = epoll_wait(pfd, ep, n_ev, timeout)) < 0))
int nfds;
if (SPA_UNLIKELY((nfds = epoll_wait(pfd, (struct epoll_event*)ev, n_ev, timeout)) < 0))
return -errno;
for (i = 0; i < nfds; i++) {
ev[i].events = ep[i].events;
ev[i].data = ep[i].data.ptr;
}
return nfds;
}

View file

@ -26,10 +26,6 @@
#define SPA_LOG_TOPIC_DEFAULT &log_topic
SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.fakesink");
struct props {
bool live;
};
#define MAX_BUFFERS 16
#define MAX_PORTS 1
@ -68,13 +64,11 @@ struct impl {
uint64_t info_all;
struct spa_node_info info;
struct spa_param_info params[1];
struct props props;
struct spa_hook_list hooks;
struct spa_callbacks callbacks;
struct spa_source timer_source;
struct itimerspec timerspec;
bool started;
uint64_t start_time;
@ -87,13 +81,6 @@ struct impl {
#define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) < MAX_PORTS)
#define DEFAULT_LIVE false
static void reset_props(struct impl *this, struct props *props)
{
props->live = DEFAULT_LIVE;
}
static int impl_node_enum_params(void *object, int seq,
uint32_t id, uint32_t start, uint32_t num,
const struct spa_pod *filter)
@ -116,14 +103,6 @@ static int impl_node_enum_params(void *object, int seq,
spa_pod_builder_init(&b, buffer, sizeof(buffer));
switch (id) {
case SPA_PARAM_Props:
if (result.index > 0)
return 0;
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Props, id,
SPA_PROP_live, SPA_POD_Bool(this->props.live));
break;
default:
return -ENOENT;
}
@ -151,26 +130,7 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
spa_return_val_if_fail(this != NULL, -EINVAL);
switch (id) {
case SPA_PARAM_Props:
{
struct port *port = &this->port;
if (param == NULL) {
reset_props(this, &this->props);
return 0;
}
spa_pod_parse_object(param,
SPA_TYPE_OBJECT_Props, NULL,
SPA_PROP_live, SPA_POD_OPT_Bool(&this->props.live));
if (this->props.live)
port->info.flags |= SPA_PORT_FLAG_LIVE;
else
port->info.flags &= ~SPA_PORT_FLAG_LIVE;
break;
}
default:
return -ENOENT;
}
@ -179,23 +139,15 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
static void set_timer(struct impl *this, bool enabled)
{
if (this->callbacks.funcs || this->props.live) {
if (enabled) {
if (this->props.live) {
uint64_t next_time = this->start_time + this->elapsed_time;
this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
} else {
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 1;
}
} else {
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 0;
}
spa_system_timerfd_settime(this->data_system,
this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &this->timerspec, NULL);
struct itimerspec ts = {0};
if (enabled) {
uint64_t next_time = this->start_time + this->elapsed_time;
ts.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
ts.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
}
spa_system_timerfd_settime(this->data_system, this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &ts, NULL);
}
static inline int read_timer(struct impl *this)
@ -203,14 +155,12 @@ static inline int read_timer(struct impl *this)
uint64_t expirations;
int res = 0;
if (this->callbacks.funcs || this->props.live) {
if ((res = spa_system_timerfd_read(this->data_system,
this->timer_source.fd, &expirations)) < 0) {
if (res != -EAGAIN)
spa_log_error(this->log, "%p: timerfd error: %s",
this, spa_strerror(res));
}
if ((res = spa_system_timerfd_read(this->data_system, this->timer_source.fd, &expirations)) < 0) {
if (res != -EAGAIN)
spa_log_error(this->log, "%p: timerfd error: %s",
this, spa_strerror(res));
}
return res;
}
@ -298,10 +248,7 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
return 0;
clock_gettime(CLOCK_MONOTONIC, &now);
if (this->props.live)
this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
else
this->start_time = 0;
this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
this->buffer_count = 0;
this->elapsed_time = 0;
@ -651,10 +598,8 @@ static int impl_node_process(void *object)
io->buffer_id = SPA_ID_INVALID;
io->status = SPA_STATUS_OK;
}
if (this->callbacks.funcs == NULL)
return consume_buffer(this);
else
return SPA_STATUS_OK;
return SPA_STATUS_OK;
}
static const struct spa_node_methods impl_node = {
@ -758,8 +703,6 @@ impl_init(const struct spa_handle_factory *factory,
this->params[0] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
this->info.params = this->params;
this->info.n_params = 1;
reset_props(this, &this->props);
this->timer_source.func = on_input;
this->timer_source.data = this;
@ -767,10 +710,6 @@ impl_init(const struct spa_handle_factory *factory,
SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
this->timer_source.mask = SPA_IO_IN;
this->timer_source.rmask = 0;
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 0;
this->timerspec.it_interval.tv_sec = 0;
this->timerspec.it_interval.tv_nsec = 0;
if (this->data_loop)
spa_loop_add_source(this->data_loop, &this->timer_source);
@ -779,9 +718,7 @@ impl_init(const struct spa_handle_factory *factory,
port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
SPA_PORT_CHANGE_MASK_PARAMS;
port->info = SPA_PORT_INFO_INIT();
port->info.flags = SPA_PORT_FLAG_NO_REF;
if (this->props.live)
port->info.flags |= SPA_PORT_FLAG_LIVE;
port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_LIVE;
port->params[0] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
port->params[1] = SPA_PARAM_INFO(SPA_PARAM_IO, 0);
port->params[2] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);

View file

@ -27,7 +27,6 @@
SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.fakesrc");
struct props {
bool live;
uint32_t pattern;
};
@ -75,7 +74,6 @@ struct impl {
struct spa_callbacks callbacks;
struct spa_source timer_source;
struct itimerspec timerspec;
bool started;
uint64_t start_time;
@ -89,12 +87,10 @@ struct impl {
#define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) < MAX_PORTS)
#define DEFAULT_LIVE false
#define DEFAULT_PATTERN 0
static void reset_props(struct impl *this, struct props *props)
{
props->live = DEFAULT_LIVE;
props->pattern = DEFAULT_PATTERN;
}
@ -129,7 +125,6 @@ static int impl_node_enum_params(void *object, int seq,
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Props, id,
SPA_PROP_live, SPA_POD_Bool(p->live),
SPA_PROP_patternType, SPA_POD_CHOICE_ENUM_Int(2, p->pattern, p->pattern));
break;
}
@ -164,7 +159,6 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
case SPA_PARAM_Props:
{
struct props *p = &this->props;
struct port *port = &this->port;
if (param == NULL) {
reset_props(this, p);
@ -172,13 +166,7 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
}
spa_pod_parse_object(param,
SPA_TYPE_OBJECT_Props, NULL,
SPA_PROP_live, SPA_POD_OPT_Bool(&p->live),
SPA_PROP_patternType, SPA_POD_OPT_Int(&p->pattern));
if (p->live)
port->info.flags |= SPA_PORT_FLAG_LIVE;
else
port->info.flags &= ~SPA_PORT_FLAG_LIVE;
break;
}
default:
@ -194,23 +182,15 @@ static int fill_buffer(struct impl *this, struct buffer *b)
static void set_timer(struct impl *this, bool enabled)
{
if (this->callbacks.funcs || this->props.live) {
if (enabled) {
if (this->props.live) {
uint64_t next_time = this->start_time + this->elapsed_time;
this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
} else {
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 1;
}
} else {
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 0;
}
spa_system_timerfd_settime(this->data_system,
this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &this->timerspec, NULL);
struct itimerspec ts = {0};
if (enabled) {
uint64_t next_time = this->start_time + this->elapsed_time;
ts.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
ts.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
}
spa_system_timerfd_settime(this->data_system, this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &ts, NULL);
}
static inline int read_timer(struct impl *this)
@ -218,14 +198,12 @@ static inline int read_timer(struct impl *this)
uint64_t expirations;
int res = 0;
if (this->callbacks.funcs || this->props.live) {
if ((res = spa_system_timerfd_read(this->data_system,
this->timer_source.fd, &expirations)) < 0) {
if (res != -EAGAIN)
spa_log_error(this->log, "%p: timerfd error: %s",
this, spa_strerror(res));
}
if ((res = spa_system_timerfd_read(this->data_system, this->timer_source.fd, &expirations)) < 0) {
if (res != -EAGAIN)
spa_log_error(this->log, "%p: timerfd error: %s",
this, spa_strerror(res));
}
return res;
}
@ -311,10 +289,7 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
return 0;
clock_gettime(CLOCK_MONOTONIC, &now);
if (this->props.live)
this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
else
this->start_time = 0;
this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
this->buffer_count = 0;
this->elapsed_time = 0;
@ -682,10 +657,7 @@ static int impl_node_process(void *object)
io->buffer_id = SPA_ID_INVALID;
}
if (this->callbacks.funcs == NULL)
return make_buffer(this);
else
return SPA_STATUS_OK;
return SPA_STATUS_OK;
}
static const struct spa_node_methods impl_node = {
@ -797,10 +769,6 @@ impl_init(const struct spa_handle_factory *factory,
SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
this->timer_source.mask = SPA_IO_IN;
this->timer_source.rmask = 0;
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 0;
this->timerspec.it_interval.tv_sec = 0;
this->timerspec.it_interval.tv_nsec = 0;
if (this->data_loop)
spa_loop_add_source(this->data_loop, &this->timer_source);
@ -809,9 +777,7 @@ impl_init(const struct spa_handle_factory *factory,
port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
SPA_PORT_CHANGE_MASK_PARAMS;
port->info = SPA_PORT_INFO_INIT();
port->info.flags = SPA_PORT_FLAG_NO_REF;
if (this->props.live)
port->info.flags |= SPA_PORT_FLAG_LIVE;
port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_LIVE;
port->params[0] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
port->params[1] = SPA_PARAM_INFO(SPA_PARAM_IO, 0);
port->params[2] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);

View file

@ -98,9 +98,9 @@ static int emit_info(struct impl *this, bool full)
(this->dev.cap.version >> 8) & 0xFF,
(this->dev.cap.version) & 0xFF);
ADD_ITEM(SPA_KEY_API_V4L2_CAP_VERSION, version);
snprintf(capabilities, sizeof(capabilities), "%08x", this->dev.cap.capabilities);
snprintf(capabilities, sizeof(capabilities), "0x%08x", this->dev.cap.capabilities);
ADD_ITEM(SPA_KEY_API_V4L2_CAP_CAPABILITIES, capabilities);
snprintf(device_caps, sizeof(device_caps), "%08x", this->dev.cap.device_caps);
snprintf(device_caps, sizeof(device_caps), "0x%08x", this->dev.cap.device_caps);
ADD_ITEM(SPA_KEY_API_V4L2_CAP_DEVICE_CAPS, device_caps);
#undef ADD_ITEM
info.props = &SPA_DICT_INIT(items, n_items);

View file

@ -35,17 +35,14 @@ enum pattern {
PATTERN_SNOW,
};
#define DEFAULT_LIVE true
#define DEFAULT_PATTERN PATTERN_SMPTE_SNOW
struct props {
bool live;
uint32_t pattern;
};
static void reset_props(struct props *props)
{
props->live = DEFAULT_LIVE;
props->pattern = DEFAULT_PATTERN;
}
@ -97,9 +94,7 @@ struct impl {
struct spa_hook_list hooks;
struct spa_callbacks callbacks;
bool async;
struct spa_source *timer_source;
struct itimerspec timerspec;
bool started;
uint64_t start_time;
@ -141,13 +136,6 @@ static int impl_node_enum_params(void *object, int seq,
switch (result.index) {
case 0:
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_live),
SPA_PROP_INFO_description, SPA_POD_String("Configure live mode of the source"),
SPA_PROP_INFO_type, SPA_POD_Bool(p->live));
break;
case 1:
spa_pod_builder_push_object(&b, &f[0], SPA_TYPE_OBJECT_PropInfo, id);
spa_pod_builder_add(&b,
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_patternType),
@ -176,7 +164,6 @@ static int impl_node_enum_params(void *object, int seq,
case 0:
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Props, id,
SPA_PROP_live, SPA_POD_Bool(p->live),
SPA_PROP_patternType, SPA_POD_Int(p->pattern));
break;
default:
@ -231,7 +218,6 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
case SPA_PARAM_Props:
{
struct props *p = &this->props;
struct port *port = &this->port;
if (param == NULL) {
reset_props(p);
@ -239,13 +225,8 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
}
spa_pod_parse_object(param,
SPA_TYPE_OBJECT_Props, NULL,
SPA_PROP_live, SPA_POD_OPT_Bool(&p->live),
SPA_PROP_patternType, SPA_POD_OPT_Int(&p->pattern));
if (p->live)
port->info.flags |= SPA_PORT_FLAG_LIVE;
else
port->info.flags &= ~SPA_PORT_FLAG_LIVE;
break;
}
default:
@ -263,22 +244,15 @@ static int fill_buffer(struct impl *this, struct buffer *b)
static void set_timer(struct impl *this, bool enabled)
{
if (this->async || this->props.live) {
if (enabled) {
if (this->props.live) {
uint64_t next_time = this->start_time + this->elapsed_time;
this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC;
this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC;
} else {
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 1;
}
} else {
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 0;
}
spa_loop_utils_update_timer(this->loop_utils, this->timer_source, &this->timerspec.it_value, &this->timerspec.it_interval, true);
struct timespec ts = {0};
if (enabled) {
uint64_t next_time = this->start_time + this->elapsed_time;
ts.tv_sec = next_time / SPA_NSEC_PER_SEC;
ts.tv_nsec = next_time % SPA_NSEC_PER_SEC;
}
spa_loop_utils_update_timer(this->loop_utils, this->timer_source, &ts, NULL, true);
}
static int make_buffer(struct impl *this)
@ -358,10 +332,7 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
return 0;
clock_gettime(CLOCK_MONOTONIC, &now);
if (this->props.live)
this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
else
this->start_time = 0;
this->start_time = SPA_TIMESPEC_TO_NSEC(&now);
this->frame_count = 0;
this->elapsed_time = 0;
@ -755,9 +726,6 @@ static inline void reuse_buffer(struct impl *this, struct port *port, uint32_t i
b->outstanding = false;
spa_list_append(&port->empty, &b->link);
if (!this->props.live)
set_timer(this, true);
}
static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
@ -795,10 +763,7 @@ static int impl_node_process(void *object)
io->buffer_id = SPA_ID_INVALID;
}
if (!this->props.live)
return make_buffer(this);
else
return SPA_STATUS_OK;
return SPA_STATUS_OK;
}
static const struct spa_node_methods impl_node = {
@ -907,18 +872,12 @@ impl_init(const struct spa_handle_factory *factory,
reset_props(&this->props);
this->timer_source = spa_loop_utils_add_timer(this->loop_utils, on_output, this);
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 0;
this->timerspec.it_interval.tv_sec = 0;
this->timerspec.it_interval.tv_nsec = 0;
port = &this->port;
port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
SPA_PORT_CHANGE_MASK_PARAMS;
port->info = SPA_PORT_INFO_INIT();
port->info.flags = SPA_PORT_FLAG_NO_REF;
if (this->props.live)
port->info.flags |= SPA_PORT_FLAG_LIVE;
port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_LIVE;
port->params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
port->params[1] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
port->params[2] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);

Some files were not shown because too many files have changed in this diff Show more