Overview

Request 5748 (accepted)

New upstream release, NOTE do not ack until https://build.opensuse.org/request/show/1079411 is acked and published in TW

Submit package home:zaitor:...s:Essentials / pipewire-aptx to package Essentials / pipewire-aptx

pipewire-aptx.changes Changed
x
 
1
@@ -1,4 +1,9 @@
2
 -------------------------------------------------------------------
3
+Fri Apr 14 13:35:34 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
4
+
5
+- Update to version 0.3.69
6
+
7
+-------------------------------------------------------------------
8
 Sat Apr  8 17:49:24 UTC 2023 - Bjørn Lie <zaitor@opensuse.org>
9
 
10
 - Update to version 0.3.68
11
pipewire-aptx.spec Changed
19
 
1
@@ -7,7 +7,7 @@
2
 %define soversion 0_2
3
 
4
 Name:           pipewire-aptx
5
-Version:        0.3.68
6
+Version:        0.3.69
7
 Release:        0
8
 Summary:        PipeWire Bluetooth aptX codec plugin
9
 License:        MIT
10
@@ -16,7 +16,7 @@
11
 
12
 BuildRequires:  c++_compiler
13
 BuildRequires:  c_compiler
14
-BuildRequires:  meson >= 0.59.0
15
+BuildRequires:  meson >= 0.61.1
16
 BuildRequires:  pkgconfig
17
 BuildRequires:  pkgconfig(bluez)
18
 BuildRequires:  pkgconfig(dbus-1)
19
pipewire-0.3.68.tar.gz/NEWS -> pipewire-0.3.69.tar.gz/NEWS Changed
71
 
1
@@ -1,3 +1,59 @@
2
+# PipeWire 0.3.69 (2023-04-13)
3
+
4
+This is a quick bugfix release that is API and ABI compatible with previous
5
+0.3.x releases.
6
+
7
+## Highlights
8
+  - Reverted the UCM changes, they seem to cause regressions causing audio
9
+    to be muted in some cases.
10
+  - Fix a regression in the scheduler where a driver node might not be marked
11
+    runnable in some cases, like when echo-cancel is used. (#3145)
12
+  - Handle links from the driver to itself. This makes the midi bridge work
13
+    again. (#3153)
14
+  - ALSA rate matching for sources was fixed. It would previously wait too
15
+    long for rate matching and then cause drift. This should reduce
16
+    crackling and stuttering whan capturing in low latency.
17
+  - Fix the GStreamer clock to make cheese video recording work again. (#3149)
18
+  - More fixes and improvements.
19
+
20
+## PipeWire
21
+  - Fix a regression in the scheduler where a driver node might not be marked
22
+    runnable in some cases, like when echo-cancel is used. (#3145)
23
+  - Handle links from the driver to itself. This makes the midi bridge work
24
+    again. (#3153)
25
+  - Some man pages were improved.
26
+  - Fix a potential crash when thread-loop is destroyed before the loop.
27
+    (#3150)
28
+
29
+## Modules
30
+  - A new raw biquad filter was added to filter-chain. You can manually set the
31
+    6 parameters and you can use this to create custom filters per sample rate.
32
+    (#3139)
33
+  - The echo-canceler now supports different channels for the capture and playback
34
+    streams.
35
+
36
+## SPA
37
+  - A SB Audigy specific profile set was added to make better use of the
38
+    controls. (#2934)
39
+  - More ALSA IRQ based scheduling improvements.
40
+  - ALSA rate matching for sources was fixed. It would previously wait too
41
+    long for rate matching and then cause drift. This should reduce
42
+    crackling and stuttering whan capturing in low latency.
43
+  - The echo-cancel plugin API has a new method to make it possible to have
44
+    different channels for capture, source and playback.
45
+  - Reverted the UCM changes, they seem to cause regressions causing audio
46
+    to be muted in some cases.
47
+
48
+## Bluetooth
49
+  - Many more BAP fixes and improvements. Devices are now created as a set
50
+    and can be combined into one device by the session manager.
51
+
52
+## GStreamer
53
+  - Fix the GStreamer clock to make cheese video recording work again. (#3149)
54
+
55
+
56
+Older versions:
57
+
58
 # PipeWire 0.3.68 (2023-04-06)
59
 
60
 This is a bugfix release that is API and ABI compatible with previous
61
@@ -135,9 +191,6 @@
62
 ## GStreamer
63
   - Sort the device by priority in deviceprovider. (#3072)
64
 
65
-
66
-Older versions:
67
-
68
 # PipeWire 0.3.67 (2023-03-09)
69
 
70
 This is a bugfix release that is API and ABI compatible with previous
71
pipewire-0.3.68.tar.gz/man/pipewire.conf.5.rst.in -> pipewire-0.3.69.tar.gz/man/pipewire.conf.5.rst.in Changed
11
 
1
@@ -69,8 +69,7 @@
2
 of use, a relaxed format may be used where:
3
 
4
  * ``:`` to delimit keys and values can be substuted by ``=`` or a space.
5
- * ``"`` around keys and string can be omited as long as no special characters
6
-         are used in the strings.
7
+ * ``"`` around keys and string can be omited as long as no special characters are used in the strings.
8
  * ``,`` to separate objects can be replaced with a whitespace character.
9
  * ``#`` can be used to start a comment until the line end
10
 
11
pipewire-0.3.68.tar.gz/man/pw-cli.1.rst.in -> pipewire-0.3.69.tar.gz/man/pw-cli.1.rst.in Changed
12
 
1
@@ -174,10 +174,6 @@
2
 send-command *object-id*
3
   Send a command to an object.
4
 
5
-
6
-EXAMPLES
7
-========
8
-
9
 AUTHORS
10
 =======
11
 
12
pipewire-0.3.68.tar.gz/man/pw-metadata.1.rst.in -> pipewire-0.3.69.tar.gz/man/pw-metadata.1.rst.in Changed
28
 
1
@@ -48,11 +48,23 @@
2
   Keeps running and log the changes to the metadata.
3
 
4
 -d | --delete
5
+  Delete all metadata for *id* or for the specified *key* of object *id*.
6
+  Without any option, all metadata is removed.
7
 
8
-  Delete all metadata for *id* or for the
9
-  specified *key* of object *id*
10
+-n | --name
11
+  Metadata name (Default: "default").
12
 
13
-  Without any option, all metadata is removed
14
+EXAMPLES
15
+========
16
+
17
+**pw-metadata**
18
+  Show metadata in default name.
19
+
20
+**pw-metadata** -n settings 0
21
+  Display settings.
22
+
23
+**pw-metadata** -n settings 0 clock.quantum 1024
24
+  Change clock.quantum to 1024.
25
 
26
 AUTHORS
27
 =======
28
pipewire-0.3.68.tar.gz/meson.build -> pipewire-0.3.69.tar.gz/meson.build Changed
8
 
1
@@ -1,5 +1,5 @@
2
 project('pipewire', 'c' ,
3
-  version : '0.3.68',
4
+  version : '0.3.69',
5
   license :  'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' ,
6
   meson_version : '>= 0.61.1',
7
   default_options :  'warning_level=3',
8
pipewire-0.3.68.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.69.tar.gz/pipewire-jack/src/pipewire-jack.c Changed
97
 
1
@@ -1361,10 +1361,16 @@
2
    return c->sample_rate == sample_rate;
3
 }
4
 
5
+static inline uint64_t get_time_ns(void)
6
+{
7
+   struct timespec ts;
8
+   clock_gettime(CLOCK_MONOTONIC, &ts);
9
+   return SPA_TIMESPEC_TO_NSEC(&ts);
10
+}
11
+
12
 static inline uint32_t cycle_run(struct client *c)
13
 {
14
    uint64_t cmd;
15
-   struct timespec ts;
16
    int fd = c->socket_source->fd;
17
    struct spa_io_position *pos = c->rt.position;
18
    struct pw_node_activation *activation = c->activation;
19
@@ -1383,9 +1389,8 @@
20
    if (SPA_UNLIKELY(cmd > 1))
21
        pw_log_info("%p: missed %"PRIu64" wakeups", c, cmd - 1);
22
 
23
-   clock_gettime(CLOCK_MONOTONIC, &ts);
24
    activation->status = PW_NODE_ACTIVATION_AWAKE;
25
-   activation->awake_time = SPA_TIMESPEC_TO_NSEC(&ts);
26
+   activation->awake_time = get_time_ns();
27
 
28
    if (SPA_UNLIKELY(c->first)) {
29
        if (c->thread_init_callback)
30
@@ -1442,15 +1447,13 @@
31
 
32
 static inline void signal_sync(struct client *c)
33
 {
34
-   struct timespec ts;
35
    uint64_t cmd, nsec;
36
    struct link *l;
37
    struct pw_node_activation *activation = c->activation;
38
 
39
    complete_process(c, c->buffer_frames);
40
 
41
-   clock_gettime(CLOCK_MONOTONIC, &ts);
42
-   nsec = SPA_TIMESPEC_TO_NSEC(&ts);
43
+   nsec = get_time_ns();
44
    activation->status = PW_NODE_ACTIVATION_FINISHED;
45
    activation->finish_time = nsec;
46
 
47
@@ -5700,7 +5703,6 @@
48
 {
49
    struct client *c = (struct client *) client;
50
    struct spa_io_position *pos;
51
-   struct timespec ts;
52
    uint64_t diff;
53
 
54
    spa_return_val_if_fail(c != NULL, 0);
55
@@ -5708,17 +5710,14 @@
56
    if (SPA_UNLIKELY((pos = c->rt.position) == NULL))
57
        return 0;
58
 
59
-   clock_gettime(CLOCK_MONOTONIC, &ts);
60
-   diff = SPA_TIMESPEC_TO_NSEC(&ts) - pos->clock.nsec;
61
+   diff = get_time_ns() - pos->clock.nsec;
62
    return (jack_nframes_t) floor(((double)c->sample_rate * diff) / SPA_NSEC_PER_SEC);
63
 }
64
 
65
 SPA_EXPORT
66
 jack_nframes_t jack_frame_time (const jack_client_t *client)
67
 {
68
-   struct timespec ts;
69
-   clock_gettime(CLOCK_MONOTONIC, &ts);
70
-   return jack_time_to_frames(client, SPA_TIMESPEC_TO_USEC(&ts));
71
+   return jack_time_to_frames(client, jack_get_time());
72
 }
73
 
74
 SPA_EXPORT
75
@@ -5801,9 +5800,7 @@
76
 SPA_EXPORT
77
 jack_time_t jack_get_time(void)
78
 {
79
-   struct timespec ts;
80
-   clock_gettime(CLOCK_MONOTONIC, &ts);
81
-   return SPA_TIMESPEC_TO_USEC(&ts);
82
+   return get_time_ns()/SPA_NSEC_PER_USEC;
83
 }
84
 
85
 SPA_EXPORT
86
@@ -6000,9 +5997,7 @@
87
    running = pos->clock.position - pos->offset;
88
 
89
    if (pos->state == SPA_IO_POSITION_STATE_RUNNING) {
90
-       struct timespec ts;
91
-       clock_gettime(CLOCK_MONOTONIC, &ts);
92
-       uint64_t nsecs = SPA_TIMESPEC_TO_NSEC(&ts) - pos->clock.nsec;
93
+       uint64_t nsecs = get_time_ns() - pos->clock.nsec;
94
        running += (uint64_t)floor((((double) c->sample_rate) / SPA_NSEC_PER_SEC) * nsecs);
95
    }
96
    seg = &pos->segments0;
97
pipewire-0.3.68.tar.gz/spa/include/spa/interfaces/audio/aec.h -> pipewire-0.3.69.tar.gz/spa/include/spa/interfaces/audio/aec.h Changed
31
 
1
@@ -40,7 +40,7 @@
2
 };
3
 
4
 struct spa_audio_aec_methods {
5
-#define SPA_VERSION_AUDIO_AEC_METHODS  2
6
+#define SPA_VERSION_AUDIO_AEC_METHODS  3
7
         uint32_t version;
8
 
9
    int (*add_listener) (void *object,
10
@@ -60,6 +60,12 @@
11
    int (*enum_props) (void* object, int index, struct spa_pod_builder* builder);
12
    int (*get_params) (void* object, struct spa_pod_builder* builder);
13
    int (*set_params) (void *object, const struct spa_pod *args);
14
+
15
+   /* version 1:3 */
16
+   int (*init2) (void *object, const struct spa_dict *args,
17
+           struct spa_audio_info_raw *play_info,
18
+           struct spa_audio_info_raw *rec_info,
19
+           struct spa_audio_info_raw *out_info);
20
 };
21
 
22
 #define spa_audio_aec_method(o,method,version,...)         \
23
@@ -81,6 +87,7 @@
24
 #define spa_audio_aec_enum_props(o,...)        spa_audio_aec_method(o, enum_props, 2, __VA_ARGS__)
25
 #define spa_audio_aec_get_params(o,...)        spa_audio_aec_method(o, get_params, 2, __VA_ARGS__)
26
 #define spa_audio_aec_set_params(o,...)        spa_audio_aec_method(o, set_params, 2, __VA_ARGS__)
27
+#define spa_audio_aec_init2(o,...)     spa_audio_aec_method(o, init2, 3, __VA_ARGS__)
28
 
29
 #ifdef __cplusplus
30
 }  /* extern "C" */
31
pipewire-0.3.68.tar.gz/spa/plugins/aec/aec-webrtc.cpp -> pipewire-0.3.69.tar.gz/spa/plugins/aec/aec-webrtc.cpp Changed
150
 
1
@@ -22,7 +22,9 @@
2
 
3
    struct spa_log *log;
4
    std::unique_ptr<webrtc::AudioProcessing> apm;
5
-   spa_audio_info_raw info;
6
+   spa_audio_info_raw rec_info;
7
+   spa_audio_info_raw out_info;
8
+   spa_audio_info_raw play_info;
9
    std::unique_ptr<float *> play_buffer, rec_buffer, out_buffer;
10
 };
11
 
12
@@ -38,9 +40,12 @@
13
    return default_value;
14
 }
15
 
16
-static int webrtc_init(void *object, const struct spa_dict *args, const struct spa_audio_info_raw *info)
17
+static int webrtc_init2(void *object, const struct spa_dict *args,
18
+       struct spa_audio_info_raw *rec_info, struct spa_audio_info_raw *out_info,
19
+       struct spa_audio_info_raw *play_info)
20
 {
21
    auto impl = static_cast<struct impl_data*>(object);
22
+   int res;
23
 
24
    bool extended_filter = webrtc_get_spa_bool(args, "webrtc.extended_filter", true);
25
    bool delay_agnostic = webrtc_get_spa_bool(args, "webrtc.delay_agnostic", true);
26
@@ -67,16 +72,16 @@
27
    config.Set<webrtc::ExperimentalNs>(new webrtc::ExperimentalNs(experimental_ns));
28
 
29
    webrtc::ProcessingConfig pconfig = {{
30
-       webrtc::StreamConfig(info->rate, info->channels, false), /* input stream */
31
-       webrtc::StreamConfig(info->rate, info->channels, false), /* output stream */
32
-       webrtc::StreamConfig(info->rate, info->channels, false), /* reverse input stream */
33
-       webrtc::StreamConfig(info->rate, info->channels, false), /* reverse output stream */
34
+       webrtc::StreamConfig(rec_info->rate, rec_info->channels, false), /* input stream */
35
+       webrtc::StreamConfig(out_info->rate, out_info->channels, false), /* output stream */
36
+       webrtc::StreamConfig(play_info->rate, play_info->channels, false), /* reverse input stream */
37
+       webrtc::StreamConfig(play_info->rate, play_info->channels, false), /* reverse output stream */
38
    }};
39
 
40
    auto apm = std::unique_ptr<webrtc::AudioProcessing>(webrtc::AudioProcessing::Create(config));
41
-   if (apm->Initialize(pconfig) != webrtc::AudioProcessing::kNoError) {
42
-       spa_log_error(impl->log, "Error initialising webrtc audio processing module");
43
-       return -1;
44
+   if ((res = apm->Initialize(pconfig)) != webrtc::AudioProcessing::kNoError) {
45
+       spa_log_error(impl->log, "Error initialising webrtc audio processing module: %d", res);
46
+       return -EINVAL;
47
    }
48
 
49
    apm->high_pass_filter()->Enable(high_pass_filter);
50
@@ -94,48 +99,72 @@
51
    apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital);
52
    apm->gain_control()->Enable(gain_control);
53
    impl->apm = std::move(apm);
54
-   impl->info = *info;
55
-   impl->play_buffer = std::make_unique<float *>(info->channels);
56
-   impl->rec_buffer = std::make_unique<float *>(info->channels);
57
-   impl->out_buffer = std::make_unique<float *>(info->channels);
58
+   impl->rec_info = *rec_info;
59
+   impl->out_info = *out_info;
60
+   impl->play_info = *play_info;
61
+   impl->play_buffer = std::make_unique<float *>(play_info->channels);
62
+   impl->rec_buffer = std::make_unique<float *>(rec_info->channels);
63
+   impl->out_buffer = std::make_unique<float *>(out_info->channels);
64
    return 0;
65
 }
66
 
67
+static int webrtc_init(void *object, const struct spa_dict *args,
68
+       const struct spa_audio_info_raw *info)
69
+{
70
+   int res;
71
+   struct spa_audio_info_raw rec_info = *info;
72
+   struct spa_audio_info_raw out_info = *info;
73
+   struct spa_audio_info_raw play_info = *info;
74
+   res = webrtc_init2(object, args, &rec_info, &out_info, &play_info);
75
+   if (rec_info.channels != out_info.channels)
76
+       res = -EINVAL;
77
+   return res;
78
+}
79
+
80
 static int webrtc_run(void *object, const float *rec, const float *play, float *out, uint32_t n_samples)
81
 {
82
    auto impl = static_cast<struct impl_data*>(object);
83
-   webrtc::StreamConfig config =
84
-       webrtc::StreamConfig(impl->info.rate, impl->info.channels, false);
85
-   unsigned int num_blocks = n_samples * 1000 / impl->info.rate / 10;
86
+   int res;
87
+
88
+   webrtc::StreamConfig play_config =
89
+       webrtc::StreamConfig(impl->play_info.rate, impl->play_info.channels, false);
90
+   webrtc::StreamConfig rec_config =
91
+       webrtc::StreamConfig(impl->rec_info.rate, impl->rec_info.channels, false);
92
+   webrtc::StreamConfig out_config =
93
+       webrtc::StreamConfig(impl->out_info.rate, impl->out_info.channels, false);
94
+   unsigned int num_blocks = n_samples * 1000 / impl->play_info.rate / 10;
95
 
96
-   if (n_samples * 1000 / impl->info.rate % 10 != 0) {
97
+   if (n_samples * 1000 / impl->play_info.rate % 10 != 0) {
98
        spa_log_error(impl->log, "Buffers must be multiples of 10ms in length (currently %u samples)", n_samples);
99
-       return -1;
100
+       return -EINVAL;
101
    }
102
 
103
    for (size_t i = 0; i < num_blocks; i ++) {
104
-       for (size_t j = 0; j < impl->info.channels; j++) {
105
-           impl->play_bufferj = const_cast<float *>(playj) + config.num_frames() * i;
106
-           impl->rec_bufferj = const_cast<float *>(recj) + config.num_frames() * i;
107
-           impl->out_bufferj = outj + config.num_frames() * i;
108
-       }
109
+       for (size_t j = 0; j < impl->play_info.channels; j++)
110
+           impl->play_bufferj = const_cast<float *>(playj) + play_config.num_frames() * i;
111
+       for (size_t j = 0; j < impl->rec_info.channels; j++)
112
+           impl->rec_bufferj = const_cast<float *>(recj) + rec_config.num_frames() * i;
113
+       for (size_t j = 0; j < impl->out_info.channels; j++)
114
+           impl->out_bufferj = outj + out_config.num_frames() * i;
115
+
116
        /* FIXME: ProcessReverseStream may change the playback buffer, in which
117
        * case we should use that, if we ever expose the intelligibility
118
        * enhancer */
119
-       if (impl->apm->ProcessReverseStream(impl->play_buffer.get(), config, config, impl->play_buffer.get()) !=
120
+       if ((res = impl->apm->ProcessReverseStream(impl->play_buffer.get(),
121
+                   play_config, play_config, impl->play_buffer.get())) !=
122
                webrtc::AudioProcessing::kNoError) {
123
-           spa_log_error(impl->log, "Processing reverse stream failed");
124
+           spa_log_error(impl->log, "Processing reverse stream failed: %d", res);
125
        }
126
 
127
        // Extra delay introduced by multiple frames
128
        impl->apm->set_stream_delay_ms((num_blocks - 1) * 10);
129
 
130
-       if (impl->apm->ProcessStream(impl->rec_buffer.get(), config, config, impl->out_buffer.get()) !=
131
+       if ((res = impl->apm->ProcessStream(impl->rec_buffer.get(),
132
+                   rec_config, out_config, impl->out_buffer.get())) !=
133
                webrtc::AudioProcessing::kNoError) {
134
-           spa_log_error(impl->log, "Processing stream failed");
135
+           spa_log_error(impl->log, "Processing stream failed: %d", res);
136
        }
137
    }
138
-
139
    return 0;
140
 }
141
 
142
@@ -144,6 +173,7 @@
143
    .add_listener = NULL,
144
    .init = webrtc_init,
145
    .run = webrtc_run,
146
+   .init2 = webrtc_init2,
147
 };
148
 
149
 static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
150
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/acp.c -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/acp.c Changed
131
 
1
@@ -362,7 +362,7 @@
2
                            devstr, NULL, &m->sample_spec,
3
                            &m->channel_map, SND_PCM_STREAM_PLAYBACK,
4
                            &try_period_size, &try_buffer_size,
5
-                           0, NULL, NULL, NULL, NULL, false))) {
6
+                           0, NULL, NULL, false))) {
7
                pa_alsa_init_proplist_pcm(NULL, m->output_proplist, m->output_pcm);
8
                pa_proplist_setf(m->output_proplist, "clock.name", "api.alsa.%u", index);
9
                pa_alsa_close(&m->output_pcm);
10
@@ -392,7 +392,7 @@
11
                            devstr, NULL, &m->sample_spec,
12
                            &m->channel_map, SND_PCM_STREAM_CAPTURE,
13
                            &try_period_size, &try_buffer_size,
14
-                           0, NULL, NULL, NULL, NULL, false))) {
15
+                           0, NULL, NULL, false))) {
16
                pa_alsa_init_proplist_pcm(NULL, m->input_proplist, m->input_pcm);
17
                pa_proplist_setf(m->input_proplist, "clock.name", "api.alsa.%u", index);
18
                pa_alsa_close(&m->input_pcm);
19
@@ -449,8 +449,8 @@
20
                    pa_dynarray_append(&impl->out.devices, dev);
21
                }
22
                if (impl->use_ucm) {
23
-                   if (m->ucm_context.ucm_device) {
24
-                       pa_alsa_ucm_add_port(NULL, &m->ucm_context,
25
+                   if (m->ucm_context.ucm_devices) {
26
+                       pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context,
27
                            true, impl->ports, ap, NULL);
28
                        pa_alsa_ucm_add_ports(&dev->ports, m->proplist, &m->ucm_context,
29
                            true, impl, dev->pcm_handle, impl->profile_set->ignore_dB);
30
@@ -473,8 +473,8 @@
31
                }
32
 
33
                if (impl->use_ucm) {
34
-                   if (m->ucm_context.ucm_device) {
35
-                       pa_alsa_ucm_add_port(NULL, &m->ucm_context,
36
+                   if (m->ucm_context.ucm_devices) {
37
+                       pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context,
38
                            false, impl->ports, ap, NULL);
39
                        pa_alsa_ucm_add_ports(&dev->ports, m->proplist, &m->ucm_context,
40
                            false, impl, dev->pcm_handle, impl->profile_set->ignore_dB);
41
@@ -608,7 +608,7 @@
42
 static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask)
43
 {
44
    pa_card *impl = snd_mixer_elem_get_callback_private(melem);
45
-   snd_hctl_elem_t **_elem = snd_mixer_elem_get_private(melem), *elem;
46
+   snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem);
47
    snd_ctl_elem_value_t *elem_value;
48
    bool plugged_in, any_input_port_available;
49
    void *state;
50
@@ -618,8 +618,6 @@
51
    enum acp_available active_available = ACP_AVAILABLE_UNKNOWN;
52
    size_t size;
53
 
54
-   pa_assert(_elem);
55
-   elem = *_elem;
56
 #if 0
57
    /* Changing the jack state may cause a port change, and a port change will
58
     * make the sink or source change the mixer settings. If there are multiple
59
@@ -888,17 +886,13 @@
60
 static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask)
61
 {
62
    pa_card *impl = snd_mixer_elem_get_callback_private(melem);
63
-   snd_hctl_elem_t **_elem = snd_mixer_elem_get_private(melem), *elem;
64
-   int device;
65
+   snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem);
66
+   int device = snd_hctl_elem_get_device(elem);
67
    const char *old_monitor_name;
68
    pa_device_port *p;
69
    pa_hdmi_eld eld;
70
    bool changed = false;
71
 
72
-   pa_assert(_elem);
73
-   elem = *_elem;
74
-   device = snd_hctl_elem_get_device(elem);
75
-
76
    if (mask == SND_CTL_EVENT_MASK_REMOVE)
77
        return 0;
78
 
79
@@ -1259,7 +1253,8 @@
80
    * will be NULL, but the UCM device enable sequence will still need to be
81
    * executed. */
82
    if (dev->active_port && dev->ucm_context) {
83
-       if ((res = pa_alsa_ucm_set_port(dev->ucm_context, dev->active_port)) < 0)
84
+       if ((res = pa_alsa_ucm_set_port(dev->ucm_context, dev->active_port,
85
+                   dev->direction == PA_ALSA_DIRECTION_OUTPUT)) < 0)
86
            return res;
87
    }
88
 
89
@@ -1434,7 +1429,8 @@
90
    /* if UCM is available for this card then update the verb */
91
    if (impl->use_ucm && !(np->profile.flags & ACP_PROFILE_PRO)) {
92
        if ((res = pa_alsa_ucm_set_profile(&impl->ucm, impl,
93
-           np->profile.flags & ACP_PROFILE_OFF ? NULL : np, op)) < 0) {
94
+           np->profile.flags & ACP_PROFILE_OFF ? NULL : np->profile.name,
95
+           op ? op->profile.name : NULL)) < 0) {
96
            return res;
97
        }
98
    }
99
@@ -1443,8 +1439,8 @@
100
        PA_IDXSET_FOREACH(am, np->output_mappings, idx) {
101
            if (impl->use_ucm) {
102
                /* Update ports priorities */
103
-               if (am->ucm_context.ucm_device) {
104
-                   pa_alsa_ucm_add_port(am->output.ports, &am->ucm_context,
105
+               if (am->ucm_context.ucm_devices) {
106
+                   pa_alsa_ucm_add_ports_combination(am->output.ports, &am->ucm_context,
107
                        true, impl->ports, np, NULL);
108
                }
109
            }
110
@@ -1456,8 +1452,8 @@
111
        PA_IDXSET_FOREACH(am, np->input_mappings, idx) {
112
            if (impl->use_ucm) {
113
                /* Update ports priorities */
114
-               if (am->ucm_context.ucm_device) {
115
-                   pa_alsa_ucm_add_port(am->input.ports, &am->ucm_context,
116
+               if (am->ucm_context.ucm_devices) {
117
+                   pa_alsa_ucm_add_ports_combination(am->input.ports, &am->ucm_context,
118
                        false, impl->ports, np, NULL);
119
                }
120
            }
121
@@ -1848,7 +1844,8 @@
122
        mixer_volume_init(impl, d);
123
 
124
        sync_mixer(d, p);
125
-       res = pa_alsa_ucm_set_port(d->ucm_context, p);
126
+       res = pa_alsa_ucm_set_port(d->ucm_context, p,
127
+                   dev->direction == ACP_DIRECTION_PLAYBACK);
128
    } else {
129
        pa_alsa_port_data *data;
130
 
131
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-mixer.c -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/alsa-mixer.c Changed
10
 
1
@@ -5000,7 +5000,7 @@
2
     handle = pa_alsa_open_by_template(
3
                               m->device_strings, dev_id, NULL, &try_ss,
4
                               &try_map, mode, &try_period_size,
5
-                              &try_buffer_size, 0, NULL, NULL, NULL, NULL, exact_channels);
6
+                              &try_buffer_size, 0, NULL, NULL, exact_channels);
7
     if (handle && !exact_channels && m->channel_map.channels != try_map.channels) {
8
         char bufPA_CHANNEL_MAP_SNPRINT_MAX;
9
         pa_log_debug("Channel map for mapping '%s' permanently changed to '%s'", m->name,
10
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-mixer.h -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/alsa-mixer.h Changed
20
 
1
@@ -354,7 +354,7 @@
2
     pa_alsa_device output;
3
     pa_alsa_device input;
4
 
5
-    /* ucm device context */
6
+    /* ucm device context*/
7
     pa_alsa_ucm_mapping_context ucm_context;
8
 };
9
 
10
@@ -381,9 +381,6 @@
11
     pa_idxset *input_mappings;
12
     pa_idxset *output_mappings;
13
 
14
-    /* ucm device context */
15
-    pa_alsa_ucm_profile_context ucm_context;
16
-
17
     struct {
18
    pa_dynarray devices;
19
     } out;
20
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-ucm.c -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/alsa-ucm.c Changed
1816
 
1
@@ -72,8 +72,9 @@
2
 
3
 
4
 static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
5
-                               pa_alsa_ucm_device *device);
6
+                               pa_alsa_ucm_device **devices, unsigned n_devices);
7
 static void ucm_port_data_free(pa_device_port *port);
8
+static void ucm_port_update_available(pa_alsa_ucm_port_data *port);
9
 
10
 static struct ucm_type types = {
11
     {"None", PA_DEVICE_PORT_TYPE_UNKNOWN},
12
@@ -169,6 +170,17 @@
13
     return (char *)value;
14
 }
15
 
16
+static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) {
17
+    pa_alsa_ucm_device *d;
18
+    uint32_t idx;
19
+
20
+    PA_IDXSET_FOREACH(d, idxset, idx)
21
+        if (d == dev)
22
+            return 1;
23
+
24
+    return 0;
25
+}
26
+
27
 static void ucm_add_devices_to_idxset(
28
         pa_idxset *idxset,
29
         pa_alsa_ucm_device *me,
30
@@ -494,10 +506,10 @@
31
     n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
32
     pa_xfree(id);
33
 
34
-    device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
35
     if (n_confdev <= 0)
36
         pa_log_debug("No %s for device %s", "_conflictingdevs", device_name);
37
     else {
38
+        device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
39
         ucm_add_devices_to_idxset(device->conflicting_devices, device, verb->devices, devices, n_confdev);
40
         snd_use_case_free_list(devices, n_confdev);
41
     }
42
@@ -506,10 +518,10 @@
43
     n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
44
     pa_xfree(id);
45
 
46
-    device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
47
     if (n_suppdev <= 0)
48
         pa_log_debug("No %s for device %s", "_supporteddevs", device_name);
49
     else {
50
+        device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
51
         ucm_add_devices_to_idxset(device->supported_devices, device, verb->devices, devices, n_suppdev);
52
         snd_use_case_free_list(devices, n_suppdev);
53
     }
54
@@ -518,16 +530,10 @@
55
 };
56
 
57
 /* Create a property list for this ucm modifier */
58
-static int ucm_get_modifier_property(
59
-        pa_alsa_ucm_modifier *modifier,
60
-        snd_use_case_mgr_t *uc_mgr,
61
-        pa_alsa_ucm_verb *verb,
62
-        const char *modifier_name) {
63
+static int ucm_get_modifier_property(pa_alsa_ucm_modifier *modifier, snd_use_case_mgr_t *uc_mgr, const char *modifier_name) {
64
     const char *value;
65
     char *id;
66
     int i;
67
-    const char **devices;
68
-    int n_confdev, n_suppdev;
69
 
70
     for (i = 0; itemi.id; i++) {
71
         int err;
72
@@ -544,28 +550,16 @@
73
     }
74
 
75
     id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name);
76
-    n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
77
+    modifier->n_confdev = snd_use_case_get_list(uc_mgr, id, &modifier->conflicting_devices);
78
     pa_xfree(id);
79
-
80
-    modifier->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
81
-    if (n_confdev <= 0)
82
+    if (modifier->n_confdev < 0)
83
         pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name);
84
-    else {
85
-        ucm_add_devices_to_idxset(modifier->conflicting_devices, NULL, verb->devices, devices, n_confdev);
86
-        snd_use_case_free_list(devices, n_confdev);
87
-    }
88
 
89
     id = pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name);
90
-    n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
91
+    modifier->n_suppdev = snd_use_case_get_list(uc_mgr, id, &modifier->supported_devices);
92
     pa_xfree(id);
93
-
94
-    modifier->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
95
-    if (n_suppdev <= 0)
96
+    if (modifier->n_suppdev < 0)
97
         pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name);
98
-    else {
99
-        ucm_add_devices_to_idxset(modifier->supported_devices, NULL, verb->devices, devices, n_suppdev);
100
-        snd_use_case_free_list(devices, n_suppdev);
101
-    }
102
 
103
     return 0;
104
 };
105
@@ -602,59 +596,6 @@
106
     return 0;
107
 };
108
 
109
-static long ucm_device_status(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) {
110
-    const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
111
-    char *devstatus;
112
-    long status = 0;
113
-
114
-    devstatus = pa_sprintf_malloc("_devstatus/%s", dev_name);
115
-    if (snd_use_case_geti(ucm->ucm_mgr, devstatus, &status) < 0) {
116
-        pa_log_debug("Failed to get status for UCM device %s", dev_name);
117
-        status = -1;
118
-    }
119
-    pa_xfree(devstatus);
120
-
121
-    return status;
122
-}
123
-
124
-static int ucm_device_disable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) {
125
-    const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
126
-
127
-    /* If any of dev's conflicting devices is enabled, trying to disable
128
-     * dev gives an error despite the fact that it's already disabled.
129
-     * Check that dev is enabled to avoid this error. */
130
-    if (ucm_device_status(ucm, dev) == 0) {
131
-        pa_log_debug("UCM device %s is already disabled", dev_name);
132
-        return 0;
133
-    }
134
-
135
-    pa_log_debug("Disabling UCM device %s", dev_name);
136
-    if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) < 0) {
137
-        pa_log("Failed to disable UCM device %s", dev_name);
138
-        return -1;
139
-    }
140
-
141
-    return 0;
142
-}
143
-
144
-static int ucm_device_enable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) {
145
-    const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
146
-
147
-    /* We don't need to enable devices that are already enabled */
148
-    if (ucm_device_status(ucm, dev) > 0) {
149
-        pa_log_debug("UCM device %s is already enabled", dev_name);
150
-        return 0;
151
-    }
152
-
153
-    pa_log_debug("Enabling UCM device %s", dev_name);
154
-    if (snd_use_case_set(ucm->ucm_mgr, "_enadev", dev_name) < 0) {
155
-        pa_log("Failed to enable UCM device %s", dev_name);
156
-        return -1;
157
-    }
158
-
159
-    return 0;
160
-}
161
-
162
 static int ucm_get_modifiers(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
163
     const char **mod_list;
164
     int num_mod, i;
165
@@ -685,57 +626,6 @@
166
     return 0;
167
 };
168
 
