1 /*
2  * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the licence, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <glib.h>
19 #include <glib-object.h>
20 #include <libmatemixer/matemixer.h>
21 #include <libmatemixer/matemixer-private.h>
22 
23 #include <pulse/pulseaudio.h>
24 
25 #include "pulse-connection.h"
26 #include "pulse-helpers.h"
27 #include "pulse-monitor.h"
28 #include "pulse-stream.h"
29 #include "pulse-stream-control.h"
30 
31 struct _PulseStreamControlPrivate
32 {
33     guint32           index;
34     guint             volume;
35     pa_cvolume        cvolume;
36     pa_volume_t       base_volume;
37     pa_channel_map    channel_map;
38     PulseConnection  *connection;
39     PulseMonitor     *monitor;
40     MateMixerAppInfo *app_info;
41 };
42 
43 enum {
44     PROP_0,
45     PROP_INDEX,
46     PROP_CONNECTION,
47     N_PROPERTIES
48 };
49 
50 static GParamSpec *properties[N_PROPERTIES] = { NULL, };
51 
52 static void pulse_stream_control_get_property (GObject                 *object,
53                                                guint                    param_id,
54                                                GValue                  *value,
55                                                GParamSpec              *pspec);
56 static void pulse_stream_control_set_property (GObject                 *object,
57                                                guint                    param_id,
58                                                const GValue            *value,
59                                                GParamSpec              *pspec);
60 
61 static void pulse_stream_control_dispose      (GObject                 *object);
62 static void pulse_stream_control_finalize     (GObject                 *object);
63 
64 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (PulseStreamControl, pulse_stream_control, MATE_MIXER_TYPE_STREAM_CONTROL)
65 
66 static MateMixerAppInfo *       pulse_stream_control_get_app_info         (MateMixerStreamControl   *mmsc);
67 
68 static gboolean                 pulse_stream_control_set_mute             (MateMixerStreamControl   *mmsc,
69                                                                            gboolean                  mute);
70 
71 static guint                    pulse_stream_control_get_num_channels     (MateMixerStreamControl   *mmsc);
72 
73 static guint                    pulse_stream_control_get_volume           (MateMixerStreamControl   *mmsc);
74 static gboolean                 pulse_stream_control_set_volume           (MateMixerStreamControl   *mmsc,
75                                                                            guint                     volume);
76 
77 static gdouble                  pulse_stream_control_get_decibel          (MateMixerStreamControl   *mmsc);
78 static gboolean                 pulse_stream_control_set_decibel          (MateMixerStreamControl   *mmsc,
79                                                                            gdouble                   decibel);
80 
81 static guint                    pulse_stream_control_get_channel_volume   (MateMixerStreamControl   *mmsc,
82                                                                            guint                     channel);
83 static gboolean                 pulse_stream_control_set_channel_volume   (MateMixerStreamControl   *mmsc,
84                                                                            guint                     channel,
85                                                                            guint                     volume);
86 
87 static gdouble                  pulse_stream_control_get_channel_decibel  (MateMixerStreamControl   *mmsc,
88                                                                            guint                     channel);
89 static gboolean                 pulse_stream_control_set_channel_decibel  (MateMixerStreamControl   *mmsc,
90                                                                            guint                     channel,
91                                                                            gdouble                   decibel);
92 
93 static MateMixerChannelPosition pulse_stream_control_get_channel_position (MateMixerStreamControl   *mmsc,
94                                                                            guint                     channel);
95 static gboolean                 pulse_stream_control_has_channel_position (MateMixerStreamControl   *mmsc,
96                                                                            MateMixerChannelPosition  position);
97 
98 static gboolean                 pulse_stream_control_set_balance          (MateMixerStreamControl   *mmsc,
99                                                                            gfloat                    balance);
100 
101 static gboolean                 pulse_stream_control_set_fade             (MateMixerStreamControl   *mmsc,
102                                                                            gfloat                    fade);
103 
104 static gboolean                 pulse_stream_control_get_monitor_enabled  (MateMixerStreamControl   *mmsc);
105 static gboolean                 pulse_stream_control_set_monitor_enabled  (MateMixerStreamControl   *mmsc,
106                                                                            gboolean                  enabled);
107 
108 static guint                    pulse_stream_control_get_min_volume       (MateMixerStreamControl   *mmsc);
109 static guint                    pulse_stream_control_get_max_volume       (MateMixerStreamControl   *mmsc);
110 static guint                    pulse_stream_control_get_normal_volume    (MateMixerStreamControl   *mmsc);
111 static guint                    pulse_stream_control_get_base_volume      (MateMixerStreamControl   *mmsc);
112 
113 static void                     on_monitor_value (PulseMonitor       *monitor,
114                                                   gdouble             value,
115                                                   PulseStreamControl *control);
116 
117 static void                     set_balance_fade (PulseStreamControl *control);
118 
119 static gboolean                 set_cvolume      (PulseStreamControl *control,
120                                                   pa_cvolume         *cvolume);
121 
122 static void
pulse_stream_control_class_init(PulseStreamControlClass * klass)123 pulse_stream_control_class_init (PulseStreamControlClass *klass)
124 {
125     GObjectClass                *object_class;
126     MateMixerStreamControlClass *control_class;
127 
128     object_class = G_OBJECT_CLASS (klass);
129     object_class->dispose      = pulse_stream_control_dispose;
130     object_class->finalize     = pulse_stream_control_finalize;
131     object_class->get_property = pulse_stream_control_get_property;
132     object_class->set_property = pulse_stream_control_set_property;
133 
134     control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass);
135     control_class->get_app_info         = pulse_stream_control_get_app_info;
136     control_class->set_mute             = pulse_stream_control_set_mute;
137     control_class->get_num_channels     = pulse_stream_control_get_num_channels;
138     control_class->get_volume           = pulse_stream_control_get_volume;
139     control_class->set_volume           = pulse_stream_control_set_volume;
140     control_class->get_decibel          = pulse_stream_control_get_decibel;
141     control_class->set_decibel          = pulse_stream_control_set_decibel;
142     control_class->get_channel_volume   = pulse_stream_control_get_channel_volume;
143     control_class->set_channel_volume   = pulse_stream_control_set_channel_volume;
144     control_class->get_channel_decibel  = pulse_stream_control_get_channel_decibel;
145     control_class->set_channel_decibel  = pulse_stream_control_set_channel_decibel;
146     control_class->get_channel_position = pulse_stream_control_get_channel_position;
147     control_class->has_channel_position = pulse_stream_control_has_channel_position;
148     control_class->set_balance          = pulse_stream_control_set_balance;
149     control_class->set_fade             = pulse_stream_control_set_fade;
150     control_class->get_monitor_enabled  = pulse_stream_control_get_monitor_enabled;
151     control_class->set_monitor_enabled  = pulse_stream_control_set_monitor_enabled;
152     control_class->get_min_volume       = pulse_stream_control_get_min_volume;
153     control_class->get_max_volume       = pulse_stream_control_get_max_volume;
154     control_class->get_normal_volume    = pulse_stream_control_get_normal_volume;
155     control_class->get_base_volume      = pulse_stream_control_get_base_volume;
156 
157     properties[PROP_INDEX] =
158         g_param_spec_uint ("index",
159                            "Index",
160                            "Index of the stream control",
161                            0,
162                            G_MAXUINT,
163                            0,
164                            G_PARAM_READWRITE |
165                            G_PARAM_CONSTRUCT_ONLY |
166                            G_PARAM_STATIC_STRINGS);
167 
168     properties[PROP_CONNECTION] =
169         g_param_spec_object ("connection",
170                              "Connection",
171                              "PulseAudio connection",
172                              PULSE_TYPE_CONNECTION,
173                              G_PARAM_READWRITE |
174                              G_PARAM_CONSTRUCT_ONLY |
175                              G_PARAM_STATIC_STRINGS);
176 
177     g_object_class_install_properties (object_class, N_PROPERTIES, properties);
178 }
179 
180 static void
pulse_stream_control_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)181 pulse_stream_control_get_property (GObject    *object,
182                                    guint       param_id,
183                                    GValue     *value,
184                                    GParamSpec *pspec)
185 {
186     PulseStreamControl *control;
187 
188     control = PULSE_STREAM_CONTROL (object);
189 
190     switch (param_id) {
191     case PROP_INDEX:
192         g_value_set_uint (value, control->priv->index);
193         break;
194     case PROP_CONNECTION:
195         g_value_set_object (value, control->priv->connection);
196         break;
197     default:
198         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
199         break;
200     }
201 }
202 
203 static void
pulse_stream_control_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)204 pulse_stream_control_set_property (GObject      *object,
205                                    guint         param_id,
206                                    const GValue *value,
207                                    GParamSpec   *pspec)
208 {
209     PulseStreamControl *control;
210 
211     control = PULSE_STREAM_CONTROL (object);
212 
213     switch (param_id) {
214     case PROP_INDEX:
215         control->priv->index = g_value_get_uint (value);
216         break;
217     case PROP_CONNECTION:
218         control->priv->connection = g_value_dup_object (value);
219         break;
220     default:
221         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
222         break;
223     }
224 }
225 
226 static void
pulse_stream_control_init(PulseStreamControl * control)227 pulse_stream_control_init (PulseStreamControl *control)
228 {
229     control->priv = pulse_stream_control_get_instance_private (control);
230 
231     /* Initialize empty volume and channel map structures, they will be used
232      * if the stream does not support volume */
233     pa_cvolume_init (&control->priv->cvolume);
234 
235     pa_channel_map_init (&control->priv->channel_map);
236 }
237 
238 static void
pulse_stream_control_dispose(GObject * object)239 pulse_stream_control_dispose (GObject *object)
240 {
241     PulseStreamControl *control;
242 
243     control = PULSE_STREAM_CONTROL (object);
244 
245     g_clear_object (&control->priv->monitor);
246     g_clear_object (&control->priv->connection);
247 
248     G_OBJECT_CLASS (pulse_stream_control_parent_class)->dispose (object);
249 }
250 
251 static void
pulse_stream_control_finalize(GObject * object)252 pulse_stream_control_finalize (GObject *object)
253 {
254     PulseStreamControl *control;
255 
256     control = PULSE_STREAM_CONTROL (object);
257 
258     if (control->priv->app_info != NULL)
259         _mate_mixer_app_info_free (control->priv->app_info);
260 
261     G_OBJECT_CLASS (pulse_stream_control_parent_class)->finalize (object);
262 }
263 
264 guint32
pulse_stream_control_get_index(PulseStreamControl * control)265 pulse_stream_control_get_index (PulseStreamControl *control)
266 {
267     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), PA_INVALID_INDEX);
268 
269     return control->priv->index;
270 }
271 
272 guint32
pulse_stream_control_get_stream_index(PulseStreamControl * control)273 pulse_stream_control_get_stream_index (PulseStreamControl *control)
274 {
275     MateMixerStream *stream;
276 
277     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), PA_INVALID_INDEX);
278 
279     stream = mate_mixer_stream_control_get_stream (MATE_MIXER_STREAM_CONTROL (control));
280     if (G_UNLIKELY (stream == NULL))
281         return PA_INVALID_INDEX;
282 
283     return pulse_stream_get_index (PULSE_STREAM (stream));
284 }
285 
286 PulseConnection *
pulse_stream_control_get_connection(PulseStreamControl * control)287 pulse_stream_control_get_connection (PulseStreamControl *control)
288 {
289     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL);
290 
291     return control->priv->connection;
292 }
293 
294 PulseMonitor *
pulse_stream_control_get_monitor(PulseStreamControl * control)295 pulse_stream_control_get_monitor (PulseStreamControl *control)
296 {
297     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL);
298 
299     return control->priv->monitor;
300 }
301 
302 const pa_cvolume *
pulse_stream_control_get_cvolume(PulseStreamControl * control)303 pulse_stream_control_get_cvolume (PulseStreamControl *control)
304 {
305     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL);
306 
307     return &control->priv->cvolume;
308 }
309 
310 const pa_channel_map *
pulse_stream_control_get_channel_map(PulseStreamControl * control)311 pulse_stream_control_get_channel_map (PulseStreamControl *control)
312 {
313     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL);
314 
315     return &control->priv->channel_map;
316 }
317 
318 void
pulse_stream_control_set_app_info(PulseStreamControl * control,MateMixerAppInfo * info,gboolean take)319 pulse_stream_control_set_app_info (PulseStreamControl *control,
320                                    MateMixerAppInfo   *info,
321                                    gboolean            take)
322 {
323     g_return_if_fail (PULSE_IS_STREAM_CONTROL (control));
324 
325     if (G_UNLIKELY (control->priv->app_info != NULL))
326         _mate_mixer_app_info_free (control->priv->app_info);
327 
328     if (take == TRUE)
329         control->priv->app_info = info;
330     else
331         control->priv->app_info = _mate_mixer_app_info_copy (info);
332 }
333 
334 void
pulse_stream_control_set_channel_map(PulseStreamControl * control,const pa_channel_map * map)335 pulse_stream_control_set_channel_map (PulseStreamControl *control, const pa_channel_map *map)
336 {
337     MateMixerStreamControlFlags flags;
338 
339     g_return_if_fail (PULSE_IS_STREAM_CONTROL (control));
340 
341     flags = mate_mixer_stream_control_get_flags (MATE_MIXER_STREAM_CONTROL (control));
342 
343     if (map != NULL && pa_channel_map_valid (map)) {
344         if (pa_channel_map_can_balance (map))
345             flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE;
346         else
347             flags &= ~MATE_MIXER_STREAM_CONTROL_CAN_BALANCE;
348 
349         if (pa_channel_map_can_fade (map))
350             flags |= MATE_MIXER_STREAM_CONTROL_CAN_FADE;
351         else
352             flags &= ~MATE_MIXER_STREAM_CONTROL_CAN_FADE;
353 
354         control->priv->channel_map = *map;
355     } else {
356         flags &= ~(MATE_MIXER_STREAM_CONTROL_CAN_BALANCE | MATE_MIXER_STREAM_CONTROL_CAN_FADE);
357 
358         /* If the channel map is not valid, create an empty channel map, which
359          * also won't validate, but at least we know what it is */
360         pa_channel_map_init (&control->priv->channel_map);
361     }
362 
363     _mate_mixer_stream_control_set_flags (MATE_MIXER_STREAM_CONTROL (control), flags);
364 }
365 
366 void
pulse_stream_control_set_cvolume(PulseStreamControl * control,const pa_cvolume * cvolume,pa_volume_t base_volume)367 pulse_stream_control_set_cvolume (PulseStreamControl *control,
368                                   const pa_cvolume   *cvolume,
369                                   pa_volume_t         base_volume)
370 {
371     MateMixerStreamControlFlags flags;
372 
373     g_return_if_fail (PULSE_IS_STREAM_CONTROL (control));
374 
375     /* The base volume is not a property */
376     control->priv->base_volume = base_volume;
377 
378     flags = mate_mixer_stream_control_get_flags (MATE_MIXER_STREAM_CONTROL (control));
379 
380     g_object_freeze_notify (G_OBJECT (control));
381 
382     if (cvolume != NULL && pa_cvolume_valid (cvolume)) {
383         /* Decibel volume and volume settability flags must be provided by
384          * the implementation */
385         flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE;
386 
387         if (pa_cvolume_equal (&control->priv->cvolume, cvolume) == 0) {
388             control->priv->cvolume = *cvolume;
389             control->priv->volume  = (guint) pa_cvolume_max (&control->priv->cvolume);
390 
391             g_object_notify (G_OBJECT (control), "volume");
392         }
393     } else {
394         flags &= ~(MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE |
395                    MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE |
396                    MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL);
397 
398         /* If the cvolume is not valid, create an empty cvolume, which also
399          * won't validate, but at least we know what it is */
400         pa_cvolume_init (&control->priv->cvolume);
401 
402         if (control->priv->volume != (guint) PA_VOLUME_MUTED) {
403             control->priv->volume = (guint) PA_VOLUME_MUTED;
404 
405             g_object_notify (G_OBJECT (control), "volume");
406         }
407     }
408 
409     _mate_mixer_stream_control_set_flags (MATE_MIXER_STREAM_CONTROL (control), flags);
410 
411     /* Changing volume may change the balance and fade values as well */
412     set_balance_fade (control);
413 
414     g_object_thaw_notify (G_OBJECT (control));
415 }
416 
417 static MateMixerAppInfo *
pulse_stream_control_get_app_info(MateMixerStreamControl * mmsc)418 pulse_stream_control_get_app_info (MateMixerStreamControl *mmsc)
419 {
420     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), NULL);
421 
422     return PULSE_STREAM_CONTROL (mmsc)->priv->app_info;
423 }
424 
425 static gboolean
pulse_stream_control_set_mute(MateMixerStreamControl * mmsc,gboolean mute)426 pulse_stream_control_set_mute (MateMixerStreamControl *mmsc, gboolean mute)
427 {
428     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
429 
430     return PULSE_STREAM_CONTROL_GET_CLASS (mmsc)->set_mute (PULSE_STREAM_CONTROL (mmsc), mute);
431 }
432 
433 static guint
pulse_stream_control_get_num_channels(MateMixerStreamControl * mmsc)434 pulse_stream_control_get_num_channels (MateMixerStreamControl *mmsc)
435 {
436     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), 0);
437 
438     return PULSE_STREAM_CONTROL (mmsc)->priv->channel_map.channels;
439 }
440 
441 static guint
pulse_stream_control_get_volume(MateMixerStreamControl * mmsc)442 pulse_stream_control_get_volume (MateMixerStreamControl *mmsc)
443 {
444     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED);
445 
446     return PULSE_STREAM_CONTROL (mmsc)->priv->volume;
447 }
448 
449 static gboolean
pulse_stream_control_set_volume(MateMixerStreamControl * mmsc,guint volume)450 pulse_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume)
451 {
452     PulseStreamControl *control;
453     pa_cvolume          cvolume;
454 
455     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
456 
457     control = PULSE_STREAM_CONTROL (mmsc);
458     cvolume = control->priv->cvolume;
459 
460     if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL)
461         return FALSE;
462 
463     return set_cvolume (control, &cvolume);
464 }
465 
466 static gdouble
pulse_stream_control_get_decibel(MateMixerStreamControl * mmsc)467 pulse_stream_control_get_decibel (MateMixerStreamControl *mmsc)
468 {
469     gdouble value;
470 
471     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), -MATE_MIXER_INFINITY);
472 
473     value = pa_sw_volume_to_dB (pulse_stream_control_get_volume (mmsc));
474 
475     /* PA_VOLUME_MUTED is converted to PA_DECIBEL_MININFTY */
476     return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value;
477 }
478 
479 static gboolean
pulse_stream_control_set_decibel(MateMixerStreamControl * mmsc,gdouble decibel)480 pulse_stream_control_set_decibel (MateMixerStreamControl *mmsc, gdouble decibel)
481 {
482     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
483 
484     return pulse_stream_control_set_volume (mmsc,
485                                             pa_sw_volume_from_dB (decibel));
486 }
487 
488 static guint
pulse_stream_control_get_channel_volume(MateMixerStreamControl * mmsc,guint channel)489 pulse_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, guint channel)
490 {
491     PulseStreamControl *control;
492 
493     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED);
494 
495     control = PULSE_STREAM_CONTROL (mmsc);
496 
497     if (channel >= control->priv->cvolume.channels)
498         return (guint) PA_VOLUME_MUTED;
499 
500     return (guint) control->priv->cvolume.values[channel];
501 }
502 
503 static gboolean
pulse_stream_control_set_channel_volume(MateMixerStreamControl * mmsc,guint channel,guint volume)504 pulse_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, guint channel, guint volume)
505 {
506     PulseStreamControl *control;
507     pa_cvolume          cvolume;
508 
509     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
510 
511     control = PULSE_STREAM_CONTROL (mmsc);
512 
513     if (channel >= control->priv->cvolume.channels)
514         return FALSE;
515 
516     /* This is safe, because the cvolume is validated by set_cvolume() */
517     cvolume = control->priv->cvolume;
518     cvolume.values[channel] = (pa_volume_t) volume;
519 
520     return set_cvolume (control, &cvolume);
521 }
522 
523 static gdouble
pulse_stream_control_get_channel_decibel(MateMixerStreamControl * mmsc,guint channel)524 pulse_stream_control_get_channel_decibel (MateMixerStreamControl *mmsc, guint channel)
525 {
526     PulseStreamControl *control;
527     gdouble             value;
528 
529     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), -MATE_MIXER_INFINITY);
530 
531     control = PULSE_STREAM_CONTROL (mmsc);
532 
533     if (channel >= control->priv->cvolume.channels)
534         return -MATE_MIXER_INFINITY;
535 
536     value = pa_sw_volume_to_dB (control->priv->cvolume.values[channel]);
537 
538     return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value;
539 }
540 
541 static gboolean
pulse_stream_control_set_channel_decibel(MateMixerStreamControl * mmsc,guint channel,gdouble decibel)542 pulse_stream_control_set_channel_decibel (MateMixerStreamControl *mmsc,
543                                           guint                   channel,
544                                           gdouble                 decibel)
545 {
546     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
547 
548     return pulse_stream_control_set_channel_volume (mmsc,
549                                                     channel,
550                                                     pa_sw_volume_from_dB (decibel));
551 }
552 
553 static MateMixerChannelPosition
pulse_stream_control_get_channel_position(MateMixerStreamControl * mmsc,guint channel)554 pulse_stream_control_get_channel_position (MateMixerStreamControl *mmsc, guint channel)
555 {
556     PulseStreamControl *control;
557 
558     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), MATE_MIXER_CHANNEL_UNKNOWN);
559 
560     control = PULSE_STREAM_CONTROL (mmsc);
561 
562     if (channel >= control->priv->channel_map.channels)
563         return MATE_MIXER_CHANNEL_UNKNOWN;
564 
565     if (control->priv->channel_map.map[channel] == PA_CHANNEL_POSITION_INVALID)
566         return MATE_MIXER_CHANNEL_UNKNOWN;
567 
568     return pulse_channel_map_from[control->priv->channel_map.map[channel]];
569 }
570 
571 static gboolean
pulse_stream_control_has_channel_position(MateMixerStreamControl * mmsc,MateMixerChannelPosition position)572 pulse_stream_control_has_channel_position (MateMixerStreamControl  *mmsc,
573                                            MateMixerChannelPosition position)
574 {
575     PulseStreamControl *control;
576 
577     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
578 
579     control = PULSE_STREAM_CONTROL (mmsc);
580 
581     /* Handle invalid position as a special case, otherwise this function would
582      * return TRUE for e.g. unknown index in a default channel map */
583     if (pulse_channel_map_to[position] == PA_CHANNEL_POSITION_INVALID)
584         return FALSE;
585 
586     if (pa_channel_map_has_position (&control->priv->channel_map,
587                                      pulse_channel_map_to[position]) != 0)
588         return TRUE;
589     else
590         return FALSE;
591 }
592 
593 static gboolean
pulse_stream_control_set_balance(MateMixerStreamControl * mmsc,gfloat balance)594 pulse_stream_control_set_balance (MateMixerStreamControl *mmsc, gfloat balance)
595 {
596     PulseStreamControl *control;
597     pa_cvolume          cvolume;
598 
599     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
600 
601     control = PULSE_STREAM_CONTROL (mmsc);
602     cvolume = control->priv->cvolume;
603 
604     if (pa_cvolume_set_balance (&cvolume, &control->priv->channel_map, balance) == NULL)
605         return FALSE;
606 
607     return set_cvolume (control, &cvolume);
608 }
609 
610 static gboolean
pulse_stream_control_set_fade(MateMixerStreamControl * mmsc,gfloat fade)611 pulse_stream_control_set_fade (MateMixerStreamControl *mmsc, gfloat fade)
612 {
613     PulseStreamControl *control;
614     pa_cvolume          cvolume;
615 
616     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
617 
618     control = PULSE_STREAM_CONTROL (mmsc);
619     cvolume = control->priv->cvolume;
620 
621     if (pa_cvolume_set_fade (&cvolume, &control->priv->channel_map, fade) == NULL)
622         return FALSE;
623 
624     return set_cvolume (control, &cvolume);
625 }
626 
627 static gboolean
pulse_stream_control_get_monitor_enabled(MateMixerStreamControl * mmsc)628 pulse_stream_control_get_monitor_enabled (MateMixerStreamControl *mmsc)
629 {
630     PulseStreamControl *control;
631 
632     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
633 
634     control = PULSE_STREAM_CONTROL (mmsc);
635 
636     if (control->priv->monitor != NULL)
637         return pulse_monitor_get_enabled (control->priv->monitor);
638 
639     return FALSE;
640 }
641 
642 static gboolean
pulse_stream_control_set_monitor_enabled(MateMixerStreamControl * mmsc,gboolean enabled)643 pulse_stream_control_set_monitor_enabled (MateMixerStreamControl *mmsc, gboolean enabled)
644 {
645     PulseStreamControl *control;
646 
647     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
648 
649     control = PULSE_STREAM_CONTROL (mmsc);
650 
651     if (enabled == TRUE) {
652         if (control->priv->monitor == NULL) {
653             control->priv->monitor =
654                 PULSE_STREAM_CONTROL_GET_CLASS (control)->create_monitor (control);
655 
656             if (G_UNLIKELY (control->priv->monitor == NULL))
657                 return FALSE;
658 
659             g_signal_connect (G_OBJECT (control->priv->monitor),
660                               "value",
661                               G_CALLBACK (on_monitor_value),
662                               control);
663         }
664     } else {
665         if (control->priv->monitor == NULL)
666             return FALSE;
667     }
668     return pulse_monitor_set_enabled (control->priv->monitor, enabled);
669 }
670 
671 static guint
pulse_stream_control_get_min_volume(MateMixerStreamControl * mmsc)672 pulse_stream_control_get_min_volume (MateMixerStreamControl *mmsc)
673 {
674     return (guint) PA_VOLUME_MUTED;
675 }
676 
677 static guint
pulse_stream_control_get_max_volume(MateMixerStreamControl * mmsc)678 pulse_stream_control_get_max_volume (MateMixerStreamControl *mmsc)
679 {
680     MateMixerStreamControlFlags flags;
681 
682     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED);
683 
684     flags = mate_mixer_stream_control_get_flags (mmsc);
685 
686     /*
687      * From PulseAudio wiki:
688      * For all volumes that are > PA_VOLUME_NORM (i.e. beyond the maximum volume
689      * setting of the hw) PA will do digital amplification. This works only for
690      * devices that have PA_SINK_DECIBEL_VOLUME/PA_SOURCE_DECIBEL_VOLUME set. For
691      * devices that lack this flag do not extend the volume slider like this, it
692      * will not have any effect.
693      */
694     if (flags & MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL)
695         return (guint) PA_VOLUME_UI_MAX;
696     else
697         return (guint) PA_VOLUME_NORM;
698 }
699 
700 static guint
pulse_stream_control_get_normal_volume(MateMixerStreamControl * mmsc)701 pulse_stream_control_get_normal_volume (MateMixerStreamControl *mmsc)
702 {
703     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED);
704 
705     return (guint) PA_VOLUME_NORM;
706 }
707 
708 static guint
pulse_stream_control_get_base_volume(MateMixerStreamControl * mmsc)709 pulse_stream_control_get_base_volume (MateMixerStreamControl *mmsc)
710 {
711     PulseStreamControl *control;
712 
713     g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED);
714 
715     control = PULSE_STREAM_CONTROL (mmsc);
716 
717     if (control->priv->base_volume > 0)
718         return (guint) control->priv->base_volume;
719     else
720         return (guint) PA_VOLUME_NORM;
721 }
722 
723 static void
on_monitor_value(PulseMonitor * monitor,gdouble value,PulseStreamControl * control)724 on_monitor_value (PulseMonitor *monitor, gdouble value, PulseStreamControl *control)
725 {
726     g_signal_emit_by_name (G_OBJECT (control),
727                            "monitor-value",
728                            value);
729 }
730 
731 static void
set_balance_fade(PulseStreamControl * control)732 set_balance_fade (PulseStreamControl *control)
733 {
734     gfloat value;
735 
736     /* PulseAudio returns the default 0.0f value on error, so skip checking validity
737      * of the channel map and cvolume */
738     value = pa_cvolume_get_balance (&control->priv->cvolume,
739                                     &control->priv->channel_map);
740 
741     _mate_mixer_stream_control_set_balance (MATE_MIXER_STREAM_CONTROL (control), value);
742 
743     value = pa_cvolume_get_fade (&control->priv->cvolume,
744                                  &control->priv->channel_map);
745 
746     _mate_mixer_stream_control_set_fade (MATE_MIXER_STREAM_CONTROL (control), value);
747 }
748 
749 static gboolean
set_cvolume(PulseStreamControl * control,pa_cvolume * cvolume)750 set_cvolume (PulseStreamControl *control, pa_cvolume *cvolume)
751 {
752     PulseStreamControlClass *klass;
753 
754     if (pa_cvolume_valid (cvolume) == 0)
755         return FALSE;
756     if (pa_cvolume_equal (cvolume, &control->priv->cvolume) != 0)
757         return TRUE;
758 
759     klass = PULSE_STREAM_CONTROL_GET_CLASS (control);
760 
761     if (klass->set_volume (control, cvolume) == FALSE)
762         return FALSE;
763 
764     control->priv->cvolume = *cvolume;
765     control->priv->volume  = (guint) pa_cvolume_max (cvolume);
766 
767     g_object_notify (G_OBJECT (control), "volume");
768 
769     /* Changing volume may change the balance and fade values as well */
770     set_balance_fade (control);
771     return TRUE;
772 }
773