Croquet

Intro

Basic croquet rules, as played by a bunch of uncultured Northerners. Northern-monkey terminology is in “quotes”:

There are two teams, ideally with the same number of people on each.

There are six hoops (“goals”), arranged in a 3×2 grid (central hoops are closer to each other compared to other pairs):

 ╭───╮                                  ╭───╮
 │ 2 │                                  │ 3 │
                   ╭───╮
                   │ 6 │



                     ↑




                   ╭───╮
                   │ 5 │
 ╭───╮                                  ╭───╮
 │ 1 │                                  │ 4 │

There’s also a peg in between the middle hoops.

Each team has one ball. This definitely is not how croquet is supposed to be played, but it makes things more fun and is also your only option if you have less than three balls available. Each team also has a mallet (“hammer-shaped thing”).

How to play (kinda)

Each team takes it in turn to hit their ball *once* with the mallet. Like in Pool. Also like in Pool, you get an extra shot if you score. “scoring” happens when you knock your team’s ball through the next goal. And if you do that, you get an (one) extra shot.

At this point, I should mention the actual flow we play:
One team (“us guys”) starts at hoop #1 and goes 1-2-3-4-5-6.
The other team (“you fat wasters”) either do the same which makes things more fun when you involve croqueting/roqueting (see below), but which is also not correct according to “the actual rules”. If you’re bothered about such things, then the other team goes 3-4-1-2-6-5, so basically the same pattern as the first team but rotated by π radians.

The objective is to go through your sequence of hoops TWICE (note: not quite correct, the order for the second time is slightly different according to “the actual rules”), then to hit the weird little peg that’s in the middle.

So each team is trying to score 12 goals hoops, then hit the middle peg to win. This also isn’t correct according to “the actual rules”, as there’s some point-scoring system involved too, but keeping track of points is difficult after 38 pints of stella so we apply some “UX design” to “the actual rules”.

Where it gets fun/daft/pretentious

This brings me onto the croqueting and roqueting crap. This is where the game sounds kind of pretentious and silly if you use “the actual names” for things. If on your turn, your ball hits the enemy’s your opponent’s ball, then it has made roquet on that ball and that ball is now fucked croqueted, and you get an extra hit. This extra hit is a croquet stroke and you start it by first moving your ball so that it’s touching the ball that you croqueted. Then you hit. If in this extra stroke, you hit and move the roqueted ball and do so without violating any of “the actual rules” then you get an extra hit.

Extra hits don’t accumulate though. If you hit the croqueted ball and also put yours through a hoop, then you only get one extra hit not two. If you roquet (“hit”) the other team (“enemy”)’s ball and also put yours through a hoop then you only get one extra stroke (but still get an extra one if you move the croqueted ball on your next stroke).

If you knock the other team (“fat bastard”)’s ball through a hoop then even if yours goes through, you don’t get an extra turn and instead they get a point (which doesn’t matter since we don’t do the “points” thing). You can substitute this “point” by requiring the fouling team to get in another round of stella.

And that, fellow monkeys, is not actually how you play croquet.