169
-static long ucm_modifier_status(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) {
170
-    const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
171
-    char *modstatus;
172
-    long status = 0;
173
-
174
-    modstatus = pa_sprintf_malloc("_modstatus/%s", mod_name);
175
-    if (snd_use_case_geti(ucm->ucm_mgr, modstatus, &status) < 0) {
176
-        pa_log_debug("Failed to get status for UCM modifier %s", mod_name);
177
-        status = -1;
178
-    }
179
-    pa_xfree(modstatus);
180
-
181
-    return status;
182
-}
183
-
184
-static int ucm_modifier_disable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) {
185
-    const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
186
-
187
-    /* We don't need to disable modifiers that are already disabled */
188
-    if (ucm_modifier_status(ucm, mod) == 0) {
189
-        pa_log_debug("UCM modifier %s is already disabled", mod_name);
190
-        return 0;
191
-    }
192
-
193
-    pa_log_debug("Disabling UCM modifier %s", mod_name);
194
-    if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) {
195
-        pa_log("Failed to disable UCM modifier %s", mod_name);
196
-        return -1;
197
-    }
198
-
199
-    return 0;
200
-}
201
-
202
-static int ucm_modifier_enable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) {
203
-    const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
204
-
205
-    /* We don't need to enable modifiers that are already enabled */
206
-    if (ucm_modifier_status(ucm, mod) > 0) {
207
-        pa_log_debug("UCM modifier %s is already enabled", mod_name);
208
-        return 0;
209
-    }
210
-
211
-    pa_log_debug("Enabling UCM modifier %s", mod_name);
212
-    if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) {
213
-        pa_log("Failed to enable UCM modifier %s", mod_name);
214
-        return -1;
215
-    }
216
-
217
-    return 0;
218
-}
219
-
220
 static void add_role_to_device(pa_alsa_ucm_device *dev, const char *dev_name, const char *role_name, const char *role) {
221
     const char *cur = pa_proplist_gets(dev->proplist, role_name);
222
 
223
@@ -752,19 +642,27 @@
224
                 role_name));
225
 }
226
 
227
-static void add_media_role(pa_alsa_ucm_device *dev, const char *role_name, const char *role, bool is_sink) {
228
-    const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
229
-    const char *sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
230
-    const char *source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
231
+static void add_media_role(const char *name, pa_alsa_ucm_device *list, const char *role_name, const char *role, bool is_sink) {
232
+    pa_alsa_ucm_device *d;
233
+
234
+    PA_LLIST_FOREACH(d, list) {
235
+        const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
236
+
237
+        if (pa_streq(dev_name, name)) {
238
+            const char *sink = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK);
239
+            const char *source = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE);
240
 
241
-    if (is_sink && sink)
242
-        add_role_to_device(dev, dev_name, role_name, role);
243
-    else if (!is_sink && source)
244
-        add_role_to_device(dev, dev_name, role_name, role);
245
+            if (is_sink && sink)
246
+                add_role_to_device(d, dev_name, role_name, role);
247
+            else if (!is_sink && source)
248
+                add_role_to_device(d, dev_name, role_name, role);
249
+            break;
250
+        }
251
+    }
252
 }
253
 
254
 static char *modifier_name_to_role(const char *mod_name, bool *is_sink) {
255
-    char *sub = NULL, *tmp, *pos;
256
+    char *sub = NULL, *tmp;
257
 
258
     *is_sink = false;
259
 
260
@@ -774,32 +672,26 @@
261
     } else if (pa_startswith(mod_name, "Capture"))
262
         sub = pa_xstrdup(mod_name + 7);
263
 
264
-    pos = sub;
265
-    while (pos && *pos == ' ') pos++;
266
-
267
-    if (!pos || !*pos) {
268
+    if (!sub || !*sub) {
269
         pa_xfree(sub);
270
         pa_log_warn("Can't match media roles for modifier %s", mod_name);
271
         return NULL;
272
     }
273
 
274
-    tmp = pos;
275
+    tmp = sub;
276
 
277
     do {
278
         *tmp = tolower(*tmp);
279
     } while (*(++tmp));
280
 
281
-    tmp = pa_xstrdup(pos);
282
-    pa_xfree(sub);
283
-    return tmp;
284
+    return sub;
285
 }
286
 
287
-static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, const char *mod_name) {
288
-    pa_alsa_ucm_device *dev;
289
+static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, pa_alsa_ucm_device *list, const char *mod_name) {
290
+    int i;
291
     bool is_sink = false;
292
     char *sub = NULL;
293
     const char *role_name;
294
-    uint32_t idx;
295
 
296
     sub = modifier_name_to_role(mod_name, &is_sink);
297
     if (!sub)
298
@@ -809,11 +701,11 @@
299
     modifier->media_role = sub;
300
 
301
     role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
302
-    PA_IDXSET_FOREACH(dev, modifier->supported_devices, idx) {
303
+    for (i = 0; i < modifier->n_suppdev; i++) {
304
         /* if modifier has no specific pcm, we add role intent to its supported devices */
305
         if (!pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SINK) &&
306
                 !pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SOURCE))
307
-            add_media_role(dev, role_name, sub, is_sink);
308
+            add_media_role(modifier->supported_devicesi, list, role_name, sub, is_sink);
309
     }
310
 }
311
 
312
@@ -821,17 +713,29 @@
313
     uint32_t idx;
314
     pa_alsa_ucm_device *d;
315
 
316
-    PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx)
317
-        if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0)
318
-            pa_log_warn("Add lost conflicting device %s to %s",
319
-                    pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
320
-                    pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
321
+    if (dev->conflicting_devices) {
322
+        PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
323
+            if (!d->conflicting_devices)
324
+                d->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
325
+
326
+            if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0)
327
+                pa_log_warn("Add lost conflicting device %s to %s",
328
+                        pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
329
+                        pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
330
+        }
331
+    }
332
+
333
+    if (dev->supported_devices) {
334
+        PA_IDXSET_FOREACH(d, dev->supported_devices, idx) {
335
+            if (!d->supported_devices)
336
+                d->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
337
 
338
-    PA_IDXSET_FOREACH(d, dev->supported_devices, idx)
339
-        if (pa_idxset_put(d->supported_devices, dev, NULL) == 0)
340
-            pa_log_warn("Add lost supported device %s to %s",
341
-                    pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
342
-                    pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
343
+            if (pa_idxset_put(d->supported_devices, dev, NULL) == 0)
344
+                pa_log_warn("Add lost supported device %s to %s",
345
+                        pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
346
+                        pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
347
+        }
348
+    }
349
 }
350
 
351
 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
352
@@ -883,7 +787,7 @@
353
         free((void *)value);
354
     }
355
 
356
-    /* get a list of all UCM verbs for this card */
357
+    /* get a list of all UCM verbs (profiles) for this card */
358
     num_verbs = snd_use_case_verb_list(ucm->ucm_mgr, &verb_list);
359
     if (num_verbs < 0) {
360
         pa_log("UCM verb list not found for %s", card_name);
361
@@ -972,11 +876,11 @@
362
         const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
363
 
364
         /* Modifier properties */
365
-        ucm_get_modifier_property(mod, uc_mgr, verb, mod_name);
366
+        ucm_get_modifier_property(mod, uc_mgr, mod_name);
367
 
368
         /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */
369
         pa_log_debug("Set media roles for verb %s, modifier %s", verb_name, mod_name);
370
-        ucm_set_media_roles(mod, mod_name);
371
+        ucm_set_media_roles(mod, verb->devices, mod_name);
372
     }
373
 
374
     *p_verb = verb;
375
@@ -995,27 +899,43 @@
376
     pa_device_port *port;
377
     pa_alsa_ucm_port_data *data;
378
     pa_alsa_ucm_device *dev;
379
+    const char *eld_mixer_device_name;
380
     void *state;
381
+    int idx, eld_device;
382
 
383
     PA_HASHMAP_FOREACH(port, hash, state) {
384
         data = PA_DEVICE_PORT_DATA(port);
385
-        dev = data->device;
386
-        data->eld_device = dev->eld_device;
387
-        pa_xfree(data->eld_mixer_device_name);
388
-        data->eld_mixer_device_name = pa_xstrdup(dev->eld_mixer_device_name);
389
+        eld_mixer_device_name = NULL;
390
+        eld_device = -1;
391
+        PA_DYNARRAY_FOREACH(dev, data->devices, idx) {
392
+            if (dev->eld_device >= 0 && dev->eld_mixer_device_name) {
393
+                if (eld_device >= 0 && eld_device != dev->eld_device) {
394
+                    pa_log_error("The ELD device is already set!");
395
+                } else if (eld_mixer_device_name && pa_streq(dev->eld_mixer_device_name, eld_mixer_device_name)) {
396
+                    pa_log_error("The ELD mixer device is already set (%s, %s)!", dev->eld_mixer_device_name, dev->eld_mixer_device_name);
397
+                } else {
398
+                    eld_mixer_device_name = dev->eld_mixer_device_name;
399
+                    eld_device = dev->eld_device;
400
+                }
401
+            }
402
+        }
403
+        data->eld_device = eld_device;
404
+        if (data->eld_mixer_device_name)
405
+            pa_xfree(data->eld_mixer_device_name);
406
+        data->eld_mixer_device_name = pa_xstrdup(eld_mixer_device_name);
407
     }
408
 }
409
 
410
-static void update_mixer_paths(pa_hashmap *ports, const char *verb_name) {
411
+static void update_mixer_paths(pa_hashmap *ports, const char *profile) {
412
     pa_device_port *port;
413
     pa_alsa_ucm_port_data *data;
414
     void *state;
415
 
416
     /* select volume controls on ports */
417
     PA_HASHMAP_FOREACH(port, ports, state) {
418
-        pa_log_info("Updating mixer path for %s: %s", verb_name, port->name);
419
+        pa_log_info("Updating mixer path for %s: %s", profile, port->name);
420
         data = PA_DEVICE_PORT_DATA(port);
421
-        data->path = pa_hashmap_get(data->paths, verb_name);
422
+        data->path = pa_hashmap_get(data->paths, profile);
423
     }
424
 }
425
 
426
@@ -1025,29 +945,39 @@
427
     pa_alsa_ucm_port_data *data;
428
     pa_alsa_ucm_device *dev;
429
     snd_mixer_t *mixer_handle;
430
-    const char *verb_name, *mdev;
431
+    const char *profile, *mdev, *mdev2;
432
     void *state, *state2;
433
+    int idx;
434
 
435
     PA_HASHMAP_FOREACH(port, hash, state) {
436
         data = PA_DEVICE_PORT_DATA(port);
437
 
438
-        dev = data->device;
439
-        mdev = get_mixer_device(dev, is_sink);
440
+        mdev = NULL;
441
+        PA_DYNARRAY_FOREACH(dev, data->devices, idx) {
442
+            mdev2 = get_mixer_device(dev, is_sink);
443
+            if (mdev && mdev2 && !pa_streq(mdev, mdev2)) {
444
+                pa_log_error("Two mixer device names found ('%s', '%s'), using s/w volume", mdev, mdev2);
445
+                goto fail;
446
+            }
447
+            if (mdev2)
448
+                mdev = mdev2;
449
+        }
450
+
451
         if (mdev == NULL || !(mixer_handle = pa_alsa_open_mixer_by_name(mixers, mdev, true))) {
452
             pa_log_error("Failed to find a working mixer device (%s).", mdev);
453
             goto fail;
454
         }
455
 
456
-        PA_HASHMAP_FOREACH_KV(verb_name, path, data->paths, state2) {
457
+        PA_HASHMAP_FOREACH_KV(profile, path, data->paths, state2) {
458
             if (pa_alsa_path_probe(path, NULL, mixer_handle, ignore_dB) < 0) {
459
                 pa_log_warn("Could not probe path: %s, using s/w volume", path->name);
460
-                pa_hashmap_remove(data->paths, verb_name);
461
+                pa_hashmap_remove(data->paths, profile);
462
             } else if (!path->has_volume && !path->has_mute) {
463
                 pa_log_warn("Path %s is not a volume or mute control", path->name);
464
-                pa_hashmap_remove(data->paths, verb_name);
465
+                pa_hashmap_remove(data->paths, profile);
466
             } else
467
                 pa_log_debug("Set up h/w %s using '%s' for %s:%s", path->has_volume ? "volume" : "mute",
468
-                                path->name, verb_name, port->name);
469
+                                path->name, profile, port->name);
470
         }
471
     }
472
 
473
@@ -1061,141 +991,91 @@
474
     }
475
 }
476
 
477
-static char *devset_name(pa_idxset *devices, const char *sep) {
478
-    int i = 0;
479
-    int num = pa_idxset_size(devices);
480
-    pa_alsa_ucm_device *sortednum, *dev;
481
-    char *dev_names = NULL;
482
-    char *tmp = NULL;
483
-    uint32_t idx;
484
-
485
-    PA_IDXSET_FOREACH(dev, devices, idx) {
486
-        sortedi = dev;
487
-        i++;
488
-    }
489
-
490
-    /* Sort by alphabetical order so as to have a deterministic naming scheme */
491
-    qsort(&sorted0, num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp);
492
-
493
-    for (i = 0; i < num; i++) {
494
-        dev = sortedi;
495
-        const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
496
-
497
-        if (!dev_names) {
498
-            dev_names = pa_xstrdup(dev_name);
499
-        } else {
500
-            tmp = pa_sprintf_malloc("%s%s%s", dev_names, sep, dev_name);
501
-            pa_xfree(dev_names);
502
-            dev_names = tmp;
503
-        }
504
-    }
505
-
506
-    return dev_names;
507
-}
508
-
509
-PA_UNUSED static char *devset_description(pa_idxset *devices, const char *sep) {
510
-    int i = 0;
511
-    int num = pa_idxset_size(devices);
512
-    pa_alsa_ucm_device *sortednum, *dev;
513
-    char *dev_descs = NULL;
514
-    char *tmp = NULL;
515
-    uint32_t idx;
516
-
517
-    PA_IDXSET_FOREACH(dev, devices, idx) {
518
-        sortedi = dev;
519
-        i++;
520
-    }
521
-
522
-    /* Sort by alphabetical order to match devset_name() */
523
-    qsort(&sorted0, num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp);
524
-
525
-    for (i = 0; i < num; i++) {
526
-        dev = sortedi;
527
-        const char *dev_desc = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
528
-
529
-        if (!dev_descs) {
530
-            dev_descs = pa_xstrdup(dev_desc);
531
-        } else {
532
-            tmp = pa_sprintf_malloc("%s%s%s", dev_descs, sep, dev_desc);
533
-            pa_xfree(dev_descs);
534
-            dev_descs = tmp;
535
-        }
536
-    }
537
-
538
-    return dev_descs;
539
-}
540
-
541
-/* If invert is true, uses the formula 1/p = 1/p1 + 1/p2 + ... 1/pn.
542
- * This way, the result will always be less than the individual components,
543
- * yet higher components will lead to higher result. */
544
-static unsigned devset_playback_priority(pa_idxset *devices, bool invert) {
545
-    pa_alsa_ucm_device *dev;
546
-    uint32_t idx;
547
-    double priority = 0;
548
-
549
-    PA_IDXSET_FOREACH(dev, devices, idx) {
550
-        if (dev->playback_priority > 0 && invert)
551
-            priority += 1.0 / dev->playback_priority;
552
-        else
553
-            priority += dev->playback_priority;
554
-    }
555
-
556
-    if (priority > 0 && invert)
557
-        return 1.0 / priority;
558
-
559
-    return (unsigned) priority;
560
-}
561
-
562
-static unsigned devset_capture_priority(pa_idxset *devices, bool invert) {
563
-    pa_alsa_ucm_device *dev;
564
-    uint32_t idx;
565
-    double priority = 0;
566
-
567
-    PA_IDXSET_FOREACH(dev, devices, idx) {
568
-        if (dev->capture_priority > 0 && invert)
569
-            priority += 1.0 / dev->capture_priority;
570
-        else
571
-            priority += dev->capture_priority;
572
-    }
573
-
574
-    if (priority > 0 && invert)
575
-        return 1.0 / priority;
576
-
577
-    return (unsigned) priority;
578
-}
579
-
580
-void pa_alsa_ucm_add_port(
581
+static void ucm_add_port_combination(
582
         pa_hashmap *hash,
583
         pa_alsa_ucm_mapping_context *context,
584
         bool is_sink,
585
+        pa_alsa_ucm_device **pdevices,
586
+        int num,
587
         pa_hashmap *ports,
588
         pa_card_profile *cp,
589
         pa_core *core) {
590
 
591
     pa_device_port *port;
592
+    int i;
593
     unsigned priority;
594
+    double prio2;
595
     char *name, *desc;
596
     const char *dev_name;
597
     const char *direction;
598
-    const char *verb_name;
599
-    pa_alsa_ucm_device *dev;
600
+    const char *profile;
601
+    pa_alsa_ucm_device *sortednum, *dev;
602
     pa_alsa_ucm_port_data *data;
603
     pa_alsa_ucm_volume *vol;
604
-    pa_alsa_jack *jack;
605
-    pa_device_port_type_t type;
606
+    pa_alsa_jack *jack, *jack2;
607
+    pa_device_port_type_t type, type2;
608
     void *state;
609
 
610
-    dev = context->ucm_device;
611
-    if (!dev)
612
-        return;
613
+    for (i = 0; i < num; i++)
614
+        sortedi = pdevicesi;
615
+
616
+    /* Sort by alphabetical order so as to have a deterministic naming scheme
617
+     * for combination ports */
618
+    qsort(&sorted0, num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp);
619
 
620
+    dev = sorted0;
621
     dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
622
+
623
     name = pa_sprintf_malloc("%s%s", is_sink ? PA_UCM_PRE_TAG_OUTPUT : PA_UCM_PRE_TAG_INPUT, dev_name);
624
-    desc = pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION));
625
+    desc = num == 1 ? pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION))
626
+            : pa_sprintf_malloc("Combination port for %s", dev_name);
627
+
628
     priority = is_sink ? dev->playback_priority : dev->capture_priority;
629
+    prio2 = (priority == 0 ? 0 : 1.0/priority);
630
     jack = ucm_get_jack(context->ucm, dev);
631
     type = dev->type;
632
 
633
+    for (i = 1; i < num; i++) {
634
+        char *tmp;
635
+
636
+        dev = sortedi;
637
+        dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
638
+
639
+        tmp = pa_sprintf_malloc("%s+%s", name, dev_name);
640
+        pa_xfree(name);
641
+        name = tmp;
642
+
643
+        tmp = pa_sprintf_malloc("%s,%s", desc, dev_name);
644
+        pa_xfree(desc);
645
+        desc = tmp;
646
+
647
+        priority = is_sink ? dev->playback_priority : dev->capture_priority;
648
+        if (priority != 0 && prio2 > 0)
649
+            prio2 += 1.0/priority;
650
+
651
+        jack2 = ucm_get_jack(context->ucm, dev);
652
+        if (jack2) {
653
+            if (jack && jack != jack2)
654
+                pa_log_warn("Multiple jacks per combined device '%s': '%s' '%s'", name, jack->name, jack2->name);
655
+            jack = jack2;
656
+        }
657
+
658
+        type2 = dev->type;
659
+        if (type2 != PA_DEVICE_PORT_TYPE_UNKNOWN) {
660
+            if (type != PA_DEVICE_PORT_TYPE_UNKNOWN && type != type2)
661
+                pa_log_warn("Multiple device types per combined device '%s': %d %d", name, type, type2);
662
+            type = type2;
663
+        }
664
+    }
665
+
666
+    /* Make combination ports always have lower priority, and use the formula
667
+       1/p = 1/p1 + 1/p2 + ... 1/pn.
668
+       This way, the result will always be less than the individual components,
669
+       yet higher components will lead to higher result. */
670
+
671
+    if (num > 1)
672
+        priority = prio2 > 0 ? 1.0/prio2 : 0;
673
+
674
     port = pa_hashmap_get(ports, name);
675
     if (!port) {
676
         pa_device_port_new_data port_data;
677
@@ -1212,32 +1092,37 @@
678
         pa_device_port_new_data_done(&port_data);
679
 
680
         data = PA_DEVICE_PORT_DATA(port);
681
-        ucm_port_data_init(data, context->ucm, port, dev);
682
+        ucm_port_data_init(data, context->ucm, port, pdevices, num);
683
         port->impl_free = ucm_port_data_free;
684
 
685
         pa_hashmap_put(ports, port->name, port);
686
         pa_log_debug("Add port %s: %s", port->name, port->description);
687
 
688
-        PA_HASHMAP_FOREACH_KV(verb_name, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) {
689
-            pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem,
690
-                                                         is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
691
-
692
-            if (!path)
693
-                pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem);
694
-            else {
695
-                if (vol->master_elem) {
696
-                    pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false);
697
-                    e->switch_use = PA_ALSA_SWITCH_MUTE;
698
-                    e->volume_use = PA_ALSA_VOLUME_MERGE;
699
-                }
700
+        if (num == 1) {
701
+            /* To keep things simple and not worry about stacking controls, we only support hardware volumes on non-combination
702
+             * ports. */
703
+            PA_HASHMAP_FOREACH_KV(profile, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) {
704
+                pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem,
705
+                                                             is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
706
+
707
+                if (!path)
708
+                    pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem);
709
+                else {
710
+                    if (vol->master_elem) {
711
+                        pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false);
712
+                        e->switch_use = PA_ALSA_SWITCH_MUTE;
713
+                        e->volume_use = PA_ALSA_VOLUME_MERGE;
714
+                    }
715
 
716
-                pa_hashmap_put(data->paths, pa_xstrdup(verb_name), path);
717
+                    pa_hashmap_put(data->paths, pa_xstrdup(profile), path);
718
 
719
-                /* Add path also to already created empty path set */
720
-                if (is_sink)
721
-                    pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
722
-                else
723
-                    pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
724
+                    /* Add path also to already created empty path set */
725
+                    dev = sorted0;
726
+                    if (is_sink)
727
+                        pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
728
+                    else
729
+                        pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
730
+                }
731
             }
732
         }
733
     }
734
@@ -1258,127 +1143,113 @@
735
     if (hash) {
736
         pa_hashmap_put(hash, port->name, port);
737
     }
738
-
739
-    /* ELD devices */
740
-    set_eld_devices(ports);
741
 }
742
 
743
-static bool devset_supports_device(pa_idxset *devices, pa_alsa_ucm_device *dev) {
744
-    const char *sink, *sink2, *source, *source2;
745
-    pa_alsa_ucm_device *d;
746
-    uint32_t idx;
747
-
748
-    pa_assert(devices);
749
-    pa_assert(dev);
750
-
751
-    /* Can add anything to empty group */
752
-    if (pa_idxset_isempty(devices))
753
-        return true;
754
-
755
-    /* Device already selected */
756
-    if (pa_idxset_contains(devices, dev))
757
-        return true;
758
+static int ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink) {
759
+    int ret = 0;
760
+    const char *r;
761
+    const char *state = NULL;
762
+    size_t len;
763
 
764
-    /* No conflicting device must already be selected */
765
-    if (!pa_idxset_isdisjoint(devices, dev->conflicting_devices))
766
+    if (!port_name || !dev_name)
767
         return false;
768
 
769
-    /* No already selected device must be unsupported */
770
-    if (!pa_idxset_isempty(dev->supported_devices))
771
-        if (!pa_idxset_issubset(devices, dev->supported_devices))
772
-           return false;
773
-
774
-    sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
775
-    source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
776
-
777
-    PA_IDXSET_FOREACH(d, devices, idx) {
778
-        /* Must not be unsupported by any selected device */
779
-        if (!pa_idxset_isempty(d->supported_devices))
780
-            if (!pa_idxset_contains(d->supported_devices, dev))
781
-                return false;
782
+    port_name += is_sink ? strlen(PA_UCM_PRE_TAG_OUTPUT) : strlen(PA_UCM_PRE_TAG_INPUT);
783
 
784
-        /* PlaybackPCM must not be the same as any selected device */
785
-        sink2 = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK);
786
-        if (sink && sink2 && pa_streq(sink, sink2))
787
-            return false;
788
-
789
-        /* CapturePCM must not be the same as any selected device */
790
-        source2 = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE);
791
-        if (source && source2 && pa_streq(source, source2))
792
-            return false;
793
+    while ((r = pa_split_in_place(port_name, "+", &len, &state))) {
794
+        if (strlen(dev_name) == len && !strncmp(r, dev_name, len)) {
795
+            ret = 1;
796
+            break;
797
+        }
798
     }
799
 
800
-    return true;
801
+    return ret;
802
 }
803
 
804
-/* Iterates nonempty subsets of UCM devices that can be simultaneously
805
- * used, including subsets of previously returned subsets. At start,
806
- * *state should be NULL. It's not safe to modify the devices argument
807
- * until iteration ends. The returned idxsets must be freed by the
808
- * caller. */
809
-static pa_idxset *iterate_device_subsets(pa_idxset *devices, void **state) {
810
-    uint32_t idx;
811
-    pa_alsa_ucm_device *dev;
812
+static int ucm_check_conformance(
813
+        pa_alsa_ucm_mapping_context *context,
814
+        pa_alsa_ucm_device **pdevices,
815
+        int dev_num,
816
+        pa_alsa_ucm_device *dev) {
817
 
818
-    pa_assert(devices);
819
-    pa_assert(state);
820
+    uint32_t idx;
821
+    pa_alsa_ucm_device *d;
822
+    int i;
823
 
824
-    if (*state == NULL) {
825
-        /* First iteration, start adding from first device */
826
-        *state = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
827
-        dev = pa_idxset_first(devices, &idx);
828
+    pa_assert(dev);
829
 
830
-    } else {
831
-        /* Backtrack the most recent device we added and skip it */
832
-        dev = pa_idxset_steal_last(*state, NULL);
833
-        pa_idxset_get_by_data(devices, dev, &idx);
834
-        if (dev)
835
-            dev = pa_idxset_next(devices, &idx);
836
+    pa_log_debug("Check device %s conformance with %d other devices",
837
+            pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), dev_num);
838
+    if (dev_num == 0) {
839
+        pa_log_debug("First device in combination, number 1");
840
+        return 1;
841
     }
842
 
843
-    /* Try adding devices we haven't decided on yet */
844
-    for (; dev; dev = pa_idxset_next(devices, &idx)) {
845
-        if (devset_supports_device(*state, dev))
846
-            pa_idxset_put(*state, dev, NULL);
847
+    if (dev->conflicting_devices) { /* the device defines conflicting devices */
848
+        PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
849
+            for (i = 0; i < dev_num; i++) {
850
+                if (pdevicesi == d) {
851
+                    pa_log_debug("Conflicting device found");
852
+                    return 0;
853
+                }
854
+            }
855
+        }
856
+    } else if (dev->supported_devices) { /* the device defines supported devices */
857
+        for (i = 0; i < dev_num; i++) {
858
+            if (!ucm_device_exists(dev->supported_devices, pdevicesi)) {
859
+                pa_log_debug("Supported device not found");
860
+                return 0;
861
+            }
862
+        }
863
+    } else { /* not support any other devices */
864
+        pa_log_debug("Not support any other devices");
865
+        return 0;
866
     }
867
 
868
-    if (pa_idxset_isempty(*state)) {
869
-        /* No more choices to backtrack on, therefore no more subsets to
870
-         * return after this. Don't return the empty set, instead clean
871
-         * up and end iteration. */
872
-        pa_idxset_free(*state, NULL);
873
-        *state = NULL;
874
-        return NULL;
875
-    }
876
+    pa_log_debug("Device added to combination, number %d", dev_num + 1);
877
+    return 1;
878
+}
879
+
880
+static inline pa_alsa_ucm_device *get_next_device(pa_idxset *idxset, uint32_t *idx) {
881
+    pa_alsa_ucm_device *dev;
882
+
883
+    if (*idx == PA_IDXSET_INVALID)
884
+        dev = pa_idxset_first(idxset, idx);
885
+    else
886
+        dev = pa_idxset_next(idxset, idx);
887
 
888
-    return pa_idxset_copy(*state, NULL);
889
+    return dev;
890
 }
891
 
892
-/* This a wrapper around iterate_device_subsets() that only returns the
893
- * biggest possible groups and not any of their subsets. */
894
-static pa_idxset *iterate_maximal_device_subsets(pa_idxset *devices, void **state) {
895
-    uint32_t idx;
896
+static void ucm_add_ports_combination(
897
+        pa_hashmap *hash,
898
+        pa_alsa_ucm_mapping_context *context,
899
+        bool is_sink,
900
+        pa_alsa_ucm_device **pdevices,
901
+        int dev_num,
902
+        uint32_t map_index,
903
+        pa_hashmap *ports,
904
+        pa_card_profile *cp,
905
+        pa_core *core) {
906
+
907
     pa_alsa_ucm_device *dev;
908
-    pa_idxset *subset;
909
+    uint32_t idx = map_index;
910
 
911
-    pa_assert(devices);
912
-    pa_assert(state);
913
-
914
-    subset = iterate_device_subsets(devices, state);
915
-    if (!subset)
916
-        return subset;
917
-
918
-    /* Skip this group if it's incomplete, by checking if we can add any
919
-     * other device. If we can, this iteration is a subset of another
920
-     * group that we already returned or eventually return. */
921
-    PA_IDXSET_FOREACH(dev, devices, idx) {
922
-        if (!pa_idxset_contains(subset, dev) && devset_supports_device(subset, dev)) {
923
-            pa_idxset_free(subset, NULL);
924
-            return iterate_maximal_device_subsets(devices, state);
925
-        }
926
+    if ((dev = get_next_device(context->ucm_devices, &idx)) == NULL)
927
+        return;
928
+
929
+    /* check if device at map_index can combine with existing devices combination */
930
+    if (ucm_check_conformance(context, pdevices, dev_num, dev)) {
931
+        /* add device at map_index to devices combination */
932
+        pdevicesdev_num = dev;
933
+        /* add current devices combination as a new port */
934
+        ucm_add_port_combination(hash, context, is_sink, pdevices, dev_num + 1, ports, cp, core);
935
+        /* try more elements combination */
936
+        ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num + 1, idx, ports, cp, core);
937
     }
938
 
939
-    return subset;
940
+    /* try other device with current elements number */
941
+    ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num, idx, ports, cp, core);
942
 }
943
 
944
 static char* merge_roles(const char *cur, const char *add) {
945
@@ -1410,6 +1281,28 @@
946
     return ret;
947
 }
948
 
949
+void pa_alsa_ucm_add_ports_combination(
950
+        pa_hashmap *p,
951
+        pa_alsa_ucm_mapping_context *context,
952
+        bool is_sink,
953
+        pa_hashmap *ports,
954
+        pa_card_profile *cp,
955
+        pa_core *core) {
956
+
957
+    pa_alsa_ucm_device **pdevices;
958
+
959
+    pa_assert(context->ucm_devices);
960
+
961
+    if (pa_idxset_size(context->ucm_devices) > 0) {
962
+        pdevices = pa_xnew(pa_alsa_ucm_device *, pa_idxset_size(context->ucm_devices));
963
+        ucm_add_ports_combination(p, context, is_sink, pdevices, 0, PA_IDXSET_INVALID, ports, cp, core);
964
+        pa_xfree(pdevices);
965
+    }
966
+
967
+    /* ELD devices */
968
+    set_eld_devices(ports);
969
+}
970
+
971
 void pa_alsa_ucm_add_ports(
972
         pa_hashmap **p,
973
         pa_proplist *proplist,
974
@@ -1419,6 +1312,7 @@
975
         snd_pcm_t *pcm_handle,
976
         bool ignore_dB) {
977
 
978
+    uint32_t idx;
979
     char *merged_roles;
980
     const char *role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
981
     pa_alsa_ucm_device *dev;
982
@@ -1429,39 +1323,34 @@
983
     pa_assert(*p);
984
 
985
     /* add ports first */
986
-    pa_alsa_ucm_add_port(*p, context, is_sink, card->ports, NULL, card->core);
987
+    pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core);
988
 
989
     /* now set up volume paths if any */
990
     probe_volumes(*p, is_sink, pcm_handle, context->ucm->mixers, ignore_dB);
991
 
992
-    /* probe_volumes() removes per-verb paths from ports if probing them
993
-     * fails. The path for the current verb is cached in
994
+    /* probe_volumes() removes per-profile paths from ports if probing them
995
+     * fails. The path for the current profile is cached in
996
      * pa_alsa_ucm_port_data.path, which is not cleared by probe_volumes() if
997
      * the path gets removed, so we have to call update_mixer_paths() here to
998
      * unset the cached path if needed. */
999
-    if (context->ucm->active_verb) {
1000
-        const char *verb_name;
1001
-        verb_name = pa_proplist_gets(context->ucm->active_verb->proplist, PA_ALSA_PROP_UCM_NAME);
1002
-        update_mixer_paths(*p, verb_name);
1003
-    }
1004
+    if (card->card.active_profile_index < card->card.n_profiles)
1005
+        update_mixer_paths(*p, card->card.profilescard->card.active_profile_index->name);
1006
 
1007
     /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
1008
     merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES));
1009
-
1010
-    dev = context->ucm_device;
1011
-    if (dev) {
1012
+    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1013
         const char *roles = pa_proplist_gets(dev->proplist, role_name);
1014
         tmp = merge_roles(merged_roles, roles);
1015
         pa_xfree(merged_roles);
1016
         merged_roles = tmp;
1017
     }
1018
 
1019
-    mod = context->ucm_modifier;
1020
-    if (mod) {
1021
-        tmp = merge_roles(merged_roles, mod->media_role);
1022
-        pa_xfree(merged_roles);
1023
-        merged_roles = tmp;
1024
-    }
1025
+    if (context->ucm_modifiers)
1026
+        PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
1027
+            tmp = merge_roles(merged_roles, mod->media_role);
1028
+            pa_xfree(merged_roles);
1029
+            merged_roles = tmp;
1030
+        }
1031
 
1032
     if (merged_roles)
1033
         pa_proplist_sets(proplist, PA_PROP_DEVICE_INTENDED_ROLES, merged_roles);
1034
@@ -1471,81 +1360,85 @@
1035
 }
1036
 
1037
 /* Change UCM verb and device to match selected card profile */
