<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://adrianiainlam.tk/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://adrianiainlam.tk/blog/" rel="alternate" type="text/html" /><updated>2025-06-14T03:04:19+01:00</updated><id>https://adrianiainlam.tk/blog/feed.xml</id><title type="html">Blog | Adrian I. Lam</title><subtitle>Adrian I. Lam&apos;s personal blog
</subtitle><author><name>Adrian I. Lam</name></author><entry><title type="html">Codegolf - Reverse NATO phonetic alphabet</title><link href="https://adrianiainlam.tk/blog/2025/06/14/codegolf-reverse-nato/" rel="alternate" type="text/html" title="Codegolf - Reverse NATO phonetic alphabet" /><published>2025-06-14T01:52:56+01:00</published><updated>2025-06-14T01:52:56+01:00</updated><id>https://adrianiainlam.tk/blog/2025/06/14/codegolf-reverse-nato</id><content type="html" xml:base="https://adrianiainlam.tk/blog/2025/06/14/codegolf-reverse-nato/"><![CDATA[<p>A long time ago, I used to visit Codegolf Stack Exchange regularly,
and took part in a few contests. Due to certain reasons, I have decided
not to contribute to the Stack Exchange network anymore, but I recently
came across a codegolf contest that looked interesting, so I thought I’d
post my solution in my blog instead.</p>

<p>The post is linked <a href="https://codegolf.stackexchange.com/a/282211">here</a>, but to summarise, the task is to convert
a string spelled out in NATO phonetic alphabet, without any delimiter,
back to a string spelled normally, so for example, from
“ECHOXRAYALFAMIKEPAPALIMAECHO” to “EXAMPLE”.</p>

<p>I decided to attempt this in the RISC-V ISA, for no particular reason
other than that I have been using it at work. My current score is
<strong>52 bytes</strong>.</p>

<p>A few details before getting to the code:</p>

<ul>
  <li>Instead of a full program handling I/O, I will only be implementing
a function, using the standard RISC-V ABI.</li>
  <li>I am using the 32-bit ISA with the Zca and Zcb extensions.</li>
  <li>I will assume C strings (i.e. NUL-terminated array of chars) in ASCII.</li>
  <li>I will assume the caller has allocated a sufficiently large output buffer.</li>
</ul>

<p>My solution relies on the fact that all phonetic alphabets start with
the letter it represents, for example, ALFA starts with A, BRAVO starts
with B. So all it needs to do is know how long each phonetic alphabet is,
and just copy the first letter and skip over the rest.</p>

<p>Here’s the assembly:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    .globl revnato
revnato:
    la a3, revnato_data - 65
revnato_loop:
    lbu a2, 0(a0)
    sb a2, 0(a1)
    beqz a2, revnato_ret
    add a2, a2, a3
    lbu a2, 0(a2)
    add a0, a0, a2
    addi a1, a1, 1
    j revnato_loop
revnato_ret:
    ret
revnato_data:
    .byte 4,5,7,5,4,7,4,5,5,7,4,4,4,8,5,4,6,5,6,5,7,6,7,4,6,4
</code></pre></div></div>

<p>If you’re not familiar with the syntax, all operations are in the form
<code class="language-plaintext highlighter-rouge">opcode destination operand</code> or <code class="language-plaintext highlighter-rouge">opcode destination operand1 operand2</code>.</p>

<p>Here, <code class="language-plaintext highlighter-rouge">a0</code> is expected to contain the input string, and <code class="language-plaintext highlighter-rouge">a1</code> the output buffer.
We first <strong>l</strong>oad the <strong>a</strong>ddress (<code class="language-plaintext highlighter-rouge">la</code>) of a lookup table to <code class="language-plaintext highlighter-rouge">a3</code>. This table is
the string length of each phonetic alphabet. The <code class="language-plaintext highlighter-rouge">- 65</code> will be explained later.</p>

<p>In the loop, we first <strong>l</strong>oad one <strong>u</strong>nsigned <strong>b</strong>yte (<code class="language-plaintext highlighter-rouge">lbu</code>) at <code class="language-plaintext highlighter-rouge">a0[0]</code>
into <code class="language-plaintext highlighter-rouge">a2</code>, and <strong>s</strong>tore that <strong>b</strong>yte (<code class="language-plaintext highlighter-rouge">sb</code>) into <code class="language-plaintext highlighter-rouge">a1[0]</code>, i.e. copy the
first character.
We exit from the loop (and <strong>ret</strong>urn from the function) if the byte is NUL
(<code class="language-plaintext highlighter-rouge">beqz</code>: <strong>b</strong>ranch if <strong>eq</strong>ual to <strong>z</strong>ero).</p>

<p>If non-zero, we compute the index for the lookup table.
The unoptimised way would be to subtract <code class="language-plaintext highlighter-rouge">'A'</code> (ASCII 65)
from the byte, and then add it to the lookup table address. To optimise
this, I have moved the “subtract 65” to the address itself, so that we can
just <strong>add</strong> <code class="language-plaintext highlighter-rouge">a3</code> to <code class="language-plaintext highlighter-rouge">a2</code>. We then dereference the address by <strong>l</strong>oading
the <strong>u</strong>nsigned <strong>b</strong>yte.*</p>

<p>We then increment the input pointer by the lookup table entry, and
increment the output pointer by 1, and loop.</p>

<p>*: Note that another way to optimise it would be to leave the <code class="language-plaintext highlighter-rouge">a3</code> address
as is, and do <code class="language-plaintext highlighter-rouge">lbu a2, -65(a2)</code> instead. This may be more readable, but
actually produces bigger code, because <code class="language-plaintext highlighter-rouge">-65</code> is outside the range of the
16-bit <code class="language-plaintext highlighter-rouge">lbu</code> immediate opcode range (see RISC-V Zcb), so a 32-bit <code class="language-plaintext highlighter-rouge">lbu</code>
has to be used instead.</p>

<p>This can be compiled to an object file with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>YOUR-TOOLCHAIN-gcc -mabi=ilp32e -march=rv32emzca_zcb -c -o revnato.o revnato.s
</code></pre></div></div>

<p>We can look at the disassembly to check the binary size:†</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>YOUR-TOOLCHAIN-objdump -S revnato.o &gt; revnato.dump
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00000000 &lt;revnato&gt;:
   0:	00000697          	auipc	a3,0x0
   4:	00068693          	mv	a3,a3

00000008 &lt;revnato_loop&gt;:
   8:	8110                	lbu	a2,0(a0)
   a:	8990                	sb	a2,0(a1)
   c:	c611                	beqz	a2,18 &lt;revnato_ret&gt;
   e:	9636                	add	a2,a2,a3
  10:	8210                	lbu	a2,0(a2)
  12:	9532                	add	a0,a0,a2
  14:	0585                	addi	a1,a1,1
  16:	bfcd                	j	8 &lt;revnato_loop&gt;

00000018 &lt;revnato_ret&gt;:
  18:	8082                	ret

0000001a &lt;revnato_data&gt;:
  1a:	05070504          	.word	0x05070504
  1e:	05040704          	.word	0x05040704
  22:	04040705          	.word	0x04040705
  26:	04050804          	.word	0x04050804
  2a:	05060506          	.word	0x05060506
  2e:	04070607          	.word	0x04070607
  32:	0406                	.short	0x0406

</code></pre></div></div>

<p>Note that the <code class="language-plaintext highlighter-rouge">mv a3,a3</code>, which looks like a NOP, is actually space
reserved for a relocation. This is because we are only creating an
object file without linking, so the address of <code class="language-plaintext highlighter-rouge">revnato_data</code> is not
yet known. You can prove this by generating a linked ELF with
<code class="language-plaintext highlighter-rouge">YOUR-TOOLCHAIN-gcc -mabi=ilp32e -march=rv32emzca_zcb -o revnato.elf revnato.s -nostdlib</code>
and observing in that disassembly that the instruction has now changed,
for example, to <code class="language-plaintext highlighter-rouge">addi a3,a3,-39</code>.</p>

<p>†: Alternatively, we can just run <code class="language-plaintext highlighter-rouge">readelf -S revnato.o</code> and look at the
size field of .text.</p>

<p>Finally, just for completeness, an example C program to call this function:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span><span class="kt">void</span> <span class="nf">revnato</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="p">);</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">in</span> <span class="o">=</span> <span class="s">"ECHOXRAYALFAMIKEPAPALIMAECHO"</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">out</span><span class="p">[</span><span class="mi">100</span><span class="p">];</span>
    <span class="n">revnato</span><span class="p">(</span><span class="n">in</span><span class="p">,</span> <span class="n">out</span><span class="p">);</span>
    <span class="n">puts</span><span class="p">(</span><span class="n">out</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>Adrian I. Lam</name></author><summary type="html"><![CDATA[A long time ago, I used to visit Codegolf Stack Exchange regularly, and took part in a few contests. Due to certain reasons, I have decided not to contribute to the Stack Exchange network anymore, but I recently came across a codegolf contest that looked interesting, so I thought I’d post my solution in my blog instead.]]></summary></entry><entry><title type="html">New Gachapon shop in London!</title><link href="https://adrianiainlam.tk/blog/2025/05/03/gachapon/" rel="alternate" type="text/html" title="New Gachapon shop in London!" /><published>2025-05-03T17:19:51+01:00</published><updated>2025-05-03T17:19:51+01:00</updated><id>https://adrianiainlam.tk/blog/2025/05/03/gachapon</id><content type="html" xml:base="https://adrianiainlam.tk/blog/2025/05/03/gachapon/"><![CDATA[<p>New Gachapon shop in London (Wing Yip Cricklewood)!</p>

<p>Gave up after spending £16 and still not getting Senjougahara :/</p>

<p><img src="/blog/images/2025-05-03-gachapon.0.jpg" alt="IMG20250503152112.jpg" /><br />
<a href="/blog/sig/2025-05-03-gachapon.0.jpg.sig">(Signature)</a>
<img src="/blog/images/2025-05-03-gachapon.1.jpg" alt="IMG20250503152140.jpg" /><br />
<a href="/blog/sig/2025-05-03-gachapon.1.jpg.sig">(Signature)</a></p>

<p>There are a lot other series. To name a few I’ve watched:
Oshi no Ko, KiseKoi, Frieren, Spy × Family, TenSura, Cardcaptor Sakura.</p>]]></content><author><name>Adrian I. Lam</name></author><summary type="html"><![CDATA[New Gachapon shop in London (Wing Yip Cricklewood)!]]></summary></entry><entry><title type="html">Firefox custom search engine - search.json.mozlz4</title><link href="https://adrianiainlam.tk/blog/2025/05/03/firefox-custom-search/" rel="alternate" type="text/html" title="Firefox custom search engine - search.json.mozlz4" /><published>2025-05-03T04:16:49+01:00</published><updated>2025-05-03T04:16:49+01:00</updated><id>https://adrianiainlam.tk/blog/2025/05/03/firefox-custom-search</id><content type="html" xml:base="https://adrianiainlam.tk/blog/2025/05/03/firefox-custom-search/"><![CDATA[<p>So I have an ancient custom search engine XML that was broken by remote API changes.
In trying to fix it, I inadvertently had it deleted from search.json.mozlz4,
and have to figure out how to add it back.</p>

<p>This is basically a rewrite of <a href="https://blog.onee3.org/2018/04/manually-add-a-search-engine-to-firefox-quantum/">this post by Frederick Zhang</a>.
That post is excellent, but there have been some changes since 2018.
The following is tested on the latest stable Firefox (138.0.1).</p>

<p>First download <a href="https://gist.github.com/kaefer3000/73febe1eec898cd50ce4de1af79a332a/a266410033455d6b4af515d7a9d34f5afd35beec">this Python script</a>.
Use it to decompress search.json.mozlz4 (located in the Firefox profile directory):
<code class="language-plaintext highlighter-rouge">python3 mozlz4a.py -d search.json.mozlz4 search.json</code></p>

<p>Optionally, format it with <code class="language-plaintext highlighter-rouge">python3 -m json.tool</code>.</p>

<p>Add a new object to the <code class="language-plaintext highlighter-rouge">"engines"</code> array. My example for Startpage with
custom params:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        {
            "id": "38c37483-6e61-4f86-bfaf-2b99ed7d8464",
            "_name": "Startpage (Unfiltered)",
            "_loadPath": "[profile]/searchplugins/startpage-unfiltered.xml",
            "_iconMapObj": {
                "16": "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2jkj+9YtD/vWLQ/71i0P+9otD/vaLRP72i0T+9YtE/vWLRP72i0T+9otD/vaNRP72jUT+9otF/vaLRf73kkv+9Yc///WJP//1iT//9Yk///rAmf/94Mz/+sCa//aRTv/1iUH/9ok///aJP//2i0H/9otB//aJQv/2iUL/9otC//aNRP/2jUT/9o1E//aNRP/6wpv////////////96dr/95dQ//aNRP/2kET/9pBG//aQRv/2kEb/9pBG//aRR//3lEz/95BH//mueP/7xJ3/959g//efYf/4p23//vDm//3p2//3kEr/95FJ//aRSf/niFH/95FK//aRSv/2mE//95hS/vq4iP/////////////////81bj/95xZ//q4iP//////+bF+//eZT//njFT/PSqi/2xGjv/2mVD/951V/vedVv783cX///////vQrf/++PP///////748//+8uj///////m3gf/olFr/PSuj/w8Pt/9sSJD/951V//eeWf73oVv++8ul///////5sXf/+KRi//vRsf////////////3r3v/olF//Piyk/w8Pt/9sSJH/+J5Z//ieWv/3oV/++KZf/vihXP/97N7//vn0//zTs//6wJP/+bBy//q6iP/onW//Piyl/w8Pt/8fGbH/m2iB/+icY//4pGD/96hl/viqZf74pmD/+Kxr//3iy/////////n1//ivbP/onGj/Pi2m/w8Pt/8uJKz/fFeQ/x8Zsf8+Lqb/6J9r//ivbP74rm3++Klm//mpZv/5q2f/+bR9//m0e//poW7/Pi6n/w8Pt/9sTZj/+Ktp//ira/+rd4P/Dw+3/4xijv/5snH++LN1/vmvbf/5r23/+a5t//mvb//4r2//TTuk/w8Pt/8fGrL/6ah1//ivcP/4r3P/q3yI/w8Pt/+MZpP/+bN5/vm4ev75t3X/+bV1//m1df/5t3X/+Ld3/8qUhP98XZn/Hxqz/+mse//5t3f/2p+B/x8as/8PD7f/u4qK//m7fv76u4D++bl7//m3fP/5uXz/+bl8//m5fP/5t3z/+bl//x8as/9NPKf/fWCb/x8as/8PD7f/bVOh//q5f//6v4X++sGI/vm9g//5voX/+b6F//m9hf/6vYX/+r6F//nCh/+bepr/Hxu0/w8Pt/8PD7f/fWOh//q+hf/6wof/+saN/vrGjf75xIv/+ceL//nEi//5xIv/+sSL//rHi//6x43/+ceN/+m7kP+7lpj/6ruQ//rHkP/6x43/+seQ//rLlf76ypT++seR//rJkf/6yZH/+seR//rJkf/6yZH/+8mR//vJlP/7yZT/+smU//rJlP/6yZT/+8yV//rJlf/6zpn+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
            },
            "_metaData": {
                "loadPathHash": "&lt;SEE BELOW&gt;",
                "alias": "sp",
                "hideOneOffButton": false,
                "order": 8
            },
            "_urls": [
                {
                    "params": [
                        {
                            "name": "query",
                            "value": "{searchTerms}"
                        },
                        {
                            "name": "cat",
                            "value": "web"
                        },
                        {
                            "name": "lui",
                            "value": "english"
                        },
                        {
                            "name": "prfe",
                            "value": "c774daad5dd23ae2db70252aa321b9354508577d11ee4a79664d62c5d56672dd946c593d0daa5e9f3e7d1ebafaf773669d191e7b802605a743232b31a2254feeccf8ccc0f306bf45f8fa73f3"
                        }
                    ],
                    "rels": [],
                    "template": "https://www.startpage.com/sp/search",
                    "method": "POST"
                }
            ],
            "_orderHint": null,
            "_telemetryId": null,
            "_filePath": "&lt;ABSOLUTE PATH TO PROFILE DIRECTORY&gt;/searchplugins/startpage-unfiltered.xml",
            "_definedAliases": [],
            "_updateInterval": null,
            "_updateURL": null
        }
</code></pre></div></div>

<p>I assume the <code class="language-plaintext highlighter-rouge">"id"</code> is just some UUID. I ended up reusing an existing one that I no longer needed.
I’m not sure whether it would be “validated” in any way.</p>

<p>The <code class="language-plaintext highlighter-rouge">"loadPathHash"</code> is calculated using the following function:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> function getVerificationHash(name, profileDir = PathUtils.profileDir) {
   let disclaimer =
     "By modifying this file, I agree that I am doing so " +
     "only within $appName itself, using official, user-driven search " +
     "engine selection processes, and in a way which does not circumvent " +
     "user consent. I acknowledge that any attempt to change this file " +
     "from outside of $appName is a malicious act, and will be responded " +
     "to accordingly.";

  let salt =
    PathUtils.filename(profileDir) +
    name +
    disclaimer.replace(/\$appName/g, Services.appinfo.name);

  let data = new TextEncoder().encode(salt);
  let hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
    Ci.nsICryptoHash
  );
  hasher.init(hasher.SHA256);
  hasher.update(data, data.length);

  return hasher.finish(true);
}
</code></pre></div></div>

<p>This code is taken from the Firefox source code <a href="https://searchfox.org/mozilla-central/rev/4c065f1df299065c305fb48b36cdae571a43d97c/toolkit/components/search/SearchUtils.sys.mjs">here</a>.
It is to be pasted into the Browser Console, then called with
<code class="language-plaintext highlighter-rouge">getVerificationHash("&lt;VALUE IN _loadPath&gt;", "&lt;ABSOLUTE PATH TO PROFILE DIRECTORY&gt;")</code>.
The second argument is required if you are running on a different profile.</p>

<p>The <code class="language-plaintext highlighter-rouge">"order"</code> is just whichever unused number that comes next.</p>