Also, if you go past a “goal” then you can’t just go through it in the opposite direction. You have to go behind it again and go through it in the “correct” direction, otherwise Zalgo will rise through the center and o̰̤̮̱̲h̖ ̻͞m̗̗̼͍̜̗̼y̫̜̪̰͔̣ ̷̰̝̦̙̘g͎͟o̜̝̗̪̼̹d̵̲͚̜̙ i̫̣̮̫͕̞t̩͓̹̖̹̤̰̼ ̡̪̭̗̼̭͠b̴̟͇͡u̮͇͎͔̺r̲͉n̰̼̱̻s̸̵̤̫̯̘̰̰͇ͅ ͈͘i̴̢͚̺̱̫̳͕͈ͅt͏̰͚͇̦͎͚̭̮̥ ̧҉̯̪̣̮b̨͖̯͔̼͔̤̺̝̭u̧̲̗͓͖̝͉͓͓r̗͙̬̟̺̫͓̟̼n̳̯͟͜ͅs̼̹ ͙͕̦̮̱̻m̧̬̺̲͍̹a̛̯̮̫͡k̝̠͈̕e͓͇̼̹̘̫̻ ̹̞̩i҉̧͙͔͖͟t͞҉͙̲͈̰͓͈̬̕ ̛̼͈̞s̘̠̱͎̠̀t̖̤͕͚̹͖́͠o͕̼̖͉̻͔̕ͅp҉̷͓͇͎ ̵̲̻͉̤̩̳̩̹͢i̷̗̪̪̣͕̬͇̳̻͎͚͔̩͎̯͡ ͏̦̳͔̞́͢c̴̵̟͓̱̣̻͖͉̪̭ͅà̴̳̟͔͚̺̩̩̖̯̰͔̖͟͟ͅͅn̵̴̨̥̫͉̗̤͓̙̻͕̠͚͔͟ṭ̹̱̺̭̪͢ ̛̮̣̳̦̭̞̼̺̀͜ţ̶͇̜̭͉̫̫̲̣́ͅa̷̫̣͍̺̫̟͍̦͇̤̳̕͡͠͠k̸̡̩̼͈͇̕e̸̘̝̻̯͉̟̰̙͎͇̕͟ ͏̶̡̥͇̝̬̦̱̭͔̞̙͍̟̳́i̧̛̜͎͈͇̕͘͢t̷͚̖͇̫͉͎̲̼̭̪̕͞ ̢̱͍̲͙͙̪̤̻̲͎̞͙̳̣͟a͏̴̢̟͚͇̠͙̫̹̰̬͕͖̗ǹ̷̸͕̼͓͖̳͇̗̠̫̜͢͞ͅy̴̡̡҉̼̦͔̪̦ ̷̨̢̡͇͚͕̭̮͡m̵̼͉͎̘͎̤̯̖͔͘ò̸̝͙̹͖̮̲͍͙̲̜̜͎̘̬̯̫͝r̢̛̻̹̘̪̩̼̘̮̟̯͘͜͢é̖̟̝͚͚̟͉͍̲͇͓̫͈̞̝͢͡

Digital piano: Systemd user service to start low-latency synth and bind to MIDI controller

For years, I’ve had an M-Audio Evolution MK449C MIDI controller. It has plenty of nice controls: 8 rotary, 9 sliders, 10 toggleable buttons, 1 wheel, 1 return-to-centre wheel, and obviously also four octaves keys.

While (for the ~£50 I paid for it back in 2008) I can’t find any reason to fault it massively, I have finally decided to replace/augment it with a Roland A-88. That’s a full 88 weighted keys with piano mechanisms to add to the immersion. But also 3x the weight and with less controls.

Back when I used a desktop PC, I used the ultra-low latency JACK audio environment as standard, with the more friendly PulseAudio running on top of it. This way, I could plug the inputs and outputs of any programs together, for chaining effects, mixing, monitoring, etc. Like what KX Studio did for Creative sound cards, but on steroids.

On a laptop though, this isn’t so practical. JACK (and also PulseAudio with low fragment sizes) consumes quite a bit of CPU and battery, which isn’t ideal on a portable device. So I needed a convenient way to switch between PulseAudio→ALSA for normal use, and PulseAudio→JACK→ALSA for real-time use. Ideally in such a way that plugging in a MIDI controller causes switching to the JACK-enabled stack where PulseAudio has low fragment sizes (for low latency), and removing all MIDI controllers causes switching back to the (boot-default) PulseAudio with large fragment sizes (for low CPU usage).

Systemd to the rescue!

In addition to general system administration, systemd can also be used on a per-user basis (therefore not requiring root either). I chose to use systemd user services to orchestrate the audio configuration, since this is the exact kind of task that systemd excels at.

Simplified configuration is provided here:

Preparation

# Create directory in home folder for user-systemd units
mkdir -p ~/.config/systemd/user

# Enter the directory
cd ~/.config/systemd/user

# Create the unit files (use your preferred editor, e.g. gedit, nano, ed)
vim pulse.service jack.service synth@.service keyboard.service

pulse.service

[Unit]
Description=Pulseaudio
Conflicts=jack.service

[Service]
Type=simple
Environment="DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus"
ExecStart=/usr/bin/pulseaudio -nF /etc/pulse/default.pa

[Install]
WantedBy=multi-user.target

jack.service

