From e2d1ed2cb0b066a7b34db15e42af0a4b49626ec8 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 18 Jun 2013 15:00:27 -0400 Subject: Allow setting the microphone volume if an app using it is running --- src/service.vala | 38 ++++++++++++++++++++ src/volume-control.vala | 93 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 126 insertions(+), 5 deletions(-) diff --git a/src/service.vala b/src/service.vala index 269ddd7..83d0d17 100644 --- a/src/service.vala +++ b/src/service.vala @@ -25,6 +25,7 @@ public class IndicatorSound.Service { this.settings = new Settings ("com.canonical.indicator.sound"); this.volume_control = new VolumeControl (); + this.volume_control.notify["active-mic"].connect (active_mic_changed); this.players = new MediaPlayerList (); this.players.player_added.connect (this.player_added); @@ -34,6 +35,7 @@ public class IndicatorSound.Service { this.actions.add_entries (action_entries, this); this.actions.add_action (this.create_mute_action ()); this.actions.add_action (this.create_volume_action ()); + this.actions.add_action (this.create_mic_volume_action ()); this.menu = create_menu (); this.root_menu = create_root_menu (this.menu); @@ -112,6 +114,26 @@ public class IndicatorSound.Service { return menu; } + void active_mic_changed () { + var volume_section = this.menu.get_item_link (0, "section") as Menu; + if (this.volume_control.active_mic) { + if (volume_section.get_n_items () < 3) { + var slider = new MenuItem (null, "indicator.mic-volume"); + slider.set_attribute ("x-canonical-type", "s", "com.canonical.unity.slider"); + slider.set_attribute_value ("min-icon", g_icon_serialize (new ThemedIcon ("audio-input-microphone-low-zero-panel"))); + slider.set_attribute_value ("max-icon", g_icon_serialize (new ThemedIcon ("audio-input-microphone-high-panel"))); + slider.set_attribute ("min-value", "d", 0.0); + slider.set_attribute ("max-value", "d", 1.0); + slider.set_attribute ("step", "d", 0.01); + volume_section.append_item (slider); + } + } + else { + if (volume_section.get_n_items () > 2) + volume_section.remove (2); + } + } + Action create_mute_action () { var mute_action = new SimpleAction.stateful ("mute", null, this.volume_control.mute); @@ -146,6 +168,22 @@ public class IndicatorSound.Service { return volume_action; } + Action create_mic_volume_action () { + var volume_action = new SimpleAction.stateful ("mic-volume", null, this.volume_control.get_mic_volume ()); + + volume_action.change_state.connect ( (action, val) => { + volume_control.set_mic_volume (val.get_double ()); + }); + + this.volume_control.mic_volume_changed.connect ( (volume) => { + volume_action.set_state (volume); + }); + + this.volume_control.bind_property ("ready", volume_action, "enabled", BindingFlags.SYNC_CREATE); + + return volume_action; + } + void bus_acquired (DBusConnection connection, string name) { try { connection.export_action_group ("/com/canonical/indicator/sound", this.actions); diff --git a/src/volume-control.vala b/src/volume-control.vala index 5ce5a05..9475f53 100644 --- a/src/volume-control.vala +++ b/src/volume-control.vala @@ -30,12 +30,17 @@ public class VolumeControl : Object private PulseAudio.Context context; private bool _mute = true; private double _volume = 0.0; + private double _mic_volume = 0.0; public signal void volume_changed (double v); + public signal void mic_volume_changed (double v); /** true when connected to the pulse server */ public bool ready { get; set; } + /** true when a microphone is active **/ + public bool active_mic { get; private set; default = false; } + public VolumeControl () { if (loop == null) @@ -61,9 +66,28 @@ public class VolumeControl : Object /* PulseAudio logic*/ private void context_events_cb (Context c, Context.SubscriptionEventType t, uint32 index) { - if ((t & Context.SubscriptionEventType.FACILITY_MASK) == Context.SubscriptionEventType.SINK) + switch (t & Context.SubscriptionEventType.FACILITY_MASK) { - get_properties (); + case Context.SubscriptionEventType.SINK: + update_sink (); + break; + + case Context.SubscriptionEventType.SOURCE: + update_source (); + break; + + case Context.SubscriptionEventType.SOURCE_OUTPUT: + switch (t & Context.SubscriptionEventType.TYPE_MASK) + { + case Context.SubscriptionEventType.NEW: + c.get_source_output_info (index, source_output_info_cb); + break; + + case Context.SubscriptionEventType.REMOVE: + this.active_mic = false; + break; + } + break; } } @@ -85,6 +109,18 @@ public class VolumeControl : Object } } + private void source_info_cb (Context c, SourceInfo? i, int eol) + { + if (i == null) + return; + + if (_mic_volume != volume_to_double (i.volume.values[0])) + { + _mic_volume = volume_to_double (i.volume.values[0]); + mic_volume_changed (_mic_volume); + } + } + private void server_info_cb_for_props (Context c, ServerInfo? i) { if (i == null) @@ -92,18 +128,39 @@ public class VolumeControl : Object context.get_sink_info_by_name (i.default_sink_name, sink_info_cb_for_props); } - private void get_properties () + private void update_sink () { context.get_server_info (server_info_cb_for_props); } + private void update_source () + { + context.get_server_info ( (c, i) => { + if (i != null) + context.get_source_info_by_name (i.default_source_name, source_info_cb); + }); + } + + private void source_output_info_cb (Context c, SourceOutputInfo? i, int eol) + { + if (i == null) + return; + + var role = i.proplist.gets (PulseAudio.Proplist.PROP_MEDIA_ROLE); + if (role == "phone" || role == "production") + this.active_mic = true; + } + private void context_state_callback (Context c) { if (c.get_state () == Context.State.READY) { - c.subscribe (PulseAudio.Context.SubscriptionMask.SINK); + c.subscribe (PulseAudio.Context.SubscriptionMask.SINK | + PulseAudio.Context.SubscriptionMask.SOURCE | + PulseAudio.Context.SubscriptionMask.SOURCE_OUTPUT); c.set_subscribe_callback (context_events_cb); - get_properties (); + update_sink (); + update_source (); this.ready = true; } else @@ -182,8 +239,34 @@ public class VolumeControl : Object context.get_server_info (server_info_cb_for_set_volume); } + void set_mic_volume_success_cb (Context c, int success) + { + if ((bool)success) + mic_volume_changed (_mic_volume); + } + + public void set_mic_volume (double volume) + { + return_if_fail (context.get_state () == Context.State.READY); + + _mic_volume = volume; + + context.get_server_info ( (c, i) => { + if (i != null) { + unowned CVolume cvol = CVolume (); + cvol = vol_set (cvol, 1, double_to_volume (_mic_volume)); + c.set_source_volume_by_name (i.default_source_name, cvol, set_mic_volume_success_cb); + } + }); + } + public double get_volume () { return _volume; } + + public double get_mic_volume () + { + return _mic_volume; + } } -- cgit v1.2.3