X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=ursaryd.c;h=e28cebfa4cd2060c8764c053e45c6e53be3d1a62;hb=9462e20b38064dc32852b1f60ad590d65e943f60;hp=dda13680f53329d721f890fbbb1329bf458fbc05;hpb=895d49619a0c85532af667ff9d6a284ea464eb7f;p=ursary.git diff --git a/ursaryd.c b/ursaryd.c index dda1368..e28cebf 100644 --- a/ursaryd.c +++ b/ursaryd.c @@ -1,10 +1,10 @@ /* * The Ursary Audio Controls * - * (c) 2014 Martin Mares + * (c) 2014--2020 Martin Mares */ -#undef LOCAL_DEBUG +#define LOCAL_DEBUG #include #include @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -21,24 +22,28 @@ #include #include "ursaryd.h" +#include "usb.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 sink Compress - dtto - * 3 - - - - * 4 MPD mute play/pause/stop - * 5 Albireo mute - - * 6 Ogion mute - - * 7 Ursula mute - + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * 0 sink PCH mute use headphones + * 1 - - - + * 2 - - - + * 3 desk brightness - desk lights on + * 4 MPD mute MPD play/pause + * 5 Albireo MPV mute MPD stop + * 6 Albireo other mute MPD prev + * 7 other machines mute MPD next * - * center all sinks - * slider - + * center - + * slider light color temperature */ +#define PCH_SINK "alsa_output.pci-0000_00_1f.3.analog-stereo" + /*** Sink controls ***/ static double volume_from_pa(pa_volume_t vol) @@ -73,6 +78,18 @@ static void update_ring_from_sink(int ring, const char *sink_name) noct_set_button(ring, 0); } +static void update_button_from_port(int button, const char *sink_name, const char *port_name) +{ + struct pulse_sink *s = pulse_sink_by_name(sink_name); + if (!s) + { + noct_set_button(button, 0); + return; + } + + noct_set_button(button, !strcmp(s->active_port, port_name)); +} + static void update_sink_from_rotary(int delta, const char *sink_name) { struct pulse_sink *s = pulse_sink_by_name(sink_name); @@ -103,47 +120,68 @@ static void update_sink_mute_from_button(int on, const char *sink_name) pulse_sink_set_mute(s->idx, !s->mute); } +static void update_port_from_button(int on, const char *sink_name, const char *port1, const char *port2) +{ + if (!on) + return; + + struct pulse_sink *s = pulse_sink_by_name(sink_name); + if (!s) + return; + + const char *port = port1; + if (!strcmp(s->active_port, port1)) + port = port2; + + DBG("## Setting port of sink %s to %s", s->name, port); + pulse_sink_set_port(s->idx, port); +} + /*** Client controls ***/ struct client_map { - int rotary; + int group; const char *client; const char *host; - double range; }; static struct client_map client_map[] = { - { 4, "Music Player Daemon", "albireo", 1.5 }, - { 5, NULL, "albireo", 1.5 }, - { 6, NULL, "ogion", 1.5 }, - { 7, NULL, "ursula", 1.5 }, + { 4, "Music Player Daemon", "albireo", }, + { 5, "mpv", "albireo", }, + { 6, NULL, "albireo", }, + { 7, NULL, NULL, }, }; -#define NUM_CLIENTS ARRAY_SIZE(client_map) +#define CLIENT_MAP_SIZE ARRAY_SIZE(client_map) -struct client_state { +struct group_config { + bool enabled; + double range; +}; + +struct group_state { double volume; bool have_muted[2]; }; -static struct client_state client_state[NUM_CLIENTS]; +#define NUM_GROUPS 9 -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 struct group_config group_config[NUM_GROUPS] = { + [4] = { .enabled = 1, .range = 1.5 }, + [5] = { .enabled = 1, .range = 1.5 }, + [6] = { .enabled = 1, .range = 1.5 }, + [7] = { .enabled = 1, .range = 1.5 }, +}; -static void calc_clients(void) +static struct group_state group_state[NUM_GROUPS]; + +static void calc_groups(void) { - bzero(client_state, sizeof(client_state)); + bzero(group_state, sizeof(group_state)); CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list) { - s->noct_client_idx = -1; + s->noct_group_idx = -1; if (s->client_idx < 0 || s->sink_idx < 0) continue; @@ -152,67 +190,74 @@ static void calc_clients(void) if (!c) continue; - for (uns i=0; i < NUM_CLIENTS; i++) + for (uns i=0; i < CLIENT_MAP_SIZE; 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; + int g = cm->group; + struct group_state *gs = &group_state[g]; + DBG("@@ Client #%d, sink input #%d -> group %d", s->client_idx, s->idx, g); + s->noct_group_idx = g; + gs->volume = MAX(gs->volume, s->volume); + gs->have_muted[!!s->mute] = 1; break; } } } } -static void update_clients(void) +static void update_groups(void) { - calc_clients(); + calc_groups(); - for (uns i=0; i < NUM_CLIENTS; i++) + for (uns i=0; i < NUM_GROUPS; i++) { - struct client_map *cm = &client_map[i]; - struct client_state *cs = &client_state[i]; - if (!cs->have_muted[0] && !cs->have_muted[1]) + struct group_config *gc = &group_config[i]; + struct group_state *gs = &group_state[i]; + if (!gc->enabled) + continue; + + DBG("@@ Group #%d: mute=%d/%d volume=%.3f/%.3f", i, gs->have_muted[0], gs->have_muted[1], volume_from_pa(gs->volume), gc->range); + + if (!gs->have_muted[0] && !gs->have_muted[1]) { - noct_set_ring(cm->rotary, RING_MODE_LEFT, 0); - noct_set_button(cm->rotary, 0); + noct_set_ring(i, RING_MODE_LEFT, 0); + noct_set_button(i, 0); } - else if (!cs->have_muted[0]) + else if (!gs->have_muted[0]) { - noct_set_ring(cm->rotary, RING_MODE_SINGLE_ON, 0x7f); - noct_set_button(cm->rotary, 1); + noct_set_ring(i, RING_MODE_SINGLE_ON, 0x7f); + noct_set_button(i, 1); } else { - double vol = CLAMP(volume_from_pa(cs->volume), 0, cm->range); - int val = 0x7f * vol / cm->range; + double vol = CLAMP(volume_from_pa(gs->volume), 0, gc->range); + int val = 0x7f * vol / gc->range; val = CLAMP(val, 12, 0x7f); - noct_set_ring(cm->rotary, RING_MODE_LEFT, val); - noct_set_button(cm->rotary, 0); + noct_set_ring(i, RING_MODE_LEFT, val); + noct_set_button(i, 0); } } } -static void update_client_from_rotary(int rotary, int delta) +static void update_group_from_rotary(int i, int delta) { - int i = find_client_by_rotary(rotary); - if (i < 0) + if (i >= NUM_GROUPS) + return; + struct group_config *gc = &group_config[i]; + struct group_state *gs = &group_state[i]; + if (!gc->enabled) 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)); + calc_groups(); + double vol = volume_from_pa(gs->volume) + delta*0.02; + pa_volume_t pavol = volume_to_pa(CLAMP(vol, 0, gc->range)); CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list) { - if (s->noct_client_idx == i && s->volume != pavol) + if (s->noct_group_idx == i && s->volume != pavol) { DBG("@@ Client #%d, sink input #%d: setting volume=%u", s->client_idx, s->idx, pavol); pa_cvolume cvol; @@ -222,24 +267,25 @@ static void update_client_from_rotary(int rotary, int delta) } } -static void update_client_from_button(int button, int on) +static void update_group_from_button(int i, int on) { - if (button >= 8 || !on) + if (!on) return; - - int i = find_client_by_rotary(button); - if (i < 0) + if (i >= NUM_GROUPS) + return; + struct group_config *gc = &group_config[i]; + struct group_state *gs = &group_state[i]; + if (!gc->enabled) return; - struct client_state *cs = &client_state[i]; - calc_clients(); - if (!cs->have_muted[0] && !cs->have_muted[1]) + calc_groups(); + if (!gs->have_muted[0] && !gs->have_muted[1]) return; - uns mute = !cs->have_muted[1]; + uns mute = !gs->have_muted[1]; CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list) { - if (s->noct_client_idx == i) + if (s->noct_group_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); @@ -247,12 +293,14 @@ static void update_client_from_button(int button, int on) } } +#if 0 // Not used at the moment + 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]) + for (uns i=0; i < NUM_GROUPS; i++) + if (group_config[i].enabled && noct_rotary_touched[i]) { if (touched >= 0) return -1; @@ -261,14 +309,18 @@ static int find_touched_client(void) return touched; } +#endif + /*** Default sink controls ***/ +#if 0 // Not mapped to any button at the moment + 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) + if (s->noct_group_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; @@ -360,7 +412,7 @@ static void update_default_sink_from_button(int button, int on) return; CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list) - if (s->noct_client_idx == i) + if (s->noct_group_idx == i) { DBG("Moving input #%d to sink #%d", s->idx, sk->idx); pulse_sink_input_move(s->idx, sk->idx); @@ -373,6 +425,8 @@ static void update_default_sink_from_button(int button, int on) } } +#endif + /*** MPD controls ***/ static bool mpd_flash_state; @@ -457,6 +511,91 @@ static void update_mpd_from_button(int button UNUSED, int on) } } +/*** Lights ***/ + +static bool lights_on[2]; +static double lights_brightness[2]; +static double lights_temperature[2]; + +static void update_lights(void) +{ + if (!dmx_is_ready()) + { + noct_set_ring(3, RING_MODE_SINGLE_ON, 0x7f); + noct_set_button(11, 0); + return; + } + + for (uint i=0; i<1; i++) + { + uint warm, cold; + if (lights_on[i]) + { + noct_set_ring(3, RING_MODE_LEFT, lights_brightness[i] * 127); + noct_set_button(11, 1); + double r = 2; + double x = (exp(r*lights_brightness[i]) - 1) / (exp(r) - 1); + double t = lights_temperature[i]; + double w = 2*x*(1-t); + double c = 2*x*t; + warm = CLAMP((int)(255*w), 0, 255); + cold = CLAMP((int)(255*c), 0, 255); + } + else + { + noct_set_ring(3, RING_MODE_LEFT, 0); + noct_set_button(11, 0); + warm = cold = 0; + } + DBG("Lights[%d]: on=%d bri=%.3f temp=%.3f -> warm=%d cold=%d", i, lights_on[i], lights_brightness[i], lights_temperature[i], warm, cold); + dmx_set_pwm(2*i, warm); + dmx_set_pwm(2*i+1, cold); + } +} + +static void update_lights_from_rotary(int ch, int delta) +{ + if (lights_on[ch]) + lights_brightness[ch] = CLAMP(lights_brightness[ch] + 0.015*delta*abs(delta), 0., 1.); + update_lights(); +} + +static void update_lights_from_slider(int value) +{ + lights_temperature[0] = value / 127.; + update_lights(); +} + +static void lights_button_timeout(struct main_timer *t) +{ + int ch = (uintptr_t) t->data; + DBG("Lights[%d]: Full throttle!", ch); + timer_del(t); + lights_on[ch] = 1; + lights_brightness[ch] = 1; + update_lights(); +} + +static void update_lights_from_button(int ch, int on) +{ + static struct main_timer lights_button_timer[2] = {{ + .handler = lights_button_timeout, + .data = (void *)(uintptr_t) 0, + },{ + .handler = lights_button_timeout, + .data = (void *)(uintptr_t) 1, + }}; + + if (on) + { + lights_on[ch] = !lights_on[ch]; + update_lights(); + timer_add_rel(&lights_button_timer[ch], 1000); + } + else + timer_del(&lights_button_timer[ch]); +} + /*** Main update routines ***/ static struct main_timer update_timer; @@ -555,11 +694,14 @@ static void do_update(struct main_timer *t) } // Everything normal - update_ring_from_sink(0, "ursarium"); - update_ring_from_sink(1, "catarium"); - update_clients(); + update_ring_from_sink(0, PCH_SINK); + update_button_from_port(8, PCH_SINK, "analog-output-headphones"); + update_groups(); +#if 0 update_default_sink(); +#endif update_mpd(); + update_lights(); } void schedule_update(void) @@ -593,17 +735,16 @@ void notify_rotary(int rotary, int delta) switch (rotary) { case 0: - update_sink_from_rotary(delta, "ursarium"); + update_sink_from_rotary(delta, PCH_SINK); break; - case 1: - update_sink_from_rotary(delta, "catarium"); + case 3: + update_lights_from_rotary(0, delta); break; - case 8: - update_sink_from_rotary(delta, "ursarium"); - update_sink_from_rotary(delta, "catarium"); + case 9: + update_lights_from_slider(delta); break; default: - update_client_from_rotary(rotary, delta); + update_group_from_rotary(rotary, delta); } } @@ -615,37 +756,56 @@ void notify_button(int button, int on) switch (button) { case 0: - update_sink_mute_from_button(on, "ursarium"); - break; - case 1: - update_sink_mute_from_button(on, "catarium"); + update_sink_mute_from_button(on, PCH_SINK); break; case 8: + update_port_from_button(on, PCH_SINK, "analog-output-lineout", "analog-output-headphones"); + break; +#if 0 case 9: case 10: update_default_sink_from_button(button, on); break; +#endif + case 11: + update_lights_from_button(0, on); + break; case 12: update_mpd_from_button(button, on); break; + case 13: + if (on) + mpd_stop(); + break; + case 14: + if (on) + mpd_prev(); + break; + case 15: + if (on) + mpd_next(); + break; default: - update_client_from_button(button, on); + update_group_from_button(button, on); } } -void notify_touch(int rotary, int on UNUSED) +void notify_touch(int rotary UNUSED, int on UNUSED) { if (!prepare_notify()) return; +#if 0 // Rotary touches switch meaning of LEDs, this is handled inside display updates if (rotary >= 4 && rotary < 8) schedule_update(); +#endif } /*** Main entry point ***/ static int debug; +static int no_fork; static struct opt_section options = { OPT_ITEMS { @@ -654,6 +814,7 @@ static struct opt_section options = { OPT_HELP("Options:"), OPT_HELP_OPTION, OPT_BOOL('d', "debug", debug, 0, "\tEnable debugging mode (no fork etc.)"), + OPT_BOOL(0, "no-fork", no_fork, 0, "\tDo not fork\n"), OPT_END } }; @@ -668,7 +829,9 @@ static void daemon_body(struct daemon_params *dp) main_init(); update_timer.handler = do_update; + usb_init(); noct_init(); + dmx_init(); pulse_init(); mpd_init(); @@ -691,7 +854,7 @@ int main(int argc UNUSED, char **argv) unsetenv("HOME"); struct daemon_params dp = { - .flags = (debug ? DAEMON_FLAG_SIMULATE : 0), + .flags = ((debug || no_fork) ? DAEMON_FLAG_SIMULATE : 0), .pid_file = "/run/ursaryd.pid", .run_as_user = "ursary", }; @@ -701,6 +864,7 @@ int main(int argc UNUSED, char **argv) if (!debug) { struct log_stream *ls = log_new_syslog("daemon", LOG_PID); + ls->levels = ~(1U << L_DEBUG); log_set_default_stream(ls); }