#include <string.h>
#include <stdlib.h>
-#include <pulse/pulseaudio.h>
-
#include "ursaryd.h"
-/*** High-level logic ***/
+/*
+ * 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 -
+ */
-static struct main_timer update_timer;
+/*** Sink controls ***/
static double volume_from_pa(pa_volume_t vol)
{
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;
}
}
+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);
}
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)
}
}
-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);
-}
-
-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);
- }
- }
-}
-
void notify_button(int button, int on)
{
if (pulse_state != PS_ONLINE)
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]);
update_timer.handler = do_update;
noct_init();
-
- msg(L_INFO, "Initializing PulseAudio");
pulse_init();
+ mpd_init();
- msg(L_INFO, "Entering main loop");
+ msg(L_DEBUG, "Entering main loop");
main_loop();
return 0;