From: Martin Mares Date: Sun, 9 Nov 2014 14:09:53 +0000 (+0100) Subject: Ursary: First light... X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=a90d0ef0b3d290c17e5b58d6f5bc9119e349a0d0;p=misc.git Ursary: First light... --- diff --git a/ursaryd/nocturn.c b/ursaryd/nocturn.c index 8ed7b18..50d0557 100644 --- a/ursaryd/nocturn.c +++ b/ursaryd/nocturn.c @@ -7,7 +7,7 @@ * see https://github.com/dewert/nocturn-linux-midi for inspiration. */ -#define LOCAL_DEBUG +#undef LOCAL_DEBUG #include #include @@ -127,6 +127,7 @@ static void noct_read_done(struct libusb_transfer *xfer) int r = cmd - 0x40; int delta = (arg < 0x40 ? arg : arg - 0x80); DBG("Noct: Rotary %d = %d", r, delta); + notify_rotary(r, delta); continue; } break; @@ -145,6 +146,7 @@ static void noct_read_done(struct libusb_transfer *xfer) { int delta = (arg < 0x40 ? arg : arg - 0x80); DBG("Noct: Center = %d", delta); + notify_rotary(8, delta); continue; } break; @@ -179,6 +181,7 @@ static void noct_read_done(struct libusb_transfer *xfer) int b = cmd - 0x70; int state = !!arg; DBG("Noct: Button %d = %d", b, state); + notify_button(b, state); continue; } break; @@ -204,7 +207,7 @@ static void noct_read_init(void) } static byte noct_button_state[16]; -static byte noct_ring_mode[8]; // 0=from-min, 1=from-max, 2=from-mid-right, 3=from-mid-both, 4=single-on, 5=single-off +static byte noct_ring_mode[8]; // RING_MODE_xxx static byte noct_ring_val[9]; static uns noct_dirty_button; @@ -223,8 +226,11 @@ static void noct_write_done(struct libusb_transfer *xfer) if (xfer->status != LIBUSB_TRANSFER_COMPLETED) { msg(L_ERROR, "USB write failed with status %d", xfer->status); + // FIXME: Handle the error in a meaningful way return; } + if (len < xfer->length) + msg(L_ERROR, "USB partial write: %d out of %d", len, xfer->length); noct_write_pending = 0; noct_sched_write(); @@ -275,6 +281,38 @@ static void noct_sched_write(void) } } +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_state[button] != val) + { + noct_button_state[button] = val; + noct_dirty_button |= 1U << button; + noct_sched_write(); + } +} + static void noct_write_init(void) { DBG("Noct: Write init"); @@ -294,14 +332,14 @@ static void noct_write_init(void) noct_sched_write(); } -void noct_init(void) +static struct main_timer noct_connect_timer; + +static void noct_connect(struct main_timer *t) { + timer_del(t); + msg(L_DEBUG, "Looking for Nocturn"); int err; - if ((err = libusb_init(&usb_ctx)) < 0) - die("libusb_init failed: error %d", err); - libusb_set_debug(usb_ctx, 3); - libusb_device **dev_list; libusb_device *found_dev = NULL; ssize_t len = libusb_get_device_list(usb_ctx, &dev_list); @@ -315,16 +353,23 @@ void noct_init(void) { msg(L_DEBUG, "Found device: bus %d, addr %d", libusb_get_bus_number(dev), libusb_get_device_address(dev)); if (found_dev) - die("Multiple Nocturn devices found. Please fix me to handle it."); + { + 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) - die("No Nocturn device found"); + { + msg(L_INFO, "No Nocturn device found"); + timer_add_rel(t, 5000); + return; + } - msg(L_DEBUG, "Initializing device"); + msg(L_DEBUG, "Initializing Nocturn"); if ((err = libusb_open(found_dev, &usb_dev)) < 0) die("libusb_open failed: error %d", err); @@ -351,7 +396,26 @@ void noct_init(void) libusb_interrupt_transfer(usb_dev, 0x02, xxx, 2, &done, 5000); #endif - DBG("USB: Connecting libusb to mainloop"); + noct_read_init(); + noct_write_init(); + schedule_update(); +} + +bool noct_is_ready(void) +{ + return !!usb_dev; // FIXME +} + +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"); @@ -365,6 +429,7 @@ void noct_init(void) usb_added_fd(fds[i]->fd, fds[i]->events, NULL); free(fds); - noct_read_init(); - noct_write_init(); + // Schedule search for the Nocturn + noct_connect_timer.handler = noct_connect; + timer_add_rel(&noct_connect_timer, 0); } diff --git a/ursaryd/ursaryd.h b/ursaryd/ursaryd.h index 39aae3d..8342e83 100644 --- a/ursaryd/ursaryd.h +++ b/ursaryd/ursaryd.h @@ -1,6 +1,25 @@ +/* main */ + +void schedule_update(void); + +void notify_rotary(int rotary, int delta); +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); + +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-ucw.c */ diff --git a/ursaryd/ut.c b/ursaryd/ut.c index 5e3fab4..0468955 100644 --- a/ursaryd/ut.c +++ b/ursaryd/ut.c @@ -13,14 +13,11 @@ #include "ursaryd.h" -/* - * Interface to PulseAudio - */ +/*** Interface to PulseAudio ***/ static pa_context *pulse_ctx; static void pulse_dump(void); -static void pulse_schedule_update(void); enum pulse_state { PS_OFFLINE, @@ -72,9 +69,16 @@ static void pulse_op_cancel_all(void) #define PULSE_ASYNC_RUN(name, ...) do { struct pulse_op *_op = pulse_op_new(); _op->o = name(__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(__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); +} + static void pulse_dump_proplist(pa_proplist *pl UNUSED) { -#ifdef LOCAL_DEBUG +#if 0 void *iterator = NULL; const char *key; @@ -115,7 +119,7 @@ static void pulse_sink_input_cb(pa_context *ctx UNUSED, const pa_sink_input_info if (op->is_init) { PULSE_STATE(PS_ONLINE); - pulse_schedule_update(); + schedule_update(); } pulse_op_done(op); return; @@ -131,7 +135,7 @@ static void pulse_sink_input_cb(pa_context *ctx UNUSED, const pa_sink_input_info s->sink_idx = i->sink; s->volume = pa_cvolume_avg(&i->volume); s->mute = i->mute; - pulse_schedule_update(); + schedule_update(); } static void pulse_sink_input_gone(int idx) @@ -139,7 +143,7 @@ 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); pulse_sink_input_remove(s); - pulse_schedule_update(); + schedule_update(); } struct pulse_sink { @@ -183,7 +187,7 @@ static void pulse_sink_cb(pa_context *ctx, const pa_sink_info *i, int eol, void s->volume = pa_cvolume_avg(&i->volume); s->base_volume = i->base_volume; s->mute = i->mute; - pulse_schedule_update(); + schedule_update(); } static void pulse_sink_gone(int idx) @@ -191,7 +195,18 @@ static void pulse_sink_gone(int idx) DBG("Pulse: REMOVE SINK #%d", idx); struct pulse_sink *s = pulse_sink_lookup(idx); pulse_sink_remove(s); - pulse_schedule_update(); + schedule_update(); +} + +static struct pulse_sink *pulse_sink_by_name(const char *name) +{ + HASH_FOR_ALL(pulse_sink, s) + { + if (!strcmp(s->name, name)) + return s; + } + HASH_END_FOR; + return NULL; } struct pulse_client { @@ -232,7 +247,7 @@ static void pulse_client_cb(pa_context *ctx, const pa_client_info *i, int eol, v struct pulse_client *c = pulse_client_lookup(i->index); SET_STRING(c->name, i->name); SET_STRING(c->host, host); - pulse_schedule_update(); + schedule_update(); } static void pulse_client_gone(int idx) @@ -240,7 +255,7 @@ static void pulse_client_gone(int idx) DBG("Pulse: REMOVE CLIENT #%d", idx); struct pulse_client *c = pulse_client_lookup(idx); pulse_client_remove(c); - pulse_schedule_update(); + schedule_update(); } static void pulse_subscribe_done_cb(pa_context *ctx, int success, void *userdata) @@ -302,6 +317,7 @@ static void pulse_state_cb(pa_context *ctx, void *userdata UNUSED) { PULSE_STATE(PS_OFFLINE); pulse_op_cancel_all(); + // FIXME: Reset all data structures } } } @@ -329,41 +345,152 @@ static void pulse_dump(void) HASH_END_FOR; } -static struct main_timer pulse_update_timer; +static void pulse_init(void) +{ + pmain_init(); + clist_init(&pulse_op_list); + pulse_client_init(); + pulse_sink_init(); + pulse_sink_input_init(); -static void pulse_update(struct main_timer *t) + 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); +} + +/*** High-level logic ***/ + +static struct main_timer update_timer; + +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 = pa_sw_volume_to_linear(s->volume); + vol = CLAMP(vol, 0, 1); + int val = 0x7f * vol; + val = CLAMP(val, 0, 0x7f); + noct_set_ring(ring, RING_MODE_LEFT, val); + noct_set_button(ring, 0); +} + +static void do_update(struct main_timer *t) { timer_del(t); + if (pulse_state != PS_ONLINE) + { + DBG("## UPDATE: Pulse is not online"); + return; + } + if (!noct_is_ready()) + { + DBG("## UPDATE: Nocturn is not ready"); + return; + } + DBG("## UPDATE"); pulse_dump(); + + update_ring_from_sink(0, "ursarium"); + update_ring_from_sink(1, "catarium"); } -static void pulse_schedule_update(void) +void schedule_update(void) { - timer_add_rel(&pulse_update_timer, 500); + timer_add_rel(&update_timer, 10); // FIXME } -static void pulse_init(void) +static void update_sink_from_rotary(int delta, const char *sink_name) { - pmain_init(); - clist_init(&pulse_op_list); - pulse_client_init(); - pulse_sink_init(); - pulse_sink_input_init(); - pulse_update_timer.handler = pulse_update; + struct pulse_sink *s = pulse_sink_by_name(sink_name); + if (!s) + return; + + double vol = pa_sw_volume_to_linear(s->volume); + vol += delta * 0.02; + vol = CLAMP(vol, 0, 1); + pa_cvolume cvol; + pa_cvolume_set(&cvol, 2, pa_sw_volume_from_linear(vol)); + + DBG("## Setting volume of sink %s to %d", s->name, cvol.values[0]); + PULSE_ASYNC_RUN(pa_context_set_sink_volume_by_index, pulse_ctx, s->idx, &cvol, pulse_success_cb); +} - 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 notify_rotary(int rotary, int delta) +{ + if (pulse_state != PS_ONLINE) + { + DBG("## NOTIFY: Pulse is not inline"); + 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; + } +} + +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_ASYNC_RUN(pa_context_set_sink_mute_by_index, pulse_ctx, s->idx, !s->mute, pulse_success_cb); +} + +void notify_button(int button, int on) +{ + if (pulse_state != PS_ONLINE) + { + DBG("## NOTIFY: Pulse is not inline"); + return; + } + + switch (button) + { + case 0: + update_sink_mute_from_button(on, "ursarium"); + break; + case 1: + update_sink_mute_from_button(on, "catarium"); + break; + } } int main(int argc UNUSED, char **argv) { log_init(argv[0]); main_init(); + update_timer.handler = do_update; - // msg(L_INFO, "Initializing Nocturn"); - // noct_init(); + noct_init(); msg(L_INFO, "Initializing PulseAudio"); pulse_init();