Skip to content

Instantly share code, notes, and snippets.

@chadmed
Last active July 4, 2022 11:22
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save chadmed/2c772c8fdac8280cb17846388203a213 to your computer and use it in GitHub Desktop.
Save chadmed/2c772c8fdac8280cb17846388203a213 to your computer and use it in GitHub Desktop.
j314s-alsa-notes
# File written by chadmed <jcalligeros99@gmail.com>
# This file is organised from top to bottom as sound enters ALSA from user space.
# Set up device and set default for Pulse, PipeWire, JACK, etc.
# XXX: doesn't work reliably with PipeWire for some reason.
pcm.!default {
type plug
slave.pcm pass6
}
ctl.!default {
type hw
card 0
}
# We need to copy the L and R signal to 6 channels, each corresponding with a
# driver in the array. We use the assignments from the ASoC driver for
# simplicity. This must be a plug device for LADSPA to work properly.
pcm.pass6 {
type plug
slave.pcm filters
slave.channels 6
ttable {
0.0 = 1
0.2 = 1
0.4 = 1
1.1 = 1
1.3 = 1
1.5 = 1
}
}
# The six channels are then routed into this PCM, which applies our filters
# accordng to the crossover network I determined.
pcm.filters {
type ladspa
slave.pcm dumbplug
path "/usr/lib/ladspa"
channels 6
plugins {
# HPFs on the tweeters
0 {
label hpf
policy none
input.bindings.2 "Input";
output.bindings.2 "Output";
input {
controls [ 2000 ]
}
}
1 {
label hpf
policy none
input.bindings.3 "Input";
output.bindings.3 "Output";
input {
controls [ 2000 ]
}
}
# LPFs on the main woofers
2 {
label lpf
policy none
input.bindings.0 "Input";
output.bindings.0 "Output";
input {
controls [ 2000 ]
}
}
3 {
label lpf
policy none
input.bindings.1 "Input";
output.bindings.1 "Output";
input {
controls [ 2000 ]
}
}
# HPFs on the "main" woofers
4 {
label hpf
policy none
input.bindings.0 "Input";
output.bindings.0 "Output";
input {
controls [ 80 ]
}
}
5 {
label hpf
policy none
input.bindings.1 "Input";
output.bindings.1 "Output";
input {
controls [ 80 ]
}
}
# LPFs on the "sub" woofers
6 {
label lpf
policy none
input.bindings.4 "Input";
output.bindings.4 "Output";
input {
controls [ 400 ]
}
}
7 {
label lpf
policy none
input.bindings.5 "Input";
output.bindings.5 "Output";
input {
controls [ 400 ]
}
}
# HPFs on the "sub" woofers
8 {
label hpf
policy none
input.bindings.5 "Input";
output.bindings.5 "Output";
input {
controls [ 80 ]
}
}
9 {
label hpf
policy none
input.bindings.4 "Input";
output.bindings.4 "Output";
input {
controls [ 80 ]
}
}
}
}
# LADSPA MUST have a plug device as its slave. This slave simply passes
# whatever it gets from LADSPA to the actual speaker array
pcm.dumbplug {
type plug
slave.pcm j314s-array
}
# It is in this device that we map the output of LADSPA 1:1 with the speakers
# on the machine. The coefficients are for fine tuning the volume of each
# driver.
pcm.j314s-array {
type route
slave outputs
ttable {
0.0 = 1
2.2 = 0.75
4.4 = 1
1.1 = 1
3.3 = 0.75
5.5 = 1
}
}
# This is the actual interface to the hardware. Do not change this.
pcm_slave.outputs {
pcm "hw:0,0"
channels 6
}
macOS does two very interesting things to the audio signal. Firstly, it EQs away
almost all of the real low end below ~80-100Hz. This is actually a great idea,
since doing so stops the machine vibrating, which under Linux gave me a headache
after about 5 minutes of playing around with FIRs in Carla. It also clarifies the
sound big time.
The second thing it does is significantly boost the low mids. This is a very
common trick in consumer sound equipment. Consumers "like" the illusion of
fullness and richness, but this all this does is make things muddier. The real
shame here is that it's not even needed, these speakers sound quite excellent with
nothing more than basic crossovers and per-driver volume tuning in a routing
matrix.
Given that whatever processing macOS applies to the speakers actually hinders
their performance, I reccommend that we do not attempt to copy it. Rather,
whatever FIRs/IIRs we end up applying should be based on a series of parameters
I have experimentally determined to maximise the natural performance of this
extremely high quality speaker array. The quick version:
a) bandpass on drivers 4 and 5 of 80-400 Hz
b) bandpass on drivers 0 and 1 of 80-2000 Hz
c) high pass on drivers 2 and 3 set at 2000 Hz
d) linear coefficient of 0.75 applied to drivers 2 and 3
e) -3 dB at 550 Hz, Q = 2.75, applied directly to the stereo signal
f) no cross mixing for stereo separation, it's not needed
For the why and how of these values, read on.
A SCIENCE-ADJACENT EXPERIMENT
-----------------------------
I have experimentally determined that these parameters result in the most
natural-sounding audio direct from ALSA. /etc/asound.conf included in this
Gist for reference. I am a scientist by trade and I simply could not live
with myself if I did not publish my findings properly, so for your perusal
here is a vaguely scientific writeup on what I did.
EQUIPMENT
---------
The source music used for this experiment consisted of:
Kenny G - Breathless (whole album) [CD :: 16/44.1 FLAC]
DALI - ムーンライト伝説 (track) [CD :: 16/44.1 FLAC]
Sade - No Ordinary Love (track) [24/192 ALAC]
Aqua - Doctor Jones (track) [CD :: 16/44.1 FLAC]
Horii Katsumi Project - Sky Cruisin' (album) [CD :: 16/44.1 FLAC]
George Gershwin (rec. Royal Phil. Orch.) - Rhapsody In Blue [24/96 ALAC]
Michael Jackson - Off The Wall (album) [LP :: 24/96 FLAC]
花江夏樹 - 青春は残酷じゃない (track) [YouTube (garbage)]
角松敏生 - On The City Shore (album) [LP :: 24/192 FLAC]
Kelly Bailey - Half-Life 2 Original Sountrack (album) [CD :: 16/44.1 FLAC]
菊池桃子 - Adventure (album) [LP :: 24/192 FLAC]
ABBA - Arrival (album) [LP :: 24/192 FLAC]
These tracks and albums were chosen to ensure that whatever parameters were
applied preserved the experience intended by the artists/engineers across many
genres and styles. (I'm also not going to sit here all day listening to
music I don't like)
All music samples were played through my home HiFi as a reference. Physical
formats specified above indicate the source of the digital transfers used
on the J314s. ALACs were sourced from Apple Music, and YouTube was sourced from
Google. As my digital rips were made on different equipment to what I have now,
I also used the physical copies as reference material to verify that the transfers
are of acceptable quality for this experiment. They are of marginally lower
quality than I would have liked, but any differences were noted and accounted
for.
Reference Setup
------------------
Speakers:
Richter Wizard S6 (3-way floorstanders)
Amp:
Yamaha A-S2200
Digital Signal Chain:
J314s MacBook Pro (CoreAudio) feeds
Schiit Hel 2 (24/192) line level output feeds
Yamaha amp line in
Physical Sources:
LP: Denon DP-47F w/ Ortofon 2M Blue cartridge/stylus
CD: Onkyo 6-CD carousel (using builtin DAC)
Test Machine
-------------
J314s running the Asahi reference distro plus Plasma under Wayland.
Vanilla PulseAudio feeding a custom ALSA device as per the included asound.conf.
PA daemon manually set to 24/96 with resampling enabled to match the hardware.
All drivers were configured to 50% volume in alsamixer with no amp gain.
The ASoC driver seems to significantly overdrive the amps and causes wild
clipping above this.
METHODS
--------
Testing was conducted in an A/B pattern. I would listen to the source material
for a while, mute it, then move over to the J314s under Linux and play the
corresponding audio file. Crossover values would be manually set in asound.conf,
PA restarted from the command line, and then the test continued until the values
below were determined to produce the nicest sound.
Additional testing consisted of applying IIRs to the stereo signal via
the LSP 32-band PEQ plugin in Carla.
RESULTS
--------
The table below outlines the crossover network I landed on at the end of this
experiment.
Freq Range (Hz) | Drivers Note that frequencies below 80 Hz are filtered
----------------|--------- out of the signal. As discussed, this greatly
80-400 | 0 1 4 5 improves the sound clarity and stops the machine
400-2000 | 0 1 from vibrating. The cones are too small to
2000-20000 | 2 3 properly reproduce anything that low anyway.
Mixing coefficients are applied to each driver in order to balance
the sound profile. The tweeters are significantly louder than the woofers, and
so a 1:1 mix causes the sound to be harsh and aggressive. Using a linear
coefficient of 0.75 cuts virtually all harshness out of the sound and results
in a mostly flat sound profile with a very slight emphasis on the low end. The
woofers are all mixed at 1:1.
After applying these parameters via a series of chained PCM devices in
asound.conf, the speaker array sounds incredibly good, better than it
does in macOS in my opinion.
The array is fairly linear in response, with slight booming at 450-650 Hz and
harmonics thereof. This is fairly typical for laptop speaker arrays. These are
diminished with a -3 dB reduction at 550 Hz with Q = 2.25.
Additionally, stereo separation is fantastic even without any fancy cross
mixing as done by macOS.
## BEGIN NOTES ##
HOUSEKEEPING
All testing conducted with channels set to 40% in alsamixer,
with no amp gain.
Do NOT try to play sound with the speakers set to 100% in alsamixer,
you will fry the cones!
DRIVER MAPPINGS/ALSA QUIRKS
The speaker array as set up by the ASoC driver maps like
this on a J314s:
0: Left Woofer 1
1: Right Woofer 1
2: Left Tweeter
3: Right Tweeter
4: Left (Sub)Woofer 2
5: Right (Sub)Woofer 2
ALSA sets up the speaker array on the J314s as a 4.0 surround system,
with the RL and RR channels duplicated across the woofers like this:
2: Front Left
3: Front Right
0: Rear Left
1: Rear Right
4: Rear Left
5: Rear Right
Obviously this is not correct, but for us it does not matter, since
we can just tell ALSA to route FL and FR to all drivers, presenting
it to the rest of userspace as a stereo device. Surround sources
are downmixed appropriately.
SOUND CHECK
Testing reveals that drivers 4 and 5 are likely
only there to help with bass and sub bass. They
are extremely bad at reproducing frequencies above
~500Hz, and even with the help of the tweeters sound
rough/deep fried in the mids. Drivers 0 and 1 are obviously
intended to be the main woofers in the array.
If we weren't intending to mimic whatever macOS does, my ear-only
testing would have me setting up a xover network like this:
Freq Range (Hz) | Drivers
----------------|---------
0-300 | 0 1 4 5
300-6500 | 0 1
6500-20000 | 2 3
THIS TABLE IS WRONG: SEE UPDATE BELOW!
Figures based on typical {LP,BP,HP}F rolloff characteristics.
Using all 4 woofers below 300Hz moves more air than just using the
(sub)woofers alone. 3-way loudspeakers work like this conventionally.
The ttable in the j314s-array pcm device tries to compensate for the lack of
EQ right now by greatly reducing the volume of 4 and 5, and slightly reducing
the volume of 0 and 1 relative to 2 and 3. I have found this gives an acceptably
clear sound without being too bright or losing too much out of the mids. Bass is
nonexistent, though I suspect this is just because we have not applied appropriate
correction to overcome the machine's housing yet.
FIRs can be applied to a 6 channel slave PCM which would then feed the routing
table
## END NOTES ##
@povik
Copy link

povik commented Mar 10, 2022

# ALSA sets up the speaker array on the J314s as a 4.0 surround system,
# with the RL and RR channels duplicated across the woofers like this:
# 2: Front Left
# 3: Front Right
# 0: Rear Left
# 1: Rear Right
# 4: Rear Left
# 5: Rear Right

That's just something I put there for the time being: https://github.com/povik/linux/blob/asahi-sound-wip/sound/soc/apple/macaudio.c#L487

I suppose the woofers are not "rear enough" to qualify...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment