Blog

Note #20578

Kids: We want porridge for breakfast.
Me: How do you want it?
Kids: Surprise us.

Presenting… oatmeal, honey and sultanas with a toasted marshmallow.

Two bowls of porridge with sultanas, each topped with a jumbo marshmallow (one pink, one white) being toasted by a blowtorch (mostly off-camera).

×

My First MP3

Duration

Podcast Version

This post is also available as a podcast. Listen here, download for later, or subscribe wherever you consume podcasts.

Somebody shared with me a tweet about the tragedy of being a Gen X’er and having to buy all your music again and again as formats evolve. Somebody else shared with me Kyla La Grange‘s cover of a particular song .Together… these reminded me that I’ve never told you the story of my first MP31

Screenshot of tweet by @bewgtweets posted Oct 17, 2021, reading: If you want to know why Gen X’ers are always mad it’s because we had to replace our record collections with a tape collection that was then replaced with a cd collection that was then replaced with MP3’s and damn it how many time must I pay to listen to grunge
I didn’t/don’t own much vinyl – perhaps mostly because I had a tape deck in my bedroom years before a record player – but I’ve felt this pain. And don’t get me started on the videogames I’ve paid for multiple times.

In the Summer of 1995 I bought the CD single of the (still excellent!) Set You Free by N-Trance.2 I’d heard about this new-fangled “MP3” audio format, so soon afterwards I decided to rip a copy of the song to my PC.

I was using a 66MHz 486SX CPU, and without an embedded FPU I didn’t quite have the spare processing power to rip-and-encode in a single pass.3 So instead I first ripped to an uncompressed PCM .wav file and then performed the encoding: the former step was done almost in real-time (I listened to the track as it ripped!), about 7 minutes. The latter step took about 20 minutes.

So… about half an hour in total, to rip a single song.

Dan, as a teenager, sits at a desk with his hand to his chin. In the foreground, a beiege two-button wired ball-type computer mouse rests on the corner of the desk. Dan is wearing a black t-shirt with a red devil face printed onto it.
Progress bar, you say? I’ll just sit here and wait then, I guess. Actual contemporary-ish photo.

Creating a (what would now be considered an apalling) 32kHz mono-channel file, this meant that I briefly stored both a 27MB wave file and the final ~4MB MP3 file. 31MB might not sound huge, but I only had a total of 145MB of hard drive space at the time, so 31MB consumed over a fifth of my entire fixed storage! Even after deleting the intermediary wave file I was left with a single song consuming around 3% of my space, which is mind-boggling to think about in hindsight.

But it felt like magic. I called my friend Gary to tell him about it. “This is going to be massive!” I said. At the time, I meant for techy people: I could imagine a future in which, with more hard drive space, I’d keep all my music this way… or else bundle entire artists onto writable CDs in this new format, making albums obsolete. I never considered that over the coming decade or so the format would enter the public consciousness, let alone that it’d take off like it did.

A young man in jeans and a blue coat stands on the patio in the back garden of a terraced house, dropping a half-brick onto the floor. In the background, an unused rabbit hutch and a dustbin can be seen. The photo is clearly taken using a flash, at night.
If you’re thinking of Gary and I as the kind of reprobates who helped bring on the golden age of music piracy… I’d like to distract you with a bigger show of yobbish behaviour in the form of this photo from the day we played at dropping half-bricks onto starter pistol ammunition.

The MP3 file I produced had a fault. Most of the way through the encoding process, I got bored and ran another program, and this must’ve interfered with the stream because there was an audible “blip” noise about 30 seconds from the end of the track. You’d have to be listening carefully to hear it, or else know what you were looking for, but it was there. I didn’t want to go through the whole process again, so I left it.

But that artefact uniquely identified that copy of what was, in the end, a popular song to have in your digital music collection. As the years went by and I traded MP3 files in bulk at LAN parties or on CD-Rs or, on at least one ocassion, on an Iomega Zip disk (remember those?), I’d ocassionally see N-Trance - (Only Love Can) Set You Free.mp34 being passed around and play it, to see if it was “my” copy.

Sometimes the ID3 tags had been changed because for example the previous owner had decided it deserved to be considered Genre: Dance instead of Genre: Trance5. But I could still identify that file because of the audio fingerprint, distinct to the first MP3 I ever created.

I still had that file when I went to university (where it occupied a smaller proportion of my hard drive space) and hearing that distinctive “blip” would remind me about the ordeal that was involved in its creation. I don’t have it any more, but perhaps somebody else still does.

Footnotes

1 I might never have told this story on my blog, but eagle-eyed readers may remember that I’ve certainly hinted at it before now.

2 Rewatching that music video, I’m struck by a recollection of how crazy popular crossfades were on 1990s dance music videos. More than just a transition, I’m pretty sure that most of the frames of that video are mid-crossfade: it feels like I’m watching Kelly Llorenna hanging out of a sunroof but I accidentally left one of my eyeballs in a smoky nightclub and can still see out of it as well.

3 I initially tried to convert directly from red book format to an MP3 file, but the encoding process was too slow and the CD drive’s buffer filled up and didn’t get drained by the processor, which was still presumably bogged down with framing or fourier-transforming earlier parts of the track. The CD drive reasonably assumed that it wasn’t actually being used and spun-down the drive motor, and this caused it to lose its place in the track, killing the whole process and leaving me with about a 40 second recording.

4 Yes, that filename isn’t quite the correct title. I was wrong.

5 No, it’s clearly trance. They were wrong.

× ×

XPath Scraping with FreshRSS

I’ve been spending a while running on reduced brain capacity lately so, to ease myself back into thinking like a programmer, I upgraded my preferred feed reader FreshRSS to version 1.20.0 – which was released a couple of weeks ago – and tried out what I believe is its killer new feature: HTML + XPath scraping.

Screenshot showing Beverley Newing's weblog; two articles are visible - Paperback copy of 'Disability Visibility', edited by Alice Wong, next to a cup of tea Setting up an Accessibility Book Club, published on 1 March 2022, and Reflecting on 2021, published on 1 January 2022.
I like to keep up-to-date with my friend Bev’s blog, but they don’t have an RSS feed.

I’ve been using RSS1 for about 20 years and I love it. It feels great to be able to curate my updates based on “what I care about”, and not on “what some social network thinks I should care about”, to keep things to read later, to prioritise effectively based on my own categorisation, to consume content offline and have my to-read list synchronise later, etc.

RSS never went away, of course (what do you think a podcast is?), but it got steamrollered out of the public eye by big companies who make their money out of keeping your eyes on their platforms and off the open Web. But it feels like it’s slowly coming back: even Substack – whose entire thing is that an email client is more-convenient than a feed reader for most people – launched an RSS reader this week!

A smartphone on a wooden surface. The screen shows the FeedMe app, showing the most-recent blog post from Beverley's blog.
My day usually starts in my feed reader, accessed via the FeedMe app from my mobile (although FreshRSS provides a reasonably good responsive interface out-of-the-box!)

I love RSS so much that I routinely retrofit other people’s websites with feeds just so I can subscribe to them: I even published the tool I use to do so! Whether filtering sports headlines out of BBC News, turning retro webcomics into “reading lists” so I can track my progress, or just working around sites that really should have feeds but refuse to, I just love sidestepping these “missing feeds”. My friend Beverley has a blog without any kind of feed, so I added one so I could subscribe to it. Magic.

But with FreshRSS 1.20.0, I no longer have to maintain my own tool to get this brilliant functionality, and I’m overjoyed. Let’s look at how it works by re-subscribing to Beverley’s blog but without a middleware tool.

Screenshot showing FetchRSS being used to graphically create a feed from Beverley's blog.
This post is about to get pretty technical. If you don’t want to learn some XPath but just want to make a feed out of a web page, use a graphical tool like FetchRSS.

In the latest version of FreshRSS, when you add a new feed to your reader, a new section “Type of feed source” is available. Unfold it, and you can change from the default (“RSS / Atom”) to the new option “HTML + XPath (Web scraping)”. Put a human-readable page address rather than a feed address into the “Feed URL” field and fill these fields to tell FreshRSS how to parse the page to get the content you want. Note that it doesn’t matter if the web page isn’t valid XML (e.g. missing closing tags) because it’s going to get run through PHP’s DOMDocument anyway which will “correct” for some really sloppy code if needed.

Browser debugger running document.evaluate('//li[@class="blog__post-preview"]', document).iterateNext() on Beverley's weblog and getting the first blog entry.
You can use your browser’s debugger to help check your XPath rules: here I’ve run  document.evaluate('//li[@class="blog__post-preview"]', document).iterateNext() and got back the first blog post on the page, so I know I’m on the right track.
You’ll need to use XPath to express how to find a “feed item” on the page. Here’s the rules I used for https://webdevbev.co.uk/blog.html (many of these fields were optional – I didn’t have to do this much work):
  • Feed title: //h1
    I override this anyway in FreshRSS, so I could just have used the a string, but I wanted the XPath practice. There’s only one <h1> on the page, and it can be considered the “title” of the feed.
  • Finding items: //li[@class="blog__post-preview"]
    Each “post” on the page is an <li class="blog__post-preview">.
  • Item titles: descendant::h2
    Each post has a <h2> which is the post title. The descendant:: selector scopes the search to each post as found above.
  • Item content: descendant::p[3]
    Beverley’s static site generator template puts the post summary in the third paragraph of the <li>, which we can select like this.
  • Item link: descendant::h2/a/@href
    This expects a URL, so we need the /@href to make sure we get the value of the <h2><a href="...">, rather than its contents.
  • Item thumbnail: descendant::img[@class="blog__image--preview"]/@src
    Again, this expects a URL, which we get from the <img src="...">.
  • Item author: "Beverley Newing"
    Beverley’s blog doesn’t host any guest posts, so I just use a string literal here.
  • Item date: substring-after(descendant::p[@class="blog__date-posted"], "Date posted: ")
    This is the only complicated one: the published dates on Beverley’s blog aren’t explicitly marked-up, but part of a string that begins with the words “Date posted: “, so I use XPath’s substring-after function to strtip this. The result gets passed to PHP’s strtotime(), which is pretty tolerant of different date formats (although not of the words “Date posted:” it turns out!).
