2 * Copyright (C) 2016,2017 Konsulko Group
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <QtCore/QDebug>
22 : m_init(false), m_ml(nullptr), m_mlapi(nullptr), m_ctx(nullptr)
32 void PaClient::close()
35 pa_threaded_mainloop_stop(m_ml);
36 pa_threaded_mainloop_free(m_ml);
40 static void set_sink_volume_cb(pa_context *c, int success, void *data)
45 qWarning() << "PaClient: set sink volume: " <<
46 pa_strerror(pa_context_errno(c));
49 void PaClient::incDecVolume(const int volume_delta)
52 pa_context *c = context();
53 pa_sink_info *i = getDefaultSinkInfo();
56 pa_cvolume_inc_clamp(&i->volume, volume_delta, 65536);
58 pa_cvolume_dec(&i->volume, abs(volume_delta));
60 o = pa_context_set_sink_volume_by_index(c, i->index, &i->volume, set_sink_volume_cb, NULL);
62 qWarning() << "PaClient: set sink #" << i->index <<
63 " volume: " << pa_strerror(pa_context_errno(c));
66 pa_operation_unref(o);
69 void get_sink_info_change_cb(pa_context *c,
70 const pa_sink_info *i,
80 PaClient *self = reinterpret_cast<PaClient*>(data);
81 pa_sink_info *si = self->getDefaultSinkInfo();
82 if (i->index == si->index) {
83 self->setDefaultSinkInfo(i);
84 pa_cvolume *cvolume = &self->getDefaultSinkInfo()->volume;
85 emit self->volumeExternallyChanged(pa_cvolume_avg(cvolume));
89 void get_default_sink_info_cb(pa_context *c,
90 const pa_sink_info *i,
100 PaClient *self = reinterpret_cast<PaClient*>(data);
101 self->setDefaultSinkInfo(i);
104 pa_sink_info *PaClient::getDefaultSinkInfo(void)
106 return &m_default_sink_info;
109 void PaClient::setDefaultSinkInfo(const pa_sink_info *i)
111 m_default_sink_info.index = i->index;
112 m_default_sink_info.channel_map.channels = i->channel_map.channels;
113 pa_cvolume *cvolume = &m_default_sink_info.volume;
114 cvolume->channels = i->volume.channels;
115 for (int chan = 0; chan < i->channel_map.channels; chan++) {
116 cvolume->values[chan] = i->volume.values[chan];
120 void get_server_info_cb(pa_context *c,
121 const pa_server_info *i,
125 o = pa_context_get_sink_info_by_name(c, i->default_sink_name, get_default_sink_info_cb, data);
127 qWarning() << "PaClient: get sink info by name: " <<
128 pa_strerror(pa_context_errno(c));
133 void subscribe_cb(pa_context *c,
134 pa_subscription_event_type_t type,
140 if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) {
141 qWarning("PaClient: unhandled subscribe event operation");
145 switch (type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
146 case PA_SUBSCRIPTION_EVENT_SINK:
147 o = pa_context_get_sink_info_by_index(c, index, get_sink_info_change_cb, data);
149 qWarning() << "PaClient: get sink info by index: " <<
150 pa_strerror(pa_context_errno(c));
155 qWarning("PaClient: unhandled subscribe event facility");
159 void context_state_cb(pa_context *c, void *data)
162 PaClient *self = reinterpret_cast<PaClient*>(data);
164 switch (pa_context_get_state(c)) {
165 case PA_CONTEXT_CONNECTING:
166 case PA_CONTEXT_AUTHORIZING:
167 case PA_CONTEXT_SETTING_NAME:
169 case PA_CONTEXT_READY:
170 o = pa_context_get_server_info(c, get_server_info_cb, data);
172 qWarning() << "PaClient: get server info: " <<
173 pa_strerror(pa_context_errno(c));
176 pa_operation_unref(o);
177 pa_context_set_subscribe_callback(c, subscribe_cb, data);
178 o = pa_context_subscribe(c, (pa_subscription_mask_t)(PA_SUBSCRIPTION_MASK_SINK), NULL, NULL);
180 qWarning() << "PaClient: subscribe: " <<
181 pa_strerror(pa_context_errno(c));
185 case PA_CONTEXT_TERMINATED:
189 case PA_CONTEXT_FAILED:
191 qCritical() << "PaClient: connection failed: " <<
192 pa_strerror(pa_context_errno(c));
198 void PaClient::init()
200 m_ml = pa_threaded_mainloop_new();
202 qCritical("PaClient: failed to create mainloop");
206 pa_threaded_mainloop_set_name(m_ml, "PaClient mainloop");
208 m_mlapi = pa_threaded_mainloop_get_api(m_ml);
212 m_ctx = pa_context_new(m_mlapi, "HomeScreen");
214 qCritical("PaClient: failed to create context");
216 pa_threaded_mainloop_free(m_ml);
219 pa_context_set_state_callback(m_ctx, context_state_cb, this);
221 if (pa_context_connect(m_ctx, 0, (pa_context_flags_t)0, 0) < 0) {
222 qCritical("PaClient: failed to connect");
223 pa_context_unref(m_ctx);
225 pa_threaded_mainloop_free(m_ml);
229 if (pa_threaded_mainloop_start(m_ml) != 0) {
230 qCritical("PaClient: failed to start mainloop");
231 pa_context_unref(m_ctx);
233 pa_threaded_mainloop_free(m_ml);