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 __attribute__((unused)))
43 qWarning() << "PaClient: set sink volume: " <<
44 pa_strerror(pa_context_errno(c));
47 static void set_source_volume_cb(pa_context *c, int success, void *data __attribute__((unused)))
50 qWarning() << "PaClient: set source volume: " <<
51 pa_strerror(pa_context_errno(c));
54 void PaClient::setVolume(uint32_t type, uint32_t index, uint32_t channel, uint32_t volume)
57 pa_context *c = context();
58 pa_cvolume *cvolume = NULL;
61 cvolume = m_sink_states.value(index);
62 cvolume->values[channel] = volume;
63 if (!(o = pa_context_set_sink_volume_by_index(c, index, cvolume, set_sink_volume_cb, NULL))) {
64 qWarning() << "PaClient: set sink #" << index <<
65 " channel #" << channel <<
66 " volume: " << pa_strerror(pa_context_errno(c));
69 pa_operation_unref(o);
70 } else if (type == C_SOURCE) {
71 cvolume = m_sink_states.value(index);
72 cvolume->values[channel] = volume;
73 if (!(o = pa_context_set_source_volume_by_index(c, index, cvolume, set_source_volume_cb, NULL))) {
74 qWarning() << "PaClient: set source #" << index <<
75 " channel #" << channel <<
76 " volume: " << pa_strerror(pa_context_errno(c));
79 pa_operation_unref(o);
83 void get_source_list_cb(pa_context *c,
84 const pa_source_info *i,
90 PaClient *self = reinterpret_cast<PaClient*>(data);
93 qWarning() << "PaClient: get source list: " <<
94 pa_strerror(pa_context_errno(c));
101 self->addOneControlState(C_SOURCE, i->index, &i->volume);
102 for (chan = 0; chan < i->channel_map.channels; chan++) {
103 emit self->controlAdded(i->index, QString(i->description), C_SOURCE, chan,
104 channel_position_string[i->channel_map.map[chan]],
105 i->volume.values[chan]);
110 void get_sink_list_cb(pa_context *c,
111 const pa_sink_info *i,
116 PaClient *self = reinterpret_cast<PaClient*>(data);
119 qWarning() << "PaClient: get sink list: " <<
120 pa_strerror(pa_context_errno(c));
126 self->addOneControlState(C_SINK, i->index, &i->volume);
127 for (chan = 0; chan < i->channel_map.channels; chan++) {
128 emit self->controlAdded(i->index, QString(i->description), C_SINK, chan,
129 channel_position_string[i->channel_map.map[chan]],
130 i->volume.values[chan]);
135 void context_state_cb(pa_context *c, void *data)
138 PaClient *self = reinterpret_cast<PaClient*>(data);
140 switch (pa_context_get_state(c)) {
141 case PA_CONTEXT_CONNECTING:
142 case PA_CONTEXT_AUTHORIZING:
143 case PA_CONTEXT_SETTING_NAME:
145 case PA_CONTEXT_READY:
146 // Fetch the controls of interest
147 if (!(o = pa_context_get_source_info_list(c, get_source_list_cb, data))) {
148 qWarning() << "PaClient: get source info list: " <<
149 pa_strerror(pa_context_errno(c));
152 pa_operation_unref(o);
153 if (!(o = pa_context_get_sink_info_list(c, &get_sink_list_cb, data))) {
154 qWarning() << "PaClient: get sink info list: " <<
155 pa_strerror(pa_context_errno(c));
159 case PA_CONTEXT_TERMINATED:
163 case PA_CONTEXT_FAILED:
165 qCritical() << "PaClient: connection failed: " <<
166 pa_strerror(pa_context_errno(c));
172 void PaClient::init()
174 m_ml = pa_threaded_mainloop_new();
176 qCritical("PaClient: failed to create mainloop");
180 pa_threaded_mainloop_set_name(m_ml, "PaClient mainloop");
182 m_mlapi = pa_threaded_mainloop_get_api(m_ml);
186 m_ctx = pa_context_new(m_mlapi, "Mixer");
188 qCritical("PaClient: failed to create context");
189 pa_threaded_mainloop_free(m_ml);
192 pa_context_set_state_callback(m_ctx, context_state_cb, this);
194 if (pa_context_connect(m_ctx, 0, (pa_context_flags_t)0, 0) < 0) {
195 qCritical("PaClient: failed to connect");
196 pa_context_unref(m_ctx);
197 pa_threaded_mainloop_free(m_ml);
201 if (pa_threaded_mainloop_start(m_ml) != 0) {
202 qCritical("PaClient: failed to start mainloop");
203 pa_context_unref(m_ctx);
204 pa_threaded_mainloop_free(m_ml);
213 void PaClient::addOneControlState(int type, int index, const pa_cvolume *cvolume)
216 pa_cvolume *cvolume_new;
218 cvolume_new = new pa_cvolume;
219 cvolume_new->channels = cvolume->channels;
220 for (i = 0; i < cvolume->channels; i++)
221 cvolume_new->values[i] = cvolume->values[i];
223 m_sink_states.insert(index, cvolume_new);
224 else if (type == C_SOURCE)
225 m_source_states.insert(index, cvolume_new);