Generating Music with Markov Chains and Alda

generating music

A while ago I read a fantastic article by Alex Bainter about how he used markov chains to generate new versions of Aphex Twin’s track ‘aisatsana’. After reading it I also wanted to try my hand at generating music using markov chains, but mix it up by tring out alda.

‘aisatsana’ is very different to the rest of Aphex Twin’s 2012 released Syro album. It’s a calm, soothing piano piece that could easily place you into a meditative state after listening to it.

Alda is a text-based programming language for music composition. If you haven’t tried it before, you’ll get a feel for how it works in this post. If you want to learn about it through some much simpler examples, this quick start guide is a good place to start.

Generating music is made easier using the simple language that alda provides.

As an example, here are 4 x samples ‘phrases’ that are generated with markov chains (based off the aisatsana starting state), and played back with Alda. I’ve picked 4 x random phrases out of 32 that sounded similar to me, but were different in each case. A generated track will not necessarily consist of all similar sounding phrases, but might contain a number of these.

Markov Chains 101

On my journey, the first stop was to learn more about markov chains.

Markov chains are mathematical “stochastic” systems that change from one “state” (a situation or set of values) to another. In addition to this a Markov chain tells you the probabilitiy of transitioning from one state to another.

Using a honey bee worker as an example we might say: A honey bee has a bunch of different states:

  • At the hive
  • Leaving the hive
  • Collecting pollen
  • Make honey
  • Returning to hive
  • Cleaning hive
  • Defending hive

After observing honey bees for a while, you might model their behaviour using a markov chain like so:

  • When at the hive they have:
    • 50% chance to make honey
    • 40% chance to leave the hive
    • 10% chance to clean the hive
  • When leaving the hive they have:
    • 95% chance to collect pollen
    • 5% chance to defend the hive
  • When collecting pollen they have:
    • 85% chance of collecting pollen
    • 10% chance of returning to hive
    • 5% chance to defend the hive
  • etc…

The above illustrates what is needed to create a markov chain. A list of states (the “state space”), and the probabilities of transitioning between them.

To play around with Markov chains and simple string generation, I created a small codebase (nodejs / typescript). The app takes a list of ‘chat messages logs’ (really any line separated list of strings) as input. It then uses random selection to find any lines containing the ‘seed’ string.

With the seed string, it generates new and potentially unique ‘chat messages’ based on this input seed and the ‘state’ (which is the list of chat messages fed in).

Using a random function and initial filtering means that the generation probability is constrained to the size of the input and filtered list, but it still helped me understand some of the concepts.

Converting Aisatana MIDI to Alda Format

To start, the first thing I needed was a list of musical segments from the original track. These are what we refer to as ‘phrases’.

As Alex did in his implementation, I grabbed a MIDI version of Aisatsana. I then fed it into a MIDI to JSON converter, yielding a breakdown of the track into individual notes. Here is what the first two notes look like:

[
  {
    "name": "E3",
    "midi": 52,
    "time": 0,
    "velocity": 0.30708661417322836,
    "duration": 0.5882355
  },
  {
    "name": "G3",
    "midi": 55,
    "time": 0.5882355,
    "velocity": 0.31496062992125984,
    "duration": 0.5882355
  }
]

From there I wrote some javascript to take these notes in JSON format, parse the time values and order them into the 32 ‘phrases’ that aisatsana is made up of.

That is, there are 32 ‘phrases’, with each consisting of 32 ‘half-beats’ at 0.294117647058824 seconds per half beat. Totalling the 301 seconds.

const notes = [] // <-- MIDI to JSON notes here

// constants specific to the aisatsana track
const secPerHalfBeat = 0.294117647058824;
const phraseHalfBeats = 32;

// Array to store quantized phrases
let phrases = [];

notes.forEach(n => {
  const halfBeat = Math.round(n.time / secPerHalfBeat);
  const phraseIndex = Math.floor(halfBeat / phraseHalfBeats);
  const note = n.name.substring(0, 1).toLowerCase();
  const octave = n.name.substring(1, 2);
  const time = n.time;
  const duration = n.duration;

  // Store note in correct 'phrase'
  if (!phrases[phraseIndex]) {
  	phrases[phraseIndex] = [];
  }

  phrases[phraseIndex].push({ note: note, octave: octave, time: time, duration: duration });
});

It also gathers information such as the note symbol, octave, and duration for each note and stores it in a phrases array, which also happens to be ordered by phrase index.

Grouping by Chord

Next, the script runs through each phrase and groups the notes by time. If a note is played at the same timestamp, that means it is part of the same chord. To play correctly with alda, I need to know this, so a chords array is setup for each phrase.

phrases.forEach(phrase => {
  let chords = []
  const groupByTime = groupBy('time');
  phrase.chords = [];
  const chordGrouping = groupByTime(phrase);

  for (let [chordTimestamp, notes] of Object.entries(chordGrouping)) {
    phrase.chords.push(notes)
  }
});

Generating alda Compatible Strings

With chord grouping done, we can now convert the track into 32 phrases that alda will understand.

phrases.forEach(phrase => {
  let aldaStr = "piano: (tempo 51) (quant 90) ";
  phrase.chords.forEach(chord => {
    if (chord.length > 1) {
      // Alda plays notes together as a chord when separated by a '/'
      // character. Generate the alda string based on whether or not
      // it needs to have multiple notes in the chord, separating with
      // '/' if so.
      for (let [idx, note] of Object.entries(chord)) {
        if (idx == chord.length - 1) {
          aldaStr += `o${note.octave} ${note.note} ${note.duration}s `;
        } else {
          aldaStr += `o${note.octave} ${note.note} ${note.duration}s / `;
        }
        
      };
    } else {
      chord.forEach(note => {
        aldaStr += `o${note.octave} ${note.note} ${note.duration}s `;
      });
    }
  });
  // Output the phrase as an alda-compatible / playable string (you can
  // also copy this directly into alda's REPL to play it)
  console.log(aldaStr);
})

Here is the full script to convert the MIDI to alda phrase strings.

Generating Music with Markov Chains

There are different entry points that I could have used to create the markov chain initial state, but I went with feeding in the alda strings directly to see what patterns would emerge.

Here are the first 4 x phrases from aisatsana in alda-compatible format:

piano: (tempo 51) (quant 90) o3 e 0.5882355s o3 g 0.5882355s o3 c 0.5882354999999999s o4 c 7.6470615s
piano: (tempo 51) (quant 90) o3 e 0.5882354999999997s o3 g 0.5882354999999997s o3 c 0.5882354999999997s o4 c 0.5882354999999997s o3 b 2.3529420000000005s o4 e 4.705884000000001s
piano: (tempo 51) (quant 90) o3 e 0.5882354999999997s o3 g 0.5882354999999997s o3 c 0.5882354999999997s o4 c 0.5882354999999997s o3 b 7.058826s
piano: (tempo 51) (quant 90) o3 e 0.5882354999999997s o3 g 0.5882354999999997s o3 c 0.5882354999999997s o4 c 0.5882354999999997s o3 b 1.1764709999999994s o4 e 5.882354999999997s

If you like, you can drop those right into alda’s REPL to play them, or drop them into a text file and play them with:

alda play --file first-four-phrases.alda

The strings are quite ugly to look at, but it turns out that they can still be used to generate new and original phrases based off the aisatsana track phrases using markov chains.

Using the markov-chains npm package, I wrote a small nodejs app to generate new phrases. It takes the 32 x alda compatible phrase strings from the original MIDI track of ‘aisatsana’ as a list of states and walks the chain to create new phrases.

E.g.

const states = [
  // [ alda phrase strings here ],
  // [ alda phrase strings here ],
  // [ alda phrase strings here ]
  // etc...
]

const chain = new Chain(states);
 
// generate new phrase(s)
const newPhrases = chain.walk();

I threw together a small function that you can run directly to generate new phrases. Give it a try here. Hitting this URL in the browser will give you new phrases from the markov generation.

If you want a text version that you can drop right into the alda REPL or into a file for alda to play try this:

curl -s https://ofaas.shogan.co.uk/function/generative-aisatana-alda | jq -r '.phrases[]'

I’ve uploaded the code here that does the markov chain generation using the initial alda phrase strings as input state.

Results and Alda Serverless

Generated Music

The results from generating music off the phrases from the original track are certainly fun and interesting to listen to. The new phrases play out in different ways to the original track, but still have the feeling of belonging to the same piece of music.

Going forward I’ll be definitely experiment further with markov chains and music generation using alda.

Experimenting with alda and Serverless

Something I got side-tracked on during this experiment was hosting the alda player in a serverless function. I got pretty far along using AWS Lambda Layers, but the road was bumpy. Alda requires some fairly chunky dependencies.

Even after managing to squeeze Java and the Alda binaries into lambda layers the audio playback engine was failing to start in a serverless function.

I managed to clear through a number of problems but eventually my patience wore down and I settled with writing my own serverless function to generate the strings to feed into alda directly.

My goal here was to generate unique phrases, output them to MIDI, and then convert them to Audio to be played almost instantenously. For now it’s easy enough to take the generated strings and drop them directly into the alda REPL or play them direct from file though.

It will be nice to see alda develop further and offer an online REPL – which would mean the engine itself would be light enough to perform the above too.

3dfx Voodoo – A Brief History and a Retro PC Build

3dfx voodoo 2 3d accelerator card

In the late ninetees, as a teenager I had the priveledge of owning a second hand Creative Labs 3dfx Voodoo 2 graphics card. It was an upgrade for my own hand-built PC – an AMD K6-2 333mhz system I had painstakingly saved up for and cobbled together.

Nostalgia running high, I recently set about building up a ‘retro’ gaming PC. With roughly the same specification as my original K6-2 machine from the late ninetees, it also features an original 3dfx Voodoo 2 accelerator card.

Unreal Tournament playing on my retro PC with the 3dfx Voodoo 2

The Brief Story of 3dfx Interactive and the Voodoo

A group of colleagues originally working together at SGI decided to break out and create 3dfx Interactive in San Jose, California (1994). This was after an initial failed attempt at selling rather pricey IrisVision boards for PCs.

Their original plan was to create specialist hardware solutions for arcade games, but they changed direction to instead design PC add-on boards. The reason for their pivot came about as a result of a few favourable factors. Namely:

  • The cost of RAM was low enough to make this a feasible endeavour.
  • RAM latency was much improved and allowed for ‘high’ clock speeds of up to 50MHz.
  • 3D games were picking up in popularity. Spearheaded initially no doubt by id software with games such as Wolfenstein 3D, DOOM, the market was clearly headed to a point where 3D accelerated graphics would be in demand.

The first design, the SST1 (marketed as the Voodoo 1), targeted the $300-$400 price range for consumer PCs. A variety of OEMs picked up the design and released their own reference design boards, leading to the success of the original Voodoo line-up of 3D accelerator cards.

Enter the Voodoo 2

After the initial success of the Voodoo 1, the 3dfx Voodoo 2 (SST2) was soon born. It had vastly improved specifications over the original SST1 design such as:

  • 100MHz clock rated EDO RAM (running at 90 MHz, 25 ns)
  • 90MHz ASICs
  • 2 x Texture Mapping Units (TMUs)

The fillrate of the Voodoo 2 was around 90 MPixels/s, and frame rates in benchmarks showed a massive uplift, nearly doubling in some cases.

My first (original) 3DFX Voodoo 2 PC Build

My first self-built (from scratch) PC was a bit of a frankenstein build. I had carefully budgeted and selected a motherboard that had onboard everything. Graphics, sound and networking. It would also run an AMD K6-2 CPU – a budget friendly option compared to Intel at that time.

The onboard graphics card shared 8MB of memory from system RAM (of which I only had 32MB total). As far as I recall it was capable of running Direct3D enabled games, albeit hobbling along with crippled frame rates.

Upgrading to a 3dfx Accelerator Card

After enduring this ‘fps hardship’ and learning more about the recently successful 3dfx graphics cards (a friend had a 4MB 3dfx Voodoo ‘1’ card), I found out about a second hand option. It was available through a friend of a friend, and would cost me 500.00 South African Rand (ZAR). This was a lot for a teen back then. I had saved up money, and bargained with my parents to combine my funds with a birthday gift contribution in order to purchase this card.

The Creative Labs 3D Blaster Voodoo 2. Mine came in almost identical packaging, but I had the 12MB version.

As I recall, I had to make massive concessions in order to fit the graphics card into my machine.

My motherboard only had 2 PCI slots. Both were horizontally in line with the CPU socket. The board design was probably never intended to house cards longer than the average PCI device at the time.

I had to remove the K6-2’s heatsink I was using and replace it with one from an older 486 machine that I had lying around. The profile of the 486 heatsink was much lower, allowing the 3dfx Voodoo 2 card to slot into a PCI slot, extending over the heatsink.

The problem now was that the 3dfx card’s lower PCB edge was now just about in direct contact with the heatsink. Another issue was that the heatsink was not made for mounting to a Socket 7 board. This forced me to lay my computer’s chassis down on it’s side in order for gravity to keep the heatsink in place!

I had a plan to keep pressure on the heatsink to maintain better contact with the CPU. Using a bit of non-conductive material between the 3dfx card’s PCB and the heatsink, I kept things in place. I also had to permanently angle a large fan blowing into the now open case.

This was the price I would pay to have my budget 3dfx voodoo 2 enabled system.

The Retro 3dfx Voodoo 2 PC Build

Recently after a bout of nostalgia and watching LGR videos, I started trawling eBay for old PC parts. The goal was to almost identically match my original PC build.

After a number of weeks of searching, I found the following parts. The motherboard was the most difficult to find (at a good price). Most Super Socket 7 boards I would find were dead and listed as spare parts.

all the parts for the Retro PC build, including the 3dfx voodoo 2 card.
Most of the parts lined up and ready for the build.
  • Iwill XA100 Aladdin V Super7 Motherboard, an AMD K6-2 300 CPU, and 64MB of PC100 SDRAM (168 pin)
  • S3 Trio 3D/2X graphics card (the Voodoo only handled 3D, and uses a loopback cable to plug into a ‘2D’ card)
  • 3DFX JoyMedia Apollo 3D fast II 12Mb Video Card PCI Voodoo2 3DFX V2 Rev A1
  • A Retro styled, beige ATX PC Chassis, Computer MIDI Tower Case
  • Creative Sound Blaster Live 5.1 Digital SB0220 PCI Sound Card
  • An 80GB IDE HDD, IDE ribbon cable, and an older DVD-R drive.
  • I used a modern 80 plus certified ATX power supply. It has a power connector with an older 20 pin layout which was perfect to re-use.

To test the hardware, I powered up the basic components on top of a cardboard box. Generally a good idea and especially so when using older hardware.

first bench test of the hardware.
‘Bench’ test of the hardware
POST checking the motherboard and components.
First POST successful, aside from USB keyboard not working (needed legacy option for USB enabled)
The ‘retro’ looking MIDI tower chassis I got for the build

Gaming on the Retro PC Build

After digging out a CD burner and an old image of Windows 98 SE, I’ve got an operating system running and have installed drivers along with a bunch of games.

They play just as I remember. I’ve installed Quake, Quake 2, Unreal Tournament, and a few others to keep me busy for now.

unreal tournament game running with a 3dfx voodoo 2 accelerator card.
Unreal Tournament in Glide 3dfx rendered mode.

The next step is to replace the temporary modern day LCD monitor I’m using right now. A period correct CRT monitor would really complete the build. I remember Samsung SyncMaster CRTs being excellent. My aim is pick one of these up to complete the system.

If you’re nostalgic like me and want to revisit the PC and games hardware of the late 1990s I highly recommend a build like this.

Follow my ‘Retro’ PC series blog posts:

Using JSONPath Queries on JSON Data

JSON data for querying with JSONPath

JSONPath does for JSON processing what XPath (defined as a W3C standard) does for XML. JSONPath queries can be super useful, and are a great addition to any developer or ops person’s toolbox.

You may want to do a quick data query, test, or run through some JSON parsing scenarios for your code. If you have your data easily available in JSON format, then using JSONPath queries or expressions can be a great way to filter your data quickly and efficiently.

JSONPath 101

JSONPath expressions use $ to refer to the outer level object. If for example you have an array at the root, $ would refer to that array.

When writing JSONPath expressions, you can use dot notation or bracket notation. For example:

  • $.animals.land[0].weight
  • $['animals']['land'][0]['weight']

You can use filter expressions to filter out specific items in your queries. For example: ?(<bool expression>)

Here is an example that would filter our collection of land animals to show only those heavier than 50.0, returning their names:

$.animals.land[?(@.weight > 50.0)].name

The wildcard character * is used to select all objects or elements.

Note the @ symbol that is used to select the ‘current’ item being iterated in the boolean expression.

There are more JSONPath syntax elements to learn about, but the above are what I find most useful and commonly required.

JSONPath Query Example

Here is a chunk of JSON data, and some basic queries that show how you can easily filter down the dataset and select what you need.

JSONPath Queries – Example 1

Find all “Report runs” where root.id is equal to a specific value:

$.runs[?(@.root.id=="af1bcd6b-406f-43f9-86b3-9f01ee211ddc")]

JSONPath Queries – Example 2 (AND operator)

Find all “Report runs” where root.id is equal to a specific value, and shell.id is equal to a specific value:

$..runs[?(@.root.id=="af1bcd6b-406f-43f9-86b3-9f01ee211ddc" && @.shell.id=='d743537e393d')]

Useful JSONPath Resources

Use this webapp to write and test JSONPath expressions live in your browser.

The Pentium Era – My Retro Computer Hardware Journey

In the mid 1990s at the age of 10 or 11 our family computer got an upgrade. Retaining it’s old beige ATX chassis, the internals were upgraded from a 486 DX2-66, to a Pentium 166 MMX. Quite the jump in processing power!

Unfortunately (for me) my parents were not keen on upgrading DOS and Windows 3.1 to the newly available Windows 98. My uncle, the seasoned computer expert, had convinced my parents that Windows 95 was evil and we wait for the bugs and other issues to be sorted out first. Not a bad software strategy in hindsight though.

DOS gaming on the Pentium

For the first couple of years I had to make do with DOS gaming, even with the ‘unlimted power’ the Pentium 166mhz processor could offer.

Windows 95 and DirectX had finally given a good, common API for game developers to use for game development. Gamers everwhere were starting to reap the rewards of this movement. Unfortunately I would have to wait until our machine got Windows 95.

For the time being games such as Duke Nukem 3D, Quake, and Carmageddon (all DOS compatible at the time) would have to do. Looking back I was quite young to be playing those games, but I knew it was all fantasy and they didn’t negatively affect me.

Windows 95 Upgrade

After a number of years, the system received the long awaited Windows 95 upgrade. Some of the first Windows 95 games I remember being able to play (and binging on) were:

  • Diablo
  • StarCraft (yes it was already 1998 by this point!)
  • POD: Planet of Death (first game to leverage the MMX instruction set as I recall)

Reliving the Pentium Days

In a moment of nostalgia, I recently picked up an old retro Pentium Compaq Armada 1592DT laptop. With Windows 98 installed, I’ve been enjoying some memories from the mid to late 1990s.

I’ve now got a bunch of DOS and early Windows games installed, including:

  • Turbo Pascal 7 (reliving my early teens programming memories)
  • Diablo. I own the boxed orginal, and loved this game. It runs great on this system.
  • DOOM
  • Duke Nukem 3D, and a bunch of old Apogee shareware games, like Terminal Velocity, Wacky Wheels, ROTT, Raptor, etc…
  • Final Reality benchmark – unfortunately this is too ‘new’ for the machine, and can only render in Direct3D Software Mode at a few frames per second.
diablo installer on the pentium system.
Diablo’s Installer – DirectX 3.0. Wow. This must have been one of the first games on Windows 95.

Even my son has shown interest in this aging system. He saw me testing some of the games out, and immediately took to playing Duke Nukem (1).

I’ve found myself getting this system out on quiet evenings to play the odd game here and there, and even tried my hand at writing some Pascal on it.

It may be clunky and extremely slow by today’s standards, but there is something magical about reliving parts of your childhood like this.

Follow my ‘Retro’ PC series blog posts:

Hashcat RTX 3090 Benchmarking and Performance

hashcat rtx 3090 benchmarking

I picked up an nVidia RTX 3090 toward the end of last year. In hindsight, I was lucky to have purchased it early on soon after release. The GPU shortage has caused a massive spike in prices and this card is now worth double what I originally paid for it! Anyway, acquisition story aside, I was curious how it would perform in a Hashcat benchmark (all) run. Here are my Hashcat RTX 3090 benchmark results.

For a quick and easy run I’m using the hashcat 6.2.2 (Windows) binary.

.\hashcat.exe -b --benchmark-all
hashcat rtx 3090 benchmark

The performance seems on-par if not slightly higher than some other RTX 3090 benchmarks I have seen around. An impressive set of results.

I am running the MSI GeForce RTX 3090 Ventus 3X OC 24GB model card. I upgraded from a GeForce 1080 Ti (12GB) model and the hashing speeds are way faster. The 3090 is a power hungry beast though. It gets hot and the fans are noisier than my 1080 Ti’s were. To ensure my system’s power delivery was up to the task, I also upgraded to a Seasonic Focus PX-850 850W 80+ Platinum at the same time.

Here is a shortened log of my benchmark –all run:

CUDA API (CUDA 11.2)
====================
* Device #1: GeForce RTX 3090, 23336/24576 MB, 82MCU

OpenCL API (OpenCL 1.2 CUDA 11.2.109) - Platform #1 [NVIDIA Corporation]
========================================================================
* Device #2: GeForce RTX 3090, skipped

Benchmark relevant options:
===========================
* --benchmark-all
* --optimized-kernel-enable

Hashmode: 0 - MD5
Speed.#1.........: 67033.9 MH/s (40.78ms) @ Accel:32 Loops:1024 Thr:1024 Vec:8

Hashmode: 10 - md5($pass.$salt)
Speed.#1.........: 66278.8 MH/s (41.26ms) @ Accel:32 Loops:1024 Thr:1024 Vec:8

Hashmode: 11 - Joomla < 2.5.18
Speed.#1.........: 64972.6 MH/s (42.10ms) @ Accel:32 Loops:1024 Thr:1024 Vec:8

Hashmode: 12 - PostgreSQL
Speed.#1.........: 64460.9 MH/s (42.44ms) @ Accel:32 Loops:1024 Thr:1024 Vec:8

Hashmode: 20 - md5($salt.$pass)
Speed.#1.........: 35775.1 MH/s (76.66ms) @ Accel:32 Loops:1024 Thr:1024 Vec:4

Hashmode: 21 - osCommerce, xt:Commerce
Speed.#1.........: 36124.8 MH/s (75.92ms) @ Accel:32 Loops:1024 Thr:1024 Vec:4

Hashmode: 22 - Juniper NetScreen/SSG (ScreenOS)
Speed.#1.........: 35747.7 MH/s (76.72ms) @ Accel:32 Loops:1024 Thr:1024 Vec:4

Hashmode: 23 - Skype
Speed.#1.........: 35632.9 MH/s (76.96ms) @ Accel:32 Loops:1024 Thr:1024 Vec:4

Hashmode: 24 - SolarWinds Serv-U
Speed.#1.........: 35107.4 MH/s (78.12ms) @ Accel:32 Loops:1024 Thr:1024 Vec:1

Hashmode: 30 - md5(utf16le($pass).$salt)
Speed.#1.........: 65511.3 MH/s (41.73ms) @ Accel:32 Loops:1024 Thr:1024 Vec:4

Hashmode: 40 - md5($salt.utf16le($pass))
Speed.#1.........: 36398.3 MH/s (75.35ms) @ Accel:32 Loops:1024 Thr:1024 Vec:4

Hashmode: 50 - HMAC-MD5 (key = $pass)
Speed.#1.........: 10893.9 MH/s (62.90ms) @ Accel:8 Loops:1024 Thr:1024 Vec:1

