Brief history of the saugns program, SAU language and project as a whole.


2011–2012: Beginning

The program was originally developed in 2011-2012. Meant to have a "retro"-touch from the start, it was originally inspired by the "sound" of the 16-bit video game era, particularly Sega Genesis games and the FM synth side of the samples vs. FM divide.

The design unfolded bottom-up, experimentally, into something very compact and simple – which also applies to the scripting language. But after a few months, and in large part arriving at the early design, it became clearer over time that much work would be needed to make it truly suitable for writing music in. It had only become fit for experimenting with sound. Far greater expressiveness was hoped for than was straightforward to arrive at.

In hindsight, many changes were rushed or poorly done after the initial design was arrived at. The first four months or so brought most things of more lasting quality. The remaining ten were spent breaking and unbreaking things in various ways, and leaving extra debugging and clean-up work for later years.

The program was first called "mgensys" and then renamed "sgensys". Two sgensys versions were released in 2012 at Gna!, with OSS system audio support and WAV file output support. Historical versions, including other old snapshots, are included in and precede newer work in the git repository, which can be found on GitHub, Codeberg, or Framagit. (The development style for new work has involved a whole lot of rebasing, but old snapshots prior to 2017 are included in the history. For newer work, older versions are preserved in the history of tags and snapshot branches.) The old, historical versions never had any known users other than the original developer.

Various sketchy ideas for further development remain from this 2011–2012 period.

2011-01-21: Quick tests

The first snapshot is a quick test supporting playing the classic wave types: sine, triangle, square, saw. The script syntax is different from later variations, and uses D to set default values, W to insert a wait time, E to wait until playing done (until end), and S to insert a "sound":

# This is the first mgensys script made
D      a0.5
W      t0.5
Ssqr   f220 t1
Ssqr   f110 t0.5 a.05  E
Ssqr   f440 t1.5
Ssqr   f220 t0.5 a.05  E
Ssqr   f400 t0.5       E
Ssqr   f350 t0.5       E
Ssqr   f325 t1.5       E
W      t1
Ssqr   f220 t1         E
Ssqr   f440 t1.5       E
Ssqr   f415 t0.5       E
Ssqr   f445 t0.5       E
Ssqr   f490 t1         E
Ssqr   f420 t1         E
Ssqr   f440 t1.5       E

A quick second version the same day changes the syntax, and switches to the basic wave-table oscillator (one cycle per wave type) then kept. In three longer-lived changes, S sets a default value, W plays a wave of a given type, and / inserts a delay:

# This is the first mgensys script made
S      a0.5
Wsin   f220 t1
Wsin   f110 t0.5 a.05  E
Wsin   f440 t1.5
Wsin   f220 t0.5 a.05  E
Wsin   f400 t0.5       E
Wsin   f350 t0.5       E
Wsin   f325 t1.5       /1 E
Wsin   f220 t1         E
Wsin   f440 t1.5       E
Wsin   f415 t0.5       E
Wsin   f445 t0.5       E
Wsin   f490 t1         E
Wsin   f420 t1         E
Wsin   f440 t1.5       E

"Wait nodes" were replaced by timing calculations using a "wait before using this node" delay time in each sound-generating node. The sound generation processes as many nodes as it reaches before having to wait before taking on the latest new node (and in turn any after).

2011-02-21: PM synthesis achieved

On 02-13, label assignment and referencing was added to the syntax, so that sounds can be named and then changed further after a delay. By 02-16, scope level handling was added and a modulator list syntax, m<...>, was added for grouping modulator sounds for carrier sounds. (The syntax for waiting until the end of prior sounds was also changed to |.) Basically correctly-sounding PM finally appeared in the 02-21 version, along with supporting relative frequencies for modulators.

Wsin f444 t2 m<Wsin r1.23456789> |
'a Wsin f444 t2 m<Wsin r(1/7.12)> |
:a t2 m<Wsin r(1/2.255)> |
:a f222 t2 |

Wsin f137 t1 m<Wsin f032 m<Wsin f42>> |
Wsin f137 t1 m<Wsin f132 m<Wsin f42>> |
Wsin f137 t1 m<Wsin f232 m<Wsin f42>> |
Wsin f137 t1 m<Wsin f332 m<Wsin f42>> |
Wsin f137 t1 m<Wsin f432 m<Wsin f42>> |
Wsin f137 t1 m<Wsin f532 m<Wsin f42>> |
Wsin f137 t1 m<Wsin f632 m<Wsin f42>> |
Wsin f137 t1 m<Wsin f732 m<Wsin f42>> |
Wsin f137 t.5 m<Wsin f832 m<Wsin f42>> |
Wsin f137 t.5 m<Wsin f932 m<Wsin f42>> |
Wsin f137 t2 m<Wsin f1032 m<Wsin f42>>

2011-04-04: "sgensys is the Sound GENeration SYStem"

AM/RM and FM support were added by 03-04, and the modulator list syntax was also changed for PM then: a!{...} for AM/RM, f!{...} for FM, and m{...} for PM. Soon after, PM syntax became p!{...}.

The 04-04 version changed sound generation to fill and use buffers or "blocks", instead of re-traversing the nodes on a sample by sample basis.

A joke that never took off: "sgensys is the Sound GENeration SYStem. Throw out your fancy pre-recorded samples and replace them with terse scripts in a primitive language invoking the powers of FM synthesis." (Compare to "ed is the standard editor"; the only problem was/is the young age of my program.)

2011-06-04: Early feature plateau

A series of quick design tweaks and feature additions led up to an early feature plateau. A few early features were added after 06-04, but most was completed by then, and most of what was striven for afterwards was left unfinished.

By 04-26, some fancy syntactic sugar was added in the form of the ;-separator (still around as the compound step language feature) that allows grouping a series of changes for the same sound in one place in a script, even if other things also done in the script alternate with some of those changes in the flow of time.

By 05-08, basic support for value ramps (linear, and curves using a polynomial chosen by ear for "exponential and logarithmic") was added. That was just after support for writing frequencies as notes in C-major scale was also added (a simple-enough addition, but of questionable utility for music in its current form).

A mixture of ideas were in the inconsistent syntax of early June. The letter O became used for an "operator" (oscillator) in a long-lived change. But the PM syntax was also replaced with a new scheme, basically meant to be developed further as a new modulator syntax in general, but then dropped and that change undone. For an oscillator, a - meant "link it to what follows", flattening the PM modulator list syntax. (A line break marked the end of nesting using one or more - in the absence of other nesting characters.) Furthermore, a scope nesting <...> syntax was meant to be combined with it, to allow pushing/popping such scope in a more conventional way, as in <-...> where nesting ends after the >.

2012-04-01: All the rest, then at rest

All remaining syntax changes in the early versions were done by 01-23. That snapshot added support for letting modulator oscillators either have their time duration determined by the carrier (as earlier), or set so as to be limited to a shorter time.

WAV file output was added in the 02-10 snapshot. By 02-26, preparations to release it at Gna! were visibly done. Further fixes and little design tweaks mark the 03-05 release and the 04-01 release.

2013–2014: Stagnation

The project stagnated after a time of mostly theoretical focus. Old notes on possible future work had accumulated, with far more held in mind than written down about the subtle issues and limitations of different design choices.

In 2013, Linux ALSA support was added to the old program, and some quick experimentation with redesigns in mind remain from that year and 2014.

Two 2014 snapshots in the version history capture the sketchy outline of a lexer (meant to go along with a new language with an undecided syntax), and before that, incomplete ideas for an intermediate redesign of the old system.

The old program design and language were seen as a dead end, but no substantial ideas for a rewrite developed.

2017–2020: Cleaning it up

The project was revived on November 27, 2017, with a focus torn between three paths:

  1. Bugfix and refactoring clean-ups.
  2. Deep-going redesign with the old language and features still the basis.
  3. Experimenting on and developing a new language.

