Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/audio audio(4): Make recording buffer more robust.



details:   https://anonhg.NetBSD.org/src/rev/de02ebbbf31e
branches:  trunk
changeset: 365716:de02ebbbf31e
user:      isaki <isaki%NetBSD.org@localhost>
date:      Wed Apr 20 06:05:22 2022 +0000

description:
audio(4): Make recording buffer more robust.
Previously, main buffer in recording track was usrbuf, which is the closest
buffer to the userland.  Because, this buffer arrangement was symmetrical
with the playback track, and had affinity with the past implementation.
However, in the current implementation, read(2) (from user application)
takes recorded block out from inputbuf, which is the closest buffer to
rmixer, to usrbuf.  So it was not good way to use the usrbuf as main buffer.
Now, usrbuf in recording track holds only fragment bytes in order to
transfer to the userland, and main buffer in recording track is the inputbuf,
the closest to rmixer.

Buffer size of the inputbuf is also modified.  Previously, it was less than
64KB or at least 4 blocks.  This had affinity with playback track and the
past implementation.  But this was not appropriate for both formats with
too large frames or too small frames.  In large frames (for example,
192kHz/12ch), 184KB buffer would be allocated but it corresponds to only
40msec.  In opposite, in small frames (for example, 8000Hz/1ch), 64KB
buffer would be allocated and it corresponds to 4.1 seconds.  But for such
machines that have 8000Hz/1ch device, in-kernel 64KB memory would probably
be expensive.
Now, inputbuf will always be allocated 16(NBLKIN) blocks, regardless of its
hardware format.  It corresponds to 160msec on modern archs (if blk_ms=10),
or 640msec on antique archs (if blk_ms=40).

diffstat:

 sys/dev/audio/audio.c    |  290 +++++++++++++++++++++++++++++++---------------
 sys/dev/audio/audiodef.h |   23 +++-
 2 files changed, 213 insertions(+), 100 deletions(-)

diffs (truncated from 488 to 300 lines):

diff -r beb9bf1ab43f -r de02ebbbf31e sys/dev/audio/audio.c
--- a/sys/dev/audio/audio.c     Wed Apr 20 04:41:29 2022 +0000
+++ b/sys/dev/audio/audio.c     Wed Apr 20 06:05:22 2022 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: audio.c,v 1.125 2022/04/20 04:41:29 isaki Exp $        */
+/*     $NetBSD: audio.c,v 1.126 2022/04/20 06:05:22 isaki Exp $        */
 
 /*-
  * Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -181,7 +181,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: audio.c,v 1.125 2022/04/20 04:41:29 isaki Exp $");
+__KERNEL_RCSID(0, "$NetBSD: audio.c,v 1.126 2022/04/20 06:05:22 isaki Exp $");
 
 #ifdef _KERNEL_OPT
 #include "audio.h"
@@ -608,7 +608,8 @@
 
 static int audio_query_devinfo(struct audio_softc *, mixer_devinfo_t *);
 
-static __inline int audio_track_readablebytes(const audio_track_t *);
+static int audio_track_inputblk_as_usrbyte(const audio_track_t *, int);
+static int audio_track_readablebytes(const audio_track_t *);
 static int audio_file_setinfo(struct audio_softc *, audio_file_t *,
        const struct audio_info *);
 static int audio_track_setinfo_check(audio_track_t *,
@@ -2775,10 +2776,10 @@
                int bytes;
 
                TRACET(3, track,
-                   "while resid=%zd input=%d/%d/%d usrbuf=%d/%d/H%d",
+                   "while resid=%zd input=%d/%d/%d usrbuf=%d/%d/C%d",
                    uio->uio_resid,
                    input->head, input->used, input->capacity,
-                   usrbuf->head, usrbuf->used, track->usrbuf_usedhigh);
+                   usrbuf->head, usrbuf->used, usrbuf->capacity);
 
                /* Wait when buffers are empty. */
                mutex_enter(sc->sc_lock);
@@ -2805,34 +2806,27 @@
                mutex_exit(sc->sc_lock);
 
                audio_track_lock_enter(track);
