Page MenuHomeFreeBSD

snd_uaudio(4): Adapt buffer length to buffer_ms tunable.
ClosedPublic

Authored by dev_submerge.ch on Sep 22 2023, 2:44 PM.
Referenced Files
Unknown Object (File)
Tue, Dec 17, 9:51 PM
Unknown Object (File)
Fri, Dec 6, 7:22 AM
Unknown Object (File)
Nov 20 2024, 2:01 PM
Unknown Object (File)
Nov 8 2024, 8:55 PM
Unknown Object (File)
Nov 8 2024, 8:46 PM
Unknown Object (File)
Nov 8 2024, 5:39 PM
Unknown Object (File)
Oct 25 2024, 11:37 PM
Unknown Object (File)
Oct 25 2024, 11:36 PM

Details

Summary

Adapt the length of the driver side audio buffer to the USB transfer
interval, which is adjustable through the buffer_ms tunable. This
eliminates unnecessary latency in USB audio playback.

To reduce power consumption caused by frequent CPU wakeups, increase the
default buffer_ms value to 4ms. In combination with adaptive buffer
length, this still results in less roundtrip latency compared to the
previous 2ms default.

Extend the buffer_ms value range to 1ms for low latency applications.

Test Plan

These changes were tested on two different amd64 machines, with several USB audio devices from consumer grade to professional multichannel audio interfaces. All tests happened on 13.2-RELEASE, it would be unreasonable to run CURRENT for my audio setups. That should be ok, according to git there were no significant changes regarding snd_uaudio.

Buffer Length changes to the buffer_ms sysctl will be effective when the pcm device is (re-)opened by an audio application. The driver side buffer size appears as a [b:total_size/block_size/blocks|...] triplet, e.g. [b:1536/768/2|...] in /dev/sndstat:

# sysctl hw.snd.verbose=2
# cat /dev/sndstat

In all tests the buffer size matched a double buffer of the specified buffer_ms length, as expected.

Listening Tests revealed no problems or regressions.

Latency was measured through JACK (audio/jack) and the jack_iodelay utility from jack-example-tools (audio/jack-example-tools). Some latency is introduced by the hardware, but the buffer-related parts come down to roughly:

buffer_msplayback bufferplayback transferrecording transfertotal
82 * 8ms8ms8ms32ms
42 * 4ms4ms4ms16ms
22 * 2ms2ms2ms8ms
12 * 1ms1ms1ms4ms

Prior to this change the default of 2ms for buffer_ms would result in 2 * 8ms playback buffer plus 2ms latency for both playback and recording USB transfers. This means that our new default of 4 reduces roundtrip latency to 16ms from previously 20ms.

Power Consumption from CPU wakeups was reduced by around 0.5W on my Thinkpad X230 with the new default of 4ms. Playback resulted in 8.2W (+0.9W) compared to previous 8.7W (+1.4W) on an otherwise idle machine. Measured through the battery info:

# acpiconf -i0 | grep rate

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

@mav I hope it's ok to add you as a reviewer / committer here? Or should I ask someone else instead?

@meka_tilda.center Sorry to hijack your efforts from D41627: Lower minimal USB audio buffer size limit from 2ms to 1ms, but this was a really nice opportunity to bring down USB audio latency to reasonable levels...

Ping! Also, patch briefly tested on 15.0-CURRENT as of today. No regressions, works as expected.

I finally found the time to test this patch. I'm running CURRENT and I only have Behringer XR18 at my disposal. First test I ran was lowering the buffer_ms to 1 and just playing audio for few hours. I didn't tweak virtual_oss parameters or anything, and audacious was using virtual_oss. Not a single jitter, glitch or xrun in those few hours, no audible defects of any kind. In the following days I intend to test the latency with jack_iodelay.

I ran jack_iodelay with same buffer_ms value and these are the results:

Original
1416.952 frames     29.520 ms total roundtrip latency
  extra loopback latency: 1032 frames
  use 516 for the backend arguments -I and -O