Screenshot: Adding a "HTML + XPath (Web scraping)" feed via FreshRSS.
I’d love one day for FreshRSS to provide some kind of “preview” feature here so you can see what you’ll expect to get back, as you work. That, and support for different input types (JSON, perhaps?), perhaps other selectors (I find CSS-style selectors much simpler than XPath), and maybe even an option to execute Javascript on the page before scraping (I use this in my own toolchain, but that’s just because I want to have my cake and eat it too). But this is still all pretty awesome.

I hope that this is just the beginning for this new killer feature in FreshRSS: there’s so much more it can be and do. But for now, I’m still mighty impressed that I can begin to phase-out my use of my relatively resource-intensive feed-building middleware and use my feed reader to do more and more of the heavy lifting for which I love it so much.

I also love that this functionally adds h-feed support in by the back door. I’d still prefer there to be a “h-feed” option in the “Type of feed source” drop-down, but at least I can add such support manually, now!

Beverley's blog post "Setting up an Accessibility Book Club" in FreshRSS.
The finished result: Bev’s blog posts appear directly in my feed reader, even though they don’t have a feed, and now without going through the middleware I’d set up for that purpose.

Footnotes

1 When I say RSS, I mean feed. Most of the feeds I subscribe to are RSS feeds, but some are Atom feeds, h-feed, etc. But I can’t get over the old-fashioned name, and I don’t care to try.

× × × × × ×

Note #20517

Was a 10th century speaker of Old Saxon a “Saxophone”? 🤔

All the game descriptions from the sale

This is a repost promoting content originally published elsewhere. See more things Dan's reposted.

Video game poster for "It's Probably Fine", showing a woman driving with red and blue lights behind her.

You’ve got 37 unpaid parking tickets. You just got pulled over for speeding. In your defense, you were texting your sister about how drunk you are. Plus there’s all that blood on your windshield. Obviously you know it’s deer blood, but the police officers walking toward your vehicle don’t. Still, in the time it takes them to figure that out, maybe you’ll sober up. Or escape on foot! Either way, it’ll probably be fine.

User Tags: Poor Choices / Story Rich / Multiple Endings / Parkour

Video game poster for Dead Seagull Zoo Magnate, showing dead seagulls in a cartoony style.

Collect dead seagulls and build a zoo to house them all. Beautify the zoo with artistic flair and deodorizing sprays. Design creative group promotions to stir up interest! Is that a customer? You’d better hope it’s not the owner of the live seagull zoo down the street, because he’s probably got some questions.

User Tags: Hard Work / Supply / Demand / Diseases & Parasites

Claire Hummel produced fake video game art for the Steam Summer Sale, which was already excellent, but when @g-a-y-g-o-y-l-e reblogged, asking for more context, Claire delivered and then some. Every single one of these “game descriptions” is a special kind of comedy gold… and yet somehow believable from the store that sells us Dream Daddy, IKEA VR Pancake Kitchen, Organ Trail, Oh… Sir!! The Insult Simulator, and Goat Simulator (all of which I own copies of). Go read the full list.

× ×

Covid Brain

I managed to dodge infection for 922 days of the Covid pandemic1, but it caught up with me eventually.

Lateral flow test, with "DAN" written on it, showing a solid control line and a very clear solid test line: a clear positive result.
Well, shit.

Frankly, it’s surprising that it took this long. We’ve always been careful, in accordance with guidance at any given time, nd we all got our jabs and boosters as soon as we were able… but conversely: we’ve got school-age children who naturally seem to be the biggest disease vectors imaginable. Our youngest, in fact, already had Covid, but the rest of us managed to dodge it perhaps thanks to all these precautions.

Vials of Covid vaccine scattered across a red background. Photo courtesy Maksim Goncharenok.
The vaccine provide protection, but it’s not a magical force-field.

Luckily I’m not suffering too badly, probably thanks to the immunisation. It’s still not great, but I dread to think how it might have been without the benefit of the jab! A minor fever came and went, and then it’s just been a few days of coughing, exhaustion, and… the most-incredible level of brain-fog.

