11 Feb 2021, 10:23

Split Tracks and Pulseaudio

I’m learning to play the drums. Something I’m trialling is split tracks, where the mix of the recording is separated into left and right, with left being only drums and vocals, and right being everything except drums and vocals. This is great except when played through stereo headphones, the mix sounds odd and is uncomfortable to listen to. The solution is to convert the stereo output to mono, however most home setups won’t have the kind of gear required for this.

My solution is to use Linux Pulseaudio to create a ‘sound sink’ which is essentially a virtual output device that sits between my music player software and the sound card.

I added 3 lines to the end of /etc/pulse/default.pa - each one creates a new sound sink, in addition to the normal stereo output.

load-module module-remap-sink sink_name=Mono sink_properties="device.description='Stereo to Mono'" master=alsa_output.pci-0000_00_1b.0.analog-stereo channels=2 channel_map=mono,mono

load-module module-remap-sink sink_name=Left-to-Mono sink_properties="device.description='Left to Mono'" master=alsa_output.pci-0000_00_1b.0.analog-stereo channels=1 master_channel_map=mono channel_map=left

load-module module-remap-sink sink_name=Right-to-Mono sink_properties="device.description='Right to Mono'" master=alsa_output.pci-0000_00_1b.0.analog-stereo channels=1 master_channel_map=mono channel_map=right

The first line creates the ‘Stereo to Mono’ sink, useful for listening to both left and right channels of the split track together. Then there is ‘Left to Mono’, useful for listening to drums only - instead of them coming out of only the left headphone, they come out of both headphones. Then ‘Right to Mono’.

When playing back a split track song I can switch between the 3 different sinks in the audio config settings as required.

17 Jun 2020, 00:00

Sam Harris & Trump

NOTE: work in progress

Sam Harris is a brilliant thinker, and has an amazing ability to articulate his thoughts to a broad audience. I should say I’m no expert on Harris’ past work, having never read his books and only ever listened to his podcasts posted to Youtube.

Most recently I listened to his ‘Making Sense’ podcast entitled ‘Can We Pull Back From The Brink?'. He methodically works through the issues surrounding the recent George Floyd protests, police brutality, and the enduring problem of race relations in America. Throughout this podcast Harris makes commentary on Trump which I found really jarring in the way it contrasts with Harris’ usual calm, rational approach. He calls Trump names such as ‘unhinged’ and ‘crackpot’ which I find puzzling and a little unsettling. I’m no fanboy for Trump. Trump is a boastful, self-promoting, egotistical bully. At the same time, I’ve seen nothing in his words or behaviour that would make me think he is literally crazy.

17 Mar 2020, 21:38

4K Disappointment

tl;dr - before going down the 4K path, make sure your graphics hardware is capable of driving your display at full spec.

My old faithful Dell U2412 monitor recently started playing up. It is my main ‘work from home’ monitor and I needed to get work done, so I rather hastily found a replacement. I thought to myself “hey, 4K has been around for ages and prices are reasonable so may aswell grab one of those”. I settled on a Samsung LU28E590DS capable of upto 3840x2160@60hz. I plugged it in a cranked the resolution right up - sweet! Looks gorgeous. I ended up enabling 200% scaling to make the text readable (unfortunately fractional scaling is not officially supported yet in Gnome).

I quickly realised that the interface was noticeably laggy - like everything had been dipped in molasses. My first inclination was that my graphics adaptor was struggling at 4K resolution, but after some research into the wonderful world of computer display graphics I came to the conclusion that the issue was in fact the refresh rate - my hardware (Intel HD530 integrated GPU, coupled with Gigabyte H110M-H motherboard) was only capable of outputting at 4K@24hz instead of the 60hz I’d been used to. That was annoying but I thought to myself, “oh well I’ll just drop the resolution down to something that can output at 60hz”. Unfortunately I then struck the problem of non-native resolution and the blurriness that results from scaling; for me this was an unacceptable tradeoff - in my opinion worse than the lower refresh rate! Again I naively thought “4K has been around for ages, surely I can grab a low end graphics card that will output 4K@60hz!?” Alas, no. Well technically, yes, but only Nvidia it seems. I’m running Linux and, unless you’re a gamer (which I’m not), Nvidia’s drivers are not worth the hassle IMHO.

