snd_polyp_t *p;
 
-    char *device;
+    char *source;
+    char *sink;
 
-    pa_cvolume volume;
+    pa_cvolume sink_volume;
+    pa_cvolume source_volume;
+
+    int sink_muted;
+    int source_muted;
 
     int subscribed;
     int updated;
 } snd_ctl_polyp_t;
 
-#define MIXER_NAME "Master"
+#define SOURCE_VOL_NAME "Capture Volume"
+#define SOURCE_MUTE_NAME "Capture Switch"
+#define SINK_VOL_NAME "Master Playback Volume"
+#define SINK_MUTE_NAME "Master Playback Switch"
 
 static void sink_info_cb(pa_context *c, const pa_sink_info *i, int is_last, void *userdata)
 {
 
     assert(ctl && i);
 
-    if (ctl->volume.channels == i->volume.channels) {
-        for (chan = 0;chan < ctl->volume.channels;chan++)
-            if (i->volume.values[chan] != ctl->volume.values[chan])
+    if (ctl->sink_volume.channels == i->volume.channels) {
+        for (chan = 0;chan < ctl->sink_volume.channels;chan++)
+            if (i->volume.values[chan] != ctl->sink_volume.values[chan])
                 break;
 
-        if (chan == ctl->volume.channels)
+        if (chan == ctl->sink_volume.channels)
             return;
 
         ctl->updated = 1;
     }
 
-    memcpy(&ctl->volume, &i->volume, sizeof(pa_cvolume));
+    memcpy(&ctl->sink_volume, &i->volume, sizeof(pa_cvolume));
+
+    if (!!ctl->sink_muted != !!i->mute) {
+        ctl->sink_muted = i->mute;
+        ctl->updated = 1;
+    }
+}
+
+static void source_info_cb(pa_context *c, const pa_source_info *i, int is_last, void *userdata)
+{
+    snd_ctl_polyp_t *ctl = (snd_ctl_polyp_t*)userdata;
+    int chan;
+
+    if (is_last)
+        return;
+
+    assert(ctl && i);
+
+    if (ctl->source_volume.channels == i->volume.channels) {
+        for (chan = 0;chan < ctl->source_volume.channels;chan++)
+            if (i->volume.values[chan] != ctl->source_volume.values[chan])
+                break;
+
+        if (chan == ctl->source_volume.channels)
+            return;
+
+        ctl->updated = 1;
+    }
+
+    memcpy(&ctl->source_volume, &i->volume, sizeof(pa_cvolume));
+
+    if (!!ctl->source_muted != !!i->mute) {
+        ctl->source_muted = i->mute;
+        ctl->updated = 1;
+    }
 }
 
 static void event_cb(pa_context *c, pa_subscription_event_type_t t,
 
     assert(ctl && ctl->p && ctl->p->context);
 
-    o = pa_context_get_sink_info_by_name(ctl->p->context, ctl->device,
+    o = pa_context_get_sink_info_by_name(ctl->p->context, ctl->sink,
         sink_info_cb, ctl);
     pa_operation_unref(o);
+
+    o = pa_context_get_source_info_by_name(ctl->p->context, ctl->source,
+        source_info_cb, ctl);
+    pa_operation_unref(o);
 }
 
 static int polyp_update_volume(snd_ctl_polyp_t *ctl)
 
     assert(ctl && ctl->p && ctl->p->context);
 
-    o = pa_context_get_sink_info_by_name(ctl->p->context, ctl->device,
+    o = pa_context_get_sink_info_by_name(ctl->p->context, ctl->sink,
         sink_info_cb, ctl);
     err = polyp_wait_operation(ctl->p, o);
     pa_operation_unref(o);
     if (err < 0)
         return err;
 
+    o = pa_context_get_source_info_by_name(ctl->p->context, ctl->source,
+        source_info_cb, ctl);
+    err = polyp_wait_operation(ctl->p, o);
+    pa_operation_unref(o);
+    if (err < 0)
+        return err;
+
     return 0;
 }
 
 static int polyp_elem_count(snd_ctl_ext_t *ext)
 {
     snd_ctl_polyp_t *ctl = ext->private_data;
+    int count = 0;
 
     assert(ctl);
 
-    if (ctl->device)
-        return 1;
+    if (ctl->source)
+        count += 2;
+    if (ctl->sink)
+        count += 2;
 
-    return 0;
+    return count;
 }
 
 static int polyp_elem_list(snd_ctl_ext_t *ext, unsigned int offset,
     assert(ctl);
 
     snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
-    snd_ctl_elem_id_set_name(id, MIXER_NAME);
+
+    if (ctl->source) {
+        if (offset == 0)
+            snd_ctl_elem_id_set_name(id, SOURCE_VOL_NAME);
+        else if (offset == 1)
+            snd_ctl_elem_id_set_name(id, SOURCE_MUTE_NAME);
+    } else
+        offset += 2;
+
+    if (offset == 2)
+        snd_ctl_elem_id_set_name(id, SINK_VOL_NAME);
+    else if (offset == 3)
+        snd_ctl_elem_id_set_name(id, SINK_MUTE_NAME);
 
     return 0;
 }
 
     name = snd_ctl_elem_id_get_name(id);
 
-    if (strcmp(name, MIXER_NAME) == 0)
+    if (strcmp(name, SOURCE_VOL_NAME) == 0)
         return 0;
+    if (strcmp(name, SOURCE_MUTE_NAME) == 0)
+        return 1;
+    if (strcmp(name, SINK_VOL_NAME) == 0)
+        return 2;
+    if (strcmp(name, SINK_MUTE_NAME) == 0)
+        return 3;
 
     return SND_CTL_EXT_KEY_NOT_FOUND;
 }
 
     assert(ctl && ctl->p);
 
-    if (key != 0)
+    if (key > 3)
         return -EINVAL;
 
     err = polyp_finish_poll(ctl->p);
     if (err < 0)
         return err;
 
-    *type = SND_CTL_ELEM_TYPE_INTEGER;
+    if (key & 1)
+        *type = SND_CTL_ELEM_TYPE_BOOLEAN;
+    else
+        *type = SND_CTL_ELEM_TYPE_INTEGER;
+
     *acc = SND_CTL_EXT_ACCESS_READWRITE;
-    *count = ctl->volume.channels;
+
+    if (key == 0)
+        *count = ctl->source_volume.channels;
+    else if (key == 2)
+        *count = ctl->sink_volume.channels;
+    else
+        *count = 1;
 
     return 0;
 }
 static int polyp_get_integer_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
     long *imin, long *imax, long *istep)
 {
-    if (key != 0)
-        return -EINVAL;
-
     *istep = 1;
     *imin = 0;
     *imax = PA_VOLUME_NORM;
 {
     snd_ctl_polyp_t *ctl = ext->private_data;
     int err, i;
+    pa_cvolume *vol = NULL;
 
     assert(ctl && ctl->p);
 
-    if (key != 0)
-        return -EINVAL;
-
     err = polyp_finish_poll(ctl->p);
     if (err < 0)
         return err;
     if (err < 0)
         return err;
 
-    for (i = 0;i < ctl->volume.channels;i++)
-        value[i] = ctl->volume.values[i];
+    switch (key) {
+    case 0:
+        vol = &ctl->source_volume;
+        break;
+    case 1:
+        *value = !ctl->source_muted;
+        break;
+    case 2:
+        vol = &ctl->sink_volume;
+        break;
+    case 3:
+        *value = !ctl->sink_muted;
+        break;
+    default:
+        return -EINVAL;
+    }
+
+    if (vol) {
+        for (i = 0;i < vol->channels;i++)
+            value[i] = vol->values[i];
+    }
 
     return 0;
 }
 {
     snd_ctl_polyp_t *ctl = ext->private_data;
     int err, i;
-    pa_cvolume vol;
     pa_operation *o;
+    pa_cvolume *vol = NULL;
 
     assert(ctl && ctl->p && ctl->p->context);
 
-    if (key != 0)
-        return -EINVAL;
-
     err = polyp_finish_poll(ctl->p);
     if (err < 0)
         return err;
     if (err < 0)
         return err;
 
-    for (i = 0;i < ctl->volume.channels;i++)
-        if (value[i] != ctl->volume.values[i])
-            break;
-
-    if (i == ctl->volume.channels)
-        return 0;
-
-    memset(&vol, 0, sizeof(pa_cvolume));
+    switch (key) {
+    case 0:
+        vol = &ctl->source_volume;
+        break;
+    case 1:
+        if (!!ctl->source_muted == !*value)
+            return 0;
+        ctl->source_muted = !*value;
+        break;
+    case 2:
+        vol = &ctl->sink_volume;
+        break;
+    case 3:
+        if (!!ctl->sink_muted == !*value)
+            return 0;
+        ctl->sink_muted = !*value;
+        break;
+    default:
+        return -EINVAL;
+    }
 
-    vol.channels = ctl->volume.channels;
-    for (i = 0;i < vol.channels;i++)
-        vol.values[i] = value[i];
+    if (vol) {
+        for (i = 0;i < vol->channels;i++)
+            if (value[i] != vol->values[i])
+                break;
 
-    o = pa_context_set_sink_volume_by_name(ctl->p->context, ctl->device, &vol,
-        NULL, NULL);
+        if (i == vol->channels)
+            return 0;
+
+        for (i = 0;i < vol->channels;i++)
+            vol->values[i] = value[i];
+
+        if (key == 0)
+            o = pa_context_set_source_volume_by_name(ctl->p->context,
+                ctl->source, vol, NULL, NULL);
+        else
+            o = pa_context_set_sink_volume_by_name(ctl->p->context,
+                ctl->sink, vol, NULL, NULL);
+    } else {
+        if (key == 1)
+            o = pa_context_set_source_mute_by_name(ctl->p->context,
+                ctl->source, ctl->source_muted, NULL, NULL);
+        else
+            o = pa_context_set_sink_mute_by_name(ctl->p->context,
+                ctl->sink, ctl->sink_muted, NULL, NULL);
+    }
 
     err = polyp_wait_operation(ctl->p, o);
     pa_operation_unref(o);
     if (ctl->p)
         polyp_free(ctl->p);
 
-    if (ctl->device)
-        free(ctl->device);
+    if (ctl->source)
+        free(ctl->source);
+    if (ctl->sink)
+        free(ctl->sink);
 
        free(ctl);
 }
 {
     snd_ctl_polyp_t *ctl = (snd_ctl_polyp_t*)userdata;
 
-    assert(ctl && i && i->default_sink_name);
+    assert(ctl && i);
 
-    ctl->device = strdup(i->default_sink_name);
+    if (i->default_source_name && !ctl->source)
+        ctl->source = strdup(i->default_source_name);
+    if (i->default_sink_name && !ctl->sink)
+        ctl->sink = strdup(i->default_sink_name);
 }
 
 SND_CTL_PLUGIN_DEFINE_FUNC(polyp)
        snd_config_iterator_t i, next;
        const char *server = NULL;
        const char *device = NULL;
+       const char *source = NULL;
+       const char *sink = NULL;
        int err;
        snd_ctl_polyp_t *ctl;
     pa_operation *o;
                 return -EINVAL;
             }
             continue;
