~ubuntu-branches/ubuntu/saucy/mpd/saucy

« back to all changes in this revision

Viewing changes to src/mixer/pulse_mixer.c

  • Committer: Bazaar Package Importer
  • Author(s): Angel Abad
  • Date: 2011-02-02 12:26:30 UTC
  • mfrom: (1.5.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20110202122630-bdyx8w4k94doz4fs
Tags: 0.16.1-1ubuntu1
* Merge from debian unstable. Remaining changes:
  - debian/control:
    + Don't build-depend on libmikmod2-dev (Debian bug #510675).
    + Move avahi-daemon from Suggests field to Recommends field.
  - debian/mpd.init.d:
    + Read mpd user from mpd.conf.
  - debian/control, debian/rules:
    + Add libmp3lame-dev to the build dependencies and enable lame.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2003-2009 The Music Player Daemon Project
3
 
 * http://www.musicpd.org
4
 
 *
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.
9
 
 *
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.
14
 
 *
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.
18
 
 */
19
 
 
20
 
#include "mixer_api.h"
21
 
#include "conf.h"
22
 
 
23
 
#include <glib.h>
24
 
#include <pulse/volume.h>
25
 
#include <pulse/pulseaudio.h>
26
 
 
27
 
#include <string.h>
28
 
 
29
 
#undef G_LOG_DOMAIN
30
 
#define G_LOG_DOMAIN "pulse_mixer"
31
 
 
32
 
struct pulse_mixer {
33
 
        struct mixer base;
34
 
 
35
 
        const char *server;
36
 
        const char *sink;
37
 
        const char *output_name;
38
 
 
39
 
        uint32_t index;
40
 
        bool online;
41
 
 
42
 
        struct pa_context *context;
43
 
        struct pa_threaded_mainloop *mainloop;
44
 
        struct pa_cvolume volume;
45
 
 
46
 
};
47
 
 
48
 
/**
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),
53
 
 *     false otherwise
54
 
 */
55
 
static bool
56
 
pulse_wait_for_operation(struct pa_threaded_mainloop *mainloop,
57
 
                         struct pa_operation *operation)
58
 
{
59
 
        pa_operation_state_t state;
60
 
 
61
 
        assert(mainloop != NULL);
62
 
        assert(operation != NULL);
63
 
 
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);
68
 
        }
69
 
 
70
 
        pa_operation_unref(operation);
71
 
 
72
 
        return state == PA_OPERATION_DONE;
73
 
}
74
 
 
75
 
static void
76
 
sink_input_cb(G_GNUC_UNUSED pa_context *context, const pa_sink_input_info *i,
77
 
              int eol, void *userdata)
78
 
{
79
 
 
80
 
        struct pulse_mixer *pm = userdata;
81
 
 
82
 
        if (eol) {
83
 
                g_debug("eol error sink_input_cb");
84
 
                return;
85
 
        }
86
 
 
87
 
        if (i == NULL) {
88
 
                g_debug("Sink input callback failure");
89
 
                return;
90
 
        }
91
 
 
92
 
        g_debug("sink input cb %s, index %d ",i->name,i->index);
93
 
 
94
 
        if (strcmp(i->name,pm->output_name) == 0) {
95
 
                pm->index = i->index;
96
 
                pm->online = true;
97
 
                pm->volume = i->volume;
98
 
        } else
99
 
                g_debug("bad name");
100
 
}
101
 
 
102
 
static void
103
 
sink_input_vol(G_GNUC_UNUSED pa_context *context, const pa_sink_input_info *i,
104
 
               int eol, void *userdata)
105
 
{
106
 
 
107
 
        struct pulse_mixer *pm = userdata;
108
 
 
109
 
        if (eol) {
110
 
                g_debug("eol error sink_input_vol");
111
 
                return;
112
 
        }
113
 
 
114
 
        if (i == NULL) {
115
 
                g_debug("Sink input callback failure");
116
 
                return;
117
 
        }
118
 
 
119
 
        g_debug("sink input vol %s, index %d ", i->name, i->index);
120
 
 
121
 
        pm->volume = i->volume;
122
 
 
123
 
        pa_threaded_mainloop_signal(pm->mainloop, 0);
124
 
}
125
 
 
126
 
static void
127
 
