Audiophile ways on SailfishOS

As this blog isn't only about design but also my adventures with technology, I'll describe what I've learned and discovered while trying to be audiophile on SailfishOS. In simple terms, its hard yet if you got skills its doable.

Leszek made nice video about quickly improving audio quality. I told him to change to soxr-vhq which is the best pa resampling method but it turns out sfos doesn't have it and it defaulted to worst resampling method, and we were all saying its better sounding :D Jokes aside read description and set it to speex-float-10 for best quality. On my Zenfone 5z which has snap845 CPU usage of pulseaudio went from 5-6% to 15% (depends on song) but its still worth it. You can watch video here

But speex-float-10 is for peasants, let's go higher! First I checked why soxr is missing in our pulseaudio. Turns out you need to compile pulseaudio with libsoxr. Thankfully soxr had no dependencies so building that was piece of cake. Then I forked pulseaudio sources on and added libsoxr to BuildRequires. Everything is being built on  jolla's obs and packages are available here. You can add this repo to your device using:

  1. zypper ar mister
  2. zypper ref mister
  3. zypper in --from=mister pulseaudio

Of course everything needs to be run as root and make sure you are on 3.4. You can always remove it and install back pulseaudio from jolla (preferably do that before update >.>). If I haven't mentioned yet, this is for advanced people only, if you are noob you will most likely just hurt yourself.

Okay so now by running "pulseaudio --dump-resample-methods" you will most likely get "soxr-vhq" on the list. You can go now to "/etc/pulse/daemon.conf.d/50-sfos.daemon.conf" and change "resample-method" to "soxr-vhq". 

Now by running "pactl list sink-inputs" while playing some music you should see "Resample method: soxr-vhq". CPU usage depending on song can be lower or higher than speex-float-10, around 10-16%. Remember that's single core usage and with 8 of them it's still like 5% of overall CPU. 

Additionally, to resample method you can add:

  • avoid-resampling = yes - doesn't really work and makes no sense but why not
  • default-fragments = 2 
    default-fragment-size-msec = 125

The last two, will decrease audio stuttering  so it's useful.

Now we made all hearable changes, but we are not there yet! We are still on pathetic 16bit sample format and tiny 48kHz sampling rate. While specifications of snap845 say that you can go up to 384kHz and even dsd128. So let's get to work.

SailfishOS pulseaudio connects to android pulseaudio using pulseaudio-modules-droid or -hidl for 15.1/16.0 bases, if you have hidl or you aren't using android as a base then you can stop reading as it won't help you. But let's carry on.

I dived into pa-modules-droid and android audio configuration in search of limitation. Links for reference:

If you go into audio_policy_configuration you can see that "deep_buffer" has only 48kHz 32bit output and droid primary has 16bit. SFOS is using 16bit on both, don't know why, and by default it uses deep_buffer. pa-modules-droid in default profile adds only primary and deep_buffer and I wasn't able to find where to make your own profile. But if you go below that you can see that direct_pcm has all sample formats and rates possible. That's what we want to use not some puny deep_buffer.

So now my mission was to find, how can I use direct_pcm instead of deep_buffer. I already found module options in source files, so I looked over options, I tried changing module_id but as you can see the entire file is module primary so that was stupid. After realizing it started looking over parameters to find the proper one. Parameters are available here.

Soon enough I found my parameter "sink_name". Okay so what will happen if I set it to sink_name=direct_pcm… bingo! Now in pactl list sinks I have direct_pcm and null. And audio also works! To set module paraments you need to go to /etc/pulse/ and add them after "load-module module-droid-card". By default, it looks like this "load-module module-droid-card rate=48000". And of course as per file comments if you have /etc/pulse/ then you need to change that file (my ports will have it).

As you probably noticed, we set parameter "rate=48000". Yep it's forcing rate to 48kHz. That utterly disgraceful so I removed that parameter. Sadly, avoid-resampling doesn't work and you need to set static sampling rate in pulseaudio config. If you go to daemon.conf you can see property "default-sample-rate". You can set that to any sampling rate your heart desire (and hw supports). 

Other than sampling rate, in audio_policy_configuration you can see different sample format. 5z has AUDIO_FORMAT_PCM_32_BIT, so we also set "default-sample-format" to s32le.

Now if you set sample format to s32le and 384kHz sample rate, you will use maximum potential of your phone's DAC and pulseaudio instead of using 10-15% will now use 45% of one core! Above and beyond!

Being more reasonable, you can lower it to sane 96kHz as your headphones most likely have 20Hz-20kHz range (same as your ears) so they won't be able to play that high frequency. But it's still marginally better as you don't have to downsample in some cases which preserves quality. I would leave sample format on 32bit or 24bit though, nothing wrong with it.

So that's the best audio quality you can get on SailfishOS well… almost. You can also connect USB DAC, make chroot with MPD, setup MPD to play bit perfect audio on USB DAC and have absolutely no limitation or audio downgrade. 

That's my current setup of my Zenfone 5z. Most audiophile device you could make. Perhaps I could recompile kernel with some audio optimizations… but we passed obviously hearable difference in second paragraph. 

I hope someone was interested in all this. It can be reproduced on any device quite easily. Next step would be to play DSD using alsa but that, I'm not sure how to achieve. For now, I'll play DSD on my USB DAC. And with most of my phone's DAC I can use its speaker… why was I doing this again?