Patched
1314.950 frames     27.395 ms total roundtrip latency
  extra loopback latency: 738 frames
  use 369 for the backend arguments -I and -O

Because of different impedance on input and output of my interface, I run it through digital processor which has "no-amp" mode, or just copy input to output. I use it to connect it to different impedance in/out. I am sure it adds on latency, but the decrease of lag is obvious. The latency displayed is huge for any real-time use, but I guess playing with parameters more can lower the latency.

Thanks for testing, Meka! Could you elaborate on how you did the latency measurements?
Because jack_iodelay reports a decrease of only ~2ms in total roundtrip latency, whereas the extra loopback latency decreases by ~300 frames (~6ms at 48kHz). Any other changes in the setup or Jack settings?

A digital processor in the loop would probably account for 3-5ms extra latency. AD/DA is typically ~1ms each at 48kHz. If the second measurement is correct, that leaves you with around 8-10ms for the uaudio buffers. Was buffer_ms set to 2ms?

christos added inline comments.
sys/dev/sound/usb/uaudio.c
126–127

I think we should define these values (1 and 8) as constants at the top of the file.

1343

Same as my first comment.

dev_submerge.ch edited the test plan for this revision. (Show Details)

Introduced UAUDIO_BUFFER_MS_MIN and UAUDIO_BUFFER_MS_MAX as constants.
Intentionally left the comments that mention 8 ms max buffer length. It
gives some understandable context and that value will probably never change
as long as current USB audio standards are supported.

@meka_tilda.center Any comment on your test results?

@christos Did you test this patch? Measuring latency with jack_iodelay is somewhat more involved. But if you set buffer_ms=1 and do some interactive stuff on the bare pcm device (disable sound servers), there should be a tangible drop in latency compared to current HEAD.

Also, should we MFC this? I think it would be nice to have this in 14.1-RELEASE.

The snd_uaudio(4) man page is missing information on the hw.usb.uaudio tunables, BTW. Maybe in a follow-up patch.

sys/dev/sound/usb/uaudio.c
139

IMO indicating the units explicitly should be our preference, e.g. uaudio buffering delay in ms, from 1 to 8. (Although ms is in the sysctl name.)

See other examples like net.inet6.ip6.fraglifetime_ms: Fragment lifetime, in milliseconds

Clarified buffer_ms sysctl description as suggested by @emaste.

Sorry for the delay, I got sick (again). Anyway, it took me a while to figure out what's going on. As I didn't have speakers on, I didn't notice there are jitters, that's why the numbers don't match. I took similar measurements with higher period size in jack and these are the results

Main branch
-----------
hw.usb.uaudio.buffer_ms = 2

period = 1024
  4097.948 frames     85.374 ms total roundtrip latency
	extra loopback latency: 1025 frames
	use 512 for the backend arguments -I and -O

period = 512
  2562.949 frames     53.395 ms total roundtrip latency
	extra loopback latency: 1026 frames
	use 513 for the backend arguments -I and -O

Main + uaudio patch
-------------------
hw.usb.uaudio.buffer_ms = 2

period = 1024
  3816.954 frames     79.520 ms total roundtrip latency
	extra loopback latency: 744 frames
	use 372 for the backend arguments -I and -O

period = 512
  2280.954 frames     47.520 ms total roundtrip latency
	extra loopback latency: 744 frames
	use 372 for the backend arguments -I and -O

I can see ~6ms reduction in latency. I don't yet understand the corelation between period and frame so I don't really understand why different periods give (almost) the same number of frames.

Hi Meka, sorry to hear, hope you are well now.
Your measurements seem to be accurate. Unfortunately they do not match my expectations, as I would have estimated more like a ~12ms reduction in this scenario.
I'll try to verify my own tests, but could you measure the new default of buffer_ms=4 in your setup? And a period of 768 in addition to 512 and 1024? Thanks!
I may have to check with the version of Jack in ports, I did my measurements with the new sosso backend which does mmap io.

Explanation for period and frame: Let's have a look at the first line.

3816.954 frames     79.520 ms total roundtrip latency