1038
-int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile) {
1039
+int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) {
1040
     int ret = 0;
1041
-    const char *verb_name, *profile_name;
1042
+    const char *profile;
1043
     pa_alsa_ucm_verb *verb;
1044
-    pa_alsa_mapping *map;
1045
-    uint32_t idx;
1046
 
1047
     if (new_profile == old_profile)
1048
-        return 0;
1049
+        return ret;
1050
+    else if (new_profile == NULL || old_profile == NULL)
1051
+        profile = new_profile ? new_profile : SND_USE_CASE_VERB_INACTIVE;
1052
+    else if (!pa_streq(new_profile, old_profile))
1053
+        profile = new_profile;
1054
+    else
1055
+        return ret;
1056
 
1057
-    if (new_profile == NULL) {
1058
-        verb = NULL;
1059
-        profile_name = SND_USE_CASE_VERB_INACTIVE;
1060
-        verb_name = SND_USE_CASE_VERB_INACTIVE;
1061
-    } else {
1062
-        verb = new_profile->ucm_context.verb;
1063
-        profile_name = new_profile->name;
1064
-        verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
1065
+    /* change verb */
1066
+    pa_log_info("Set UCM verb to %s", profile);
1067
+    if ((ret = snd_use_case_set(ucm->ucm_mgr, "_verb", profile)) < 0) {
1068
+        pa_log("Failed to set verb %s: %s", profile, snd_strerror(ret));
1069
     }
1070
 
1071
-    pa_log_info("Set profile to %s", profile_name);
1072
-    if (ucm->active_verb != verb) {
1073
-        /* change verb */
1074
-        pa_log_info("Set UCM verb to %s", verb_name);
1075
-        if ((snd_use_case_set(ucm->ucm_mgr, "_verb", verb_name)) < 0) {
1076
-            pa_log("Failed to set verb %s", verb_name);
1077
-            ret = -1;
1078
+    /* find active verb */
1079
+    ucm->active_verb = NULL;
1080
+    PA_LLIST_FOREACH(verb, ucm->verbs) {
1081
+        const char *verb_name;
1082
+        verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
1083
+        if (pa_streq(verb_name, profile)) {
1084
+            ucm->active_verb = verb;
1085
+            break;
1086
         }
1087
-
1088
-    } else if (ucm->active_verb) {
1089
-        /* Disable modifiers not in new profile. Has to be done before
1090
-         * devices, because _dismod fails if a modifier's supported
1091
-         * devices are disabled. */
1092
-        PA_IDXSET_FOREACH(map, old_profile->input_mappings, idx)
1093
-            if (new_profile && !pa_idxset_contains(new_profile->input_mappings, map))
1094
-                if (map->ucm_context.ucm_modifier && ucm_modifier_disable(ucm, map->ucm_context.ucm_modifier) < 0)
1095
-                    ret = -1;
1096
-
1097
-        PA_IDXSET_FOREACH(map, old_profile->output_mappings, idx)
1098
-            if (new_profile && !pa_idxset_contains(new_profile->output_mappings, map))
1099
-                if (map->ucm_context.ucm_modifier && ucm_modifier_disable(ucm, map->ucm_context.ucm_modifier) < 0)
1100
-                    ret = -1;
1101
-
1102
-        /* Disable devices not in new profile */
1103
-        PA_IDXSET_FOREACH(map, old_profile->input_mappings, idx)
1104
-            if (new_profile && !pa_idxset_contains(new_profile->input_mappings, map))
1105
-                if (map->ucm_context.ucm_device && ucm_device_disable(ucm, map->ucm_context.ucm_device) < 0)
1106
-                    ret = -1;
1107
-
1108
-        PA_IDXSET_FOREACH(map, old_profile->output_mappings, idx)
1109
-            if (new_profile && !pa_idxset_contains(new_profile->output_mappings, map))
1110
-                if (map->ucm_context.ucm_device && ucm_device_disable(ucm, map->ucm_context.ucm_device) < 0)
1111
-                    ret = -1;
1112
     }
1113
-    ucm->active_verb = verb;
1114
-
1115
-    update_mixer_paths(card->ports, verb_name);
1116
 
1117
+    update_mixer_paths(card->ports, profile);
1118
     return ret;
1119
 }
1120
 
1121
-int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port) {
1122
+int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
1123
+    int i;
1124
+    int ret = 0;
1125
     pa_alsa_ucm_config *ucm;
1126
+    const char **enable_devs;
1127
+    int enable_num = 0;
1128
+    uint32_t idx;
1129
     pa_alsa_ucm_device *dev;
1130
-    pa_alsa_ucm_port_data *data;
1131
 
1132
     pa_assert(context && context->ucm);
1133
 
1134
     ucm = context->ucm;
1135
     pa_assert(ucm->ucm_mgr);
1136
 
1137
-    data = PA_DEVICE_PORT_DATA(port);
1138
-    dev = data->device;
1139
+    enable_devs = pa_xnew(const char *, pa_idxset_size(context->ucm_devices));
1140
+
1141
+    /* first disable then enable */
1142
+    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1143
+        const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1144
+
1145
+        if (ucm_port_contains(port->name, dev_name, is_sink))
1146
+            enable_devsenable_num++ = dev_name;
1147
+        else {
1148
+            pa_log_debug("Disable ucm device %s", dev_name);
1149
+            if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) > 0) {
1150
+                pa_log("Failed to disable ucm device %s", dev_name);
1151
+                ret = -1;
1152
+                break;
1153
+            }
1154
+        }
1155
+    }
1156
 
1157
-    return ucm_device_enable(ucm, dev);
1158
+    for (i = 0; i < enable_num; i++) {
1159
+        pa_log_debug("Enable ucm device %s", enable_devsi);
1160
+        if (snd_use_case_set(ucm->ucm_mgr, "_enadev", enable_devsi) < 0) {
1161
+            pa_log("Failed to enable ucm device %s", enable_devsi);
1162
+            ret = -1;
1163
+            break;
1164
+        }
1165
+    }
1166
+
1167
+    pa_xfree(enable_devs);
1168
+
1169
+    return ret;
1170
 }
1171
 
1172
 static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) {
1173
@@ -1581,7 +1474,7 @@
1174
     const char *new_desc, *mdev;
1175
     bool is_sink = m->direction == PA_ALSA_DIRECTION_OUTPUT;
1176
 
1177
-    m->ucm_context.ucm_device = device;
1178
+    pa_idxset_put(m->ucm_context.ucm_devices, device, NULL);
1179
 
1180
     new_desc = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1181
     cur_desc = m->description;
1182
@@ -1610,7 +1503,7 @@
1183
     const char *new_desc, *mod_name, *channel_str;
1184
     uint32_t channels = 0;
1185
 
1186
-    m->ucm_context.ucm_modifier = modifier;
1187
+    pa_idxset_put(m->ucm_context.ucm_modifiers, modifier, NULL);
1188
 
1189
     new_desc = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1190
     cur_desc = m->description;
1191
@@ -1651,11 +1544,17 @@
1192
         pa_channel_map_init(&m->channel_map);
1193
 }
1194
 
1195
-static pa_alsa_mapping* ucm_alsa_mapping_get(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, const char *verb_name, const char *ucm_name, bool is_sink) {
1196
+static pa_alsa_mapping* ucm_alsa_mapping_get(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, const char *verb_name, const char *device_str, bool is_sink) {
1197
     pa_alsa_mapping *m;
1198
     char *mapping_name;
1199
+    size_t ucm_alibpref_len = 0;
1200
 
1201
-    mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, ucm_name, is_sink ? "sink" : "source");
1202
+    /* find private alsa-lib's configuration device prefix */
1203
+
1204
+    if (ucm->alib_prefix && pa_startswith(device_str, ucm->alib_prefix))
1205
+        ucm_alibpref_len = strlen(ucm->alib_prefix);
1206
+
1207
+    mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str + ucm_alibpref_len, is_sink ? "sink" : "source");
1208
 
1209
     m = pa_alsa_mapping_get(ps, mapping_name);
1210
 
1211
@@ -1670,6 +1569,7 @@
1212
 static int ucm_create_mapping_direction(
1213
         pa_alsa_ucm_config *ucm,
1214
         pa_alsa_profile_set *ps,
1215
+        pa_alsa_profile *p,
1216
         pa_alsa_ucm_device *device,
1217
         const char *verb_name,
1218
         const char *device_name,
1219
@@ -1679,7 +1579,7 @@
1220
     pa_alsa_mapping *m;
1221
     unsigned priority, rate, channels;
1222
 
1223
-    m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_name, is_sink);
1224
+    m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink);
1225
 
1226
     if (!m)
1227
         return -1;
1228
@@ -1690,7 +1590,8 @@
1229
     rate = is_sink ? device->playback_rate : device->capture_rate;
1230
     channels = is_sink ? device->playback_channels : device->capture_channels;
1231
 
1232
-    if (!m->ucm_context.ucm_device) {   /* new mapping */
1233
+    if (!m->ucm_context.ucm_devices) {   /* new mapping */
1234
+        m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1235
         m->ucm_context.ucm = ucm;
1236
         m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
1237
 
1238
@@ -1698,6 +1599,7 @@
1239
         m->device_strings0 = pa_xstrdup(device_str);
1240
         m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
1241
 
1242
+        ucm_add_mapping(p, m);
1243
         if (rate)
1244
             m->sample_spec.rate = rate;
1245
         pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1246
@@ -1719,6 +1621,7 @@
1247
 static int ucm_create_mapping_for_modifier(
1248
         pa_alsa_ucm_config *ucm,
1249
         pa_alsa_profile_set *ps,
1250
+        pa_alsa_profile *p,
1251
         pa_alsa_ucm_modifier *modifier,
1252
         const char *verb_name,
1253
         const char *mod_name,
1254
@@ -1727,14 +1630,16 @@
1255
 
1256
     pa_alsa_mapping *m;
1257
 
1258
-    m = ucm_alsa_mapping_get(ucm, ps, verb_name, mod_name, is_sink);
1259
+    m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink);
1260
 
1261
     if (!m)
1262
         return -1;
1263
 
1264
     pa_log_info("UCM mapping: %s modifier %s", m->name, mod_name);
1265
 
1266
-    if (!m->ucm_context.ucm_device && !m->ucm_context.ucm_modifier) {   /* new mapping */
1267
+    if (!m->ucm_context.ucm_devices && !m->ucm_context.ucm_modifiers) {   /* new mapping */
1268
+        m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1269
+        m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1270
         m->ucm_context.ucm = ucm;
1271
         m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
1272
 
1273
@@ -1743,7 +1648,10 @@
1274
         m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
1275
         /* Modifier sinks should not be routed to by default */
1276
         m->priority = 0;
1277
-    }
1278
+
1279
+        ucm_add_mapping(p, m);
1280
+    } else if (!m->ucm_context.ucm_modifiers) /* share pcm with device */
1281
+        m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1282
 
1283
     alsa_mapping_add_ucm_modifier(m, modifier);
1284
 
1285
@@ -1753,6 +1661,7 @@
1286
 static int ucm_create_mapping(
1287
         pa_alsa_ucm_config *ucm,
1288
         pa_alsa_profile_set *ps,
1289
+        pa_alsa_profile *p,
1290
         pa_alsa_ucm_device *device,
1291
         const char *verb_name,
1292
         const char *device_name,
1293
@@ -1767,9 +1676,9 @@
1294
     }
1295
 
1296
     if (sink)
1297
-        ret = ucm_create_mapping_direction(ucm, ps, device, verb_name, device_name, sink, true);
1298
+        ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, sink, true);
1299
     if (ret == 0 && source)
1300
-        ret = ucm_create_mapping_direction(ucm, ps, device, verb_name, device_name, source, false);
1301
+        ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, source, false);
1302
 
1303
     return ret;
1304
 }
1305
@@ -1842,28 +1751,27 @@
1306
         pa_alsa_ucm_config *ucm,
1307
         pa_alsa_profile_set *ps,
1308
         pa_alsa_ucm_verb *verb,
1309
-        pa_idxset *mappings,
1310
-        const char *profile_name,
1311
-        const char *profile_desc,
1312
-        unsigned int profile_priority) {
1313
+        const char *verb_name,
1314
+        const char *verb_desc) {
1315
 
1316
     pa_alsa_profile *p;
1317
-    pa_alsa_mapping *map;
1318
-    uint32_t idx;
1319
+    pa_alsa_ucm_device *dev;
1320
+    pa_alsa_ucm_modifier *mod;
1321
+    int i = 0;
1322
+    const char *name, *sink, *source;
1323
+    unsigned int priority;
1324
 
1325
     pa_assert(ps);
1326
 
1327
-    if (pa_hashmap_get(ps->profiles, profile_name)) {
1328
-        pa_log("Profile %s already exists", profile_name);
1329
+    if (pa_hashmap_get(ps->profiles, verb_name)) {
1330
+        pa_log("Verb %s already exists", verb_name);
1331
         return -1;
1332
     }
1333
 
1334
     p = pa_xnew0(pa_alsa_profile, 1);
1335
     p->profile_set = ps;
1336
-    p->name = pa_xstrdup(profile_name);
1337
-    p->description = pa_xstrdup(profile_desc);
1338
-    p->priority = profile_priority;
1339
-    p->ucm_context.verb = verb;
1340
+    p->name = pa_xstrdup(verb_name);
1341
+    p->description = pa_xstrdup(verb_desc);
1342
 
1343
     p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1344
     p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1345
@@ -1871,36 +1779,10 @@
1346
     p->supported = true;
1347
     pa_hashmap_put(ps->profiles, p->name, p);
1348
 
1349
-    PA_IDXSET_FOREACH(map, mappings, idx)
1350
-        ucm_add_mapping(p, map);
1351
-
1352
-    pa_alsa_profile_dump(p);
1353
-
1354
-    return 0;
1355
-}
1356
-
1357
-static int ucm_create_verb_profiles(
1358
-        pa_alsa_ucm_config *ucm,
1359
-        pa_alsa_profile_set *ps,
1360
-        pa_alsa_ucm_verb *verb,
1361
-        const char *verb_name,
1362
-        const char *verb_desc) {
1363
-
1364
-    pa_idxset *verb_devices, *p_devices, *p_mappings;
1365
-    pa_alsa_ucm_device *dev;
1366
-    pa_alsa_ucm_modifier *mod;
1367
-    int i = 0;
1368
-    int n_profiles = 0;
1369
-    const char *name, *sink, *source;
1370
-    char *p_name, *p_desc, *tmp;
1371
-    unsigned int verb_priority, p_priority;
1372
-    uint32_t idx;
1373
-    void *state = NULL;
1374
-
1375
     /* TODO: get profile priority from policy management */
1376
-    verb_priority = verb->priority;
1377
+    priority = verb->priority;
1378
 
1379
-    if (verb_priority == 0) {
1380
+    if (priority == 0) {
1381
         char *verb_cmp, *c;
1382
         c = verb_cmp = pa_xstrdup(verb_name);
1383
         while (*c) {
1384
@@ -1909,13 +1791,15 @@
1385
         }
1386
         for (i = 0; verb_infoi.id; i++) {
1387
             if (strcasecmp(verb_infoi.id, verb_cmp) == 0) {
1388
-                verb_priority = verb_infoi.priority;
1389
+                priority = verb_infoi.priority;
1390
                 break;
1391
             }
1392
         }
1393
         pa_xfree(verb_cmp);
1394
     }
1395
 
1396
+    p->priority = priority;
1397
+
1398
     PA_LLIST_FOREACH(dev, verb->devices) {
1399
         pa_alsa_jack *jack;
1400
         const char *jack_hw_mute;
1401
@@ -1925,7 +1809,7 @@
1402
         sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
1403
         source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
1404
 
1405
-        ucm_create_mapping(ucm, ps, dev, verb_name, name, sink, source);
1406
+        ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source);
1407
 
1408
         jack = ucm_get_jack(ucm, dev);
1409
         if (jack)
1410
@@ -1976,74 +1860,12 @@
1411
         source = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SOURCE);
1412
 
1413
         if (sink)
1414
-            ucm_create_mapping_for_modifier(ucm, ps, mod, verb_name, name, sink, true);
1415
+            ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, sink, true);
1416
         else if (source)
1417
-            ucm_create_mapping_for_modifier(ucm, ps, mod, verb_name, name, source, false);
1418
+            ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, source, false);
1419
     }
1420
 
1421
-    verb_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1422
-    PA_LLIST_FOREACH(dev, verb->devices)
1423
-        pa_idxset_put(verb_devices, dev, NULL);
1424
-
1425
-    while ((p_devices = iterate_maximal_device_subsets(verb_devices, &state))) {
1426
-        p_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1427
-
1428
-        /* Add the mappings that include our selected devices */
1429
-        PA_IDXSET_FOREACH(dev, p_devices, idx) {
1430
-            if (dev->playback_mapping)
1431
-                pa_idxset_put(p_mappings, dev->playback_mapping, NULL);
1432
-            if (dev->capture_mapping)
1433
-                pa_idxset_put(p_mappings, dev->capture_mapping, NULL);
1434
-        }
1435
-
1436
-        /* Add mappings only for the modifiers that can work with our
1437
-         * device selection */
1438
-        PA_LLIST_FOREACH(mod, verb->modifiers)
1439
-            if (pa_idxset_isempty(mod->supported_devices) || pa_idxset_issubset(mod->supported_devices, p_devices))
1440
-                if (pa_idxset_isdisjoint(mod->conflicting_devices, p_devices)) {
1441
-                    if (mod->playback_mapping)
1442
-                        pa_idxset_put(p_mappings, mod->playback_mapping, NULL);
1443
-                    if (mod->capture_mapping)
1444
-                        pa_idxset_put(p_mappings, mod->capture_mapping, NULL);
1445
-                }
1446
-
1447
-        /* If we'll have multiple profiles for this verb, their names
1448
-         * must be unique. Use a list of chosen devices to disambiguate
1449
-         * them. If the profile contains all devices of a verb, we'll
1450
-         * generate only onle profile whose name should be the verb
1451
-         * name. GUIs usually show the profile description instead of
1452
-         * the name, add the device names to those as well. */
1453
-        tmp = devset_name(p_devices, ", ");
1454
-        if (pa_idxset_equals(p_devices, verb_devices)) {
1455
-            p_name = pa_xstrdup(verb_name);
1456
-            p_desc = pa_xstrdup(verb_desc);
1457
-        } else {
1458
-            p_name = pa_sprintf_malloc("%s (%s)", verb_name, tmp);
1459
-            p_desc = pa_sprintf_malloc("%s (%s)", verb_desc, tmp);
1460
-        }
1461
-
1462
-        /* Make sure profiles with higher-priority devices are
1463
-         * prioritized. */
1464
-        p_priority = verb_priority + devset_playback_priority(p_devices, false) + devset_capture_priority(p_devices, false);
1465
-
1466
-        if (ucm_create_profile(ucm, ps, verb, p_mappings, p_name, p_desc, p_priority) == 0) {
1467
-            pa_log_debug("Created profile %s for UCM verb %s", p_name, verb_name);
1468
-            n_profiles++;
1469
-        }
1470
-
1471
-        pa_xfree(tmp);
1472
-        pa_xfree(p_name);
1473
-        pa_xfree(p_desc);
1474
-        pa_idxset_free(p_mappings, NULL);
1475
-        pa_idxset_free(p_devices, NULL);
1476
-    }
1477
-
1478
-    pa_idxset_free(verb_devices, NULL);
1479
-
1480
-    if (n_profiles == 0) {
1481
-        pa_log("UCM verb %s created no profiles", verb_name);
1482
-        return -1;
1483
-    }
1484
+    pa_alsa_profile_dump(p);
1485
 
1486
     return 0;
1487
 }
1488
@@ -2052,6 +1874,7 @@
1489
 {
1490
     pa_alsa_ucm_mapping_context *context = &m->ucm_context;
1491
     pa_alsa_ucm_device *dev;
1492
+    uint32_t idx;
1493
     char *mdev, *alib_prefix;
1494
     snd_pcm_info_t *info;
1495
     int pcm_card, pcm_device;
1496
@@ -2067,12 +1890,13 @@
1497
 
1498
     alib_prefix = context->ucm->alib_prefix;
1499
 
1500
-    dev = context->ucm_device;
1501
-    mdev = pa_sprintf_malloc("%shw:%i", alib_prefix ? alib_prefix : "", pcm_card);
1502
-    if (mdev == NULL)
1503
-        return;
1504
-    dev->eld_mixer_device_name = mdev;
1505
-    dev->eld_device = pcm_device;
1506
+    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1507
+       mdev = pa_sprintf_malloc("%shw:%i", alib_prefix ? alib_prefix : "", pcm_card);
1508
+       if (mdev == NULL)
1509
+           continue;
1510
+       dev->eld_mixer_device_name = mdev;
1511
+       dev->eld_device = pcm_device;
1512
+    }
1513
 }
1514
 
1515
 static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m, int mode) {
1516
@@ -2094,7 +1918,7 @@
1517
     try_buffer_size = ucm->default_n_fragments * try_period_size;
1518
 
1519
     pcm = pa_alsa_open_by_device_string(m->device_strings0, NULL, &try_ss,
1520
-            &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, NULL, NULL, exact_channels);
1521
+            &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, exact_channels);
1522
 
1523
     if (pcm) {
1524
         if (!exact_channels)
1525
@@ -2136,39 +1960,38 @@
1526
     snd_mixer_t *mixer_handle;
1527
     pa_alsa_ucm_mapping_context *context = &m->ucm_context;
1528
     pa_alsa_ucm_device *dev;
1529
-    bool has_control;
1530
+    uint32_t idx;
1531
 
1532
-    dev = context->ucm_device;
1533
-    if (!dev->jack || !dev->jack->mixer_device_name)
1534
-        return;
1535
+    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1536
+        bool has_control;
1537
 
1538
-    mixer_handle = pa_alsa_open_mixer_by_name(mixers, dev->jack->mixer_device_name, true);
1539
-    if (!mixer_handle) {
1540
-        pa_log_error("Unable to determine open mixer device '%s' for jack %s", dev->jack->mixer_device_name, dev->jack->name);
1541
-        return;
1542
-    }
1543
+        if (!dev->jack || !dev->jack->mixer_device_name)
1544
+            continue;
1545
+
1546
+        mixer_handle = pa_alsa_open_mixer_by_name(mixers, dev->jack->mixer_device_name, true);
1547
+        if (!mixer_handle) {
1548
+            pa_log_error("Unable to determine open mixer device '%s' for jack %s", dev->jack->mixer_device_name, dev->jack->name);
1549
+            continue;
1550
+        }
1551
 
1552
-    has_control = pa_alsa_mixer_find_card(mixer_handle, &dev->jack->alsa_id, 0) != NULL;
1553
-    pa_alsa_jack_set_has_control(dev->jack, has_control);
1554
-    pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control);
1555
+        has_control = pa_alsa_mixer_find_card(mixer_handle, &dev->jack->alsa_id, 0) != NULL;
1556
+        pa_alsa_jack_set_has_control(dev->jack, has_control);
1557
+        pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control);
1558
+    }
1559
 }
1560
 
1561
 static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) {
1562
     void *state;
1563
     pa_alsa_profile *p;
1564
     pa_alsa_mapping *m;
1565
-    const char *verb_name;
1566
     uint32_t idx;
1567
 
1568
     PA_HASHMAP_FOREACH(p, ps->profiles, state) {
1569
-        pa_log_info("Probing profile %s", p->name);
1570
-
1571
         /* change verb */
1572
-        verb_name = pa_proplist_gets(p->ucm_context.verb->proplist, PA_ALSA_PROP_UCM_NAME);
1573
-        pa_log_info("Set ucm verb to %s", verb_name);
1574
+        pa_log_info("Set ucm verb to %s", p->name);
1575
 
1576
-        if ((snd_use_case_set(ucm->ucm_mgr, "_verb", verb_name)) < 0) {
1577
-            pa_log("Failed to set verb %s", verb_name);
1578
+        if ((snd_use_case_set(ucm->ucm_mgr, "_verb", p->name)) < 0) {
1579
+            pa_log("Failed to set verb %s", p->name);
1580
             p->supported = false;
1581
             continue;
1582
         }
1583
@@ -2238,7 +2061,7 @@
1584
                                        (pa_free_cb_t) pa_alsa_profile_free);
1585
     ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1586
 
1587
-    /* create profiles for each verb */
1588
+    /* create a profile for each verb */
1589
     PA_LLIST_FOREACH(verb, ucm->verbs) {
1590
         const char *verb_name;
1591
         const char *verb_desc;
1592
@@ -2250,7 +2073,7 @@
1593
             continue;
1594
         }
1595
 
1596
-        ucm_create_verb_profiles(ucm, ps, verb, verb_name, verb_desc);
1597
+        ucm_create_profile(ucm, ps, verb, verb_name, verb_desc);
1598
     }
1599
 
1600
     ucm_probe_profile_set(ucm, ps);
1601
@@ -2279,8 +2102,10 @@
1602
 
1603
         pa_proplist_free(di->proplist);
1604
 
1605
-        pa_idxset_free(di->conflicting_devices, NULL);
1606
-        pa_idxset_free(di->supported_devices, NULL);
1607
+        if (di->conflicting_devices)
1608
+            pa_idxset_free(di->conflicting_devices, NULL);
1609
+        if (di->supported_devices)
1610
+            pa_idxset_free(di->supported_devices, NULL);
1611
 
1612
         pa_xfree(di->eld_mixer_device_name);
1613
 
1614
@@ -2290,8 +2115,10 @@
1615
     PA_LLIST_FOREACH_SAFE(mi, mn, verb->modifiers) {
1616
         PA_LLIST_REMOVE(pa_alsa_ucm_modifier, verb->modifiers, mi);
1617
         pa_proplist_free(mi->proplist);
1618
-        pa_idxset_free(mi->conflicting_devices, NULL);
1619
-        pa_idxset_free(mi->supported_devices, NULL);
1620
+        if (mi->n_suppdev > 0)
1621
+            snd_use_case_free_list(mi->supported_devices, mi->n_suppdev);
1622
+        if (mi->n_confdev > 0)
1623
+            snd_use_case_free_list(mi->conflicting_devices, mi->n_confdev);
1624
         pa_xfree(mi->media_role);
1625
         pa_xfree(mi);
1626
     }
1627
@@ -2339,22 +2166,29 @@
1628
 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
1629
     pa_alsa_ucm_device *dev;
1630
     pa_alsa_ucm_modifier *mod;
1631
+    uint32_t idx;
1632
 
1633
-    dev = context->ucm_device;
1634
-    if (dev) {
1635
+    if (context->ucm_devices) {
1636
         /* clear ucm device pointer to mapping */
1637
-        if (context->direction == PA_DIRECTION_OUTPUT)
1638
-            dev->playback_mapping = NULL;
1639
-        else
1640
-            dev->capture_mapping = NULL;
1641
+        PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1642
+            if (context->direction == PA_DIRECTION_OUTPUT)
1643
+                dev->playback_mapping = NULL;
1644
+            else
1645
+                dev->capture_mapping = NULL;
1646
+        }
1647
+
1648
+        pa_idxset_free(context->ucm_devices, NULL);
1649
     }
1650
 
1651
-    mod = context->ucm_modifier;
1652
-    if (mod) {
1653
-        if (context->direction == PA_DIRECTION_OUTPUT)
1654
-            mod->playback_mapping = NULL;
1655
-        else
1656
-            mod->capture_mapping = NULL;
1657
+    if (context->ucm_modifiers) {
1658
+        PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
1659
+            if (context->direction == PA_DIRECTION_OUTPUT)
1660
+                mod->playback_mapping = NULL;
1661
+            else
1662
+                mod->capture_mapping = NULL;
1663
+        }
1664
+
1665
+        pa_idxset_free(context->ucm_modifiers, NULL);
1666
     }
1667
 }
1668
 
1669
@@ -2368,7 +2202,12 @@
1670
     PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
1671
         if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
1672
             if (mod->enabled_counter == 0) {
1673
-                ucm_modifier_enable(ucm, mod);
1674
+                const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
1675
+
1676
+                pa_log_info("Enable ucm modifier %s", mod_name);
1677
+                if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) {
1678
+                    pa_log("Failed to enable ucm modifier %s", mod_name);
1679
+                }
1680
             }
1681
 
1682
             mod->enabled_counter++;
1683
@@ -2388,14 +2227,27 @@
1684
         if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
1685
 
1686
             mod->enabled_counter--;
1687
-            if (mod->enabled_counter == 0)
1688
-                ucm_modifier_disable(ucm, mod);
1689
+            if (mod->enabled_counter == 0) {
1690
+                const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
1691
+
1692
+                pa_log_info("Disable ucm modifier %s", mod_name);
1693
+                if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) {
1694
+                    pa_log("Failed to disable ucm modifier %s", mod_name);
1695
+                }
1696
+            }
1697
 
1698
             break;
1699
         }
1700
     }
1701
 }
1702
 
1703
+static void device_add_ucm_port(pa_alsa_ucm_device *device, pa_alsa_ucm_port_data *port) {
1704
+    pa_assert(device);
1705
+    pa_assert(port);
1706
+
1707
+    pa_dynarray_append(device->ucm_ports, port);
1708
+}
1709
+
1710
 static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) {
1711
     pa_assert(device);
1712
     pa_assert(jack);
1713
@@ -2428,7 +2280,7 @@
1714
     device->available = available;
1715
 
1716
     PA_DYNARRAY_FOREACH(port, device->ucm_ports, idx)
1717
-        pa_device_port_set_available(port->core_port, port->device->available);
1718
+        ucm_port_update_available(port);
1719
 }
1720
 
1721
 void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device) {
1722
@@ -2452,21 +2304,26 @@
1723
 }
1724
 
1725
 static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
1726
-                               pa_alsa_ucm_device *device) {
1727
+                               pa_alsa_ucm_device **devices, unsigned n_devices) {
1728
+    unsigned i;
1729
+
1730
     pa_assert(ucm);
1731
     pa_assert(core_port);
1732
-    pa_assert(device);
1733
+    pa_assert(devices);
1734
 
1735
     port->ucm = ucm;
1736
     port->core_port = core_port;
1737
+    port->devices = pa_dynarray_new(NULL);
1738
     port->eld_device = -1;
1739
 
1740
-    port->device = device;
1741
-    pa_dynarray_append(device->ucm_ports, port);
1742
+    for (i = 0; i < n_devices; i++) {
1743
+        pa_dynarray_append(port->devices, devicesi);
1744
+        device_add_ucm_port(devicesi, port);
1745
+    }
1746
 
1747
     port->paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree, NULL);
1748
 
1749
-    pa_device_port_set_available(port->core_port, port->device->available);
1750
+    ucm_port_update_available(port);
1751
 }
1752
 
1753
 static void ucm_port_data_free(pa_device_port *port) {
1754
@@ -2476,12 +2333,34 @@
1755
 
1756
     ucm_port = PA_DEVICE_PORT_DATA(port);
1757
 
1758
+    if (ucm_port->devices)
1759
+        pa_dynarray_free(ucm_port->devices);
1760
+
1761
     if (ucm_port->paths)
1762
         pa_hashmap_free(ucm_port->paths);
1763
 
1764
     pa_xfree(ucm_port->eld_mixer_device_name);
1765
 }
1766
 
1767
+static void ucm_port_update_available(pa_alsa_ucm_port_data *port) {
1768
+    pa_alsa_ucm_device *device;
1769
+    unsigned idx;
1770
+    pa_available_t available = PA_AVAILABLE_YES;
1771
+
1772
+    pa_assert(port);
1773
+
1774
+    PA_DYNARRAY_FOREACH(device, port->devices, idx) {
1775
+        if (device->available == PA_AVAILABLE_UNKNOWN)
1776
+            available = PA_AVAILABLE_UNKNOWN;
1777
+        else if (device->available == PA_AVAILABLE_NO) {
1778
+            available = PA_AVAILABLE_NO;
1779
+            break;
1780
+        }
1781
+    }
1782
+
1783
+    pa_device_port_set_available(port->core_port, available);
1784
+}
1785
+
1786
 #else /* HAVE_ALSA_UCM */
1787
 
1788
 /* Dummy functions for systems without UCM support */
1789
@@ -2495,7 +2374,7 @@
1790
     return NULL;
1791
 }
1792
 
1793
-int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile) {
1794
+int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) {
1795
     return -1;
1796
 }
1797
 
1798
@@ -2513,7 +2392,7 @@
1799
         bool ignore_dB) {
1800
 }
1801
 
1802
-void pa_alsa_ucm_add_port(
1803
+void pa_alsa_ucm_add_ports_combination(
1804
         pa_hashmap *hash,
1805
         pa_alsa_ucm_mapping_context *context,
1806
         bool is_sink,
1807
@@ -2522,7 +2401,7 @@
1808
         pa_core *core) {
1809
 }
1810
 
1811
-int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port) {
1812
+int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
1813
     return -1;
1814
 }
1815
 
1816
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-ucm.h -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/alsa-ucm.h Changed
78
 
1
@@ -142,13 +142,12 @@
2
 typedef struct pa_alsa_ucm_device pa_alsa_ucm_device;
3
 typedef struct pa_alsa_ucm_config pa_alsa_ucm_config;
4
 typedef struct pa_alsa_ucm_mapping_context pa_alsa_ucm_mapping_context;
5
-typedef struct pa_alsa_ucm_profile_context pa_alsa_ucm_profile_context;
6
 typedef struct pa_alsa_ucm_port_data pa_alsa_ucm_port_data;
7
 typedef struct pa_alsa_ucm_volume pa_alsa_ucm_volume;
8
 
9
 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index);
10
 pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map);
11
-int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile);
12
+int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile);
13
 
14
 int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb);
15
 
16
@@ -160,14 +159,14 @@
17
         pa_card *card,
18
         snd_pcm_t *pcm_handle,
19
         bool ignore_dB);
20
-void pa_alsa_ucm_add_port(
21
+void pa_alsa_ucm_add_ports_combination(
22
         pa_hashmap *hash,
23
         pa_alsa_ucm_mapping_context *context,
24
         bool is_sink,
25
         pa_hashmap *ports,
26
         pa_card_profile *cp,
27
         pa_core *core);
28
-int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port);
29
+int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink);
30
 
31
 void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm);
32
 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context);
33
@@ -224,8 +223,11 @@
34
 
35
     pa_proplist *proplist;
36
 
37
-    pa_idxset *conflicting_devices;
38
-    pa_idxset *supported_devices;
39
+    int n_confdev;
40
+    int n_suppdev;
41
+
42
+    const char **conflicting_devices;
43
+    const char **supported_devices;
44
 
45
     pa_direction_t action_direction;
46
 
47
@@ -268,23 +270,21 @@
48
     pa_alsa_ucm_config *ucm;
49
     pa_direction_t direction;
50
 
51
-    pa_alsa_ucm_device *ucm_device;
52
-    pa_alsa_ucm_modifier *ucm_modifier;
53
-};
54
-
55
-struct pa_alsa_ucm_profile_context {
56
-    pa_alsa_ucm_verb *verb;
57
+    pa_idxset *ucm_devices;
58
+    pa_idxset *ucm_modifiers;
59
 };
60
 
61
 struct pa_alsa_ucm_port_data {
62
     pa_alsa_ucm_config *ucm;
63
     pa_device_port *core_port;
64
 
65
-    pa_alsa_ucm_device *device;
66
+    /* A single port will be associated with multiple devices if it represents
67
+     * a combination of devices. */
68
+    pa_dynarray *devices; /* pa_alsa_ucm_device */
69
 
70
-    /* verb name -> pa_alsa_path for volume control */
71
+    /* profile name -> pa_alsa_path for volume control */
72
     pa_hashmap *paths;
73
-    /* Current path, set when activating verb */
74
+    /* Current path, set when activating profile */
75
     pa_alsa_path *path;
76
 
77
     /* ELD info */
78
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-util.c -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/alsa-util.c Changed
363
 
1
@@ -505,8 +505,6 @@
2
         snd_pcm_uframes_t tsched_size,
3
         bool *use_mmap,
4
         bool *use_tsched,
5
-        pa_sample_format_t **query_supported_formats,
6
-        unsigned int **query_supported_rates,
7
         pa_alsa_profile_set *ps,
8
         pa_alsa_mapping **mapping) {
9
 
10
@@ -545,8 +543,6 @@
11
                 tsched_size,
12
                 use_mmap,
13
                 use_tsched,
14
-                query_supported_formats,
15
-                query_supported_rates,
16
                 m);
17
 
18
         if (pcm_handle) {
19
@@ -574,8 +570,6 @@
20
                 tsched_size,
21
                 use_mmap,
22
                 use_tsched,
23
-                query_supported_formats,
24
-                query_supported_rates,
25
                 m);
26
 
27
         if (pcm_handle) {
28
@@ -600,8 +594,6 @@
29
             tsched_size,
30
             use_mmap,
31
             use_tsched,
32
-            query_supported_formats,
33
-            query_supported_rates,
34
             false);
35
     pa_xfree(d);
36
 
37
@@ -623,8 +615,6 @@
38
         snd_pcm_uframes_t tsched_size,
39
         bool *use_mmap,
40
         bool *use_tsched,
41
-        pa_sample_format_t **query_supported_formats,
42
-        unsigned int **query_supported_rates,
43
         pa_alsa_mapping *m) {
44
 
45
     snd_pcm_t *pcm_handle;
46
@@ -654,8 +644,6 @@
47
             tsched_size,
48
             use_mmap,
49
             use_tsched,
50
-            query_supported_formats,
51
-            query_supported_rates,
52
             pa_channel_map_valid(&m->channel_map) /* Query the channel count if we don't know what we want */);
53
 
54
     if (!pcm_handle)
55
@@ -693,8 +681,6 @@
56
         snd_pcm_uframes_t tsched_size,
57
         bool *use_mmap,
58
         bool *use_tsched,