The first of these ended up given most of the focus, until early 2021. For whatever reason, I found myself stuck moving in the other directions. The main exception is reworking the command-line interface, and expanding its options. Strings can be evaluated with -e, after using new low-level code spun off from the 2014 test lexer to scan the script text. Further, some smaller syntax changes made while still feeling torn on how it should ultimately look, don't significantly extend features.

2019-01: A new name – saugns

In January 2019, the project and program was renamed to saugns and the language to SAU (Scriptable AUdio). The first of the tagged versions is v0.3.0, released in July 2019. v0.3.0 brought better audio mixing, 3 added wave types, changes and additions to value ramp curves, and automatic downscaling of amplitude by the number of voices (easily added as voices were already counted). Further versions with varying changes to design and features, but mostly quite similar to use, continue throughout v0.3.x in 2019–2021.

Currently, some old sketchy ideas for future work remain from 2019 on the saugns GitHub project wiki.

2020-06 "mgensys": Almost starting over

In early 2020, following more bugfixing, for half a year I forked off the more simply designed early 2011 version from around the time the name changed from "mgensys". Some new ideas had begun to echo really old ones in the design of the program. The elegance of the early design was rediscovered, a goal set to re-expand it for a fuller feature set with newer experience and code to draw on.

In this new-old "mgensys" program, which did not end up becoming saugns v0.4.0, noise generation was implemented, minor syntax tweaks done, and other features planned for. (This experimental redesign is kept in the branch named old-dev_202006.)

Eventually, I decided to bridge the feature and design gap the other way around. Further saugns v0.3.x versions debug and tweak, along with bringing across changes of increasing size from the new "mgensys" (from smaller code updates to some design ideas). This is planned to lead to the new v0.4.x, once design and feature synthesis is completed. Following more changes in direction...

2021–2022+: Reorienting

Having felt somewhat bored and aimless with the overall design, I left it for half a year while rethinking what's meaningful for the program to be. Over time, alongside a few smaller steps toward fuller redesign, I've developed further ideas for audio generation features to add and experiment with later on.

By the time of v0.3.9, some older v0.3.x complications were peeled off without removing functionality, and some low-hanging syntax extension-and-reworking fruit was picked up in its place, beginning with extending numerical expressions with a (very) small set of named, built-in mathematical functions. Concerning other types of syntax, v0.3.10 fixed a big old remaining timing bug from 2011 (and some smaller ones), and reworked the timing syntax. On variations of modulation, v0.3.10b also added frequency-linked PM.

A redone oscillator using anti-aliasing through pre-integrated wave tables also came in v0.3.9, for a DPW-like result (6dB aliasing reduction per octave, but for all waveforms and for FM and PM), following experimentation in the old-dev_202109 branch. A regression (ability to handle large PM amplitudes well) was fixed in v0.3.10b, using fancier look-up table interpolation. That type of oscillator seems to end up too computationally complex to be worth it, and other options may be explored later.

Very different further (re)designs are possible. For wave oscillators, eventually I may abandon the wavetable approach entirely. I remember some early ideas I had for more flexibly wavetype-morphing oscillators but not worked on yet. It may make for more interesting synthesis if implemented, and if it's made to work without terrible aliasing noise.

2021-11: Extending numerical expressions

From very early versions, numerical expressions in the language work like a parse-time calculator with conventional infix syntax (except for some long-lived bugs and flaws). Fancier uses of this saw little use, as there's only so much to be done with basic arithmetic – until I finally added some named functions after a decade in v0.3.9 (and then later more in the way of stateful parse-time actions).

Half a year after this, I decided to try extending the extension, for more types of function – the most interesting additions being rand(), seed(x), and time(), which allow making both predictably and unpredictably randomized scripts. (Attaching state to the parse-time numerical processing seems an interesting way to add flexibility, which does not require adding any new syntax elements elsewhere.)

2022-05: More command-line interoperability

In what more ways can the program be used? After checking out some live audio stream visualizing programs, I decided to add more options to make saugns easy to use with programs that accept audio over stdin in general.