This is the total latency that jack_iodelay experiences. It includes latency introduced through internal buffers in the Jack audio server, which is usually 3 * period frames (samples). If we change the period from 1024 to 512, we see a drop of about 3 * (1024 - 512) frames in total latency which is minus 32ms:

2280.954 frames     47.520 ms total roundtrip latency

Jack reports this known internal latency to jack_iodelay, so it can be subtracted to result in 2280 - (3 * 512) frames extra latency:

	extra loopback latency: 744 frames
	use 372 for the backend arguments -I and -O

This extra latency is approximately what you get from pcm and driver buffers, hardware and DA/AD converters. And yes, it should be the same regardless of Jack period. It's 15.5ms here, still a bit high for buffer_ms=2.

Thank you for the explanation. I took the measurement and I tried to get the lowest latency without jitters, so here is the complete measurement.

Main branch
-----------
hw.usb.uaudio.buffer_ms = 2

period = 1024
  4097.948 frames     85.374 ms total roundtrip latency
	extra loopback latency: 1025 frames
	use 512 for the backend arguments -I and -O

period = 768
  2561.949 frames     53.374 ms total roundtrip latency
	extra loopback latency: 1025 frames
	use 512 for the backend arguments -I and -O

period = 512
  2562.949 frames     53.395 ms total roundtrip latency
	extra loopback latency: 1026 frames
	use 513 for the backend arguments -I and -O

Main + uaudio patch
-------------------
hw.usb.uaudio.buffer_ms = 2

period = 1024
  3816.954 frames     79.520 ms total roundtrip latency
	extra loopback latency: 744 frames
	use 372 for the backend arguments -I and -O

period = 768
2279.950 frames     47.499 ms total roundtrip latency
	extra loopback latency: 743 frames
	use 371 for the backend arguments -I and -O

period = 512
  2280.954 frames     47.520 ms total roundtrip latency
	extra loopback latency: 744 frames
	use 372 for the backend arguments -I and -O


hw.usb.uaudio.buffer_ms = 1
period = 768
  2273.951 frames     47.374 ms total roundtrip latency
	extra loopback latency: 737 frames
	use 368 for the backend arguments -I and -O

period = 386
  1511.951 frames     31.499 ms total roundtrip latency
	extra loopback latency: 743 frames
	use 371 for the backend arguments -I and -O

period = 192
  1312.951 frames     27.353 ms total roundtrip latency
	extra loopback latency: 736 frames
	use 368 for the backend arguments -I and -O

As my sampling frequency is 48kHz it makes sense that period is multiple of 48. I listened to ardour session with ~20 tracks for few minutes on the lowest latency settings and no glitches or jitters this time.

I missed 4ms request from you, so here are those measurements

Main branch
-----------
hw.usb.uaudio.buffer_ms = 4
period = 768
  3337.952 frames     69.541 ms total roundtrip latency
	extra loopback latency: 1033 frames
	use 516 for the backend arguments -I and -O

period = 384
  2185.952 frames     45.541 ms total roundtrip latency
	extra loopback latency: 1033 frames
	use 516 for the backend arguments -I and -O

period = 192
  1609.952 frames     33.541 ms total roundtrip latency
	extra loopback latency: 1033 frames
	use 516 for the backend arguments -I and -O




Main + uaudio patch
-------------------
hw.usb.uaudio.buffer_ms = 4
period = 768
  2273.952 frames     47.374 ms total roundtrip latency
	extra loopback latency: 737 frames
	use 368 for the backend arguments -I and -O

period = 384
  1895.952 frames     39.499 ms total roundtrip latency
	extra loopback latency: 743 frames
	use 371 for the backend arguments -I and -O

period = 192
  1313.952 frames     27.374 ms total roundtrip latency
	extra loopback latency: 737 frames
	use 368 for the backend arguments -I and -O

Thanks for all the measurements, Meka! It seems like the buffer_ms sysctl doesn't have any effect at all. I suspect your audio interface to demand a longer interval in its USB descriptor. Could you

  1. Make a measurement using buffer_ms=8, with or without patch.
  2. Post dmesg | grep uaudio to give some info on the audio interface.
  3. Dump the USB descriptor of your audio interface.