+        }
+        if (strcmp(id, "source") == 0) {
+            if (snd_config_get_string(n, &source) < 0) {
+                SNDERR("Invalid type for %s", id);
+                return -EINVAL;
+            }
+            continue;
+        }
+        if (strcmp(id, "sink") == 0) {
+            if (snd_config_get_string(n, &sink) < 0) {
+                SNDERR("Invalid type for %s", id);
+                return -EINVAL;
+            }
+            continue;
         }
                SNDERR("Unknown field %s", id);
                return -EINVAL;
     if (err < 0)
         goto error;
 
-    if (device)
-        ctl->device = strdup(device);
-    else {
+    if (source)
+        ctl->source = strdup(source);
+    else if (device)
+        ctl->source = strdup(device);
+
+    if (sink)
+        ctl->sink = strdup(sink);
+    else if (device)
+        ctl->sink = strdup(device);
+
+    if (!ctl->source || !ctl->sink) {
         o = pa_context_get_server_info(ctl->p->context, server_info_cb, ctl);
         err = polyp_wait_operation(ctl->p, o);
         pa_operation_unref(o);
 
     pa_context_set_subscribe_callback(ctl->p->context, event_cb, ctl);
 
-    o = pa_context_subscribe(ctl->p->context, PA_SUBSCRIPTION_MASK_SINK, NULL, NULL);
+    o = pa_context_subscribe(ctl->p->context,
+        PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, NULL, NULL);
     err = polyp_wait_operation(ctl->p, o);
     pa_operation_unref(o);
     if (err < 0)
     return 0;
 
 error:
-    if (ctl->device)
-        free(ctl->device);
+    if (ctl->source)
+        free(ctl->source);
+    if (ctl->sink)
+        free(ctl->sink);
 
     if (ctl->p)
         polyp_free(ctl->p);