59
-        pa_sample_format_t **query_supported_formats,
60
-        unsigned int **query_supported_rates,
61
         bool require_exact_channel_number) {
62
 
63
     int err;
64
@@ -722,12 +708,6 @@
65
         pa_log_info("ALSA device open '%s' %s: %p", d,
66
            mode == SND_PCM_STREAM_CAPTURE ? "capture" : "playback", pcm_handle);
67
 
68
-        if (query_supported_formats)
69
-            *query_supported_formats = pa_alsa_get_supported_formats(pcm_handle, ss->format);
70
-
71
-        if (query_supported_rates)
72
-            *query_supported_rates = pa_alsa_get_supported_rates(pcm_handle, ss->rate);
73
-
74
         if ((err = pa_alsa_set_hw_params(
75
                      pcm_handle,
76
                      ss,
77
@@ -801,8 +781,6 @@
78
         snd_pcm_uframes_t tsched_size,
79
         bool *use_mmap,
80
         bool *use_tsched,
81
-        pa_sample_format_t **query_supported_formats,
82
-        unsigned int **query_supported_rates,
83
         bool require_exact_channel_number) {
84
 
85
     snd_pcm_t *pcm_handle;
86
@@ -824,8 +802,6 @@
87
                 tsched_size,
88
                 use_mmap,
89
                 use_tsched,
90
-                query_supported_formats,
91
-                query_supported_rates,
92
                 require_exact_channel_number);
93
 
94
         pa_xfree(d);
95
@@ -1435,24 +1411,6 @@
96
 
97
     return pa_sprintf_malloc("Audio%i", i);
98
 }
99
-#endif
100
-
101
-static void dump_supported_rates(unsigned int* values)
102
-{
103
-    pa_strbuf *buf;
104
-    char *str;
105
-    int i;
106
-
107
-    buf = pa_strbuf_new();
108
-
109
-    for (i = 0; valuesi; i++) {
110
-        pa_strbuf_printf(buf, " %u", valuesi);
111
-    }
112
-
113
-    str = pa_strbuf_to_string_free(buf);
114
-    pa_log_debug("Supported rates:%s", str);
115
-    pa_xfree(str);
116
-}
117
 
118
 unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm, unsigned int fallback_rate) {
119
     static unsigned int all_rates = { 8000, 11025, 12000,
120
@@ -1460,8 +1418,7 @@
121
                                         32000, 44100, 48000,
122
                                         64000, 88200, 96000,
123
                                         128000, 176400, 192000,
124
-                                        352800, 384000,
125
-                                        705600, 768000 };
126
+                                        384000 };
127
     bool supportedPA_ELEMENTSOF(all_rates) = { false, };
128
     snd_pcm_hw_params_t *hwparams;
129
     unsigned int i, j, n, *rates = NULL;
130
@@ -1503,40 +1460,39 @@
131
         rates1 = 0;
132
     }
133
 
134
-    dump_supported_rates(rates);
135
     return rates;
136
 }
137
 
138
 pa_sample_format_t *pa_alsa_get_supported_formats(snd_pcm_t *pcm, pa_sample_format_t fallback_format) {
139
-    static const snd_pcm_format_t format_trans_to_pcm = {
140
-        PA_SAMPLE_U8 = SND_PCM_FORMAT_U8,
141
-        PA_SAMPLE_ALAW = SND_PCM_FORMAT_A_LAW,
142
-        PA_SAMPLE_ULAW = SND_PCM_FORMAT_MU_LAW,
143
-        PA_SAMPLE_S16LE = SND_PCM_FORMAT_S16_LE,
144
-        PA_SAMPLE_S16BE = SND_PCM_FORMAT_S16_BE,
145
-        PA_SAMPLE_FLOAT32LE = SND_PCM_FORMAT_FLOAT_LE,
146
-        PA_SAMPLE_FLOAT32BE = SND_PCM_FORMAT_FLOAT_BE,
147
-        PA_SAMPLE_S32LE = SND_PCM_FORMAT_S32_LE,
148
-        PA_SAMPLE_S32BE = SND_PCM_FORMAT_S32_BE,
149
-        PA_SAMPLE_S24LE = SND_PCM_FORMAT_S24_3LE,
150
-        PA_SAMPLE_S24BE = SND_PCM_FORMAT_S24_3BE,
151
-        PA_SAMPLE_S24_32LE = SND_PCM_FORMAT_S24_LE,
152
-        PA_SAMPLE_S24_32BE = SND_PCM_FORMAT_S24_BE,
153
+    static const snd_pcm_format_t format_trans_to_pa = {
154
+        SND_PCM_FORMAT_U8 = PA_SAMPLE_U8,
155
+        SND_PCM_FORMAT_A_LAW = PA_SAMPLE_ALAW,
156
+        SND_PCM_FORMAT_MU_LAW = PA_SAMPLE_ULAW,
157
+        SND_PCM_FORMAT_S16_LE = PA_SAMPLE_S16LE,
158
+        SND_PCM_FORMAT_S16_BE = PA_SAMPLE_S16BE,
159
+        SND_PCM_FORMAT_FLOAT_LE = PA_SAMPLE_FLOAT32LE,
160
+        SND_PCM_FORMAT_FLOAT_BE = PA_SAMPLE_FLOAT32BE,
161
+        SND_PCM_FORMAT_S32_LE = PA_SAMPLE_S32LE,
162
+        SND_PCM_FORMAT_S32_BE = PA_SAMPLE_S32BE,
163
+        SND_PCM_FORMAT_S24_3LE = PA_SAMPLE_S24LE,
164
+        SND_PCM_FORMAT_S24_3BE = PA_SAMPLE_S24BE,
165
+        SND_PCM_FORMAT_S24_LE = PA_SAMPLE_S24_32LE,
166
+        SND_PCM_FORMAT_S24_BE = PA_SAMPLE_S24_32BE,
167
     };
168
-    static const pa_sample_format_t all_formats = {
169
-        PA_SAMPLE_U8,
170
-        PA_SAMPLE_ALAW,
171
-        PA_SAMPLE_ULAW,
172
-        PA_SAMPLE_S16LE,
173
-        PA_SAMPLE_S16BE,
174
-        PA_SAMPLE_FLOAT32LE,
175
-        PA_SAMPLE_FLOAT32BE,
176
-        PA_SAMPLE_S32LE,
177
-        PA_SAMPLE_S32BE,
178
-        PA_SAMPLE_S24LE,
179
-        PA_SAMPLE_S24BE,
180
-        PA_SAMPLE_S24_32LE,
181
-        PA_SAMPLE_S24_32BE,
182
+    static const snd_pcm_format_t all_formats = {
183
+        SND_PCM_FORMAT_U8,
184
+        SND_PCM_FORMAT_A_LAW,
185
+        SND_PCM_FORMAT_MU_LAW,
186
+        SND_PCM_FORMAT_S16_LE,
187
+        SND_PCM_FORMAT_S16_BE,
188
+        SND_PCM_FORMAT_FLOAT_LE,
189
+        SND_PCM_FORMAT_FLOAT_BE,
190
+        SND_PCM_FORMAT_S32_LE,
191
+        SND_PCM_FORMAT_S32_BE,
192
+        SND_PCM_FORMAT_S24_3LE,
193
+        SND_PCM_FORMAT_S24_3BE,
194
+        SND_PCM_FORMAT_S24_LE,
195
+        SND_PCM_FORMAT_S24_BE,
196
     };
197
     bool supportedPA_ELEMENTSOF(all_formats) = {
198
         false,
199
@@ -1554,7 +1510,7 @@
200
     }
201
 
202
     for (i = 0, n = 0; i < PA_ELEMENTSOF(all_formats); i++) {
203
-        if (snd_pcm_hw_params_test_format(pcm, hwparams, format_trans_to_pcmall_formatsi) == 0) {
204
+        if (snd_pcm_hw_params_test_format(pcm, hwparams, all_formatsi) == 0) {
205
             supportedi = true;
206
             n++;
207
         }
208
@@ -1565,7 +1521,7 @@
209
 
210
         for (i = 0, j = 0; i < PA_ELEMENTSOF(all_formats); i++) {
211
             if (supportedi)
212
-                formatsj++ = all_formatsi;
213
+                formatsj++ = format_trans_to_paall_formatsi;
214
         }
215
 
216
         formatsj = PA_SAMPLE_MAX;
217
@@ -1573,7 +1529,7 @@
218
         formats = pa_xnew(pa_sample_format_t, 2);
219
 
220
         formats0 = fallback_format;
221
-        if ((ret = snd_pcm_hw_params_set_format(pcm, hwparams, format_trans_to_pcmformats0)) < 0) {
222
+        if ((ret = snd_pcm_hw_params_set_format(pcm, hwparams, format_trans_to_paformats0)) < 0) {
223
             pa_log_debug("snd_pcm_hw_params_set_format() failed: %s", pa_alsa_strerror(ret));
224
             pa_xfree(formats);
225
             return NULL;
226
@@ -1584,6 +1540,7 @@
227
 
228
     return formats;
229
 }
230
+#endif
231
 
232
 bool pa_alsa_pcm_is_hw(snd_pcm_t *pcm) {
233
     snd_pcm_info_t* info;
234
@@ -1643,16 +1600,14 @@
235
                                             snd_ctl_elem_iface_t iface,
236
                                             const char *name,
237
                                             unsigned int index,
238
-                                            unsigned int device,
239
-                                            unsigned int subdevice) {
240
+                                            unsigned int device) {
241
     snd_mixer_elem_t *elem;
242
 
243
     for (elem = snd_mixer_first_elem(mixer); elem; elem = snd_mixer_elem_next(elem)) {
244
-        snd_hctl_elem_t **_helem, *helem;
245
+        snd_hctl_elem_t *helem;
246
         if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_PULSEAUDIO)
247
             continue;
248
-        _helem = snd_mixer_elem_get_private(elem);
249
-        helem = *_helem;
250
+        helem = snd_mixer_elem_get_private(elem);
251
         if (snd_hctl_elem_get_interface(helem) != iface)
252
             continue;
253
         if (!pa_streq(snd_hctl_elem_get_name(helem), name))
254
@@ -1661,19 +1616,17 @@
255
             continue;
256
         if (snd_hctl_elem_get_device(helem) != device)
257
             continue;
258
-        if (snd_hctl_elem_get_subdevice(helem) != subdevice)
259
-            continue;
260
         return elem;
261
     }
262
     return NULL;
263
 }
264
 
265
 snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, struct pa_alsa_mixer_id *alsa_id, unsigned int device) {
266
-    return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_CARD, alsa_id->name, alsa_id->index, device, 0);
267
+    return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_CARD, alsa_id->name, alsa_id->index, device);
268
 }
269
 
270
 snd_mixer_elem_t *pa_alsa_mixer_find_pcm(snd_mixer_t *mixer, const char *name, unsigned int device) {
271
-    return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_PCM, name, 0, device, 0);
272
+    return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_PCM, name, 0, device);
273
 }
274
 
275
 static int mixer_class_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2)
276
@@ -1682,26 +1635,15 @@
277
     return c1 == c2 ? 0 : (c1 > c2 ? 1 : -1);
278
 }
279
 
280
-static void mixer_melem_free(snd_mixer_elem_t *elem)
281
-{
282
-    snd_hctl_elem_t **_helem;
283
-    _helem = snd_mixer_elem_get_private(elem);
284
-    pa_xfree(_helem);
285
-}
286
-
287
 static int mixer_class_event(snd_mixer_class_t *class, unsigned int mask,
288
            snd_hctl_elem_t *helem, snd_mixer_elem_t *melem)
289
 {
290
     int err;
291
     const char *name = snd_hctl_elem_get_name(helem);
292
-    snd_hctl_elem_t **_helem;
293
-    /* NOTE: The remove event is defined as '~0U`. */
294
+    // NOTE: The remove event defined as '~0U`.
295
     if (mask == SND_CTL_EVENT_MASK_REMOVE) {
296
-        /* NOTE: Unless we remove the pointer to melem from the linked-list at
297
-         * private_data of helem, an assertion will be hit in alsa-lib since
298
-         * the list is not empty. */
299
-        _helem = snd_mixer_elem_get_private(melem);
300
-        *_helem = NULL;
301
+        // NOTE: unless remove pointer to melem from link-list at private_data of helem, hits
302
+   // assersion in alsa-lib since the list is not empty.
303
         snd_mixer_elem_detach(melem, helem);
304
     } else if (mask & SND_CTL_EVENT_MASK_ADD) {
305
         snd_ctl_elem_iface_t iface = snd_hctl_elem_get_interface(helem);
306
@@ -1711,50 +1653,26 @@
307
             const char *name = snd_hctl_elem_get_name(helem);
308
             const int index = snd_hctl_elem_get_index(helem);
309
             const int device = snd_hctl_elem_get_device(helem);
310
-            const int subdevice = snd_hctl_elem_get_subdevice(helem);
311
             snd_mixer_elem_t *new_melem;
312
-            bool found = true;
313
 
314
-            new_melem = pa_alsa_mixer_find(mixer, iface, name, index, device, subdevice);
315
+            new_melem = pa_alsa_mixer_find(mixer, iface, name, index, device);
316
             if (!new_melem) {
317
-                _helem = pa_xmalloc(sizeof(snd_hctl_elem_t *));
318
-                *_helem = helem;
319
                 /* Put the hctl pointer as our private data - it will be useful for callbacks */
320
-                if ((err = snd_mixer_elem_new(&new_melem, SND_MIXER_ELEM_PULSEAUDIO, 0, _helem, mixer_melem_free)) < 0) {
321
+                if ((err = snd_mixer_elem_new(&new_melem, SND_MIXER_ELEM_PULSEAUDIO, 0, helem, NULL)) < 0) {
322
                     pa_log_warn("snd_mixer_elem_new failed: %s", pa_alsa_strerror(err));
323
                     return 0;
324
                 }
325
-                found = false;
326
-            } else {
327
-                _helem = snd_mixer_elem_get_private(new_melem);
328
-                if (_helem) {
329
-                    char *s1, *s2;
330
-                    snd_ctl_elem_id_t *id1, *id2;
331
-                    snd_ctl_elem_id_alloca(&id1);
332
-                    snd_ctl_elem_id_alloca(&id2);
333
-                    snd_hctl_elem_get_id(helem, id1);
334
-                    snd_hctl_elem_get_id(*_helem, id2);
335
-                    s1 = snd_ctl_ascii_elem_id_get(id1);
336
-                    s2 = snd_ctl_ascii_elem_id_get(id2);
337
-                    pa_log_warn("mixer_class_event - duplicate mixer controls: %s | %s", s1, s2);
338
-                    free(s2);
339
-                    free(s1);
340
-                    return 0;
341
-                }
342
-                *_helem = helem;
343
             }
344
 
345
             if ((err = snd_mixer_elem_attach(new_melem, helem)) < 0) {
346
                 pa_log_warn("snd_mixer_elem_attach failed: %s", pa_alsa_strerror(err));
347
-                snd_mixer_elem_free(melem);
348
+       snd_mixer_elem_free(melem);
349
                 return 0;
350
             }
351
 
352
-            if (!found) {
353
-                if ((err = snd_mixer_elem_add(new_melem, class)) < 0) {
354
-                    pa_log_warn("snd_mixer_elem_add failed: %s", pa_alsa_strerror(err));
355
-                    return 0;
356
-                }
357
+            if ((err = snd_mixer_elem_add(new_melem, class)) < 0) {
358
+                pa_log_warn("snd_mixer_elem_add failed: %s", pa_alsa_strerror(err));
359
+                return 0;
360
             }
361
         }
362
     }
363
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-util.h -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/alsa-util.h Changed
37
 
1
@@ -64,8 +64,6 @@
2
         snd_pcm_uframes_t tsched_size,
3
         bool *use_mmap,                   /* modified at return */
4
         bool *use_tsched,                 /* modified at return */
5
-        pa_sample_format_t **query_supported_formats, /* modified at return */
6
-        unsigned int **query_supported_rates,         /* modified at return */
7
         pa_alsa_profile_set *ps,
8
         pa_alsa_mapping **mapping);       /* modified at return */
9
 #endif
10
@@ -82,8 +80,6 @@
11
         snd_pcm_uframes_t tsched_size,
12
         bool *use_mmap,                   /* modified at return */
13
         bool *use_tsched,                 /* modified at return */
14
-        pa_sample_format_t **query_supported_formats, /* modified at return */
15
-        unsigned int **query_supported_rates,         /* modified at return */
16
         pa_alsa_mapping *mapping);
17
 
18
 /* Opens the explicit ALSA device */
19
@@ -98,8 +94,6 @@
20
         snd_pcm_uframes_t tsched_size,
21
         bool *use_mmap,                   /* modified at return */
22
         bool *use_tsched,                 /* modified at return */
23
-        pa_sample_format_t **query_supported_formats, /* modified at return */
24
-        unsigned int **query_supported_rates,         /* modified at return */
25
         bool require_exact_channel_number);
26
 
27
 /* Opens the explicit ALSA device with a fallback list */
28
@@ -115,8 +109,6 @@
29
         snd_pcm_uframes_t tsched_size,
30
         bool *use_mmap,                   /* modified at return */
31
         bool *use_tsched,                 /* modified at return */
32
-        pa_sample_format_t **query_supported_formats, /* modified at return */
33
-        unsigned int **query_supported_rates,        /* modified at return */
34
         bool require_exact_channel_number);
35
 
36
 #if 0
37
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/compat.h -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/compat.h Changed
23
 
1
@@ -47,12 +47,10 @@
2
 #define PA_LIKELY(x) (__builtin_expect(!!(x),1))
3
 #define PA_UNLIKELY(x) (__builtin_expect(!!(x),0))
4
 #define PA_PRINTF_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1)))
5
-#define PA_UNUSED __attribute__ ((unused))
6
 #else
7
 #define PA_LIKELY(x) (x)
8
 #define PA_UNLIKELY(x) (x)
9
 #define PA_PRINTF_FUNC(fmt, arg1)
10
-#define PA_UNUSED
11
 #endif
12
 
13
 #define PA_MIN(a,b)                    \
14
@@ -98,7 +96,7 @@
15
    PA_AVAILABLE_YES = 2,
16
 } pa_available_t;
17
 
18
-#define PA_RATE_MAX (48000U*16U)
19
+#define PA_RATE_MAX (48000U*8U)
20
 
21
 typedef enum pa_sample_format {
22
    PA_SAMPLE_U8,       /**< Unsigned 8 Bit PCM */
23
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/idxset.h -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/idxset.h Changed
126
 
1
@@ -130,25 +130,13 @@
2
    return count;
3
 }
4
 
5
-static inline pa_idxset_item *pa_idxset_search(pa_idxset *s, uint32_t *idx)
6
+static inline void *pa_idxset_search(pa_idxset *s, uint32_t *idx)
7
 {
8
         pa_idxset_item *item;
9
    for (item = pa_array_get_unchecked(&s->array, *idx, pa_idxset_item);
10
         pa_array_check(&s->array, item); item++, (*idx)++) {
11
        if (item->ptr != NULL)
12
-           return item;
13
-   }
14
-   *idx = PA_IDXSET_INVALID;
15
-   return NULL;
16
-}
17
-
18
-static inline pa_idxset_item *pa_idxset_reverse_search(pa_idxset *s, uint32_t *idx)
19
-{
20
-        pa_idxset_item *item;
21
-   for (item = pa_array_get_unchecked(&s->array, *idx, pa_idxset_item);
22
-        pa_array_check(&s->array, item); item--, (*idx)--) {
23
-       if (item->ptr != NULL)
24
-           return item;
25
+           return item->ptr;
26
    }
27
    *idx = PA_IDXSET_INVALID;
28
    return NULL;
29
@@ -156,93 +144,29 @@
30
 
31
 static inline void *pa_idxset_next(pa_idxset *s, uint32_t *idx)
32
 {
33
-   pa_idxset_item *item;
34
    (*idx)++;;
35
-   item = pa_idxset_search(s, idx);
36
-   return item ? item->ptr : NULL;
37
+   return pa_idxset_search(s, idx);
38
 }
39
 
40
 static inline void* pa_idxset_first(pa_idxset *s, uint32_t *idx)
41
 {
42
    uint32_t i = 0;
43
-   pa_idxset_item *item = pa_idxset_search(s, &i);
44
+   void *ptr = pa_idxset_search(s, &i);
45
    if (idx)
46
        *idx = i;
47
-   return item ? item->ptr : NULL;
48
-}
49
-
50
-static inline void* pa_idxset_last(pa_idxset *s, uint32_t *idx)
51
-{
52
-   uint32_t i = pa_array_get_len(&s->array, pa_idxset_item) - 1;
53
-   pa_idxset_item *item = pa_idxset_reverse_search(s, &i);
54
-   if (idx)
55
-       *idx = i;
56
-   return item ? item->ptr : NULL;
57
-}
58
-
59
-static inline void* pa_idxset_steal_last(pa_idxset *s, uint32_t *idx)
60
-{
61
-   uint32_t i = pa_array_get_len(&s->array, pa_idxset_item) - 1;
62
-   void *ptr = NULL;
63
-   pa_idxset_item *item = pa_idxset_reverse_search(s, &i);
64
-   if (idx)
65
-       *idx = i;
66
-   if (item) {
67
-       ptr = item->ptr;
68
-       item->ptr = NULL;
69
-       pa_array_remove(&s->array, item);
70
-   }
71
    return ptr;
72
 }
73
 
74
 static inline void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx)
75
 {
76
    pa_idxset_item *item = pa_idxset_find(s, p);
77
-   if (item == NULL) {
78
-       if (idx)
79
-           *idx = PA_IDXSET_INVALID;
80
+   if (item == NULL)
81
        return NULL;
82
-   }
83
    if (idx)
84
        *idx = item - (pa_idxset_item*)s->array.data;
85
    return item->ptr;
86
 }
87
 
88
-static inline bool pa_idxset_contains(pa_idxset *s, const void *p)
89
-{
90
-   return pa_idxset_get_by_data(s, p, NULL) == p;
91
-}
92
-
93
-static inline bool pa_idxset_isdisjoint(pa_idxset *s, pa_idxset *t)
94
-{
95
-   pa_idxset_item *item;
96
-   pa_array_for_each(item, &s->array) {
97
-       if (item->ptr && pa_idxset_contains(t, item->ptr))
98
-           return false;
99
-   }
100
-   return true;
101
-}
102
-
103
-static inline bool pa_idxset_issubset(pa_idxset *s, pa_idxset *t)
104
-{
105
-   pa_idxset_item *item;
106
-   pa_array_for_each(item, &s->array) {
107
-       if (item->ptr && !pa_idxset_contains(t, item->ptr))
108
-           return false;
109
-   }
110
-   return true;
111
-}
112
-
113
-static inline bool pa_idxset_issuperset(pa_idxset *s, pa_idxset *t)
114
-{
115
-    return pa_idxset_issubset(t, s);
116
-}
117
-
118
-static inline bool pa_idxset_equals(pa_idxset *s, pa_idxset *t)
119
-{
120
-    return pa_idxset_issubset(s, t) && pa_idxset_issuperset(s, t);
121
-}
122
-
123
 static inline void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx)
124
 {
125
         pa_idxset_item *item;
126
pipewire-0.3.68.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/alsa-pcm.c Changed
217
 
1
@@ -1567,10 +1567,13 @@
2
    is_batch = snd_pcm_hw_params_is_batch(params) &&
3
        !state->disable_batch;
4
 
5
+   /* no period size specified. If we are batch or not using timers,
6
+    * use the graph duration as the period */
7
+   if (period_size == 0 && (is_batch || state->disable_tsched))
8
+       period_size = state->position ? state->position->clock.duration : DEFAULT_PERIOD;
9
+
10
    if (is_batch) {
11
        if (period_size == 0)
12
-           period_size = state->position ? state->position->clock.duration : DEFAULT_PERIOD;
13
-       if (period_size == 0)
14
            period_size = DEFAULT_PERIOD;
15
        /* batch devices get their hw pointers updated every period. Make
16
         * the period smaller and add one period of headroom. Limit the
17
@@ -1616,10 +1619,18 @@
18
    }
19
 
20
    state->headroom = state->default_headroom;
21
-   /* If tsched is disabled, we know the pointers are updated when we wake
22
-    * up, so we don't need the additional headroom */
23
-   if (is_batch && !state->disable_tsched)
24
-       state->headroom += period_size;
25
+   if (!state->disable_tsched) {
26
+       /* When using timers, we might miss the pointer update for batch
27
+        * devices so add some extra headroom. With IRQ, we know the pointers
28
+        * are updated when we wake up and we don't need the headroom. */
29
+       if (is_batch)
30
+           state->headroom += period_size;
31
+       /* Add 32 extra samples of headroom to handle jitter in capture.
32
+        * For IRQ, we don't need this because when we wake up, we have
33
+        * exactly enough samples to read or write. */
34
+       if (state->stream == SND_PCM_STREAM_CAPTURE)
35
+           state->headroom = SPA_MAX(state->headroom, 32u);
36
+   }
37
 
38
    state->max_delay = state->buffer_frames / 2;
39
    if (spa_strstartswith(state->props.device, "a52") ||
40
@@ -1921,9 +1932,7 @@
41
        *delay = state->buffer_frames - avail;
42
    } else {
43
        *delay = avail;
44
-       *target = SPA_MAX(*target, state->read_size);
45
-       if (state->matching)
46
-           *target += 32;
47
+       *target = SPA_MAX(*target, state->read_size + state->headroom);
48
    }
49
    *target = SPA_CLAMP(*target, state->min_delay, state->max_delay);
50
    return 0;
51
@@ -1935,10 +1944,15 @@
52
    double err, corr;
53
    int32_t diff;
54
 
55
-   if (state->stream == SND_PCM_STREAM_PLAYBACK)
56
-       err = delay - target;
57
-   else
58
-       err = target - delay;
59
+   if (state->disable_tsched && !follower) {
60
+       err = (int64_t)(current_time - state->next_time);
61
+       err = err / 1e9 * state->rate;
62
+   } else {
63
+       if (state->stream == SND_PCM_STREAM_PLAYBACK)
64
+           err = delay - target;
65
+       else
66
+           err = target - delay;
67
+   }
68
 
69
    if (SPA_UNLIKELY(state->dll.bw == 0.0)) {
70
        spa_dll_set_bw(&state->dll, SPA_DLL_BW_MAX, state->threshold, state->rate);
71
@@ -1955,12 +1969,14 @@
72
        state->alsa_sync = true;
73
        state->alsa_sync_warning = false;
74
    }
75
-   if (err > state->max_error) {
76
-       err = state->max_error;
77
+   if (err > state->max_resync) {
78
        state->alsa_sync = true;
79
-   } else if (err < -state->max_error) {
80
-       err = -state->max_error;
81
+       if (err > state->max_error)
82
+           err = state->max_error;
83
+   } else if (err < -state->max_resync) {
84
        state->alsa_sync = true;
85
+       if (err < -state->max_error)
86
+           err = -state->max_error;
87
    }
88
 
89
    if (!follower || state->matching)
90
@@ -2002,8 +2018,8 @@
91
        state->clock->next_nsec = state->next_time;
92
    }
93
 
94
-   spa_log_trace_fp(state->log, "%p: follower:%d %"PRIu64" %f %ld %f %f %u",
95
-           state, follower, current_time, corr, delay, err, state->threshold * corr,
96
+   spa_log_trace_fp(state->log, "%p: follower:%d %"PRIu64" %f %ld %ld %f %f %u",
97
+           state, follower, current_time, corr, delay, target, err, state->threshold * corr,
98
            state->threshold);
99
 
100
    return 0;
101
@@ -2065,6 +2081,7 @@
102
            return -EIO;
103
        state->threshold = SPA_SCALE32_UP(state->duration, state->rate, state->rate_denom);
104
        state->max_error = SPA_MAX(256.0f, state->threshold / 2.0f);
105
+       state->max_resync = SPA_MIN(state->threshold, state->max_error);
106
        state->resample = ((uint32_t)state->rate != state->rate_denom) || state->matching;
107
        state->alsa_sync = true;
108
    }
109
@@ -2478,14 +2495,15 @@
110
    int res;
111
    struct spa_io_buffers *io;
112
 
113
-   if (SPA_UNLIKELY(delay < target)) {
114
-       spa_log_trace(state->log, "%p: early wakeup %ld %ld", state, delay, target);
115
+   if (SPA_UNLIKELY(delay < state->read_size)) {
116
+       spa_log_trace(state->log, "%p: early wakeup %ld %ld %d", state, delay, target,
117
+               state->read_size);
118
        state->next_time = current_time + (target - delay) * SPA_NSEC_PER_SEC /
119
            state->rate;
120
        return -EAGAIN;
121
    }
122
 
123
-   if (SPA_UNLIKELY(res = update_time(state, current_time, delay, target, false)) < 0)
124
+   if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, false)) < 0))
125
        return res;
126
 
127
    if ((res = spa_alsa_read(state)) < 0)
128
@@ -2514,6 +2532,14 @@
129
    return 0;
130
 }
131
 
132
+static uint64_t get_time_ns(struct state *state)
133
+{
134
+   struct timespec now;
135
+   if (spa_system_clock_gettime(state->data_system, CLOCK_MONOTONIC, &now) < 0)
136
+       return 0;
137
+   return SPA_TIMESPEC_TO_NSEC(&now);
138
+}
139
+
140
 static void alsa_wakeup_event(struct spa_source *source)