In the USB descriptor we're looking for bInterval values, notably with value > 4. You can obtain a dump as root, replace X and Y with your device numbers:

# usbconfig list
# usbconfig -d ugenX.Y dump_curr_config_desc
Main branch
-----------
hw.usb.uaudio.buffer_ms = 8
period = 768
  2563.934 frames     53.415 ms total roundtrip latency
	extra loopback latency: 1027 frames
	use 513 for the backend arguments -I and -O

period = 384
  2179.936 frames     45.415 ms total roundtrip latency
	extra loopback latency: 1027 frames
	use 513 for the backend arguments -I and -O

period = 192
  1603.937 frames     33.415 ms total roundtrip latency
	extra loopback latency: 1027 frames
	use 513 for the backend arguments -I and -O



Main + uaudio patch
-------------------
hw.usb.uaudio.buffer_ms = 8
period = 768
  2273.943 frames     47.374 ms total roundtrip latency
	extra loopback latency: 737 frames
	use 368 for the backend arguments -I and -O

period = 384
  1889.943 frames     39.374 ms total roundtrip latency
	extra loopback latency: 737 frames
	use 368 for the backend arguments -I and -O

period = 192
  1313.942 frames     27.374 ms total roundtrip latency
	extra loopback latency: 737 frames
	use 368 for the backend arguments -I and -O