subscribe_cb(pa_context *c, pa_subscription_event_type_t t,
128
 
             uint32_t idx, void *userdata)
129
 
{
130
 
 
131
 
        struct pulse_mixer *pm = userdata;
132
 
 
133
 
        g_debug("subscribe call back");
134
 
 
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 &&
139
 
                    pm->index == idx)
140
 
                        pm->online = false;
141
 
                else {
142
 
                        pa_operation *o;
143
 
 
144
 
                        o = pa_context_get_sink_input_info(c, idx,
145
 
                                                           sink_input_cb, pm);
146
 
                        if (o == NULL) {
147
 
                                g_debug("pa_context_get_sink_input_info() failed");
148
 
                                return;
149
 
                        }
150
 
 
151
 
                        pa_operation_unref(o);
152
 
                }
153
 
 
154
 
                break;
155
 
        }
156
 
}
157
 
 
158
 
static void
159
 
context_state_cb(pa_context *context, void *userdata)
160
 
{
161
 
        struct pulse_mixer *pm = userdata;
162
 
 
163
 
        switch (pa_context_get_state(context)) {
164
 
        case PA_CONTEXT_READY: {
165
 
                pa_operation *o;
166
 
 
167
 
                pa_context_set_subscribe_callback(context, subscribe_cb, pm);
168
 
 
169
 
                o = pa_context_subscribe(context,
170
 
                                         (pa_subscription_mask_t)PA_SUBSCRIPTION_MASK_SINK_INPUT,
171
 
                                         NULL, NULL);
172
 
                if (o == NULL) {
173
 
                        g_debug("pa_context_subscribe() failed");
174
 
                        return;
175
 
                }
176
 
 
177
 
                pa_operation_unref(o);
178
 
 
179
 
                o = pa_context_get_sink_input_info_list(context,
180
 
                                                        sink_input_cb, pm);
181
 
                if (o == NULL) {
182
 
                        g_debug("pa_context_get_sink_input_info_list() failed");
183
 
                        return;
184
 
                }
185
 
 
186
 
                pa_operation_unref(o);
187
 
 
188
 
                pa_threaded_mainloop_signal(pm->mainloop, 0);
189
 
                break;
190
 
        }
191
 
 
192
 
        case PA_CONTEXT_UNCONNECTED:
193
 
        case PA_CONTEXT_CONNECTING:
194
 
        case PA_CONTEXT_AUTHORIZING:
195
 
        case PA_CONTEXT_SETTING_NAME:
196
 
                break;
197
 
        case PA_CONTEXT_TERMINATED:
198
 
        case PA_CONTEXT_FAILED:
199
 
                pa_threaded_mainloop_signal(pm->mainloop, 0);
200
 
                break;
201
 
        }
202
 
}
203
 
 
204
 
 
205
 
static struct mixer *
206
 
pulse_mixer_init(const struct config_param *param)
207
 
{
208
 
        struct pulse_mixer *pm = g_new(struct pulse_mixer,1);
209
 
        mixer_init(&pm->base, &pulse_mixer);
210
 
 
211
 
        pm->online = false;
212
 
 
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);
216
 
 
217
 
        return &pm->base;
218
 
}
219
 
 
220
 
static void
221
 
pulse_mixer_finish(struct mixer *data)
222
 
{
223
 
        struct pulse_mixer *pm = (struct pulse_mixer *) data;
224
 
 
225
 
        g_free(pm);
226
 
}
227
 
 
228
 
static bool
229
 
pulse_mixer_setup(struct pulse_mixer *pm)
230
 
{
231
 
        pa_context_set_state_callback(pm->context, context_state_cb, pm);
232
 
 
233
 
        if (pa_context_connect(pm->context, pm->server,
234
 
                               (pa_context_flags_t)0, NULL) < 0) {
235
 
                g_debug("context server fail");
236
 
                return false;
237
 
        }
238
 
 
239
 
        pa_threaded_mainloop_lock(pm->mainloop);
240
 
 
241
 
        if (pa_threaded_mainloop_start(pm->mainloop) < 0) {
242
 
                pa_threaded_mainloop_unlock(pm->mainloop);
243
 
                g_debug("error start mainloop");
244
 
                return false;
245
 
        }
246
 
 
247
 
        pa_threaded_mainloop_wait(pm->mainloop);
248
 
 
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");
252
 
                return false;
253
 
        }
