FreeBSD audio diagnostics and optimization


A comprehensive guide to audio diagnostics and optimization in FreeBSD, especially for USB DAC devices and music interfaces operating in bitperfect mode and real-time. Solid documentation of practical solutions for sound system analysis and calibration.

For professional and audiophile applications, where every bit and every millisecond counts, sound system diagnostics becomes a key element in achieving optimal playback quality. Therefore, knowledge of diagnostic methods and the ability to interpret their results allows for making informed decisions based on measurable parameters rather than subjective impressions.

It also happens that while striving for optimal system parameters, we introduce changes that complicate the audio configuration or produce unexpected results. FreeBSD offers exceptional capabilities in the audio domain: detailed control over sound subsystem parameters, process handling, USB power management, and CPU states, allowing precise system tuning to meet professional audio equipment requirements.

A detailed guide on sound configuration in FreeBSD I described in the article: FreeBSD and hi-fi audio setup: bit-perfect, equalizer, real-time.

 

 

Gathering information about the sound system and audio device

Accurate diagnostics and analysis requires a complete picture of system parameters and audio device configuration. FreeBSD offers several complementary tools that together provide complete information about the audio subsystem state.

 

/dev/sndstat analysis

The primary diagnostic tool in FreeBSD for sound is the hw.snd syscall and the /dev/sndstat file, which provides detailed information about detected audio devices and their current state. Before reading, it’s worth enabling full verbose mode for a more complete picture:

# cat /dev/sndstat

Installed devices:
pcm0:  (play)
pcm1:  (play)
pcm2:  (play)
pcm3:  (play)
pcm4:  (play/rec)
pcm5:  (play)
pcm6:  (play/rec) default

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

Selected output for the main audio interface: Focusrite Scarlett Solo 4th Gen:

pcm6:  on uaudio0 (1p:0v/1r:0v) default
        snddev flags=0x20fc
        [dsp6.play.0]: spd 44100, fmt 0x00201000, flags 0x2000014c, 0x00000001, pid 2032 (musicpd)
                interrupts 8678947, underruns 0, feed 8678946, ready 16384
                [b:2832/1416/2|bs:16384/2048/8]
                channel flags=0x2000014c
                {userland} -> feeder_root(0x00201000) -> {hardware}
        [dsp6.record.0]: spd 48000, fmt 0x00200010/0x00401000, flags 0x00000000, 0x00000063
                interrupts 0, overruns 0, feed 0, hfree 6144, sfree 32768
                [b:6144/3072/2|bs:32768/256/128]
                channel flags=0x0
                {hardware} -> feeder_root(0x00401000) -> feeder_format(0x00401000 -> 0x00400010) -> feeder_matrix(4.0 -> 2.0) -> feeder_volume(0x00200010) -> {userland}

Key elements:

Device header: pcm6: (1p:0v/1r:0v) default provides information about the PCM device number, hardware name, number of physical and virtual channels (1 play channel, 0 virtual / 1 record channel, 0 virtual), and default device status.

Device flags (snddev flags):

  • SOFTPCMVOL — software PCM volume control
  • BUSY — device currently in use
  • MPSAFE — safe for multithreading (symmetric multiprocessing)
  • REGISTERED — device properly registered in the sound system
  • BITPERFECT — no format conversion, data transmitted 1:1 to hardware
  • VPC — Volume Per Channel, independent volume control for each application
  • RVCHANS — Record Virtual Channels, support for virtual recording channels

Play channel parameters:

  • spd 44100, fmt 0x00201000 — 44.1 kHz sample rate, s32le:2.0 format (32-bit signed little-endian, stereo)
  • pid 2020 (musicpd) — process and application name using the device
  • interrupts 361669, underruns 0 — number of interrupts and underruns (xruns – an important parameter, discussed later in the article)

Buffer structure: [b:2832/1416/2|bs:4096/128/32], where the first part is the hardware buffer (2832 bytes / 1416 frames / 2 blocks), and the second is the software buffer (4096 bytes / 128 frames / 32 blocks).

