From nobody Wed Jun 17 10:52:51 2026 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4ggLKw3w5hz6h8th for ; Wed, 17 Jun 2026 10:52:52 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R13" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4ggLKw1RfGz3QRr for ; Wed, 17 Jun 2026 10:52:52 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781693572; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=3/Ciw8n3knFWRm+jdC9HyTdNsvlXP2QQL7sbfi+rQ+4=; b=Iw0TdBq/+rzktO9952Ae1y8SJAVTJSFoOnqqITD9GOWtbfkI87Qss8xpyWTkHwuQKrwd3s xoc1ZH7aWc5ZvUHG39Vhw9oylQZRV4IDObzbJHhuBNznM0vHgVR8tMxiBYOM0JIq+DC1fq DQC5Wldz5Rcg69PN+7CCXuOU1aGxXGxN0MkZXn6AJNgjJ86uO3Us/8gBj7Yc3ItirQjMtu AqTgdbVbcWb96CM8zjGzKo5vcyaKP+aHWkRWSkgBj/6z9hSv6kmLXwZUUQwWljJ3lWBh2Z 6MIHovqX3p7tDvUPB6gIVDQBbONaAA+EooByby/6WfHXzs+Fk35YSv7u+U7eHA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1781693572; a=rsa-sha256; cv=none; b=TYr5s64jhGMNcAclZuhWyziqGv52c/9BBa0IjgtwSgig9YYwWLGwQFMrACBJShmVDy0zsv 4nWyVUF464ohm6aKN6P+UXeCsMB3xmhrYse+ThtyZh0SA0j6bT+pIPVrlI8Qb0DRM/gi70 5A9RohsggAjerIHVtcsK6KQOMCXjnyNqplKBYpt+U0J6QA3QpZvNmIFiVwp9LWqvSgGMkl n7vaUCfHsAqlo9yfRkSPlDiYDWYYIsTRMgIEGxZGnDJ/eNid0j7DUf7IHsmp8g4IrFpgc/ pD1FOzvLCUshpFCEWbipSziaIDlzqQtETUixzv2pznwbwLiX6gVUfLW1Tc83qg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781693572; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=3/Ciw8n3knFWRm+jdC9HyTdNsvlXP2QQL7sbfi+rQ+4=; b=cIuB8mI7hAvcw9leLleS8T0WAimmznEn31xtJCM84V+mccDZzm/nVVFX1Qsrykf00ziGMt kpEdhQA+/1cJW+bBoAYbjpuC1TgFLOwGCR3hmOdDp0lJtjzRgQocbaGSNERkDpyu1LNAc2 fT2YAzDQgU8P0j1EKVdqzFva0VLAoCkNQR61nm7+a575rfPOHo4HslnOhh8/3rsbJKrqq2 ahl5MbSW3B8w+cx/sS6v3jV5KSQjACnGeYqdscLaOlwy3FzwsJbcqtC+EkdvfRuItK9/e1 AhuVOaM0khsb+EUUGUuHQ1jNKoUnxhhFzRkGjIobK6jkcRXKnc6H2l9WVk1WUg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4ggLKw010Rz17bj for ; Wed, 17 Jun 2026 10:52:52 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 1d17d by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Wed, 17 Jun 2026 10:52:51 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Cc: Goran=?utf-8?Q? Meki=C4=87?= From: Christos Margiolis Subject: git: a48bbef5eb32 - main - sound: Adjust mmap example to use kqueue List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: christos X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: a48bbef5eb32508a8d7b3b986c9b1d28176d1694 Auto-Submitted: auto-generated Date: Wed, 17 Jun 2026 10:52:51 +0000 Message-Id: <6a327c83.1d17d.446e4ec@gitrepo.freebsd.org> The branch main has been updated by christos: URL: https://cgit.FreeBSD.org/src/commit/?id=a48bbef5eb32508a8d7b3b986c9b1d28176d1694 commit a48bbef5eb32508a8d7b3b986c9b1d28176d1694 Author: Goran Mekić AuthorDate: 2026-06-17 10:36:12 +0000 Commit: Christos Margiolis CommitDate: 2026-06-17 10:50:44 +0000 sound: Adjust mmap example to use kqueue Reviewed by: christos Differential Revision: https://reviews.freebsd.org/D57410 --- share/examples/sound/mmap.c | 178 +++++++++++++------------------------------- 1 file changed, 50 insertions(+), 128 deletions(-) diff --git a/share/examples/sound/mmap.c b/share/examples/sound/mmap.c index 7f165d417020..3710483361ac 100644 --- a/share/examples/sound/mmap.c +++ b/share/examples/sound/mmap.c @@ -26,114 +26,18 @@ */ /* - * This program demonstrates low-latency audio pass-through using mmap. - * Opens input and output audio devices using memory-mapped I/O, - * synchronizes them in a sync group for simultaneous start, - * then continuously copies audio data from input to output. + * This program demonstrates low-latency audio pass-through using mmap + * and kqueue. It opens input and output audio devices using memory- + * mapped I/O, synchronizes them in a sync group for simultaneous start, + * then continuously copies audio data from input to output. Buffer + * positions are obtained from kqueue's ext[0] (replacing GETIPTR/ + * GETOPTR ioctls) and error counters from ext[1] (replacing GETERROR). */ -#include +#include #include "oss.h" -/* - * Get current time in nanoseconds using monotonic clock. - * Monotonic clock is not affected by system time changes. - */ -static int64_t -gettime_ns(void) -{ - struct timespec ts; - - if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) - err(1, "clock_gettime failed"); - return ((int64_t)ts.tv_sec * 1000000000LL + ts.tv_nsec); -} - -/* - * Sleep until the specified absolute time (in nanoseconds). - * Uses TIMER_ABSTIME for precise timing synchronization. - */ -static void -sleep_until_ns(int64_t target_ns) -{ - struct timespec ts; - - ts.tv_sec = target_ns / 1000000000LL; - ts.tv_nsec = target_ns % 1000000000LL; - if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL) != 0) - err(1, "clock_nanosleep failed"); -} - -/* - * Calculate the number of frames to process per iteration. - * Higher sample rates require larger steps to maintain efficiency. - */ -static unsigned -frame_stepping(unsigned sample_rate) -{ - return (16U * (1U + (sample_rate / 50000U))); -} - -/* - * Update the mmap pointer and calculate progress. - * Returns the absolute progress in bytes. - * - * fd: file descriptor for the audio device - * request: ioctl request (SNDCTL_DSP_GETIPTR or SNDCTL_DSP_GETOPTR) - * map_pointer: current pointer position in the ring buffer - * map_progress: absolute progress in bytes - * buffer_bytes: total size of the ring buffer - * frag_size: size of each fragment - * frame_size: size of one audio frame in bytes - */ -static int64_t -update_map_progress(int fd, unsigned long request, int *map_pointer, - int64_t *map_progress, int buffer_bytes, int frag_size, int frame_size) -{ - count_info info = {}; - unsigned delta, max_bytes, cycles; - int fragments; - - if (ioctl(fd, request, &info) < 0) - err(1, "Failed to get mmap pointer"); - if (info.ptr < 0 || info.ptr >= buffer_bytes) - errx(1, "Pointer out of bounds: %d", info.ptr); - if ((info.ptr % frame_size) != 0) - errx(1, "Pointer %d not aligned to frame size %d", info.ptr, - frame_size); - if (info.blocks < 0) - errx(1, "Invalid block count %d", info.blocks); - - /* - * Calculate delta: how many bytes have been processed since last check. - * Handle ring buffer wraparound using modulo arithmetic. - */ - delta = (info.ptr + buffer_bytes - *map_pointer) % buffer_bytes; - - /* - * Adjust delta based on reported blocks available. - * This accounts for cases where the pointer has wrapped multiple times. - */ - max_bytes = (info.blocks + 1) * frag_size - 1; - if (max_bytes >= delta) { - cycles = max_bytes - delta; - cycles -= cycles % buffer_bytes; - delta += cycles; - } - - /* Verify fragment count matches expected value */ - fragments = delta / frag_size; - if (info.blocks < fragments || info.blocks > fragments + 1) - warnx("Pointer block mismatch: ptr=%d blocks=%d delta=%u", - info.ptr, info.blocks, delta); - - /* Update pointer and progress tracking */ - *map_pointer = info.ptr; - *map_progress += delta; - return (*map_progress); -} - /* * Copy data between ring buffers, handling wraparound. * The copy starts at 'offset' and copies 'length' bytes. @@ -169,8 +73,6 @@ main(int argc, char *argv[]) int ch, bytes; int frag_size, frame_size, verbose = 0; int map_pointer = 0; - unsigned step_frames; - int64_t frame_ns, start_ns, next_wakeup_ns; int64_t read_progress = 0, write_progress = 0; oss_syncgroup sync_group = { 0, 0, { 0 } }; struct config config_in = { @@ -187,6 +89,8 @@ main(int argc, char *argv[]) .sample_rate = 48000, .mmap = 1, }; + struct kevent ev; + int kq; while ((ch = getopt(argc, argv, "v")) != -1) { switch (ch) { @@ -228,10 +132,6 @@ main(int argc, char *argv[]) errx(1, "Input and output configurations have different fragment sizes"); - /* Calculate timing parameters */ - step_frames = frame_stepping(config_in.sample_rate); - frame_ns = 1000000000LL / config_in.sample_rate; - /* Clear output buffer to prevent noise on startup */ memset(config_out.buf, 0, bytes); @@ -245,25 +145,51 @@ main(int argc, char *argv[]) if (ioctl(config_in.fd, SNDCTL_DSP_SYNCSTART, &sync_group.id) < 0) err(1, "Starting sync group failed"); - /* Initialize timing and progress tracking */ - start_ns = gettime_ns(); - read_progress = update_map_progress(config_in.fd, SNDCTL_DSP_GETIPTR, - &map_pointer, &read_progress, bytes, frag_size, frame_size); - write_progress = read_progress; - next_wakeup_ns = start_ns; + /* Create kqueue and register input device for read events */ + kq = kqueue(); + if (kq < 0) + err(1, "kqueue failed"); + EV_SET(&ev, config_in.fd, EVFILT_READ, EV_ADD, 0, 0, NULL); + if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0) + err(1, "kevent register failed"); /* * Main processing loop: - * 1. Sleep until next scheduled wakeup - * 2. Check how much new audio data is available - * 3. Copy available data from input to output buffer - * 4. Schedule next wakeup + * Block on kevent() until input data is available. + * ext[0] holds the current DMA pointer (GETIPTR/GETOPTR equivalent). + * ext[1] holds the xrun count for the channel (GETERROR equivalent). */ for (;;) { - sleep_until_ns(next_wakeup_ns); - read_progress = update_map_progress(config_in.fd, - SNDCTL_DSP_GETIPTR, &map_pointer, &read_progress, bytes, - frag_size, frame_size); + int n; + int ptr; + unsigned delta; + + n = kevent(kq, NULL, 0, &ev, 1, NULL); + if (n < 0) + err(1, "kevent failed"); + if (n == 0) + continue; + + ptr = (int)ev.ext[0]; + if (ptr < 0 || ptr >= bytes) + errx(1, "Pointer out of bounds: %d", ptr); + if ((ptr % frame_size) != 0) + errx(1, "Pointer %d not aligned to frame size %d", ptr, + frame_size); + + /* + * Calculate delta: how many bytes have been processed since + * last check. Handle ring buffer wraparound. + */ + delta = (ptr + bytes - map_pointer) % bytes; + + /* Update pointer and progress tracking */ + map_pointer = ptr; + read_progress += delta; + + /* Report xruns if any */ + if (ev.ext[1] != 0 && verbose) + warnx("xruns: %llu", (unsigned long long)ev.ext[1]); /* Copy new audio data if available */ if (read_progress > write_progress) { @@ -277,13 +203,9 @@ main(int argc, char *argv[]) printf("copied %d bytes at %d (abs %lld)\n", length, offset, (long long)write_progress); } - - /* Schedule next wakeup based on frame timing */ - next_wakeup_ns += (int64_t)step_frames * frame_ns; - if (next_wakeup_ns < gettime_ns()) - next_wakeup_ns = gettime_ns(); } + close(kq); if (munmap(config_in.buf, bytes) != 0) err(1, "Memory unmap failed"); config_in.buf = NULL;