mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-04-11 08:21:07 -04:00
Compare commits
235 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5075f27ea0 | ||
|
|
c2f85ffc51 | ||
|
|
b1a9bc966b | ||
|
|
1ed1349e7c | ||
|
|
42415eadd9 | ||
|
|
eeaddbb385 | ||
|
|
f5259828b6 | ||
|
|
14310e66fe | ||
|
|
ffa855d76e | ||
|
|
cec53759dd | ||
|
|
fdfede8b96 | ||
|
|
e661c72272 | ||
|
|
f5c5c9d7a3 | ||
|
|
ef4ff8cfd0 | ||
|
|
a73988d38d | ||
|
|
d9821d09c7 | ||
|
|
4e62826e01 | ||
|
|
3f386ecd34 | ||
|
|
1d0c51f057 | ||
|
|
ef8f820d4a | ||
|
|
bdaecfebb8 | ||
|
|
20d648aaad | ||
|
|
dc47f9ea45 | ||
|
|
0f00ad19cb | ||
|
|
bf7f2a5d88 | ||
|
|
49073cf527 | ||
|
|
446e36807f | ||
|
|
7012594926 | ||
|
|
fbbc4271a3 | ||
|
|
0e0c325194 | ||
|
|
abd8c8f666 | ||
|
|
5474c3c3a5 | ||
|
|
1a3df16e27 | ||
|
|
d7be4353ad | ||
|
|
337801717e | ||
|
|
c9ecbf9fab | ||
|
|
247918339e | ||
|
|
50aacea749 | ||
|
|
57299da899 | ||
|
|
785bf36b9b | ||
|
|
8fd7982087 | ||
|
|
ad0bab69a1 | ||
|
|
6b0248d68c | ||
|
|
54a4515b09 | ||
|
|
ee1b429441 | ||
|
|
a35b6b0c4b | ||
|
|
49d5f4f236 | ||
|
|
67dd3549a7 | ||
|
|
54c517b2d9 | ||
|
|
0121bdc475 | ||
|
|
5b86e3d418 | ||
|
|
3080bca85a | ||
|
|
41b5bc662e | ||
|
|
9ba92bd728 | ||
|
|
18c97222c4 | ||
|
|
03f894bab0 | ||
|
|
7f4baba41c | ||
|
|
00b4717c6e | ||
|
|
84e7b744a6 | ||
|
|
29b221671f | ||
|
|
7599079d61 | ||
|
|
60062432b8 | ||
|
|
50fcf64058 | ||
|
|
9eeb2f1930 | ||
|
|
7fe191bc10 | ||
|
|
ea28343166 | ||
|
|
67070762d0 | ||
|
|
0d1280a5b2 | ||
|
|
22c1a16dce | ||
|
|
39f4cbfc98 | ||
|
|
bc87bc8588 | ||
|
|
4f56442249 | ||
|
|
e1f53b7f39 | ||
|
|
2e7aee3573 | ||
|
|
01b9abc5ef | ||
|
|
d47b4974ce | ||
|
|
52b23384e0 | ||
|
|
9ba0c3cfd3 | ||
|
|
c02cdcb5ce | ||
|
|
3dff64364f | ||
|
|
1dd8729d13 | ||
|
|
115525d000 | ||
|
|
3b422e31a2 | ||
|
|
5ade045654 | ||
|
|
244c3b597f | ||
|
|
41d8ce7fff | ||
|
|
b16a2e41e8 | ||
|
|
e4693ebc83 | ||
|
|
6bf81ebe59 | ||
|
|
f4587ea434 | ||
|
|
8db1153519 | ||
|
|
36740e0100 | ||
|
|
edb074f438 | ||
|
|
f846879399 | ||
|
|
e5809ee052 | ||
|
|
1a37f445a2 | ||
|
|
653b8703bc | ||
|
|
90fd6fbc65 | ||
|
|
11d1f3653a | ||
|
|
b1b5653393 | ||
|
|
6544996a33 | ||
|
|
3c1b8dcdcc | ||
|
|
3273ba6333 | ||
|
|
95eac7b4e5 | ||
|
|
9098996b87 | ||
|
|
00148467ef | ||
|
|
e4e5f62d44 | ||
|
|
16bde0c61d | ||
|
|
1344bec7bb | ||
|
|
f4558472df | ||
|
|
3e209f6d20 | ||
|
|
41520f1022 | ||
|
|
ca91b368c1 | ||
|
|
45e3af5cdc | ||
|
|
ad195b289a | ||
|
|
f11ab0da3e | ||
|
|
0393fd8a72 | ||
|
|
357f27658e | ||
|
|
a671625637 | ||
|
|
9495e2b8a9 | ||
|
|
7c5b5d12ed | ||
|
|
b0065bfe9a | ||
|
|
e447b46d36 | ||
|
|
283052c15a | ||
|
|
d17e68c43f | ||
|
|
cd84d007cd | ||
|
|
469929d4f6 | ||
|
|
9c7aa4d423 | ||
|
|
2f65cf7124 | ||
|
|
6e332a5d32 | ||
|
|
00f1d6c3d8 | ||
|
|
f45e619559 | ||
|
|
5cc63c1b34 | ||
|
|
5f4b422ab1 | ||
|
|
7ecd51dc80 | ||
|
|
6a3ac7f7b0 | ||
|
|
8f22785cf0 | ||
|
|
5c67ab2a7a | ||
|
|
d33066cdd7 | ||
|
|
810617997b | ||
|
|
a661f14d2c | ||
|
|
dc1738ce57 | ||
|
|
abf37dbdde | ||
|
|
5e37d43881 | ||
|
|
22a5fad902 | ||
|
|
75c3d3ecf8 | ||
|
|
2548fcad25 | ||
|
|
f6939a1cf0 | ||
|
|
c745582ef5 | ||
|
|
f7bb4c95ed | ||
|
|
99079dd955 | ||
|
|
d42646e91f | ||
|
|
67b4732c26 | ||
|
|
5f8ece7017 | ||
|
|
f9e2b1d8b9 | ||
|
|
ddf63e0863 | ||
|
|
231a41a22f | ||
|
|
6f73e42d05 | ||
|
|
3a2d16a3bc | ||
|
|
48c22e2aa7 | ||
|
|
d1c372f5ee | ||
|
|
70b7b42f5d | ||
|
|
a179e8c695 | ||
|
|
784ad934a4 | ||
|
|
118d8574c8 | ||
|
|
437a8d32f2 | ||
|
|
106b4a37d4 | ||
|
|
e6a5951a47 | ||
|
|
efd1526423 | ||
|
|
49300d8ee0 | ||
|
|
a3853c2c3d | ||
|
|
95e89f786a | ||
|
|
182f52603c | ||
|
|
ea21281f38 | ||
|
|
797cdbc72f | ||
|
|
b8e27cc02b | ||
|
|
6e2522b657 | ||
|
|
87087a4629 | ||
|
|
f4e174870e | ||
|
|
d206b06c70 | ||
|
|
06f336a581 | ||
|
|
8ceb671cc8 | ||
|
|
354ec08b9b | ||
|
|
b05fcdbfbd | ||
|
|
8eed27820c | ||
|
|
ee18160c4e | ||
|
|
9ad5ca2e5a | ||
|
|
dee2d5ee06 | ||
|
|
3a1b790588 | ||
|
|
a065cff8c1 | ||
|
|
db713c8264 | ||
|
|
a1db2b8d35 | ||
|
|
c09bcfdc97 | ||
|
|
cd77dd0e62 | ||
|
|
79d8e7f61d | ||
|
|
b67937f247 | ||
|
|
fa04146cfb | ||
|
|
4a399172b6 | ||
|
|
d6654e84a7 | ||
|
|
6daa8ccc0d | ||
|
|
be3c63d55e | ||
|
|
03662b3dfe | ||
|
|
bdbb5f6d27 | ||
|
|
7dd924797b | ||
|
|
ed361a856f | ||
|
|
6753c51ab8 | ||
|
|
692590b30a | ||
|
|
848ac24490 | ||
|
|
6961bfeaa1 | ||
|
|
5a285602e2 | ||
|
|
426a5be235 | ||
|
|
ff04b47942 | ||
|
|
c847b81629 | ||
|
|
367ce4626c | ||
|
|
846096d435 | ||
|
|
476220c18b | ||
|
|
973f48dde7 | ||
|
|
9e82e49446 | ||
|
|
ce18660127 | ||
|
|
8f3d8d77ab | ||
|
|
5bd93b97ad | ||
|
|
e46bfe67b6 | ||
|
|
d7c3e8c2bc | ||
|
|
d8b06f94ee | ||
|
|
d4329600d1 | ||
|
|
c244fbf945 | ||
|
|
f5107f3e83 | ||
|
|
569c2dce55 | ||
|
|
987579b7b7 | ||
|
|
2fb38af3e0 | ||
|
|
7887c365d1 | ||
|
|
e7ca02c4d8 | ||
|
|
be0e037809 | ||
|
|
6eb4483069 | ||
|
|
b8b2c58cda |
211 changed files with 17098 additions and 5262 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
393
doc/dox/internals/running.dox
Normal file
393
doc/dox/internals/running.dox
Normal 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.
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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': {},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
573
po/kk.po
|
|
@ -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
382
po/sv.po
|
|
@ -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"
|
||||
|
|
|
|||
117
po/zh_CN.po
117
po/zh_CN.po
|
|
@ -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 "蓝牙免提"
|
||||
|
||||
|
|
|
|||
540
po/zh_TW.po
540
po/zh_TW.po
|
|
@ -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 "頭戴式耳機"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
445
spa/include/spa/utils/json-builder.h
Normal file
445
spa/include/spa/utils/json-builder.h
Normal 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 */
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
63
spa/plugins/audioconvert/channelmix-ops-avx.c
Normal file
63
spa/plugins/audioconvert/channelmix-ops-avx.c
Normal 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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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++) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
struct volume {
|
||||
uint32_t cpu_flags;
|
||||
uint32_t func_cpu_flags;
|
||||
const char *func_name;
|
||||
|
||||
struct spa_log *log;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue