New notebook – The verdict

Well, Phoronix did a review of a similar machine and apparently it’s far from being a slouch and also has the fastest integrated GPU currently available. More on that GPU later. So no regret when it comes to those performance benchmarks. Actually no regret at all, so far the notebook performs really well.

There are some more things worth mentioning that add up to the positive verdict besides all the pros I already mentioned in my earlier posts. There is the battery life which is still pretty good given the performant and power greedy CPU. It can run for hours when idling. When running Ardour it’s done in about two hours though but then I work with all the sluices wide open. But it charges pretty fast. Another thing that struck me is that the notebook is so much quieter than the old one. And the keyboard is just really nice now that I got a bit more used to it. And I managed to map the last media key, the stop one, to something useful with my old friend xdotool. Mapped this media key to the “stop/cancel” keycode using udev and added a keyboard shortcut in XFCE that gets triggered by this keycode and that executes a small script that looks like this:

#!/bin/bash

xdotool search --classname ardour_ardour key --delay 100 h space h

Now if Ardour is running and I press the Stop media key, xdotool sends the key sequence “h space h” to Ardour with a delay of 100ms between the key strokes. “h” sets the playhead to start, “space” stops the transport and another “h” to be sure the playhead is at the start position. Only thing that I’d like to add is some kind of detection if transport is running or not.

Also managed to achieve an even lower latency with my USB interface by adding the option implicit_fb=1 to the snd-usb-audio kernel module. This not only gets rid of the kernel ring buffer getting flooded with warnings but it also results in clean audio at 32*3/48, so 2ms of systemic latency. So it’s on par with my old notebook, albeit with some more headroom. Lower doesn’t seem to be possible, it results in slowed down, distorted audio.

So would I advise everyone doing Linux audio to get this notebook or a similar spec’d one? Well, there’s this GPU that still seems to be a bit too new, too shiny and too fast for the kernel I’m currently running (6.7.2) so I’m getting reliable crashes with software like OwlPlug and occasional crashes when connected via HDMI to a second screen. But it’s tolerable and it will probably get sorted out sooner or later. Other than that, this thing flies and hopefully I can do another decade with this machine.

Edit: worked around the GPU crashing by copying /usr/share/X11/xorg.conf.d/10-amdgpu.conf to /etc/X11/xorg.conf.d/10-amdgpu.conf and disabling hardware acceleration by adding the line Option "Accel" "off" to it. Content looks like this:

Section "OutputClass"
        Identifier "AMDgpu"
        MatchDriver "amdgpu"
        Driver "amdgpu"
        Option "HotplugDriver" "amdgpu"
        Option "Accel" "off"
EndSection

After restarting Xorg I can now open OwlPlug without issues.

New notebook – The verdict

New notebook – Doing real-time audio

Configuring this new machine took some effort but it can now run reliably with a few milliseconds of latency. First thing I did was installing a liquorix kernel as I have a good experience with those. Added the threadirqs kernel option to /etc/default/grub and updated the Grub configuration with sudo update-grub. After a reboot I was greeted with threaded IRQ’s.

Next step was to prioritize the desired IRQ’s, highest prio for the USB bus to which my audio interface is connected and also a high prio for the onboard audio. I decided to go the custom route as the tool I normally use for this, rtirq, prioritizes all USB threads and I only want the USB threads prioritized that do the audio work. Additional challenge was that these IRQ’s change on every boot. Concocted the following script snippet.

#!/bin/bash

# Prioritize USB port with sound card attached
# Since IRQ's change on every boot figure out IRQ dynamically

# Set maximum priority of the IRQ thread
prio=90

# Next IRQ thread found will get a priority
# decreased with the value set below
prio_step=5

# System paths to look for information
proc_path=/proc/asound
sys_pci_bus_path=/sys/class/pci_bus

# The logic - A for loop that does the following:
# * Iterates through all cards that are set by ALSA
# * Determines if it's an USB card
# * Sets priority on IRQ thread if this is the case,
    lowest card number gets the highest priority
