summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am7
-rw-r--r--src/interface.c147
-rw-r--r--src/interface.h17
-rw-r--r--src/pa-sink-ctl.c195
-rw-r--r--src/pa-sink-ctl.h25
-rw-r--r--src/sink.c93
-rw-r--r--src/sink.h35
-rw-r--r--src/sink_input.c78
-rw-r--r--src/sink_input.h25
9 files changed, 622 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..8b4e688
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,7 @@
+bin_PROGRAMS = pa_sink_ctl
+pa_sink_ctl_SOURCES =interface.c pa-sink-ctl.c sink.c sink_input.c
+
+AM_CFLAGS =-std=c99 -pedantic -Wall -Werror @PULSE_CFLAGS@
+pa_sink_ctl_LDADD =@PULSE_LIBS@ @CURSES_LIBS@
+
+noinst_HEADERS = interface.h pa-sink-ctl.h sink.h sink_input.h
diff --git a/src/interface.c b/src/interface.c
new file mode 100644
index 0000000..c48b953
--- /dev/null
+++ b/src/interface.c
@@ -0,0 +1,147 @@
+#include <stdio.h>
+#include <pulse/pulseaudio.h>
+#include <ncurses.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "interface.h"
+#include "sink.h"
+#include "pa-sink-ctl.h"
+
+#define VOLUME_MAX UINT16_MAX
+#define VOLUME_BAR_LEN 50
+#define WIDTH 80
+#define HEIGHT 10
+
+// ncurses
+WINDOW *menu_win;
+int chooser;
+int startx;
+int starty;
+
+extern int sink_counter;
+extern int sink_max;
+extern sink_info** sink_list;
+
+extern pa_context* context;
+
+void interface_init(void)
+{
+ // ncurses
+ chooser = 0;
+
+ initscr();
+ clear();
+ noecho();
+ cbreak(); /* Line buffering disabled. pass on everything */
+ startx = (80 - WIDTH) / 2;
+ starty = (24 - HEIGHT) / 2;
+ menu_win = newwin(HEIGHT, WIDTH, starty, startx);
+ keypad(menu_win, TRUE);
+ mvprintw(0, 0, "Use arrow keys to go up and down, Press enter to select a choice");
+ refresh();
+}
+
+void print_sinks(void) {
+ int x, y, i;
+ x = 2;
+ y = 2;
+
+ box(menu_win, 0, 0);
+
+// printf("print sinks: %d\n", sink_input_counter);
+
+// qsort(sink_input_list, sink_input_counter, sizeof(sink_input_info*), cmp_sink_input_list);
+
+ for (i = 0; i < sink_counter; ++i) {
+ mvwprintw(menu_win, y+i, x, "%d\t%s\t",
+ sink_list[i]->index,
+ sink_list[i]->name);
+ }
+ y += i;
+/* for (i = 0; i < sink_input_counter; ++i) {
+ if (i == chooser)
+ wattron(menu_win, A_REVERSE);
+
+ mvwprintw(menu_win, y+i, x, "%d\t%s\t",
+ sink_input_list[i]->sink,
+ sink_input_list[i]->name);
+
+ if (i == chooser)
+ wattroff(menu_win, A_REVERSE);
+
+ print_volume(sink_input_list[i]->vol, y+i);
+ }*/
+}
+
+void print_volume(pa_volume_t volume, int y) {
+
+ int x = 20;
+
+ unsigned int vol = (unsigned int) ( (((double)volume) / ((double)VOLUME_MAX)) * VOLUME_BAR_LEN );
+ mvwprintw(menu_win, y, x - 1 , "[");
+ for (int i = 0; i < vol; ++i)
+ mvwprintw(menu_win, y, x + i, "=");
+ for (int i = vol; i < VOLUME_BAR_LEN; ++i)
+ mvwprintw(menu_win, y, x + i, " ");
+
+ mvwprintw(menu_win, y, x + VOLUME_BAR_LEN, "]");
+}
+
+void get_input(void)
+{
+ int c;
+// uint32_t sink;
+ c = wgetch(menu_win);
+ switch (c) {
+ case KEY_UP:
+ if (chooser > 0)
+ --chooser;
+ break;
+
+ case KEY_DOWN:
+// if (chooser < sink_input_counter - 1)
+// ++chooser;
+ break;
+
+ case KEY_LEFT:
+ break;
+
+ case KEY_RIGHT:
+ break;
+
+ case 32:
+
+/* if (sink_input_list[chooser]->sink < sink_max)
+ sink = sink_input_list[chooser]->sink + 1;
+ else
+ sink = 0;
+
+ pa_operation_unref(
+ pa_context_move_sink_input_by_index(
+ context,
+ sink_input_list[chooser]->index,
+ sink,
+ change_callback,
+ NULL));
+ return;*/
+ break;
+
+ default:
+ printf("key: %d\n", c);
+ quit();
+ break;
+ }
+
+ pa_operation_unref(pa_context_get_sink_info_list(context, get_sink_info_callback, NULL));
+// sink_input_counter = 0;
+// pa_operation_unref(pa_context_get_sink_input_info_list(context, get_sink_input_info_callback, NULL));
+}
+
+void interface_clear(void)
+{
+ clrtoeol();
+ refresh();
+ endwin();
+ exit(0);
+}
diff --git a/src/interface.h b/src/interface.h
new file mode 100644
index 0000000..d152c7d
--- /dev/null
+++ b/src/interface.h
@@ -0,0 +1,17 @@
+#ifndef INTERFACE_H
+#define INTERFACE_H
+
+#include <ncurses.h>
+
+#define VOLUME_MAX UINT16_MAX
+#define VOLUME_BAR_LEN 50
+#define WIDTH 80
+#define HEIGHT 10
+
+void print_sinks(void);
+void print_volume(pa_volume_t, int);
+void get_input(void);
+void interface_init(void);
+void interface_clear(void);
+
+#endif
diff --git a/src/pa-sink-ctl.c b/src/pa-sink-ctl.c
new file mode 100644
index 0000000..5fe67c1
--- /dev/null
+++ b/src/pa-sink-ctl.c
@@ -0,0 +1,195 @@
+#define _XOPEN_SOURCE 500
+#include <string.h>
+#include <stdio.h>
+#include <pulse/pulseaudio.h>
+#include <ncurses.h>
+#include <stdlib.h>
+
+#include "sink_input.h"
+#include "sink.h"
+#include "interface.h"
+#include "pa-sink-ctl.h"
+
+#define VOLUME_MAX UINT16_MAX
+#define VOLUME_BAR_LEN 50
+#define WIDTH 80
+#define HEIGHT 10
+
+sink_info** sink_list = NULL;
+uint32_t sink_counter;
+uint32_t sink_max;
+
+pa_mainloop_api *mainloop_api = NULL;
+pa_context *context = NULL;
+
+// ncurses
+WINDOW *menu_win;
+int chooser;
+int startx;
+int starty;
+
+int main(int argc, char** argv)
+{
+ sink_counter = 0;
+ sink_max = 1;
+ sink_list = sink_list_init(sink_max);
+
+ interface_init();
+ pa_mainloop *m = NULL;
+ int ret = 1;
+
+ if (!(m = pa_mainloop_new())) {
+ printf("error: pa_mainloop_new() failed.\n");
+ return -1;
+ }
+
+ mainloop_api = pa_mainloop_get_api(m);
+
+ if (!(context = pa_context_new(mainloop_api, "pa-sink-ctl"))) {
+ printf("error: pa_context_new() failed.\n");
+ return -1;
+ }
+
+ // define callback for connection init
+ pa_context_set_state_callback(context, context_state_callback, NULL);
+ if (pa_context_connect(context, "tcp:127.0.0.1:4712", PA_CONTEXT_NOAUTOSPAWN, NULL)) {
+ printf("error: pa_context_connect() failed.\n");
+ }
+
+ if (pa_mainloop_run(m, &ret) < 0) {
+ printf("error: pa_mainloop_run() failed.\n");
+ return -1;
+ }
+
+ return ret;
+}
+/*
+ * is called after connection
+ */
+void context_state_callback(pa_context *c, void *userdata) {
+
+ switch (pa_context_get_state(c)) {
+ case PA_CONTEXT_CONNECTING:
+// printf("connecting...\n");
+ break;
+
+ case PA_CONTEXT_AUTHORIZING:
+// printf("authorizing...\n");
+ break;
+
+ case PA_CONTEXT_SETTING_NAME:
+// printf("setting name\n");
+ break;
+
+ case PA_CONTEXT_READY:
+// printf("Menue\n");
+ pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
+// pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
+ break;
+ default:
+ printf("unknown state\n");
+ break;
+ }
+}
+
+/*
+ * the begin of the callback loops
+ */
+void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
+
+ if (is_last < 0) {
+ printf("Failed to get sink information: %s", pa_strerror(pa_context_errno(c)));
+ quit();
+ }
+
+ if (is_last) {
+ pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
+ return;
+ }
+
+ sink_list_check(sink_list, &sink_max, sink_counter);
+
+ sink_list[sink_counter] = (sink_info*) calloc(1, sizeof(sink_info));
+ sink_list[sink_counter]->index = i->index;
+ sink_list[sink_counter]->mute = i->mute;
+
+ sink_list[sink_counter]->name = strdup(i->name);
+ ++sink_counter;
+}
+
+/*
+ * is called after sink-input
+ */
+void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
+ char t[32], k[32]; //,cv[PA_CVOLUME_SNPRINT_MAX];
+
+ if (is_last < 0) {
+ printf("Failed to get sink input information: %s\n", pa_strerror(pa_context_errno(c)));
+ return;
+ }
+
+ if (is_last) {
+ print_sinks();
+ get_input();
+ return;
+ }
+
+ if (!(i->client != PA_INVALID_INDEX)) return;
+
+ snprintf(t, sizeof(t), "%u", i->owner_module);
+ snprintf(k, sizeof(k), "%u", i->client);
+
+/* printf( "Sink Input #%u"
+ "\tClient: %s"
+ "\tSink: %u"
+ "\tMute: %d"
+ "\tVolume: %s"
+ "\tname: %s"
+ "\tpid: %s\n",
+ i->index,
+ i->client != PA_INVALID_INDEX ? k : "n/a",
+ i->sink,
+ i->mute,
+ pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+ pa_proplist_gets(i->proplist, "application.name"),
+ pa_proplist_gets(i->proplist, "application.process.id"));
+*/
+
+// const char *name = pa_proplist_gets(i->proplist, "application.name");
+
+ int sink_num = i->sink;
+ int counter = sink_list[sink_num]->input_counter;
+
+ // check the length of the list
+ sink_check_input_list(sink_list[sink_num]);
+ // check the current element of the list
+ sink_input_check(&(sink_list[ sink_num ]->input_list[ counter ]));
+ sink_input_info* input = sink_list[sink_num]->input_list[counter];
+
+ input->name = strdup(pa_proplist_gets(i->proplist, "application.name"));
+ input->index = i->index;
+ input->vol = pa_cvolume_avg(&i->volume);
+
+ ++sink_list[sink_num]->input_counter;
+}
+
+void quit(void) {
+ sink_list_clear(sink_list, &sink_max, &sink_counter);
+ interface_clear();
+ exit(0);
+}
+
+/*
+ * is called, after user input
+ */
+void change_callback(pa_context* c, int success, void* userdate) {
+
+
+ // get information about sinks
+ pa_operation_unref(pa_context_get_sink_input_info_list(context, get_sink_input_info_callback, NULL));
+}
+
+void collect_all_infomation(void) {
+ sink_list_reset(sink_list, &sink_counter);
+
+}
diff --git a/src/pa-sink-ctl.h b/src/pa-sink-ctl.h
new file mode 100644
index 0000000..eb14ad2
--- /dev/null
+++ b/src/pa-sink-ctl.h
@@ -0,0 +1,25 @@
+#ifndef PA_SINK_CTL_H
+#define PA_SINK_CTL_H
+
+#include <stdio.h>
+#include <pulse/pulseaudio.h>
+#include <ncurses.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "sink_input.h"
+#include "sink.h"
+#include "interface.h"
+
+#define VOLUME_MAX UINT16_MAX
+#define VOLUME_BAR_LEN 50
+#define WIDTH 80
+#define HEIGHT 10
+
+void context_state_callback(pa_context*, void *);
+void get_sink_info_callback(pa_context *, const pa_sink_info *, int, void *);
+void get_sink_input_info_callback(pa_context *, const pa_sink_input_info*, int, void *);
+void change_callback(pa_context* c, int success, void* userdate);
+void quit(void);
+
+#endif
diff --git a/src/sink.c b/src/sink.c
new file mode 100644
index 0000000..5abff14
--- /dev/null
+++ b/src/sink.c
@@ -0,0 +1,93 @@
+#include <stdio.h>
+#include <pulse/pulseaudio.h>
+#include <ncurses.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "sink_input.h"
+#include "sink.h"
+
+/*
+ * return an initilized sink
+ */
+sink_info* sink_init(void) {
+
+ sink_info* sink = (sink_info*) calloc(1, sizeof(sink_info));
+
+ sink->name = NULL;
+ sink->input_counter = 0;
+ sink->input_max = 1;
+ sink->input_list = NULL;
+
+ sink->input_list = sink_input_list_init(sink->input_max);
+
+ return sink;
+}
+
+/*
+ * frees all components of a sink
+ */
+void sink_clear(sink_info* sink) {
+
+ if (sink->name != NULL)
+ free(sink->name);
+
+ sink_input_list_clear(sink->input_list, &sink->input_max);
+
+ free(sink);
+ sink = NULL;
+}
+
+void sink_check(sink_info** sink) {
+
+ if ((*sink) == NULL)
+ (*sink) = (sink_info*) calloc(1, sizeof(sink_input_info));
+}
+/*
+ * check the list length and resize the list, if current position = max
+ */
+void sink_list_check(sink_info** sink_list, uint32_t* max, uint32_t counter) {
+ if (counter >= (*max)) {
+ (*max) *= 2;
+ sink_list = (sink_info**) realloc(sink_list, (*max) * sizeof(sink_info*));
+ for (int i = counter; i < (*max); ++i)
+ sink_list[i] = NULL;
+ }
+}
+
+void sink_check_input_list(sink_info* sink) {
+
+ if (sink->input_counter >= sink->input_max)
+ sink_input_list_enlarge(sink->input_list, &sink->input_max, sink->input_counter);
+}
+
+sink_info** sink_list_init(uint32_t max) {
+
+ sink_info** sink_list = (sink_info**) calloc(max, sizeof(sink_info*));
+
+ for (int i = 0; i < max; ++i)
+ sink_list[i] = NULL;
+
+ return sink_list;
+}
+
+void sink_list_reset(sink_info** sink_list, uint32_t* counter) {
+
+ for (int i = 0; i < (*counter); ++i)
+ sink_list[i]->input_counter = 0;
+
+ (*counter) = 0;
+}
+
+void sink_list_clear(sink_info** sink_list, uint32_t* max, uint32_t* counter) {
+
+ for (int i = 0; i < (*max); ++i)
+ if (sink_list[i] != NULL)
+ sink_clear(sink_list[i]);
+
+ (*max) = 0;
+ (*counter) = 0;
+
+ free(sink_list);
+ sink_list = NULL;
+}
diff --git a/src/sink.h b/src/sink.h
new file mode 100644
index 0000000..78f5837
--- /dev/null
+++ b/src/sink.h
@@ -0,0 +1,35 @@
+#ifndef SINK_H
+#define SINK_H
+
+#include <stdio.h>
+#include <pulse/pulseaudio.h>
+#include <ncurses.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "sink_input.h"
+
+typedef struct _sink_info {
+ uint32_t index;
+ char* name;
+ int mute;
+ pa_volume_t vol;
+
+ // input list
+ int input_counter;
+ int input_max;
+ sink_input_info** input_list;
+} sink_info;
+
+sink_info* sink_init(void);
+void sink_clear(sink_info*);
+
+void sink_check(sink_info**);
+void sink_list_check(sink_info**, uint32_t*, uint32_t);
+void sink_check_input_list(sink_info*);
+
+sink_info** sink_list_init(uint32_t);
+void sink_list_reset(sink_info**, uint32_t*);
+void sink_list_clear(sink_info**, uint32_t*, uint32_t*);
+
+#endif
diff --git a/src/sink_input.c b/src/sink_input.c
new file mode 100644
index 0000000..54f7c04
--- /dev/null
+++ b/src/sink_input.c
@@ -0,0 +1,78 @@
+#include <stdio.h>
+#include <pulse/pulseaudio.h>
+#include <ncurses.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "sink_input.h"
+
+sink_input_info* sink_input_init() {
+
+ sink_input_info* sink_input = (sink_input_info*) calloc(1, sizeof(sink_input_info));
+ sink_input->name = NULL;
+ sink_input->pid = NULL;
+
+ return sink_input;
+}
+
+void sink_input_clear(sink_input_info* sink_input) {
+
+ if (sink_input->name != NULL)
+ free(sink_input->name);
+
+ if (sink_input->pid != NULL)
+ free(sink_input->pid);
+
+ free(sink_input);
+ sink_input = NULL;
+}
+
+sink_input_info** sink_input_list_init(int max) {
+
+ sink_input_info** sink_input_list = (sink_input_info**) calloc(max, sizeof(sink_input_info*));
+
+ for (int i = 0; i < max; ++i)
+ sink_input_list = NULL;
+
+ return sink_input_list;
+}
+
+void sink_input_list_enlarge(sink_input_info** sink_input_list, int* max, int counter) {
+
+ (*max) *= 2;
+ sink_input_list = (sink_input_info**) realloc(sink_input_list, (*max) * sizeof(sink_input_info*));
+
+ for (int i = counter; i < (*max); ++i)
+ sink_input_list[i] = NULL;
+}
+
+void sink_input_list_clear(sink_input_info** sink_input_list, int *max) {
+
+ for (int i = 0; i < (*max); ++i)
+ sink_input_clear(sink_input_list[i]);
+
+ (*max) = 0;
+
+ free(sink_input_list);
+ sink_input_list = NULL;
+}
+
+void sink_input_check(sink_input_info** sink_input) {
+
+ if ((*sink_input) == NULL)
+ (*sink_input) = (sink_input_info*) calloc(1, sizeof(sink_input_info));
+}
+
+int cmp_sink_input_list(const void *a, const void *b) {
+
+ sink_input_info* sinka = *((sink_input_info**) a);
+ sink_input_info* sinkb = *((sink_input_info**) b);
+
+ if (sinka->sink < sinkb->sink)
+ return -1;
+ else if (sinka->sink > sinkb->sink)
+ return 1;
+ else
+ return 0;
+}
+
diff --git a/src/sink_input.h b/src/sink_input.h
new file mode 100644
index 0000000..d16924c
--- /dev/null
+++ b/src/sink_input.h
@@ -0,0 +1,25 @@
+#ifndef SINK_INPUT_H
+#define SINK_INPUT_H
+
+#include <pulse/pulseaudio.h>
+// TODO: change this with the given define from pulselib
+#define VOLUME_MAX UINT16_MAX
+
+typedef struct _sink_input_info {
+ uint32_t index;
+ uint32_t sink;
+ char *name;
+ char *pid; // maybe useless?!!?
+ pa_volume_t vol; // TOTO: exchange with the channel-list
+} sink_input_info;
+
+sink_input_info* sink_input_init();
+void sink_input_clear(sink_input_info*);
+
+sink_input_info** sink_input_list_init(int);
+void sink_input_list_enlarge(sink_input_info**, int*, int);
+void sink_input_list_clear(sink_input_info**, int*);
+void sink_input_check(sink_input_info**);
+int cmp_sink_input_list(const void *, const void *);
+
+#endif