Hashmode: 60 - HMAC-MD5 (key = $salt)
Speed.#1.........: 22468.1 MH/s (60.99ms) @ Accel:32 Loops:512 Thr:1024 Vec:1

Hashmode: 70 - md5(utf16le($pass))
Speed.#1.........: 64396.2 MH/s (42.49ms) @ Accel:32 Loops:1024 Thr:1024 Vec:1

Hashmode: 100 - SHA1
Speed.#1.........: 21045.1 MH/s (65.11ms) @ Accel:16 Loops:1024 Thr:1024 Vec:1

Hashmode: 101 - nsldap, SHA-1(Base64), Netscape LDAP SHA
Speed.#1.........: 20874.3 MH/s (65.66ms) @ Accel:16 Loops:1024 Thr:1024 Vec:1

Hashmode: 110 - sha1($pass.$salt)
Speed.#1.........: 21217.0 MH/s (64.60ms) @ Accel:32 Loops:512 Thr:1024 Vec:1

Hashmode: 111 - nsldaps, SSHA-1(Base64), Netscape LDAP SSHA
Speed.#1.........: 20608.3 MH/s (66.51ms) @ Accel:16 Loops:1024 Thr:1024 Vec:1

Full results can be downloaded here:

As for the PC build around the RTX 3090, here are a few photos…

You might notice an AIO installed, but not connected – I was in the process of testing a dual 240mm radiator (AIO) versus a high performing Noctua air cooler, so had left it in the chassis during transition.

I’ll see if I can run the same benchmark suite on my uBuntu install and update the results here. I have not tested the RTX 3090 card under this OS yet so I’m not sure if I’ll run into any driver issues or not.

Funny story of how I ended up with an RTX 3090 (it is a bit overkill!)…

Back in September or October of 2020 (I forget when the 3xxx series launch was), I had pre-ordered the MSI RTX 3080 at MSRP. I was 180 or so in the pre-order queue after months of already waiting. My queue position was barely changing week over week and I got impatient. I saw a handful of RTX 3090 cards come in stock at a local retailer and purchased one.

These cards would generally remain in stock for a few days due to everyone holding out for the much cheaper (at the time) RTX 3080 pre-order promises.

It was a lucky break for me, as those 3080 cards never came for most in that queue. GPU mining and GPU shortages made sure of that. Prices sky rocketed. Looking up this card now I see it costs almost double what I originally paid last year (if you can even get stock that is).

Now I just hope the card lasts at least a few years or more so I don’t ever have to worry about RMA and stock levels…

The 486 era – My Computer Hardware Journey

486 processor

My computer hardware journey started in the mid 1990s, when I was about 10 or 11 years old. It started out with me dismantling and reconfiguring various 486 machines. These were also known as i486 or Intel 80486 systems.

Electronics had always been a big curiosity for me. As a child I liked to open up devices and extract their internals. I would collect PCBs and join them in nonsensical ways, imagining I had invented new and wonderful devices, capable of many great things.

To illustrate my passion for tinkering with electronics, here is a quick story going back to my toddler years: My parents had noticed my love for tools as a toddler and had tried in vain to make them safe for me. According to my father, he once added a solid lump of pratley steel putty to the end of a sharp screwdriver for me. An attempt to make it safe for me to handle. As soon as I saw this, I threw it to the ground in protest. I only wanted real tools. One of the toys I had was an old 5+1⁄4-inch floppy disk drive!

My First 486 Build

Fast forwarding to the mid 1990s, I was far more capable with tools and had begun to scavenge surplus PC hardware. My uncle owned a PC repair shop where I grew up. As far as I recall I managed to scavenge old discarded parts from his collection, as well as other sources. Schools or universities that would throw out old machines as they cycled or upgraded them, cousins or family friends that had no need for old systems, etc…

The very first working system that I managed to modify was an Acer branded 486 desktop system. It had a horizontally aligned desktop chassis, one that allowed you to place a CRT monitor on top of.

This isn’t the exact one, but mine looked just like this, minus the CDROM drive.

I remember it had a 486 SX-33 CPU. A rather weakly clocked 486 processor, that didn’t even require a heatsink and fan assembly. The 486 SX processors had their FPU (floating point unit) disabled. I imagine this reduced cost and heat output.

