Freespin makes music using the floppy's stepper motor. As amusing as that sounds at first, it is actually a rather limited music device:
- One channel.
- One waveform (rectangle).
- No volume. No tremolo. No hull curves.
- One frequency change per 1/50s. Hence, only the "chiptune bpms" (107, 125 etc.) work.
- Limited memory. Typically about 256 bytes for ~10 seconds of music.
- Loading breaks. Every 7-20 seconds, the audio goes silent for 0.14 seconds.
The above list makes it sound a lot like the 1541 floppy drive acts like a PC speaker. However, the stepper motor has much more unique and interesting audio qualities.
For starters, even though you can't really change the volume, different pitches will drastically vary in loudness.
This seems to be because different frequencies catch different resonant frequencies of the drive's case. This actally also causes different frequencies to have a rather different sound.
Additionally, we found that by overlaying two rectangle waveforms (using "OR"), we can generate a new "instrument". What actually happens here are two things:
- We change the pulse wave duty cycle, since the two rectangle waveforms will be slightly shifted with regards to each other.
- Because the second waveform has a different frequency (we used 1.5 * f), the pulse will be modulated over time.
To save space, we rarely used both "instruments" at the same time, however. So instead a pattern either uses the "simple rectangle" waveform or the "f | f * 1.5" waveform. This also depends on the effect. Some effects (like the double scroller) don't have the rastertime to combine two waveforms.
One surprisingly hard problem turned out to be how to switch the sound off, especially during base solos.
Turns out that just disabling the rectangle wave generation (Set $180b to $40) altogether will cause the read head to "settle", which produces a click.
Instead, we opted to have silent parts of the music play a really high note (15kHz) to keep the stepper motor suspended. This makes the click go away and instead causes some chirping, which is fine.
The loading breaks
As mentioned, between every part of the demo (so every 7-20 seconds), there's a loading break that silences the audio for 0.14 seconds and also causes a "click" near the end of that timespan. And of course, the video goes black at the same time, thus emphasizing the break. It's as if the video is pointing a finger at the audio at that exact time.
In our first attempts to generate a soundtrack, we payed a lot of respect to the loading breaks. PVCF would make the song drop all the melody "voices" and only play base, thus creating enough gaps in the music to squeeze in the loading break. (For an example of this, look at the transition from the moving chessboard to the up/down double scroller) This works nicely and does a great job at masquerading the loading breaks, but makes it hard to establish a musical flow, since you end up winding down and spinning back up your voice structure every few seconds. Fewer and longer effects help, but effects can only run for so long before running out of memory.
In the end, we just swallowed the pill and removed notes from the music and replaced them with loading breaks. (What seemed to work best were the last 1/4 note of a measure, at the end of a sequence, for 107bpm)
Still, the loading breaks dictated a lot of the demo's timeline. Not every pattern did lend itself to be being interrupted. (That's e.g. why the chessboard scroller at the start of the demo keeps going until the end of the music pattern it started together with.)
Composing the music
We used Renoise initially, working with recordings of each note. If you want to try out 1541 composing, here are the Renoise instruments:
Later we switched to a programmatic approach. For example, here's the source code of the music used during the "blinking cursor" part:
notes = [14, P, 14, 14, 14, 14, 2, 2, 50, 50, P, P, 50, 50, P, P, 12, P, 12, 12, 12, 12, 0, 0, 48, 48, P, P, 48, 48, 0, 0, 14, P, 14, 14, 14, 14, 2, 2, 50, 50, P, P, 50, 50, P, P, 12, P, 12, 12, 12, 12, 0, 0, 48, 48, P, P, 45, 45, 43, 43, P, P, P] p.play_sequence(i2, 36, notes, note_length=7) notes = [7, P, 7, 7, P, P, 7, P, 7, P, 7, P, P] p.play_sequence(i0, 36, notes, note_length=7)
Compressing the music
Most parts in freespin had less than 256 bytes to spare for audatory entertainment. We spent a significant amount of time developing special compressors just for the music patterns. In the end, we had eight different compressors in use, most highly specialized to target a specific section of the music.