Channel flags:

  • RUNNING — channel actively processing audio
  • TRIGGERED — channel triggered to start (precise control of playback start moment)
  • NBIO — non-blocking I/O, operations don’t block the process
  • BITPERFECT — data passes to the device without conversion and resampling

Feeder chain: {userland} -> feeder_root(0x00201000) -> {hardware} shows a simple signal path — data from the application goes directly to hardware without intermediate processing. This is the desired state for bitperfect mode.

 

Sound driver settings – hw.snd

In addition to information from /dev/sndstat, it’s worth checking the global settings of the FreeBSD sound(4) sound subsystem, which affect all audio devices:

# sysctl hw.snd

From the resulting parameters in the context of sound system diagnostics, we will be particularly interested in:

  • hw.snd.latency: 2 — global latency profile (0-10, where lower values = less latency and smaller buffers)
  • hw.snd.latency_profile: 1 — latency profile enabled
  • hw.snd.syncdelay: -1 — synchronization delay (-1 = automatic)
  • hw.snd.usefrags: 0 — buffer fragmentation mode (0 = disabled)
  • hw.snd.timeout: 5 — audio operation timeout in seconds

In bitperfect mode – dev.pcm.X.bitperfect=1 many of the remaining output parameters are bypassed — resampling, vchan mixing, and format conversion don’t occur, and audio data goes directly to hardware. Resampler parameters (feeder_rate_*) only matter when bitperfect is disabled or a given application uses a sample rate not natively supported by the device.

 

Focusrite Scarlett Solo 4th Gen, PreSonus AudioBox iOne, Cambridge Audio DacMagic Plus, and Trends UD-10.1 Lite with original Burr-Brown PCM2704 chip.

The list of USB audio devices I used while thoroughly exploring FreeBSD’s sound subsystem architecture is long. Here are some of them: Focusrite Scarlett Solo 4th Gen, PreSonus AudioBox iOne, Cambridge Audio DacMagic Plus, and Trends UD-10.1 Lite with the original, good old Burr-Brown PCM2704 chip.

 

Working with sndctl(8)

Another useful tool for audio diagnostics is the sndctl(8) application, which has been available in FreeBSD since version 14.0. It effectively operates on data retrieved from /dev/snd and provides a complete set of organized information about the capabilities and current configuration of a specific audio device. When invoked without additional parameters, it shows the state of the main (default) device:

# sndctl -v

pcm6:  on uaudio0 (play/rec)
    name                = pcm6
    desc                = Focusrite Scarlett Solo 4th Gen
    status              = on uaudio0
    devnode             = dsp6
    from_user           = 0
    unit                = 6
    caps                = INPUT,MMAP,OUTPUT,REALTIME,TRIGGER
    bitperfect          = 1
    autoconv            = 0
    realtime            = 0
    play.format         = s32le:2.0
    play.rate           = 44100
    play.vchans         = 0
    play.min_rate       = 44100
    play.max_rate       = 192000
    play.min_chans      = 2
    play.max_chans      = 2
    play.formats        = s32le
    rec.rate            = 48000
    rec.format          = s16le:2.0
    rec.vchans          = 1
    rec.min_rate        = 44100
    rec.max_rate        = 192000
    rec.min_chans       = 4
    rec.max_chans       = 4
    rec.formats         = s32le
    dsp6.play.0
        parentchan          =
        unit                = 0
        caps                = MMAP,OUTPUT,REALTIME,TRIGGER
        latency             = 2
        rate                = 44100
        format              = s32le:2.0
        pid                 = 2049
        proc                = musicpd
        interrupts          = 1969614
        xruns               = 0
        feedcount           = 1969613
        volume              = 0.45:0.45
        hwbuf.format        = s32le:2.0
        hwbuf.rate          = 44100
        hwbuf.size_bytes    = 2832
        hwbuf.size_frames   = 354
        hwbuf.blksz         = 1416
        hwbuf.blkcnt        = 2
        hwbuf.free          = 0
        hwbuf.ready         = 2832
        swbuf.format        = s32le:2.0
        swbuf.rate          = 44100
        swbuf.size_bytes    = 16384
        swbuf.size_frames   = 2048
        swbuf.blksz         = 2048
        swbuf.blkcnt        = 8
        swbuf.free          = 1416
        swbuf.ready         = 14968
        feederchain         = [userland -> feeder_root(s32le:2.0) -> hardware]

 