141
 {
142
    struct state *state = source->data;
143
@@ -2526,6 +2552,8 @@
144
        int err;
145
        unsigned short revents;
146
 
147
+       current_time = get_time_ns(state);
148
+
149
        for (int i = 0; i < state->n_fds; i++) {
150
            state->pfdsi.revents = state->sourcei.rmask;
151
            /* Reset so that we only handle all our sources' events once */
152
@@ -2543,17 +2571,20 @@
153
            spa_log_trace_fp(state->log, "Woken up with no work to do");
154
            return;
155
        }
156
-   } else if (SPA_LIKELY(state->started)) {
157
-       if (SPA_UNLIKELY((res = spa_system_timerfd_read(state->data_system,
158
+   } else {
159
+       if (SPA_LIKELY(state->started)) {
160
+           if (SPA_UNLIKELY((res = spa_system_timerfd_read(state->data_system,
161
                        state->timerfd, &expire)) < 0)) {
162
            /* we can get here when the timer is changed since the last
163
-            * timerfd wakeup, for example by do_reassign_follower() executed
164
-            * in the same epoll wakeup cycle */
165
-           if (res != -EAGAIN)
166
-               spa_log_warn(state->log, "%p: error reading timerfd: %s",
167
-                       state, spa_strerror(res));
168
-           return;
169
+                * timerfd wakeup, for example by do_reassign_follower() executed
170
+                * in the same epoll wakeup cycle */
171
+               if (res != -EAGAIN)
172
+                   spa_log_warn(state->log, "%p: error reading timerfd: %s",
173
+                           state, spa_strerror(res));
174
+               return;
175
+           }
176
        }
177
+       current_time = state->next_time;
178
    }
179
 
180
    if (SPA_UNLIKELY((res = check_position_config(state)) < 0)) {
181
@@ -2562,8 +2593,6 @@
182
        return;
183
    }
184
 
185
-   current_time = state->next_time;
186
-
187
    if (SPA_UNLIKELY(get_status(state, current_time, &delay, &target) < 0)) {
188
        spa_log_error(state->log, "get_status error");
189
        state->next_time += state->threshold * 1e9 / state->rate;
190
@@ -2572,11 +2601,7 @@
191
 
192
 #ifndef FASTPATH
193
    if (SPA_UNLIKELY(spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_TRACE))) {
194
-       struct timespec now;
195
-       uint64_t nsec;
196
-       if (spa_system_clock_gettime(state->data_system, CLOCK_MONOTONIC, &now) < 0)
197
-           return;
198
-       nsec = SPA_TIMESPEC_TO_NSEC(&now);
199
+       uint64_t nsec = get_time_ns(state);
200
        spa_log_trace_fp(state->log, "%p: wakeup %lu %lu %"PRIu64" %"PRIu64" %"PRIi64
201
                " %d %"PRIi64, state, delay, target, nsec, nsec,
202
                nsec - current_time, state->threshold, state->sample_count);
203
@@ -2636,12 +2661,7 @@
204
 
205
 static int setup_sources(struct state *state)
206
 {
207
-   struct timespec now;
208
-   int res;
209
-
210
-   if ((res = spa_system_clock_gettime(state->data_system, CLOCK_MONOTONIC, &now)) < 0)
211
-       return res;
212
-   state->next_time = SPA_TIMESPEC_TO_NSEC(&now);
213
+   state->next_time = get_time_ns(state);
214
 
215
    if (state->following) {
216
        /* Disable wakeups from this node */
217
pipewire-0.3.68.tar.gz/spa/plugins/alsa/alsa-pcm.h -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/alsa-pcm.h Changed
9
 
1
@@ -213,6 +213,7 @@
2
 
3
    struct spa_dll dll;
4
    double max_error;
5
+   double max_resync;
6
 
7
    struct spa_latency_info latency2;
8
    struct spa_process_latency_info process_latency;
9
pipewire-0.3.69.tar.gz/spa/plugins/alsa/mixer/paths/audigy-analog-output-mirror.conf Added
58
 
1
@@ -0,0 +1,56 @@
2
+; Mixer path for the Sound Blaster Audigy series, which uses the EMU10K2 DSP.
3
+; We target 'Wave' and other non-'PCM' controls as a special case for when
4
+; the device's stereo-to-all-speakers mirroring mode is in use.  (For example,
5
+; the Analog Stereo Output profile.)
6
+; https://docs.kernel.org/sound/cards/audigy-mixer.html
7
+;
8
+; See analog-output.conf.common for an explanation on the directives
9
+
10
+General
11
+priority = 99
12
+description-key = analog-output
13
+
14
+Element Master
15
+switch = mute
16
+volume = merge
17
+override-map.1 = all
18
+override-map.2 = all-left,all-right
19
+
20
+Element Wave
21
+volume = merge
22
+override-map.1 = all
23
+override-map.2 = all-left,all-right
24
+
25
+# The following elements also exist in analog-output.conf.  We list them here
26
+# instead of including that file, for ideal positioning of the Wave element:
27
+# Placing Wave below the Master element prevents Master from reaching its
28
+# loudest until the user raises the unified volume control to maximum.
29
+# (This should reduce the chance of a surprise speaker blow-out.)
30
+# Placing Wave above the per-channel elements yields even steps at low volume.
31
+
32
+Element Front
33
+volume = merge
34
+override-map.1 = all-front
35
+override-map.2 = front-left,front-right
36
+
37
+Element Surround
38
+volume = merge
39
+override-map.1 = all-rear
40
+override-map.2 = rear-left,rear-right
41
+
42
+Element Side
43
+volume = merge
44
+override-map.1 = all-side
45
+override-map.2 = side-left,side-right
46
+
47
+Element Center
48
+volume = merge
49
+override-map.1 = all-center
50
+override-map.2 = all-center,all-center
51
+
52
+Element LFE
53
+volume = merge
54
+override-map.1 = lfe
55
+override-map.2 = lfe,lfe
56
+
57
+.include analog-output.conf.common
58
pipewire-0.3.69.tar.gz/spa/plugins/alsa/mixer/paths/audigy-analog-output.conf Added
46
 
1
@@ -0,0 +1,44 @@
2
+; Mixer path for the Sound Blaster Audigy series, which uses the EMU10K2 DSP.
3
+; We target 'PCM Front' and similarly named controls instead of 'Front' et al.
4
+; because the latter affect volume only in the device's stereo-to-all-speakers
5
+; mirroring mode, which is not used by most profiles.
6
+; https://docs.kernel.org/sound/cards/audigy-mixer.html
7
+;
8
+; See analog-output.conf.common for an explanation on the directives
9
+
10
+General
11
+priority = 99
12
+description-key = analog-output
13
+
14
+Element Master
15
+switch = mute
16
+volume = merge
17
+override-map.1 = all
18
+override-map.2 = all-left,all-right
19
+
20
+Element PCM Front
21
+volume = merge
22
+override-map.1 = all-front
23
+override-map.2 = front-left,front-right
24
+
25
+Element PCM Surround
26
+volume = merge
27
+override-map.1 = all-rear
28
+override-map.2 = rear-left,rear-right
29
+
30
+Element PCM Side
31
+volume = merge
32
+override-map.1 = all-side
33
+override-map.2 = side-left,side-right
34
+
35
+Element PCM Center
36
+volume = merge
37
+override-map.1 = all-center
38
+override-map.2 = all-center,all-center
39
+
40
+Element PCM LFE
41
+volume = merge
42
+override-map.1 = lfe
43
+override-map.2 = lfe,lfe
44
+
45
+.include analog-output.conf.common
46
pipewire-0.3.68.tar.gz/spa/plugins/alsa/mixer/profile-sets/audigy.conf -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/mixer/profile-sets/audigy.conf Changed
85
 
1
@@ -15,10 +15,11 @@
2
 
3
 ; Creative Sound Blaster Audigy product line
4
 ;
5
-; These are just copies of the mappings we find in default.conf, with the
6
-; small change of making analog-stereo and analog-mono non-fallback mappings.
7
-; This is needed because these cards only support duplex profiles with mono
8
-; inputs, and in the default configuration, with stereo being a fallback
9
+; These are copies of the mappings we find in default.conf, but with analog
10
+; mixer paths targeting appropriate Audigy driver controls, and the small
11
+; change of making analog-stereo and analog-mono non-fallback mappings.
12
+; The latter is needed because these cards only support duplex profiles with
13
+; mono inputs, and in the default configuration, with stereo being a fallback
14
 ; mapping, the mono mapping is never tried.
15
 ;
16
 ; See default.conf for an explanation on the directives used here.
17
@@ -30,7 +31,7 @@
18
 Mapping analog-stereo
19
 device-strings = hw:%f
20
 channel-map = front-left,front-right
21
-paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2
22
+paths-output = audigy-analog-output-mirror analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2
23
 paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headphone-mic analog-input-headset-mic
24
 priority = 1
25
 
26
@@ -38,43 +39,42 @@
27
 Mapping analog-mono
28
 device-strings = hw:%f
29
 channel-map = mono
30
-paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2 analog-output-mono
31
+paths-output = audigy-analog-output-mirror analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2 analog-output-mono
32
 paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headset-mic
33
 priority = 1
34
 
35
-# The rest of these are identical to what's in default.conf
36
 Mapping analog-surround-21
37
 device-strings = surround21:%f
38
 channel-map = front-left,front-right,lfe
39
-paths-output = analog-output analog-output-lineout analog-output-speaker
40
+paths-output = audigy-analog-output analog-output-lineout analog-output-speaker
41
 priority = 13
42
 direction = output
43
 
44
 Mapping analog-surround-40
45
 device-strings = surround40:%f
46
 channel-map = front-left,front-right,rear-left,rear-right
47
-paths-output = analog-output analog-output-lineout analog-output-speaker
48
+paths-output = audigy-analog-output analog-output-lineout analog-output-speaker
49
 priority = 12
50
 direction = output
51
 
52
 Mapping analog-surround-41
53
 device-strings = surround41:%f
54
 channel-map = front-left,front-right,rear-left,rear-right,lfe
55
-paths-output = analog-output analog-output-lineout analog-output-speaker
56
+paths-output = audigy-analog-output analog-output-lineout analog-output-speaker
57
 priority = 13
58
 direction = output
59
 
60
 Mapping analog-surround-50
61
 device-strings = surround50:%f
62
 channel-map = front-left,front-right,rear-left,rear-right,front-center
63
-paths-output = analog-output analog-output-lineout analog-output-speaker
64
+paths-output = audigy-analog-output analog-output-lineout analog-output-speaker
65
 priority = 12
66
 direction = output
67
 
68
 Mapping analog-surround-51
69
 device-strings = surround51:%f
70
 channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
71
-paths-output = analog-output analog-output-lineout analog-output-speaker
72
+paths-output = audigy-analog-output analog-output-lineout analog-output-speaker
73
 priority = 13
74
 direction = output
75
 
76
@@ -82,7 +82,7 @@
77
 device-strings = surround71:%f
78
 channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
79
 description = Analog Surround 7.1
80
-paths-output = analog-output analog-output-lineout analog-output-speaker
81
+paths-output = audigy-analog-output analog-output-lineout analog-output-speaker
82
 priority = 12
83
 direction = output
84
 
85
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/backend-native.c -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/backend-native.c Changed
17
 
1
@@ -1062,6 +1062,7 @@
2
        rfcomm_send_reply(rfcomm, "OK");
3
    } else if (sscanf(buf, "AT+BIEV=%u,%u", &indicator, &indicator_value) == 2) {
4
        process_hfp_hf_indicator(rfcomm, indicator, indicator_value);
5
+       rfcomm_send_reply(rfcomm, "OK");
6
    } else if (sscanf(buf, "AT+XAPL=%04x-%04x-%*^,,%u", &xapl_vendor, &xapl_product, &xapl_features) == 3) {
7
        if (xapl_features & SPA_BT_HFP_HF_XAPL_FEATURE_BATTERY_REPORTING) {
8
            /* claim, that we support battery status reports */
9
@@ -1083,6 +1084,7 @@
10
            process_iphoneaccev_indicator(rfcomm, key, value);
11
            buf += r;
12
        }
13
+       rfcomm_send_reply(rfcomm, "OK");
14
    } else if (spa_strstartswith(buf, "AT+APLSIRI?")) {
15
        // This command is sent when we activate Apple extensions
16
        rfcomm_send_reply(rfcomm, "OK");
17
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/bap-codec-caps.h -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/bap-codec-caps.h Changed
29
 
1
@@ -83,14 +83,6 @@
2
 
3
 #define LC3_MAX_CHANNELS 28
4
 
5
-typedef struct {
6
-    uint8_t rate;
7
-   uint8_t frame_duration;
8
-   uint32_t channels;
9
-   uint16_t framelen;
10
-   uint8_t n_blks;
11
-} __attribute__ ((packed)) bap_lc3_t;
12
-
13
 #define BT_ISO_QOS_CIG_UNSET    0xff
14
 #define BT_ISO_QOS_CIS_UNSET    0xff
15
 
16
@@ -98,6 +90,12 @@
17
 #define BT_ISO_QOS_TARGET_LATENCY_BALANCED 0x02
18
 #define BT_ISO_QOS_TARGET_LATENCY_RELIABILITY  0x03
19
 
20
+struct __attribute__((packed)) ltv {
21
+   uint8_t  len;
22
+   uint8_t  type;
23
+   uint8_t  value;
24
+};
25
+
26
 struct bap_endpoint_qos {
27
         uint8_t  framing;
28
         uint8_t  phy;
29
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/bap-codec-lc3.c -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/bap-codec-lc3.c Changed
319
 
1
@@ -34,17 +34,19 @@
2
    unsigned int codesize;
3
 };
4
 
5
-struct __attribute__((packed)) ltv {
6
-   uint8_t  len;
7
-   uint8_t  type;
8
-   uint8_t  value;
9
-};
10
-
11
 struct pac_data {
12
    const uint8_t *data;
13
    size_t size;
14
 };
15
 
16
+typedef struct {
17
+   uint8_t rate;
18
+   uint8_t frame_duration;
19
+   uint32_t channels;
20
+   uint16_t framelen;
21
+   uint8_t n_blks;
22
+} bap_lc3_t;
23
+
24
 static int write_ltv(uint8_t *dest, uint8_t type, void* value, size_t len)
25
 {
26
    struct ltv *ltv = (struct ltv *)dest;
27
@@ -82,6 +84,7 @@
28
    data += write_ltv_uint8(data, LC3_TYPE_DUR, LC3_DUR_ANY);
29
    data += write_ltv_uint8(data, LC3_TYPE_CHAN, LC3_CHAN_1 | LC3_CHAN_2);
30
    data += write_ltv(data, LC3_TYPE_FRAMELEN, framelen, sizeof(framelen));
31
+   /* XXX: we support only one frame block -> max 2 frames per SDU */
32
    data += write_ltv_uint8(data, LC3_TYPE_BLKS, 2);
33
 
34
    return data - caps;
35
@@ -119,9 +122,26 @@
36
    return pac + 1;
37
 }
38
 
39
-static bool parse_capabilities(bap_lc3_t *conf, const uint8_t *data, size_t data_size)
40
+static uint8_t get_num_channels(uint32_t channels)
41
+{
42
+   uint8_t num;
43
+
44
+   if (channels == 0)
45
+       return 1;  /* MONO */
46
+
47
+   for (num = 0; channels; channels >>= 1)
48
+       if (channels & 0x1)
49
+           ++num;
50
+
51
+   return num;
52
+}
53
+
54
+static bool select_config(bap_lc3_t *conf, const struct pac_data *pac)
55
 {
56
+   const uint8_t *data = pac->data;
57
+   size_t data_size = pac->size;
58
    uint16_t framelen_min = 0, framelen_max = 0;
59
+   int max_frames = -1;
60
 
61
    if (!data_size)
62
        return false;
63
@@ -129,6 +149,9 @@
64
 
65
    conf->frame_duration = 0xFF;
66
 
67
+   /* XXX: we always use one frame block */
68
+   conf->n_blks = 1;
69
+
70
    while (data_size > 0) {
71
        struct ltv *ltv = (struct ltv *)data;
72
 
73
@@ -168,12 +191,11 @@
74
            spa_return_val_if_fail(ltv->len == 2, false);
75
            {
76
                uint8_t channels = ltv->value0;
77
-               /* Only mono or stereo streams are currently supported,
78
-                * in both case Audio location is defined as both Front Left
79
-                * and Front Right, difference is done by the n_blks parameter.
80
-                */
81
-               if ((channels & LC3_CHAN_2) || (channels & LC3_CHAN_1))
82
+               /* XXX: we hardcode mono or stereo stream */
83
+               if (channels & LC3_CHAN_2)
84
                    conf->channels = LC3_CONFIG_CHNL_FR | LC3_CONFIG_CHNL_FL;
85
+               else if (channels & LC3_CHAN_1)
86
+                   conf->channels = 0;   /* mono (omit Audio_Channel_Allocation) */
87
                else
88
                    return false;
89
            }
90
@@ -185,9 +207,7 @@
91
            break;
92
        case LC3_TYPE_BLKS:
93
            spa_return_val_if_fail(ltv->len == 2, false);
94
-           conf->n_blks = ltv->value0;
95
-           if (!conf->n_blks)
96
-               return false;
97
+           max_frames = ltv->value0;
98
            break;
99
        default:
100
            return false;
101
@@ -196,37 +216,42 @@
102
        data += ltv->len + 1;
103
    }
104
 
105
+   /* Default: 1 per channel (BAP v1.0.1 Sec 4.3.1) */
106
+   if (max_frames < 0)
107
+       max_frames = get_num_channels(conf->channels);
108
+   if (max_frames < get_num_channels(conf->channels))
109
+       return false;
110
+
111
    if (framelen_min < LC3_MIN_FRAME_BYTES || framelen_max > LC3_MAX_FRAME_BYTES)
112
        return false;
113
    if (conf->frame_duration == 0xFF || !conf->rate)
114
        return false;
115
-   if (!conf->channels)
116
-       conf->channels = LC3_CONFIG_CHNL_FL;
117
 
118
+   /* BAP v1.0.1 Table 5.2; high-reliability */
119
    switch (conf->rate) {
120
    case LC3_CONFIG_FREQ_48KHZ:
121
        if (conf->frame_duration == LC3_CONFIG_DURATION_7_5)
122
-           conf->framelen = 117;
123
+           conf->framelen = 117;   /* 48_5_2 */
124
        else
125
-           conf->framelen = 120;
126
+           conf->framelen = 120;   /* 48_4_2 */
127
        break;
128
    case LC3_CONFIG_FREQ_24KHZ:
129
        if (conf->frame_duration == LC3_CONFIG_DURATION_7_5)
130
-           conf->framelen = 45;
131
+           conf->framelen = 45;    /* 24_1_2 */
132
        else
133
-           conf->framelen = 60;
134
+           conf->framelen = 60;    /* 24_2_2 */
135
        break;
136
    case LC3_CONFIG_FREQ_16KHZ:
137
        if (conf->frame_duration == LC3_CONFIG_DURATION_7_5)
138
-           conf->framelen = 30;
139
+           conf->framelen = 30;    /* 16_1_2 */
140
        else
141
-           conf->framelen = 40;
142
+           conf->framelen = 40;    /* 16_2_2 */
143
        break;
144
    case LC3_CONFIG_FREQ_8KHZ:
145
        if (conf->frame_duration == LC3_CONFIG_DURATION_7_5)
146
-           conf->framelen = 26;
147
+           conf->framelen = 26;    /* 8_1_2 */
148
        else
149
-           conf->framelen = 30;
150
+           conf->framelen = 30;    /* 8_2_2 */
151
        break;
152
    default:
153
            return false;
154
@@ -243,6 +268,9 @@
155
 
156
    conf->frame_duration = 0xFF;
157
 
158
+   /* Absent Codec_Frame_Blocks_Per_SDU means 0x1 (BAP v1.0.1 Sec 4.3.2) */
159
+   conf->n_blks = 1;
160
+
161
    while (data_size > 0) {
162
        struct ltv *ltv = (struct ltv *)data;
163
 
164
@@ -269,7 +297,8 @@
165
        case LC3_TYPE_BLKS:
166
            spa_return_val_if_fail(ltv->len == 2, false);
167
            conf->n_blks = ltv->value0;
168
-           if (!conf->n_blks)
169
+           /* XXX: we only support 1 frame block for now */
170
+           if (conf->n_blks != 1)
171
                return false;
172
            break;
173
        default:
174
@@ -325,8 +354,8 @@
175
    bap_lc3_t conf1, conf2;
176
    int res1, res2;
177
 
178
-   res1 = parse_capabilities(&conf1, pac1->data, pac1->size) ? (int)sizeof(bap_lc3_t) : -EINVAL;
179
-   res2 = parse_capabilities(&conf2, pac2->data, pac2->size) ? (int)sizeof(bap_lc3_t) : -EINVAL;
180
+   res1 = select_config(&conf1, pac1) ? (int)sizeof(bap_lc3_t) : -EINVAL;
181
+   res2 = select_config(&conf2, pac2) ? (int)sizeof(bap_lc3_t) : -EINVAL;
182
 
183
    return conf_cmp(&conf1, res1, &conf2, res2);
184
 }
185
@@ -353,12 +382,16 @@
186
 
187
    qsort(pacs, npacs, sizeof(struct pac_data), pac_cmp);
188
 
189
-   if (!parse_capabilities(&conf, pacs0.data, pacs0.size))
190
+   if (!select_config(&conf, &pacs0))
191
        return -ENOTSUP;
192
 
193
    data += write_ltv_uint8(data, LC3_TYPE_FREQ, conf.rate);
194
    data += write_ltv_uint8(data, LC3_TYPE_DUR, conf.frame_duration);
195
-   data += write_ltv_uint32(data, LC3_TYPE_CHAN, htobl(conf.channels));
196
+
197
+   /* Indicate MONO with absent Audio_Channel_Allocation (BAP v1.0.1 Sec. 4.3.2) */
198
+   if (conf.channels != 0)
199
+       data += write_ltv_uint32(data, LC3_TYPE_CHAN, htobl(conf.channels));
200
+
201
    data += write_ltv_uint16(data, LC3_TYPE_FRAMELEN, htobs(conf.framelen));
202
    data += write_ltv_uint8(data, LC3_TYPE_BLKS, conf.n_blks);
203
 
204
@@ -378,19 +411,14 @@
205
    return conf_cmp(&conf1, res1, &conf2, res2);
206
 }
207
 
208
-static uint8_t channels_to_positions(uint32_t channels, uint8_t n_channels, uint32_t *position)
209
+static uint8_t channels_to_positions(uint32_t channels, uint32_t *position)
210
 {
211
+   uint8_t n_channels = get_num_channels(channels);
212
    uint8_t n_positions = 0;
213
 
214
    spa_assert(n_channels <= SPA_AUDIO_MAX_CHANNELS);
215
 
216
-   /* First check if stream is configure for Mono, i.e. 1 block for both Front
217
-    * Left anf Front Right,
218
-    * else map LE Audio locations to PipeWire locations in the ascending order
219
-    * which will be used as block order in stream.
220
-    */
221
-   if ((channels & (LC3_CONFIG_CHNL_FR | LC3_CONFIG_CHNL_FL)) == (LC3_CONFIG_CHNL_FR | LC3_CONFIG_CHNL_FL) &&
222
-        n_channels == 1) {
223
+   if (channels == 0) {
224
        position0 = SPA_AUDIO_CHANNEL_MONO;
225
        n_positions = 1;
226
    } else {
227
@@ -428,6 +456,9 @@
228
 #undef CHANNEL_2_SPACHANNEL
229
    }
230
 
231
+   if (n_positions != n_channels)
232
+       return 0;  /* error */
233
+
234
    return n_positions;
235
 }
236
 
237
@@ -485,7 +516,7 @@
238
        choice->body.type = SPA_CHOICE_Enum;
239
    spa_pod_builder_pop(b, &f1);
240
 
241
-   res = channels_to_positions(conf.channels, conf.n_blks, position);
242
+   res = channels_to_positions(conf.channels, position);
243
    if (res == 0)
244
        return -EINVAL;
245
    spa_pod_builder_add(b,
246
@@ -533,7 +564,7 @@
247
        return -EINVAL;
248
    }
249
 
250
-   res = channels_to_positions(conf.channels, conf.n_blks, info->info.raw.position);
251
+   res = channels_to_positions(conf.channels, info->info.raw.position);
252
    if (res == 0)
253
        return -EINVAL;
254
    info->info.raw.channels = res;
255
@@ -568,32 +599,35 @@
256
        qos->phy = 0x1;
257
    else
258
        qos->phy = 0x2;
259
-   qos->retransmission = 2; /* default */
260
-   qos->sdu = conf.framelen * conf.n_blks;
261
-   qos->latency = 20; /* default */
262
-   qos->delay = 40000U;
263
+   qos->sdu = conf.framelen * conf.n_blks * get_num_channels(conf.channels);
264
    qos->interval = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 7500 : 10000);
265
-   qos->target_latency = BT_ISO_QOS_TARGET_LATENCY_BALANCED;
266
+   qos->target_latency = BT_ISO_QOS_TARGET_LATENCY_RELIABILITY;
267
+
268
+   /* Default values from BAP v1.0.1 Table 5.2; high-reliability */
269
+   qos->delay = 40000U;
270
+   qos->retransmission = 13;
271
 
272
    switch (conf.rate) {
273
-       case LC3_CONFIG_FREQ_8KHZ:
274
-       case LC3_CONFIG_FREQ_16KHZ:
275
-       case LC3_CONFIG_FREQ_24KHZ:
276
-       case LC3_CONFIG_FREQ_32KHZ:
277
-           qos->retransmission = 2;
278
-           qos->latency = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 8 : 10);
279
-           break;
280
-       case LC3_CONFIG_FREQ_48KHZ:
281
-           qos->retransmission = 5;
282
-           qos->latency = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 15 : 20);
283
-           break;
284
+   case LC3_CONFIG_FREQ_8KHZ:
285
+   case LC3_CONFIG_FREQ_16KHZ:
286
+   case LC3_CONFIG_FREQ_24KHZ:
287
+   case LC3_CONFIG_FREQ_32KHZ:
288
+       /* F_1_2, F_2_2 */
289
+       qos->latency = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 75 : 95);
290
+       break;
291
+   case LC3_CONFIG_FREQ_48KHZ:
292
+       /* 48_5_2, 48_4_2 */
293
+       qos->latency = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 75 : 100);
294
+       break;
295
+   default:
296
+       qos->latency = 100;
297
+       break;
298
    }
299
 
300
-   /* Clamp to ASE values */
301
+   /* Clamp to ASE values (if known) */
302
    if (endpoint_qos->latency >= 0x0005 && endpoint_qos->latency <= 0x0FA0)
303
        /* Values outside the range are RFU */
304
-       qos->latency = SPA_MAX(qos->latency, endpoint_qos->latency);
305
-
306
+       qos->latency = endpoint_qos->latency;
307
    if (endpoint_qos->delay_min)
308
        qos->delay = SPA_MAX(qos->delay, endpoint_qos->delay_min);
309
    if (endpoint_qos->delay_max)
310
@@ -651,7 +685,7 @@
311
        res = -EINVAL;
312
        goto error;
313
    }
314
-   this->codesize = this->samples * this->channels * sizeof(int32_t);
315
+   this->codesize = this->samples * this->channels * conf.n_blks * sizeof(int32_t);
316
 
317
    if (!(flags & MEDIA_CODEC_FLAG_SINK)) {
318
        for (ich = 0; ich < this->channels; ich++) {
319
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/bluez5-dbus.c Changed
573
 
1
@@ -10,6 +10,7 @@
2
 #include <sys/stat.h>
3
 #include <sys/socket.h>
4
 #include <fcntl.h>
5
+#include <limits.h>
6
 
7
 #include <bluetooth/bluetooth.h>
8
 
9
@@ -38,6 +39,8 @@
10
 #include "iso-io.h"
11
 #include "defs.h"
12
 
13
+#include "bap-codec-caps.h"
14
+
15
 static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5");
16
 #undef SPA_LOG_TOPIC_DEFAULT
17
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
18
@@ -1266,6 +1269,7 @@
19
    spa_list_init(&d->remote_endpoint_list);
20
    spa_list_init(&d->transport_list);
21
    spa_list_init(&d->codec_switch_list);
22
+   spa_list_init(&d->set_membership_list);
23
 
24
    spa_hook_list_init(&d->listener_list);
25
 
26
@@ -1292,6 +1296,7 @@
27
    struct spa_bt_media_codec_switch *sw;
28
    struct spa_bt_transport *t, *tt;
29
    struct spa_bt_monitor *monitor = device->monitor;
30
+   struct spa_bt_set_membership *s;
31
 
32
    spa_log_debug(monitor->log, "%p", device);
33
 
34
@@ -1321,6 +1326,13 @@
35
    spa_list_consume(sw, &device->codec_switch_list, device_link)
36
        media_codec_switch_free(sw);
37
 
38
+   spa_list_consume(s, &device->set_membership_list, link) {
39
+       spa_list_remove(&s->link);
40
+       spa_list_remove(&s->others);
41
+       free(s->path);
42
+       free(s);
43
+   }
44
+
45
    spa_list_remove(&device->link);
46
    free(device->path);
47
    free(device->alias);
48
@@ -1332,6 +1344,84 @@
49
    free(device);
50
 }
51
 
52
+static struct spa_bt_set_membership *device_set_find(struct spa_bt_monitor *monitor, const char *path)
53
+{
54
+   struct spa_bt_device *d;
55
+
56
+   spa_list_for_each(d, &monitor->device_list, link) {
57
+       struct spa_bt_set_membership *s;
58
+
59
+       spa_list_for_each(s, &d->set_membership_list, link) {
60
+           if (spa_streq(s->path, path))
61
+               return s;
62
+       }
63
+   }
64
+
65
+   return NULL;
66
+}
67
+
68
+static int device_add_device_set(struct spa_bt_device *device, const char *path, uint8_t rank)
69
+{
70
+   struct spa_bt_monitor *monitor = device->monitor;
71
+   struct spa_bt_set_membership *s, *set;
72
+
73
+   spa_list_for_each(s, &device->set_membership_list, link) {
74
+       if (spa_streq(s->path, path)) {
75
+           if (rank)
76
+               s->rank = rank;
77
+           return 0;
78
+       }
79
+   }
80
+
81
+   s = calloc(1, sizeof(struct spa_bt_set_membership));
82
+   if (s == NULL)
83
+       return -ENOMEM;
84
+
85
+   s->path = strdup(path);
86
+   if (!s->path) {
87
+       free(s);
88
+       return -ENOMEM;
89
+   }
90
+
91
+   s->device = device;
92
+   s->rank = rank;
93
+
94
+   spa_list_init(&s->others);
95
+
96
+   /* Join with other set members, if any */
97
+   set = device_set_find(monitor, path);
98
+   if (set)
99
+       spa_list_append(&set->others, &s->others);
100
+
101
+   spa_list_append(&device->set_membership_list, &s->link);
102
+
103
+   spa_log_debug(monitor->log, "device %p: add %s to device set %s", device,
104
+           device->path, path);
105
+
106
+   return 1;
107
+}
108
+
109
+static bool device_remove_device_set(struct spa_bt_device *device, const char *path)
110
+{
111
+   struct spa_bt_monitor *monitor = device->monitor;
112
+   struct spa_bt_set_membership *s;
113
+
114
+   spa_list_for_each(s, &device->set_membership_list, link) {
115
+       if (spa_streq(s->path, path)) {
116
+           spa_log_debug(monitor->log,
117
+                   "device %p: remove %s from device set %s", device,
118
+                   device->path, path);
119
+           spa_list_remove(&s->link);
120
+           spa_list_remove(&s->others);
121
+           free(s->path);
122
+           free(s);
123
+           return true;
124
+       }
125
+   }
126
+
127
+   return false;
128
+}
129
+
130
 int spa_bt_format_vendor_product_id(uint16_t source_id, uint16_t vendor_id, uint16_t product_id,
131
        char *vendor_str, int vendor_str_size, char *product_str, int product_str_size)
132
 {
133
@@ -1668,6 +1758,7 @@
134
 int spa_bt_device_check_profiles(struct spa_bt_device *device, bool force)
135
 {
136
    struct spa_bt_monitor *monitor = device->monitor;
137
+   struct spa_bt_set_membership *s, *set;
138
    uint32_t connected_profiles = device->connected_profiles;
139
    uint32_t connectable_profiles =
140
        device->adapter ? adapter_connectable_profiles(device->adapter) : 0;
141
@@ -1677,6 +1768,7 @@
142
        SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY,
143
    };
144
    bool direction_connected = false;
145
+   bool set_connected = true;
146
    bool all_connected;
147
    size_t i;
148
 
149
@@ -1693,14 +1785,19 @@
150
 
151
    all_connected = (device->profiles & connected_profiles) == device->profiles;
152
 
153
-   spa_log_debug(monitor->log, "device %p: profiles %08x %08x connectable:%08x added:%d all:%d dir:%d",
154
+   spa_list_for_each(set, &device->set_membership_list, link)
155
+       spa_bt_for_each_set_member(s, set)
156
+           if ((s->device->connected_profiles & s->device->profiles) != s->device->profiles)
157
+               set_connected = false;
158
+
159
+   spa_log_debug(monitor->log, "device %p: profiles %08x %08x connectable:%08x added:%d all:%d dir:%d set:%d",
160
            device, device->profiles, connected_profiles, connectable_profiles,
161
-           device->added, all_connected, direction_connected);
162
+           device->added, all_connected, direction_connected, set_connected);
163
 
164
    if (connected_profiles == 0 && spa_list_is_empty(&device->codec_switch_list)) {
165
        device_stop_timer(device);
166
        device_connected(monitor, device, BT_DEVICE_DISCONNECTED);
167
-   } else if (force || direction_connected || all_connected) {
168
+   } else if (force || ((direction_connected || all_connected) && set_connected)) {
169
        device_stop_timer(device);
170
        device_connected(monitor, device, BT_DEVICE_CONNECTED);
171
    } else {
172
@@ -1734,10 +1831,14 @@
173
    }
174
 }
175
 
176
+static void device_update_set_status(struct spa_bt_device *device, bool force, const char *path);
177
+
178
 int spa_bt_device_connect_profile(struct spa_bt_device *device, enum spa_bt_profile profile)
179
 {
180
    uint32_t prev_connected = device->connected_profiles;
181
    device->connected_profiles |= profile;
182
+   if ((prev_connected ^ device->connected_profiles) & SPA_BT_PROFILE_BAP_DUPLEX)
183
+       device_update_set_status(device, true, NULL);
184
    spa_bt_device_check_profiles(device, false);
185
    if (device->connected_profiles != prev_connected)
186
        spa_bt_device_emit_profiles_changed(device, device->profiles, prev_connected);
187
@@ -1761,6 +1862,214 @@
188
    spa_log_debug(monitor->log, "hw-volume-profiles:%08x", (int)device->hw_volume_profiles);
189
 }
190
 
191
+static bool device_set_update_leader(struct spa_bt_set_membership *set)
192
+{
193
+   struct spa_bt_set_membership *s, *leader;
194
+   int min_rank = INT_MAX;
195
+   int leader_rank = INT_MAX;
196
+
197
+   leader = NULL;
198
+
199
+   spa_bt_for_each_set_member(s, set) {
200
+       if (!(s->device->connected_profiles & SPA_BT_PROFILE_BAP_DUPLEX))
201
+           continue;
202
+       min_rank = SPA_MIN(min_rank, s->rank);
203
+       if (s->leader) {
204
+           leader_rank = s->rank;
205
+           leader = s;
206
+       }
207
+   }
208
+
209
+   if (min_rank >= leader_rank && leader)
210
+       return false;
211
+
212
+   spa_bt_for_each_set_member(s, set) {
213
+       if (leader == NULL && s->rank == min_rank &&
214
+               (s->device->connected_profiles & SPA_BT_PROFILE_BAP_DUPLEX)) {
215
+           s->leader = true;
216
+           leader = s;
217
+       } else {
218
+           s->leader = false;
219
+       }
220
+   }
221
+
222
+   if (leader) {
223
+       struct spa_bt_monitor *monitor = leader->device->monitor;
224
+
225
+       spa_log_debug(monitor->log, "device set %s: leader is %s",
226
+               leader->path, leader->device->path);
227
+   }
228
+
229
+   return true;
230
+}
231
+
232
+static void device_update_set_status(struct spa_bt_device *device, bool force, const char *path)
233
+{
234
+   struct spa_bt_set_membership *s, *set;
235
+
236
+   spa_list_for_each(set, &device->set_membership_list, link) {
237
+       if (path && !spa_streq(set->path, path))
238
+           continue;
239
+
240
+       if (device_set_update_leader(set) || force) {
241
+           spa_bt_for_each_set_member(s, set)
242
+               if (!s->leader)
243
+                   spa_bt_device_emit_device_set_changed(s->device);
244
+           spa_bt_for_each_set_member(s, set)
245
+               if (s->leader)
246
+                   spa_bt_device_emit_device_set_changed(s->device);
247
+       }
248
+   }
249
+}
250
+
251
+static int device_set_update_props(struct spa_bt_monitor *monitor,
252
+       const char *path, DBusMessageIter *props_iter, DBusMessageIter *invalidated_iter)
253
+{
254
+   struct spa_bt_device *old256;
255
+   struct spa_bt_device *new256;
256
+   struct spa_bt_set_membership *set;
257
+   size_t num_old = 0, num_new = 0;
258
+   size_t i;
259
+
260
+   if (!props_iter)
261
+       goto done;
262
+
263
+   /* Find current devices */
264
+   while (dbus_message_iter_get_arg_type(props_iter) != DBUS_TYPE_INVALID) {
265
+       DBusMessageIter it2;
266
+       const char *key;
267
+
268
+       dbus_message_iter_recurse(props_iter, &it0);
269
+       dbus_message_iter_get_basic(&it0, &key);
270
+       dbus_message_iter_next(&it0);
271
+       dbus_message_iter_recurse(&it0, &it1);
272
+
273
+       if (spa_streq(key, "Devices")) {
274
+           DBusMessageIter iter;
275
+           int i = 0;
276
+
277
+           if (!check_iter_signature(&it1, "ao"))
278
+               goto next;
279
+
280
+           dbus_message_iter_recurse(&it1, &iter);
281
+
282
+           while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) {
283
+               struct spa_bt_device *d;
284
+               const char *dev_path;
285
+
286
+               dbus_message_iter_get_basic(&iter, &dev_path);
287
+
288
+               spa_log_debug(monitor->log, "device set %s: Devices%d=%s",
289
+                       path, i++, dev_path);
290
+
291
+               if (num_new >= SPA_N_ELEMENTS(new))
292
+                   break;
293
+               d = spa_bt_device_find(monitor, dev_path);
294
+               if (d)
295
+                   newnum_new++ = d;
296
+
297
+               dbus_message_iter_next(&iter);
298
+           }
299
+
300
+       }
301
+       else
302
+           spa_log_debug(monitor->log, "device set %s: unhandled key %s",
303
+                   path, key);
304
+
305
+next:
306
+       dbus_message_iter_next(props_iter);
307
+   }
308
+
309
+done:
310
+   /* Find devices to remove */
311
+   set = device_set_find(monitor, path);
312
+   if (set) {
313
+       struct spa_bt_set_membership *s;
314
+
315
+       spa_bt_for_each_set_member(s, set) {
316
+           for (i = 0; i < num_new; ++i)
317
+               if (s->device == newi)
318
+                   break;
319
+           if (i == num_new) {
320
+               if (num_old >= SPA_N_ELEMENTS(old))
321
+                   break;
322
+               oldnum_old++ = s->device;
323
+           }
324
+       }
325
+   }
326
+
327
+   /* Remove old devices */
328
+   for (i = 0; i < num_old; ++i)
329
+       device_remove_device_set(oldi, path);
330
+
331
+   /* Add new devices */
332
+   for (i = 0; i < num_new; ++i)
333
+       device_add_device_set(newi, path, 0);
334
+
335
+   /* Emit signals & update set leader */
336
+   for (i = 0; i < num_old; ++i)
337
+       spa_bt_device_emit_device_set_changed(oldi);
338
+
339
+   if (num_new > 0)
340
+       device_update_set_status(new0, true, path);
341
+
342
+   return 0;
343
+}
344
+
345
+static int device_update_device_sets_prop(struct spa_bt_device *device,
346
+       DBusMessageIter *iter)
347
+{
348
+   struct spa_bt_monitor *monitor = device->monitor;
349
+   DBusMessageIter it5;
350
+   bool changed = false;
351
+
352
+   if (!check_iter_signature(iter, "a{oa{sv}}"))
353
+       return -EINVAL;
354
+
355
+   dbus_message_iter_recurse(iter, &it0);
356
+
357
+   while (dbus_message_iter_get_arg_type(&it0) != DBUS_TYPE_INVALID) {
358
+       uint8_t rank = 0;
359
+       const char *set_path;
360
+
361
+       dbus_message_iter_recurse(&it0, &it1);
362
+       dbus_message_iter_get_basic(&it1, &set_path);
363
+       dbus_message_iter_next(&it1);
364
+       dbus_message_iter_recurse(&it1, &it2);
365
+
366
+       while (dbus_message_iter_get_arg_type(&it2) != DBUS_TYPE_INVALID) {
367
+           const char *key;
368
+           int type;
369
+
370
+           dbus_message_iter_recurse(&it2, &it3);
371
+           dbus_message_iter_get_basic(&it3, &key);
372
+           dbus_message_iter_next(&it3);
373
+           dbus_message_iter_recurse(&it3, &it4);
374
+
375
+           type = dbus_message_iter_get_arg_type(&it4);
376
+
377
+           if (spa_streq(key, "Rank") && type == DBUS_TYPE_BYTE)
378
+               dbus_message_iter_get_basic(&it4, &rank);
379
+
380
+           dbus_message_iter_next(&it2);
381
+       }
382
+
383
+       spa_log_debug(monitor->log, "device %p: path %s device set %s rank %d",
384
+               device, device->path, set_path, (int)rank);
385
+
386
+       /* Only add. Removals are handled in device set updates. */
387
+       if (device_add_device_set(device, set_path, rank) == 1)
388
+           changed = true;
389
+
390
+       dbus_message_iter_next(&it0);
391
+   }
392
+
393
+   /* Emit change signals */
394
+   device_update_set_status(device, changed, NULL);
395
+
396
+   return 0;
397
+}
398
+
399
 static int device_update_props(struct spa_bt_device *device,
400
                   DBusMessageIter *props_iter,
401
                   DBusMessageIter *invalidated_iter)
402
@@ -1909,6 +2218,9 @@
403
                spa_bt_device_emit_profiles_changed(
404
                    device, prev_profiles, device->connected_profiles);
405
        }
406
+       else if (spa_streq(key, "Sets")) {
407
+           device_update_device_sets_prop(device, &it1);
408
+       }
409
        else
410
            spa_log_debug(monitor->log, "device %p: unhandled key %s type %d", device, key, type);
411
 
412
@@ -2332,8 +2644,11 @@
413
        spa_list_remove(&transport->device_link);
414
    }