Dan with the dog, in the garden.
Today, for example, I completey blanked the word “toilet” and struggled for some time to express to the dog why I’d brought her into the garden, while she stared at me expectantly.

I’ve taken the week off work to recover, which was a wise choice. As well as getting rest, it’s meant that I’ve managed to avoid writing production code with my addled brain! Instead, I’ve spent a lot of time chilling in bed and watching all of the films that I’d been meaning to! This week, I’ve watched:

  • Peggy Sue Got Married (y’know, that other mid-1980s movie about time travel and being a teenager in the 1950s). It was okay; some bits of the direction were spectacular for its age, like the “through the mirror” filming.
  • Fall. I enjoyed this more than I expected to. It’s not great, but while I spent most of the time complaining about the lack of believability in the setting and the characters’ reactions, the acting was good and the tension “worked”: it was ocassionally pretty vertigo-inducing, and that’s not just because I’ve been having some Covid-related dizziness!
  • RRR. Oh my god this Tollywood action spectacle was an adventure. At one point it’s a bromantic buddy comedy, then later there’s a dance-off, then for a while there’s a wonderful “even language can’t divide us” romance, but then later a man picks up a motorcycle with one hand and uses it to beat up an entire army, and somehow it all feels like it belongs together. The symbolism’s so thick you can spread it (tl;dr: colonialism bad), but it’s still a riot of a film.
  • Cyrano, which I feel was under-rated but that could just be that I have a soft spot for the story… and a love of musical theatre.
  • Also, at times when I didn’t think my brain had the focus for something new, I re-watched Dude, Where’s My Car? because I figured a stoner comedy that re-replains the plot every 20 minutes or so was about as good as I could expect my brain to handle at the time, and Everything Everywhere All At Once which I’ve now seen three times and loved every single one: it’s one of my favourite films.
Dan lying in bed, giving a weak "thumbs up".
See, I’m fine! (Feel like I’ve spent a lot of time lying here, this week.)

Anyway: hopefully next week I’ll be feeling more normal and my poor Covid-struck brain can be trusted with code again. Until then: time to try to rest some more.

Footnotes

1 Based on the World Health Organisation’s declaration of the outbreak being a pandemic on 11 March 2020 and my positive test on 19 September 2022, I stayed uninfected for two years, six months, one week, and one day. But who’s counting?

× × × ×

Dan Q found GCA002Z Busted out!

This checkin to GCA002Z Busted out! reflects a geocaching.com log entry. See more of Dan's cache logs.

Could have been expected to get the FTF for this one, given that it’s (a) literally 20 seconds walk from my front door and (b) the CO had indicated that one would be hidden around here, but unfortunately I contracted covid last weekend and any walk longer tab my garden was quickly leaving me exhausted. This evening I felt a little better and so the geohound and I (pictured) braved a couple of minutes in the rain to come and sign the logbook.

Note to future cachers planning to park and grab: the “layby” indicated is a working bus stop, albeit with an infrequent (every 2 hours, weekday daytimes) schedule, so remember to be a polite cacher and try not to park in it at times that it’ll be needed by the minibus!

I might need to find a new home for my replacement to GC90RH3, whose bridge hiding place is only 100m or so (less than the requisite 0.1 miles!) from this new cache! Ah well, that’ll teach me to be a slow CO!

TFTC, and for getting me out of the house for a walk for the first time since I got sick almost a week ago.

Dan with the dog, in the garden.

×

Dan Q found GC33BBW WWW#6 – www.geochecker.com

This checkin to GC33BBW WWW#6 – www.geochecker.com reflects a geocaching.com log entry. See more of Dan's cache logs.

After I found the right hiding place, this one was pretty easy, though I was initially hesitant to put my hand into it after I mistook the cache’s unusual container for (a very large version of) something else that could be laid in a place like this. TFTC.

Dan Q found GC33BAJ WWW#4 – earthcache.org

This checkin to GC33BAJ WWW#4 – earthcache.org reflects a geocaching.com log entry. See more of Dan's cache logs.

Geopup and I found quite easily while out on a walk. The excitable doggo isn’t so keen on stopping and searching for caches when there are so many new and exciting smells just over her visual horizon, so today’s expedition might only give me a couple of minutes to hunt for each: we’ll have to see if that’s enough to log any further finds this morning.

Dan Q performed maintenance for GC9Z37K Friar’s Farm – Far-Flung Fence

This checkin to GC9Z37K Friar's Farm - Far-Flung Fence reflects a geocaching.com log entry. See more of Dan's cache logs.

Found left out in clear with lid open but otherwise not vandalised. Closed and re-hid deeper into hiding place. Will add a geocaching information card on my next visit to better explain its presence to any muggles passing by.