In addition to parameters we already know from /dev/sndstat and hw.snd output, it’s worth noting the following:

caps = INPUT,MMAP,OUTPUT,REALTIME,TRIGGER

Capabilities and functions of the pcm6 device, here Focusrite Scarlett Solo 4th Gen, in other words the sum of all channel capabilities:

  • INPUT – device supports recording (rec channel).
  • OUTPUT – device supports playback (play channel).
  • MMAP – both directions support buffer memory mapping.
  • REALTIME – device supports real-time priority operations.
  • TRIGGER – both directions support precise triggering.

 

bitperfect = 1

Confirmation that the device is operating in FreeBSD sound driver bitperfect mode.

 

realtime = 0

The realtime field is somewhat misleading — it’s not about operating system real-time mode nor does it refer to audio device or channel parameters. realtime is an internal sndctl(8) parameter controlling a set of three system parameters affecting buffering and timer precision. Value realtime=0 means standard settings, value 1 — configuration optimized for minimal latency, changing the following sysctls:

  • hw.snd.latency=0 — controls default audio buffer size. Value 0 means minimal buffering, higher values increase buffer at the cost of latency.
  • hw.snd.latency_profile=0 — selects latency profile. Value 0 is aggressive profile (smaller buffers), value 1 is conservative profile (larger buffers, fewer xruns).
  • kern.timecounter.alloweddeviation=0 — specifies allowable system clock deviation in microseconds. Value 0 enforces maximum precision, which is important for audio stream synchronization. Lower latency increases xrun risk without noticeable benefits, causing audible artifacts during playback or recording (more about alloweddeviation and xruns later in the article).

 

caps = MMAP,OUTPUT,REALTIME,TRIGGER (dsp6.play.0)

Play channel capabilities for pcm6 device, indicating full support for low-latency audio:

  • MMAP – channel supports buffer memory mapping to application. Instead of write(), the application writes directly to shared buffer — less copying, lower latency.
  • OUTPUT – currently supported channel — audio playback. For recording it would be INPUT.
  • REALTIME – channel supports low-latency operation with small buffers. Driver guarantees predictable timing.
  • TRIGGER – channel supports precise start/stop triggering. Application can fill the buffer, then atomically start playback at the exact moment — useful for synchronizing multiple streams.

 

latency = 2

Latency profile – as above, for hw.snd.

 

interrupts = 1969614
feedcount = 196961

The interrupts and feedcount fields in sndctl output are counters showing audio channel activity since it was started:

  • interrupts is the number of hardware interrupts generated by the audio device. Each interrupt signals that hardware has processed a data block from the buffer and is ready for the next one. At 44100 Hz sample rate and 1416 byte block size (354 frames), interrupts occur approximately 125 times per second.
  • feedcount is the number of data blocks delivered by the feeder chain to the hardware buffer. In a properly functioning system, this value should be close to the interrupt count — each interrupt should result in delivery of a new data block.

The difference between these values is diagnostically significant. In the presented example interrupts = 1969614 and feedcount = 1969613 — the difference is 1 and most likely results from sampling timing, meaning the system keeps up with data delivery. A larger discrepancy could indicate performance issues and potential audio artifacts, also resulting from xruns.

 

