@@ 1,5 1,6 @@
#define _POSIX_C_SOURCE 200809L
-#include <alsa/asoundlib.h>
+#include <assert.h>
+#include <sys/poll.h>
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <GL/glut.h>
@@ 20,10 21,27 @@
#include <dev/evdev/input-event-codes.h>
#endif
+#include <spa/param/audio/layout.h>
+#include <spa/param/audio/format-utils.h>
+#include <spa/param/props.h>
+#include <spa/utils/result.h>
+
+#include <pipewire/pipewire.h>
+#include <pipewire/global.h>
+
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
+#define PEAK_DURATION 20
+#define HYSTERESIS 0.0005
+
+enum event_loop_fd {
+ EVENT_LOOP_WAYLAND,
+ EVENT_LOOP_PIPEWIRE,
+};
+
struct context {
+ struct wl_display *display;
struct wl_compositor *compositor;
struct wl_surface *surface;
struct xdg_wm_base *xdg_wm_base;
@@ 33,22 51,35 @@ struct context {
struct zwlr_layer_surface_v1 *layer_surface;
struct wl_seat *seat;
+ struct spa_hook stream_listener;
+ struct pw_context *pw_context;
+ struct pw_core *pw_core;
+ struct pw_loop *pw_loop;
+ struct pw_stream *pw_stream;
+
int width;
int height;
bool dirty;
bool pending;
+ bool running;
+
+ int channels;
+ int rate;
+ int target;
+ int latency;
double val;
double peak;
+ double last_val;
+ double last_peak;
+ double peak_decay;
struct wl_egl_window *wl_egl_window;
EGLConfig egl_config;
EGLContext egl_context;
EGLDisplay egl_display;
EGLSurface egl_surface;
-
- bool running;
};
static void layer_surface_configure(void *data,
@@ 251,6 282,9 @@ static int draw(struct context *ctx) {
glEnd();
ctx->pending = true;
+ if (ctx->peak_decay != 0) {
+ ctx->peak_decay--;
+ }
struct wl_callback *callback = wl_surface_frame(ctx->surface);
wl_callback_add_listener(callback, &frame_listener, ctx);
@@ 262,36 296,13 @@ static int draw(struct context *ctx) {
return 0;
}
-#define PEAK_DURATION 20
-#define HYSTERESIS 0.0005
-
-static int max_s16(void *buffer, int frames, int channels) {
- int max = 0;
- for (int i = 0; i < frames*channels; i++) {
- int b = abs(((short*)buffer)[i]);
- if (b > max)
- max = b;
- }
- return max;
-}
-
-static int max_s32(void *buffer, int frames, int channels) {
- int max = 0;
- for (int i = 0; i < frames*channels; i++) {
- int b = abs(((int*)buffer)[i]);
- if (b > max)
- max = b;
- }
- return max;
-}
-
static int display_dispatch(struct pollfd *pfd, int polln, struct wl_display *display, int timeout) {
assert(polln > 0);
if (wl_display_prepare_read(display) == -1) {
return wl_display_dispatch_pending(display);
}
- pfd[0].events = POLLOUT;
+ pfd[EVENT_LOOP_WAYLAND].events = POLLOUT;
while (wl_display_flush(display) == -1) {
if (errno != EAGAIN && errno != EPIPE) {
wl_display_cancel_read(display);
@@ 307,7 318,7 @@ static int display_dispatch(struct pollfd *pfd, int polln, struct wl_display *di
}
}
- pfd[0].events = POLLIN;
+ pfd[EVENT_LOOP_WAYLAND].events = POLLIN;
while (poll(pfd, polln, timeout) == -1) {
if (errno != EINTR) {
wl_display_cancel_read(display);
@@ 327,177 338,163 @@ static int display_dispatch(struct pollfd *pfd, int polln, struct wl_display *di
return wl_display_dispatch_pending(display);
}
-static snd_pcm_t *open_pcm(char *device, void **buffer, int *frame_capacity, unsigned int *channels, int *bits) {
- int ret;
- snd_pcm_t *handle_capture;
- if ((ret = snd_pcm_open(&handle_capture, device, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) != 0) {
- fprintf(stderr, "Could not open capture device '%s': %s\n", device, snd_strerror(ret));
- return NULL;
+static bool equal_within(double a, double b, double hysteresis) {
+ return a > b - hysteresis && a < b + hysteresis;
+}
+
+static double max_f32(void *buffer, int frames) {
+ float max = 0;
+ for (int idx = 0; idx < frames; idx++) {
+ float val = fabsf(((float*)buffer)[idx]);
+ if (val > max) {
+ max = val;
+ }
+ }
+
+ return max;
+}
+
+static void on_process(void *userdata) {
+ struct context *ctx = userdata;
+
+ struct pw_buffer *b = pw_stream_dequeue_buffer(ctx->pw_stream);
+ if (b == NULL) {
+ fprintf(stderr, "Out of buffers: %s\n", strerror(errno));
+ return;
}
- snd_pcm_hw_params_t *hw_params;
- if ((ret = snd_pcm_hw_params_malloc(&hw_params)) != 0) {
- fprintf(stderr, "Could not allocate hardware parameter structure (%s)\n",
- snd_strerror(ret));
- return NULL;
+ struct spa_buffer *buf = b->buffer;
+ struct spa_data *d = &buf->datas[0];
+ uint8_t *p = d->data;
+ if (p == NULL)
+ return;
+
+ int offset = SPA_MIN(d->chunk->offset, d->maxsize);
+ int size = SPA_MIN(d->chunk->size, d->maxsize - offset);
+ p += offset;
+
+ ctx->val = max_f32(p, size / (4 * ctx->channels));
+ if (ctx->peak_decay == 0 || ctx->val > ctx->peak) {
+ ctx->peak = ctx->val;
+ ctx->peak_decay = PEAK_DURATION;
}
-
- if ((ret = snd_pcm_hw_params_any(handle_capture, hw_params)) != 0) {
- fprintf(stderr, "Could not initialize hardware parameter structure (%s)\n",
- snd_strerror(ret));
- return NULL;
+
+ pw_stream_queue_buffer(ctx->pw_stream, b);
+
+ if (equal_within(ctx->last_val, ctx->val, HYSTERESIS) &&
+ equal_within(ctx->last_peak, ctx->peak, HYSTERESIS)) {
+ return;
}
- snd_pcm_hw_params_get_channels(hw_params, channels);
-
- unsigned int rate = 44100;
- snd_pcm_hw_params_get_rate(hw_params, &rate, 0);
-
- snd_pcm_format_t format;
- snd_pcm_hw_params_get_format(hw_params, &format);
-
- switch (format) {
- case SND_PCM_FORMAT_S32_LE:
- *bits = 32;
- break;
- case SND_PCM_FORMAT_S16_LE:
- *bits = 16;
- break;
- default:
- fprintf(stderr, "Unknown format for input device: %d\n", format);
- return NULL;
- }
-
- snd_pcm_hw_params_set_access(handle_capture, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
- if ((ret = snd_pcm_hw_params(handle_capture, hw_params)) != 0) {
- fprintf(stderr, "Could not set hardware parameters (%s)\n",
- snd_strerror(ret));
- return NULL;
- }
-
- int bufsize = rate * (*bits / 8) / PEAK_DURATION;
- *buffer = calloc(*channels, bufsize);
- if (*buffer == NULL) {
- fprintf(stderr, "Could not allocate buffer: %s\n", strerror(errno));
- return NULL;
- }
- *frame_capacity = bufsize / 4;
- return handle_capture;
+ ctx->last_val = ctx->val;
+ ctx->last_peak = ctx->peak;
+ ctx->dirty = true;
+ draw(ctx);
}
-static const char usage[] = ""
-"usage: %s [options] device\n"
-" -h show this help message\n"
-" -l use layer shell\n"
-" -w <width> bar width when using layer shell\n"
-" -a <side> side to anchor to (left, right, top, bottom)\n"
-" device ALSA device (e.g. 'dsnoop:0,0')\n";
+static const struct pw_stream_events stream_events = {
+ PW_VERSION_STREAM_EVENTS,
+ .process = on_process,
+};
-int main(int argc, char **argv) {
- bool use_layer_shell = false;
- int bar_width = 8;
- int anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
- bool vertical = true;
+static int setup_pipewire(struct context *ctx) {
+ pw_init(NULL, NULL);
+ ctx->pw_loop = pw_loop_new(NULL);
+ if (ctx->pw_loop == NULL) {
+ fprintf(stderr, "Could not create pipewire event loop\n");
+ return -1;
+ }
- int opt;
- while ((opt = getopt(argc, argv, "hlw:a:")) != -1) {
- switch (opt) {
- case 'l':
- use_layer_shell = true;
- break;
- case 'w':
- bar_width = strtol(optarg, NULL, 10);
- break;
- case 'a':
- if (strcmp(optarg, "left") == 0) {
- anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
- vertical = true;
- } else if (strcmp(optarg, "right") == 0) {
- anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
- vertical = true;
- } else if (strcmp(optarg, "top") == 0) {
- anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
- vertical = false;
- } else if (strcmp(optarg, "bottom") == 0) {
- anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
- vertical = false;
- } else {
- fprintf(stderr, "unknown side: %s\n", optarg);
- return EXIT_FAILURE;
- }
- break;
- default:
- fprintf(stderr, usage, argv[0]);
- return opt == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
- }
+ ctx->pw_context = pw_context_new(ctx->pw_loop, NULL, 0);
+ if (ctx->pw_context == NULL) {
+ fprintf(stderr, "Could not create pipewire context\n");
+ return -1;
}
- if (optind >= argc) {
- fprintf(stderr, "Expected an ALSA device name after arguments\n");
- return EXIT_FAILURE;
+ ctx->pw_core = pw_context_connect(ctx->pw_context, NULL, 0);
+ if (ctx->pw_core == NULL) {
+ fprintf(stderr, "Could not connect to pipewire context\n");
+ return -1;
}
- char *device = argv[optind];
- struct context ctx = {
- .height = bar_width,
- .width = 1024,
- .running = true,
- .val = 0.0,
- .peak = 0.0,
- .dirty = true,
- };
+ struct pw_properties *core_props = pw_core_get_properties(ctx->pw_core);
- void *buffer = NULL;
- int frame_capacity = 0, bits = 0;
- unsigned int channels = 0;
- snd_pcm_t *handle_capture = open_pcm(device, &buffer, &frame_capacity, &channels, &bits);
- if (handle_capture == NULL) {
- return EXIT_FAILURE;
+ int rate = 48000;
+ const char *default_rate = pw_properties_get(core_props, "default.clock.rate");
+ if (default_rate != NULL) {
+ rate = strtol(default_rate, NULL, 10);
+ }
+
+ struct pw_properties *props = pw_properties_new(
+ PW_KEY_MEDIA_TYPE, "Audio",
+ PW_KEY_MEDIA_CATEGORY, "Capture",
+ PW_KEY_MEDIA_ROLE, "Monitor",
+ PW_KEY_STREAM_MONITOR, "true",
+ PW_KEY_STREAM_DONT_REMIX, "true",
+ NULL);
+
+ pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", rate / ctx->latency, rate);
+
+ ctx->pw_stream = pw_stream_new(ctx->pw_core, "wlavu", props);
+
+ pw_stream_add_listener(ctx->pw_stream, &ctx->stream_listener, &stream_events, ctx);
+
+ void *sound_buffer[8192];
+ struct spa_pod_builder builder = SPA_POD_BUILDER_INIT(sound_buffer, sizeof(sound_buffer));
+
+ const struct spa_pod *params[1];
+ params[0] = spa_format_audio_raw_build(&builder, SPA_PARAM_EnumFormat,
+ &SPA_AUDIO_INFO_RAW_INIT(
+ .format = SPA_AUDIO_FORMAT_F32,
+ .channels = ctx->channels,
+ .rate = rate,
+ ));
+
+ int ret = pw_stream_connect(ctx->pw_stream,
+ PW_DIRECTION_INPUT,
+ ctx->target,
+ PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS,
+ params, 1);
+ if (ret < 0) {
+ fprintf(stderr, "Could not connect to stream: %s\n", spa_strerror(ret));
+ return -1;
}
+ return 0;
+}
- struct wl_display *display = wl_display_connect(NULL);
- if (display == NULL) {
+static int setup_wayland(struct context *ctx, bool layer_shell, bool vertical, int anchor) {
+ ctx->display = wl_display_connect(NULL);
+ if (ctx->display == NULL) {
fprintf(stderr, "failed to create display\n");
- return EXIT_FAILURE;
+ return -1;
}
- struct wl_registry *registry = wl_display_get_registry(display);
- wl_registry_add_listener(registry, ®istry_listener, &ctx);
- wl_display_dispatch(display);
- wl_display_roundtrip(display);
+ struct wl_registry *registry = wl_display_get_registry(ctx->display);
+ wl_registry_add_listener(registry, ®istry_listener, ctx);
+ wl_display_dispatch(ctx->display);
+ wl_display_roundtrip(ctx->display);
- if (ctx.compositor == NULL || ctx.xdg_wm_base == NULL) {
+ if (ctx->compositor == NULL) {
fprintf(stderr, "no wl_compositor support\n");
- return EXIT_FAILURE;
+ return -1;
}
- ctx.egl_display = eglGetDisplay((EGLNativeDisplayType)display);
- if (ctx.egl_display == EGL_NO_DISPLAY) {
+ ctx->egl_display = eglGetDisplay((EGLNativeDisplayType)ctx->display);
+ if (ctx->egl_display == EGL_NO_DISPLAY) {
fprintf(stderr, "failed to create EGL display\n");
- return EXIT_FAILURE;
+ return -1;
}
eglBindAPI(EGL_OPENGL_API);
- glutInit(&argc, argv);
EGLint major, minor;
- if (!eglInitialize(ctx.egl_display, &major, &minor)) {
+ if (!eglInitialize(ctx->egl_display, &major, &minor)) {
fprintf(stderr, "failed to initialize EGL\n");
- return EXIT_FAILURE;
+ return -1;
}
EGLint count;
- eglGetConfigs(ctx.egl_display, NULL, 0, &count);
+ eglGetConfigs(ctx->egl_display, NULL, 0, &count);
EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
@@ 509,121 506,179 @@ int main(int argc, char **argv) {
};
EGLint n = 0;
EGLConfig *configs = calloc(count, sizeof(EGLConfig));
- eglChooseConfig(ctx.egl_display, config_attribs, configs, count, &n);
+ eglChooseConfig(ctx->egl_display, config_attribs, configs, count, &n);
if (n == 0) {
fprintf(stderr, "failed to choose an EGL config\n");
- return EXIT_FAILURE;
+ return -1;
}
- ctx.egl_config = configs[0];
+ ctx->egl_config = configs[0];
EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE,
};
- ctx.egl_context = eglCreateContext(ctx.egl_display, ctx.egl_config,
+ ctx->egl_context = eglCreateContext(ctx->egl_display, ctx->egl_config,
EGL_NO_CONTEXT, context_attribs);
- ctx.surface = wl_compositor_create_surface(ctx.compositor);
+ ctx->surface = wl_compositor_create_surface(ctx->compositor);
- if (!use_layer_shell) {
- if (ctx.xdg_wm_base == NULL) {
+ if (!layer_shell) {
+ if (ctx->xdg_wm_base == NULL) {
fprintf(stderr, "no xdg_wm_base support\n");
- return EXIT_FAILURE;
+ return -1;
}
- ctx.xdg_surface = xdg_wm_base_get_xdg_surface(ctx.xdg_wm_base, ctx.surface);
- ctx.xdg_toplevel = xdg_surface_get_toplevel(ctx.xdg_surface);
+ ctx->xdg_surface = xdg_wm_base_get_xdg_surface(ctx->xdg_wm_base, ctx->surface);
+ ctx->xdg_toplevel = xdg_surface_get_toplevel(ctx->xdg_surface);
- xdg_surface_add_listener(ctx.xdg_surface, &xdg_surface_listener, &ctx);
- xdg_toplevel_add_listener(ctx.xdg_toplevel, &xdg_toplevel_listener, &ctx);
+ xdg_surface_add_listener(ctx->xdg_surface, &xdg_surface_listener, ctx);
+ xdg_toplevel_add_listener(ctx->xdg_toplevel, &xdg_toplevel_listener, ctx);
} else {
- if (ctx.layer_shell == NULL) {
+ if (ctx->layer_shell == NULL) {
fprintf(stderr, "no layer_shell support\n");
- return EXIT_FAILURE;
+ return -1;
}
- ctx.layer_surface = zwlr_layer_shell_v1_get_layer_surface(ctx.layer_shell, ctx.surface,
+ ctx->layer_surface = zwlr_layer_shell_v1_get_layer_surface(ctx->layer_shell, ctx->surface,
NULL, ZWLR_LAYER_SHELL_V1_LAYER_TOP, "wlavu");
if (vertical) {
- zwlr_layer_surface_v1_set_size(ctx.layer_surface, bar_width, 0);
+ zwlr_layer_surface_v1_set_size(ctx->layer_surface, ctx->width, 0);
} else {
- zwlr_layer_surface_v1_set_size(ctx.layer_surface, 0, bar_width);
+ zwlr_layer_surface_v1_set_size(ctx->layer_surface, 0, ctx->width);
}
- zwlr_layer_surface_v1_set_anchor(ctx.layer_surface, anchor);
- zwlr_layer_surface_v1_set_exclusive_zone(ctx.layer_surface, bar_width);
- zwlr_layer_surface_v1_set_layer(ctx.layer_surface, ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM);
- zwlr_layer_surface_v1_add_listener(ctx.layer_surface, &layer_surface_listener, &ctx);
+ zwlr_layer_surface_v1_set_anchor(ctx->layer_surface, anchor);
+ zwlr_layer_surface_v1_set_exclusive_zone(ctx->layer_surface, ctx->width);
+ zwlr_layer_surface_v1_set_layer(ctx->layer_surface, ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM);
+ zwlr_layer_surface_v1_add_listener(ctx->layer_surface, &layer_surface_listener, ctx);
}
- wl_surface_commit(ctx.surface);
- wl_display_roundtrip(display);
+ wl_surface_commit(ctx->surface);
+ wl_display_roundtrip(ctx->display);
- ctx.wl_egl_window = wl_egl_window_create(ctx.surface, ctx.width, ctx.height);
- ctx.egl_surface = eglCreateWindowSurface(ctx.egl_display, ctx.egl_config,
- (EGLNativeWindowType)ctx.wl_egl_window, NULL);
+ ctx->wl_egl_window = wl_egl_window_create(ctx->surface, ctx->width, ctx->height);
+ ctx->egl_surface = eglCreateWindowSurface(ctx->egl_display, ctx->egl_config,
+ (EGLNativeWindowType)ctx->wl_egl_window, NULL);
+
+ if (!eglMakeCurrent(ctx->egl_display, ctx->egl_surface, ctx->egl_surface, ctx->egl_context)) {
+ fprintf(stderr, "Could not make EGL context current\n");
+ return -1;
+ }
+
+ eglSwapInterval(ctx->egl_display, 0);
+
+ return 0;
+}
+
+static const char usage[] = ""
+"usage: %s [options]\n"
+" -h show this help message\n"
+" -l use layer shell\n"
+" -w <width> bar width when using layer shell\n"
+" -a <side> side to anchor to (left, right, top, bottom)\n"
+" -c <channels> channels (default: 2)\n"
+" -t <target> target device (default: any)\n"
+" -L <latency> latency in fractions of a second (defualt: 20)\n";
+
+int main(int argc, char **argv) {
+ bool use_layer_shell = false;
+ int anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
+ bool vertical = true;
+
+ struct context ctx = {
+ .height = 8,
+ .width = 1024,
+ .running = true,
+ .val = 0.0,
+ .peak = 0.0,
+ .dirty = true,
+ .channels = 2,
+ .target = PW_ID_ANY,
+ .latency = 20,
+ };
+
+ int opt;
+ while ((opt = getopt(argc, argv, "hlw:a:r:c:t:L:")) != -1) {
+ switch (opt) {
+ case 'l':
+ use_layer_shell = true;
+ break;
+ case 'w':
+ ctx.width = strtol(optarg, NULL, 10);
+ break;
+ case 'a':
+ if (strcmp(optarg, "left") == 0) {
+ anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
+ vertical = true;
+ } else if (strcmp(optarg, "right") == 0) {
+ anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
+ vertical = true;
+ } else if (strcmp(optarg, "top") == 0) {
+ anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
+ vertical = false;
+ } else if (strcmp(optarg, "bottom") == 0) {
+ anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
+ vertical = false;
+ } else {
+ fprintf(stderr, "unknown side: %s\n", optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'c':
+ ctx.channels = strtol(optarg, NULL, 10);
+ break;
+ case 't':
+ ctx.target = strtol(optarg, NULL, 10);
+ break;
+ case 'L':
+ ctx.latency = strtol(optarg, NULL, 10);
+ break;
+ default:
+ fprintf(stderr, usage, argv[0]);
+ return opt == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
+ }
+ }
- if (!eglMakeCurrent(ctx.egl_display, ctx.egl_surface, ctx.egl_surface, ctx.egl_context)) {
- fprintf(stderr, "eglMakeCurrent failed\n");
+ if (setup_pipewire(&ctx) == -1) {
return EXIT_FAILURE;
}
- eglSwapInterval(ctx.egl_display, 0);
+ glutInit(&argc, argv);
- int polln = snd_pcm_poll_descriptors_count(handle_capture) + 1;
- struct pollfd *pfd = calloc(polln, sizeof(struct pollfd));
- if (pfd == NULL) {
- fprintf(stderr, "Could not allocate poll descriptors\n");
+ if (setup_wayland(&ctx, use_layer_shell, vertical, anchor) == -1) {
return EXIT_FAILURE;
}
- pfd[0].fd = wl_display_get_fd(display);
- polln = snd_pcm_poll_descriptors(handle_capture, &pfd[1], polln-1) + 1;
- // For some reason we must read at least once
- snd_pcm_readi(handle_capture, buffer, frame_capacity);
+
+ struct pollfd pollfds[] = {
+ [EVENT_LOOP_WAYLAND] = {
+ .fd = wl_display_get_fd(ctx.display),
+ .events = POLLIN,
+ },
+ [EVENT_LOOP_PIPEWIRE] = {
+ .fd = pw_loop_get_fd(ctx.pw_loop),
+ .events = POLLIN,
+ },
+ };
draw(&ctx);
-
- double peak;
- int peak_timer = 0;
- double divisor = INT_MAX;
- int (*max)(void *buffer, int frames, int channels) = bits == 32 ? max_s32 : max_s16;
-
- int ret;
- while (display_dispatch(pfd, polln, display, -1) != -1 && ctx.running) {
- unsigned short revent = 0;
- if ((ret = snd_pcm_poll_descriptors_revents(handle_capture, &pfd[1], polln-1, &revent)) != 0) {
- fprintf(stderr, "Could not convert alsa revents (%s)\n",
- snd_strerror(ret));
- return EXIT_FAILURE;
- }
- if ((revent & POLLIN) == 0) {
- continue;
- }
- snd_pcm_sframes_t frames = snd_pcm_readi(handle_capture, buffer, frame_capacity);
- if (frames < 0) {
- if ((ret = snd_pcm_recover(handle_capture, frames, 0)) != 0) {
- fprintf(stderr, "Could not recover from read error: %s\n", snd_strerror(ret));
+ while (display_dispatch(pollfds, 2, ctx.display, -1) != -1 && ctx.running) {
+ if (pollfds[EVENT_LOOP_PIPEWIRE].revents & POLLIN) {
+ int ret = pw_loop_iterate(ctx.pw_loop, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Could not iterate pipewire loop: %s", spa_strerror(ret));
return EXIT_FAILURE;
}
- continue;
- }
-
- double val = ((double)max(buffer, frames, channels)) / divisor;
- if (val >= peak || peak_timer == 0) {
- peak = val;
- peak_timer = PEAK_DURATION;
- } else if (peak_timer > 0) {
- peak_timer--;
- }
-
- if (val < ctx.val - HYSTERESIS || val > ctx.val + HYSTERESIS ||
- peak < ctx.peak - HYSTERESIS || peak > ctx.peak + HYSTERESIS) {
- ctx.dirty = true;
- ctx.val = val;
- ctx.peak = peak;
- draw(&ctx);
}
}
- snd_pcm_close(handle_capture);
wl_surface_destroy(ctx.surface);
return EXIT_SUCCESS;