aboutsummaryrefslogtreecommitdiff
path: root/pavolctld.c
diff options
context:
space:
mode:
Diffstat (limited to 'pavolctld.c')
-rw-r--r--pavolctld.c148
1 files changed, 148 insertions, 0 deletions
diff --git a/pavolctld.c b/pavolctld.c
new file mode 100644
index 0000000..deb5796
--- /dev/null
+++ b/pavolctld.c
@@ -0,0 +1,148 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pulse/pulseaudio.h>
+
+//TODO pa_operation_unref(o);
+
+static int default_sink_index;
+static int command_sink_index; // commands will modify this sink
+
+
+static void sink_info_volume_callback(pa_context *c, const pa_sink_info *i, int eol, void *userdata) {
+ pa_volume_t vol;
+ if (eol) return;
+
+ vol = pa_cvolume_avg(&i->volume);
+ printf("v%d,%d,%.02f,%d\n", i->index, vol*100/PA_VOLUME_NORM, pa_sw_volume_to_dB(vol), i->mute);
+ fflush(stdout);
+}
+
+static void sink_info_default_index_callback(pa_context *c, const pa_sink_info *i, int eol, void *userdata) {
+ if (eol) return;
+ default_sink_index = i->index;
+}
+
+static void sink_info_sink_desc_callback(pa_context *c, const pa_sink_info *i, int eol, void *userdata) {
+ if (eol) return;
+ printf("s%d,%s,%s\n", i->index, i->name, i->description);
+ fflush(stdout);
+}
+
+static void server_info_default_sink_callback(pa_context *c, const pa_server_info *i, void *userdata) {
+ pa_context_get_sink_info_by_name(c, i->default_sink_name, sink_info_default_index_callback, userdata);
+ printf("f%d\n", default_sink_index);
+ fflush(stdout);
+}
+
+static void subscription_callback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+ switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
+ case PA_SUBSCRIPTION_EVENT_SINK:
+ // check if sink was removed
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ printf("x%d\n", idx);
+ fflush(stdout);
+ return;
+ }
+
+ // check if sink was added
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW)
+ pa_context_get_sink_info_by_index(c, idx, sink_info_sink_desc_callback, userdata);
+
+ // output the volume
+ pa_context_get_sink_info_by_index(c, idx, sink_info_volume_callback, userdata);
+ break;
+ case PA_SUBSCRIPTION_EVENT_SERVER:
+ pa_context_get_server_info(c, server_info_default_sink_callback, userdata);
+ break;
+ }
+}
+
+static void context_state_callback(pa_context *c, void *userdata) {
+ if (pa_context_get_state(c) == PA_CONTEXT_READY) {
+ pa_context_set_subscribe_callback(c, subscription_callback, userdata);
+
+ // output initial default sink
+ pa_context_get_server_info(c, server_info_default_sink_callback, userdata);
+
+ // set command sink equal to the default sink on startup
+ command_sink_index = default_sink_index;
+
+ // output initial sink descriptions for each sink
+ pa_context_get_sink_info_list(c, sink_info_sink_desc_callback, userdata);
+
+ // output initial volume levels for each sink
+ pa_context_get_sink_info_list(c, sink_info_volume_callback, userdata);
+
+ // subscribe to sink & server events to listen to changes
+ pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SERVER, NULL, NULL);
+ }
+}
+
+/* commands */
+static void sink_info_set_volume_callback(pa_context *c, const pa_sink_info *i, int eol, void *userdata) {
+ pa_cvolume *vol;
+ double input_vol;
+ char *cmd = (char *)userdata;
+ if (eol) return;
+
+ vol = (pa_cvolume *)&i->volume; // disregard const qualifier, avoid warnings :)
+ switch(cmd[1]) {
+ case '\n':
+ pa_context_get_sink_info_by_index(c, i->index, sink_info_volume_callback, userdata);
+ return;
+ case '+':
+ input_vol = atof(&cmd[2])/100;
+ vol = pa_cvolume_inc(vol, (pa_volume_t)(input_vol * PA_VOLUME_NORM));
+ break;
+ case '-':
+ input_vol = atof(&cmd[2])/100;
+ vol = pa_cvolume_dec(vol, (pa_volume_t)(input_vol * PA_VOLUME_NORM));
+ break;
+ default:
+ input_vol = atof(&cmd[1])/100;
+ vol = pa_cvolume_set(vol, i->volume.channels, (pa_volume_t)(input_vol * PA_VOLUME_NORM * -1));
+ break;
+ }
+
+ pa_context_set_sink_volume_by_index(c, i->index, vol, NULL, NULL);
+}
+
+static void handle_command(pa_context *c, char *cmd) {
+ // send the command to each callback using userdata
+ switch (cmd[0]) {
+ case 'v':
+ pa_context_get_sink_info_by_index(c, command_sink_index, sink_info_set_volume_callback, (void *)cmd);
+ break;
+ }
+}
+
+int main() {
+ pa_threaded_mainloop *mainloop = pa_threaded_mainloop_new();
+ pa_mainloop_api *mainloop_api = pa_threaded_mainloop_get_api(mainloop);
+ pa_context *context = pa_context_new(mainloop_api, "pavolctld");
+ char stdin_buffer[256];
+
+ pa_context_connect(context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
+
+ pa_context_set_state_callback(context, context_state_callback, NULL);
+
+ // main loop
+ pa_threaded_mainloop_start(mainloop);
+
+ while(1) {
+ if (fgets(stdin_buffer, sizeof(stdin_buffer), stdin) != NULL) {
+ handle_command(context, stdin_buffer);
+ //printf("You entered: %s", stdin_buffer);
+ //fflush(stdout);
+ }
+ }
+
+ // clean up
+ pa_context_disconnect(context);
+ pa_context_unref(context);
+ pa_threaded_mainloop_free(mainloop);
+
+ return 0;
+}
+