It’s also worth noting and comparing the sample format in play.format (s32le:2.0 — where s32le is the format and 2.0 is the channel layout) with the list of native formats in play.formats (s32le). This allows verification of whether conversion occurs. When the sample format is present in the native formats list, data doesn’t require processing by the conversion feeder. Similarly, play.rate within the min_rate–max_rate range confirms that the device supports the given sample rate natively without resampling.

 

xruns = 0

xruns is the last and perhaps most important system parameter for audio diagnostics and troubleshooting in bitperfect and real-time modes. More in the section below.

 

Similar to a mixing console in a professional recording studio, FreeBSD offers an extensive audio subsystem control panel - dozens of parameters allowing precise tuning of every aspect of the signal path.

Similar to a mixing console in a professional recording studio, FreeBSD offers an extensive audio subsystem control panel – dozens of parameters allowing precise tuning of every aspect of the signal path (real photo by Yassine Khalfalli / Unsplash)

 

 

Xruns – audio stability indicator

The xruns indicator, available from FreeBSD kernel data, is a measurable value of potential audible audio artifacts. During system or audio device calibration, especially USB DACs for bitperfect or low-latency operation, audible crackles, clicks, or dropouts may occur during playback or recording. The most common cause is xruns (buffer underrun / overrun), situations where the system can’t keep up with delivering audio data to the device:

  • underrun (playback) – application doesn’t deliver data fast enough — buffer empties, artifacts or silence are heard.
  • overrun (recording) – application doesn’t receive data fast enough — buffer overflows, data is lost.

The cause is most often poorly chosen latency and USB buffer values. An xruns value other than zero clearly indicates this problem.

The xruns parameter can be read fastest using /dev/sndstat (with verbose=4 mode enabled), checking the underruns and overruns fields:

# grep -E 'under|over' /dev/sndstat

 

The xruns parameter for a specific device can also be verified using sndctl(8):

# sndctl -v | grep xruns

 

We can run a command in a loop that displays the xruns parameter value when it occurs and changes, along with a timestamp, which facilitates analysis during listening:

while true; do
  sndctl -v | awk -F= -v t="$(date +%H:%M:%S)" '/xruns/ && $2 != 0 {print t, $0}'
  sleep 1
done

 

I also published my own program on GitHub: xruns shell script, parsing sndctl(8) output:

# xruns -h
usage: xruns [-d device] [-p] [-w] [-i interval]

Options:
  -d N      Monitor device pcmN (default: system default)
  -p        Show only playback channels
  -w        Watch mode - loop and show only changes
  -i SEC    Interval in seconds for watch mode (default: 1)
  -h        Show this help

Examples:
  xruns              Show xruns for default device
  xruns -d 1         Show xruns for pcm1
  xruns -d 0 -p      Show only playback xruns for pcm0
  xruns -d 0 -p -w   Watch playback xruns on pcm0

Usage examples and capabilities:

# Show xruns for default audio device
xruns

# Show xruns for pcm6
xruns -d 6

# Show only play channel xruns for pcm6
xruns -d 6 -p

# Watch mode — monitor changes in real-time
xruns -w

# Monitor playback xruns on pcm6, check every 2 seconds
xruns -d 6 -p -w -i 

 

 

Latency parameters for sound(4)

The hw.snd.latency and hw.snd.latency_profile parameters of the FreeBSD sound(4) sound driver control audio buffer sizes, but not in a way we might intuitively expect. Instead of directly setting latency in milliseconds, they operate on conversion tables built into the sound(4) subsystem.

How the parameters work:

  • hw.snd.latency_profile defines buffering strategy: 0 – aggressive profile (low latency, underrun risk under high load); 1 – moderate/safe profile (default, balanced).
  • hw.snd.latency specifies latency level in range 0-10: 0 – lowest latency; 2 – low but stable latency (recommended for most uses); 10 – highest latency.

 

Typical music playback (default):

  • hw.snd.latency: 2
  • hw.snd.latency_profile: 1

Professional applications (recording, monitoring):

  • hw.snd.latency: 0-1
  • hw.snd.latency_profile: 0

