From: Martin Mares Date: Sun, 9 Nov 2014 23:39:09 +0000 (+0100) Subject: Ursary: Moved to a separate repo X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=0cc4ee2a17aabff354834540842cda52efeb4b4f;p=misc.git Ursary: Moved to a separate repo --- diff --git a/ursaryd/DESC b/ursaryd/DESC deleted file mode 100644 index ca25e81..0000000 --- a/ursaryd/DESC +++ /dev/null @@ -1,98 +0,0 @@ -Bus 006 Device 003: ID 1235:000a Novation EMS -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 1.00 - bDeviceClass 255 Vendor Specific Class - bDeviceSubClass 0 - bDeviceProtocol 255 - bMaxPacketSize0 8 - idVendor 0x1235 Novation EMS - idProduct 0x000a - bcdDevice 0.09 - iManufacturer 1 Novation DMS Ltd - iProduct 2 Nocturn - iSerial 0 - bNumConfigurations 2 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 32 - bNumInterfaces 1 - bConfigurationValue 1 - iConfiguration 3 High brightness mode - bmAttributes 0x80 - (Bus Powered) - MaxPower 220mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 2 - bInterfaceClass 255 Vendor Specific Class - bInterfaceSubClass 0 - bInterfaceProtocol 0 - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x81 EP 1 IN - bmAttributes 3 - Transfer Type Interrupt - Synch Type None - Usage Type Data - wMaxPacketSize 0x0008 1x 8 bytes - bInterval 10 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x02 EP 2 OUT - bmAttributes 3 - Transfer Type Interrupt - Synch Type None - Usage Type Data - wMaxPacketSize 0x0008 1x 8 bytes - bInterval 10 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 32 - bNumInterfaces 1 - bConfigurationValue 2 - iConfiguration 4 Power saving mode - bmAttributes 0x80 - (Bus Powered) - MaxPower 110mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 2 - bInterfaceClass 255 Vendor Specific Class - bInterfaceSubClass 0 - bInterfaceProtocol 0 - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x81 EP 1 IN - bmAttributes 3 - Transfer Type Interrupt - Synch Type None - Usage Type Data - wMaxPacketSize 0x0008 1x 8 bytes - bInterval 10 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x02 EP 2 OUT - bmAttributes 3 - Transfer Type Interrupt - Synch Type None - Usage Type Data - wMaxPacketSize 0x0008 1x 8 bytes - bInterval 10 -Device Status: 0x0000 - (Bus Powered) diff --git a/ursaryd/Makefile b/ursaryd/Makefile deleted file mode 100644 index 5d8d44c..0000000 --- a/ursaryd/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -LIBUSB_CFLAGS := $(shell pkg-config --cflags libusb-1.0) -LIBUSB_LIBS := $(shell pkg-config --libs libusb-1.0) - -LIBPULSE_CFLAGS := $(shell pkg-config --cflags libpulse) -LIBPULSE_LIBS := $(shell pkg-config --libs libpulse) - -LIBUCW_PKG := /home/mj/src/libucw/run/lib/pkgconfig -LIBUCW_CFLAGS := $(shell PKG_CONFIG_PATH=$(LIBUCW_PKG) pkg-config --cflags libucw) -LIBUCW_LIBS := $(shell PKG_CONFIG_PATH=$(LIBUCW_PKG) pkg-config --libs libucw) - -CFLAGS=-O2 -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wredundant-decls -std=gnu99 $(LIBUCW_CFLAGS) $(LIBUSB_CFLAGS) $(LIBPULSE_CFLAGS) -g2 -LDLIBS=$(LIBUCW_LIBS) $(LIBUSB_LIBS) $(LIBPULSE_LIBS) - -all: ursaryd - -ursaryd: ursaryd.o mpd.o nocturn.o pulse.o pulse-ucw.o - -ursaryd.o: ursaryd.c ursaryd.h -mpd.o: mpd.c ursaryd.h -nocturn.o: nocturn.c ursaryd.h -pulse.o: pulse.c ursaryd.h -pulse-ucw.o: pulse-ucw.c ursaryd.h - -clean: - rm -f `find . -name "*~" -or -name "*.[oa]" -or -name "\#*\#" -or -name TAGS -or -name core -or -name .depend -or -name .#*` - rm -f ursaryd diff --git a/ursaryd/jt.c b/ursaryd/jt.c deleted file mode 100644 index 3fa5558..0000000 --- a/ursaryd/jt.c +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include -#include - -#include - -typedef jack_default_audio_sample_t sample_t; - -static jack_client_t *jc; -static jack_port_t *inport[2], *outport[4]; - -static int process_callback(jack_nframes_t nframes, void *arg __attribute__((unused))) -{ - for (int ch=0; ch<2; ch++) { - sample_t *in = jack_port_get_buffer(inport[ch], nframes); - sample_t *out = jack_port_get_buffer(outport[ch], nframes); - sample_t *out2 = jack_port_get_buffer(outport[2+ch], nframes); - for (jack_nframes_t i=0; i %s\n", a, b); - } - - puts("Running..."); - char xxx[16]; - read(0, xxx, sizeof(xxx)); - - puts("exiting"); - jack_client_close(jc); - return 0; -} diff --git a/ursaryd/mpd.c b/ursaryd/mpd.c deleted file mode 100644 index f0da2a3..0000000 --- a/ursaryd/mpd.c +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Interface to Music Player Daemon - * - * (c) 2014 Martin Mares - */ - -#define LOCAL_DEBUG - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ursaryd.h" - -static struct main_timer mpd_connect_timer; -static struct main_file mpd_connect_file; -static struct main_rec_io mpd_rio; -static int mpd_sk = -1; - -enum mpd_state mpd_state; -#define MPD_STATE(s) do { mpd_state = s; DBG("MPD: " #s); } while (0) - -struct mpd_cmd { // malloc'ed - cnode n; - char *cmd; // malloc'ed - void (*done)(struct mpd_cmd *c); - char *status; - int err; - int idle; // 1=this is an idle command, 2=aborted idle command - clist output; // list of simp2_node's allocated from mpd_reply_pool -}; - -static clist mpd_cmd_queue; -static struct mpd_cmd *mpd_current_command; // ... at the head of the queue -static struct mempool *mpd_reply_pool; - -static void mpd_send_cmd(void); -static void mpd_cmd(void (*done)(struct mpd_cmd *c), const char *cmd, ...); - -static struct mpd_cmd *mpd_new_cmd(void) -{ - struct mpd_cmd *c = xmalloc_zero(sizeof(*c)); - clist_add_tail(&mpd_cmd_queue, &c->n); - clist_init(&c->output); - return c; -} - -static void mpd_free_cmd(struct mpd_cmd *c) -{ - xfree(c->cmd); - xfree(c); -} - -static void mpd_error(const char *text, ...) -{ - va_list args; - va_start(args, text); - const char *x = stk_vprintf(text, args); - msg(L_ERROR, "MPD: %s", x); - va_end(args); - - DBG("MPD: Flushing commands"); - mpd_current_command = NULL; - struct mpd_cmd *c; - while (c = (struct mpd_cmd *) clist_remove_head(&mpd_cmd_queue)) - mpd_free_cmd(c); - - DBG("MPD: Tearing down server connection"); - rec_io_del(&mpd_rio); - file_del(&mpd_connect_file); - if (mpd_sk >= 0) - { - close(mpd_sk); - mpd_sk = -1; - } - - DBG("MPD: Scheduling reconnect"); - timer_add_rel(&mpd_connect_timer, 5000); - MPD_STATE(MPD_OFFLINE); - schedule_update(); -} - -static void mpd_cmd_done(struct mpd_cmd *c) -{ - DBG("MPD: Command finished (err=%d)", c->err); - rec_io_set_timeout(&mpd_rio, 0); - if (c->done) - c->done(c); - clist_remove(&c->n); - mpd_free_cmd(c); - - mpd_current_command = NULL; - mpd_send_cmd(); -} - -static const char *mpd_find_output(struct mpd_cmd *c, const char *key, const char *deflt) -{ - CLIST_FOR_EACH(struct simp2_node *, n, c->output) - if (!strcmp(n->s1, key)) - return n->s2; - return deflt; -} - -static char *mpd_player_state; - -static void mpd_got_status(struct mpd_cmd *c) -{ - const char *state = mpd_find_output(c, "state", ""); - DBG("MPD: Player state <%s>", state); - SET_STRING(mpd_player_state, state); - schedule_update(); -} - -const char *mpd_get_player_state(void) -{ - if (mpd_state != MPD_ONLINE) - return "down"; - else if (mpd_player_state) - return mpd_player_state; - else - return "unknown"; -} - -static void mpd_got_idle(struct mpd_cmd *c) -{ - const char *chg = mpd_find_output(c, "changed", ""); - if (!strcmp(chg, "player")) - mpd_cmd(mpd_got_status, "status"); -} - -static bool mpd_got_line(char *line) -{ - if (mpd_state == MPD_WAIT_GREETING) - { - if (!str_has_prefix(line, "OK ")) - mpd_error("Invalid welcome line"); - MPD_STATE(MPD_ONLINE); - mpd_cmd(mpd_got_status, "status"); - return 1; - } - - ASSERT(mpd_state == MPD_ONLINE); - - struct mpd_cmd *c = mpd_current_command; - if (!c) - { - mpd_error("Reply for no command"); - return 0; - } - - if (!strcmp(line, "OK")) - { - c->status = line; - c->err = 0; - mpd_cmd_done(c); - } - else if (str_has_prefix(line, "ACK ")) - { - c->status = line; - if (line[4] != '[') - { - mpd_error("Reply syntax error: <%s>", line); - return 0; - } - c->err = atoi(line+5); - mpd_cmd_done(c); - } - else - { - char *sep = strchr(line, ':'); - if (!sep || sep[1] != ' ') - { - mpd_error("Reply syntax error: <%s>", line); - return 0; - } - *sep++ = 0; - *sep++ = 0; - simp2_node *sn = simp2_append(mpd_reply_pool, &c->output); - sn->s1 = mp_strdup(mpd_reply_pool, line); - sn->s2 = mp_strdup(mpd_reply_pool, sep); - } - - return 1; -} - -static uint mpd_read_handler(struct main_rec_io *rio) -{ - uint len = rec_io_parse_line(rio); - if (!len) - return 0; - - char line[len]; - memcpy(line, rio->read_rec_start, len-1); - line[len-1] = 0; - - DBG("Received <%s>", line); - if (mpd_got_line(line)) - return len; - else - return ~0U; -} - -static int mpd_notify_handler(struct main_rec_io *rio UNUSED, int status) -{ - switch (status) - { - case RIO_EVENT_EOF: - mpd_error("Connection closed by server"); - return HOOK_IDLE; - default: - if (status < 0) - { - mpd_error("Error on server connection (status=%d errno=%d)", status, errno); - return HOOK_IDLE; - } - return HOOK_RETRY; - } -} - -static void mpd_send_cmd(void) -{ - struct mpd_cmd *c; - - if (c = mpd_current_command) - { - if (c->idle == 1) - { - DBG("MPD: Sending noidle"); - rec_io_write(&mpd_rio, "noidle\n", 7); - c->idle = 2; - } - return; - } - - c = (struct mpd_cmd *) clist_head(&mpd_cmd_queue); - if (!c) - { - c = mpd_new_cmd(); - c->cmd = malloc(6); - strcpy(c->cmd, "idle\n"); - c->idle = 1; - c->done = mpd_got_idle; - } - mpd_current_command = c; - - DBG("MPD: Sending command <%s>", c->cmd); - rec_io_write(&mpd_rio, c->cmd, strlen(c->cmd)); - if (!c->idle) - rec_io_set_timeout(&mpd_rio, 5000); -} - -static void mpd_cmd(void (*done)(struct mpd_cmd *c), const char *cmd, ...) -{ - struct mpd_cmd *c = mpd_new_cmd(); - c->done = done; - - va_list args, args2; - va_start(args, cmd); - va_copy(args2, args); - int len = vsnprintf(NULL, 0, cmd, args); - c->cmd = xmalloc(len + 2); - vsnprintf(c->cmd, len + 1, cmd, args2); - c->cmd[len] = '\n'; - c->cmd[len+1] = 0; - va_end(args); - va_end(args2); - - mpd_send_cmd(); -} - -static int mpd_connected(struct main_file *f) -{ - if (f) - { - // Called from the hook - DBG("MPD: Connection hook"); - file_del(f); - timer_del(&mpd_connect_timer); - int err; - socklen_t len = sizeof(err); - if (getsockopt(mpd_sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0) - ASSERT(0); - if (err) - { - mpd_error("Connection refused: %s", strerror(err)); - return HOOK_IDLE; - } - } - - MPD_STATE(MPD_WAIT_GREETING); - mpd_rio.read_handler = mpd_read_handler; - mpd_rio.notify_handler = mpd_notify_handler; - mpd_rio.read_rec_max = 16384; - rec_io_add(&mpd_rio, mpd_sk); - rec_io_start_read(&mpd_rio); - - return HOOK_IDLE; -} - -void mpd_play(void) -{ - return mpd_cmd(NULL, "play"); -} - -void mpd_stop(void) -{ - return mpd_cmd(NULL, "stop"); -} - -void mpd_pause(int arg) -{ - return mpd_cmd(NULL, "pause %d", arg); -} - -static void mpd_connect(struct main_timer *t) -{ - timer_del(t); - - if (mpd_state == MPD_CONNECTING) - { - mpd_error("Attempt to connect timed out"); - return; - } - - DBG("MPD: Trying to connect"); - - mpd_sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (mpd_sk < 0) - die("Cannot create socket: %m"); - - fcntl(mpd_sk, F_SETFL, fcntl(mpd_sk, F_GETFL) | O_NONBLOCK); - - bzero(&mpd_rio, sizeof(mpd_rio)); - bzero(&mpd_connect_file, sizeof(mpd_connect_file)); - mpd_connect_file.fd = mpd_sk; - mpd_connect_file.write_handler = mpd_connected; - MPD_STATE(MPD_CONNECTING); - - struct sockaddr_in sin; - sin.sin_family = AF_INET; - sin.sin_port = htons(6600); - sin.sin_addr.s_addr = htonl(0x7f000001); - if (connect(mpd_sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) - { - if (errno == EINPROGRESS) - { - file_add(&mpd_connect_file); - timer_add_rel(&mpd_connect_timer, 5000); - } - else - mpd_error("Unable to connect: %m"); - } - else - mpd_connected(NULL); -} - -void mpd_init(void) -{ - clist_init(&mpd_cmd_queue); - mpd_reply_pool = mp_new(4096); - - mpd_connect_timer.handler = mpd_connect; - timer_add_rel(&mpd_connect_timer, 100); - mpd_state = MPD_OFFLINE; -} diff --git a/ursaryd/nocturn.c b/ursaryd/nocturn.c deleted file mode 100644 index ba5edf2..0000000 --- a/ursaryd/nocturn.c +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Interface to Novation Nocturn - * - * (c) 2014 Martin Mares - * - * Protocol reverse-engineered by De Wet van Niekerk , - * see https://github.com/dewert/nocturn-linux-midi for inspiration. - */ - -#undef LOCAL_DEBUG - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include "ursaryd.h" - -static libusb_context *usb_ctx; -static libusb_device_handle *usb_dev; -static bool usb_iface_claimed; - -static void noct_error(int usb_err, char *text); - -static struct main_file **usb_fds; - -static int usb_fd_ready(struct main_file *f UNUSED) -{ - DBG("USB: Handling events (ready on fd %d)", f->fd); - struct timeval tv = { 0, 0 }; - int comp = 0; - int err = libusb_handle_events_timeout_completed(usb_ctx, &tv, &comp); - if (err < 0) - msg(L_ERROR, "libusb_handle_events: error %d", err); - return HOOK_IDLE; -} - -static void usb_added_fd(int fd, short events, void *user_data UNUSED) -{ - if (fd >= (int) GARY_SIZE(usb_fds)) - GARY_RESIZE(usb_fds, fd + 1); - - struct main_file *f = usb_fds[fd]; - if (!f) - { - f = xmalloc_zero(sizeof(*f)); - usb_fds[fd] = f; - } - else if (file_is_active(f)) - { - DBG("USB: Releasing fd %d", fd); - file_del(f); - } - - DBG("USB: Adding fd %d with event mask %u", fd, events); - f->fd = fd; - f->read_handler = (events & POLLIN) ? usb_fd_ready : NULL; - f->write_handler = (events & POLLOUT) ? usb_fd_ready : NULL; - file_add(f); -} - -static void usb_removed_fd(int fd, void *user_data UNUSED) -{ - DBG("USB: Releasing fd %d", fd); - ASSERT(fd < (int) GARY_SIZE(usb_fds)); - struct main_file *f = usb_fds[fd]; - ASSERT(f); - ASSERT(file_is_active(f)); - file_del(f); -} - -static const char noct_magic[4][9] = { - { 3, 0xb0, 0x00, 0x00 }, - { 8, 0x28, 0x00, 0x2b, 0x4a, 0x2c, 0x00, 0x2e, 0x35 }, - { 6, 0x2a, 0x02, 0x2c, 0x72, 0x2e, 0x30 }, - { 2, 0x7f, 0x00 }, -}; - -static struct libusb_transfer *noct_read_xfer; -static bool noct_read_pending; -char noct_rotary_touched[10]; -char noct_button_pressed[16]; - -static void noct_read_done(struct libusb_transfer *xfer) -{ - byte *pkt = xfer->buffer; - int len = xfer->actual_length; - DBG("USB: Read done: status %d, length %d", xfer->status, len); - noct_read_pending = 0; - - if (xfer->status != LIBUSB_TRANSFER_COMPLETED) - return noct_error(0, stk_printf("USB read failed with status %d", xfer->status)); - -#ifdef LOCAL_DEBUG - char buf[256]; - mem_to_hex(buf, pkt, len, ' '); - DBG("USB: Read <%s>", buf); -#endif - - int i = 0; - while (i < len) - { - if (i + 3 > len) - { - msg(L_ERROR, "Unknown USB packet: length %d not divisible by 3", len); - break; - } - if (pkt[i] != 0xb0) - { - msg(L_ERROR, "Unknown USB packet: expected 0xb0 at position %d", i); - break; - } - int cmd = pkt[i+1]; - int arg = pkt[i+2]; - i += 3; - switch (cmd) - { - case 0x30: - // Unknown packet sent during init - continue; - case 0x40 ... 0x47: - if (arg < 0x80) - { - int r = cmd - 0x40; - int delta = (arg < 0x40 ? arg : arg - 0x80); - DBG("Noct: Rotary %d = %d", r, delta); - notify_rotary(r, delta); - continue; - } - break; - case 0x48: - if (arg < 0x80) - { - DBG("Noct: Slider value = %d", arg); - notify_rotary(9, arg); - continue; - } - break; - case 0x49: - // Unknown packet, maybe least significant bit of slider - continue; - case 0x4a: - if (arg < 0x80) - { - int delta = (arg < 0x40 ? arg : arg - 0x80); - DBG("Noct: Center = %d", delta); - notify_rotary(8, delta); - continue; - } - break; - case 0x52: - if (arg == 0x00 || arg == 0x7f) - { - int state = !!arg; - DBG("Noct: Center touch = %d", state); - noct_rotary_touched[8] = state; - notify_touch(8, state); - continue; - } - break; - case 0x53: - if (arg == 0x00 || arg == 0x7f) - { - int state = !!arg; - DBG("Noct: Slider touch = %d", state); - noct_rotary_touched[9] = state; - notify_touch(9, state); - continue; - } - break; - case 0x60 ... 0x67: - if (arg == 0x00 || arg == 0x7f) - { - int r = cmd - 0x60; - int state = !!arg; - DBG("Noct: Rotary %d touch = %d", r, state); - noct_rotary_touched[r] = state; - notify_touch(r, state); - continue; - } - break; - case 0x70 ... 0x7f: - if (arg == 0x00 || arg == 0x7f) - { - int b = cmd - 0x70; - int state = !!arg; - DBG("Noct: Button %d = %d", b, state); - noct_button_pressed[b] = state; - notify_button(b, state); - continue; - } - break; - } - msg(L_ERROR, "Unknown USB packet: unrecognized cmd=%02x arg=%02x", cmd, arg); - } - - int err; - if ((err = libusb_submit_transfer(xfer)) < 0) - noct_error(err, "Cannot submit transfer"); - else - noct_read_pending = 1; -} - -static void noct_read_init(void) -{ - DBG("Noct: Read init"); - - noct_read_xfer = libusb_alloc_transfer(0); - libusb_fill_interrupt_transfer(noct_read_xfer, usb_dev, 0x81, xmalloc(8), 8, noct_read_done, NULL, 0); - - int err; - if ((err = libusb_submit_transfer(noct_read_xfer)) < 0) - noct_error(err, "Cannot submit transfer"); - else - noct_read_pending = 1; -} - -static byte noct_button_light[16]; -static byte noct_ring_mode[8]; // RING_MODE_xxx -static byte noct_ring_val[9]; - -static uint noct_dirty_button; -static uint noct_dirty_ring_mode; -static uint noct_dirty_ring_val; - -static struct libusb_transfer *noct_write_xfer; -static bool noct_write_pending; -static void noct_sched_write(void); - -static void noct_write_done(struct libusb_transfer *xfer) -{ - int len = xfer->actual_length; - DBG("USB: Write done: status %d, length %d", xfer->status, len); - - if (xfer->status != LIBUSB_TRANSFER_COMPLETED) - return noct_error(0, stk_printf("USB write failed with status %d", xfer->status)); - if (len < xfer->length) - msg(L_ERROR, "USB partial write: %d out of %d", len, xfer->length); - - noct_write_pending = 0; - noct_sched_write(); -} - -static void noct_do_write(uint cmd, uint arg) -{ - DBG("USB: Submitting write %02x %02x", cmd, arg); - ASSERT(!noct_write_pending); - noct_write_pending = 1; - - struct libusb_transfer *xfer = noct_write_xfer; - byte *pkt = xfer->buffer; - pkt[0] = cmd; - pkt[1] = arg; - xfer->length = 2; - - int err; - if ((err = libusb_submit_transfer(xfer)) < 0) - noct_error(err, "Cannot submit transfer"); -} - -static void noct_sched_write(void) -{ - if (noct_write_pending) - return; - - if (noct_dirty_button) - { - int i = bit_ffs(noct_dirty_button); - noct_dirty_button ^= 1U << i; - noct_do_write(0x70 + i, noct_button_light[i]); - } - else if (noct_dirty_ring_mode) - { - int i = bit_ffs(noct_dirty_ring_mode); - noct_dirty_ring_mode ^= 1U << i; - noct_do_write(0x48 + i, noct_ring_mode[i] << 4); - } - else if (noct_dirty_ring_val) - { - int i = bit_ffs(noct_dirty_ring_val); - noct_dirty_ring_val ^= 1U << i; - if (i == 8) - noct_do_write(0x50, noct_ring_val[i]); - else - noct_do_write(0x40 + i, noct_ring_val[i]); - } -} - -void noct_set_ring(int ring, int mode, int val) -{ - ASSERT(ring >= 0 && ring <= 8); - ASSERT(val >= 0 && val <= 0x7f); - ASSERT(mode >= 0 && mode <= 5); - ASSERT(ring < 8 || !mode); - if (noct_ring_mode[ring] != mode) - { - noct_ring_mode[ring] = mode; - noct_dirty_ring_mode |= 1U << ring; - noct_dirty_ring_val |= 1U << ring; // HW needs to re-send the value - } - if (noct_ring_val[ring] != val) - { - noct_ring_val[ring] = val; - noct_dirty_ring_val |= 1U << ring; - } - noct_sched_write(); -} - -void noct_set_button(int button, int val) -{ - ASSERT(button >= 0 && button < 16); - ASSERT(val == 0 || val == 1); - if (noct_button_light[button] != val) - { - noct_button_light[button] = val; - noct_dirty_button |= 1U << button; - noct_sched_write(); - } -} - -static void noct_write_init(void) -{ - DBG("Noct: Write init"); - - noct_write_xfer = libusb_alloc_transfer(0); - libusb_fill_interrupt_transfer(noct_write_xfer, usb_dev, 0x02, xmalloc(8), 0, noct_write_done, NULL, 1000); - - bzero(noct_button_pressed, sizeof(noct_button_pressed)); - bzero(noct_rotary_touched, sizeof(noct_rotary_touched)); - noct_dirty_button = 0xffff; - noct_dirty_ring_mode = 0xff; - noct_dirty_ring_val = 0x1ff; - noct_sched_write(); -} - -static struct main_timer noct_connect_timer; -static struct main_hook noct_error_hook; - -static void noct_connect(struct main_timer *t) -{ - timer_del(t); - msg(L_DEBUG, "Looking for Nocturn"); - int err; - - libusb_device **dev_list; - libusb_device *found_dev = NULL; - ssize_t len = libusb_get_device_list(usb_ctx, &dev_list); - for (ssize_t i=0; i < len; i++) - { - libusb_device *dev = dev_list[i]; - struct libusb_device_descriptor desc; - if (libusb_get_device_descriptor(dev, &desc) >= 0 && - desc.idVendor == 0x1235 && - desc.idProduct == 0x000a) - { - msg(L_INFO, "Nocturn found at bus %d, addr %d", libusb_get_bus_number(dev), libusb_get_device_address(dev)); - if (found_dev) - { - msg(L_ERROR, "Multiple Nocturn devices found. Using the first one."); - break; - } - found_dev = libusb_ref_device(dev); - } - } - libusb_free_device_list(dev_list, 1); - - if (!found_dev) - { - msg(L_INFO, "No Nocturn device found"); - timer_add_rel(t, 5000); - return; - } - - DBG("Initializing Nocturn"); - - if ((err = libusb_open(found_dev, &usb_dev)) < 0) - return noct_error(err, "libusb_open failed"); - - // There exist configurations 1 (high brightness) and 2 (power-save) - if ((err = libusb_set_configuration(usb_dev, 2)) < 0) - return noct_error(err, "libusb_set_configuration failed"); - - if ((err = libusb_claim_interface(usb_dev, 0)) < 0) - return noct_error(err, "libusb_claim_interface failed"); - usb_iface_claimed = 1; - - for (int i=0; i<4; i++) - { - int done; - if ((err = libusb_interrupt_transfer(usb_dev, 0x02, (byte *) noct_magic[i] + 1, noct_magic[i][0], &done, 5000)) < 0) - return noct_error(err, "Cannot send init packets"); - if (done != noct_magic[i][0]) - return noct_error(err, stk_printf("Partial send of init packet (%d < %d)", done, noct_magic[i][0])); - } - - noct_read_init(); - noct_write_init(); - schedule_update(); -} - -static int noct_error_handler(struct main_hook *h) -{ - DBG("Noct: Entered error handling hook"); - hook_del(h); - - if (usb_dev) - { - if (noct_read_xfer) - { - if (noct_read_pending) - { - DBG("Noct: Cancelling pending read"); - libusb_cancel_transfer(noct_read_xfer); - noct_read_pending = 0; - } - DBG("Noct: Tearing down read xfer"); - xfree(noct_read_xfer->buffer); - libusb_free_transfer(noct_read_xfer); - noct_read_xfer = NULL; - } - if (noct_write_xfer) - { - if (noct_write_pending) - { - DBG("Noct: Cancelling pending write"); - libusb_cancel_transfer(noct_write_xfer); - noct_write_pending = 0; - } - DBG("Noct: Tearing down write xfer"); - xfree(noct_write_xfer->buffer); - libusb_free_transfer(noct_write_xfer); - noct_write_xfer = NULL; - } - if (usb_iface_claimed) - { - DBG("Noct: Unclaiming interface"); - libusb_release_interface(usb_dev, 0); - usb_iface_claimed = 0; - } - DBG("Noct: Resetting device"); - libusb_reset_device(usb_dev); - libusb_close(usb_dev); - usb_dev = NULL; - } - - DBG("Noct: Scheduling rescan after error"); - timer_add_rel(&noct_connect_timer, 3000); - - return HOOK_IDLE; -} - -static void noct_error(int usb_err, char *text) -{ - if (usb_err) - msg(L_ERROR, "Nocturn: %s: error %d (%s)", text, usb_err, libusb_error_name(usb_err)); - else - msg(L_ERROR, "Nocturn: %s", text); - - DBG("Noct: Scheduling error handling hook"); - hook_add(&noct_error_hook); -} - -bool noct_is_ready(void) -{ - return !!usb_dev; -} - -void noct_init(void) -{ - int err; - - // Initialize libusb - if ((err = libusb_init(&usb_ctx)) < 0) - die("libusb_init failed: error %d", err); - libusb_set_debug(usb_ctx, 3); - - // Connect libusb to UCW mainloop - - if (!libusb_pollfds_handle_timeouts(usb_ctx)) - die("Unsupported version of libusb, please fix me"); - - GARY_INIT_ZERO(usb_fds, 0); - libusb_set_pollfd_notifiers(usb_ctx, usb_added_fd, usb_removed_fd, NULL); - - const struct libusb_pollfd **fds = libusb_get_pollfds(usb_ctx); - ASSERT(fds); - for (int i=0; fds[i]; i++) - usb_added_fd(fds[i]->fd, fds[i]->events, NULL); - free(fds); - - // Prepare error handling hook - noct_error_hook.handler = noct_error_handler; - - // Schedule search for the Nocturn - noct_connect_timer.handler = noct_connect; - timer_add_rel(&noct_connect_timer, 100); -} diff --git a/ursaryd/pulse-ucw.c b/ursaryd/pulse-ucw.c deleted file mode 100644 index 4ef032c..0000000 --- a/ursaryd/pulse-ucw.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Glue between PulseAudio and LibUCW Mainloop - * - * (c) 2014 Martin Mares - */ - -#undef LOCAL_DEBUG - -#include -#include -#include - -#include -#include - -#include "ursaryd.h" - -struct pmain_io { - cnode n; - struct main_file f; - clist io_events; -}; - -static clist pmain_io_list; - -struct pa_io_event { - cnode n; - cnode gc_n; - struct pmain_io *io; - pa_io_event_flags_t events; - pa_io_event_cb_t callback; - pa_io_event_destroy_cb_t destroy_callback; - void *userdata; -}; - -static clist pmain_io_gc_list; - -static pa_io_event *pmain_io_new(pa_mainloop_api *api, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata); -static void pmain_io_enable(pa_io_event *e, pa_io_event_flags_t events); -static void pmain_io_free(pa_io_event *e); -static void pmain_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb); - -struct pa_time_event { - cnode n; - struct main_timer t; - pa_time_event_cb_t callback; - pa_time_event_destroy_cb_t destroy_callback; - void *userdata; - struct timeval tv; -}; - -static clist pmain_time_gc_list; - -static pa_time_event *pmain_time_new(pa_mainloop_api *api, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata); -static void pmain_time_restart(pa_time_event *e, const struct timeval *tv); -static void pmain_time_free(pa_time_event *e); -static void pmain_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb); - -struct pa_defer_event { - cnode n; - struct main_hook h; - pa_defer_event_cb_t callback; - pa_defer_event_destroy_cb_t destroy_callback; - void *userdata; -}; - -static clist pmain_defer_gc_list; - -static pa_defer_event *pmain_defer_new(pa_mainloop_api *api, pa_defer_event_cb_t cb, void *userdata); -static void pmain_defer_enable(pa_defer_event *e, int b); -static void pmain_defer_free(pa_defer_event *e); -static void pmain_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb); - -static void pmain_quit(pa_mainloop_api *a, int retval); - -static struct main_hook pmain_gc_hook; - -static void pmain_schedule_gc(void); - -struct pa_mainloop_api pmain_api = { - .io_new = pmain_io_new, - .io_enable = pmain_io_enable, - .io_free = pmain_io_free, - .io_set_destroy = pmain_io_set_destroy, - - .time_new = pmain_time_new, - .time_restart = pmain_time_restart, - .time_free = pmain_time_free, - .time_set_destroy = pmain_time_set_destroy, - - .defer_new = pmain_defer_new, - .defer_enable = pmain_defer_enable, - .defer_free = pmain_defer_free, - .defer_set_destroy = pmain_defer_set_destroy, - - .quit = pmain_quit, -}; - -static struct pmain_io *pmain_get_io(int fd) -{ - CLIST_FOR_EACH(struct pmain_io *, io, pmain_io_list) - if (io->f.fd == fd) - { - DBG("Pulse: Recycling IO master"); - return io; - } - - struct pmain_io *io = xmalloc_zero(sizeof(*io)); - io->f.fd = fd; - io->f.data = io; - clist_add_tail(&pmain_io_list, &io->n); - clist_init(&io->io_events); - return io; -} - -static pa_io_event *pmain_io_new(pa_mainloop_api *api UNUSED, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata) -{ - struct pa_io_event *e = xmalloc_zero(sizeof(*e)); - DBG("Pulse: Creating new IO %p for fd %u", e, fd); - - e->io = pmain_get_io(fd); - e->callback = cb; - e->userdata = userdata; - clist_add_head(&e->io->io_events, &e->n); // Do not call the new IO if created from another IO on the same fd - pmain_io_enable(e, events); - return e; -} - -static int pmain_io_read(struct main_file *f) -{ - struct pmain_io *io = f->data; - DBG("Pulse: fd %d ready for read", io->f.fd); - - CLIST_FOR_EACH(struct pa_io_event *, e, io->io_events) - if (e->events & PA_IO_EVENT_INPUT) - { - DBG("Pulse: Callback on IO %p", e); - e->callback(&pmain_api, e, io->f.fd, PA_IO_EVENT_INPUT, e->userdata); - } - - DBG("Pulse: fd %d read done", io->f.fd); - return HOOK_IDLE; -} - -static int pmain_io_write(struct main_file *f) -{ - struct pmain_io *io = f->data; - DBG("Pulse: fd %d ready for write", io->f.fd); - - CLIST_FOR_EACH(struct pa_io_event *, e, io->io_events) - if (e->events & PA_IO_EVENT_OUTPUT) - { - DBG("Pulse: Callback on IO %p", e); - e->callback(&pmain_api, e, io->f.fd, PA_IO_EVENT_OUTPUT, e->userdata); - } - - DBG("Pulse: fd %d write done", io->f.fd); - return HOOK_IDLE; -} - -static void pmain_io_enable(pa_io_event *e, pa_io_event_flags_t events) -{ - struct pmain_io *io = e->io; - DBG("Pulse: Changing IO event mask for IO %p on fd %d to %02x", e, io->f.fd, events); - e->events = events; - - pa_io_event_flags_t mask = 0; - CLIST_FOR_EACH(struct pa_io_event *, f, io->io_events) - mask |= f->events; - DBG("Pulse: Recalculated IO mask for fd %d to %02x", io->f.fd, mask); - - if (mask) - { - io->f.read_handler = (mask & PA_IO_EVENT_INPUT) ? pmain_io_read : NULL; - io->f.write_handler = (mask & PA_IO_EVENT_OUTPUT) ? pmain_io_write : NULL; - if (file_is_active(&io->f)) - file_chg(&io->f); - else - file_add(&io->f); - } - else - file_del(&io->f); -} - -static void pmain_io_free(pa_io_event *e) -{ - DBG("Pulse: Deleting IO %p for fd %d", e, e->io->f.fd); - pmain_io_enable(e, 0); - clist_add_tail(&pmain_io_gc_list, &e->gc_n); - pmain_schedule_gc(); -} - -static void pmain_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb) -{ - e->destroy_callback = cb; -} - -static void pmain_time_handler(struct main_timer *t) -{ - struct pa_time_event *e = t->data; - DBG("Pulse: Timer %p triggered", e); - timer_del(t); - e->callback(&pmain_api, e, &e->tv, e->userdata); - DBG("Pulse: Timer %p done", e); -} - -static pa_time_event *pmain_time_new(pa_mainloop_api *api UNUSED, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata) -{ - struct pa_time_event *e = xmalloc_zero(sizeof(*e)); - DBG("Pulse: Creating timer %p", e); - e->callback = cb; - e->userdata = userdata; - e->t.handler = pmain_time_handler; - e->t.data = e; - pmain_time_restart(e, tv); - return e; -} - -static timestamp_t timeval_to_timestamp(const struct timeval *tv) -{ - return 1000 * (timestamp_t) tv->tv_sec + tv->tv_usec / 1000; -} - -static void pmain_time_restart(pa_time_event *e, const struct timeval *tv) -{ - struct timeval now; - gettimeofday(&now, NULL); - timestamp_t ts_now = timeval_to_timestamp(&now); - timestamp_t ts_fire = timeval_to_timestamp(tv); - timestamp_t ts_delta = ts_fire - ts_now; - DBG("Pulse: Setting timer %p to %+d", e, (int) ts_delta); - timer_del(&e->t); - e->tv = *tv; - timer_add_rel(&e->t, ts_delta); -} - -static void pmain_time_free(pa_time_event *e) -{ - DBG("Pulse: Timer %p deleted", e); - timer_del(&e->t); - clist_add_tail(&pmain_time_gc_list, &e->n); - pmain_schedule_gc(); -} - -static void pmain_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb) -{ - e->destroy_callback = cb; -} - -static int pmain_defer_handler(struct main_hook *h) -{ - struct pa_defer_event *e = h->data; - DBG("Pulse: Deferred event %p triggered", e); - e->callback(&pmain_api, e, e->userdata); - DBG("Pulse: Deferred event done"); - return hook_is_active(&e->h) ? HOOK_RETRY : HOOK_IDLE; -} - -static pa_defer_event *pmain_defer_new(pa_mainloop_api *api UNUSED, pa_defer_event_cb_t cb, void *userdata) -{ - struct pa_defer_event *e = xmalloc_zero(sizeof(*e)); - DBG("Pulse: Creating defer %p", e); - e->callback = cb; - e->userdata = userdata; - e->h.handler = pmain_defer_handler; - e->h.data = e; - pmain_defer_enable(e, 1); - return e; -} - -static void pmain_defer_enable(pa_defer_event *e, int b) -{ - DBG("Pulse: %sabling defer %p", (b ? "En" : "Dis"), e); - if (b) - hook_add(&e->h); - else - hook_del(&e->h); -} - -static void pmain_defer_free(pa_defer_event *e) -{ - DBG("Pulse: Deferred event %p deleted", e); - hook_del(&e->h); - clist_add_tail(&pmain_defer_gc_list, &e->n); - pmain_schedule_gc(); -} - -static void pmain_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb) -{ - e->destroy_callback = cb; -} - -static void pmain_quit(pa_mainloop_api *a UNUSED, int retval UNUSED) -{ - DBG("Pulse: Main loop quit not implemented"); -} - -static int pmain_gc_handler(struct main_hook *h) -{ - DBG("Pulse: Garbage collector"); - hook_del(h); - - cnode *n; - while (n = clist_remove_head(&pmain_io_gc_list)) - { - struct pa_io_event *ei = SKIP_BACK(struct pa_io_event, gc_n, n); - struct pmain_io *io = ei->io; - DBG("Pulse: GC of IO event %p on fd %d", ei, io->f.fd); - if (ei->destroy_callback) - ei->destroy_callback(&pmain_api, ei, ei->userdata); - clist_remove(&ei->n); - if (clist_empty(&io->io_events)) - { - ASSERT(!file_is_active(&io->f)); - DBG("Pulse: GC of IO master for fd %d", io->f.fd); - clist_remove(&io->n); - xfree(io); - } - xfree(ei); - } - - struct pa_time_event *et; - while (et = (struct pa_time_event *) clist_remove_head(&pmain_time_gc_list)) - { - DBG("Pulse: GC for timer %p", et); - if (et->destroy_callback) - et->destroy_callback(&pmain_api, et, et->userdata); - xfree(et); - } - - struct pa_defer_event *ed; - while (ed = (struct pa_defer_event *) clist_remove_head(&pmain_defer_gc_list)) - { - DBG("Pulse: GC for defer %p", ed); - if (ed->destroy_callback) - ed->destroy_callback(&pmain_api, ed, ed->userdata); - xfree(ed); - } - - DBG("Pulse: Garbage collector done"); - return HOOK_RETRY; -} - -static void pmain_schedule_gc(void) -{ - hook_add(&pmain_gc_hook); -} - -void pmain_init(void) -{ - clist_init(&pmain_io_list); - clist_init(&pmain_io_gc_list); - clist_init(&pmain_time_gc_list); - clist_init(&pmain_defer_gc_list); - pmain_gc_hook.handler = pmain_gc_handler; -} diff --git a/ursaryd/pulse.c b/ursaryd/pulse.c deleted file mode 100644 index 21005ba..0000000 --- a/ursaryd/pulse.c +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Asynchronous Interface to PulseAudio - * - * (c) 2014 Martin Mares - */ - -#define LOCAL_DEBUG - -#include -#include -#include -#include - -#include -#include -#include - -#include "ursaryd.h" - -enum pulse_state pulse_state; -#define PULSE_STATE(s) do { pulse_state = s; DBG("Pulse: " #s); } while (0) - -clist pulse_client_list, pulse_sink_list, pulse_sink_input_list; - -static pa_context *pulse_ctx; -static struct main_timer pulse_connect_timer; - -/*** Tracking of currently running asynchronous operations ***/ - -struct pulse_op { - cnode n; - pa_operation *o; - bool is_init; -}; - -static clist pulse_op_list; - -static struct pulse_op *pulse_op_new(void) -{ - struct pulse_op *op = xmalloc_zero(sizeof(*op)); - clist_add_tail(&pulse_op_list, &op->n); - return op; -} - -static void pulse_op_done(struct pulse_op *op) -{ - if (op->o) - pa_operation_unref(op->o); - clist_remove(&op->n); - xfree(op); -} - -static void pulse_op_cancel_all(void) -{ - struct pulse_op *op; - while (op = (struct pulse_op *) clist_head(&pulse_op_list)) - { - DBG("Pulse: Cancelling pending operation"); - pa_operation_cancel(op->o); - pulse_op_done(op); - } -} - -#define PULSE_ASYNC_RUN(name, ...) do { struct pulse_op *_op = pulse_op_new(); _op->o = name(pulse_ctx, __VA_ARGS__, _op); } while (0) -#define PULSE_ASYNC_INIT_RUN(name, ...) do { struct pulse_op *_op = pulse_op_new(); _op->is_init = 1; _op->o = name(pulse_ctx, __VA_ARGS__, _op); } while (0) - -static void pulse_success_cb(pa_context *ctx UNUSED, int success, void *userdata) -{ - if (!success) - msg(L_ERROR, "Pulse: Failure reported"); - pulse_op_done(userdata); -} - -/*** Debugging dumps ***/ - -void pulse_dump(void) -{ - msg(L_DEBUG, "## Server: default_sink=%s", pulse_default_sink_name); - - CLIST_FOR_EACH(struct pulse_client *, c, pulse_client_list) - msg(L_DEBUG, "## Client #%d: %s host=%s", c->idx, c->name, c->host); - - CLIST_FOR_EACH(struct pulse_sink *, s, pulse_sink_list) - msg(L_DEBUG, "## Sink #%d: %s channels=%u volume=%u base_vol=%u mute=%u", - s->idx, s->name, s->channels, s->volume, s->base_volume, s->mute); - - CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list) - msg(L_DEBUG, "## Sink input #%d: %s client=%d sink=%d channels=%u volume=%u mute=%u", - s->idx, s->name, s->client_idx, s->sink_idx, s->channels, s->volume, s->mute); -} - -static void pulse_dump_proplist(pa_proplist *pl UNUSED) -{ -#if 0 - void *iterator = NULL; - const char *key; - - while (key = pa_proplist_iterate(pl, &iterator)) - { - const char *val = pa_proplist_gets(pl, key); - DBG(" %s = %s", key, val); - } -#endif -} - -/*** Server state ***/ - -char *pulse_default_sink_name; - -static void pulse_server_cb(pa_context *ctx UNUSED, const pa_server_info *i, void *userdata) -{ - struct pulse_op *op = userdata; - - DBG("Pulse: SERVER default_sink=%s", i->default_sink_name); - SET_STRING(pulse_default_sink_name, i->default_sink_name); - - if (op->is_init) - { - PULSE_STATE(PS_ONLINE); - msg(L_INFO, "PulseAudio is ready"); - } - pulse_op_done(op); - schedule_update(); -} - -void pulse_server_set_default_sink(const char *name) -{ - PULSE_ASYNC_RUN(pa_context_set_default_sink, name, pulse_success_cb); -} - -/*** Sink inputs ***/ - -#define HASH_NODE struct pulse_sink_input -#define HASH_PREFIX(x) pulse_sink_input_##x -#define HASH_KEY_ATOMIC idx -#define HASH_WANT_CLEANUP -#define HASH_WANT_LOOKUP -#define HASH_WANT_REMOVE -#define HASH_ZERO_FILL -#include - -static void pulse_sink_input_cb(pa_context *ctx UNUSED, const pa_sink_input_info *i, int eol, void *userdata) -{ - struct pulse_op *op = userdata; - - if (eol) - { - if (op->is_init) - { - PULSE_STATE(PS_GET_SERVER); - PULSE_ASYNC_INIT_RUN(pa_context_get_server_info, pulse_server_cb); - } - pulse_op_done(op); - return; - } - - DBG("Pulse: SINK INPUT #%u: %s client=%d sink=%d chans=%d has_vol=%d vol_rw=%d volume=%u mute=%d", - i->index, i->name, i->client, i->sink, i->channel_map.channels, i->has_volume, i->volume_writable, i->volume.values[0], i->mute); - pulse_dump_proplist(i->proplist); - - struct pulse_sink_input *s = pulse_sink_input_lookup(i->index); - if (!clist_is_linked(&s->n)) - clist_add_tail(&pulse_sink_input_list, &s->n); - SET_STRING(s->name, i->name); - s->client_idx = i->client; - s->sink_idx = i->sink; - s->channels = i->channel_map.channels; - s->volume = pa_cvolume_avg(&i->volume); - s->mute = i->mute; - schedule_update(); -} - -static void pulse_sink_input_gone(int idx) -{ - DBG("Pulse: REMOVE SINK INPUT #%d", idx); - struct pulse_sink_input *s = pulse_sink_input_lookup(idx); - clist_remove(&s->n); - pulse_sink_input_remove(s); - schedule_update(); -} - -void pulse_sink_input_set_volume(int idx, pa_cvolume *cvol) -{ - PULSE_ASYNC_RUN(pa_context_set_sink_input_volume, idx, cvol, pulse_success_cb); -} - -void pulse_sink_input_set_mute(int idx, bool mute) -{ - PULSE_ASYNC_RUN(pa_context_set_sink_input_mute, idx, mute, pulse_success_cb); -} - -void pulse_sink_input_move(int input_idx, int sink_idx) -{ - PULSE_ASYNC_RUN(pa_context_move_sink_input_by_index, input_idx, sink_idx, pulse_success_cb); -} - -/*** Sinks ***/ - -#define HASH_NODE struct pulse_sink -#define HASH_PREFIX(x) pulse_sink_##x -#define HASH_KEY_ATOMIC idx -#define HASH_WANT_CLEANUP -#define HASH_WANT_LOOKUP -#define HASH_WANT_REMOVE -#define HASH_ZERO_FILL -#include - -static void pulse_sink_cb(pa_context *ctx UNUSED, const pa_sink_info *i, int eol, void *userdata) -{ - struct pulse_op *op = userdata; - - if (eol) - { - if (op->is_init) - { - PULSE_STATE(PS_GET_SINK_INPUTS); - PULSE_ASYNC_INIT_RUN(pa_context_get_sink_input_info_list, pulse_sink_input_cb); - } - pulse_op_done(op); - return; - } - - DBG("Pulse: SINK #%u: %s (%s) flags=%08x channels=%u volume=%u mute=%d base_vol=%u state=%u", - i->index, i->name, i->description, i->flags, i->channel_map.channels, i->volume.values[0], i->mute, i->base_volume, i->state); - pulse_dump_proplist(i->proplist); - - struct pulse_sink *s = pulse_sink_lookup(i->index); - if (!clist_is_linked(&s->n)) - clist_add_tail(&pulse_sink_list, &s->n); - SET_STRING(s->name, i->name); - s->channels = i->channel_map.channels; - s->volume = pa_cvolume_avg(&i->volume); - s->base_volume = i->base_volume; - s->mute = i->mute; - schedule_update(); -} - -static void pulse_sink_gone(int idx) -{ - DBG("Pulse: REMOVE SINK #%d", idx); - struct pulse_sink *s = pulse_sink_lookup(idx); - clist_remove(&s->n); - pulse_sink_remove(s); - schedule_update(); -} - -struct pulse_sink *pulse_sink_by_name(const char *name) -{ - CLIST_FOR_EACH(struct pulse_sink *, s, pulse_sink_list) - if (!strcmp(s->name, name)) - return s; - return NULL; -} - -struct pulse_sink *pulse_sink_by_idx(int idx) -{ - return pulse_sink_lookup(idx); -} - -void pulse_sink_set_volume(int idx, pa_cvolume *cvol) -{ - PULSE_ASYNC_RUN(pa_context_set_sink_volume_by_index, idx, cvol, pulse_success_cb); -} - -void pulse_sink_set_mute(int idx, bool mute) -{ - PULSE_ASYNC_RUN(pa_context_set_sink_mute_by_index, idx, mute, pulse_success_cb); -} - -/*** Clients ***/ - -#define HASH_NODE struct pulse_client -#define HASH_PREFIX(x) pulse_client_##x -#define HASH_KEY_ATOMIC idx -#define HASH_WANT_CLEANUP -#define HASH_WANT_LOOKUP -#define HASH_WANT_REMOVE -#define HASH_ZERO_FILL -#include - -static void pulse_client_cb(pa_context *ctx UNUSED, const pa_client_info *i, int eol, void *userdata) -{ - struct pulse_op *op = userdata; - - if (eol) - { - if (op->is_init) - { - PULSE_STATE(PS_GET_SINKS); - PULSE_ASYNC_INIT_RUN(pa_context_get_sink_info_list, pulse_sink_cb); - } - pulse_op_done(op); - return; - } - - char *host = stk_strdup(pa_proplist_gets(i->proplist, "application.process.host") ? : "?"); - DBG("Pulse: CLIENT #%u: %s mod=%u drv=%s host=%s", - i->index, i->name, i->owner_module, i->driver, host); - pulse_dump_proplist(i->proplist); - - struct pulse_client *c = pulse_client_lookup(i->index); - if (!clist_is_linked(&c->n)) - clist_add_tail(&pulse_client_list, &c->n); - SET_STRING(c->name, i->name); - SET_STRING(c->host, host); - schedule_update(); -} - -static void pulse_client_gone(int idx) -{ - DBG("Pulse: REMOVE CLIENT #%d", idx); - struct pulse_client *c = pulse_client_lookup(idx); - clist_remove(&c->n); - pulse_client_remove(c); - schedule_update(); -} - -struct pulse_client *pulse_client_by_idx(int idx) -{ - return pulse_client_lookup(idx); -} - -/*** Events ***/ - -static void pulse_subscribe_done_cb(pa_context *ctx UNUSED, int success, void *userdata) -{ - pulse_op_done(userdata); - - if (!success) - msg(L_ERROR, "pa_context_subscribe failed: success=%d", success); - - PULSE_STATE(PS_GET_CLIENTS); - PULSE_ASYNC_INIT_RUN(pa_context_get_client_info_list, pulse_client_cb); -} - -static void pulse_event_cb(pa_context *ctx UNUSED, pa_subscription_event_type_t type, uint32_t idx, void *userdata UNUSED) -{ - DBG("Pulse: SUBSCRIBE EVENT type=%08x idx=%u", type, idx); - - uint object = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; - uint action = type & PA_SUBSCRIPTION_EVENT_TYPE_MASK; - switch (object) - { - case PA_SUBSCRIPTION_EVENT_CLIENT: - if (action == PA_SUBSCRIPTION_EVENT_NEW || action == PA_SUBSCRIPTION_EVENT_CHANGE) - PULSE_ASYNC_RUN(pa_context_get_client_info, idx, pulse_client_cb); - else if (action == PA_SUBSCRIPTION_EVENT_REMOVE) - pulse_client_gone(idx); - break; - case PA_SUBSCRIPTION_EVENT_SINK: - if (action == PA_SUBSCRIPTION_EVENT_NEW || action == PA_SUBSCRIPTION_EVENT_CHANGE) - PULSE_ASYNC_RUN(pa_context_get_sink_info_by_index, idx, pulse_sink_cb); - else if (action == PA_SUBSCRIPTION_EVENT_REMOVE) - pulse_sink_gone(idx); - break; - case PA_SUBSCRIPTION_EVENT_SINK_INPUT: - if (action == PA_SUBSCRIPTION_EVENT_NEW || action == PA_SUBSCRIPTION_EVENT_CHANGE) - PULSE_ASYNC_RUN(pa_context_get_sink_input_info, idx, pulse_sink_input_cb); - else if (action == PA_SUBSCRIPTION_EVENT_REMOVE) - pulse_sink_input_gone(idx); - break; - case PA_SUBSCRIPTION_EVENT_SERVER: - if (action == PA_SUBSCRIPTION_EVENT_CHANGE) - PULSE_ASYNC_RUN(pa_context_get_server_info, pulse_server_cb); - break; - } -} - -/*** Server state ***/ - -static void pulse_shutdown(void) -{ - DBG("Pulse: Shutting down"); - pulse_client_cleanup(); - pulse_sink_cleanup(); - pulse_sink_input_cleanup(); -} - -static void pulse_state_cb(pa_context *ctx, void *userdata UNUSED) -{ - int state = pa_context_get_state(ctx); - DBG("Pulse: State callback, new state = %d", state); - if (state == PA_CONTEXT_READY) - { - if (pulse_state == PS_OFFLINE) - { - PULSE_STATE(PS_SUBSCRIBE); - pa_context_set_subscribe_callback(ctx, pulse_event_cb, NULL); - PULSE_ASYNC_INIT_RUN(pa_context_subscribe, PA_SUBSCRIPTION_MASK_ALL, pulse_subscribe_done_cb); - } - } - else - { - if (pulse_state != PS_OFFLINE) - { - msg(L_INFO, "Lost connection to PulseAudio"); - PULSE_STATE(PS_OFFLINE); - pulse_op_cancel_all(); - pulse_shutdown(); - schedule_update(); - } - if (state == PA_CONTEXT_FAILED && !timer_is_active(&pulse_connect_timer)) - timer_add_rel(&pulse_connect_timer, 2000); - } -} - -static void pulse_connect(struct main_timer *t) -{ - msg(L_DEBUG, "Connecting to PulseAudio"); - timer_del(t); - - clist_init(&pulse_op_list); - clist_init(&pulse_client_list); - clist_init(&pulse_sink_list); - clist_init(&pulse_sink_input_list); - pulse_client_init(); - pulse_sink_init(); - pulse_sink_input_init(); - - if (pulse_ctx) - pa_context_unref(pulse_ctx); - pulse_ctx = pa_context_new(&pmain_api, "ursaryd"); - - pa_context_set_state_callback(pulse_ctx, pulse_state_cb, NULL); - pa_context_connect(pulse_ctx, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL); -} - -void pulse_init(void) -{ - pmain_init(); - - pulse_connect_timer.handler = pulse_connect; - timer_add_rel(&pulse_connect_timer, 0); -} diff --git a/ursaryd/ursaryd.c b/ursaryd/ursaryd.c deleted file mode 100644 index eaa5654..0000000 --- a/ursaryd/ursaryd.c +++ /dev/null @@ -1,575 +0,0 @@ -/* - * The Ursary Audio Controls - * - * (c) 2014 Martin Mares - */ - -#define LOCAL_DEBUG - -#include -#include -#include -#include - -#include -#include -#include - -#include "ursaryd.h" - -/* - * Map of all controls - * - * rotary red button green button - * 0 sink Ursarium mute select as default (or assign to client selected by touch) - * 1 sink Catarium mute dtto - * 2 - - - - * 3 - - - - * 4 MPD mute play/pause/stop - * 5 Albireo mute - - * 6 Ogion mute - - * 7 Ursula mute - - * - * center all sinks - * slider - - */ - -/*** Sink controls ***/ - -static double volume_from_pa(pa_volume_t vol) -{ - return (double) vol / PA_VOLUME_NORM; -} - -static pa_volume_t volume_to_pa(double vol) -{ - return vol * PA_VOLUME_NORM + 0.0001; -} - -static void update_ring_from_sink(int ring, const char *sink_name) -{ - struct pulse_sink *s = pulse_sink_by_name(sink_name); - if (!s) - { - noct_set_ring(ring, RING_MODE_SINGLE_ON, 0x7f); - noct_set_button(ring, 0); - return; - } - - if (s->mute) - { - noct_set_ring(ring, RING_MODE_SINGLE_ON, 0x7f); - noct_set_button(ring, 1); - return; - } - - double vol = CLAMP(volume_from_pa(s->volume), 0, 1); - noct_set_ring(ring, RING_MODE_LEFT, CLAMP((int)(0x7f * vol), 12, 0x7f)); - noct_set_button(ring, 0); -} - -static void update_sink_from_rotary(int delta, const char *sink_name) -{ - struct pulse_sink *s = pulse_sink_by_name(sink_name); - if (!s) - return; - - double vol = volume_from_pa(s->volume) + delta * 0.02; - pa_volume_t pavol = volume_to_pa(CLAMP(vol, 0, 1)); - if (pavol == s->volume) - return; - pa_cvolume cvol; - pa_cvolume_set(&cvol, s->channels, pavol); - - DBG("## Setting volume of sink %s to %d", s->name, cvol.values[0]); - pulse_sink_set_volume(s->idx, &cvol); -} - -static void update_sink_mute_from_button(int on, const char *sink_name) -{ - if (!on) - return; - - struct pulse_sink *s = pulse_sink_by_name(sink_name); - if (!s) - return; - - DBG("## Setting mute of sink %s to %d", s->name, !s->mute); - pulse_sink_set_mute(s->idx, !s->mute); -} - -/*** Client controls ***/ - -struct client_map { - int rotary; - const char *client; - const char *host; - double range; -}; - -static struct client_map client_map[] = { - { 4, "Music Player Daemon", "albireo", 1 }, - { 5, "MPlayer", NULL, 1 }, - { 6, NULL, "ogion", 1 }, - { 7, NULL, "ursula", 1 }, -}; - -#define NUM_CLIENTS ARRAY_SIZE(client_map) - -struct client_state { - double volume; - bool have_muted[2]; -}; - -static struct client_state client_state[NUM_CLIENTS]; - -static int find_client_by_rotary(int rotary) -{ - uns i; - for (i=0; i < NUM_CLIENTS; i++) - if (client_map[i].rotary == rotary) - return i; - return -1; -} - -static void calc_clients(void) -{ - bzero(client_state, sizeof(client_state)); - - CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list) - { - s->noct_client_idx = -1; - - if (s->client_idx < 0 || s->sink_idx < 0) - continue; - - struct pulse_client *c = pulse_client_by_idx(s->client_idx); - if (!c) - continue; - - for (uns i=0; i < NUM_CLIENTS; i++) - { - struct client_map *cm = &client_map[i]; - struct client_state *cs = &client_state[i]; - if ((!cm->client || !strcmp(cm->client, c->name)) && - (!cm->host || !strcmp(cm->host, c->host))) - { - // DBG("@@ Client #%d, sink input #%d -> rotary %d", s->client_idx, s->idx, cm->rotary); - s->noct_client_idx = i; - cs->volume = MAX(cs->volume, s->volume); - cs->have_muted[!!s->mute] = 1; - break; - } - } - } -} - -static void update_clients(void) -{ - calc_clients(); - - for (uns i=0; i < NUM_CLIENTS; i++) - { - struct client_map *cm = &client_map[i]; - struct client_state *cs = &client_state[i]; - if (!cs->have_muted[0] && !cs->have_muted[1]) - { - noct_set_ring(cm->rotary, RING_MODE_LEFT, 0); - noct_set_button(cm->rotary, 0); - } - else if (!cs->have_muted[0]) - { - noct_set_ring(cm->rotary, RING_MODE_SINGLE_ON, 0x7f); - noct_set_button(cm->rotary, 1); - } - else - { - double vol = CLAMP(volume_from_pa(cs->volume), 0, cm->range); - int val = 0x7f * vol / cm->range; - val = CLAMP(val, 12, 0x7f); - noct_set_ring(cm->rotary, RING_MODE_LEFT, val); - noct_set_button(cm->rotary, 0); - } - } -} - -static void update_client_from_rotary(int rotary, int delta) -{ - int i = find_client_by_rotary(rotary); - if (i < 0) - return; - struct client_map *cm = &client_map[i]; - struct client_state *cs = &client_state[i]; - - calc_clients(); - double vol = volume_from_pa(cs->volume) + delta*0.02; - pa_volume_t pavol = volume_to_pa(CLAMP(vol, 0, cm->range)); - - CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list) - { - if (s->noct_client_idx == i && s->volume != pavol) - { - DBG("@@ Client #%d, sink input #%d: setting volume=%u", s->client_idx, s->idx, pavol); - pa_cvolume cvol; - pa_cvolume_set(&cvol, s->channels, pavol); - pulse_sink_input_set_volume(s->idx, &cvol); - } - } -} - -static void update_client_from_button(int button, int on) -{ - if (button >= 8 || !on) - return; - - int i = find_client_by_rotary(button); - if (i < 0) - return; - struct client_state *cs = &client_state[i]; - - calc_clients(); - if (!cs->have_muted[0] && !cs->have_muted[1]) - return; - uns mute = !cs->have_muted[1]; - - CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list) - { - if (s->noct_client_idx == i) - { - DBG("@@ Client #%d, sink input #%d: setting mute=%u", s->client_idx, s->idx, mute); - pulse_sink_input_set_mute(s->idx, mute); - } - } -} - -static int find_touched_client(void) -{ - int touched = -1; - - for (uns i=0; i < NUM_CLIENTS; i++) - if (noct_rotary_touched[client_map[i].rotary]) - { - if (touched >= 0) - return -1; - touched = i; - } - return touched; -} - -/*** Default sink controls ***/ - -static const char *get_client_sink(int i) -{ - const char *sink = NULL; - - CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list) - if (s->noct_client_idx == i) - { - struct pulse_sink *sk = (s->sink_idx >= 0) ? pulse_sink_by_idx(s->sink_idx) : NULL; - const char *ss = sk ? sk->name : NULL; - if (!sink) - sink = ss; - else if (strcmp(sink, ss)) - sink = "?"; - } - return sink ? : "?"; -} - -static void update_default_sink(void) -{ - int i = find_touched_client(); - const char *sink; - if (i >= 0) - sink = get_client_sink(i); - else - sink = pulse_default_sink_name ? : "?"; - - if (!strcmp(sink, "ursarium")) - { - noct_set_button(8, 1); - noct_set_button(9, 0); - } - else if (!strcmp(sink, "catarium")) - { - noct_set_button(8, 0); - noct_set_button(9, 1); - } - else - { - noct_set_button(8, 0); - noct_set_button(9, 0); - } -} - -static void update_default_sink_from_button(int button, int on) -{ - if (!on) - return; - - int i = find_touched_client(); - const char *sink; - if (i >= 0) - sink = get_client_sink(i); - else - sink = pulse_default_sink_name ? : "?"; - - const char *switch_to = NULL; - if (button == 8) - { - if (!strcmp(sink, "ursarium")) - switch_to = "burrow"; - else - switch_to = "ursarium"; - } - else if (button == 9) - { - if (!strcmp(sink, "catarium")) - switch_to = "burrow"; - else - switch_to = "catarium"; - } - - if (!switch_to) - return; - - if (i >= 0) - { - struct pulse_sink *sk = pulse_sink_by_name(switch_to); - if (!sk) - return; - - CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list) - if (s->noct_client_idx == i) - { - DBG("Moving input #%d to sink #%d", s->idx, sk->idx); - pulse_sink_input_move(s->idx, sk->idx); - } - } - else - { - DBG("Switching default sink to %s", switch_to); - pulse_server_set_default_sink(switch_to); - } -} - -/*** MPD controls ***/ - -static bool mpd_flash_state; - -static void mpd_flash_timeout(struct main_timer *t) -{ - mpd_flash_state ^= 1; - noct_set_button(12, mpd_flash_state); - timer_add_rel(t, 500); -} - -static struct main_timer mpd_flash_timer = { - .handler = mpd_flash_timeout, -}; - -static void update_mpd(void) -{ - const char *state = mpd_get_player_state(); - if (!strcmp(state, "play")) - { - noct_set_button(12, 1); - timer_del(&mpd_flash_timer); - } - else if (!strcmp(state, "pause")) - { - if (!timer_is_active(&mpd_flash_timer)) - { - mpd_flash_state = 1; - mpd_flash_timeout(&mpd_flash_timer); - } - } - else - { - noct_set_button(12, 0); - timer_del(&mpd_flash_timer); - } -} - -static void mpd_button_timeout(struct main_timer *t) -{ - DBG("MPD stop"); - timer_del(t); - mpd_stop(); -} - -static void update_mpd_from_button(int button UNUSED, int on) -{ - static struct main_timer mpd_button_timer = { - .handler = mpd_button_timeout, - }; - - const char *state = mpd_get_player_state(); - - if (!on) - { - if (timer_is_active(&mpd_button_timer)) - { - timer_del(&mpd_button_timer); - if (!strcmp(state, "play")) - { - DBG("MPD pause"); - mpd_pause(1); - } - else if (!strcmp(state, "pause")) - { - DBG("MPD resume"); - mpd_pause(0); - } - } - return; - } - - if (!strcmp(state, "stop")) - { - DBG("MPD play"); - mpd_play(); - } - else - { - DBG("MPD starting button timer"); - timer_add_rel(&mpd_button_timer, 1000); - } -} - -/*** Main update routines ***/ - -static struct main_timer update_timer; - -static void do_update(struct main_timer *t) -{ - timer_del(t); - if (!noct_is_ready()) - { - DBG("## UPDATE: Nocturn is not ready"); - return; - } - - static bool dead; - if (pulse_state != PS_ONLINE) - { - DBG("## UPDATE: Pulse is not online"); - for (int i=0; i<=8; i++) - noct_set_ring(i, RING_MODE_LEFT, 0); - for (int i=0; i<8; i++) - { - noct_set_button(i, 1); - noct_set_button(i+8, 0); - } - dead = 1; - return; - } - if (dead) - { - DBG("## UPDATE: Waking up from the dead"); - for (int i=0; i<=8; i++) - noct_set_ring(i, RING_MODE_LEFT, 0); - for (int i=0; i<16; i++) - noct_set_button(i, 0); - dead = 0; - } - - DBG("## UPDATE"); -#ifdef LOCAL_DEBUG - pulse_dump(); -#endif - - update_ring_from_sink(0, "ursarium"); - update_ring_from_sink(1, "catarium"); - update_clients(); - update_default_sink(); - update_mpd(); -} - -void schedule_update(void) -{ - timer_add_rel(&update_timer, 10); -} - -void notify_rotary(int rotary, int delta) -{ - if (pulse_state != PS_ONLINE) - { - DBG("## NOTIFY: Pulse is not online"); - return; - } - - switch (rotary) - { - case 0: - update_sink_from_rotary(delta, "ursarium"); - break; - case 1: - update_sink_from_rotary(delta, "catarium"); - break; - case 8: - update_sink_from_rotary(delta, "ursarium"); - update_sink_from_rotary(delta, "catarium"); - break; - default: - update_client_from_rotary(rotary, delta); - } -} - -void notify_button(int button, int on) -{ - if (pulse_state != PS_ONLINE) - { - DBG("## NOTIFY: Pulse is not online"); - return; - } - - switch (button) - { - case 0: - update_sink_mute_from_button(on, "ursarium"); - break; - case 1: - update_sink_mute_from_button(on, "catarium"); - break; - case 8: - case 9: - update_default_sink_from_button(button, on); - break; - case 12: - update_mpd_from_button(button, on); - break; - default: - update_client_from_button(button, on); - } -} - -void notify_touch(int rotary, int on UNUSED) -{ - if (pulse_state != PS_ONLINE) - { - DBG("## NOTIFY: Pulse is not online"); - return; - } - - // Rotary touches switch meaning of LEDs, this is handled inside display updates - if (rotary >= 4 && rotary < 8) - schedule_update(); -} - -/*** Main entry point ***/ - -int main(int argc UNUSED, char **argv) -{ - log_init(argv[0]); - main_init(); - update_timer.handler = do_update; - - noct_init(); - pulse_init(); - mpd_init(); - - msg(L_DEBUG, "Entering main loop"); - main_loop(); - - return 0; -} diff --git a/ursaryd/ursaryd.h b/ursaryd/ursaryd.h deleted file mode 100644 index ddfc18d..0000000 --- a/ursaryd/ursaryd.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * The Ursary Audio Controls - * - * (c) 2014 Martin Mares - */ - -#include - -#define SET_STRING(_field, _val) do { if (!_field || strcmp(_field, _val)) { xfree(_field); _field = xstrdup(_val); } } while (0) - -/* ursary.c */ - -void schedule_update(void); - -void notify_rotary(int rotary, int delta); -void notify_touch(int rotary, int on); -void notify_button(int button, int on); - -/* nocturn.c */ - -void noct_init(void); -bool noct_is_ready(void); -void noct_set_ring(int ring, int mode, int val); -void noct_set_button(int button, int val); - -extern char noct_rotary_touched[10]; // 8=center, 9=slider -extern char noct_button_pressed[16]; - -enum ring_mode { - RING_MODE_LEFT, - RING_MODE_RIGHT, - RING_MODE_MID_RIGHT, - RING_MODE_MID_SYM, - RING_MODE_SINGLE_ON, - RING_MODE_SINGLE_OFF, -}; - -/* pulse.c */ - -enum pulse_state { - PS_OFFLINE, - PS_SUBSCRIBE, - PS_GET_CLIENTS, - PS_GET_SINKS, - PS_GET_SINK_INPUTS, - PS_GET_SERVER, - PS_ONLINE, -}; - -extern enum pulse_state pulse_state; -extern char *pulse_default_sink_name; - -struct pulse_client { - cnode n; - int idx; - char *name; - char *host; -}; - -struct pulse_sink { - cnode n; - int idx; - char *name; - uint channels; - uint volume; - uint base_volume; - int mute; -}; - -struct pulse_sink_input { - cnode n; - int idx; - char *name; - int client_idx; - int sink_idx; - uint channels; - uint volume; - uint mute; - int noct_client_idx; // Used by the high-level logic below -}; - -extern clist pulse_client_list, pulse_sink_list, pulse_sink_input_list; - -void pulse_init(void); -void pulse_dump(void); -struct pulse_sink *pulse_sink_by_name(const char *name); -struct pulse_sink *pulse_sink_by_idx(int idx); -void pulse_sink_set_volume(int idx, pa_cvolume *cvol); -void pulse_sink_set_mute(int idx, bool mute); -void pulse_sink_input_set_volume(int idx, pa_cvolume *cvol); -void pulse_sink_input_set_mute(int idx, bool mute); -void pulse_sink_input_move(int input_idx, int sink_idx); -struct pulse_client *pulse_client_by_idx(int idx); -void pulse_server_set_default_sink(const char *name); - -/* pulse-ucw.c */ - -extern struct pa_mainloop_api pmain_api; - -void pmain_init(void); - -/* mpd.c */ - -enum mpd_state { - MPD_OFFLINE, - MPD_CONNECTING, - MPD_WAIT_GREETING, - MPD_ONLINE, -}; - -extern enum mpd_state mpd_state; - -void mpd_init(void); -const char *mpd_get_player_state(void); -void mpd_play(void); -void mpd_stop(void); -void mpd_pause(int arg);