415
 
416
-   if (device && device->connected_profiles != prev_connected)
417
+   if (device && device->connected_profiles != prev_connected) {
418
+       if ((prev_connected ^ device->connected_profiles) & SPA_BT_PROFILE_BAP_DUPLEX)
419
+           device_update_set_status(device, true, NULL);
420
        spa_bt_device_emit_profiles_changed(device, device->profiles, prev_connected);
421
+   }
422
 
423
    spa_list_remove(&transport->bap_transport_linked);
424
 
425
@@ -2836,6 +3151,7 @@
426
            dbus_message_iter_get_basic(&it1, &value);
427
 
428
            spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value);
429
+           transport->bap_interval = value;
430
        }
431
        else if (spa_streq(key, "Framing")) {
432
            dbus_bool_t value;
433
@@ -2878,6 +3194,16 @@
434
            else
435
                transport->bap_cis = value;
436
        }
437
+       else if (spa_streq(key, "Location")) {
438
+           uint32_t value;
439
+
440
+           if (type != DBUS_TYPE_UINT32)
441
+               goto next;
442
+           dbus_message_iter_get_basic(&it1, &value);
443
+
444
+           spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value);
445
+           transport->bap_location = value;
446
+       }
447
 next:
448
        dbus_message_iter_next(props_iter);
449
    }
450
@@ -2992,6 +3318,7 @@
451
 
452
    spa_log_debug(monitor->log, "transport %p: new ISO IO", transport);
453
    transport->iso_io = spa_bt_iso_io_create(transport->fd, sink,
454
+           transport->bap_cig, transport->bap_interval,
455
            monitor->log, monitor->data_loop, monitor->data_system);
456
    if (transport->iso_io == NULL)
457
        return -errno;
458
@@ -3845,6 +4172,64 @@
459
    return spa_bt_backend_supports_codec(monitor->backend, device, codec);
460
 }
461
 
462
+static void bap_update_codec_location(struct spa_bt_transport *t)
463
+{
464
+   uint8_t *data = t->configuration;
465
+   size_t size = t->configuration_len;
466
+   struct ltv *ltv;
467
+   uint32_t location;
468
+   int i;
469
+
470
+   if (!t->bap_location)
471
+       return;
472
+
473
+   /*
474
+    * Append channel location from BAP transport location, if no channel
475
+    * configuration is present in the configuration.
476
+    *
477
+    * XXX: The codec select_configuration should set the location
478
+    * XXX: for mono channels from the device location. We have to do
479
+    * XXX: this here because transport location is not know
480
+    * XXX: in SelectProperties (TODO: should be fixed in bluez).
481
+    */
482
+
483
+   while (size > 0) {
484
+       ltv = (struct ltv *)data;
485
+
486
+       if (ltv->len < sizeof(struct ltv) || ltv->len >= size)
487
+           return;
488
+
489
+       if (ltv->type == LC3_TYPE_CHAN)
490
+           return;  /* already has the channel info */
491
+
492
+       size -= ltv->len + 1;
493
+       data += ltv->len + 1;
494
+   }
495
+
496
+   /* Pick the first location bit set */
497
+   location = t->bap_location;
498
+   for (i = 0; i < 32; ++i) {
499
+       if (location & (1 << i)) {
500
+           location = (1 << i);
501
+           break;
502
+       }
503
+   }
504
+
505
+   /* Append LTV value to transport configuration */
506
+   size = t->configuration_len + sizeof(struct ltv) + sizeof(uint32_t);
507
+   data = realloc(t->configuration, size);
508
+   if (!data)
509
+       return;
510
+
511
+   ltv = SPA_PTROFF(data, t->configuration_len, struct ltv);
512
+   ltv->len = 5;
513
+   ltv->type = LC3_TYPE_CHAN;
514
+   memcpy(ltv->value, &location, sizeof(uint32_t));
515
+
516
+   t->configuration = data;
517
+   t->configuration_len = size;
518
+}
519
+
520
 static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn,
521
        const char *path, DBusMessage *m, void *userdata)
522
 {
523
@@ -3929,6 +4314,9 @@
524
            |= transport->device->a2dp_volume_activeSPA_BT_VOLUME_ID_TX;
525
    }
526
 