Requires stable system load, otherwise audio dropouts may occur.

Maximum stability (weak machines, high load):

  • hw.snd.latency: 4-7
  • hw.snd.latency_profile: 1

Remember that actual latency depends on the combination of hardware and software buffers and sample rate. System parameters are only hints for applications that don’t specify their own buffering preferences. Professional audio programs (JACK, DAW) set their own buffer sizes, ignoring hw.snd.latency.

 

 

The alloweddeviation kernel parameter

kern.timecounter.alloweddeviation is a FreeBSD kernel parameter that controls the maximum allowable deviation between software and hardware clocks before forcing synchronization. This is an important parameter for audio applications, where timing precision directly affects playback quality.

In short, the kern.timecounter.alloweddeviation mechanism works as follows: BSD family systems use software clocks for process wakeup synchronization. The alloweddeviation parameter specifies (in nanoseconds) the maximum allowed drift before correction. A lower value means tighter synchronization and more precise process wakeups. More on this topic in Paul Herman’s article.

Below are process wakeup latency test results using Paul Herman’s benchmark for the default value of 5 ms (kern.timecounter.alloweddeviation=5):

# ./wakeup_latency
0.007341
0.014410
0.037370
0.037336
0.037375
0.037345
0.037356

 

After setting kern.timecounter.alloweddeviation=0:

# sysctl kern.timecounter.alloweddeviation=0
# ./wakeup_latency
0.000061
0.000095
0.000077
0.000093
0.000096
0.000090
0.000062

 

Reducing wakeup latency from ~37 microseconds to ~0.09 microseconds is an improvement of approximately 400x. For audio applications, this means much more precise data delivery to buffers.

To enable maximum clock precision, add to /etc/sysctl.conf:

kern.timecounter.alloweddeviation=0

 

 

USB Polling Rate

In the context of USB audio devices (DAC, audio interfaces), the term polling rate refers to the frequency at which the operating system queries the device for audio data. It affects playback quality by reducing jitter and improving data stream stability.

FreeBSD controls USB audio polling through the hw.usb.uaudio.buffer_ms parameter (default 4 ms), which specifies the period of audio data processed at once. FreeBSD documentation warns that too low buffer_ms may cause “audible dropouts due to frequent CPU wakeups”. However, in practice:

  • hw.usb.uaudio.buffer_ms=2: safe for most systems, good balance.
  • hw.usb.uaudio.buffer_ms=4: default, most stable.
  • hw.usb.uaudio.buffer_ms=8: for weaker systems or power saving (unacceptable for audio).

Therefore, buffer_ms=2 provides the most regular and stable data stream to the DAC device without xrun risk. Lower values may cause audible dropouts.

 

 

USB transfer diagnostics

In addition to the xruns parameter, we have other important and measurable parameters that can be correlated with potential audio artifacts: UE_CONTROL_FAIL and UE_ISOCHRONOUS_FAIL. They account for data and control transfer errors (e.g., sample rate change, volume, routing). Parameters can be read using usbconfig(8).

List of installed USB devices:

# usbconfig list
ugen0.1:  at usbus0, cfg=0 md=HOST spd=SUPER (5.0Gbps) pwr=SAVE (0mA)
ugen0.2:  at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=ON (500mA)
ugen0.3:  at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE (0mA)
ugen0.4:  at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=ON (0mA)
ugen0.5:  at usbus0, cfg=0 md=HOST spd=SUPER (5.0Gbps) pwr=SAVE (0mA)

 

Parameters for the selected device:

# usbconfig -d 0.4 dump_stats
ugen0.4:  at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=ON (0mA)

{
    UE_CONTROL_OK       : 294
    UE_ISOCHRONOUS_OK   : 8660417
    UE_BULK_OK          : 23581
    UE_INTERRUPT_OK     : 0
    UE_CONTROL_FAIL     : 0
    UE_ISOCHRONOUS_FAIL : 0
    UE_BULK_FAIL        : 0
    UE_INTERRUPT_FAIL   : 0
}

