Schoenberg: The MIDI Esoteric Programming Language
Schoenberg is an esoteric programming language where programs are written as MIDI files. A MIDI file is basically digital sheet music that tells a computer which notes to play when and how loudly.
The programming language interpreter, and transpilers between Schoenberg and brainfuck programs, can be found on GitHub.
Semantics
The operational semantics of a Schoenberg program directly match those of a brainfuck program. Since brainfuck is Turing-complete, so is Schoenberg.
Like brainfuck, Schoenberg operates on an array of memory cells, each initialized to zero. There is a pointer, initially pointing to the first memory cell, and there are commands for moving the pointer, modifying the current cell, outputting the current cell, and looping.
Syntax
Since Schoenberg programs are MIDI files, its syntax is not text-based.
Instead, commands are controlled by pitch class distance, velocity, and overlap. Rhythm has no effect on a program’s behavior unless it causes notes to overlap.
Command | Description | Syntax | amount | Brainfuck |
---|---|---|---|---|
Decrement | Decrement the pointer cell by amount | Play a note 1 pitch class away from the current note | - | |
Increment | Increment the pointer cell by amount | Play a note 2 pitch classes away from the current note | + | |
Move left | Move the pointer left amount times | Play a note 3 pitch classes away from the current note | < | |
Move right | Move the pointer right amount times | Play a note 4 pitch classes away from the current note | > | |
Output | Output the pointer cell | Play a note 5 pitch classes away from the current note | N/A | . |
Input | Input a byte into the pointer cell | Play a note 6 pitch classes away from the current note | N/A | , |
Loop start | Jump past the matching loop end if the pointer cell is 0 | Play a note that overlaps the current note (a “loop note”) | N/A | [ |
Loop end | Jump back to the matching loop start if the pointer cell is not 0 | Stop playing an active loop note | N/A | ] |
Additionally:
- Playing the same note twice in a row, corresponding to a pitch class distance of 0, is a no-op. However, playing and overlapping two different notes with the same pitch class (e.g. in different octaves) can be used to start loops without adding other commands.
- When playing multiple notes at exactly the same time (e.g. a chord), the chronology of the notes is considered to be the ascending pitch order.
Every MIDI file is a syntactically valid Schoenberg program, although most don’t do anything useful.
Sample programs
I wrote (composed?) echo.mid
from scratch in Ableton and transpiled the rest from brainfuck.
MIDI | Description | Audio (synthesized using TiMidity) | Source |
---|---|---|---|
hello_world.mid | Prints Hello, World! | Esolang Wiki | |
cell_width.mid | Prints the interpreter’s cell width | Esolang Wiki | |
echo.mid | Prints the input, like echo | Me | |
wc.mid | Counts input lines, words, and bytes, like wc | Daniel B. Cristofani | |
fib.mid | Prints the entire Fibonacci sequence | Daniel B. Cristofani | |
bubble_sort.mid | Sorts the input bytes using bubble sort | Daniel B. Cristofani | |
insertion_sort.mid | Sorts the input bytes using insertion sort | Daniel B. Cristofani | |
quick_sort.mid | Sorts the input bytes using quick sort | Daniel B. Cristofani | |
brainfuck_interpreter.mid | Runs a brainfuck program on its input, which should be separated by an exclamation point | Daniel B. Cristofani |
All the programs can be found on GitHub.
FAQ
What’s with the name?
The programming language is named after Arnold Schoenberg (1874–1951), who is widely considered to be the father of atonal music.
Writing tonal music, the basis of Western music composition, requires limiting yourself to the pitch classes of a chosen key, but this is incredibly hard to do when writing Schoenberg programs.
For example, if you want to decrement the pointer cell, then the next note must be 1 pitch class away. This leaves you with only two options for the next note’s pitch class, but it’s possible neither of those pitch classes is in the chosen key.
As a result, Schoenberg programs tend to be atonal.
How did you come up with the idea?
I was inspired by Piet, an esoteric programming language where programs look like abstract paintings.
The concept of a program language where programs are written in an artistic medium was intriguing to me, so I decided to create my own programming language based on music composition, the artistic medium I have the most experience with.
Is it useful for anything?
Like most esoteric programming languages, Schoenberg is mostly a meme and mostly useless.
It could be used for steganography though. For example, you could create a brainfuck program that outputs some text and further conceal the message by transpiling it to a Schoenberg program.
Is there a one-to-one mapping between brainfuck and Schoenberg programs?
No, there is a one-to-many mapping for many reasons. Here are a few:
- Schoenberg programs can be transposed without affecting behavior.
- Schoenberg’s syntax allows specifying most commands in more than one way.
- Note length can often be changed without affecting behavior.
Why did you write it in Rust?
Rust is pretty well-suited for writing interpreters, compilers, transpilers, etc. because of its “algebraic data type” style enums. It also has a user-friendly and performant MIDI parsing and writing library called midly
.
Plus, I’ve rarely used Rust and this seemed like a good opportunity to learn more about it.
Is there an IDE?
Any MIDI editor is a Schoenberg IDE!
I personally use Ableton because that’s what I use for non-Schoenberg music production, but GarageBand and FL Studio are also good options.