527
+   if (codec->bap)
528
+       bap_update_codec_location(transport);
529
+
530
    if (codec->validate_config) {
531
        struct spa_audio_info info;
532
        if (codec->validate_config(codec, sink ? MEDIA_CODEC_FLAG_SINK : 0,
533
@@ -4699,6 +5087,9 @@
534
         * profile connection handlers can receive per-device settings during profile negotiation. */
535
        spa_bt_device_add_profile(d, SPA_BT_PROFILE_NULL);
536
    }
537
+   else if (spa_streq(interface_name, BLUEZ_DEVICE_SET_INTERFACE)) {
538
+       device_set_update_props(monitor, object_path, props_iter, NULL);
539
+   }
540
    else if (spa_streq(interface_name, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) {
541
        struct spa_bt_remote_endpoint *ep;
542
        struct spa_bt_device *d;
543
@@ -4766,6 +5157,8 @@
544
            d = spa_bt_device_find(monitor, object_path);
545
            if (d != NULL)
546
                device_free(d);
547
+       } else if (spa_streq(interface_name, BLUEZ_DEVICE_SET_INTERFACE)) {
548
+           device_set_update_props(monitor, object_path, NULL, NULL);
549
        } else if (spa_streq(interface_name, BLUEZ_ADAPTER_INTERFACE) ||
550
                spa_streq(interface_name, BLUEZ_MEDIA_INTERFACE)) {
551
            struct spa_bt_adapter *a;
552
@@ -5008,6 +5401,9 @@
553
 
554
            spa_bt_device_add_profile(d, SPA_BT_PROFILE_NULL);
555
        }
556
+       else if (spa_streq(iface, BLUEZ_DEVICE_SET_INTERFACE)) {
557
+           device_set_update_props(monitor, path, &it1, NULL);
558
+       }
559
        else if (spa_streq(iface, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) {
560
            struct spa_bt_remote_endpoint *ep;
561
            struct spa_bt_device *d;
562
@@ -5099,6 +5495,10 @@
563
    dbus_bus_add_match(this->conn,
564
            "type='signal',sender='" BLUEZ_SERVICE "',"
565
            "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
566
+           "arg0='" BLUEZ_DEVICE_SET_INTERFACE "'", &err);
567
+   dbus_bus_add_match(this->conn,
568
+           "type='signal',sender='" BLUEZ_SERVICE "',"
569
+           "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
570
            "arg0='" BLUEZ_MEDIA_ENDPOINT_INTERFACE "'", &err);
571
    dbus_bus_add_match(this->conn,
572
            "type='signal',sender='" BLUEZ_SERVICE "',"
573
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/bluez5-device.c -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/bluez5-device.c Changed
710
 
1
@@ -42,6 +42,8 @@
2
 
3
 #define DEVICE_ID_SOURCE   0
4
 #define DEVICE_ID_SINK     1
5
+#define DEVICE_ID_SOURCE_SET   2
6
+#define DEVICE_ID_SINK_SET 3
7
 #define DYNAMIC_NODE_ID_FLAG   0x1000
8
 
9
 static struct spa_i18n *_i18n;
10
@@ -98,6 +100,22 @@
11
    bool a2dp_duplex;
12
 };
13
 
14
+struct device_set_member {
15
+   struct impl *impl;
16
+   struct spa_bt_transport *transport;
17
+   struct spa_hook listener;
18
+};
19
+
20
+struct device_set {
21
+   struct impl *impl;
22
+   char *path;
23
+   bool leader;
24
+   uint32_t sinks;
25
+   uint32_t sources;
26
+   struct device_set_member sinkSPA_AUDIO_MAX_CHANNELS;
27
+   struct device_set_member sourceSPA_AUDIO_MAX_CHANNELS;
28
+};
29
+
30
 struct impl {
31
    struct spa_handle handle;
32
    struct spa_device device;
33
@@ -126,6 +144,8 @@
34
    unsigned int save_profile:1;
35
    uint32_t prev_bt_connected_profiles;
36
 
37
+   struct device_set device_set;
38
+
39
    const struct media_codec **supported_codecs;
40
    size_t supported_codec_count;
41
 
42
@@ -138,7 +158,7 @@
43
    struct spa_dict_item setting_itemsMAX_SETTINGS;
44
    struct spa_dict setting_dict;
45
 
46
-   struct node nodes2;
47
+   struct node nodes4;
48
 };
49
 
50
 static void init_node(struct impl *this, struct node *node, uint32_t id)
51
@@ -359,7 +379,7 @@
52
    struct spa_bt_transport_volume *t_volume;
53
    float prev_hw_volume;
54
 
55
-   if (!node->transport || !spa_bt_transport_volume_enabled(node->transport))
56
+   if (!node->active || !node->transport || !spa_bt_transport_volume_enabled(node->transport))
57
        return false;
58
 
59
    /* PW is the controller for remote device. */
60
@@ -463,14 +483,146 @@
61
            info.info.raw.channels * sizeof(uint32_t));
62
 }
63
 
64
+static const char *get_channel_name(uint32_t channel)
65
+{
66
+        int i;
67
+        for (i = 0; spa_type_audio_channeli.name; i++) {
68
+       if (spa_type_audio_channeli.type == channel)
69
+           return spa_debug_type_short_name(spa_type_audio_channeli.name);
70
+        }
71
+        return NULL;
72
+}
73
+
74
+static int channel_position_cmp(const void *pa, const void *pb)
75
+{
76
+   uint32_t a = *(uint32_t *)pa, b = *(uint32_t *)pb;
77
+   return (int)a - (int)b;
78
+}
79
+
80
+static void emit_device_set_node(struct impl *this, uint32_t id)
81
+{
82
+   struct spa_bt_device *device = this->bt_dev;
83
+   struct node *node = &this->nodesid;
84
+   struct spa_device_object_info info;
85
+   struct spa_dict_item items7;
86
+   char str_id32, members_json8192, channels_json512;
87
+   struct device_set_member *members;
88
+   uint32_t n_members;
89
+   uint32_t n_items = 0;
90
+   struct spa_strbuf json;
91
+   unsigned int i;
92
+
93
+   itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_ADDRESS, device->address);
94
+   itemsn_items++ = SPA_DICT_ITEM_INIT("api.bluez5.set", this->device_set.path);
95
+   itemsn_items++ = SPA_DICT_ITEM_INIT("api.bluez5.set.leader", "true");
96
+   snprintf(str_id, sizeof(str_id), "%d", id);
97
+   itemsn_items++ = SPA_DICT_ITEM_INIT("card.profile.device", str_id);
98
+
99
+   if (id == DEVICE_ID_SOURCE_SET) {
100
+       itemsn_items++ = SPA_DICT_ITEM_INIT("media.class", "Audio/Source");
101
+       members = this->device_set.source;
102
+       n_members = this->device_set.sources;
103
+   } else if (id == DEVICE_ID_SINK_SET) {
104
+       itemsn_items++ = SPA_DICT_ITEM_INIT("media.class", "Audio/Sink");
105
+       members = this->device_set.sink;
106
+       n_members = this->device_set.sinks;
107
+   } else {
108
+       spa_assert_not_reached();
109
+   }
110
+
111
+   node->impl = this;
112
+   node->active = true;
113
+   node->transport = NULL;
114
+   node->a2dp_duplex = false;
115
+   node->offload_acquired = false;
116
+   node->mute = false;
117
+   node->save = false;
118
+   node->latency_offset = 0;
119
+
120
+   /* Form channel map from members */
121
+   node->n_channels = 0;
122
+   for (i = 0; i < n_members; ++i) {
123
+       struct spa_bt_transport *t = membersi.transport;
124
+       unsigned int j;
125
+
126
+       for (j = 0; j < t->n_channels; ++j) {
127
+           unsigned int k;
128
+
129
+           if (!get_channel_name(t->channelsj))
130
+               continue;
131
+
132
+           for (k = 0; k < node->n_channels; ++k) {
133
+               if (node->channelsk == t->channelsj)
134
+                   break;
135
+           }
136
+           if (k == node->n_channels && node->n_channels < SPA_AUDIO_MAX_CHANNELS)
137
+               node->channelsnode->n_channels++ = t->channelsj;
138
+       }
139
+   }
140
+
141
+   qsort(node->channels, node->n_channels, sizeof(uint32_t), channel_position_cmp);
142
+
143
+   for (i = 0; i < node->n_channels; ++i) {
144
+       /* Session manager will override this, so put in some safe number */
145
+       node->volumesi = node->soft_volumesi = 0.064;
146
+   }
147
+
148
+   /* Produce member info json */
149
+   spa_strbuf_init(&json, members_json, sizeof(members_json));
150
+   spa_strbuf_append(&json, "");
151
+   for (i = 0; i < n_members; ++i) {
152
+       struct spa_bt_transport *t = membersi.transport;
153
+       char object_path512;
154
+       unsigned int j;
155
+       int member_id = (id == DEVICE_ID_SINK_SET) ? DEVICE_ID_SINK : DEVICE_ID_SOURCE;
156
+
157
+       if (i > 0)
158
+           spa_strbuf_append(&json, ",");
159
+       spa_scnprintf(object_path, sizeof(object_path), "%s/%s-%d",
160
+               this->device_set.path, t->device->address, member_id);
161
+       spa_strbuf_append(&json, "{\"object.path\":\"%s\",\"channels\":", object_path);
162
+       for (j = 0; j < t->n_channels; ++j) {
163
+           if (j > 0)
164
+               spa_strbuf_append(&json, ",");
165
+           spa_strbuf_append(&json, "\"%s\"", get_channel_name(t->channelsj));
166
+       }
167
+       spa_strbuf_append(&json, "}");
168
+   }
169
+   spa_strbuf_append(&json, "");
170
+   json.bufferSPA_MIN(json.pos, json.maxsize-1) = 0;
171
+   itemsn_items++ = SPA_DICT_ITEM_INIT("api.bluez5.set.members", members_json);
172
+
173
+   spa_strbuf_init(&json, channels_json, sizeof(channels_json));
174
+   spa_strbuf_append(&json, "");
175
+   for (i = 0; i < node->n_channels; ++i) {
176
+       if (i > 0)
177
+           spa_strbuf_append(&json, ",");
178
+       spa_strbuf_append(&json, "\"%s\"", get_channel_name(node->channelsi));
179
+   }
180
+   spa_strbuf_append(&json, "");
181
+   json.bufferSPA_MIN(json.pos, json.maxsize-1) = 0;
182
+   itemsn_items++ = SPA_DICT_ITEM_INIT("api.bluez5.set.channels", channels_json);
183
+
184
+   /* Emit */
185
+   info = SPA_DEVICE_OBJECT_INFO_INIT();
186
+   info.type = SPA_TYPE_INTERFACE_Node;
187
+   info.factory_name = (id == DEVICE_ID_SOURCE_SET) ? "source" : "sink";
188
+   info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS;
189
+   info.props = &SPA_DICT_INIT(items, n_items);
190
+
191
+   spa_device_emit_object_info(&this->hooks, id, &info);
192
+
193
+   emit_node_props(this, &this->nodesid, true);
194
+}
195
+
196
 static void emit_node(struct impl *this, struct spa_bt_transport *t,
197
        uint32_t id, const char *factory_name, bool a2dp_duplex)
198
 {
199
    struct spa_bt_device *device = this->bt_dev;
200
    struct spa_device_object_info info;
201
-   struct spa_dict_item items8;
202
+   struct spa_dict_item items11;
203
    uint32_t n_items = 0;
204
-   char transport32, str_id32;
205
+   char transport32, str_id32, object_path512;
206
    bool is_dyn_node = SPA_FLAG_IS_SET(id, DYNAMIC_NODE_ID_FLAG);
207
 
208
    snprintf(transport, sizeof(transport), "pointer:%p", t);
209
@@ -480,9 +632,9 @@
210
    items3 = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_ADDRESS, device->address);
211
    items4 = SPA_DICT_ITEM_INIT("device.routes", "1");
212
    n_items = 5;
213
-   if (!is_dyn_node) {
214
+   if (!is_dyn_node && !this->device_set.path) {
215
        snprintf(str_id, sizeof(str_id), "%d", id);
216
-       items5 = SPA_DICT_ITEM_INIT("card.profile.device", str_id);
217
+       itemsn_items = SPA_DICT_ITEM_INIT("card.profile.device", str_id);
218
        n_items++;
219
    }
220
    if (spa_streq(spa_bt_profile_name(t->profile), "headset-head-unit")) {
221
@@ -493,6 +645,18 @@
222
        itemsn_items = SPA_DICT_ITEM_INIT("api.bluez5.a2dp-duplex", "true");
223
        n_items++;
224
    }
225
+   if (this->device_set.path) {
226
+       itemsn_items = SPA_DICT_ITEM_INIT("api.bluez5.set", this->device_set.path);
227
+       n_items++;
228
+       itemsn_items = SPA_DICT_ITEM_INIT("api.bluez5.internal", "true");
229
+       n_items++;
230
+
231
+       /* object.path can be used in match rules with only basic node props */
232
+       spa_scnprintf(object_path, sizeof(object_path), "%s/%s-%d",
233
+               this->device_set.path, device->address, id);
234
+       itemsn_items = SPA_DICT_ITEM_INIT("object.path", object_path);
235
+       n_items++;
236
+   }
237
 
238
    info = SPA_DEVICE_OBJECT_INFO_INIT();
239
    info.type = SPA_TYPE_INTERFACE_Node;
240
@@ -503,6 +667,16 @@
241
    SPA_FLAG_CLEAR(id, DYNAMIC_NODE_ID_FLAG);
242
    spa_device_emit_object_info(&this->hooks, id, &info);
243
 
244
+   if (this->device_set.path) {
245
+       /* Device set member nodes don't have their own routes */
246
+       this->nodesid.impl = this;
247
+       this->nodesid.active = false;
248
+       if (this->nodesid.transport)
249
+           spa_hook_remove(&this->nodesid.transport_listener);
250
+       this->nodesid.transport = NULL;
251
+       return;
252
+   }
253
+
254
    if (!is_dyn_node) {
255
        uint32_t prev_channels = this->nodesid.n_channels;
256
        float boost;
257
@@ -540,9 +714,9 @@
258
    }
259
 }
260
 
261
-static struct spa_bt_transport *find_transport(struct impl *this, int profile, enum spa_bluetooth_audio_codec codec)
262
+static struct spa_bt_transport *find_device_transport(struct spa_bt_device *device, int profile,
263
+       enum spa_bluetooth_audio_codec codec)
264
 {
265
-   struct spa_bt_device *device = this->bt_dev;
266
    struct spa_bt_transport *t;
267
 
268
    spa_list_for_each(t, &device->transport_list, device_link) {
269
@@ -559,6 +733,11 @@
270
    return NULL;
271
 }
272
 
273
+static struct spa_bt_transport *find_transport(struct impl *this, int profile, enum spa_bluetooth_audio_codec codec)
274
+{
275
+   return find_device_transport(this->bt_dev, profile, codec);
276
+}
277
+
278
 static void dynamic_node_transport_destroy(void *data)
279
 {
280
    struct dynamic_node *this = data;
281
@@ -631,8 +810,8 @@
282
            SPA_PROP_volume, SPA_POD_Float(t_volume->volume));
283
    event = spa_pod_builder_pop(&b, &f0);
284
 
285
-   spa_log_debug(impl->log, "dynamic node %p: volume %d changed %f, profile %d",
286
-       node, volume_id, t_volume->volume, node->transport->profile);
287
+   spa_log_debug(impl->log, "dynamic node %d: volume %d changed %f, profile %d",
288
+           node->id, volume_id, t_volume->volume, node->transport->profile);
289
 
290
    /* Dynamic node doesn't has route, we can only set volume on adaptar node. */
291
    spa_device_emit_event(&impl->hooks, event);
292
@@ -686,10 +865,97 @@
293
    this->factory_name = NULL;
294
 }
295
 
296
+static void device_set_clear(struct impl *impl)
297
+{
298
+   struct device_set *set = &impl->device_set;
299
+   unsigned int i;
300
+
301
+   for (i = 0; i < SPA_N_ELEMENTS(set->sink); ++i) {
302
+       if (set->sinki.transport)
303
+           spa_hook_remove(&set->sinki.listener);
304
+       if (set->sourcei.transport)
305
+           spa_hook_remove(&set->sourcei.listener);
306
+   }
307
+
308
+   free(set->path);
309
+   spa_zero(*set);
310
+   set->impl = impl;
311
+}
312
+
313
+static void device_set_transport_destroy(void *data)
314
+{
315
+   struct device_set_member *member = data;
316
+
317
+   member->transport = NULL;
318
+   spa_hook_remove(&member->listener);
319
+}
320
+
321
+static const struct spa_bt_transport_events device_set_transport_events = {
322
+   SPA_VERSION_BT_DEVICE_EVENTS,
323
+   .destroy = device_set_transport_destroy,
324
+};
325
+
326
+static void device_set_update(struct impl *this)
327
+{
328
+   struct spa_bt_device *device = this->bt_dev;
329
+   struct device_set *dset = &this->device_set;
330
+   struct spa_bt_set_membership *set;
331
+
332
+   device_set_clear(this);
333
+
334
+   if (!is_bap_client(this))
335
+       return;
336
+
337
+   spa_list_for_each(set, &device->set_membership_list, link) {
338
+       struct spa_bt_set_membership *s;
339
+       int num_devices = 0;
340
+
341
+       spa_bt_for_each_set_member(s, set) {
342
+           struct spa_bt_transport *t;
343
+           bool active = false;
344
+
345
+           if (!(s->device->connected_profiles & SPA_BT_PROFILE_BAP_DUPLEX))
346
+               continue;
347
+
348
+           t = find_device_transport(s->device, SPA_BT_PROFILE_BAP_SOURCE, 0);
349
+           if (t && t->bap_initiator) {
350
+               active = true;
351
+               dset->sourcedset->sources.transport = t;
352
+               spa_bt_transport_add_listener(t, &dset->sourcedset->sources.listener,
353
+                       &device_set_transport_events, &dset->sourcedset->sources);
354
+               ++dset->sources;
355
+           }
356
+
357
+           t = find_device_transport(s->device, SPA_BT_PROFILE_BAP_SINK, this->props.codec);
358
+           if (t && t->bap_initiator) {
359
+               active = true;
360
+               dset->sinkdset->sinks.transport = t;
361
+               spa_bt_transport_add_listener(t, &dset->sinkdset->sinks.listener,
362
+                       &device_set_transport_events, &dset->sinkdset->sinks);
363
+               ++dset->sinks;
364
+           }
365
+
366
+           if (active)
367
+               ++num_devices;
368
+       }
369
+
370
+       if (num_devices <= 1 || (dset->sinks <= 1 && dset->sources <= 1)) {
371
+           device_set_clear(this);
372
+           continue;
373
+       }
374
+
375
+       dset->path = strdup(set->path);
376
+       dset->leader = set->leader;
377
+       break;
378
+   }
379
+}
380
+
381
 static int emit_nodes(struct impl *this)
382
 {
383
    struct spa_bt_transport *t;
384
 
385
+   device_set_update(this);
386
+
387
    switch (this->profile) {
388
    case DEVICE_PROFILE_OFF:
389
        break;
390
@@ -765,6 +1031,9 @@
391
                    emit_dynamic_node(&this->dyn_media_source, this, t,
392
                        DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_MEDIA_SOURCE, false);
393
            }
394
+
395
+           if (this->device_set.leader && this->device_set.sources > 0)
396
+               emit_device_set_node(this, DEVICE_ID_SOURCE_SET);
397
        }
398
 
399
        if (this->bt_dev->connected_profiles & (SPA_BT_PROFILE_BAP_SINK)) {
400
@@ -777,6 +1046,9 @@
401
                    emit_dynamic_node(&this->dyn_media_sink, this, t,
402
                        DEVICE_ID_SINK, SPA_NAME_API_BLUEZ5_MEDIA_SINK, false);
403
            }
404
+
405
+           if (this->device_set.leader && this->device_set.sinks > 0)
406
+               emit_device_set_node(this, DEVICE_ID_SINK_SET);
407
        }
408
 
409
        if (get_supported_media_codec(this, this->props.codec, NULL) == NULL)
410
@@ -832,7 +1104,7 @@
411
    remove_dynamic_node (&this->dyn_sco_source);
412
    remove_dynamic_node (&this->dyn_sco_sink);
413
 
414
-   for (uint32_t i = 0; i < 2; i++) {
415
+   for (uint32_t i = 0; i < SPA_N_ELEMENTS(this->nodes); i++) {
416
        struct node * node = &this->nodesi;
417
        node_offload_set_active(node, false);
418
        if (node->transport) {
419
@@ -864,7 +1136,7 @@
420
 
421
    if (this->profile == profile &&
422
        (this->profile != DEVICE_PROFILE_A2DP || codec == this->props.codec) &&
423
-       (this->profile != DEVICE_PROFILE_BAP || codec == this->props.codec) &&
424
+       (this->profile != DEVICE_PROFILE_BAP || codec == this->props.codec || this->device_set.path) &&
425
        (this->profile != DEVICE_PROFILE_HSP_HFP || codec == this->props.codec))
426
        return 0;
427
 
428
@@ -1036,13 +1308,35 @@
429
    emit_info(this, false);
430
 }
431
 
432
+static void device_set_changed(void *userdata)
433
+{
434
+   struct impl *this = userdata;
435
+
436
+   if (this->profile != DEVICE_PROFILE_BAP)
437
+       return;
438
+   if (!is_bap_client(this))
439
+       return;
440
+
441
+   spa_log_debug(this->log, "%p: device set changed", this);
442
+
443
+   emit_remove_nodes(this);
444
+   emit_nodes(this);
445
+
446
+   this->info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS;
447
+   this->paramsIDX_Profile.flags ^= SPA_PARAM_INFO_SERIAL;
448
+   this->paramsIDX_EnumProfile.flags ^= SPA_PARAM_INFO_SERIAL;
449
+   this->paramsIDX_Route.flags ^= SPA_PARAM_INFO_SERIAL;
450
+   this->paramsIDX_EnumRoute.flags ^= SPA_PARAM_INFO_SERIAL;
451
+   emit_info(this, false);
452
+}
453
+
454
 static void set_initial_profile(struct impl *this);
455
 
456
 static void device_connected(void *userdata, bool connected)
457
 {
458
    struct impl *this = userdata;
459
 
460
-   spa_log_debug(this->log, "connected: %d", connected);
461
+   spa_log_debug(this->log, "%p: connected: %d", this, connected);
462
 
463
    if (connected ^ (this->profile != DEVICE_PROFILE_OFF)) {
464
        emit_remove_nodes(this);
465
@@ -1055,6 +1349,7 @@
466
    .connected = device_connected,
467
    .codec_switched = codec_switched,
468
    .profiles_changed = profiles_changed,
469
+   .device_set_changed = device_set_changed,
470
 };
471
 
472
 static int impl_add_listener(void *object,
473
@@ -1101,14 +1396,19 @@
474
 
475
    switch (index) {
476
    case DEVICE_PROFILE_A2DP:
477
-   case DEVICE_PROFILE_BAP:
478
-       if (device->connected_profiles & SPA_BT_PROFILE_MEDIA_SINK)
479
+       if (device->connected_profiles & SPA_BT_PROFILE_A2DP_SINK)
480
            have_output = true;
481
 
482
        media_codec = get_supported_media_codec(this, codec, NULL);
483
        if (media_codec && media_codec->duplex_codec)
484
            have_input = true;
485
        break;
486
+   case DEVICE_PROFILE_BAP:
487
+       if (device->connected_profiles & SPA_BT_PROFILE_BAP_SINK)
488
+           have_output = true;
489
+       if (device->connected_profiles & SPA_BT_PROFILE_BAP_SOURCE)
490
+           have_input = true;
491
+       break;
492
    case DEVICE_PROFILE_HSP_HFP:
493
        if (device->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT)
494
            have_output = have_input = true;
495
@@ -1400,6 +1700,14 @@
496
            }
497
            priority = 128;
498
        }
499
+
500
+       if (this->device_set.leader) {
501
+           n_sink = this->device_set.sinks ? 1 : 0;
502
+           n_source = this->device_set.sinks ? 1 : 0;
503
+       } else if (this->device_set.path) {
504
+           n_sink = 0;
505
+           n_source = 0;
506
+       }
507
        break;
508
    }
509
    case DEVICE_PROFILE_HSP_HFP:
510
@@ -1498,6 +1806,7 @@
511
    const char *name_prefix, *description, *hfp_description, *port_type;
512
    enum spa_bt_form_factor ff;
513
    enum spa_bluetooth_audio_codec codec;
514
+   enum spa_param_availability available;
515
    char name128;
516
    uint32_t i, j, mask, next;
517
    uint32_t dev = SPA_ID_INVALID, enum_dev;
518
@@ -1573,7 +1882,7 @@
519
        direction = SPA_DIRECTION_INPUT;
520
        snprintf(name, sizeof(name), "%s-input", name_prefix);
521
        enum_dev = DEVICE_ID_SOURCE;
522
-       if (profile == DEVICE_PROFILE_A2DP)
523
+       if (profile == DEVICE_PROFILE_A2DP || profile == DEVICE_PROFILE_BAP)
524
            dev = enum_dev;
525
        else if (profile != SPA_ID_INVALID)
526
            enum_dev = SPA_ID_INVALID;
527
@@ -1582,7 +1891,7 @@
528
        direction = SPA_DIRECTION_OUTPUT;
529
        snprintf(name, sizeof(name), "%s-output", name_prefix);
530
        enum_dev = DEVICE_ID_SINK;
531
-       if (profile == DEVICE_PROFILE_A2DP)
532
+       if (profile == DEVICE_PROFILE_A2DP || profile == DEVICE_PROFILE_BAP)
533
            dev = enum_dev;
534
        else if (profile != SPA_ID_INVALID)
535
            enum_dev = SPA_ID_INVALID;
536
@@ -1607,6 +1916,32 @@
537
        else if (profile != SPA_ID_INVALID)
538
            enum_dev = SPA_ID_INVALID;
539
        break;
540
+   case 4:
541
+       if (!this->device_set.leader) {
542
+           errno = EINVAL;
543
+           return NULL;
544
+       }
545
+       direction = SPA_DIRECTION_INPUT;
546
+       snprintf(name, sizeof(name), "%s-set-input", name_prefix);
547
+       enum_dev = DEVICE_ID_SOURCE_SET;
548
+       if (profile == DEVICE_PROFILE_BAP)
549
+           dev = enum_dev;
550
+       else if (profile != SPA_ID_INVALID)
551
+           enum_dev = SPA_ID_INVALID;
552
+       break;
553
+   case 5:
554
+       if (!this->device_set.leader) {
555
+           errno = EINVAL;
556
+           return NULL;
557
+       }
558
+       direction = SPA_DIRECTION_OUTPUT;
559
+       snprintf(name, sizeof(name), "%s-set-output", name_prefix);
560
+       enum_dev = DEVICE_ID_SINK_SET;
561
+       if (profile == DEVICE_PROFILE_BAP)
562
+           dev = enum_dev;
563
+       else if (profile != SPA_ID_INVALID)
564
+           enum_dev = SPA_ID_INVALID;
565
+       break;
566
    default:
567
        errno = EINVAL;
568
        return NULL;
569
@@ -1617,6 +1952,10 @@
570
        return NULL;
571
    }
572
 
573
+   available = SPA_PARAM_AVAILABILITY_yes;
574
+   if (this->device_set.path && !(port == 4 || port == 5))
575
+       available = SPA_PARAM_AVAILABILITY_no;
576
+
577
    spa_pod_builder_push_object(b, &f0, SPA_TYPE_OBJECT_ParamRoute, id);
578
    spa_pod_builder_add(b,
579
        SPA_PARAM_ROUTE_index, SPA_POD_Int(port),
580
@@ -1624,7 +1963,7 @@
581
        SPA_PARAM_ROUTE_name,  SPA_POD_String(name),
582
        SPA_PARAM_ROUTE_description,  SPA_POD_String(description),
583
        SPA_PARAM_ROUTE_priority,  SPA_POD_Int(0),
584
-       SPA_PARAM_ROUTE_available,  SPA_POD_Id(SPA_PARAM_AVAILABILITY_yes),
585
+       SPA_PARAM_ROUTE_available,  SPA_POD_Id(available),
586
        0);
587
    spa_pod_builder_prop(b, SPA_PARAM_ROUTE_info, 0);
588
    spa_pod_builder_push_struct(b, &f1);
589
@@ -1643,6 +1982,8 @@
590
 
591
        if (j == DEVICE_PROFILE_A2DP && !(port == 0 || port == 1))
592
            continue;
593
+       if (j == DEVICE_PROFILE_BAP && !(port == 0 || port == 1 || port == 4 || port == 5))
594
+           continue;
595
        if (j == DEVICE_PROFILE_HSP_HFP && !(port == 2 || port == 3))
596
            continue;
597
 
598
@@ -1882,7 +2223,7 @@
599
    case SPA_PARAM_EnumRoute:
600
    {
601
        switch (result.index) {
602
-       case 0: case 1: case 2: case 3:
603
+       case 0: case 1: case 2: case 3: case 4: case 5:
604
            param = build_route(this, &b, id, result.index, SPA_ID_INVALID);
605
            if (param == NULL)
606
                goto next;
607
@@ -1895,7 +2236,7 @@
608
    case SPA_PARAM_Route:
609
    {
610
        switch (result.index) {
611
-       case 0: case 1: case 2: case 3:
612
+       case 0: case 1: case 2: case 3: case 4: case 5:
613
            param = build_route(this, &b, id, result.index, this->profile);
614
            if (param == NULL)
615
                goto next;
616
@@ -1959,7 +2300,7 @@
617
    if (n_volumes == 0)
618
        return -EINVAL;
619
 
620
-   spa_log_info(this->log, "node %p volume %f", node, volumes0);
621
+   spa_log_info(this->log, "node %d volume %f", node->id, volumes0);
622
 
623
    for (i = 0; i < node->n_channels; i++) {
624
        if (node->volumesi == volumesi % n_volumes)
625
@@ -1973,7 +2314,7 @@
626
    if (t_volume && t_volume->active
627
        && spa_bt_transport_volume_enabled(node->transport)) {
628
        float hw_volume = node_get_hw_volume(node);
629
-       spa_log_debug(this->log, "node %p hardware volume %f", node, hw_volume);
630
+       spa_log_debug(this->log, "node %d hardware volume %f", node->id, hw_volume);
631
 
632
        node_update_soft_volumes(node, hw_volume);
633
        spa_bt_transport_set_volume(node->transport, node->id, hw_volume);
634
@@ -1996,7 +2337,7 @@
635
    struct spa_pod_frame f1;
636
    int changed = 0;
637
 
638
-   spa_log_info(this->log, "node %p mute %d", node, mute);
639
+   spa_log_info(this->log, "node %d mute %d", node->id, mute);
640
 
641
    changed = (node->mute != mute);
642
    node->mute = mute;
643
@@ -2027,7 +2368,7 @@
644
    struct spa_pod_frame f1;
645
    int changed = 0;
646
 
647
-   spa_log_info(this->log, "node %p latency offset %"PRIi64" nsec", node, latency_offset);
648
+   spa_log_info(this->log, "node %d latency offset %"PRIi64" nsec", node->id, latency_offset);
649
 
650
    changed = (node->latency_offset != latency_offset);
651
    node->latency_offset = latency_offset;
652
@@ -2108,10 +2449,11 @@
653
 static void apply_prop_offload_active(struct impl *this, bool active)
654
 {
655
    bool old_value = this->props.offload_active;
656
+   unsigned int i;
657
 
658
    this->props.offload_active = active;
659
 
660
-   for (int i = 0; i < 2; i++) {
661
+   for (i = 0; i < SPA_N_ELEMENTS(this->nodes); i++) {
662
        node_offload_set_active(&this->nodesi, active);
663
        if (!this->nodesi.offload_acquired)
664
            this->props.offload_active = false;
665
@@ -2157,7 +2499,8 @@
666
        if (profile == SPA_ID_INVALID)
667
            return -EINVAL;
668
 
669
-       spa_log_debug(this->log, "setting profile %d codec:%d save:%d", profile, codec, (int)save);
670
+       spa_log_debug(this->log, "%p: setting profile %d codec:%d save:%d", this,
671
+               profile, codec, (int)save);
672
        return set_profile(this, profile, codec, save);
673
    }
674
    case SPA_PARAM_Route:
675
@@ -2180,7 +2523,7 @@
676
            spa_debug_log_pod(this->log, SPA_LOG_LEVEL_DEBUG, 0, NULL, param);
677
            return res;
678
        }
679
-       if (device > 1 || !this->nodesdevice.active)
680
+       if (device >= SPA_N_ELEMENTS(this->nodes) || !this->nodesdevice.active)
681
            return -EINVAL;
682
 
683
        node = &this->nodesdevice;
684
@@ -2288,6 +2631,7 @@
685
            free((void *)it->value);
686
    }
687
 
688
+   device_set_clear(this);
689
    return 0;
690
 }
691
 
692
@@ -2373,6 +2717,8 @@
693
 
694
    init_node(this, &this->nodes0, 0);
695
    init_node(this, &this->nodes1, 1);
696
+   init_node(this, &this->nodes2, 2);
697
+   init_node(this, &this->nodes3, 3);
698
 
699
    this->info = SPA_DEVICE_INFO_INIT();
700
    this->info_all = SPA_DEVICE_CHANGE_MASK_PROPS |
701
@@ -2389,6 +2735,8 @@
702
 
703
    spa_bt_device_add_listener(this->bt_dev, &this->bt_dev_listener, &bt_dev_events, this);
704
 
705
+   this->device_set.impl = this;
706
+
707
    set_initial_profile(this);
708
 
709
    return 0;
710
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/defs.h -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/defs.h Changed
62
 
1
@@ -27,6 +27,7 @@
2
 #define BLUEZ_PROFILE_INTERFACE BLUEZ_SERVICE ".Profile1"
3
 #define BLUEZ_ADAPTER_INTERFACE BLUEZ_SERVICE ".Adapter1"
4
 #define BLUEZ_DEVICE_INTERFACE BLUEZ_SERVICE ".Device1"
5
+#define BLUEZ_DEVICE_SET_INTERFACE BLUEZ_SERVICE ".DeviceSet1"
6
 #define BLUEZ_MEDIA_INTERFACE BLUEZ_SERVICE ".Media1"
7
 #define BLUEZ_MEDIA_ENDPOINT_INTERFACE BLUEZ_SERVICE ".MediaEndpoint1"
8
 #define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1"
9
@@ -440,12 +441,27 @@
10
    /** Profile configuration changed */
11
    void (*profiles_changed) (void *data, uint32_t prev_profiles, uint32_t prev_connected);
12
 
13
+   /** Device set configuration changed */
14
+   void (*device_set_changed) (void *data);
15
+
16
    /** Device freed */
17
    void (*destroy) (void *data);
18
 };
19
 
20
 struct media_codec;
21
 
22
+struct spa_bt_set_membership {
23
+   struct spa_list link;
24
+   struct spa_list others;
25
+   struct spa_bt_device *device;
26
+   char *path;
27
+   uint8_t rank;
28
+   bool leader;
29
+};
30
+
31
+#define spa_bt_for_each_set_member(s, set) \
32
+   for ((s) = (set); (s); (s) = spa_list_next((s), others), (s) = (s) != (set) ? (s) : NULL)
33
+
34
 struct spa_bt_device {
35
    struct spa_list link;
36
    struct spa_bt_monitor *monitor;
37
@@ -477,6 +493,7 @@
38
    struct spa_list remote_endpoint_list;
39
    struct spa_list transport_list;
40
    struct spa_list codec_switch_list;
41
+   struct spa_list set_membership_list;
42
    uint8_t battery;
43
    int has_battery;
44
 
45
@@ -518,6 +535,7 @@
46
 #define spa_bt_device_emit_connected(d,...)            spa_bt_device_emit(d, connected, 0, __VA_ARGS__)
47
 #define spa_bt_device_emit_codec_switched(d,...)   spa_bt_device_emit(d, codec_switched, 0, __VA_ARGS__)
48
 #define spa_bt_device_emit_profiles_changed(d,...) spa_bt_device_emit(d, profiles_changed, 0, __VA_ARGS__)
49
+#define spa_bt_device_emit_device_set_changed(d)   spa_bt_device_emit(d, device_set_changed, 0)
50
 #define spa_bt_device_emit_destroy(d)          spa_bt_device_emit(d, destroy, 0)
51
 #define spa_bt_device_add_listener(d,listener,events,data)           \
52
    spa_hook_list_append(&(d)->listener_list, listener, events, data)
53
@@ -612,6 +630,8 @@
54
    unsigned int latency_us;
55
    uint8_t bap_cig;
56
    uint8_t bap_cis;
57
+   uint32_t bap_location;
58
+   uint32_t bap_interval;
59
 
60
    struct spa_bt_iso_io *iso_io;
61
    struct spa_bt_sco_io *sco_io;
62
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/iso-io.c -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/iso-io.c Changed
120
 
1
@@ -16,8 +16,6 @@
2
 #include <spa/utils/result.h>
3
 #include <spa/node/io.h>
4
 
5
-#include <bluetooth/bluetooth.h>
6
-
7
 #include "config.h"
8
 #include "iso-io.h"
9
 
10
@@ -25,7 +23,7 @@
11
 #undef SPA_LOG_TOPIC_DEFAULT
12
 #define SPA_LOG_TOPIC_DEFAULT &log_topic
13
 
14
-#define IDLE_TIME  (100 * SPA_NSEC_PER_MSEC)
15
+#define IDLE_TIME  (200 * SPA_NSEC_PER_MSEC)
16
 
17
 struct group {
18
    struct spa_log *log;
19
@@ -159,9 +157,8 @@
20
 
21
        if (!stream->sink)
22
            continue;
23
-       if (stream->idle)
24
-           continue;
25
-       if (group->paused) {
26
+       if (stream->idle || group->paused) {
27
+           stream->this.resync = true;
28
            stream->this.size = 0;
29
            continue;
30
        }
31
@@ -195,19 +192,12 @@
32
    set_timeout(group, group->next);
33
 }
34
 
35
-static struct group *group_create(int fd, struct spa_log *log, struct spa_loop *data_loop,
36
-       struct spa_system *data_system)
37
+static struct group *group_create(uint8_t cig, uint32_t interval,
38
+       struct spa_log *log, struct spa_loop *data_loop, struct spa_system *data_system)
39
 {
40
-#if defined(HAVE_BLUETOOTH_BAP) && defined(BT_ISO_QOS)
41
    struct group *group;
42
-   struct bt_iso_qos qos;
43
-   socklen_t len;
44
-
45
-   len = sizeof(qos);
46
-   if (getsockopt(fd, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len) < 0)
47
-       return NULL;
48
 
49
-   if (qos.out.interval <= 5000) {
50
+   if (interval <= 5000) {
51
        errno = EINVAL;
52
        return NULL;
53
    }
54
@@ -218,11 +208,11 @@
55
 
56
    spa_log_topic_init(log, &log_topic);
57
 
58
-   group->cig = qos.cig;
59
+   group->cig = cig;
60
    group->log = log;
61
    group->data_loop = data_loop;
62
    group->data_system = data_system;
63
-   group->duration = qos.out.interval * SPA_NSEC_PER_USEC;
64
+   group->duration = interval * SPA_NSEC_PER_USEC;
65
 
66
    spa_list_init(&group->streams);
67
 
68
@@ -243,10 +233,6 @@
69
    spa_loop_add_source(group->data_loop, &group->source);
70
 
71
    return group;
72
-#else
73
-   errno = EOPNOTSUPP;
74
-   return NULL;
75
-#endif
76
 }
77
 
78
 static int do_remove_source(struct spa_loop *loop, bool async, uint32_t seq,
79
@@ -286,6 +272,7 @@
80
    stream->fd = fd;
81
    stream->sink = sink;
82
    stream->group = group;
83
+   stream->idle = true;
84
    stream->this.duration = group->duration;
85
 
86
    stream_link(group, stream);
87
@@ -293,13 +280,13 @@
88
    return stream;
89
 }
90
 
91
-struct spa_bt_iso_io *spa_bt_iso_io_create(int fd, bool sink, struct spa_log *log,
92
-       struct spa_loop *data_loop, struct spa_system *data_system)
93
+struct spa_bt_iso_io *spa_bt_iso_io_create(int fd, bool sink, uint8_t cig, uint32_t interval,
94
+       struct spa_log *log, struct spa_loop *data_loop, struct spa_system *data_system)
95
 {
96
    struct stream *stream;
97
    struct group *group;
98
 
99
-   group = group_create(fd, log, data_loop, data_system);
100
+   group = group_create(cig, interval, log, data_loop, data_system);
101
    if (group == NULL)
102
        return NULL;
103
 
104
@@ -366,12 +353,11 @@
105
    else if (enabled && !was_enabled)
106
        set_timers(stream->group);
107
 
108
+   stream->idle = true;
109
+   stream->this.resync = true;
110
+
111
    if (pull == NULL) {
112
        stream->this.size = 0;
113
        return;
114
    }
115
-
116
-   /* Pull data now for the next interval */
117
-   stream->this.now = stream->group->next;
118
-   stream->pull(&stream->this);
119
 }
120
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/iso-io.h -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/iso-io.h Changed
31
 
1
@@ -18,20 +18,22 @@
2
  */
3
 struct spa_bt_iso_io
4
 {
5
-   uint64_t now;
6
-   uint64_t duration;
7
+   uint64_t now;       /**< Reference time position of next packet (read-only) */
8
+   uint64_t duration;  /**< ISO interval duration in ns (read-only) */
9
+   bool resync;        /**< Resync position for next packet; (pull callback sets to
10
+                * false when done) */
11
 
12
-   uint32_t timestamp;
13
-   uint8_t buf4096;
14
-   size_t size;
15
+   uint32_t timestamp; /**< Packet timestamp (set by pull callback) */
16
+   uint8_t buf4096;    /**< Packet data (set by pull callback) */
17
+   size_t size;        /**< Packet size (set by pull callback) */
18
 
19
    void *user_data;
20
 };
21
 
22
 typedef void (*spa_bt_iso_io_pull_t)(struct spa_bt_iso_io *io);
23
 
24
-struct spa_bt_iso_io *spa_bt_iso_io_create(int fd, bool sink, struct spa_log *log,
25
-       struct spa_loop *data_loop, struct spa_system *data_system);
26
+struct spa_bt_iso_io *spa_bt_iso_io_create(int fd, bool sink, uint8_t cig, uint32_t interval,
27
+       struct spa_log *log, struct spa_loop *data_loop, struct spa_system *data_system);
28
 struct spa_bt_iso_io *spa_bt_iso_io_attach(struct spa_bt_iso_io *io, int fd, bool sink);
29
 void spa_bt_iso_io_destroy(struct spa_bt_iso_io *io);
30
 void spa_bt_iso_io_set_cb(struct spa_bt_iso_io *io, spa_bt_iso_io_pull_t pull, void *user_data);
31
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/media-sink.c -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/media-sink.c Changed
215
 
1
@@ -58,6 +58,10 @@
2
 #define BUFFER_SIZE    (8192*8)
3
 #define RATE_CTL_DIFF_MAX 0.005
4
 
5
+/* Wait for two cycles before trying to sync ISO. On start/driver reassign,
6
+ * first cycle may have strange number of samples. */
7
+#define RESYNC_CYCLES 2
8
+
9
 struct buffer {
10
    uint32_t id;
11
 #define BUFFER_FLAG_OUT    (1<<0)
12
@@ -129,8 +133,10 @@
13
    unsigned int following:1;
14
    unsigned int is_output:1;
15
    unsigned int flush_pending:1;
16
+   unsigned int iso_pending:1;
17
 
18
    unsigned int is_duplex:1;
19
+   unsigned int is_internal:1;
20
 
21
    struct spa_source source;
22
    int timerfd;
23
@@ -159,8 +165,7 @@
24
 
25
    int need_flush;
26
    bool fragment;
27
-   bool resync;
28
-   bool have_iso_packet;
29
+   uint32_t resync;
30
    uint32_t block_size;
31
    uint8_t bufferBUFFER_SIZE;
32
    uint32_t buffer_used;
33
@@ -307,7 +312,7 @@
34
    bool following;
35
 
36
    if (this->position != info->position || this->clock != info->clock)
37
-       this->resync = true;
38
+       this->resync = RESYNC_CYCLES;
39
 
40
    this->position = info->position;
41
    this->clock = info->clock;
42
@@ -460,6 +465,7 @@
43
 {
44
    struct port *port = &this->port;
45
    uint64_t t, duration_ns;
46
+   bool resampling;
47
 
48
    if (!this->process_rate || !this->process_duration) {
49
        if (this->position) {
50
@@ -481,9 +487,12 @@
51
            / port->current_format.info.raw.rate);
52
 
53
    /* Account for resampling delay */
54
-   if (port->rate_match && this->clock && SPA_FLAG_IS_SET(port->rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE))
55
+   resampling = (port->current_format.info.raw.rate != this->process_rate) || this->following;
56
+   if (port->rate_match && this->clock && resampling) {
57
        t -= (uint64_t)port->rate_match->delay * SPA_NSEC_PER_SEC
58
            / this->clock->rate.denom;
59
+       t += SPA_NSEC_PER_SEC / port->current_format.info.raw.rate;
60
+   }
61
 
62
    return t;
63
 }
64
@@ -715,6 +724,9 @@
65
    if (!this->flush_timer_source.loop && !this->transport->iso_io)
66
        return -EIO;
67
 
68
+   if (this->transport->iso_io && !this->iso_pending)
69
+       return 0;
70
+
71
    total_frames = 0;
72
 again:
73
    written = 0;
74
@@ -787,7 +799,7 @@
75
    if (this->transport->iso_io) {
76
        struct spa_bt_iso_io *iso_io = this->transport->iso_io;
77
 
78
-       if (this->need_flush && !this->have_iso_packet) {
79
+       if (this->need_flush) {
80
            size_t avail = SPA_MIN(this->buffer_used, sizeof(iso_io->buf));
81
 
82
            spa_log_trace(this->log, "%p: ISO put fd:%d size:%u sn:%u ts:%u now:%"PRIu64,
83
@@ -798,7 +810,7 @@
84
            memcpy(iso_io->buf, this->buffer, avail);
85
            iso_io->size = avail;
86
            iso_io->timestamp = this->timestamp;
87
-           this->have_iso_packet = true;
88
+           this->iso_pending = false;
89
 
90
            reset_buffer(this);
91
        }
92
@@ -953,9 +965,7 @@
93
    struct port *port = &this->port;
94
    const double period = 0.1 * SPA_NSEC_PER_SEC;
95
    uint64_t duration_ns;
96
-   double value, target, err;
97
-
98
-   this->have_iso_packet = false;
99
+   double value, target, err, max_err;
100
 
101
    if (this->resync || !this->position) {
102
        spa_bt_rate_control_init(&port->ratectl, 0);
103
@@ -978,19 +988,28 @@
104
    value = (int64_t)iso_io->now - (int64_t)get_reference_time(this, &duration_ns);
105
    target = iso_io->duration * 3/2;
106
    err = value - target;
107
+   max_err = iso_io->duration;
108
 
109
-   if (err > iso_io->duration) {
110
-       uint32_t req = err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC;
111
-
112
-       spa_log_debug(this->log, "%p: ISO sync reset frames:%u", this, (unsigned int)req);
113
-
114
-       spa_bt_rate_control_init(&port->ratectl, 0);
115
-       drop_frames(this, req);
116
-   } else if (-err > iso_io->duration) {
117
-       uint32_t req = -err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC;
118
+   if (err > max_err || (iso_io->resync && err > 0)) {
119
+       unsigned int req = err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC;
120
 
121
-       spa_log_debug(this->log, "%p: ISO sync skip flush frames:%u", this, (unsigned int)req);
122
-       return;
123
+       if (req > 0) {
124
+           spa_bt_rate_control_init(&port->ratectl, 0);
125
+           drop_frames(this, req);
126
+           spa_log_debug(this->log, "%p: ISO sync skip frames:%u resync:%d",
127
+                   this, req, iso_io->resync);
128
+       }
129
+   } else if (-err > max_err || (iso_io->resync && -err > 0)) {
130
+       unsigned int req = -err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC;
131
+       static const uint8_t empty8192 = {0};
132
+
133
+       if (req > 0) {
134
+           spa_bt_rate_control_init(&port->ratectl, 0);
135
+           req = SPA_MIN(req, sizeof(empty) / port->frame_size);
136
+           add_data(this, empty, req * port->frame_size);
137
+           spa_log_debug(this->log, "%p: ISO sync pad frames:%u resync:%d",
138
+                   this, req, iso_io->resync);
139
+       }
140
    } else {
141
        spa_bt_rate_control_update(&port->ratectl, err, 0,
142
                iso_io->duration, period, RATE_CTL_DIFF_MAX);
143
@@ -1002,7 +1021,10 @@
144
                port->ratectl.corr);
145
    }
146
 
147
+   iso_io->resync = false;
148
+
149
 done:
150
+   this->iso_pending = true;
151
    flush_data(this, this->current_time);
152
 }
153
 
154
@@ -1215,9 +1237,9 @@
155
    this->flush_source.rmask = 0;
156
    spa_loop_add_source(this->data_loop, &this->flush_source);
157
 
158
-   this->resync = true;
159
-
160
+   this->resync = RESYNC_CYCLES;
161
    this->flush_pending = false;
162
+   this->iso_pending = false;
163
 
164
    this->transport_started = true;
165
 
166
@@ -1375,7 +1397,8 @@
167
 {
168
    struct spa_dict_item node_info_items = {
169
        { SPA_KEY_DEVICE_API, "bluez5" },
170
-       { SPA_KEY_MEDIA_CLASS, this->is_output ? "Audio/Sink" : "Stream/Input/Audio" },
171
+       { SPA_KEY_MEDIA_CLASS, this->is_internal ? "Audio/Sink/Internal" :
172
+         this->is_output ? "Audio/Sink" : "Stream/Input/Audio" },
173
        { "media.name", ((this->transport && this->transport->device->name) ?
174
                    this->transport->device->name : this->codec->bap ? "BAP" : "A2DP" ) },
175
        { SPA_KEY_NODE_DRIVER, this->is_output ? "true" : "false" },
176
@@ -1796,6 +1819,8 @@
177
 
178
    if (io->status == SPA_STATUS_HAVE_DATA && io->buffer_id < port->n_buffers) {
179
        struct buffer *b = &port->buffersio->buffer_id;
180
+       struct spa_data *d = b->buf->datas;
181
+       unsigned int frames;
182
 
183
        if (!SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) {
184
            spa_log_warn(this->log, "%p: buffer %u in use", this, io->buffer_id);
185
@@ -1803,7 +1828,8 @@
186
            return -EINVAL;
187
        }
188
 
189
-       spa_log_trace(this->log, "%p: queue buffer %u", this, io->buffer_id);
190
+       frames = d ? d0.chunk->size / port->frame_size : 0;
191
+       spa_log_trace(this->log, "%p: queue buffer %u frames:%u", this, io->buffer_id, frames);
192
 
193
        spa_list_append(&port->ready, &b->link);
194
        SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
195
@@ -1831,7 +1857,8 @@
196
    }
197
 
198
    this->process_time = this->current_time;
199
-   this->resync = false;
200
+   if (this->resync)
201
+       --this->resync;
202
 
203
    setup_matching(this);
204
 
205
@@ -2056,6 +2083,9 @@
206
    if (info && (str = spa_dict_lookup(info, "api.bluez5.a2dp-duplex")) != NULL)
207
        this->is_duplex = spa_atob(str);
208
 
209
+   if (info && (str = spa_dict_lookup(info, "api.bluez5.internal")) != NULL)
210
+       this->is_internal = spa_atob(str);
211
+
212
    if (info && (str = spa_dict_lookup(info, SPA_KEY_API_BLUEZ5_TRANSPORT)))
213
        sscanf(str, "pointer:%p", &this->transport);
214
 
215
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/media-source.c -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/media-source.c Changed
28
 
1
@@ -122,6 +122,7 @@
2
 
3
    unsigned int is_input:1;
4
    unsigned int is_duplex:1;
5
+   unsigned int is_internal:1;
6
    unsigned int use_duplex_source:1;
7
 
8
    unsigned int node_latency;
9
@@ -901,7 +902,8 @@
10
 
11
    struct spa_dict_item node_info_items = {
12
        { SPA_KEY_DEVICE_API, "bluez5" },
13
-       { SPA_KEY_MEDIA_CLASS, this->is_input ? "Audio/Source" : "Stream/Output/Audio" },
14
+       { SPA_KEY_MEDIA_CLASS, this->is_internal ? "Audio/Source/Internal" :
15
+         this->is_input ? "Audio/Source" : "Stream/Output/Audio" },
16
        { SPA_KEY_NODE_LATENCY, this->is_input ? "" : latency },
17
        { "media.name", ((this->transport && this->transport->device->name) ?
18
                    this->transport->device->name : this->codec->bap ? "BAP" : "A2DP") },
19
@@ -1738,6 +1740,8 @@
20
            this->is_input = spa_streq(str, "input");
21
        if ((str = spa_dict_lookup(info, "api.bluez5.a2dp-duplex")) != NULL)
22
            this->is_duplex = spa_atob(str);
23
+       if ((str = spa_dict_lookup(info, "api.bluez5.internal")) != NULL)
24
+           this->is_internal = spa_atob(str);
25
    }
26
 
27
    if (this->transport == NULL) {
28
pipewire-0.3.68.tar.gz/src/gst/gstpipewireclock.c -> pipewire-0.3.69.tar.gz/src/gst/gstpipewireclock.c Changed
15
 
1
@@ -91,6 +91,7 @@
2
 void
3
 gst_pipewire_clock_reset (GstPipeWireClock * clock, GstClockTime time)
4
 {
5
+#if 0
6
   GstClockTimeDiff time_offset;
7
 
8
   if (clock->last_time >= time)
9
@@ -104,4 +105,5 @@
10
       "reset clock to %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT
11
       ", offset %" GST_STIME_FORMAT, GST_TIME_ARGS (time),
12
       GST_TIME_ARGS (clock->last_time), GST_STIME_ARGS (time_offset));
13
+#endif
14
 }
15
pipewire-0.3.68.tar.gz/src/modules/module-echo-cancel.c -> pipewire-0.3.69.tar.gz/src/modules/module-echo-cancel.c Changed
477
 
1
@@ -183,7 +183,9 @@
2
    struct spa_hook core_proxy_listener;
3
    struct spa_hook core_listener;
4
 
5
-   struct spa_audio_info_raw info;
6
+   struct spa_audio_info_raw rec_info;
7
+   struct spa_audio_info_raw out_info;
8
+   struct spa_audio_info_raw play_info;
9
 
10
    struct pw_properties *capture_props;
11
    struct pw_stream *capture;
12
@@ -247,10 +249,13 @@
13
        if (impl->wav_file == NULL) {
14
            struct wav_file_info info;
15
 
16
-           info.info.info.raw = impl->info;
17
-           info.info.info.raw.channels *= 3;
18
+           spa_zero(info);
19
            info.info.media_type = SPA_MEDIA_TYPE_audio;
20
            info.info.media_subtype = SPA_MEDIA_SUBTYPE_raw;
21
+           info.info.info.raw.format = SPA_AUDIO_FORMAT_F32P;
22
+           info.info.info.raw.rate = impl->rec_info.rate;
23
+           info.info.info.raw.channels = impl->play_info.channels +
24
+               impl->rec_info.channels + impl->out_info.channels;
25
 
26
            impl->wav_file = wav_file_open(impl->wav_path,
27
                    "w", &info);
28
@@ -259,14 +264,17 @@
29
                        impl->wav_path);
30
        }
31
        if (impl->wav_file) {
32
-           uint32_t i, c = impl->info.channels;
33
-           const float *datac * 3;
34
+           uint32_t i, n, c = impl->play_info.channels +
35
+               impl->rec_info.channels + impl->out_info.channels;
36
+           const float *datac;
37
+
38
+           for (i = n = 0; i < impl->play_info.channels; i++)
39
+               datan++ = playi;
40
+           for (i = 0; i < impl->rec_info.channels; i++)
41
+               datan++ = reci;
42
+           for (i = 0; i < impl->out_info.channels; i++)
43
+               datan++ = outi;
44
 
45
-           for (i = 0; i < c; i++) {
46
-               datai       = playi;
47
-               datai +   c = reci;
48
-               datai + 2*c = outi;
49
-           }
50
            wav_file_write(impl->wav_file, (void*)data, n_samples);
51
        } else {
52
            spa_zero(impl->wav_path);
53
@@ -281,18 +289,17 @@
54
 {
55
    struct pw_buffer *cout;
56
    struct pw_buffer *pout = NULL;
57
-   float rec_bufimpl->info.channelsimpl->aec_blocksize / sizeof(float);
58
-   float play_bufimpl->info.channelsimpl->aec_blocksize / sizeof(float);
59
-   float play_delayed_bufimpl->info.channelsimpl->aec_blocksize / sizeof(float);
60
-   float out_bufimpl->info.channelsimpl->aec_blocksize / sizeof(float);
61
-   const float *recimpl->info.channels;
62
-   const float *playimpl->info.channels;
63
-   const float *play_delayedimpl->info.channels;
64
-   float *outimpl->info.channels;
65
+   float rec_bufimpl->rec_info.channelsimpl->aec_blocksize / sizeof(float);
66
+   float play_bufimpl->play_info.channelsimpl->aec_blocksize / sizeof(float);
67
+   float play_delayed_bufimpl->play_info.channelsimpl->aec_blocksize / sizeof(float);
68
+   float out_bufimpl->out_info.channelsimpl->aec_blocksize / sizeof(float);
69
+   const float *recimpl->rec_info.channels;
70
+   const float *playimpl->play_info.channels;
71
+   const float *play_delayedimpl->play_info.channels;
72
+   float *outimpl->out_info.channels;
73
    struct spa_data *dd;
74
    uint32_t i, size;
75
    uint32_t rindex, pindex, oindex, pdindex, avail;
76
-   int32_t stride = 0;
77
 
78
    if (impl->playback != NULL && (pout = pw_stream_dequeue_buffer(impl->playback)) == NULL) {
79
        pw_log_debug("out of playback buffers: %m");
80
@@ -302,32 +309,37 @@
81
    size = impl->aec_blocksize;
82
 
83
    /* First read a block from the playback and capture ring buffers */
84
-
85
    spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex);
86
-   spa_ringbuffer_get_read_index(&impl->play_ring, &pindex);
87
-   spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex);
88
 
89
-   for (i = 0; i < impl->info.channels; i++) {
90
+   for (i = 0; i < impl->rec_info.channels; i++) {
91
        /* captured samples, with echo from sink */
92
        reci = &rec_bufi0;
93
-       /* echo from sink */
94
-       playi = &play_bufi0;
95
-       /* echo from sink delayed */
96
-       play_delayedi = &play_delayed_bufi0;
97
-       /* filtered samples, without echo from sink */
98
-       outi = &out_bufi0;
99
 
100
-       stride = 0;
101
        spa_ringbuffer_read_data(&impl->rec_ring, impl->rec_bufferi,
102
                impl->rec_ringsize, rindex % impl->rec_ringsize,
103
                (void*)reci, size);
104
 
105
-       stride = 0;
106
+   }
107
+   spa_ringbuffer_read_update(&impl->rec_ring, rindex + size);
108
+
109
+   for (i = 0; i < impl->out_info.channels; i++) {
110
+       /* filtered samples, without echo from sink */
111
+       outi = &out_bufi0;
112
+   }
113
+
114
+   spa_ringbuffer_get_read_index(&impl->play_ring, &pindex);
115
+   spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex);
116
+
117
+   for (i = 0; i < impl->play_info.channels; i++) {
118
+       /* echo from sink */
119
+       playi = &play_bufi0;
120
+       /* echo from sink delayed */
121
+       play_delayedi = &play_delayed_bufi0;
122
+
123
        spa_ringbuffer_read_data(&impl->play_ring, impl->play_bufferi,
124
                impl->play_ringsize, pindex % impl->play_ringsize,
125
                (void *)playi, size);
126
 
127
-       stride = 0;
128
        spa_ringbuffer_read_data(&impl->play_delayed_ring, impl->play_bufferi,
129
                impl->play_ringsize, pdindex % impl->play_ringsize,
130
                (void *)play_delayedi, size);
131
@@ -339,11 +351,9 @@
132
 
133
            dd->chunk->offset = 0;
134
            dd->chunk->size = size;
135
-           dd->chunk->stride = stride;
136
+           dd->chunk->stride = sizeof(float);
137
        }
138
    }
139
-
140
-   spa_ringbuffer_read_update(&impl->rec_ring, rindex + size);
141
    spa_ringbuffer_read_update(&impl->play_ring, pindex + size);
142
    spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + size);
143
 
144
@@ -357,19 +367,20 @@
145
        /* don't run the canceller until play_buffer has been filled,
146
         * copy silence to output in the meantime */
147
        silence_size = SPA_MIN(size, delay_left * sizeof(float));
148
-       for (i = 0; i < impl->info.channels; i++)
149
+       for (i = 0; i < impl->out_info.channels; i++)
150
            memset(outi, 0, silence_size);
151
        impl->current_delay += silence_size / sizeof(float);
152
        pw_log_debug("current_delay %d", impl->current_delay);
153
 
154
        if (silence_size != size) {
155
-           const float *pdimpl->info.channels;
156
-           float *oimpl->info.channels;
157
+           const float *pdimpl->play_info.channels;
158
+           float *oimpl->out_info.channels;
159
 
160
-           for (i = 0; i < impl->info.channels; i++) {
161
+           for (i = 0; i < impl->play_info.channels; i++)
162
                pdi = play_delayedi + delay_left;
163
+           for (i = 0; i < impl->out_info.channels; i++)
164
                oi = outi + delay_left;
165
-           }
166
+
167
            aec_run(impl, rec, pd, o, size / sizeof(float) - delay_left);
168
        }
169
    } else {
170
@@ -393,7 +404,7 @@
171
        avail += drop;
172
    }
173
 
174
-   for (i = 0; i < impl->info.channels; i++) {
175
+   for (i = 0; i < impl->out_info.channels; i++) {
176
        /* captured samples, with echo from sink */
177
        spa_ringbuffer_write_data(&impl->out_ring, impl->out_bufferi,
178
                impl->out_ringsize, oindex % impl->out_ringsize,
179
@@ -412,14 +423,14 @@
180
            break;
181
        }
182
 
183
-       for (i = 0; i < impl->info.channels; i++) {
184
+       for (i = 0; i < impl->out_info.channels; i++) {
185
            dd = &cout->buffer->datasi;
186
            spa_ringbuffer_read_data(&impl->out_ring, impl->out_bufferi,
187
                    impl->out_ringsize, oindex % impl->out_ringsize,
188
                    (void *)dd->data, size);
189
            dd->chunk->offset = 0;
190
            dd->chunk->size = size;
191
-           dd->chunk->stride = 0;
192
+           dd->chunk->stride = sizeof(float);
193
        }
194
 
195
        pw_stream_queue_buffer(impl->source, cout);
196
@@ -482,7 +493,7 @@
197
        pw_log_debug("Setting AEC block size to %u", impl->aec_blocksize);
198
    }
199
 
200
-   for (i = 0; i < impl->info.channels; i++) {
201
+   for (i = 0; i < impl->rec_info.channels; i++) {
202
        /* captured samples, with echo from sink */
203
        d = &buf->buffer->datasi;
204
 
205
@@ -573,11 +584,12 @@
206
    spa_ringbuffer_init(&impl->play_delayed_ring);
207
    spa_ringbuffer_init(&impl->out_ring);
208
 
209
-   for (i = 0; i < impl->info.channels; i++) {
210
+   for (i = 0; i < impl->rec_info.channels; i++)
211
        memset(impl->rec_bufferi, 0, impl->rec_ringsize);
212
+   for (i = 0; i < impl->play_info.channels; i++)
213
        memset(impl->play_bufferi, 0, impl->play_ringsize);
214
+   for (i = 0; i < impl->out_info.channels; i++)
215
        memset(impl->out_bufferi, 0, impl->out_ringsize);
216
-   }
217
 
218
    spa_ringbuffer_get_write_index(&impl->play_ring, &index);
219
    spa_ringbuffer_write_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay)));
220
@@ -856,7 +868,7 @@
221
        pw_log_debug("Setting AEC block size to %u", impl->aec_blocksize);
222
    }
223
 
224
-   for (i = 0; i < impl->info.channels; i++) {
225
+   for (i = 0; i < impl->play_info.channels; i++) {
226
        /* echo from sink */
227
        d = &buf->buffer->datasi;
228
 
229
@@ -1034,14 +1046,15 @@
230
 
231
    spa_pod_dynamic_builder_clean(&b);
232
 
233
-   impl->rec_ringsize = sizeof(float) * impl->max_buffer_size * impl->info.rate / 1000;
234
-   impl->play_ringsize = sizeof(float) * ((impl->max_buffer_size * impl->info.rate / 1000) + impl->buffer_delay);
235
-   impl->out_ringsize = sizeof(float) * impl->max_buffer_size * impl->info.rate / 1000;
236
-   for (i = 0; i < impl->info.channels; i++) {
237
+   impl->rec_ringsize = sizeof(float) * impl->max_buffer_size * impl->rec_info.rate / 1000;
238
+   impl->play_ringsize = sizeof(float) * ((impl->max_buffer_size * impl->play_info.rate / 1000) + impl->buffer_delay);
239
+   impl->out_ringsize = sizeof(float) * impl->max_buffer_size * impl->out_info.rate / 1000;
240
+   for (i = 0; i < impl->rec_info.channels; i++)
241
        impl->rec_bufferi = malloc(impl->rec_ringsize);
242
+   for (i = 0; i < impl->play_info.channels; i++)
243
        impl->play_bufferi = malloc(impl->play_ringsize);
244
+   for (i = 0; i < impl->out_info.channels; i++)
245
        impl->out_bufferi = malloc(impl->out_ringsize);
246
-   }
247
 
248
    reset_buffers(impl);
249
 
250
@@ -1101,14 +1114,12 @@
251
    pw_properties_free(impl->playback_props);
252
    pw_properties_free(impl->sink_props);
253
 
254
-   for (i = 0; i < impl->info.channels; i++) {
255
-       if (impl->rec_bufferi)
256
-           free(impl->rec_bufferi);
257
-       if (impl->play_bufferi)
258
-           free(impl->play_bufferi);
259
-       if (impl->out_bufferi)
260
-           free(impl->out_bufferi);
261
-   }
262
+   for (i = 0; i < impl->rec_info.channels; i++)
263
+       free(impl->rec_bufferi);
264
+   for (i = 0; i < impl->play_info.channels; i++)
265
+       free(impl->play_bufferi);
266
+   for (i = 0; i < impl->out_info.channels; i++)
267
+       free(impl->out_bufferi);
268
 
269
    free(impl);
270
 }
271
@@ -1189,6 +1200,7 @@
272
 {
273
    struct pw_context *context = pw_impl_module_get_context(module);
274
    struct pw_properties *props, *aec_props;
275
+   struct spa_audio_info_raw info;
276
    struct impl *impl;
277
    uint32_t id = pw_global_get_id(pw_impl_module_get_global(module));
278
    uint32_t pid = getpid();
279
@@ -1243,12 +1255,12 @@
280
    if (pw_properties_get(props, "resample.prefill") == NULL)
281
        pw_properties_set(props, "resample.prefill", "true");
282
 
283
-   parse_audio_info(props, &impl->info);
284
+   parse_audio_info(props, &info);
285
 
286
-   impl->capture_info = impl->info;
287
-   impl->source_info = impl->info;
288
-   impl->sink_info = impl->info;
289
-   impl->playback_info = impl->info;
290
+   impl->capture_info = info;
291
+   impl->source_info = info;
292
+   impl->sink_info = info;
293
+   impl->playback_info = info;
294
 
295
    if ((str = pw_properties_get(props, "capture.props")) != NULL)
296
        pw_properties_update_string(impl->capture_props, str, strlen(str));
297
@@ -1296,6 +1308,48 @@
298
            pw_properties_set(impl->sink_props, PW_KEY_STREAM_CAPTURE_SINK, "true");
299
    }
300
 
301
+   copy_props(impl, props, PW_KEY_NODE_GROUP);
302
+   copy_props(impl, props, PW_KEY_NODE_LINK_GROUP);
303
+   copy_props(impl, props, PW_KEY_NODE_VIRTUAL);
304
+   copy_props(impl, props, SPA_KEY_AUDIO_CHANNELS);
305
+   copy_props(impl, props, SPA_KEY_AUDIO_POSITION);
306
+   copy_props(impl, props, "resample.prefill");
307
+
308
+   impl->max_buffer_size = pw_properties_get_uint32(props,"buffer.max_size", MAX_BUFSIZE_MS);
309
+
310
+   if ((str = pw_properties_get(props, "buffer.play_delay")) != NULL) {
311
+       int req_num, req_denom;
312
+       if (sscanf(str, "%u/%u", &req_num, &req_denom) == 2) {
313
+           if (req_denom != 0) {
314
+               impl->buffer_delay = (info.rate * req_num) / req_denom;
315
+           } else {
316
+               impl->buffer_delay = DELAY_MS * info.rate / 1000;
317
+               pw_log_warn("Sample rate for buffer.play_delay is 0 using default");
318
+           }
319
+       } else {
320
+           impl->buffer_delay = DELAY_MS * info.rate / 1000;
321
+           pw_log_warn("Wrong value/format for buffer.play_delay using default");
322
+       }
323
+   } else {
324
+       impl->buffer_delay = DELAY_MS * info.rate / 1000;
325
+   }
326
+
327
+   if ((str = pw_properties_get(impl->capture_props, SPA_KEY_AUDIO_POSITION)) != NULL) {
328
+       parse_position(&impl->capture_info, str, strlen(str));
329
+   }
330
+   if ((str = pw_properties_get(impl->source_props, SPA_KEY_AUDIO_POSITION)) != NULL) {
331
+       parse_position(&impl->source_info, str, strlen(str));
332
+   }
333
+   if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_POSITION)) != NULL) {
334
+       parse_position(&impl->sink_info, str, strlen(str));
335
+       impl->playback_info = impl->sink_info;
336
+   }
337
+   if ((str = pw_properties_get(impl->playback_props, SPA_KEY_AUDIO_POSITION)) != NULL) {
338
+       parse_position(&impl->playback_info, str, strlen(str));
339
+       if (impl->playback_info.channels != impl->sink_info.channels)
340
+           impl->playback_info = impl->sink_info;
341
+   }
342
+
343
    if ((str = pw_properties_get(props, "aec.method")) != NULL)
344
        pw_log_warn("aec.method is not supported anymore use library.name");
345
 
346
@@ -1313,12 +1367,12 @@
347
        return -EINVAL;
348
    }
349
 
350
-   struct spa_dict_item info_items = {
351
+   struct spa_dict_item dict_items = {
352
        { SPA_KEY_LIBRARY_NAME, path },
353
    };
354
-   struct spa_dict info = SPA_DICT_INIT_ARRAY(info_items);
355
+   struct spa_dict dict = SPA_DICT_INIT_ARRAY(dict_items);
356
 
357
-   handle = spa_plugin_loader_load(impl->loader, SPA_NAME_AEC, &info);
358
+   handle = spa_plugin_loader_load(impl->loader, SPA_NAME_AEC, &dict);
359
    if (handle == NULL) {
360
        pw_log_error("aec plugin %s not available library.name %s", SPA_NAME_AEC, path);
361
        return -ENOENT;
362
@@ -1346,7 +1400,39 @@
363
    else
364
        aec_props = pw_properties_new(NULL, NULL);
365
 
366
-   res = spa_audio_aec_init(impl->aec, &aec_props->dict, &impl->info);
367
+
368
+   if (spa_interface_callback_check(&impl->aec->iface, struct spa_audio_aec_methods, init2, 3)) {
369
+       impl->rec_info = impl->capture_info;
370
+       impl->out_info = impl->source_info;
371
+       impl->play_info = impl->sink_info;
372
+
373
+       res = spa_audio_aec_init2(impl->aec, &aec_props->dict,
374
+               &impl->rec_info, &impl->out_info, &impl->play_info);
375
+
376
+       if (impl->sink_info.channels != impl->play_info.channels)
377
+           impl->sink_info = impl->play_info;
378
+       if (impl->playback_info.channels != impl->play_info.channels)
379
+           impl->playback_info = impl->play_info;
380
+       if (impl->capture_info.channels != impl->rec_info.channels)
381
+           impl->capture_info = impl->rec_info;
382
+       if (impl->source_info.channels != impl->out_info.channels)
383
+           impl->source_info = impl->out_info;
384
+   } else {
385
+       if (impl->source_info.channels != impl->sink_info.channels)
386
+           impl->source_info = impl->sink_info;
387
+       if (impl->capture_info.channels != impl->source_info.channels)
388
+           impl->capture_info = impl->source_info;
389
+       if (impl->playback_info.channels != impl->sink_info.channels)
390
+           impl->playback_info = impl->sink_info;
391
+
392
+       info = impl->playback_info;
393
+
394
+       res = spa_audio_aec_init(impl->aec, &aec_props->dict, &info);
395
+
396
+       impl->rec_info.channels = info.channels;
397
+       impl->out_info.channels = info.channels;
398
+       impl->play_info.channels = info.channels;
399
+   }
400
 
401
    pw_properties_free(aec_props);
402
 
403
@@ -1372,17 +1458,19 @@
404
        if (factor == 0 || new_num == 0) {
405
            pw_log_info("Setting node latency to %s", impl->aec->latency);
406
            pw_properties_set(props, PW_KEY_NODE_LATENCY, impl->aec->latency);
407
-           impl->aec_blocksize = sizeof(float) * impl->info.rate * num / denom;
408
+           impl->aec_blocksize = sizeof(float) * info.rate * num / denom;
409
        } else {
410
            pw_log_info("Setting node latency to %u/%u", new_num, req_denom);
411
            pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", new_num, req_denom);
412
-           impl->aec_blocksize = sizeof(float) * impl->info.rate * num / denom * factor;
413
+           impl->aec_blocksize = sizeof(float) * info.rate * num / denom * factor;
414
        }
415
    } else {
416
        /* Implementation doesn't care about the block size */
417
        impl->aec_blocksize = 0;
418
    }
419
 
420
+   copy_props(impl, props, PW_KEY_NODE_LATENCY);
421
+
422
    impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core);
423
    if (impl->core == NULL) {
424
        str = pw_properties_get(props, PW_KEY_REMOTE_NAME);
425
@@ -1399,51 +1487,6 @@
426
        goto error;
427
    }
428
 
429
-   copy_props(impl, props, PW_KEY_NODE_GROUP);
430
-   copy_props(impl, props, PW_KEY_NODE_LINK_GROUP);
431
-   copy_props(impl, props, PW_KEY_NODE_VIRTUAL);
432
-   copy_props(impl, props, PW_KEY_NODE_LATENCY);
433
-   copy_props(impl, props, SPA_KEY_AUDIO_CHANNELS);
434
-   copy_props(impl, props, SPA_KEY_AUDIO_POSITION);
435
-   copy_props(impl, props, "resample.prefill");
436
-
437
-   if ((str = pw_properties_get(impl->capture_props, SPA_KEY_AUDIO_POSITION)) != NULL)
438
-       parse_position(&impl->capture_info, str, strlen(str));
439
-   if ((str = pw_properties_get(impl->source_props, SPA_KEY_AUDIO_POSITION)) != NULL)
440
-       parse_position(&impl->source_info, str, strlen(str));
441
-   if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_POSITION)) != NULL)
442
-       parse_position(&impl->sink_info, str, strlen(str));
443
-   if ((str = pw_properties_get(impl->playback_props, SPA_KEY_AUDIO_POSITION)) != NULL)
444
-       parse_position(&impl->playback_info, str, strlen(str));
445
-
446
-   if (impl->capture_info.channels != impl->info.channels)
447
-       impl->capture_info = impl->info;
448
-   if (impl->source_info.channels != impl->info.channels)
449
-       impl->source_info = impl->info;
450
-   if (impl->sink_info.channels != impl->info.channels)
451
-       impl->sink_info = impl->info;
452
-   if (impl->playback_info.channels != impl->info.channels)
453
-       impl->playback_info = impl->info;
454
-
455
-   impl->max_buffer_size = pw_properties_get_uint32(props,"buffer.max_size", MAX_BUFSIZE_MS);
456
-
457
-   if ((str = pw_properties_get(props, "buffer.play_delay")) != NULL) {
458
-       int req_num, req_denom;
459
-       if (sscanf(str, "%u/%u", &req_num, &req_denom) == 2) {
460
-           if (req_denom != 0) {
461
-               impl->buffer_delay = (impl->info.rate*req_num)/req_denom;
462
-           } else {
463
-               impl->buffer_delay = DELAY_MS * impl->info.rate / 1000;
464
-               pw_log_warn("Sample rate for buffer.play_delay is 0 using default");
465
-           }
466
-       } else {
467
-           impl->buffer_delay = DELAY_MS * impl->info.rate / 1000;
468
-           pw_log_warn("Wrong value/format for buffer.play_delay using default");
469
-       }
470
-   } else {
471
-       impl->buffer_delay = DELAY_MS * impl->info.rate / 1000;
472
-   }
473
-
474
    pw_properties_free(props);
475
 
476
    pw_proxy_add_listener((struct pw_proxy*)impl->core,
477
pipewire-0.3.68.tar.gz/src/modules/module-filter-chain.c -> pipewire-0.3.69.tar.gz/src/modules/module-filter-chain.c Changed
95
 
1
@@ -162,7 +162,11 @@
2
  *
3
  * All biquad filters have an input port "In" and an output port "Out". They have
4
  * a "Freq", "Q" and "Gain" control. Their meaning depends on the particular biquad that
5
- * is used. The following labels can be used:
6
+ * is used. The biquads also have "b0", "b1", "b2", "a0", "a1" and "a2" ports that
7
+ * are read-only except for the bq_raw biquad, which can configure default values
8
+ * depending on the graph rate and change those at runtime.
9
+ *
10
+ * The following labels can be used:
11
  *
12
  * - `bq_lowpass` a lowpass filter.
13
  * - `bq_highpass` a highpass filter.
14
@@ -172,6 +176,30 @@
15
  * - `bq_peaking` a peaking filter.
16
  * - `bq_notch` a notch filter.
17
  * - `bq_allpass` an allpass filter.
18
+ * - `bq_raw` a raw biquad filter. You need a config section to specify coefficients
19
+ *         per sample rate. The coefficients of the sample rate closest to the
20
+ *         graph rate are selected:
21
+ *
22
+ *\code{.unparsed}
23
+ * filter.graph = {
24
+ *     nodes = 
25
+ *         {
26
+ *             type   = builtin
27
+ *             name   = ...
28
+ *             label  = bq_raw
29
+ *             config = {
30
+ *                 coefficients = 
31
+ *                     { rate =  44100, b0=.., b1=.., b2=.., a0=.., a1=.., a2=.. },
32
+ *                     { rate =  48000, b0=.., b1=.., b2=.., a0=.., a1=.., a2=.. },
33
+ *                     { rate = 192000, b0=.., b1=.., b2=.., a0=.., a1=.., a2=.. }
34
+ *                 
35
+ *             }
36
+ *             ...
37
+ *         }
38
+ *     }
39
+ *     ...
40
+ * }
41
+ *\endcode
42
  *
43
  * ### Convolver
44
  *
45
@@ -1006,6 +1034,20 @@
46
    node->control_changed = false;
47
 }
48
 
49
+static void update_props_param(struct impl *impl)
50
+{
51
+   struct graph *graph = &impl->graph;
52
+   uint8_t buffer1024;
53
+   struct spa_pod_dynamic_builder b;
54
+   const struct spa_pod *params1;
55
+
56
+   spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
57
+   params0 = get_props_param(graph, &b.b);
58
+
59
+   pw_stream_update_params(impl->capture, params, 1);
60
+   spa_pod_dynamic_builder_clean(&b);
61
+}
62
+
63
 static void param_props_changed(struct impl *impl, const struct spa_pod *param)
64
 {
65
    struct spa_pod_object *obj = (struct spa_pod_object *) param;
66
@@ -1018,19 +1060,12 @@
67
            changed += parse_params(graph, &prop->value);
68
    }
69
    if (changed > 0) {
70
-       uint8_t buffer1024;
71
-       struct spa_pod_dynamic_builder b;
72
-       const struct spa_pod *params1;
73
        struct node *node;
74
 
75
        spa_list_for_each(node, &graph->node_list, link)
76
            node_control_changed(node);
77
 
78
-       spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
79
-       params0 = get_props_param(graph, &b.b);
80
-
81
-       pw_stream_update_params(impl->capture, params, 1);
82
-       spa_pod_dynamic_builder_clean(&b);
83
+       update_props_param(impl);
84
    }
85
 }
86
 
87
@@ -1847,6 +1882,7 @@
88
                d->control_changed(node->hndli);
89
        }
90
    }
91
+   update_props_param(impl);
92
    return 0;
93
 error:
94
    graph_cleanup(graph);
95
pipewire-0.3.68.tar.gz/src/modules/module-filter-chain/builtin_plugin.c -> pipewire-0.3.69.tar.gz/src/modules/module-filter-chain/builtin_plugin.c Changed
500
 
1
@@ -32,10 +32,13 @@
2
    unsigned long rate;
3
    float *port64;
4
 
5
+   int type;
6
    struct biquad bq;
7
    float freq;
8
    float Q;
9
    float gain;
10
+   float b0, b1, b2;
11
+   float a0, a1, a2;
12
 };
13
 
14
 static void *builtin_instantiate(const struct fc_descriptor * Descriptor,
15
@@ -215,6 +218,159 @@
16
    .cleanup = builtin_cleanup,
17
 };
18
 
19
+/** biquads */
20
+static int bq_type_from_name(const char *name)
21
+{
22
+   if (spa_streq(name, "bq_lowpass"))
23
+       return BQ_LOWPASS;
24
+   if (spa_streq(name, "bq_highpass"))
25
+       return BQ_HIGHPASS;
26
+   if (spa_streq(name, "bq_bandpass"))
27
+       return BQ_BANDPASS;
28
+   if (spa_streq(name, "bq_lowshelf"))
29
+       return BQ_LOWSHELF;
30
+   if (spa_streq(name, "bq_highshelf"))
31
+       return BQ_HIGHSHELF;
32
+   if (spa_streq(name, "bq_peaking"))
33
+       return BQ_PEAKING;
34
+   if (spa_streq(name, "bq_notch"))
35
+       return BQ_NOTCH;
36
+   if (spa_streq(name, "bq_allpass"))
37
+       return BQ_ALLPASS;
38
+   if (spa_streq(name, "bq_raw"))
39
+       return BQ_NONE;
40
+   return BQ_NONE;
41
+}
42
+
43
+static void bq_raw_update(struct builtin *impl, float b0, float b1, float b2,
44
+       float a0, float a1, float a2)
45
+{
46
+   struct biquad *bq = &impl->bq;
47
+   impl->b0 = b0;
48
+   impl->b1 = b1;
49
+   impl->b2 = b2;
50
+   impl->a0 = a0;
51
+   impl->a1 = a1;
52
+   impl->a2 = a2;
53
+   if (a0 != 0.0f)
54
+       a0 = 1.0f / a0;
55
+   bq->b0 = impl->b0 * a0;
56
+   bq->b1 = impl->b1 * a0;
57
+   bq->b2 = impl->b2 * a0;
58
+   bq->a1 = impl->a1 * a0;
59
+   bq->a2 = impl->a2 * a0;
60
+   bq->x1 = bq->x2 = bq->y1 = bq->y2 = 0.0;
61
+}
62
+
63
+/*
64
+ * config = {
65
+ *     coefficients = 
66
+ *         { rate =  44100, b0=.., b1=.., b2=.., a0=.., a1=.., a2=.. },
67
+ *         { rate =  48000, b0=.., b1=.., b2=.., a0=.., a1=.., a2=.. },
68
+ *         { rate = 192000, b0=.., b1=.., b2=.., a0=.., a1=.., a2=.. }
69
+ *     
70
+ * }
71
+ */
72
+static void *bq_instantiate(const struct fc_descriptor * Descriptor,
73
+       unsigned long SampleRate, int index, const char *config)
74
+{
75
+   struct builtin *impl;
76
+   struct spa_json it4;
77
+   const char *val;
78
+   char key256;
79
+   uint32_t best_rate = 0;
80
+
81
+   impl = calloc(1, sizeof(*impl));
82
+   if (impl == NULL)
83
+       return NULL;
84
+
85
+   impl->rate = SampleRate;
86
+   impl->b0 = impl->a0 = 1.0f;
87
+   impl->type = bq_type_from_name(Descriptor->name);
88
+
89
+   if (config == NULL)
90
+       goto error;
91
+
92
+   spa_json_init(&it0, config, strlen(config));
93
+   if (spa_json_enter_object(&it0, &it1) <= 0)
94
+       goto error;
95
+
96
+   while (spa_json_get_string(&it1, key, sizeof(key)) > 0) {
97
+       if (spa_streq(key, "coefficients")) {
98
+           if (spa_json_enter_array(&it1, &it2) <= 0) {
99
+               pw_log_error("biquads:coefficients require an array");
100
+               goto error;
101
+           }
102
+           while (spa_json_enter_object(&it2, &it3) > 0) {
103
+               int32_t rate = 0;
104
+               float b0 = 1.0f, b1 = 0.0f, b2 = 0.0f;
105
+               float a0 = 1.0f, a1 = 0.0f, a2 = 0.0f;
106
+
107
+               while (spa_json_get_string(&it3, key, sizeof(key)) > 0) {
108
+                   if (spa_streq(key, "rate")) {
109
+                       if (spa_json_get_int(&it3, &rate) <= 0) {
110
+                           pw_log_error("biquads:rate requires a number");
111
+                           goto error;
112
+                       }
113
+                   }
114
+                   else if (spa_streq(key, "b0")) {
115
+                       if (spa_json_get_float(&it3, &b0) <= 0) {
116
+                           pw_log_error("biquads:b0 requires a float");
117
+                           goto error;
118
+                       }
119
+                   }
120
+                   else if (spa_streq(key, "b1")) {
121
+                       if (spa_json_get_float(&it3, &b1) <= 0) {
122
+                           pw_log_error("biquads:b1 requires a float");
123
+                           goto error;
124
+                       }
125
+                   }
126
+                   else if (spa_streq(key, "b2")) {
127
+                       if (spa_json_get_float(&it3, &b2) <= 0) {
128
+                           pw_log_error("biquads:b2 requires a float");
129
+                           goto error;
130
+                       }
131
+                   }
132
+                   else if (spa_streq(key, "a0")) {
133
+                       if (spa_json_get_float(&it3, &a0) <= 0) {
134
+                           pw_log_error("biquads:a0 requires a float");
135
+                           goto error;
136
+                       }
137
+                   }
138
+                   else if (spa_streq(key, "a1")) {
139
+                       if (spa_json_get_float(&it3, &a1) <= 0) {
140
+                           pw_log_error("biquads:a1 requires a float");
141
+                           goto error;
142
+                       }
143
+                   }
144
+                   else if (spa_streq(key, "a2")) {
145
+                       if (spa_json_get_float(&it3, &a2) <= 0) {
146
+                           pw_log_error("biquads:a0 requires a float");
147
+                           goto error;
148
+                       }
149
+                   }
150
+                   else if (spa_json_next(&it1, &val) < 0)
151
+                       break;
152
+               }
153
+               if (labs((long)rate - (long)SampleRate) <
154
+                   labs((long)best_rate - (long)SampleRate)) {
155
+                   best_rate = rate;
156
+                   bq_raw_update(impl, b0, b1, b2, a0, a1, a2);
157
+               }
158
+           }
159
+       }
160
+       else if (spa_json_next(&it1, &val) < 0)
161
+           break;
162
+   }
163
+
164
+   return impl;
165
+error:
166
+   free(impl);
167
+   errno = EINVAL;
168
+   return NULL;
169
+}
170
+
171
+#define BQ_NUM_PORTS       11
172
 static struct fc_port bq_ports = {
173
    { .index = 0,
174
      .name = "Out",
175
@@ -240,176 +396,225 @@
176
      .flags = FC_PORT_INPUT | FC_PORT_CONTROL,
177
      .def = 0.0f, .min = -120.0f, .max = 20.0f,
178
    },
179
+   { .index = 5,
180
+     .name = "b0",
181
+     .flags = FC_PORT_INPUT | FC_PORT_CONTROL,
182
+     .def = 1.0f, .min = -10.0f, .max = 10.0f,
183
+   },
184
+   { .index = 6,
185
+     .name = "b1",
186
+     .flags = FC_PORT_INPUT | FC_PORT_CONTROL,
187
+     .def = 0.0f, .min = -10.0f, .max = 10.0f,
188
+   },
189
+   { .index = 7,
190
+     .name = "b2",
191
+     .flags = FC_PORT_INPUT | FC_PORT_CONTROL,
192
+     .def = 0.0f, .min = -10.0f, .max = 10.0f,
193
+   },
194
+   { .index = 8,
195
+     .name = "a0",
196
+     .flags = FC_PORT_INPUT | FC_PORT_CONTROL,
197
+     .def = 1.0f, .min = -10.0f, .max = 10.0f,
198
+   },
199
+   { .index = 9,
200
+     .name = "a1",
201
+     .flags = FC_PORT_INPUT | FC_PORT_CONTROL,
202
+     .def = 0.0f, .min = -10.0f, .max = 10.0f,
203
+   },
204
+   { .index = 10,
205
+     .name = "a2",
206
+     .flags = FC_PORT_INPUT | FC_PORT_CONTROL,
207
+     .def = 0.0f, .min = -10.0f, .max = 10.0f,
208
+   },
209
+
210
 };
211
 
212
-static void bq_run(struct builtin *impl, unsigned long samples, int type)
213
+static void bq_freq_update(struct builtin *impl, int type, float freq, float Q, float gain)
214
 {
215
    struct biquad *bq = &impl->bq;
216
-   float *out = impl->port0;
217
-   float *in = impl->port1;
218
-   float freq = impl->port20;
219
-   float Q = impl->port30;
220
-   float gain = impl->port40;
221
-
222
-   if (impl->freq != freq || impl->Q != Q || impl->gain != gain) {
223
-       impl->freq = freq;
224
-       impl->Q = Q;
225
-       impl->gain = gain;
226
-       biquad_set(bq, type, freq * 2 / impl->rate, Q, gain);
227
+   impl->freq = freq;
228
+   impl->Q = Q;
229
+   impl->gain = gain;
230
+   biquad_set(bq, type, freq * 2 / impl->rate, Q, gain);
231
+   impl->port50 = impl->b0 = bq->b0;
232
+   impl->port60 = impl->b1 = bq->b1;
233
+   impl->port70 = impl->b2 = bq->b2;
234
+   impl->port80 = impl->a0 = 1.0f;
235
+   impl->port90 = impl->a1 = bq->a1;
236
+   impl->port100 = impl->a2 = bq->a2;
237
+}
238
+
239
+static void bq_activate(void * Instance)
240
+{
241
+   struct builtin *impl = Instance;
242
+   if (impl->type == BQ_NONE) {
243
+       impl->port50 = impl->b0;
244
+       impl->port60 = impl->b1;
245
+       impl->port70 = impl->b2;
246
+       impl->port80 = impl->a0;
247
+       impl->port90 = impl->a1;
248
+       impl->port100 = impl->a2;
249
+   } else {
250
+       float freq = impl->port20;
251
+       float Q = impl->port30;
252
+       float gain = impl->port40;
253
+       bq_freq_update(impl, impl->type, freq, Q, gain);
254
    }
255
-   dsp_ops_biquad_run(dsp_ops, bq, out, in, samples);
256
 }
257
 
258
-/** bq_lowpass */
259
-static void bq_lowpass_run(void * Instance, unsigned long SampleCount)
260
+static void bq_run(void *Instance, unsigned long samples)
261
 {
262
    struct builtin *impl = Instance;
263
-   bq_run(impl, SampleCount, BQ_LOWPASS);
264
+   struct biquad *bq = &impl->bq;
265
+   float *out = impl->port0;
266
+   float *in = impl->port1;
267
+
268
+   if (impl->type == BQ_NONE) {
269
+       float b0, b1, b2, a0, a1, a2;
270
+       b0 = impl->port50;
271
+       b1 = impl->port60;
272
+       b2 = impl->port70;
273
+       a0 = impl->port80;
274
+       a1 = impl->port90;
275
+       a2 = impl->port100;
276
+       if (impl->b0 != b0 || impl->b1 != b1 || impl->b2 != b2 ||
277
+           impl->a0 != a0 || impl->a1 != a1 || impl->a2 != a2) {
278
+           bq_raw_update(impl, b0, b1, b2, a0, a1, a2);
279
+       }
280
+   } else {
281
+       float freq = impl->port20;
282
+       float Q = impl->port30;
283
+       float gain = impl->port40;
284
+       if (impl->freq != freq || impl->Q != Q || impl->gain != gain)
285
+           bq_freq_update(impl, impl->type, freq, Q, gain);
286
+   }
287
+   dsp_ops_biquad_run(dsp_ops, bq, out, in, samples);
288
 }
289
 
290
+/** bq_lowpass */
291
 static const struct fc_descriptor bq_lowpass_desc = {
292
    .name = "bq_lowpass",
293
 
294
-   .n_ports = 5,
295
+   .n_ports = BQ_NUM_PORTS,
296
    .ports = bq_ports,
297
 
298
-   .instantiate = builtin_instantiate,
299
+   .instantiate = bq_instantiate,
300
    .connect_port = builtin_connect_port,
301
-   .run = bq_lowpass_run,
302
+   .activate = bq_activate,
303
+   .run = bq_run,
304
    .cleanup = builtin_cleanup,
305
 };
306
 
307
 /** bq_highpass */
308
-static void bq_highpass_run(void * Instance, unsigned long SampleCount)
309
-{
310
-   struct builtin *impl = Instance;
311
-   bq_run(impl, SampleCount, BQ_HIGHPASS);
312
-}
313
-
314
 static const struct fc_descriptor bq_highpass_desc = {
315
    .name = "bq_highpass",
316
 
317
-   .n_ports = 5,
318
+   .n_ports = BQ_NUM_PORTS,
319
    .ports = bq_ports,
320
 
321
-   .instantiate = builtin_instantiate,
322
+   .instantiate = bq_instantiate,
323
    .connect_port = builtin_connect_port,
324
-   .run = bq_highpass_run,
325
+   .activate = bq_activate,
326
+   .run = bq_run,
327
    .cleanup = builtin_cleanup,
328
 };
329
 
330
 /** bq_bandpass */
331
-static void bq_bandpass_run(void * Instance, unsigned long SampleCount)
332
-{
333
-   struct builtin *impl = Instance;
334
-   bq_run(impl, SampleCount, BQ_BANDPASS);
335
-}
336
-
337
 static const struct fc_descriptor bq_bandpass_desc = {
338
    .name = "bq_bandpass",
339
 
340
-   .n_ports = 5,
341
+   .n_ports = BQ_NUM_PORTS,
342
    .ports = bq_ports,
343
 
344
-   .instantiate = builtin_instantiate,
345
+   .instantiate = bq_instantiate,
346
    .connect_port = builtin_connect_port,
347
-   .run = bq_bandpass_run,
348
+   .activate = bq_activate,
349
+   .run = bq_run,
350
    .cleanup = builtin_cleanup,
351
 };
352
 
353
 /** bq_lowshelf */
354
-static void bq_lowshelf_run(void * Instance, unsigned long SampleCount)
355
-{
356
-   struct builtin *impl = Instance;
357
-   bq_run(impl, SampleCount, BQ_LOWSHELF);
358
-}
359
-
360
 static const struct fc_descriptor bq_lowshelf_desc = {
361
    .name = "bq_lowshelf",
362
 
363
-   .n_ports = 5,
364
+   .n_ports = BQ_NUM_PORTS,
365
    .ports = bq_ports,
366
 
367
-   .instantiate = builtin_instantiate,
368
+   .instantiate = bq_instantiate,
369
    .connect_port = builtin_connect_port,
370
-   .run = bq_lowshelf_run,
371
+   .activate = bq_activate,
372
+   .run = bq_run,
373
    .cleanup = builtin_cleanup,
374
 };
375
 
376
 /** bq_highshelf */
377
-static void bq_highshelf_run(void * Instance, unsigned long SampleCount)
378
-{
379
-   struct builtin *impl = Instance;
380
-   bq_run(impl, SampleCount, BQ_HIGHSHELF);
381
-}
382
-
383
 static const struct fc_descriptor bq_highshelf_desc = {
384
    .name = "bq_highshelf",
385
 
386
-   .n_ports = 5,
387
+   .n_ports = BQ_NUM_PORTS,
388
    .ports = bq_ports,
389
 
390
-   .instantiate = builtin_instantiate,
391
+   .instantiate = bq_instantiate,
392
    .connect_port = builtin_connect_port,
393
-   .run = bq_highshelf_run,
394
+   .activate = bq_activate,
395
+   .run = bq_run,
396
    .cleanup = builtin_cleanup,
397
 };
398
 
399
 /** bq_peaking */
400
-static void bq_peaking_run(void * Instance, unsigned long SampleCount)
401
-{
402
-   struct builtin *impl = Instance;
403
-   bq_run(impl, SampleCount, BQ_PEAKING);
404
-}
405
-
406
 static const struct fc_descriptor bq_peaking_desc = {
407
    .name = "bq_peaking",
408
 
409
-   .n_ports = 5,
410
+   .n_ports = BQ_NUM_PORTS,
411
    .ports = bq_ports,
412
 
413
-   .instantiate = builtin_instantiate,
414
+   .instantiate = bq_instantiate,
415
    .connect_port = builtin_connect_port,
416
-   .run = bq_peaking_run,
417
+   .activate = bq_activate,
418
+   .run = bq_run,
419
    .cleanup = builtin_cleanup,
420
 };
421
 
422
 /** bq_notch */
423
-static void bq_notch_run(void * Instance, unsigned long SampleCount)
424
-{
425
-   struct builtin *impl = Instance;
426
-   bq_run(impl, SampleCount, BQ_NOTCH);
427
-}
428
-
429
 static const struct fc_descriptor bq_notch_desc = {
430
    .name = "bq_notch",
431
 
432
-   .n_ports = 5,
433
+   .n_ports = BQ_NUM_PORTS,
434
    .ports = bq_ports,
435
 
436
-   .instantiate = builtin_instantiate,
437
+   .instantiate = bq_instantiate,
438
    .connect_port = builtin_connect_port,
439
-   .run = bq_notch_run,
440
+   .activate = bq_activate,
441
+   .run = bq_run,
442
    .cleanup = builtin_cleanup,
443
 };
444
 
445
 
446
 /** bq_allpass */
447
-static void bq_allpass_run(void * Instance, unsigned long SampleCount)
448
-{
449
-   struct builtin *impl = Instance;
450
-   bq_run(impl, SampleCount, BQ_ALLPASS);
451
-}
452
-
453
 static const struct fc_descriptor bq_allpass_desc = {
454
    .name = "bq_allpass",
455
 
456
-   .n_ports = 5,
457
+   .n_ports = BQ_NUM_PORTS,
458
    .ports = bq_ports,
459
 
460
-   .instantiate = builtin_instantiate,
461
+   .instantiate = bq_instantiate,
462
+   .connect_port = builtin_connect_port,
463
+   .activate = bq_activate,
464
+   .run = bq_run,
465
+   .cleanup = builtin_cleanup,
466
+};
467
+
468
+/* bq_raw */
469
+static const struct fc_descriptor bq_raw_desc = {
470
+   .name = "bq_raw",
471
+
472
+   .n_ports = BQ_NUM_PORTS,
473
+   .ports = bq_ports,
474
+
475
+   .instantiate = bq_instantiate,
476
    .connect_port = builtin_connect_port,
477
-   .run = bq_allpass_run,
478
+   .activate = bq_activate,
479
+   .run = bq_run,
480
    .cleanup = builtin_cleanup,
481
 };
482
 
483
@@ -958,6 +1163,7 @@
484
    .cleanup = delay_cleanup,
485
 };
486
 
487
+/* invert */
488
 static void invert_run(void * Instance, unsigned long SampleCount)
489
 {
490
    struct builtin *impl = Instance;
491
@@ -1019,6 +1225,8 @@
492
        return &delay_desc;
493
    case 12:
494
        return &invert_desc;
495
+   case 13:
496
+       return &bq_raw_desc;
497
    }
498
    return NULL;
499
 }
500
pipewire-0.3.68.tar.gz/src/pipewire/context.c -> pipewire-0.3.69.tar.gz/src/pipewire/context.c Changed
50
 
1
@@ -885,13 +885,16 @@
2
 
3
                pw_impl_link_prepare(l);
4
 
5
-               if (!l->prepared || t->visited)
6
+               if (!l->prepared || (t != n && t->visited))
7
                    continue;
8
 
9
                if (!l->passive)
10
                    t->runnable = true;
11
-               t->visited = true;
12
-               spa_list_append(&queue, &t->sort_link);
13
+
14
+               if (!t->visited) {
15
+                   t->visited = true;
16
+                   spa_list_append(&queue, &t->sort_link);
17
+               }
18
            }