# dmesg | grep uaudio
uaudio0 on uhub2
uaudio0: <BEHRINGER X18/XR18, class 239/2, rev 2.00/1.01, addr 1> on usbus4
uaudio0: Play[0]: 48000 Hz, 18 ch, 32-bit S-LE PCM format, 2x2ms buffer.
uaudio0: Record[0]: 48000 Hz, 18 ch, 32-bit S-LE PCM format, 2x2ms buffer.
uaudio0: MIDI sequencer.
pcm4 on uaudio0
uaudio0: No HID volume keys found.
# usbconfig -d ugen4.2 dump_curr_config_desc
ugen4.2: <BEHRINGER X18/XR18> at usbus4, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=ON (0mA)


 Configuration index 0

    bLength = 0x0009 
    bDescriptorType = 0x0002 
    wTotalLength = 0x01ea 
    bNumInterfaces = 0x0006 
    bConfigurationValue = 0x0001 
    iConfiguration = 0x0000  <no string>
    bmAttributes = 0x00c0 
    bMaxPower = 0x0000 

    Additional Descriptor

    bLength = 0x08
    bDescriptorType = 0x0b
    bDescriptorSubType = 0x00
     RAW dump: 
     0x00 | 0x08, 0x0b, 0x00, 0x04, 0x01, 0x00, 0x20, 0x00


    Interface 0
      bLength = 0x0009 
      bDescriptorType = 0x0004 
      bInterfaceNumber = 0x0000 
      bAlternateSetting = 0x0000 
      bNumEndpoints = 0x0000 
      bInterfaceClass = 0x0001  <Audio device>
      bInterfaceSubClass = 0x0001 
      bInterfaceProtocol = 0x0020 
      iInterface = 0x0007  <X18/XR18>

      Additional Descriptor

      bLength = 0x09
      bDescriptorType = 0x24
      bDescriptorSubType = 0x01
       RAW dump: 
       0x00 | 0x09, 0x24, 0x01, 0x00, 0x02, 0x08, 0xef, 0x00, 
       0x08 | 0x00

      Additional Descriptor

      bLength = 0x08
      bDescriptorType = 0x24
      bDescriptorSubType = 0x0a
       RAW dump: 
       0x00 | 0x08, 0x24, 0x0a, 0x28, 0x01, 0x07, 0x00, 0x10


      Additional Descriptor

      bLength = 0x11
      bDescriptorType = 0x24
      bDescriptorSubType = 0x02
       RAW dump: 
       0x00 | 0x11, 0x24, 0x02, 0x02, 0x01, 0x01, 0x00, 0x28, 
       0x08 | 0x12, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 
       0x10 | 0x02

      Additional Descriptor

      bLength = 0x52
      bDescriptorType = 0x24
      bDescriptorSubType = 0x06
       RAW dump: 
       0x00 | 0x52, 0x24, 0x06, 0x0a, 0x02, 0x00, 0x00, 0x00, 
       0x08 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x10 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x18 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x20 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x28 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x30 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x38 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x40 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x48 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x50 | 0x00, 0x0f


      Additional Descriptor

      bLength = 0x0c
      bDescriptorType = 0x24
      bDescriptorSubType = 0x03
       RAW dump: 
       0x00 | 0x0c, 0x24, 0x03, 0x14, 0x01, 0x03, 0x00, 0x0a, 
       0x08 | 0x28, 0x00, 0x00, 0x05


      Additional Descriptor

      bLength = 0x11
      bDescriptorType = 0x24
      bDescriptorSubType = 0x02
       RAW dump: 
       0x00 | 0x11, 0x24, 0x02, 0x01, 0x01, 0x02, 0x00, 0x28, 
       0x08 | 0x12, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 
       0x10 | 0x03

      Additional Descriptor

      bLength = 0x52
      bDescriptorType = 0x24
      bDescriptorSubType = 0x06
       RAW dump: 
       0x00 | 0x52, 0x24, 0x06, 0x0b, 0x01, 0x00, 0x00, 0x00, 
       0x08 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x10 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x18 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x20 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x28 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x30 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x38 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x40 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x48 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
       0x50 | 0x00, 0x0e


      Additional Descriptor

      bLength = 0x0c
      bDescriptorType = 0x24
      bDescriptorSubType = 0x03
       RAW dump: 
       0x00 | 0x0c, 0x24, 0x03, 0x16, 0x01, 0x01, 0x00, 0x0b, 
       0x08 | 0x28, 0x00, 0x00, 0x04



    Interface 1
      bLength = 0x0009 
      bDescriptorType = 0x0004 
      bInterfaceNumber = 0x0001 
      bAlternateSetting = 0x0000 
      bNumEndpoints = 0x0000 
      bInterfaceClass = 0x0001  <Audio device>
      bInterfaceSubClass = 0x0002 
      bInterfaceProtocol = 0x0020 
      iInterface = 0x0008  <X18/XR18 Audio Out>


    Interface 1 Alt 1
      bLength = 0x0009 
      bDescriptorType = 0x0004 
      bInterfaceNumber = 0x0001 
      bAlternateSetting = 0x0001 
      bNumEndpoints = 0x0002 
      bInterfaceClass = 0x0001  <Audio device>
      bInterfaceSubClass = 0x0002 
      bInterfaceProtocol = 0x0020 
      iInterface = 0x0009  <X18/XR18 Audio Out>

      Additional Descriptor

      bLength = 0x10
      bDescriptorType = 0x24
      bDescriptorSubType = 0x01
       RAW dump: 
       0x00 | 0x10, 0x24, 0x01, 0x02, 0x00, 0x01, 0x01, 0x00, 
       0x08 | 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x12


      Additional Descriptor

      bLength = 0x06
      bDescriptorType = 0x24
      bDescriptorSubType = 0x02
       RAW dump: 
       0x00 | 0x06, 0x24, 0x02, 0x01, 0x04, 0x18


     Endpoint 0
        bLength = 0x0007 
        bDescriptorType = 0x0005 
        bEndpointAddress = 0x0001  <OUT>
        bmAttributes = 0x0005  <ASYNC-ISOCHRONOUS>
        wMaxPacketSize = 0x0240 
        bInterval = 0x0001 
        bRefresh = 0x0000 
        bSynchAddress = 0x0000 

      Additional Descriptor

      bLength = 0x08
      bDescriptorType = 0x25
      bDescriptorSubType = 0x01
       RAW dump: 
       0x00 | 0x08, 0x25, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00


     Endpoint 1
        bLength = 0x0007 
        bDescriptorType = 0x0005 
        bEndpointAddress = 0x0081  <IN>
        bmAttributes = 0x0011  <ISOCHRONOUS>
        wMaxPacketSize = 0x0004 
        bInterval = 0x0004 
        bRefresh = 0x0000 
        bSynchAddress = 0x0000 


    Interface 2
      bLength = 0x0009 
      bDescriptorType = 0x0004 
      bInterfaceNumber = 0x0002 
      bAlternateSetting = 0x0000 
      bNumEndpoints = 0x0000 
      bInterfaceClass = 0x0001  <Audio device>
      bInterfaceSubClass = 0x0002 
      bInterfaceProtocol = 0x0020 
      iInterface = 0x000a  <X18/XR18 Audio In>


    Interface 2 Alt 1
      bLength = 0x0009 
      bDescriptorType = 0x0004 
      bInterfaceNumber = 0x0002 
      bAlternateSetting = 0x0001 
      bNumEndpoints = 0x0001 
      bInterfaceClass = 0x0001  <Audio device>
      bInterfaceSubClass = 0x0002 
      bInterfaceProtocol = 0x0020 
      iInterface = 0x000b  <X18/XR18 Audio In>

      Additional Descriptor

      bLength = 0x10
      bDescriptorType = 0x24
      bDescriptorSubType = 0x01
       RAW dump: 
       0x00 | 0x10, 0x24, 0x01, 0x16, 0x00, 0x01, 0x01, 0x00, 
       0x08 | 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x32


      Additional Descriptor

      bLength = 0x06
      bDescriptorType = 0x24
      bDescriptorSubType = 0x02
       RAW dump: 
       0x00 | 0x06, 0x24, 0x02, 0x01, 0x04, 0x18


     Endpoint 0
        bLength = 0x0007 
        bDescriptorType = 0x0005 
        bEndpointAddress = 0x0082  <IN>
        bmAttributes = 0x0005  <ASYNC-ISOCHRONOUS>
        wMaxPacketSize = 0x0240 
        bInterval = 0x0001 
        bRefresh = 0x0000 
        bSynchAddress = 0x0000 

      Additional Descriptor

      bLength = 0x08
      bDescriptorType = 0x25
      bDescriptorSubType = 0x01
       RAW dump: 
       0x00 | 0x08, 0x25, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00



    Interface 3
      bLength = 0x0009 
      bDescriptorType = 0x0004 
      bInterfaceNumber = 0x0003 
      bAlternateSetting = 0x0000 
      bNumEndpoints = 0x0000 
      bInterfaceClass = 0x0001  <Audio device>
      bInterfaceSubClass = 0x0001 
      bInterfaceProtocol = 0x0000 
      iInterface = 0x0000  <no string>

      Additional Descriptor

      bLength = 0x09
      bDescriptorType = 0x24
      bDescriptorSubType = 0x01
       RAW dump: 
       0x00 | 0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 
       0x08 | 0x01


    Interface 4
      bLength = 0x0009 
      bDescriptorType = 0x0004 
      bInterfaceNumber = 0x0004 
      bAlternateSetting = 0x0000 
      bNumEndpoints = 0x0002 
      bInterfaceClass = 0x0001  <Audio device>
      bInterfaceSubClass = 0x0003 
      bInterfaceProtocol = 0x0000 
      iInterface = 0x0000  <no string>

      Additional Descriptor

      bLength = 0x07
      bDescriptorType = 0x24
      bDescriptorSubType = 0x01
       RAW dump: 
       0x00 | 0x07, 0x24, 0x01, 0x00, 0x01, 0x3d, 0x00


      Additional Descriptor

      bLength = 0x06
      bDescriptorType = 0x24
      bDescriptorSubType = 0x02
       RAW dump: 
       0x00 | 0x06, 0x24, 0x02, 0x01, 0x33, 0x00


      Additional Descriptor

      bLength = 0x06
      bDescriptorType = 0x24
      bDescriptorSubType = 0x02
       RAW dump: 
       0x00 | 0x06, 0x24, 0x02, 0x02, 0x34, 0x52


      Additional Descriptor

      bLength = 0x09
      bDescriptorType = 0x24
      bDescriptorSubType = 0x03
       RAW dump: 
       0x00 | 0x09, 0x24, 0x03, 0x01, 0x37, 0x01, 0x34, 0x01, 
       0x08 | 0x00

      Additional Descriptor

      bLength = 0x09
      bDescriptorType = 0x24
      bDescriptorSubType = 0x03
       RAW dump: 
       0x00 | 0x09, 0x24, 0x03, 0x02, 0x38, 0x01, 0x33, 0x01, 
       0x08 | 0x53

     Endpoint 0
        bLength = 0x0009 
        bDescriptorType = 0x0005 
        bEndpointAddress = 0x0083  <IN>
        bmAttributes = 0x0002  <BULK>
        wMaxPacketSize = 0x0200 
        bInterval = 0x0000 
        bRefresh = 0x0000 
        bSynchAddress = 0x0000 

      Additional Descriptor

      bLength = 0x05
      bDescriptorType = 0x25
      bDescriptorSubType = 0x01
       RAW dump: 
       0x00 | 0x05, 0x25, 0x01, 0x01, 0x37


     Endpoint 1
        bLength = 0x0009 
        bDescriptorType = 0x0005 
        bEndpointAddress = 0x0002  <OUT>
        bmAttributes = 0x0002  <BULK>
        wMaxPacketSize = 0x0200 
        bInterval = 0x0000 
        bRefresh = 0x0000 
        bSynchAddress = 0x0000 

      Additional Descriptor

      bLength = 0x05
      bDescriptorType = 0x25
      bDescriptorSubType = 0x01
       RAW dump: 
       0x00 | 0x05, 0x25, 0x01, 0x01, 0x33



    Interface 5
      bLength = 0x0009 
      bDescriptorType = 0x0004 
      bInterfaceNumber = 0x0005 
      bAlternateSetting = 0x0000 
      bNumEndpoints = 0x0000 
      bInterfaceClass = 0x00fe  <Application specific>
      bInterfaceSubClass = 0x0001 
      bInterfaceProtocol = 0x0001 
      iInterface = 0x0000  <no string>

      Additional Descriptor

      bLength = 0x07
      bDescriptorType = 0x21
      bDescriptorSubType = 0x07
       RAW dump: 
       0x00 | 0x07, 0x21, 0x07, 0xfa, 0x00, 0x40, 0x00

Meka, according to USB descriptor your audio interface is fine with 1ms intervals (bInterval = 0x0004). So that's not the problem. And the latency doesn't even change without this patch, we know that worked before. Did you replug the device after you changed the buffer_ms sysctl? New buffer_ms values may not become effective immediately, depending on your settings.

I see what you mean. I tested with buffer_ms of 1 and 8 and I get almost exact numbers:

period = 192

buffer_ms = 1
  1313.945 frames     27.374 ms total roundtrip latency
	extra loopback latency: 737 frames
	use 368 for the backend arguments -I and -O

buffer_ms = 8
  1314.945 frames     27.395 ms total roundtrip latency
	extra loopback latency: 738 frames
	use 369 for the backend arguments -I and -O

I can do any other period, but it's almost the same. These are the commands I used

killall -9 jackdbus
sysctl hw.usb.uaudio.buffer_ms=8
jack_control start
jack_iodelay # and connect it in patchage
jack_control stop

This is with the patch applied.

I didn't answer about re-plugging. No, the interface is always connected and I don't touch the cables.

I didn't answer about re-plugging. No, the interface is always connected and I don't touch the cables.

It may be necessary to detach the device for the buffer_ms sysctl to become effective. Otherwise, the vchan system can prevent a reset of the blocksize, which triggers the buffer resize.