[Unit]
Description=Jack
Conflicts=pulse.service

[Service]
# Grant your self the right to use such limits in /etc/security/limits.d/
LimitNICE=-15
LimitRTPRIO=99
# Configure DEVICE environment variable to the ALSA name of your output device
Type=simple
Environment=DEVICE=hw:0
Environment=JACK_NO_AUDIO_RESERVATION=1
ExecStartPre=-/usr/bin/pulseaudio -k
# Configure appropriate period size/count and sample-rate
ExecStart=/usr/bin/jackd -d alsa -P ${DEVICE} -n 2 -p 128 -r 48000 -X raw

[Install]
WantedBy=multi-user.target

synth@.service

[Unit]
Description=Fluidsynth with %I soundfont

BindsTo=jack.service
After=jack.service

Before=keyboard.service
Wants=keyboard.service

[Service]
LimitNICE=-15
LimitRTPRIO=99

# Configure path to your soundfonts
Environment=SOUNDFONT=/opt/soundfonts/%I.sf2

Type=simple
# Wait for JACK to start
ExecStartPre=/usr/bin/sleep 1
# Configure appropriate sample rate & buffer size
ExecStart=/usr/bin/fluidsynth --server --no-shell --audio-driver=jack --audio-bufcount=2 --midi-driver=jack --sample-rate=48000 --audio-bufsize=64 --connect-jack-outputs --gain 1 ${SOUNDFONT}

[Install]
WantedBy=multi-user.target

keyboard.service

[Unit]
Description=Connect MIDI keyboard to Fluidsynth

[Service]
Type=oneshot
# Wait for synth to start
ExecStartPre=/usr/bin/sleep 2
# Configure regexes as appropriate
ExecStart=/usr/bin/sh -c "/usr/bin/jack_connect $(jack_lsp | grep -xP 'system:midi_capture_\\d+' | tail -n1) $(jack_lsp | grep -xP 'fluidsynth:midi_\\d+' | tail -n1)"

[Install]
WantedBy=multi-user.target

Helper script

Helper script, placed in my user bin folder (~/.bin, or whatever you decided to call yours):

#!/bin/bash

set -euo pipefail

declare -r patch="${1:---help}"

if [ "$patch" == '--help' ]; then
        echo "Patches:"
        ls -1d /opt/soundfonts/*.sf2 | xargs -n1 -I{} echo ' * {}'
        exit 1
fi

systemctl --user stop "synth@*"

if [ "$patch" == '--stop' ]; then
        systemctl --user start pulse
        echo 'Synth stopped'
        exit 0
fi

systemctl --user start "synth@${patch}"

Usage

With soundfonts in the corresponding directory, it’s just a case of using udev rules to run the helper script with the name of the default soundfont as the argument when the controller is plugged in, and then to run the script with –stop argument when the last controller is removed.

The synth@.service and keyboard.service files aren’t necessary if you always use some other program which can connect things up for you (e.g. Ardour). I use them for convenience to give a quick and simple plug’n’play experience for when I’m feeling lazy.

I called the helper script “synth”, and can use it to run FluidSynth with a specific soundfont:

# Loads bosendorfer.sf2
synth bosendorfer

Playing with PostgreSQL: Window functions

Selecting items before/after match

Create test data

--
CREATE TABLE test (x SERIAL, y INT);
INSERT INTO test (y) VALUES
	(64), (48), (32), (16),
	(50), (40), (30), (20),
	(10), (60), (70), (80);
--

View data

--
SELECT * FROM test;
 
 x  | y  
----+----
  1 | 64
  2 | 48
  3 | 32
  4 | 16
  5 | 50
  6 | 40
  7 | 30
  8 | 20
  9 | 10
 10 | 60
 11 | 70
 12 | 80

--

With respect to x-ordering, get y-value of the item before and the item after the item with y=50:

--
SELECT * FROM (
	SELECT
		x,
		lag(y,1,0) OVER (ORDER BY x) AS y_prev,
		y AS y_this,
		lead(y,1,0) OVER (ORDER BY x) AS y_next
	FROM test) AS tmp
WHERE this = 50;

| x | y_prev | y_this | y_next |
+---+--------+--------+--------+
| 5 |     16 |     50 |     40 |

--