19
        }
20
        spa_list_for_each(p, &n->output_ports, link) {
21
@@ -903,13 +906,16 @@
22
 
23
                pw_impl_link_prepare(l);
24
 
25
-               if (!l->prepared || t->visited)
26
+               if (!l->prepared || (t != n && t->visited))
27
                    continue;
28
 
29
                if (!l->passive)
30
                    t->runnable = true;
31
-               t->visited = true;
32
-               spa_list_append(&queue, &t->sort_link);
33
+
34
+               if (!t->visited) {
35
+                   t->visited = true;
36
+                   spa_list_append(&queue, &t->sort_link);
37
+               }
38
            }
39
        }
40
        /* now go through all the nodes that have the same group and
41
@@ -943,6 +949,8 @@
42
    spa_list_consume(n, nodes, sort_link) {
43
        spa_list_remove(&n->sort_link);
44
 
45
+       driver->runnable |= n->runnable;
46
+
47
        pw_log_debug(" follower: %p %s runnable:%u driver-runnable:%u", n, n->name,
48
                n->runnable, driver->runnable);
49
        pw_impl_node_set_driver(n, driver);
50
pipewire-0.3.68.tar.gz/src/pipewire/thread-loop.c -> pipewire-0.3.69.tar.gz/src/pipewire/thread-loop.c Changed
9
 
1
@@ -240,6 +240,7 @@
2
 
3
    pw_thread_loop_stop(loop);
4
 
5
+   pw_loop_set_callbacks(loop->loop, NULL, NULL);
6
    spa_hook_remove(&loop->hook);
7
 
8
    spa_hook_list_clean(&loop->listener_list);
9
Refresh

No build results available

Refresh

No rpmlint results available

Request History
Bjørn Lie's avatar

zaitor created request about 2 years ago

New upstream release, NOTE do not ack until https://build.opensuse.org/request/show/1079411 is acked and published in TW


Bjørn Lie's avatar

zaitor accepted request about 2 years ago

Xin