If you don't want to mess with cables, I think your device has a power button (speakers down first! ;-) ). As an alternative, reboot with buffer_ms change set in /boot/loader.conf.

A misunderstanding. I though you wanted me to make sure there are no reconnects. Anyway, loading the driver first, then setting buffer_ms and then turning on the interface yields the following results

period = 192
buffer_ms = 1
  690.921 frames     14.394 ms total roundtrip latency
	extra loopback latency: 306 frames
	use 153 for the backend arguments -I and -O

Now this is a great improvement! Can we document it somewhere? Suggesting, for example, to put kld_list+=snd_uaudio and then set buffer_ms as any other sysctl? In my oppinion, if we spent so much time figuring out how to properly do the measurement, I'm sure other people will need help with their setup, too, so why not make it easier for them and put this into snd_uaudio(4).

That's a great improvement. LGTM.

This revision is now accepted and ready to land.Jan 28 2024, 2:51 PM

@christos: Thanks! Since @mav didn't participate here, would someone else be so kind to commit this? Unless there's other issues that weren't brought up yet.

@meka_tilda.center Sorry about the extra work, I somehow thought you would know that. The handling of buffer_ms didn't change in this regard. My approach was to communicate it simply as a /boot/loader.conf tunable. IMHO that's more digestible by end users, less error prone. Tell them to (re-)attach the device only after buffer_ms is set would probably also work. But kld_list+=snd_uaudio and /etc/sysctl.conf come too late, due to snd_uaudio being automatically loaded early in the boot process. You'd have to explicitly kldunload snd_uaudio at that point to make the devices reattach.