<p>Save this, then compress it again with:
<code class="language-plaintext highlighter-rouge">python3 mozlz4a.py search.json search.json.mozlz4</code>.</p>

<p>Finally, create a backup of the original search.json.mozlz4,
and replace it with the new one.</p>]]></content><author><name>Adrian I. Lam</name></author><summary type="html"><![CDATA[So I have an ancient custom search engine XML that was broken by remote API changes. In trying to fix it, I inadvertently had it deleted from search.json.mozlz4, and have to figure out how to add it back.]]></summary></entry><entry><title type="html">Manjaro PinePhone no sound after October 2023 update</title><link href="https://adrianiainlam.tk/blog/2023/10/11/manjaro-pinephone-no-sound/" rel="alternate" type="text/html" title="Manjaro PinePhone no sound after October 2023 update" /><published>2023-10-11T21:31:12+01:00</published><updated>2023-10-11T21:31:12+01:00</updated><id>https://adrianiainlam.tk/blog/2023/10/11/manjaro-pinephone-no-sound</id><content type="html" xml:base="https://adrianiainlam.tk/blog/2023/10/11/manjaro-pinephone-no-sound/"><![CDATA[<p>After running a software upgrade yesterday (10 October 2023) with
<code class="language-plaintext highlighter-rouge">sudo pacman -Syu</code>, my PinePhone ended up with no sound.</p>

<p>See <a href="https://forum.manjaro.org/t/arm-unstable-no-sound-with-pinephone-after-update/140727/19">this post on the Manjaro forum</a> for my fix. To summarize:</p>

<ul>
  <li>I downgraded everything by following the “Downgrading all upgraded
packages” section in <a href="https://linuxconfig.org/how-to-rollback-pacman-updates-in-arch-linux">this article</a> (parsing /var/log/pacman.log
and restoring from /var/cache/pacman/pkg/), and uninstalled
alsa-ucm-pinephone. I then re-upgraded and manually reinstalled
alsa-ucm-pinephone, and it just magically worked.</li>
  <li>I suspect an easier fix will be to just edit
/usr/share/alsa/ucm2/conf.d/simple-card/PinePhone.conf
and only keep <code class="language-plaintext highlighter-rouge">Syntax 2</code> and the <code class="language-plaintext highlighter-rouge">SectionUseCase."HiFi"</code> and
<code class="language-plaintext highlighter-rouge">SectionUseCase."Voice Call"</code> sections, and remove everything
else.</li>
</ul>]]></content><author><name>Adrian I. Lam</name></author><summary type="html"><![CDATA[After running a software upgrade yesterday (10 October 2023) with sudo pacman -Syu, my PinePhone ended up with no sound.]]></summary></entry><entry><title type="html">LibDem misleading bar chart</title><link href="https://adrianiainlam.tk/blog/2023/07/03/libdem-bar-chart/" rel="alternate" type="text/html" title="LibDem misleading bar chart" /><published>2023-07-03T20:40:06+01:00</published><updated>2023-07-03T20:40:06+01:00</updated><id>https://adrianiainlam.tk/blog/2023/07/03/libdem-bar-chart</id><content type="html" xml:base="https://adrianiainlam.tk/blog/2023/07/03/libdem-bar-chart/"><![CDATA[<p>LibDem MP Daisy Cooper’s newsletter for St Albans contains a misleading
bar chart where a 0% bar will have a height of 6 mm (i.e. the baseline of
the chart is at -3.5%). In other words, if you stack LAB’s 9% and CON’s 39%
(total 48%), it will be taller than LIBDEM’s 50%.</p>

