On 18/08/19(Sun) 08:02, Alexandre Ratchov wrote:
> Currently the block size calculations are broken by design: the driver
> provides a round_blocksize() function which must retrun a valid block
> size in *bytes*. Unfortunately, since the driver doesn't know if it's
> called for the play or for the record block size, it's mathematically
> impossible to calculate the block size in all cases if play and record
> number of channels are different. As a consequence, there are
> half-working and weired hacks to find a usable block sizes.
> 
> The diff below addresses this by adding two new driver functions,
> which are very simple to use:
> 
> set_blksz() - calculate and set the block size in *frames*, it's
>       necessarily common to play and recording directions no matter
>       the number of channels,
> 
> set_nblks() - calculate the number of blocks per buffer for the given
>       direction.
> 
> the diff below shows how to properly calculate the block size in
> azalia and uaudio. The plan is to convert all drivers from
> round_blocksize() to the new functions and to delete
> round_blocksize().
> 
> Why is this important? besides for removing ugly (and risky) hacks, we
> want all our drivers to support common block sizes in the 5ms-50ms
> range. This would allow to implement switching between audio devices:
> for instance, start playback on a USB device, unplug the cable and
> continue on azalia.

While the cleanup in itself makes sense to me.  I'm unsure if continuing
to play on a secondary audio device is what we want.  Nowadays phones
seems to stop music players if an audio device is disconnected.

Let's assume I'm in a hackathon hearing music via a USB device, if I
unplug it, accidentally or not, I'd find more logical that the player
stop instead of forcing the whole room to listen my music.

When gilles@ and/or jcs@ will be ready with their Bluetooth code, we
could do the same there :o)

> OK?

Diff is ok with me, if you think it makes sense to do this change anyway
:)