What does this mean in practice:

  • UE_ISOCHRONOUS_FAIL – most important in the context of the audio interface. Is an error flag in the FreeBSD USB driver indicating an isochronous transfer failure. Isochronous transfers are a USB mode used by devices requiring constant, guaranteed real-time data flow, primarily audio interfaces and video cameras. This error occurs when:
    • Insufficient USB bandwidth – too many devices on a single controller, or a USB hub unable to handlethe required transfer rate.
    • Timer/interrupt issues – the system cannot keep up with processing packets at the required intervals.
    • Driver incompatibility – the driver doesn’t properly support the device’s specific isochronous mode.
    • Hardware problems – faulty cable, port, or USB controller itself.
  • UE_BULK_FAIL – bulk transfer errors. Used for large amounts of data without timing requirements, e.g. flash drives, printers, scanners.
  • UE_INTERRUPT_FAIL – interrupt transfer errors. Used for small, sporadic data requiring quick response, e.g. keyboards, mice, buttons on audio interfaces.

Zero results for UE_CONTROL_FAIL and UE_ISOCHRONOUS_FAIL parameters indicate no problems and also confirm listening test results. Otherwise, the hw.usb.uaudio.buffer_ms parameter should be adjusted, timer/interrupt issues should be investigated (the system may not be keeping up with processing packets at the required intervals), or the physical USB device connection to the computer should be examined.

 

 

Tracking USB audio interrupts

Diagnosing USB audio problems in FreeBSD often comes down to one question: are hardware interrupts being handled fast enough? Before looking for culprits in buffers, the scheduler, or DAC configuration, it’s worth examining the most fundamental level: system interrupts.

The vmstat -i command displays interrupt statistics on FreeBSD. For USB audio diagnostics, two things are crucial: the number of interrupts generated by the USB controller and the time spent handling them.

Example:

$ vmstat -i
interrupt                          total       rate
irq256: xhci0                    1847293        612
irq257: hdac0                      28451          9
...

The xHCI controller (USB 3.0) generates interrupts for all connected devices, including audio interfaces. A high rate value alongside audio problems may indicate system overload from interrupt handling or conflicts with other devices on the same interrupt line.

 

What interrupts tell us about audio stability

A USB Audio Class 2.0 interface running at 48 kHz generates a predictable number of interrupts – typically one per isochronous packet, meaning approximately 1000 per second for USB 2.0 (1 ms interval) or 8000 for USB 3.0 (125 μs interval). Deviations from these values signal problems.

Too few interrupts means dropped packets – the DAC isn’t receiving data in time. Too many may indicate retransmissions or device enumeration issues. Both situations manifest as potential playback artifacts, xruns, and certainly a rising UE_ISOCHRONOUS_FAIL counter.

Interrupt analysis helps detect non-obvious hardware conflicts. A USB controller often handles all connected devices – keyboard, mouse, flash drive, and DAC – on a shared interrupt line. HID devices with high polling rates (some gaming keyboards and mice operating at 1000 Hz) generate a constant stream of interrupts that competes with isochronous audio transfers.

Comparing rate values before and after disconnecting individual USB devices helps identify problematic peripherals. Physically moving the DAC to a different USB controller (if the system has multiple) often resolves the issue without deeper configuration changes.

For monitoring and profiling the USB audio path – tracking xruns, UE_ISOCHRONOUS_FAIL indicators, and spikes in USB interface interrupts, along with their mutual interference and correlation, I wrote a script sndchk.sh, available for download from repository on GitHub. More information and usage instructions available in the repository and in paragraph below: Audio path monitoring – sndchk.sh script.

 

 

USB power saving mode

USB port power management is also one of the elements that can cause audible artifacts (clicks, crackles, pops) in USB DAC devices, especially when the system tries to suspend inactive USB devices or when there’s a delay in data delivery.

The hw.usb.power_timeout=0 parameter in FreeBSD disables automatic USB port power monitoring, which remain active at all times, regardless of connected device activity. This ensures connection stability with DACs – external digital-to-analog converters maintain a constant connection without the re-identification process.

hw.usb.power_timeout is added to the /etc/sysctl.conf file.

The second option is to completely disable power saving parameters for USB devices and USB hubs. FreeBSD by default enables power_save mode for USB hubs and allows devices to enter sleep state.

For example, in the presented configuration, the Focusrite Scarlett Solo 4th Gen sits behind hub ugen0.3 (USB 2.1, 480Mbps). If the hub enters power_save mode, it may affect the stability of devices connected to it.

To disable power_save mode for individual USB devices and hubs, we use the usbconfig(8) tool.

First, check the power state of USB devices:

# usbconfig

ugen0.1:  at usbus0, cfg=0 md=HOST spd=SUPER (5.0Gbps) pwr=SAVE (0mA)
ugen0.2:  at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=ON (500mA)
ugen0.3:  at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE (0mA)
ugen0.4:  at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=ON (0mA)
ugen0.5:  at usbus0, cfg=0 md=HOST spd=SUPER (5.0Gbps) pwr=SAVE (0mA)

 

The pwr=SAVE labels indicate devices operating in power saving mode: ugen0.1 — root hub (XHCI), ugen0.3 — HP USB2.1 Hub, and ugen0.5 — HP USB3.1 Hub.

To disable power_save mode for a specific device, run:

# usbconfig -d ugen0.3 power_on

 

We can also use the devd(8) service to automatically apply power_save mode during startup and device connection.

First, check the id of devices we want to add to the power_save exclusion group:

# usbconfig -d ugen0.3 dump_device_desc | grep -E "idVendor|idProduct"
  idVendor = 0x03f0
  idProduct = 0x1847

# usbconfig -d ugen0.5 dump_device_desc | grep -E "idVendor|idProduct"
  idVendor = 0x03f0
  idProduct = 0x0620

 