This wasn’t exactly a scratch build, but it allowed me to get my feet wet in the world of computer hardware.

Upgrading the CPU

I swapped out the CPU for a 486 DX2-66, a much faster processor, adding a small aluminium heatsink and fan. The fan was powered by a molex power connector.

Memory and Upgrades

Another upgrade on this particular system that I remember was going from 4MB of RAM (SIMM, or single in-line memory module) to 8MB.

I don’t recall if the memory was the 30 or 72 pin type.

Hard Drive Configuration

Among other upgrades, the hard disk drive got swapped out for a slightly larger one. I think the capacity was somewhere around 400MB.

This upgrade had me reading manuals and learning about the importance of jumper placement. The drives connected with an ATA ribbon cable. It was important that the jumper on the back of the hard drive was configured according to the other devices sharing the same cable.

The jumper could be configured to make a drive device 0 or 1 (also known back then as the master or slave device) when sharing the same ATA cable.

The cables themselves were also rated for different transfer speeds. For example 16 or 33 MB/s, and later cables allows Ultra DMA to be used for speeds above 66 MB/s.

Future 486 Systems and Nostalgia

That Acer machine was a good learning experience for me. Soon after that time, I started building and configuring more 486 systems. I had all sorts from 486 DX2-66 to DX4-100 machines.

I started a little side hustle, where I would pick up old components and build fully configured systems, selling them in the local paper’s classifieds section.

Thinking back, this was seriously impressive for a kid my age. By the late 90s (at age 12 or so) I was earning chunks of cash from selling systems that I had built for next to nothing.

Fixing Old CRT Monitors

One of the big wins for me was when I learned from my uncle how to ‘fix’ blurry CRT monitors.

If opened up, all monitors had analog adjustment dials (potentiometers) that could be turned with a screwdriver to control the flyback. One of these would control focus.

I would gather up old CRT monitors that nobody wanted anymore because they had gone ‘blurry over time’. The fix was simply to adjust the focus potentiometer inside while the monitor was on. Of course this was very dangerous as these old monitors contained high voltage capacitors that remained charged up for a while, even after power was disconnected. However, having learned from an adult, I was very careful about this, and the payoff was being able to make cash from selling full systems with monitor included.

This was an amazing time for me as a kid. I learned how to operate DOS, and further operating systems such as Windows 3.1 and further on, Windows 95.

Computer Knowledge Acquired from my ‘486 era’

Here are some random things I learned in the 1990s during my time tinkering with 486 systems. I guess some of the time was also spent with older 386 machines too.

  • DOS configuration using the CONFIG.SYS and AUTOEXEC.BAT files
  • Expanded memory manager configuration with EMM386.
  • Configuring CDROM device drivers in DOS and setting them up on boot.
  • Writing simple batch (.BAT) scripts.
  • Simple QBASIC programming.
  • A little later on, Turbo Pascal programming basics.
  • Compression and decompression with tools like ARJ.
  • Manual sound card configuration by selecting specific addresses and channels.
  • Networking two machines using serial or parallel cables (COM or LPT), and using laplink software to copy files between.

Gaming and PC use in the 1990s

Not all of my computer time as a kid was spent working with computer hardware. I enjoyed playing games just as much.

Some of my most memorable and favourite DOS games that I enjoyed playing on the first 486 system my parents owned, (and later on the 486 systems I built were):

  • X-Wing and Tie Fighter
  • UFO – Enemy Unknown
  • Ultima VIII: Pagan
  • Master of Orion (MOO)
  • Wacky Wheels
  • Death Rally
  • Screamer
  • Cannon Fodder
  • Strike Commander
  • Wing Commander

I also fondly remember writing science fiction (short stories) in word processing software in DOS and then printing them out on a dot matrix printer. I would take these to school and share with friends (who would also do the same thing).

Follow my Retro Hardware blog post series:

Notes:

The featured image of the 486 SX CPU is sourced from: https://en.m.wikipedia.org/wiki/File:Intel_i486_sx_25mhz_2007_03_27b.jpg and comes with this creative commons license.