As I’ve now discovered, until relatively recently only DisplayPort has been capable of outputing 4K@60hz. However that adaptor is far less common (on PC hardware at least) than HDMI. The story with HDMI is that it was limited to 4K@30hz until v2.0 of the HDMI spec, which is only included on higher end graphics cards and motherboards!

My options are this stage:

  • put up with 24hz (what I’m currently doing)
  • buy a standalone graphics adaptor which comes with Displayport and/or HDMI 2.0 - PROS: flexibility CONS: expensive, noisy
  • replace my CPU + motherboard with something more recent, that includes onboard Displayport and/or HDMI 2.0 - PROS: updated CPU + chipset, quiet CONS: inferior graphics capability compared with standalone

UPDATE: 2020-04-19, I ended up getting a HP EliteDesk G2 800 motherboard cheap off Ebay which comes with dual displayport capable of 4k@60hz

07 Oct 2019, 09:23

Connecting to Internode with OpenWRT

Australian ISP Internode does not officially support OpenWRT.

I recently connected HFC (hybrid fibre coaxial) with NBN & Internode. To get it working I needed to tag VLAN 2 on the WAN interface. Using the Luci web interface, navigate to Networks -> Switch and set the dropdown to Tagged next to VLAN 2 under WAN. Then setup the WAN interface like a standard PPPoE with <username>@internode.on.net for the username and my account password.

Update 12th Feb 2021: if you’re not using Luci web interface or the option is missing, you can achieve the same by editing /etc/config/network and setting the ‘wan’ interface ifname option to be e.g. eth1.2 where eth1 corresponds with the WAN physical interface and .2 signifies the VLAN. Then reload the config with service network reload.

22 Dec 2018, 09:35

The Last Kingdom, a brief review

I just finished watching episodes 1-6 of the BBC historical drama The Last Kingdom. I enjoyed the first few episodes but found myself increasingly unsettled as the eposides went by. Something is very ‘off’ about this story. The TV series is based on the books, The Saxon Stories by Bernard Cornwell, so any criticism of the story and script are going to be primarily with the author.

The Good

Great Britain has a fascinating history, and there was much I learnt from watching TLK. I’m much creative license has been taken, but it certainly has piqued my interest and encouraged me to find out more about the real history.

The production values are great, with the locations and sets being very believable. The cinematography is cleverly done, and the music matches the tempo and pathos of the scenes very well. The acting is mostly very good, with some standout performances. I particularly liked the character of King Alfred (played by David Dawson) and Viking Lord Ubba (played by Rune Temte).

With the copious violence, and occasional sex scene aside, I found it pretty thought provoking viewing.

The Bad

The violence is gratuitous and I found disturbing at times. The portrayal of women is not positive. The few female lead characters seem to be nags, pious busy bodies and mostly there for the amusement of the men.

Ulricht is a really unlikeable character. He’s a foolish, arrogant, petulant, man-baby. I hope this is just the story arc that ends up with him growing up and setting things right in the end, but I so wanted to punch him in the face by episode 6.

Ubba by contrast was the bad guy I grew to love, and was sorry to see him meet his inevitable demise.

The Ugly

Humanity always filters history through the lens of the present, and it is no different with TLK.

I couldn’t figure out what moral code the main character, Ulricht, lived by. On one hand he is angrily calling for ‘justice to be done!’ meanwhile he is picking and choosing what is right and wrong based on nothing more than what feels right for him at the time.

The constant mocking of Christianity I found really tiresome. There is zero recognition that western civilisation itself is deeply rooted in its Judeo-Christian heritage. It’s almost as if Christianity was some annoying baggage hindering progress, instead of the driving force behind Britain and Europe’s advance towards what became the pinnacle of human achievement and knowledge.

