About a month and a bit in me learning the piano, I’ve had to rethink my remote piano learning setup. The latency was at points really annnoying (sometimes nearly a second) - it’s confusing when you hit a key and only hear it when you’re hitting the next key. So, time to look for a lower-latency setup.
The go-to (at this point atleast) piece of software for low-latency audio is JACK audio - I didn’t want to use it before because it sounded like too much of a hassle for my usecase, but in the end it was very easy to configure.
As before, I’m using FluidSynth, as a software synthesiser. Generated audio goes from FluidSynth and my headset microphone to JACK, and sent out to PulseAudio. On the other hand, audio generated by client applications (such as Skype and Zoom) is sent from PulseAudio to JACK, and then finally outputted to the headset.
Installing FluidSynth is as easy as
apt install fluidsynth and adding the line
OTHER_OPTS='-a jack -m alsa_seq -r 44100'
Why did I pick a sampling rate of 44kHz? Because this is what my headset works with.
I’ve also removed the auto-start as I want this to only be running when JACK is running - I’ve noticed that FluidSynth starts spinning like mad after a while when running without anything attached to it.
JACK and Qjackctl
I’m using Qjackctl, a Qt application to control the JACK sound server daemon. When you install Qjackctl it will automatically pull in jackd. You’ll also want pulseaudio-module-jack, which gives integration between PulseAudio and JACK.
Installing them is easy:
apt install qjackctl pulseaudio-module-jack.
The parameters I changed in the config are:
|Interface||hw:Seri||This determines the default capture and playback device for JACK
(see Soundcard Selection to find which one)
|Sample rate||44100||matching what my headset can process, and what I configured for FluidSynth|
|Frames/Period||256||Number of frames between JACK process() calls. The lower the better (until you start getting xruns)|
|Periods/Buffer||2||Number of periods (above) of playback latency.|
- start JACK audio server on application startup
- don’t save JACK audio server configuration
- Enable D-BUS interface
- Enable JACK D-BUS interface
I’m using the following script which is run by qjackctl before starting jackd:
$ cat jackd-start systemctl restart fluidsynth --user amixer -q -c Seri set Headset 100% unmute pacmd set-sink-mute jack_out 0 pacmd set-default-sink jack_out pacmd set-default-source jack_in
This script will (re)start FluidSynth, unmute my headset in ALSA, unmute the Pulseaudio
jack_out sink and finally
set the default sink and source for PulseAudio to the
jack_in respectively. This will make applications like
Zoom or Skype pick those inputs and outputs by default.
To find which soundcard to use, see Soundcard selection.
This stop script gets run before JACK shuts down:
$ cat jackd-stop systemctl stop fluidsynth --user pacmd set-sink-mute alsa_output.usb-Plantronics_Plantronics_Blackwire_3225_Series_1129BBD004004FF4BD2E6F2248C0D73E-00.analog-stereo 1
This will stop FluidSynth, and mute the headset. To figure out the device to mute, please refer to my previous article.
Configuring the routing in qjackctl is easily through the patchbay or through the graph.
- The PulseAudio JACK sink will relay the output of your system to the system playback (the input from eg. Skype or Zoom)
- Fluidsynth will take the MIDI input, and generate audio, which is sent to the system playback and to the PulseAudio Jack Source (which can be used with Skype or Zoom as a virtual microphone device)
- The system capture device will be sent to the PulseAudio Jack Source, capturing the microphone of the system
The above links will make the magic work. Once configured (either in the patchbay or in the graph), you can save the setup to an XML file which can then be used at JACK startup so that it routes the audio the way you want.
Figuring out the soundcard to pass to
amixer and JACK can be done by checking
$ cat /proc/asound/cards 0 [PCH ]: HDA-Intel - HDA Intel PCH HDA Intel PCH at 0xd0510000 irq 29 1 [Loopback ]: Loopback - Loopback Loopback 1 2 [Seri ]: USB-Audio - Plantronics Blackwire 3225 Seri Plantronics Plantronics Blackwire 3225 Seri at usb-0000:00:14.0-1.4, full speed 3 [RD ]: USB-Audio - RD Roland RD at usb-0000:00:14.0-2, full speed
where you can see that the Plantronics headset has card identifier ‘Seri’. You can also use the number, but as these might move around, I wanted to use the name instead.
After applying the above configuration I’ve found that the latency for the piano audio is negligible! Happy me ;)