for card_number in $(awk '/\[.*\]/ {print $1}' /proc/asound/cards); do

  if [ -e "$proc_path/card$card_number/usbid" -a -e "$proc_path/card$card_number/stream0" ]; then
    snd_dev_card=card$card_number
    snd_dev_pci_bus_ref=$(grep -Eo "usb-[^[:space:],-]+" $proc_path/$snd_dev_card/stream0 | sed "s/usb-\(.*\)/\1/")
    snd_dev_pci_bus_ref_short=$(awk -F ':' '{print $1":"$2}' <<<$snd_dev_pci_bus_ref)
    snd_dev_irq=$(cat $sys_pci_bus_path/$snd_dev_pci_bus_ref_short/device/$snd_dev_pci_bus_ref/irq)
    snd_dev_irq_pid=$(pgrep $snd_dev_irq-xhci)

    chrt -f -p $prio $snd_dev_irq_pid

    prio=$((prio-prio_step))
  fi

done

This snippet assumes the card numbers are set properly by assigning each card their own index value through the snd-usb-audio kernel module. This can be done with a file in /etc/modprobe.d/, i.e. /etc/modprobe.d/audio.conf. For my USB devices the relevant line in this file looks like this:

# RME Babyface, Edirol UA-25, Akai MPK Mini, Arturia BeatStep Pro, Behringer BCR2000
options snd-usb-audio index=0,1,5,6,7 vid=0x0424,0x0582,0x09e8,0x1c75,0x1397 pid=0x3fb7,0x0074,0x007c,0x0287,0x00bc

So my RME Babyface gets the lowest index (card number) and thus the highest real-time priority.

For onboard audio the situation was a bit trickier. The Lenovo comes with three different audio devices:

  • Onboard audio, speakers and TRRS jack
  • Digital audio, HDMI
  • Onboard mic

I wanted to index all properly so they don’t get in the way of my USB devices. For the onboard audio and HDMI this was no problem, adding the following line to /etc/modprobe.d/audio.conf was enough:

# Onboard audio
options snd-hda-intel index=10,11

Unfortunately you can’t discern between multiple devices with the snd-hda-intel driver and in my case both cards also have no model name so they show up as HD-Audio Generic cards with ID names Generic and Generic_1. Not very helpful. Luckily you can assign ID names dynamically after boot so I used that to give each card a proper ID name:

# Assign proper ID's to onboard devices
# Device with vendor ID 1002 is HDMI
# Device with vendor ID 1022 is onboard audio
for card in card{10,11}; do

  if grep -q 1002 /sys/class/sound/$card/device/vendor; then
    echo -n HDMI > /sys/class/sound/$card/id

  elif grep -q 1022 /sys/class/sound/$card/device/vendor; then
    echo -n ALC257 > /sys/class/sound/$card/id

    # Prioritize IRQ of onboard audio
    snd_dev_irq=$(cat /sys/class/sound/$card/device/irq)
    snd_dev_irq_pid=$(pgrep $snd_dev_irq-snd_hda_intel)

    chrt -f -p $prio $snd_dev_irq_pid
  fi

done

Now both cards can be used with their ID names (so hw:ALC257 for instance) and the onboard audio device gets prioritized with the $prio value set earlier on. Now the only culprit remaining was the onboard mic.

The onboard mic, which gets ID name acp63, is driven by a kernel module with the name of snd-soc-ps-mach. Now this module doesn’t take the index parameter so enter the slots parameter for the top level snd kernel module. With this parameter you can set which slot gets assigned to a specific driver. So added the following to /etc/modprobe.d/audio.conf:

# Onboard mic
options snd slots=,,,,,,,,,,,,snd-soc-ps-mach

And voilĂ , this is what arecord -l now thinks of it:

$ arecord -l
**** List of CAPTURE Hardware Devices ****
card 0: Babyface2359686 [Babyface (23596862)], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 11: ALC257 [HD-Audio Generic], device 0: ALC257 Analog [ALC257 Analog]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 12: acp63 [acp63], device 0: DMIC capture dmic-hifi-0 []
  Subdevices: 1/1
  Subdevice #0: subdevice #0