The pagan beliefs are portrayed through a very modern prism. There is no judgement in their belief system - you just do your best to observe all the right superstitions and pay your respects to the right gods and you’ll be fine - Valhallah awaits. Of course the reality of pagan belief is world of darkness and bondage: human sacrifice anyone?

15 Nov 2018, 08:00

Morning Devotion

“The Lord’s portion is his people.” – Deuteronomy 32:9

How are they his? By his own sovereign choice. He chose them, and set his love upon them. This he did altogether apart from any goodness in them at the time, or any goodness which he foresaw in them. He had mercy on whom he would have mercy, and ordained a chosen company unto eternal life; thus, therefore, are they his by his unconstrained election.

They are not only his by choice, but by purchase. He has bought and paid for them to the utmost farthing, hence about his title there can be no dispute. Not with corruptible things, as with silver and gold, but with the precious blood of the Lord Jesus Christ, the Lord’s portion has been fully redeemed. There is no mortgage on his estate; no suits can be raised by opposing claimants, the price was paid in open court, and the Church is the Lord’s freehold forever. See the blood-mark upon all the chosen, invisible to human eye, but known to Christ, for “the Lord knoweth them that are his”; he forgetteth none of those whom he has redeemed from among men; he counts the sheep for whom he laid down his life, and remembers well the Church for which he gave himself.

They are also his by conquest. What a battle he had in us before we would be won! How long he laid siege to our hearts! How often he sent us terms of capitulation! but we barred our gates, and fenced our walls against him. Do we not remember that glorious hour when he carried our hearts by storm? When he placed his cross against the wall, and scaled our ramparts, planting on our strongholds the blood-red flag of his omnipotent mercy? Yes, we are, indeed, the conquered captives of his omnipotent love. Thus chosen, purchased, and subdued, the rights of our divine possessor are inalienable: we rejoice that we never can be our own; and we desire, day by day, to do his will, and to show forth his glory.

~ Charles Spurgeon

This brought me to tears. Amazing Grace how sweet the sound! What a mystery that God both chooses those who would be His, and yet at the same time makes salvation available to anyone and everyone. It makes no sense in the natural, but God’s ways aren’t our ways.

01 Oct 2018, 12:54

FFmpeg Recipies

Any technology, no matter how primitive, is magic to those who don’t understand it. - Arthur C. Clarke / Mark Stanley

Here are some magical incantations that let me do help me do cool stuff with FFmpeg.

Fade vision + audio between 2 videos

ffmpeg -i big_buck_bunny.mp4 -i blackout.mkv -filter_complex \
[0][1]acrossfade=d=1[a]" \
-map [v] -map [a] result.mp4
  • st=5:d=1 start fade out of first clip at 5 seconds, fade duration 1 second
  • acrossfade=d=1 fade audio from clip 1 to clip 2, duration 1 second
  • PTS-STARTPTS+5/TB the second clip needs to start 5 seconds after the first clip 5/TB is “5 divided by time base

Thanks to this post: https://opensourceforu.com/2015/04/get-friendly-with-ffmpeg/

Mux mp4 + subtitles into an MKV

ffmpeg -fflags +genpts -i infile.mp4 -f srt -i subtitles.srt \
-map 0:0 -map 0:1 -map 1:0 -c:v copy -c:a copy -c:s srt outfile.mkv

-fflags +genpts necessary if you get Can't write packet with unknown timestamp errors.

Extract part of video using start + end times

Transcode to lossless Flac and while applying peak normalisation to audio track

# process tab delmited 'runsheet' file with format
#	output name	input name	start time	end time
# where start/end time are hh:mm:ss