-               /* Convert as many blocks as possible. */
-               while (usrbuf->used <=
-                           track->usrbuf_usedhigh - track->usrbuf_blksize &&
-                   input->used > 0) {
+               /* Convert one block if possible. */
+               if (usrbuf->used == 0 && input->used > 0) {
                        audio_track_record(track);
                }
 
                /* uiomove from usrbuf as many bytes as possible. */
                bytes = uimin(usrbuf->used, uio->uio_resid);
-               while (bytes > 0) {
-                       int head = usrbuf->head;
-                       int len = uimin(bytes, usrbuf->capacity - head);
-                       error = uiomove((uint8_t *)usrbuf->mem + head, len,
-                           uio);
-                       if (error) {
-                               audio_track_lock_exit(track);
-                               device_printf(sc->sc_dev,
-                                   "%s: uiomove(%d) failed: errno=%d\n",
-                                   __func__, len, error);
-                               goto abort;
-                       }
-                       auring_take(usrbuf, len);
-                       track->useriobytes += len;
-                       TRACET(3, track, "uiomove(len=%d) usrbuf=%d/%d/C%d",
-                           len,
-                           usrbuf->head, usrbuf->used, usrbuf->capacity);
-                       bytes -= len;
-               }
+               error = uiomove((uint8_t *)usrbuf->mem + usrbuf->head, bytes,
+                   uio);
+               if (error) {
+                       audio_track_lock_exit(track);
+                       device_printf(sc->sc_dev,
+                           "%s: uiomove(%d) failed: errno=%d\n",
+                           __func__, bytes, error);
+                       goto abort;
+               }
+               auring_take(usrbuf, bytes);
+               track->useriobytes += bytes;
+               TRACET(3, track, "uiomove(len=%d) usrbuf=%d/%d/C%d",
+                   bytes,
+                   usrbuf->head, usrbuf->used, usrbuf->capacity);
 
                audio_track_lock_exit(track);
        }
@@ -3298,9 +3292,31 @@
 }
 
 /*
+ * Convert n [frames] of the input buffer to bytes in the usrbuf format.
+ * n is in frames but should be a multiple of frame/block.  Note that the
+ * usrbuf's frame/block and the input buffer's frame/block may be different
+ * (i.e., if frequencies are different).
+ *
+ * This function is for recording track only.
+ */
+static int
+audio_track_inputblk_as_usrbyte(const audio_track_t *track, int n)
+{
+       int input_fpb;
+
+       /*
+        * In the input buffer on recording track, these are the same.
+        * input_fpb = frame_per_block(track->mixer, &track->input->fmt);
+        */
+       input_fpb = track->mixer->frames_per_block;
+
+       return (n / input_fpb) * track->usrbuf_blksize;
+}
+
+/*
  * Returns the number of bytes that can be read on recording buffer.
  */
-static __inline int
+static int
 audio_track_readablebytes(const audio_track_t *track)
 {
        int bytes;
@@ -3309,12 +3325,20 @@
        KASSERT(track->mode == AUMODE_RECORD);
 
        /*
-        * Although usrbuf is primarily readable data, recorded data
-        * also stays in track->input until reading.  So it is necessary
-        * to add it.  track->input is in frame, usrbuf is in byte.
+        * For recording, track->input is the main block-unit buffer and
+        * track->usrbuf holds less than one block of byte data ("fragment").
+        * Note that the input buffer is in frames and the usrbuf is in bytes.
+        *
+        * Actual total capacity of these two buffers is
+        *  input->capacity [frames] + usrbuf.capacity [bytes],
+        * but only input->capacity is reported to userland as buffer_size.
+        * So, even if the total used bytes exceed input->capacity, report it
+        * as input->capacity for consistency.
         */
-       bytes = track->usrbuf.used +
-           track->input->used * frametobyte(&track->usrbuf.fmt, 1);
+       bytes = audio_track_inputblk_as_usrbyte(track, track->input->used);
+       if (track->input->used < track->input->capacity) {
+               bytes += track->usrbuf.used;
+       }
        return bytes;
 }
 
