Bitwise Harmony

πŸ“… 2023-02-05

🏷 Music, Programming

In the spring of 2020, with the pandemic starting, I decided to spend a couple of months writing a SID-chip style audio engine and a tracker called Bitwise Harmony, all in C11 and based on SDL.

A 30-year journey

Electronic music is perhaps my second favorite hobby (programming being #1, of course). My earliest memory in this field is from the C64, where one of the tapes "borrowed" from a friend had a piano simulator where you could play and record a short song. It was slow, clunky and sounded horrendous in every way, but the idea of using a computer as a musical instrument really resonated with me β€” there are endless possibilities for creativity!

Over the years I played around with ProTracker on the Amiga 500 and later did more serious work with Scream Tracker on the PC. My skills growing, eventually I moved on to MIDI trackers on Windows, intent on composing some orchestral music using General MIDI instruments. This was around the year 2000. My music was starting to gain some complexity at that point, thanks to not being restricted to keyboard and mouse input and using an actual synthesizer keyboard for recording the notes. Later on, after becoming a Mac user, I went all in on Logic Pro and even recorded a couple of songs with lyrics.

Eventually, the amount of time and focus needed to produce a composition of acceptable quality started to be incompatible with my family duties. The situation closely mirrored what was happening with my hobby programming projects. The only way forward was to scale back to simpler and smaller things.

Modernized chiptunes

For a long time I've been fascinated by audio synthesis, i.e., procedurally generating audio samples based on simple waveforms. If you generate the samples from scratch, the computer truly has become your instrument! I kept thinking about this for several years while working on other projects.

Having started computing on the C64, SID music and chiptunes are very close to my heart. The core idea for Bitwise Harmony was taking that kind of audio synthesis and applying some modern twists for flexibility: more than three voices, layered voices, and a few effects familiar from Scream Tracker.

The project has two main components:

I wrote both components from scratch on top of SDL. Creating everything from scratch was really a key motivator for me in the project: not only making the audio part of the electronic instrument, but the UI as well.

The GUI toolkit started in Bitwise Harmony was used as a starting point for Lagrange. In comparison, this earlier version feels like a rough prototype. There are so many little details that have been improved over the past couple of years, for example support for multiple windows, proper HiDPI rendering, better Unicode text, improved behavior on different operating systems, and integration with native UI components. Still, this old GUI gets the job done well enough.

Forming waves

The heart of the audio engine is a simple waveform generator whose output is controlled by user-defined amplitude envelopes. The main challenge with this is that no matter what happens, you can never abruptly stop a wave! This would cause a loud clicking sound because such a rapid change is effectively a very short and high-frequency wave of its own. Instead, each period of the wave must be allowed to terminate to zero or be faded out gradually. When playing rapidly changing notes, some with effects applied, this can get a little tricky. In practice, the sample generator will have a little bit of asynchrony with the notes to be started. However, when you are producing 44100 samples per second, there is room for transitions.

The second challenge, specific to real-time audio, is that the system does a bit of buffering to ensure stutter-free playback. The actual amount of buffering required by an SDL app generating real-time audio seems to be highly OS-specific. In Preferences, one can adjust the size of the audio buffer. It should be as small as possible without the audio breaking down. Having to resort to a large buffer is annoying as playing notes on a delay is never a nice experience.

Sources and binaries

CC-BY-SA 4.0

The original Gemtext version of this page can be accessed with a Gemini client: gemini://skyjake.fi/gemlog/2023-02_bitwise-harmony.gmi