summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2006-02-23 14:31:13 +0000
committerTakashi Iwai <tiwai@suse.de>2006-02-23 14:31:13 +0000
commit074572b26a1403418a86872a59af1bfb4f781c71 (patch)
tree67f0bf2e452571af56a889ea260763562aac0d02
parent153757be2e8e5ab65913e7bbd0e5ebcc60c8e467 (diff)
Update polyp plugin
From: Pierre Ossman <ossman@cendio.se> Here is a final update to the plugin and the documentation. This release adds capture volume and mute switches. This should also be the last API change in polypaudio before the next release, so this plugin should be stable for now.
-rw-r--r--doc/README-polyp41
-rw-r--r--polyp/ctl_polyp.c264
2 files changed, 252 insertions, 53 deletions
diff --git a/doc/README-polyp b/doc/README-polyp
new file mode 100644
index 0000000..de46c3d
--- /dev/null
+++ b/doc/README-polyp
@@ -0,0 +1,41 @@
+Polypaudio <--> ALSA plugins
+============================
+
+This plugin allows any program that uses the ALSA API to access a Polypaudio
+sound daemon. In other words, native ALSA applications can play and record
+sound across a network.
+
+There are two plugins in the suite, one for PCM and one for mixer control. A
+typical configuration will look like:
+
+ pcm.polyp {
+ type polyp
+ }
+
+ ctl.polyp {
+ type polyp
+ }
+
+Put the above in ~/.asoundrc, or /etc/asound.conf, and use "polyp" as device
+in your ALSA applications. For example:
+
+ % aplay -Dpolyp foo.wav
+ % amixer -Dpolyp
+
+Polypaudio will accept more or less any format you throw at it. So a plug
+wrapper is unnecessary. Mixing is also handled so dmix will only cause a
+performance hit without any gain.
+
+The plugins will respect your Polypaudio environment variables (like
+POLYP_SERVER), but you can override these in ALSA's configuration files.
+
+Both plugins accept the "server" parameter, specifying which Polypaudio server
+to contact. Both also accept the "device" parameter, which indicate which
+source and sink to use.
+
+The mixer control plugin also accepts the parameters "source" and "sink" for
+when you need to specify a sink/source combination with different names. If
+you need to do this with PCM:s then specify two PCM:s with different "device".
+
+If you do not specify any source and/or sink, then the server's defaults will
+be used.
diff --git a/polyp/ctl_polyp.c b/polyp/ctl_polyp.c
index 16043bf..aba002b 100644
--- a/polyp/ctl_polyp.c
+++ b/polyp/ctl_polyp.c
@@ -30,15 +30,23 @@ typedef struct snd_ctl_polyp {
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)
{
@@ -50,18 +58,52 @@ static void sink_info_cb(pa_context *c, const pa_sink_info *i, int is_last, void
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,
@@ -72,9 +114,13 @@ 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)
@@ -84,26 +130,36 @@ 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,
@@ -114,7 +170,19 @@ 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;
}
@@ -126,8 +194,14 @@ static snd_ctl_ext_key_t polyp_find_elem(snd_ctl_ext_t *ext,
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;
}
@@ -140,7 +214,7 @@ static int polyp_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
assert(ctl && ctl->p);
- if (key != 0)
+ if (key > 3)
return -EINVAL;
err = polyp_finish_poll(ctl->p);
@@ -155,9 +229,19 @@ static int polyp_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
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;
}
@@ -165,9 +249,6 @@ static int polyp_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
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;
@@ -180,12 +261,10 @@ static int polyp_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
{
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;
@@ -198,8 +277,27 @@ static int polyp_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
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;
}
@@ -209,14 +307,11 @@ static int polyp_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
{
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;
@@ -229,21 +324,52 @@ static int polyp_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
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);
@@ -325,8 +451,10 @@ static void polyp_close(snd_ctl_ext_t *ext)
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);
}
@@ -351,9 +479,12 @@ static void server_info_cb(pa_context *c, const pa_server_info*i, void *userdata
{
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)
@@ -361,6 +492,8 @@ 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;
@@ -386,6 +519,20 @@ SND_CTL_PLUGIN_DEFINE_FUNC(polyp)
}
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;
}
@@ -403,9 +550,17 @@ SND_CTL_PLUGIN_DEFINE_FUNC(polyp)
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);
@@ -415,7 +570,8 @@ SND_CTL_PLUGIN_DEFINE_FUNC(polyp)
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)
@@ -441,8 +597,10 @@ SND_CTL_PLUGIN_DEFINE_FUNC(polyp)
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);