@@ -4488,45 +4512,98 @@
 }
 
 /*
- * When playing back: (e.g. if codec and freq stage are valid)
+ * There are two unit of buffers; A block buffer and a byte buffer.  Both use
+ * audio_ring_t.  Internally, audio data is always handled in block unit.
+ * Converting format, sythesizing tracks, transferring from/to the hardware,
+ * and etc.  Only one exception is usrbuf.  To transfer with userland, usrbuf
+ * is buffered in byte unit.
+ * For playing back, write(2) writes arbitrary length of data to usrbuf.
+ * When one block is filled, it is sent to the next stage (converting and/or
+ * synthesizing).
+ * For recording, the rmixer writes one block length of data to input buffer
+ * (the bottom stage buffer) each time.  read(2) (converts one block if usrbuf
+ * is empty and then) reads arbitrary length of data from usrbuf.
  *
- *               write
+ * The following charts show the data flow and buffer types for playback and
+ * recording track.  In this example, both have two conversion stages, codec
+ * and freq.  Every [**] represents a buffer described below.
+ *
+ * On playback track:
+ *
+ *               write(2)
+ *                |
  *                | uiomove
  *                v
- *  usrbuf      [...............]  byte ring buffer (mmap-able)
- *                | memcpy
+ *  usrbuf       [BB|BB ... BB|BB]     .. Byte ring buffer
+ *                |
+ *                | memcpy one block
  *                v
- *  codec.srcbuf[....]             1 block (ring) buffer   <-- stage input
+ *  codec.srcbuf [FF]                  .. 1 block (ring) buffer
  *       .dst ----+
+ *                |
+ *                | convert
+ *                v
+ *  freq.srcbuf  [FF]                  .. 1 block (ring) buffer
+ *      .dst  ----+
+ *                |
  *                | convert
  *                v
- *  freq.srcbuf [....]             1 block (ring) buffer
- *      .dst  ----+
- *                | convert
+ *  outbuf       [FF|FF|FF|FF]         .. NBLKOUT blocks ring buffer
+ *                |
  *                v
- *  outbuf      [...............]  NBLKOUT blocks ring buffer
+ *               pmixer
+ *
+ * There are three different types of buffers:
+ *
+ *  [BB|BB ... BB|BB]  usrbuf.  Is the buffer closest to userland.  Mandatory.
+ *                     This is a byte buffer and its length is basically less
+ *                     than or equal to 64KB or at least AUMINNOBLK blocks.
+ *
+ *  [FF]               Interim conversion stage's srcbuf if necessary.
+ *                     This is one block (ring) buffer counted in frames.
+ *
+ *  [FF|FF|FF|FF]      outbuf.  Is the buffer closest to pmixer.  Mandatory.
+ *                     This is NBLKOUT blocks ring buffer counted in frames.
  *
  *
- * When recording:
+ * On recording track:
  *
- *  freq.srcbuf [...............]  NBLKOUT blocks ring buffer <-- stage input
- *      .dst  ----+
+ *               read(2)
+ *                ^
+ *                | uiomove
+ *                |
+ *  usrbuf       [BB]                  .. Byte (ring) buffer
+ *                ^
+ *                | memcpy one block
+ *                |
+ *  outbuf       [FF]                  .. 1 block (ring) buffer
+ *                ^
  *                | convert
- *                v
- *  codec.srcbuf[.....]            1 block (ring) buffer
- *       .dst ----+
+ *                |
+ *  codec.dst ----+
+ *       .srcbuf [FF]                  .. 1 block (ring) buffer
+ *                ^
  *                | convert
- *                v
- *  outbuf      [.....]            1 block (ring) buffer
- *                | memcpy
- *                v
- *  usrbuf      [...............]  byte ring buffer (mmap-able *)
- *                | uiomove
- *                v
- *               read
+ *                |
+ *  freq.dst  ----+
+ *      .srcbuf  [FF|FF ... FF|FF]     .. NBLKIN blocks ring buffer
+ *                ^
+ *                |
+ *               rmixer
+ *
+ * There are also three different types of buffers.
  *
- *    *: usrbuf for recording is also mmap-able due to symmetry with
- *       playback buffer, but for now mmap will never happen for recording.
+ *  [BB]               usrbuf.  Is the buffer closest to userland.  Mandatory.
+ *                     This is a byte buffer and its length is one block.
+ *                     This buffer holds only "fragment".
+ *
+ *  [FF]               Interim conversion stage's srcbuf (or outbuf).
+ *                     This is one block (ring) buffer counted in frames.
+ *
+ *  [FF|FF ... FF|FF]  The bottom conversion stage's srcbuf (or outbuf).
+ *                     This is the buffer closest to rmixer, and mandatory.
+ *                     This is NBLKIN blocks ring buffer counted in frames.
+ *                     Also pointed by *input.
  */
 
 /*
@@ -4545,6 +4622,8 @@
 audio_track_set_format(audio_track_t *track, audio_format2_t *usrfmt)
 {
        struct audio_softc *sc;
+       audio_ring_t *last_dst;
+       int is_playback;
        u_int newbufsize;
        u_int oldblksize;
        u_int len;
@@ -4553,10 +4632,20 @@
        KASSERT(track);
        sc = track->mixer->sc;
 
+       is_playback = audio_track_is_playback(track);
+
        /* usrbuf is the closest buffer to the userland. */
        track->usrbuf.fmt = *usrfmt;
 
        /*
+        * Usrbuf.
+        * On the playback track, its capacity is less than or equal to 64KB
+        * (for historical reason) and must be a multiple of a block
+        * (constraint in this implementation).  But at least AUMINNOBLK
+        * blocks.
+        * On the recording track, its capacity is one block.



Home | Main Index | Thread Index | Old Index