2
* Copyright (C) 2003-2009 The Music Player Daemon Project
3
* http://www.musicpd.org
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License along
16
* with this program; if not, write to the Free Software Foundation, Inc.,
17
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
#include "mixer_api.h"
24
#include <pulse/volume.h>
25
#include <pulse/pulseaudio.h>
30
#define G_LOG_DOMAIN "pulse_mixer"
37
const char *output_name;
42
struct pa_context *context;
43
struct pa_threaded_mainloop *mainloop;
44
struct pa_cvolume volume;
49
* \brief waits for a pulseaudio operation to finish, frees it and
50
* unlocks the mainloop
51
* \param operation the operation to wait for
52
* \return true if operation has finished normally (DONE state),
56
pulse_wait_for_operation(struct pa_threaded_mainloop *mainloop,
57
struct pa_operation *operation)
59
pa_operation_state_t state;
61
assert(mainloop != NULL);
62
assert(operation != NULL);
64
state = pa_operation_get_state(operation);
65
while (state == PA_OPERATION_RUNNING) {
66
pa_threaded_mainloop_wait(mainloop);
67
state = pa_operation_get_state(operation);
70
pa_operation_unref(operation);
72
return state == PA_OPERATION_DONE;
76
sink_input_cb(G_GNUC_UNUSED pa_context *context, const pa_sink_input_info *i,
77
int eol, void *userdata)
80
struct pulse_mixer *pm = userdata;
83
g_debug("eol error sink_input_cb");
88
g_debug("Sink input callback failure");
92
g_debug("sink input cb %s, index %d ",i->name,i->index);
94
if (strcmp(i->name,pm->output_name) == 0) {
97
pm->volume = i->volume;
103
sink_input_vol(G_GNUC_UNUSED pa_context *context, const pa_sink_input_info *i,
104
int eol, void *userdata)
107
struct pulse_mixer *pm = userdata;
110
g_debug("eol error sink_input_vol");
115
g_debug("Sink input callback failure");
119
g_debug("sink input vol %s, index %d ", i->name, i->index);
121
pm->volume = i->volume;
123
pa_threaded_mainloop_signal(pm->mainloop, 0);
127
subscribe_cb(pa_context *c, pa_subscription_event_type_t t,
128
uint32_t idx, void *userdata)
131
struct pulse_mixer *pm = userdata;
133
g_debug("subscribe call back");
135
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
136
case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
137
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
138
PA_SUBSCRIPTION_EVENT_REMOVE &&
144
o = pa_context_get_sink_input_info(c, idx,
147
g_debug("pa_context_get_sink_input_info() failed");
151
pa_operation_unref(o);
159
context_state_cb(pa_context *context, void *userdata)
161
struct pulse_mixer *pm = userdata;
163
switch (pa_context_get_state(context)) {
164
case PA_CONTEXT_READY: {
167
pa_context_set_subscribe_callback(context, subscribe_cb, pm);
169
o = pa_context_subscribe(context,
170
(pa_subscription_mask_t)PA_SUBSCRIPTION_MASK_SINK_INPUT,
173
g_debug("pa_context_subscribe() failed");
177
pa_operation_unref(o);
179
o = pa_context_get_sink_input_info_list(context,
182
g_debug("pa_context_get_sink_input_info_list() failed");
186
pa_operation_unref(o);
188
pa_threaded_mainloop_signal(pm->mainloop, 0);
192
case PA_CONTEXT_UNCONNECTED:
193
case PA_CONTEXT_CONNECTING:
194
case PA_CONTEXT_AUTHORIZING:
195
case PA_CONTEXT_SETTING_NAME:
197
case PA_CONTEXT_TERMINATED:
198
case PA_CONTEXT_FAILED:
199
pa_threaded_mainloop_signal(pm->mainloop, 0);
205
static struct mixer *
206
pulse_mixer_init(const struct config_param *param)
208
struct pulse_mixer *pm = g_new(struct pulse_mixer,1);
209
mixer_init(&pm->base, &pulse_mixer);
213
pm->server = config_get_block_string(param, "server", NULL);
214
pm->sink = config_get_block_string(param, "sink", NULL);
215
pm->output_name = config_get_block_string(param, "name", NULL);
221
pulse_mixer_finish(struct mixer *data)
223
struct pulse_mixer *pm = (struct pulse_mixer *) data;
229
pulse_mixer_setup(struct pulse_mixer *pm)
231
pa_context_set_state_callback(pm->context, context_state_cb, pm);
233
if (pa_context_connect(pm->context, pm->server,
234
(pa_context_flags_t)0, NULL) < 0) {
235
g_debug("context server fail");
239
pa_threaded_mainloop_lock(pm->mainloop);
241
if (pa_threaded_mainloop_start(pm->mainloop) < 0) {
242
pa_threaded_mainloop_unlock(pm->mainloop);
243
g_debug("error start mainloop");
247
pa_threaded_mainloop_wait(pm->mainloop);
249
if (pa_context_get_state(pm->context) != PA_CONTEXT_READY) {
250
pa_threaded_mainloop_unlock(pm->mainloop);
251
g_debug("error context not ready");
255
pa_threaded_mainloop_unlock(pm->mainloop);
261
pulse_mixer_open(struct mixer *data)
263
struct pulse_mixer *pm = (struct pulse_mixer *) data;
265
g_debug("pulse mixer open");
270
pm->mainloop = pa_threaded_mainloop_new();
271
if (pm->mainloop == NULL) {
272
g_debug("failed mainloop");
276
pm->context = pa_context_new(pa_threaded_mainloop_get_api(pm->mainloop),
278
if (pm->context == NULL) {
279
pa_threaded_mainloop_stop(pm->mainloop);
280
pa_threaded_mainloop_free(pm->mainloop);
281
g_debug("failed context");
285
if (!pulse_mixer_setup(pm)) {
286
pa_threaded_mainloop_stop(pm->mainloop);
287
pa_context_disconnect(pm->context);
288
pa_context_unref(pm->context);
289
pa_threaded_mainloop_free(pm->mainloop);
297
pulse_mixer_close(struct mixer *data)
299
struct pulse_mixer *pm = (struct pulse_mixer *) data;
301
pa_threaded_mainloop_stop(pm->mainloop);
302
pa_context_disconnect(pm->context);
303
pa_context_unref(pm->context);
304
pa_threaded_mainloop_free(pm->mainloop);
310
pulse_mixer_get_volume(struct mixer *mixer)
312
struct pulse_mixer *pm = (struct pulse_mixer *) mixer;
316
pa_threaded_mainloop_lock(pm->mainloop);
319
pa_threaded_mainloop_unlock(pm->mainloop);
323
o = pa_context_get_sink_input_info(pm->context, pm->index,
326
pa_threaded_mainloop_unlock(pm->mainloop);
327
g_debug("pa_context_get_sink_input_info() failed");
331
if (!pulse_wait_for_operation(pm->mainloop, o)) {
332
pa_threaded_mainloop_unlock(pm->mainloop);
337
? (int)((100*(pa_cvolume_avg(&pm->volume)+1))/PA_VOLUME_NORM)
340
pa_threaded_mainloop_unlock(pm->mainloop);
346
pulse_mixer_set_volume(struct mixer *mixer, unsigned volume)
348
struct pulse_mixer *pm = (struct pulse_mixer *) mixer;
349
struct pa_cvolume cvolume;
352
pa_threaded_mainloop_lock(pm->mainloop);
355
pa_threaded_mainloop_unlock(pm->mainloop);
359
pa_cvolume_set(&cvolume, pm->volume.channels,
360
(pa_volume_t)volume * PA_VOLUME_NORM / 100 + 0.5);
362
o = pa_context_set_sink_input_volume(pm->context, pm->index,
363
&cvolume, NULL, NULL);
364
pa_threaded_mainloop_unlock(pm->mainloop);
366
g_debug("pa_context_set_sink_input_volume() failed");
370
pa_operation_unref(o);
375
const struct mixer_plugin pulse_mixer = {
376
.init = pulse_mixer_init,
377
.finish = pulse_mixer_finish,
378
.open = pulse_mixer_open,
379
.close = pulse_mixer_close,
380
.get_volume = pulse_mixer_get_volume,
381
.set_volume = pulse_mixer_set_volume,