]> mj.ucw.cz Git - misc.git/commitdiff
Ursary: First light...
authorMartin Mares <mj@ucw.cz>
Sun, 9 Nov 2014 14:09:53 +0000 (15:09 +0100)
committerMartin Mares <mj@ucw.cz>
Sun, 9 Nov 2014 14:09:53 +0000 (15:09 +0100)
ursaryd/nocturn.c
ursaryd/ursaryd.h
ursaryd/ut.c

index 8ed7b1835eb51ae2b96180a8482915f48892adfa..50d0557c9edff212fb797845822e66b052738647 100644 (file)
@@ -7,7 +7,7 @@
  *     see https://github.com/dewert/nocturn-linux-midi for inspiration.
  */
 
-#define LOCAL_DEBUG
+#undef LOCAL_DEBUG
 
 #include <ucw/lib.h>
 #include <ucw/bitops.h>
@@ -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);
 }
index 39aae3df19afa43944ded610ab02ba76d31b04c7..8342e83eb56ae8a37b510d7c58ed33c319467560 100644 (file)
@@ -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 */
 
index 5e3fab44ceeadfef11568ae60fd3abf5b0cbb273..0468955ed61ba778ed923c44659be3225c406ada 100644 (file)
 
 #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();