> 
> Index: share/man/man9/audio.9
> ===================================================================
> RCS file: /cvs/src/share/man/man9/audio.9,v
> retrieving revision 1.27
> diff -u -p -r1.27 audio.9
> --- share/man/man9/audio.9    12 Mar 2019 08:18:34 -0000      1.27
> +++ share/man/man9/audio.9    18 Aug 2019 05:28:35 -0000
> @@ -82,6 +82,10 @@ struct audio_hw_if {
>                   void (*)(void *), void *, struct audio_params *);
>       void    (*copy_output)(void *hdl, size_t bytes);
>       void    (*underrun)(void *hdl);
> +     int     (*set_blksz)(void *, int,
> +                 struct audio_params *, struct audio_params *, int);
> +     int     (*set_nblks)(void *, int, int,
> +                 struct audio_params *, int);
>  };
>  
>  struct audio_params {
> @@ -417,6 +421,56 @@ Drivers using bounce buffers for transfe
>  ring buffer and the device must implement this method to skip
>  one block from the audio ring buffer and transfer the
>  corresponding amount of silence to the device.
> +.It Fn "int (*set_blksz)" "void *hdl" "int mode" \
> +"struct audio_params *play" "struct audio_params *rec" "int blksz"
> +This function is called to set the audio block size.
> +.Fa mode
> +is a combination of the
> +.Dv AUMODE_RECORD
> +and
> +.Dv AUMODE_PLAY
> +flags indicating the current mode set with the
> +.Fn open
> +function.
> +The
> +.Fa play
> +and
> +.Fa rec
> +structures contain the current encoding set with the
> +.Fn set_params
> +function.
> +.Fa blksz
> +is the desired block size in frames.
> +It may be adjusted to match hardware constraints.
> +This function returns the adjusted block size.
> +.It Fn "int (*set_nblks)" "void *hdl" "int dir" "int blksz" \
> +"struct audio_params *params" "int nblks"
> +This function is called to set the number of audio blocks in
> +the ring buffer.
> +.Fa dir
> +is either the
> +.Dv AUMODE_RECORD
> +or the
> +.Dv AUMODE_PLAY
> +flag, indicating which ring buffer size is set.
> +The
> +.Fa params
> +structure contains the encoding parameters set by the
> +.Fn set_params
> +method.
> +.Fa blksz
> +is the current block size in frames set with the
> +.Fa set_params
> +function.
> +The
> +.Fa params
> +structure is the current encoding parameters, set with the
> +.Fn set_params
> +function.
> +.Fa nblks
> +is the desired number of blocks in the ring buffer.
> +It may be lowered to at least two, to match hardware constraints.
> +This function returns the adjusted number of blocks.
>  .El
>  .Pp
>  If the audio hardware is capable of input from more
> Index: sys/dev/audio.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/audio.c,v
> retrieving revision 1.180
> diff -u -p -r1.180 audio.c
> --- sys/dev/audio.c   17 Aug 2019 05:04:56 -0000      1.180
> +++ sys/dev/audio.c   18 Aug 2019 05:28:36 -0000
> @@ -193,6 +193,31 @@ audio_gcd(unsigned int a, unsigned int b
>       return a;
>  }
>  
> +/*
> + * Calculate the least block size (in frames) such that both the
> + * corresponding play and/or record block sizes (in bytes) are multiple
> + * of the given number of bytes.
> + */
> +int
> +audio_blksz_bytes(int mode,
> +     struct audio_params *p, struct audio_params *r, int bytes)
> +{
> +     unsigned int np, nr;
> +
> +     if (mode & AUMODE_PLAY) {
> +             np = bytes / audio_gcd(p->bps * p->channels, bytes);
> +             if (!(mode & AUMODE_RECORD))
> +                     nr = np;
> +     }
> +     if (mode & AUMODE_RECORD) {
> +             nr = bytes / audio_gcd(r->bps * r->channels, bytes);
> +             if (!(mode & AUMODE_PLAY))
> +                     np = nr;
> +     }
> +
> +     return nr * np / audio_gcd(nr, np);
> +}
> +
>  int
>  audio_buf_init(struct audio_softc *sc, struct audio_buf *buf, int dir)
>  {
> @@ -625,11 +650,19 @@ audio_canstart(struct audio_softc *sc)
>  }
>  
>  int
> -audio_setpar_blksz(struct audio_softc *sc)
> +audio_setpar_blksz(struct audio_softc *sc,
> +    struct audio_params *p, struct audio_params *r)
>  {
>       unsigned int nr, np, max, min, mult;
>       unsigned int blk_mult, blk_max;
>  
> +     if (sc->ops->set_blksz) {
> +             sc->round = sc->ops->set_blksz(sc->arg, sc->mode,
> +                 p, r, sc->round);
> +             DPRINTF("%s: block size set to: %u\n", DEVNAME(sc), sc->round);
> +             return 0;
> +     }
> +
>       /*
>        * get least multiplier of the number of frames per block
>        */
> @@ -706,7 +739,8 @@ audio_setpar_blksz(struct audio_softc *s
>  }
>  
>  int
> -audio_setpar_nblks(struct audio_softc *sc)
> +audio_setpar_nblks(struct audio_softc *sc,
> +    struct audio_params *p, struct audio_params *r)
>  {
>       unsigned int max;
>  
> @@ -719,6 +753,12 @@ audio_setpar_nblks(struct audio_softc *s
>                       sc->play.nblks = max;
>               else if (sc->play.nblks < 2)
>                       sc->play.nblks = 2;
> +             if (sc->ops->set_nblks) {
> +                     sc->play.nblks = sc->ops->set_nblks(sc->arg, sc->mode,
> +                         p, sc->round, sc->play.nblks);
> +                     DPRINTF("%s: play nblks -> %u\n", DEVNAME(sc),
> +                         sc->play.nblks);
> +             }
>       }
>       if (sc->mode & AUMODE_RECORD) {
>               /*
> @@ -727,6 +767,11 @@ audio_setpar_nblks(struct audio_softc *s
>                * size of maximum reliability during xruns
>                */
>               max = sc->rec.datalen / (sc->round * sc->rchan * sc->bps);
> +             if (sc->ops->set_nblks) {
> +                     max = sc->ops->set_nblks(sc->arg, sc->mode,
> +                         r, sc->round, max);
> +                     DPRINTF("%s: rec nblks -> %u\n", DEVNAME(sc), max);
> +             }
>               sc->rec.nblks = max;
>       }
>       return 0;
> @@ -874,11 +919,11 @@ audio_setpar(struct audio_softc *sc)
>       }
>       audio_calc_sil(sc);
>  
> -     error = audio_setpar_blksz(sc);
> +     error = audio_setpar_blksz(sc, &p, &r);
>       if (error)
>               return error;
>  
> -     error = audio_setpar_nblks(sc);
> +     error = audio_setpar_nblks(sc, &p, &r);
>       if (error)
>               return error;
>  
> Index: sys/dev/audio_if.h
> ===================================================================
> RCS file: /cvs/src/sys/dev/audio_if.h,v
> retrieving revision 1.35
> diff -u -p -r1.35 audio_if.h
> --- sys/dev/audio_if.h        12 Mar 2019 08:16:29 -0000      1.35
> +++ sys/dev/audio_if.h        18 Aug 2019 05:28:36 -0000
> @@ -133,6 +133,10 @@ struct audio_hw_if {
>                   void (*)(void *), void *, struct audio_params *);
>       void    (*copy_output)(void *, size_t);
>       void    (*underrun)(void *);
> +     unsigned int (*set_blksz)(void *, int,
> +         struct audio_params *, struct audio_params *, unsigned int);
> +     unsigned int (*set_nblks)(void *, int,
> +         struct audio_params *, unsigned int, unsigned int);
>  };
>  
>  struct audio_attach_args {
> @@ -149,6 +153,8 @@ struct audio_attach_args {
>  /* Attach the MI driver(s) to the MD driver. */
>  struct device *audio_attach_mi(struct audio_hw_if *, void *, struct device 
> *);
>  int         audioprint(void *, const char *);
> +int         audio_blksz_bytes(int,
> +                struct audio_params *, struct audio_params *, int);
>  
>  extern struct mutex audio_lock;
>  
> Index: sys/dev/pci/azalia.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/pci/azalia.c,v
> retrieving revision 1.250
> diff -u -p -r1.250 azalia.c
> --- sys/dev/pci/azalia.c      13 Aug 2019 15:28:12 -0000      1.250
> +++ sys/dev/pci/azalia.c      18 Aug 2019 05:28:36 -0000
> @@ -252,7 +252,10 @@ int      azalia_open(void *, int);
>  void azalia_close(void *);
>  int  azalia_set_params(void *, int, int, audio_params_t *,
>       audio_params_t *);
> -int  azalia_round_blocksize(void *, int);
> +unsigned int azalia_set_blksz(void *, int,
> +     struct audio_params *, struct audio_params *, unsigned int);
> +unsigned int azalia_set_nblks(void *, int,
> +     struct audio_params *, unsigned int, unsigned int);
>  int  azalia_halt_output(void *);
>  int  azalia_halt_input(void *);
>  int  azalia_set_port(void *, mixer_ctrl_t *);
> @@ -290,7 +293,7 @@ struct audio_hw_if azalia_hw_if = {
>       azalia_open,
>       azalia_close,
>       azalia_set_params,
> -     azalia_round_blocksize,
> +     NULL,                   /* round_blocksize */
>       NULL,                   /* commit_settings */
>       NULL,                   /* init_output */
>       NULL,                   /* init_input */
> @@ -308,7 +311,11 @@ struct audio_hw_if azalia_hw_if = {
>       azalia_round_buffersize,
>       azalia_get_props,
>       azalia_trigger_output,
> -     azalia_trigger_input
> +     azalia_trigger_input,
> +     NULL,                   /* copy_output */
> +     NULL,                   /* underrun */
> +     azalia_set_blksz,
> +     azalia_set_nblks
>  };
>  
>  static const char *pin_devices[16] = {
> @@ -3962,25 +3969,30 @@ azalia_set_params(void *v, int smode, in
>       return (0);
>  }
>  
> -int
> -azalia_round_blocksize(void *v, int blk)
> +unsigned int
> +azalia_set_blksz(void *v, int mode,
> +     struct audio_params *p, struct audio_params *r, unsigned int blksz)
>  {
> -     azalia_t *az;
> -     size_t size;
> +     int mult;
> +
> +     /* must be multiple of 128 bytes */
> +     mult = audio_blksz_bytes(mode, p, r, 128);
> +
> +     blksz += mult - 1;
> +     blksz -= blksz % mult;
> +
> +     return blksz;
> +}
>  
> -     blk &= ~0x7f;           /* must be multiple of 128 */
> -     if (blk <= 0)
> -             blk = 128;
> +unsigned int
> +azalia_set_nblks(void *v, int mode,
> +     struct audio_params *params, unsigned int blksz, unsigned int nblks)
> +{
>       /* number of blocks must be <= HDA_BDL_MAX */
> -     az = v;
> -     size = az->pstream.buffer.size;
> -     if (size > HDA_BDL_MAX * blk) {
> -             blk = size / HDA_BDL_MAX;
> -             if (blk & 0x7f)
> -                     blk = (blk + 0x7f) & ~0x7f;
> -     }
> -     DPRINTFN(1,("%s: resultant block size = %d\n", __func__, blk));
> -     return blk;
> +     if (nblks > HDA_BDL_MAX)
> +             nblks = HDA_BDL_MAX;
> +
> +     return nblks;
>  }
>  
>  int
> Index: sys/dev/usb/uaudio.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/uaudio.c,v
> retrieving revision 1.144
> diff -u -p -r1.144 uaudio.c
> --- sys/dev/usb/uaudio.c      9 May 2019 07:09:04 -0000       1.144
> +++ sys/dev/usb/uaudio.c      18 Aug 2019 05:28:37 -0000
> @@ -403,7 +403,8 @@ int uaudio_open(void *, int);
>  void uaudio_close(void *);
>  int uaudio_set_params(void *, int, int, struct audio_params *,
>      struct audio_params *);
> -int uaudio_round_blocksize(void *, int);
> +unsigned int uaudio_set_blksz(void *, int,
> +    struct audio_params *, struct audio_params *, unsigned int);
>  int uaudio_trigger_output(void *, void *, void *, int,
>      void (*)(void *), void *, struct audio_params *);
>  int uaudio_trigger_input(void *, void *, void *, int,
> @@ -457,7 +458,7 @@ struct audio_hw_if uaudio_hw_if = {
>       uaudio_open,            /* open */
>       uaudio_close,           /* close */
>       uaudio_set_params,      /* set_params */
> -     uaudio_round_blocksize, /* round_blocksize */
> +     NULL,                   /* round_blocksize */
>       NULL,                   /* commit_settings */
>       NULL,                   /* init_output */
>       NULL,                   /* init_input */
> @@ -477,7 +478,8 @@ struct audio_hw_if uaudio_hw_if = {
>       uaudio_trigger_output,  /* trigger_output */
>       uaudio_trigger_input,   /* trigger_input */
>       uaudio_copy_output,     /* copy_output */
> -     uaudio_underrun         /* underrun */
> +     uaudio_underrun,        /* underrun */
> +     uaudio_set_blksz        /* set_blksz */
>  };
>  
>  /*
> @@ -3962,57 +3964,43 @@ uaudio_set_params(void *self, int setmod
>       return 0;
>  }
>  
> -int
> -uaudio_round_blocksize(void *self, int blksz)
> +unsigned int
> +uaudio_set_blksz(void *self, int mode,
> +    struct audio_params *p, struct audio_params *r, unsigned int blksz)
>  {
>       struct uaudio_softc *sc = self;
> -     struct uaudio_alt *a;
> -     unsigned int rbpf, pbpf;
> -     unsigned int blksz_max;
> +     unsigned int fps, fps_min;
> +     unsigned int blksz_max, blksz_min;
>  
>       /*
> -      * XXX: We don't know if we're called for the play or record
> -      * direction, so we can't calculate maximum blksz. This would
> -      * require a change in the audio(9) interface. Meanwhile, we
> -      * use the direction with the greatest sample size; it gives
> -      * the correct result: indeed, if we return:
> -      *
> -      *      blksz_max = max(pbpf, rbpf) * nsamp_max
> -      *
> -      * in turn the audio(4) layer will use:
> -      *
> -      *      min(blksz_max / pbpf, blksz_max / rbpf)
> -      *
> -      * which is exactly nsamp_max.
> +      * minimum block size is two transfers, see uaudio_stream_open()
>        */
> -
> -     if (sc->mode & AUMODE_PLAY) {
> -             a = sc->params->palt;
> -             pbpf = a->bps * a->nch;
> -     } else
> -             pbpf = 1;
> -
> -     if (sc->mode & AUMODE_RECORD) {
> -             a = sc->params->ralt;
> -             rbpf = a->bps * a->nch;
> -     } else
> -             rbpf = 1;
> +     fps_min = sc->ufps;
> +     if (mode & AUMODE_PLAY) {
> +             fps = sc->params->palt->fps;
> +             if (fps_min > fps)
> +                     fps_min = fps;
> +     }
> +     if (mode & AUMODE_RECORD) {
> +             fps = sc->params->ralt->fps;
> +             if (fps_min > fps)
> +                     fps_min = fps;
> +     }
> +     blksz_min = (sc->rate * 2 + fps_min - 1) / fps_min;
>  
>       /*
> -      * Limit the block size to (slightly more than):
> -      *
> -      *      sc->host_nframes / UAUDIO_NXFERS_MIN
> -      *
> -      * (micro-)frames of audio. Transfers are slightly larger than
> -      * the audio block size (few bytes to make the "safe" block
> -      * size plus one extra millisecond). We reserve an extra 15%
> -      * for that.
> -      */
> -     blksz_max = (pbpf > rbpf ? pbpf : rbpf) *
> -         sc->rate * (sc->host_nframes / UAUDIO_NXFERS_MIN) / sc->ufps *
> -         85 / 100;
> +      * max block size is only limited by the number of frames the
> +      * host can schedule
> +      */
> +     blksz_max = sc->rate * (sc->host_nframes / UAUDIO_NXFERS_MIN) /
> +         sc->ufps * 85 / 100;
> +
> +     if (blksz > blksz_max)
> +             blksz = blksz_max;
> +     else if (blksz < blksz_min)
> +             blksz = blksz_min;
>  
> -     return blksz < blksz_max ? blksz : blksz_max;
> +     return blksz;
>  }
>  
>  int
> 

Reply via email to