+++ /dev/null
-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)
+++ /dev/null
-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
+++ /dev/null
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <jack/jack.h>
-
-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<nframes; i++) {
- out[i] = in[i];
- out2[i] = in[i];
- }
- }
- return 0;
-}
-
-static void port_callback(jack_port_id_t id, int reg, void *arg __attribute__((unused)))
-{
- jack_port_t *p = jack_port_by_id(jc, id);
- if (!p) {
- puts("port_callback: lookup failed");
- return;
- }
- const char *name = jack_port_name(p);
- printf("%s port %s\n", (reg ? "Registered" : "Unregistered"), name);
-
- const char *ss = strstr(name, ":from_slave_");
- if (reg && ss) {
- char *to = (ss[12] == '1' ? "brum:in1" : "brum:in2");
- printf("\t... connecting to %s\n", to);
- if (jack_connect(jc, name, to))
- puts("jack_connect failed");
- }
-}
-
-int main(void)
-{
- jc = jack_client_open("brum", JackNoStartServer, NULL);
- if (!jc) {
- puts("jack_client_open failed");
- return 1;
- }
-
- for (int i=0; i<2; i++) {
- char name[16];
- sprintf(name, "in%d", i+1);
- inport[i] = jack_port_register(jc, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
- if (!inport[i]) {
- puts("jack_port_register failed");
- return 1;
- }
- }
-
- for (int i=0; i<4; i++) {
- char name[16];
- sprintf(name, "out%d", i+1);
- outport[i] = jack_port_register(jc, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
- if (!outport[i]) {
- puts("jack_port_register failed");
- return 1;
- }
- }
-
- jack_set_process_callback(jc, process_callback, NULL);
- jack_set_port_registration_callback(jc, port_callback, NULL);
-
- if (jack_activate(jc)) {
- puts("jack_activate failed");
- return 1;
- }
-
- puts("Connecting ports...");
- for (int i=1; i<=4; i++) {
- char a[32], b[32];
- sprintf(a, "brum:out%d", i);
- sprintf(b, "system:playback_%d", i);
- if (jack_connect(jc, a, b))
- printf("Failed to connect %s -> %s\n", a, b);
- }
-
- puts("Running...");
- char xxx[16];
- read(0, xxx, sizeof(xxx));
-
- puts("exiting");
- jack_client_close(jc);
- return 0;
-}
+++ /dev/null
-/*
- * Interface to Music Player Daemon
- *
- * (c) 2014 Martin Mares <mj@ucw.cz>
- */
-
-#define LOCAL_DEBUG
-
-#include <ucw/lib.h>
-#include <ucw/clists.h>
-#include <ucw/mainloop.h>
-#include <ucw/mempool.h>
-#include <ucw/simple-lists.h>
-#include <ucw/stkstring.h>
-#include <ucw/string.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Interface to Novation Nocturn
- *
- * (c) 2014 Martin Mares <mj@ucw.cz>
- *
- * Protocol reverse-engineered by De Wet van Niekerk <dewert@gmail.com>,
- * see https://github.com/dewert/nocturn-linux-midi for inspiration.
- */
-
-#undef LOCAL_DEBUG
-
-#include <ucw/lib.h>
-#include <ucw/bitops.h>
-#include <ucw/clists.h>
-#include <ucw/gary.h>
-#include <ucw/mainloop.h>
-#include <ucw/stkstring.h>
-#include <ucw/string.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <sys/poll.h>
-
-#include <libusb.h>
-
-#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);
-}
+++ /dev/null
-/*
- * Glue between PulseAudio and LibUCW Mainloop
- *
- * (c) 2014 Martin Mares <mj@ucw.cz>
- */
-
-#undef LOCAL_DEBUG
-
-#include <ucw/lib.h>
-#include <ucw/clists.h>
-#include <ucw/mainloop.h>
-
-#include <sys/poll.h>
-#include <sys/time.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Asynchronous Interface to PulseAudio
- *
- * (c) 2014 Martin Mares <mj@ucw.cz>
- */
-
-#define LOCAL_DEBUG
-
-#include <ucw/lib.h>
-#include <ucw/clists.h>
-#include <ucw/mainloop.h>
-#include <ucw/stkstring.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#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 <ucw/hashtable.h>
-
-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 <ucw/hashtable.h>
-
-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 <ucw/hashtable.h>
-
-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);
-}
+++ /dev/null
-/*
- * The Ursary Audio Controls
- *
- * (c) 2014 Martin Mares <mj@ucw.cz>
- */
-
-#define LOCAL_DEBUG
-
-#include <ucw/lib.h>
-#include <ucw/clists.h>
-#include <ucw/mainloop.h>
-#include <ucw/stkstring.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#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;
-}
+++ /dev/null
-/*
- * The Ursary Audio Controls
- *
- * (c) 2014 Martin Mares <mj@ucw.cz>
- */
-
-#include <pulse/pulseaudio.h>
-
-#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);