<p><img src="/blog/images/2023-07-03-libdem-bar-chart.0.png" alt="Measurement and regression analysis" /><br />
<a href="/blog/sig/2023-07-03-libdem-bar-chart.0.png.sig">(Signature)</a>
<img src="/blog/images/2023-07-03-libdem-bar-chart.1.jpg" alt="Photograph with measurement (LIBDEM)" /><br />
<a href="/blog/sig/2023-07-03-libdem-bar-chart.1.jpg.sig">(Signature)</a>
<img src="/blog/images/2023-07-03-libdem-bar-chart.2.jpg" alt="Photograph with measurement (CON)" /><br />
<a href="/blog/sig/2023-07-03-libdem-bar-chart.2.jpg.sig">(Signature)</a>
<img src="/blog/images/2023-07-03-libdem-bar-chart.3.jpg" alt="Photograph with measurement (LAB)" /><br />
<a href="/blog/sig/2023-07-03-libdem-bar-chart.3.jpg.sig">(Signature)</a></p>]]></content><author><name>Adrian I. Lam</name></author><summary type="html"><![CDATA[LibDem MP Daisy Cooper’s newsletter for St Albans contains a misleading bar chart where a 0% bar will have a height of 6 mm (i.e. the baseline of the chart is at -3.5%). In other words, if you stack LAB’s 9% and CON’s 39% (total 48%), it will be taller than LIBDEM’s 50%.]]></summary></entry><entry><title type="html">PinePhone (Manjaro Phosh) automatic suspend bug</title><link href="https://adrianiainlam.tk/blog/2023/06/15/pinephone-auto-suspend/" rel="alternate" type="text/html" title="PinePhone (Manjaro Phosh) automatic suspend bug" /><published>2023-06-15T22:23:51+01:00</published><updated>2023-06-15T22:23:51+01:00</updated><id>https://adrianiainlam.tk/blog/2023/06/15/pinephone-auto-suspend</id><content type="html" xml:base="https://adrianiainlam.tk/blog/2023/06/15/pinephone-auto-suspend/"><![CDATA[<p>On 2023-06-06 I ran a system upgrade on my PinePhone (running Manjaro Phosh)
using <code class="language-plaintext highlighter-rouge">pacman -Syu</code>. Afterwards, automatic suspend stopped working.</p>

<h2 id="the-symptoms">The symptoms</h2>

<p>If you are in the lock screen and have the display turned off (this can
due to automatic blank screen, or due to manually pressing the power button),
then shortly before the automatic suspend timeout, the notification blue LED
will light up.</p>

<p>Previously (before the upgrade), if you ignore the notification, after
reaching the timeout, the notification will be cleared, and the PinePhone
will be suspended. If you turn on the screen before it suspended, it will
very briefly show a notification that the phone will be automatically
suspended. However the notification will clear itself almost immediately.</p>

<p>After the upgrade, the notification LED stays on, and the phone does not
suspend. When the screen is turned on again, it will always display the
notification (which will clear itself very quickly). Most notably, because
it does not actually suspend, <em>the battery drains itself within a few hours</em>.
I’d go to work in the morning (unplugging from the charger at 08:30),
arrive back home at 18:00 and the phone has already turned itself off.</p>

<h2 id="the-solution">The solution</h2>

<p>This has been provided by <a href="https://matrix.to/#/@alaraajavamma:urheiluaki.org">@alaraajavamma:urheiluaki.org</a> from
<a href="https://matrix.to/#/#pinephone:matrix.org">#pinephone</a>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gsettings set sm.puri.phosh.notifications wakeup-screen-triggers []
</code></pre></div></div>

<p>Note that this needs to be run on a terminal on the PinePhone.
<strong>It does not work via <code class="language-plaintext highlighter-rouge">ssh</code></strong>.</p>

<p>For future reference, the original value was <code class="language-plaintext highlighter-rouge">['urgency']</code>.</p>

<h2 id="other-observations">Other observations</h2>

<p>Things we tried and found out before reaching the solution.</p>

<ul>
  <li>Automatic suspend actually works if the display is turned on
and not in the the lock screen.</li>
  <li><code class="language-plaintext highlighter-rouge">sudo systemctl suspend</code> - works.</li>
  <li><code class="language-plaintext highlighter-rouge">systemctl suspend</code> - works on a terminal on the PP.
Does not work via <code class="language-plaintext highlighter-rouge">ssh</code>.</li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">systemd-inhibit --list</code> gives the following output:</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>WHO            UID  USER    PID  COMM           WHAT                                                     WHY                                          MODE
ModemManager   0    root    3533 ModemManager   sleep                                                    ModemManager needs to reset devices          delay
NetworkManager 0    root    3513 NetworkManager sleep                                                    NetworkManager needs to turn off networks    delay
UPower         0    root    3993 upowerd        sleep                                                    Pause device polling                         delay
eg25manager    0    root    3311 eg25-manager   sleep                                                    eg25manager needs to prepare modem for sleep delay
manjaro        1000 manjaro 3826 phosh          handle-power-key                                         Phosh handling power key                     block
manjaro        1000 manjaro 4045 gsd-media-keys handle-power-key:handle-suspend-key:handle-hibernate-key GNOME handling keypresses                    block
manjaro        1000 manjaro 4045 gsd-media-keys sleep                                                    GNOME handling keypresses                    delay
manjaro        1000 manjaro 4046 gsd-power      sleep                                                    GNOME needs to lock the screen               delay
manjaro        1000 manjaro 3826 phosh          sleep                                                    Phosh handling suspend                       delay

9 inhibitors listed.
</code></pre></div>    </div>
  </li>