254
 
 
255
 
        pa_threaded_mainloop_unlock(pm->mainloop);
256
 
 
257
 
        return true;
258
 
}
259
 
 
260
 
static bool
261
 
pulse_mixer_open(struct mixer *data)
262
 
{
263
 
        struct pulse_mixer *pm = (struct pulse_mixer *) data;
264
 
 
265
 
        g_debug("pulse mixer open");
266
 
 
267
 
        pm->index = 0;
268
 
        pm->online = false;
269
 
 
270
 
        pm->mainloop = pa_threaded_mainloop_new();
271
 
        if (pm->mainloop == NULL) {
272
 
                g_debug("failed mainloop");
273
 
                return false;
274
 
        }
275
 
 
276
 
        pm->context = pa_context_new(pa_threaded_mainloop_get_api(pm->mainloop),
277
 
                                     "Mixer mpd");
278
 
        if (pm->context == NULL) {
279
 
                pa_threaded_mainloop_stop(pm->mainloop);
280
 
                pa_threaded_mainloop_free(pm->mainloop);
281
 
                g_debug("failed context");
282
 
                return false;
283
 
        }
284
 
 
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);
290
 
                return false;
291
 
        }
292
 
 
293
 
        return true;
294
 
}
295
 
 
296
 
static void
297
 
pulse_mixer_close(struct mixer *data)
298
 
{
299
 
        struct pulse_mixer *pm = (struct pulse_mixer *) data;
300
 
 
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);
305
 
 
306
 
        pm->online = false;
307
 
}
308
 
 
309
 
static int
310
 
pulse_mixer_get_volume(struct mixer *mixer)
311
 
{
312
 
        struct pulse_mixer *pm = (struct pulse_mixer *) mixer;
313
 
        int ret;
314
 
        pa_operation *o;
315
 
 
316
 
        pa_threaded_mainloop_lock(pm->mainloop);
317
 
 
318
 
        if (!pm->online) {
319
 
                pa_threaded_mainloop_unlock(pm->mainloop);
320
 
                return false;
321
 
        }
322
 
 
323
 
        o = pa_context_get_sink_input_info(pm->context, pm->index,
324
 
                                           sink_input_vol, pm);
325
 
        if (o == NULL) {
326
 
                pa_threaded_mainloop_unlock(pm->mainloop);
327
 
                g_debug("pa_context_get_sink_input_info() failed");
328
 
                return false;
329
 
        }
330
 
 
331
 
        if (!pulse_wait_for_operation(pm->mainloop, o)) {
332
 
                pa_threaded_mainloop_unlock(pm->mainloop);
333
 
                return false;
334
 
        }
335
 
 
336
 
        ret = pm->online
337
 
                ? (int)((100*(pa_cvolume_avg(&pm->volume)+1))/PA_VOLUME_NORM)
338
 
                : -1;
339
 
 
340
 
        pa_threaded_mainloop_unlock(pm->mainloop);
341
 
 
342
 
        return ret;
343
 
}
344
 
 
345
 
static bool
346
 
pulse_mixer_set_volume(struct mixer *mixer, unsigned volume)
347
 
{
348
 
        struct pulse_mixer *pm = (struct pulse_mixer *) mixer;
349
 
        struct pa_cvolume cvolume;
350
 
        pa_operation *o;
351
 
 
352
 
        pa_threaded_mainloop_lock(pm->mainloop);
353
 
 
354
 
        if (!pm->online) {
355
 
                pa_threaded_mainloop_unlock(pm->mainloop);
356
 
                return false;
357
 
        }
358
 
 
359
 
        pa_cvolume_set(&cvolume, pm->volume.channels,
360
 
                       (pa_volume_t)volume * PA_VOLUME_NORM / 100 + 0.5);
361
 
 
362
 
        o = pa_context_set_sink_input_volume(pm->context, pm->index,
363
 
                                             &cvolume, NULL, NULL);
364
 
        pa_threaded_mainloop_unlock(pm->mainloop);
365
 
        if (o == NULL) {
366
 
                g_debug("pa_context_set_sink_input_volume() failed");
367
 
                return false;
368
 
        }
369
 
 
370
 
        pa_operation_unref(o);
371
 
 
372
 
        return true;
373
 
}
374
 
 
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,
382
 
};