The devd(8) daemon reads all files in /etc/devd/*.conf at startup. So we create the /etc/devd/usb-hubs.conf file and save our devices in it:

# File /etc/devd/usb-hubs.conf
#
# HP USB2.1 Hub
notify 100 {
    match "system" "USB";
    match "subsystem" "DEVICE";
    match "type" "ATTACH";
    match "vendor" "0x03f0";
    match "product" "0x1847";
    action "usbconfig -d $cdev power_on";
};

# HP USB3.1 Hub
notify 100 {
    match "system" "USB";
    match "subsystem" "DEVICE";
    match "type" "ATTACH";
    match "vendor" "0x03f0";
    match "product" "0x0620";
    action "usbconfig -d $cdev power_on";
};

After saving, we can verify our configuration by reloading the devd(8) daemon:

# service devd restart

and verify settings through usbconfig(8).

 

 

CPU C-states optimization for audio

If you experience audio artifacts or delays in FreeBSD, the cause may also be processor power saving states — so-called C-states. Audio equipment manufacturers also recommend disabling C-states, because transitions between states can cause dropouts.

What’s the problem with C-States? When the CPU is idle, the system can switch it to low power mode. However, waking from deeper C states takes microseconds, which for real-time audio can be critical — the buffer empties faster than the processor can deliver new samples.

Fortunately, FreeBSD has full control over C-states and allows programmatic control:

Check C-States for CPU:

# sysctl hw.acpi.cpu.cx_lowest

If the result is other than C1, force CPU to stay in highest performance state (C1):

# sysctl hw.acpi.cpu.cx_lowest=C1

If this solution proves helpful, add the setting to /etc/sysctl.conf permanently.

 

 

Audio path monitoring – sndchk.sh script

Diagnosing audio problems in FreeBSD requires simultaneous observation of multiple system layers – from audio buffers, through USB transfers, to hardware interrupts. For this purpose I created sndchk.sh, a tool that aggregates this information in real-time.

The script monitors three key areas:

  • Xruns — counts buffer underruns (playback) and overruns (recording). A non-zero or increasing value means the system can’t keep up with audio data flow. Typical causes include insufficient buffer size, CPU load, or incorrect latency settings.
  • USB errors – tracks UE_ISOCHRONOUS_FAIL, UE_CONTROL_FAIL, UE_BULK_FAIL and UE_INTERRUPT_FAIL counters. For USB audio, the isochronous parameter is critical, isochronous transfers are used for real-time audio streaming. Note that small increments (+2, +4) without audible artifacts may occur during track changes or sample rate switching — this is normal behavior.
  • IRQ spikes – detects anomalies in USB controller interrupt frequency. A sudden increase (e.g., 2x above baseline, 1.5x is default) often correlates with audio artifacts when other USB devices compete for bandwidth or the CPU is busy handling interrupts.

Example usage:

# List available audio devices
sndchk.sh

# Monitor default device
sndchk.sh -w

# Only xruns for pcm6
sndchk.sh -d 6 -xruns -w

# Only USB and IRQ with 2x threshold
sndchk.sh -usb -t 2.0 -w

 

The sndchk.sh script automatically maps pcm devices to their corresponding USB devices (ugen) and controllers (xhci/ehci), eliminating the need to manually trace relationships between system layers. For non-USB sound cards (PCI, built-in), the USB portion is automatically disabled and only xruns are monitored.

 

Terminal screenshot showing sndchk.sh monitoring a Focusrite Scarlett Solo 4th Gen USB audio interface during MPD playback.

sndchk.sh in action during MDP (musicpd) playback. The incremental UE_ISOCHRONOUS_FAIL counter increases (+2) are negligible – caused by manual stream interruption during track changes. All other system parameters are optimal.

 

Output appears only when a problem is detected – silence means everything is working correctly. Below is an example of the result during analysis of a miscalibrated system:

Monitoring pcm6:  (play/rec) default
USB device: ugen0.4
USB controller: xhci0 (irq64)
----------------------------------------
[10:22:06] Initial xruns: pcm6.play.0=0 pcm6.record.0=0
[10:22:06] Initial USB: CTRL=0 ISO=8 BULK=0 INT=0
[10:22:06] Initial IRQ: calibrating...
[10:22:16] xhci0 baseline: 7592/s
[10:23:47] xhci0: 7592 -> 15840/s (2.0x)
[10:23:47] UE_ISOCHRONOUS_FAIL: 8 -> 10 (+2)
[10:23:48] pcm6.play.0 xruns: 0 -> 2 (+2)

The tool is available on GitHub repository: sndchk.sh.

 

 

Summary

Inspired by Christos Margiolis’s article Vox FreeBSD: How Sound Works, I decided to write my own piece after nearly two years of working to deeply understand FreeBSD’s audio architecture. Previously, I had been using Linux-based solutions for over a dozen years. Today, I exclusively use FreeBSD for audiophile applications.

FreeBSD offers an extensive set of tools for audio subsystem diagnostics and optimization. Key information sources: /dev/sndstat, hw.snd.* parameters, sndctl(8), usbconfig(8), and vmstat(8) allow verification of device state, buffer structure, signal processing chain, interrupt statistics and USB device properties. I also recommend sndchk.sh script mentioned earlier.

Stable audio operation in bitperfect and low-latency modes requires consideration of several system layers:

  • sound(4) latency parameters
  • system clock precision
  • USB transfer and polling interval
  • USB interrupt handling efficiency
  • USB port and hub power management
  • CPU power saving states

Each of these layers affects the final result, and their combined optimization produces predictable and measurably correct results. FreeBSD provides tools to control each of them, skillful interpretation remains on our side.

 

Cover real photography by Anthony Torres / Unsplash, modified



Source link

Leave a Reply

Your email address will not be published. Required fields are marked *