Anyway, I'm happy to see the measurements make sense now :-)

Interesting. I just tested, and if I boot my machine while interface is on, kld_list and buffer_ms together do the trick. I mean, jack_iodelay reports exactly same latency as if I loaded the module, set buffer_ms and then turned on the interface. Maybe my interface is slow to respond initially, so sysctl has time to kick in? Anyway, if I were to do it differently, what should I do? Load module and set buffer_ms in loader.conf?

EDIT: I only now saw your freebsd_jack_notes link. Sorry for the noise, nice article which explains a lot!

@christos: Thanks! Since @mav didn't participate here, would someone else be so kind to commit this? Unless there's other issues that weren't brought up yet.

I will commit it.

@meka_tilda.center Sorry about the extra work, I somehow thought you would know that. The handling of buffer_ms didn't change in this regard. My approach was to communicate it simply as a /boot/loader.conf tunable. IMHO that's more digestible by end users, less error prone. Tell them to (re-)attach the device only after buffer_ms is set would probably also work. But kld_list+=snd_uaudio and /etc/sysctl.conf come too late, due to snd_uaudio being automatically loaded early in the boot process. You'd have to explicitly kldunload snd_uaudio at that point to make the devices reattach.

I agree with @meka_tilda.center on this. We should have a man page entry for this as it's not really obvious.

@christos: Thanks! Since @mav didn't participate here, would someone else be so kind to commit this? Unless there's other issues that weren't brought up yet.

I will commit it.

@meka_tilda.center Sorry about the extra work, I somehow thought you would know that. The handling of buffer_ms didn't change in this regard. My approach was to communicate it simply as a /boot/loader.conf tunable. IMHO that's more digestible by end users, less error prone. Tell them to (re-)attach the device only after buffer_ms is set would probably also work. But kld_list+=snd_uaudio and /etc/sysctl.conf come too late, due to snd_uaudio being automatically loaded early in the boot process. You'd have to explicitly kldunload snd_uaudio at that point to make the devices reattach.

I agree with @meka_tilda.center on this. We should have a man page entry for this as it's not really obvious.

See D43649.

Interesting. I just tested, and if I boot my machine while interface is on, kld_list and buffer_ms together do the trick. I mean, jack_iodelay reports exactly same latency as if I loaded the module, set buffer_ms and then turned on the interface. Maybe my interface is slow to respond initially, so sysctl has time to kick in?

You may be right, it's possible that /etc/sysctl.conf settings are already present when kld_list and autoload of snd_uaudio happen. I only remember that setting buffer_ms in /etc/sysctl.conf didn't have any effect for me - but I probably had snd_uaudio explicitly loaded in /boot/loader.conf due to USB quirks needed.