Awesome, full control again! Everything properly named and prioritized. But how does this perform?

With my RME Babyface I can go down to 64 frames/period and 3 periods at 48kHz, so that’s a nominal latency of 4ms. And it’s rock solid at this setting:

$ ./xruncounter -m

******************** SYSTEM CHECK *********************

    Sound Playback: USB-Audio - Babyface (23596862)
     Sound Capture: USB-Audio - Babyface (23596862)
      Graphic Card: Advanced Micro Devices, Inc. [AMD/ATI] Phoenix1 (rev c7)
Operating System: Debian GNU/Linux 12 (bookworm)
          Kernel: Linux 6.6.11-1-liquorix-amd64
    Architecture: x86-64
               CPU: AMD Ryzen 7 7840HS with Radeon 780M Graphics

***************** jackd start parameter ****************

    /usr/bin/jackd -P80 -S -dalsa -dhw:Babyface2359686 -r48000 -p64 -n3 -Xseq

********************** Pulseaudio **********************

    pulse is not active

********************** Test 8 Core *********************

Samplerate is 48000Hz 
Buffersize is 64 
Buffer/Periods  3
jack running with realtime priority 80
Xrun 1 at DSP load 83.76% use 3.56ms from 1.33ms jack cycle time
Xrun 2 at DSP load 92.76% use 3.32ms from 1.33ms jack cycle time
Xrun 3 at DSP load 87.17% use 2.48ms from 1.33ms jack cycle time
Xrun 4 at DSP load 92.11% use 1.15ms from 1.34ms jack cycle time
Xrun 5 at DSP load 95.91% use 3.29ms from 1.33ms jack cycle time
Xrun 6 at DSP load 97.95% use 1.85ms from 1.33ms jack cycle time
in complete 6 Xruns in 16809 cycles                                  
first Xrun happen at DSP load 83.76% in cycle 16112
process takes 3.56ms from total 1.34ms jack cycle time

On my old BTO I could go lower though, it would run at 32 frames/period and 3 periods at 48kHz with clean audio but the Lenovo is limited to 64 frames/period. If you try to go lower you will get distorted audio and the kernel ring buffer will fill up with messages like below:

[11883.067551] retire_capture_urb: 1338 callbacks suppressed
[11883.083397] xhci_hcd 0000:66:00.4: WARN Event TRB for slot 1 ep 5 with no TDs queued?

Eventually I’ll dive deeper into this but for now I’m OK with running at 64 frames/period. The onboard audio runs too at very low settings but provides way less room to do anything useful. But just about enough to run some soft synths and MIDI input. As a comparison, here’s the output of xruncounter for the onboard audio. In my case onboard audio likes period sizes higher than 3 best. And no full duplex at this setting, playback only.

$ ./xruncounter -m

******************** SYSTEM CHECK *********************

      Graphic Card: Advanced Micro Devices, Inc. [AMD/ATI] Phoenix1 (rev c7)
Operating System: Debian GNU/Linux 12 (bookworm)
          Kernel: Linux 6.6.11-1-liquorix-amd64
    Architecture: x86-64
               CPU: AMD Ryzen 7 7840HS with Radeon 780M Graphics

***************** jackd start parameter ****************

    /usr/bin/jackd -P80 -S -dalsa -dhw:ALC257 -r48000 -p32 -n6 -Xseq -P

********************** Pulseaudio **********************

    pulse is not active

********************** Test 8 Core *********************

Samplerate is 48000Hz 
Buffersize is 32 
Buffer/Periods  6
jack running with realtime priority 80
Xrun 1 at DSP load 97.32% use 0.40ms from 0.67ms jack cycle time
in complete 1 Xruns in 5781 cycles                                  
first Xrun happen at DSP load 97.32% in cycle 5527
process takes 0.40ms from total 0.67ms jack cycle time

