From nobody Tue Jun 30 06:24:34 2026 X-Original-To: dev-commits-src-branches@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 4gqCmL4pDRz6jcWC for ; Tue, 30 Jun 2026 06:24:34 +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 "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4gqCmL2CR7z3FYL for ; Tue, 30 Jun 2026 06:24:34 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1782800674; 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=MCsnQfvwfZbUWQWKXRWJqlxsnvJwRvuxBkOGIfRp/mI=; b=kZjGp0rBQxacTq+Qim7sVUjtqUQ+QvkaMJ9P41Uy75I/7ekX8AOpVvfDqc7AZalyP//UFZ j+o9ApZE0YGtzdeIgErTlyanoixWvOHXmxfUtIA6A6XA/q0Ou7s82Omd0GXVm2O5DcwEKA arTazNGafKvk81k2+TUKZYbWbTPzO7dsHfDUE/Avbd+Rm6QSEhQxYw/9xSsqqX2j19V0hN PnHdgMKNHvcAuw5zdkpT+Oz8DezbzfrWDq0ZW/XknHNV/95i2i+h8lA7Pvpd5GNoAXyAQt vhNNSkXAdAiGvu8JMNYLi9uPumzotPFTuwyPl/71Yb4mZMFNuxMhrSK/EtiijQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1782800674; a=rsa-sha256; cv=none; b=Z1sp98f1fPEj+ohuY/hgtdK/hFTj9TvtRHjihsOdhlBI/BpV2Ye6U/dyX2/NrM7iuuYlz8 iY5tLpw2fxw0DIhcOw+g4X4QBHo1JcvmfPQCe0WTtHdkjaQxjiHL7Ui7wN9/0mHuAkE4/t 5D7Sam/XLSkpa/ZH1Gy/xSD+juGVA3hmNEL/0guIOZ2XhvJNXv3J3S7tGe/wMMpmX5Q8JI L7mhWUVeFpvTM02dDx/S0780pjTrPCL0t6EMdenYdjuBwrRNugrZ5un2hoJ4P7vhoQruco r36UbVXvqXPPgMUlyjB43S0MGhHBDCjBHyrr3sDil1mR5eF9Hh5+PeXLqcpsMw== 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=1782800674; 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=MCsnQfvwfZbUWQWKXRWJqlxsnvJwRvuxBkOGIfRp/mI=; b=U+a7J6F3WkoFQf61in7BeBDvjiKQnhvA1I5O34Ejj1cPl886juAD429wwzOkPC/fejNGwi 802KP6ARzDKTGWJh6+yoputJjkOWEpkV8PJiuREbFTN0NTm4verIEvZeNLvLK55xC1oX+v G3t65aAgmXNtCTGGiS4DQwG5ON0izyalXG5wSGflN1Wco5+gEndeOT+memk286SEFWHo3d m7yoyujSBr6L/lnjXHRC03Sk1MPQGIVBWgRBD4ep6JxqHYx8OzS80Fcof6TaoY9wti4OEE LrN0eoc63RJMThesoKTIz/2OKd7uH/QMoqPs8R5VShBGFV1RI/ACszdgwCaoEw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gqCmL121CzXp0 for ; Tue, 30 Jun 2026 06:24:34 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 1e362 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 30 Jun 2026 06:24:34 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Cc: Goran=?utf-8?Q? Meki=C4=87?= From: Christos Margiolis Subject: git: a751490f5bf7 - stable/15 - sound: Adjust mmap example to use kqueue List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@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/stable/15 X-Git-Reftype: branch X-Git-Commit: a751490f5bf7186e4dc7092c3ffb9483ae853e43 Auto-Submitted: auto-generated Date: Tue, 30 Jun 2026 06:24:34 +0000 Message-Id: <6a436122.1e362.3315b82@gitrepo.freebsd.org> The branch stable/15 has been updated by christos: URL: https://cgit.FreeBSD.org/src/commit/?id=a751490f5bf7186e4dc7092c3ffb9483ae853e43 commit a751490f5bf7186e4dc7092c3ffb9483ae853e43 Author: Goran Mekić AuthorDate: 2026-06-17 10:36:12 +0000 Commit: Christos Margiolis CommitDate: 2026-06-30 06:23:57 +0000 sound: Adjust mmap example to use kqueue Reviewed by: christos Differential Revision: https://reviews.freebsd.org/D57410 (cherry picked from commit a48bbef5eb32508a8d7b3b986c9b1d28176d1694) --- 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;