</ul>]]></content><author><name>Adrian I. Lam</name></author><summary type="html"><![CDATA[On 2023-06-06 I ran a system upgrade on my PinePhone (running Manjaro Phosh) using pacman -Syu. Afterwards, automatic suspend stopped working.]]></summary></entry><entry><title type="html">Playing 夏ノ終熄 Natsu no Owari on Wine</title><link href="https://adrianiainlam.tk/blog/2023/03/26/natsu-no-owari-wine/" rel="alternate" type="text/html" title="Playing 夏ノ終熄 Natsu no Owari on Wine" /><published>2023-03-26T08:09:28+01:00</published><updated>2023-03-26T08:09:28+01:00</updated><id>https://adrianiainlam.tk/blog/2023/03/26/natsu-no-owari-wine</id><content type="html" xml:base="https://adrianiainlam.tk/blog/2023/03/26/natsu-no-owari-wine/"><![CDATA[<p>When playing this game on Wine 6.17 (64-bit), after the first day, I got this error on a pop-up:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Failed to call ConnectFilters( pSrc, pMPEG1Splitter ). : [0x80004005] Error: 0x80004005
</code></pre></div></div>

<p>When running on the command line I get this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0024:fixme:quartz:mpeg_splitter_sink_query_accept Unsupported subtype {e436eb84-524f-11ce-9f53-0020af0ba770}.
</code></pre></div></div>

<p>Searched online for these messages and found a few different solutions, but the only one that worked for me was <a href="https://forum.winehq.org/viewtopic.php?p=138631#p138631">this one by jkfloris</a>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>winetricks l3codecx directshow wmp10
</code></pre></div></div>]]></content><author><name>Adrian I. Lam</name></author><summary type="html"><![CDATA[When playing this game on Wine 6.17 (64-bit), after the first day, I got this error on a pop-up:]]></summary></entry><entry><title type="html">Using spectral analysis to watch a YouTube video</title><link href="https://adrianiainlam.tk/blog/2022/08/29/spectral-analysis-gamabooks-youtube-video/" rel="alternate" type="text/html" title="Using spectral analysis to watch a YouTube video" /><published>2022-08-29T01:49:02+01:00</published><updated>2022-08-29T01:49:02+01:00</updated><id>https://adrianiainlam.tk/blog/2022/08/29/spectral-analysis-gamabooks-youtube-video</id><content type="html" xml:base="https://adrianiainlam.tk/blog/2022/08/29/spectral-analysis-gamabooks-youtube-video/"><![CDATA[<p>So I went to watch a video on YouTube:
<a href="https://www.youtube.com/watch?v=7fSq0_Ljb_s">https://www.youtube.com/watch?v=7fSq0_Ljb_s</a></p>

<p>And about a minute in, I noticed some strange high frequency in both audio channels.
Here’s a sample:</p>

<audio controls="" src="/blog/images/2022-08-29-spectral-analysis-gamabooks-youtube-video.2.wav"></audio>
<p><a href="/blog/sig/2022-08-29-spectral-analysis-gamabooks-youtube-video.2.wav.sig">(Signature)</a></p>

<p>Not sure what this is, I decided to download it to investigate further:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ youtube-dl -F 'https://www.youtube.com/watch?v=7fSq0_Ljb_s'
</code></pre></div></div>

<p>One of the output lines is:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>140          m4a        audio only tiny  129k , m4a_dash container, mp4a.40.2@129k (44100Hz), 84.38MiB
</code></pre></div></div>

<p>So:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ youtube-dl -f 140 'https://www.youtube.com/watch?v=7fSq0_Ljb_s'
$ ffmpeg -i '【GAMABOOKS_コラボ企画】星空古本市開催！【新人VTuber】-7fSq0_Ljb_s.m4a' -ss 02:00 -t 30 gamabooks_cut.wav
$ octave-cli
octave:1&gt; [y, Fs] = audioread('gamabooks_cut.wav');
octave:2&gt; Y = fft(y);
octave:3&gt; plot(abs(Y));
</code></pre></div></div>

<p><img src="/blog/images/2022-08-29-spectral-analysis-gamabooks-youtube-video.0.png" alt="fft spectrum" /><br />
<a href="/blog/sig/2022-08-29-spectral-analysis-gamabooks-youtube-video.0.png.sig">(Signature)</a></p>

<p>Here’s our high frequency. Now zoom in to find the FFT index number:</p>

<p><img src="/blog/images/2022-08-29-spectral-analysis-gamabooks-youtube-video.1.png" alt="fft zoomed" /><br />
<a href="/blog/sig/2022-08-29-spectral-analysis-gamabooks-youtube-video.1.png.sig">(Signature)</a></p>

<p>The peak is at index 338543. Convert it to hertz (and note the 1-index):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>octave:4&gt; (338543-1) / length(y) * Fs
ans =    1.1285e+04
</code></pre></div></div>