tosecs() {
   date '+%s' --date="$1"

fromsecs() {
 printf "%02d:%02d:%02d\n" $h $m $s

for i in `cat runsheet`; do

	oname=`echo $i | cut -f1`
	iname=`echo $i | cut -f2 | tr -d "'"`
	start=`echo $i | cut -f3`
	end=`echo $i | cut -f4`

	if [[ -n $end ]]; then
		t=$(( $(tosecs "$end") - $(tosecs "$start") ))
		to="-t $(fromsecs $t)"
	if [[ -n $start ]]; then
		ss="-ss $start"
		ss="-ss 00:00:00"

	cmd="ffmpeg $ss $to -i \"../$iname\" -vcodec copy -acodec flac -filter:a loudnorm $oname.mkv"
	echo $cmd
	eval $cmd


Record from microphone, encode to Opus and send to network socket

ffmpeg -f pulse -i default -acodec libopus -b:a 96000 -vbr on -compression_level 10 -f rtp rtp://

Play from RTP stream:

Create SDP file using details output from ffmpeg command.

o=- 0 0 IN IP4
s=No Name
c=IN IP4
t=0 0
a=tool:libavformat 58.12.100
m=audio 1234 RTP/AVP 97
a=rtpmap:97 opus/48000/2
a=fmtp:97 sprop-stereo=1

Then pass SDP file to RTP client:

ffplay -i opus.sdp -protocol_whitelist file,udp,rtp

Video Capture Using EasyCAP USB analog to digital convertor

  • Capture audio from device1 (hw:1)
  • Capture video from /dev/video0 as PAL (720x576 50hz)
  • de-interlace (yadif)
  • encode video using ‘fast’ preset (using slow was getting dropped frames)
  • encode audio as AAC 128kb

software encoding

ffmpeg \
  -f alsa -ac 2 -thread_queue_size 512 -i hw:1 \
  -f v4l2 -standard PAL -thread_queue_size 512 -i /dev/video0 \
  -vf yadif -c:v libx264 -preset fast -crf 23 \
  -c:a aac -b:a 128k \

hardware encoding using VAAPI

ffmpeg \
  -f alsa -ac 2 -thread_queue_size 1024 -i hw:1 \
  -f v4l2 -standard PAL -thread_queue_size 1024 -i /dev/video0 \
  -vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' -threads 4 -vcodec h264_vaapi -qp:v 23 \
  -c:a aac -b:a 128k \

No deinterlacing with hardware encoding :(

Convert sequence of JPEG images to MP4 video

Simple glob:

ffmpeg -r 24 -i '*.JPG' -s hd1080 -vcodec libx264 timelapse.mp4

Start from DSC_0079.JPG

ffmpeg -r 24 -f image2 -start_number 79 -i DSC_%04d.JPG -s hd1080 -vcodec libx264 timelapse2.mp4
  • -r 24 - output frame rate
  • -s hd1080 - 1920x1080 resolution

Slower, better quality

Add the following after -vcodec libx264 to achieve better quality output

-crf 18 -preset slow

Bulk convert JPGs to 1920x1080, centered

convert input.jpg -resize '1920x1080^' -gravity center -crop '1920x1080+0+0' output.jpg

10 Mar 2018, 00:00

Selling a motor vehicle online

I recently sold a motorcycle via online classifieds. It was a learning experience - more accurately a re-learning experience. I’ve sold motor vehicles privately before and encountered many of the same things - it’s just that I do it so infrequently that I forget the tricks and the traps!

What follows are some thoughts and advice to my future self, when next time I go to sell a motor vehicle:

  • figure out what a fair price is in the current market. It doesn’t matter what you paid for it. Sentimental value counts for nothing. All that matters is what are people willing to pay for it today, and that will largely be based on what other vehicles of same spec are going for in your area. This can change from month to month (and even go up!), so it’s important to keep an eye on things and adjust accordingly.
  • negotiating on price is a given: most buyers will ask for a discount, so factor that into your listing price
  • set your lowest price from the beginning and stick to it. It’s OK to adjust it, just not when you’ve got a buyer pressuring you!
  • never disclose your lowest price! Almost every second enquiry asked me outright: “what’s your lowest price?". There are a few approaches to this question:
  • say something like ‘all reasonable offers will be considered’ - throw the ball back in their court
  • say that you’re happy to negotiate but only in person, and suggest they come and inspect the item. I mean, how serious can they really be if their making out like they’d buy it without looking at it in person!?
  • give them a token discount, and say “I’d be happy to take $X” - something well above your lowest price
  • be patient. You’ll likely get a few vultures sweep in early offering a low bid. Hold out and you will be rewarded
  • there are better times of year to sell than others. The warmer months are better times to sell a motorcycle. Also, avoid busy holiday periods e.g. Christmas/New Years
  • commercial buyers (car yards etc) should be a last resort - they will only ever offer you well below a fair market price
  • your time is precious; don’t waste it on people who are ‘tire kickers’:
  • don’t negotiate price via text message. Tell people that you’re willing to negotiate but via phone or in person only. That will weed out 90% of the time wasters
  • Be polite but firm. There’s nothing to be gained by being rude or getting angry with people.

A real life example of a problem buyer:

Him: “hey, can you deliver to [town name]?” (place 2hrs away from where I live!)
Me: “No, sorry"
Him: “ok, what’s your bottom price seeing as you won’t deliver"
Me: “I’d be happy with $X” (my listed price minus a bit, nowhere near the bottom)
Him: (proceeds to ask 100 questions about the bike)
Me: (answer politely)
Him: “hey, I don’t have a ramp to load the bike. Do you?“
Me: “sure no problem"
Him: “OK, I’ll offer you $Y…” (lower, but still reasonable) “including the ramp"
Me: “I can do $XY…” (half way between X and Y.) “including the ramp"
Him: “I’m offering $Y. You sure you still want $XY?” (LOL!)
[…time passes…]
Him: “I’m in another state (News to me! Turns out the place he mentioned earlier was just the half way point.) which means the registration is worthless to me. I’m offering $Z (silly low ball) and that’s my final offer"
Me: (sigh) “The price is $XY minus the registration component, take it or leave it"

10 Feb 2018, 09:35

Wireguard VPN

UPDATE 2020-03-30 Wireguard 1.0 was included in Linux Kernel 5.6.

UPDATE 2018-08-06 Wireguard has been submitted for inclusion into the Linux Kernel source tree.

I recently stumbled upon what I think may be the holy grail - a VPN method that is simple to configure, high performance, and (so I’m told) highly secure. Until now my experience of using VPNs was that you could choose any two of the above, but never expect to get all three!

The ease by which you can get this up and running is quite astonishing. The documentation is quite good, but still has a few holes which hopefully will be covered here, adapted from https://www.wireguard.com/install/

Wireguard is conceptually quite different to other VPN products in that there isn’t a daemon that runs - it all happens in the linux kernel. There also isn’t any state: no concept of a tunnel being ‘up’ or ‘down’ - just a standard network interface with configuration applied to it - not dissimilar to a wifi interface. This has the advantage of allowing traffic to route seamlessly between, for example, fixed and wireless connections.

NOTE: Wireguard is not yet merged into mainline kernel which means compiling the required kernel module from source. Fortunately, thanks to DKMS this step is painless.


Both ends of the VPN described here are running stock Centos7

$ curl -Lo /etc/yum.repos.d/wireguard.repo https://copr.fedorainfracloud.org/coprs/jdoss/wireguard/repo/epel-7/jdoss-wireguard-epel-7.repo
$ yum install epel-release
$ yum install wireguard-dkms wireguard-tools


Configuring wireguard can be done from command line with ip (from the iproute package) and wg (from wireguard package) commands. I would recommend however not doing that, but instead using the included systemd service file which reads from a config file, described below.

Each endpoint has a single config file, similar to this: /etc/wireguard/wg0.conf

ListenPort = 51820
PrivateKey = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Address =

PublicKey = yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
AllowedIPs =,,
Endpoint = x.x.x.x:51820
  • Endpoint = x.x.x.x:51820 corresponds with the public IP and listening port of your peer.
  • AllowedIPs = is set to any IPs or subnets that should be routed via this tunnel.

IPv4 routing is enabled. This is set in /etc/sysctl.conf with: net.ipv4.ip_forward = 1


The public/private key are generated using the wg utility. wg genkey generates the private key string. This string can then be piped into wg pubkey to generate a corresponding public key e.g. all-in-one command

$ wg genkey | tee /dev/tty | wg pubkey

The private key (first line) goes into local config file, and the public key goes into the peer’s config file.


Systemd can bring the VPN up/down using the included wg-quick service file. To set the VPN to come up on boot enable the service:

systemctl enable wg-quick@wg0

Now, start/stop the service like so:

systemctl start wg-quick@wg0
systemctl stop wg-quick@wg0

This adds the wg0 interface, and inserts routes corresponding with the list of allowed IPs specified in the config file.



The VPN itself uses a single UDP port. For the VPN tunnel to connect, both ends must be able to reach the other on UDP port 51280. The port number is configurable.


  • the tunnel uses the addresses for A-end and for B-end
  • routes for each end’s network(s) are sent via the VPN interface wg0


Having done all the above, if things don’t appear to be working out, here’s some things to look at first:

  • Check systemd log for the wg-quick@wg0 service: journalctl -u wg-quick@wg0
  • Check the wg0 interface is up with ip addr:
6: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 8921 qdisc noqueue state UNKNOWN qlen 1
    inet scope global wg0
       valid_lft forever preferred_lft forever
    inet6 fe80::5e6:1a69:4213:44ba/64 scope link flags 800 
       valid_lft forever preferred_lft forever
  • Attempt to ping the endpoing on the other end of the IP e.g. ping
  • Run tcpdump on each endpoint to see what traffic is coming in/out of the ethernet interfaces (eth0). Encrypted VPN traffic will show up as UDP packets on port 51280.
  • Run tcpdump on each endpoint’s wireguard interface (wg0) to see what’s passing over the tunnel itself.

Providing that the wireguard config is correct - keys match up, and allowed IPs are set - then you’re going to be dealing with a routing or firewalling issue somewhere in between.

02 Feb 2018, 21:40

OpenWRT Geofencing

Geofencing refers to allowing or blocking a connection based on country of origin. Unfortunately, OpenWRT / LEDE does not support geofencing out of the box.

The way I’ve worked around this is described below.

Shell script

I created a shell script /root/bin/ipset-update.sh to pull Maxmind Geolite2 database periodically, and use that to populate an ipset set which I can then reference from iptables.

The shell script is based on this one with a few tweaks for Openwrt: change bash to ash and change the paths of some of the utilities ipset, curl etc

The shell script takes 2 arguments: the ipset name to create/update, and the country name to pull out of the Geolite database.

If I call it like this: /root/bin/ipset-update.sh australia Australia then the resulting ipsets are australia4 and australia6 (ipv4 + ipv6 respectively)


The Maxmind database changes over time as, so it’s important to update it on a periodic basis. I installed a cronjob as root user to run the script once per week:

0 0 * * sun     /root/bin/ipset-update.sh australia Australia


From the command line, you can use the new ipset like so:

iptables -A INPUT -p tcp -m tcp --dport 22 -m set --match-set australia4 src -j ACCEPT
ip6tables -A INPUT -p tcp -m tcp --dport 22 -m set --match-set australia6 src -j ACCEPT

I prefer to use the Luci interface for configuring the firewall. While it doesn’t support setting an ipset target directly it does allow you to specify arbitrary iptables switches. When creating a port forwarding or traffic rule that requires geofencing, I put the following in the Extra arguments: section: -m set --match-set australia4 src.

As the interface only allows one ipset to be specified per rule, you can either create multiple rules for multiple countries, or create one ipset that combines multiple countries into a group.

Survive reboot

At boot time rules that use ipsets will fail to load as the ipsets will not exist at that point. To work around that I put the following lines into the /etc/rc.local:

/root/bin/ipset-update.sh australia Australia
/etc/init.d/firewall restart