So in my opinion the Lenovo performs pretty well. I do need to run these tests on my old BTO too to find out how much performance I’ve gained. And if I could find a way to work around or find a solution for those xhci_hcd warnings so that I can go even lower then that would be terrific. It could very well be a limitation of the USB implementation of this notebook but I can live with that as it runs really stable at sub 5ms latencies.

New notebook – Doing real-time audio

New notebook – The day after

Everything seems to work, network, audio, external monitor, display brightness. And this is the first notebook I’ve ever come across to that has a touch pad with an actual real middle click! Love it! Keyboard is nice too, backlight works fine and it has a nice array of function keys that are almost all mapped now. Overall feel of this machine is really nice, aluminum housing and the chiclet keyboard feels stable and solid. Startup time is twice as fast as the BTO. Compared to my new notebook the old one feels strangely completely outdated.

Now what is in there? CPU is an AMD Ryzen 7 7840HS, an 8-core CPU with SMT. Disabled SMT though, Ardour seems to run slightly better on 8 real cores. 32GB of RAM which should be more than enough, my old notebook had 16GB and never had any issues with that. Storage is a 1TB SK hynix BC901 NVMe drive with more than decent throughput. Almost all USB ports sit on their own bus which is quite nice. The only bus that shares its IRQ with something else (the WiFi module) is bus 001. USB ports that use this bus are both the USB-C and USB Type-A connectors closest to the screen. So better not use those.

The only thing that is a bit flaky is the WiFi connection but came across more mentions about that. The connection sometimes randomly reconnects. Can live with it, when making music I often completely disable WiFi. Did have to use systemd automount instead of an fstab entry to mount my NAS as the WiFi connection apparently takes a bit too long to come up.

Didn’t have to tweak a lot so far. As I already mentioned I did disable SMT (Simultaneous Multi-Threading) and I’ve prioritized the IRQ of the USB bus my audio interface sits on. Not with rtirq or udev-rtirq (which doesn’t work properly on this machine, the wrong IRQ seems to get prioritized) but with a small script to fetch the IRQ number that seems to change on every boot and feeding the outcome to chrt. Installed a liquorix kernel on it and enabled threaded IRQ’s, disabled mitigations and USB autosuspend. Also allowed my user to set CPU DMA latency.

BTO vs Lenovo

It’s running Debian 12 with XFCE again. Simple, fast and looks good enough to me with the Greybird theme and elementary icons. And no more notebook that takes off into orbit, the new one is way more quiet.

On with making music!

Edit: the script I threw together could be useful for others too with USB audio interfaces connected to a machine that uses MSI (Message-Signaled Interrupts) enabled USB controllers. You can find the snd_dev_id of your interface with aplay -l.

#!/bin/bash

prio=90
prio_step=5
proc_path=/proc/asound
sys_pci_bus_path=/sys/class/pci_bus
snd_dev_ids=( Babyface2359686 UA25 )

for snd_dev_id in ${snd_dev_ids[@]}; do
  snd_dev_card_number=$(awk '/'$snd_dev_id'/ {print $1}' $proc_path/cards)

  if [ -n "$snd_dev_card_number" ]; then
    snd_dev_card=card$snd_dev_card_number
    snd_dev_pci_bus_ref=$(grep -Eo "usb-[^[:space:],-]+" $proc_path/$snd_dev_card/stream0 | sed "s/usb-\(.*\)/\1/")
    snd_dev_pci_bus_ref_short=$(awk -F ':' '{print $1":"$2}' <<<$snd_dev_pci_bus_ref)
    snd_dev_irq=$(cat $sys_pci_bus_path/$snd_dev_pci_bus_ref_short/device/$snd_dev_pci_bus_ref/irq)
    snd_dev_irq_pid=$(pgrep $snd_dev_irq-xhci)

    chrt -f -p $prio $snd_dev_irq_pid
    
    prio=$((prio-prio_step))
  fi

done
New notebook – The day after