<p>So now we just need a way to suppress this frequency. Searching “mpv audio filter”
online tells us that <code class="language-plaintext highlighter-rouge">mpv --af=help</code> will list all audio filters available. Looking
through the list, I found:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bandreject       Apply a two-pole Butterworth band-reject filter.
</code></pre></div></div>

<p>So I just need to find out how to configure this filter. Searching online,
I eventually came across some scripts that referenced the <a href="https://ffmpeg.org/ffmpeg-filters.html#equalizer">FFmpeg manual</a>,
specifically, the “equalizer” filter: (emphasis mine)</p>

<blockquote>
  <p>Apply a two-pole peaking equalisation (EQ) filter. With this filter, the
signal-level at and around a selected frequency can be increased or decreased,
whilst <strong>(unlike bandpass and bandreject filters) that at all other frequencies is unchanged.</strong></p>
</blockquote>

<p>Sounds like what I want. So to try it, with reference to the manual (the bandwidth
and attenuation factor were determined by trial-and-error):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ mpv --af=equalizer=f=11285:t=h:width=5:g=-100 gamabooks_cut.wav
</code></pre></div></div>

<p>Sounds a lot better. Now download the video:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ youtube-dl -f 136 'https://www.youtube.com/watch?v=7fSq0_Ljb_s'
</code></pre></div></div>

<p>And play it:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ mpv 【GAMABOOKS_コラボ企画】星空古本市開催！【新人VTuber】-7fSq0_Ljb_s.mp4 --audio-file=【GAMABOOKS_コラボ企画】星空古本市開催！【新人VTuber】-7fSq0_Ljb_s.m4a --af=equalizer=f=11285:t=h:width=5:g=-100
</code></pre></div></div>

<p>Done!</p>]]></content><author><name>Adrian I. Lam</name></author><summary type="html"><![CDATA[So I went to watch a video on YouTube: https://www.youtube.com/watch?v=7fSq0_Ljb_s]]></summary></entry><entry><title type="html">Arbitrariness and randomness</title><link href="https://adrianiainlam.tk/blog/2022/06/30/veritasium-arbitrary-vs-random/" rel="alternate" type="text/html" title="Arbitrariness and randomness" /><published>2022-06-30T21:19:13+01:00</published><updated>2022-06-30T21:19:13+01:00</updated><id>https://adrianiainlam.tk/blog/2022/06/30/veritasium-arbitrary-vs-random</id><content type="html" xml:base="https://adrianiainlam.tk/blog/2022/06/30/veritasium-arbitrary-vs-random/"><![CDATA[<p><img src="/blog/images/2022-06-30-veritasium-arbitrary-vs-random.0.png" alt="Screenshot from 2022-06-30 20-53-50.png" /><br />
<a href="/blog/sig/2022-06-30-veritasium-arbitrary-vs-random.0.png.sig">(Signature)</a></p>

<p>Veritasium seems to think that “arbitrariness” is the same as “randomness”.
Without true randomness, upon repetition, the guard will gain information on
how the renumbering method was chosen, thus decreasing the prisoners’
probability of success.</p>]]></content><author><name>Adrian I. Lam</name></author><summary type="html"><![CDATA[(Signature)]]></summary></entry><entry><title type="html">Cantilever Rock</title><link href="https://adrianiainlam.tk/blog/2022/05/23/cantilever-rock/" rel="alternate" type="text/html" title="Cantilever Rock" /><published>2022-05-23T01:45:01+01:00</published><updated>2022-05-23T01:45:01+01:00</updated><id>https://adrianiainlam.tk/blog/2022/05/23/cantilever-rock</id><content type="html" xml:base="https://adrianiainlam.tk/blog/2022/05/23/cantilever-rock/"><![CDATA[<p>I just learnt that there is something called “Cantilever Rock” / “Cantilever Stone” in Wales.</p>

<p>Uh, what? It looks more like a simply supported beam than a cantilever…</p>

<p>Photo: “Cantilever Stone on Glyder Fach” by George Tod, CC BY-SA 2.0 <a href="https://www.geograph.org.uk/photo/611071">https://www.geograph.org.uk/photo/611071</a></p>

<p><img src="/blog/images/2022-05-23-cantilever-rock.0.jpg" alt="cantilever-rock.jpg" /><br />
<a href="/blog/sig/2022-05-23-cantilever-rock.0.jpg.sig">(Signature)</a></p>]]></content><author><name>Adrian I. Lam</name></author><summary type="html"><![CDATA[I just learnt that there is something called “Cantilever Rock” / “Cantilever Stone” in Wales.]]></summary></entry></feed>