Chip sounds and arpeggios
The music of a home computer of the 1980s came from a sound chip with three voices. Its composers made three voices sound like a band with two tricks: a chord played one note at a time, very fast, and a drum made by changing a voice's wave many times a second. synths has both.
The chip synths
| JavaScript | Python | What it is |
|---|---|---|
chip | chip | A plain square wave |
chipLead | chip_lead | A thin pulse wave with vibrato, for a tune |
chipBass | chip_bass | A bass whose wave changes as it sounds |
chipNoise | chip_noise | The chip's noise |
chipKick, chipSnare, chipHat | chip_kick, chip_snare, chip_hat | Drums, each from one voice |
function onMouseDown() {
makeSynth("chipLead").playNotes("C5 E5 G5 C6 G5 E5 C5:2", 0.125)
}Arpeggios, fast and musical
A chord is notes together. An arpeggio is the notes of a chord one at a time. There are two kinds here, and they sound nothing alike.
Fast: the chord of a Commodore 64 tune
One voice whose pitch jumps through the chord fifty times a second, which the ear hears as a shimmering chord. The chip had only three voices to spend.
function onMouseDown() {
playArp("Cm7", 1) // a second of C minor seventh
}The chord is a name, a note and a kind, or a list of notes. Then how long, which is half a second unless you say.
playArp("Cm7") // half a second of C minor seventh
playArp("C4", "minor", 2) // a note and a kind, for two seconds
playArp(["C4", "E4", "G4", "C5"], 1, { speed: 12, order: "up-down" })speed is steps a second: 50 unless you say, which is the speed chip music was written at. At 12 you can hear each step.
Musical: each step a note of its own, in time
Say how long a step is with step, a note length, and the arpeggio keeps time with the tempo. Every step is struck afresh, held for part of the step and let go.
setTempo(138)
const lead = makeSynth("pluck")
function onMouseDown() {
lead.arp("Am", "whole", { step: "sixteenth", octaves: 2, gate: 0.5 }) // a trance arpeggio: up through two octaves
}
function onKeyDown() {
lead.arp("C", "whole", { step: "eighth", order: "up-down", gate: 0.9 }) // a pop arpeggio: slower, nearly joined up
}A struck arpeggio is ordinary notes. So it goes through the synth's envelope and filter at every step, and plays the right recordings of an instrument made from several samples.
What an arpeggio can be told
| Option | What it does |
|---|---|
step | How long a step is: "sixteenth", "eighth", "eighth.", "1/12", or a number of beats. It follows setTempo, even while the arpeggio is sounding. This is the musical kind |
speed | Steps a second instead (50 unless you say). This is the fast kind |
order | "up", "down", "up-down", "up-down2" (the top and bottom notes twice), "as written", "random", or "together" (the whole chord at each step: stabs) |
octaves | 1 to 4: through the chord, then the same an octave up |
gate | How much of its step each note is held, from 0.05 to 1 (0.5 unless you say). Short is plucked; 1 is joined up |
swing | 0 to 0.75: every second step is that much of a step late. 0.33 is a triplet feel |
accent | How hard each step is struck: up to eight numbers from 0 to 1 that go round, as in [1, 0.5, 0.5, 0.5]. 0 is a rest |
pattern | A rhythm of its own in the place of the order: see below |
style | "strike" (each step a new note: what step gives) or "slide" (one voice whose pitch jumps: what speed gives). Say it to have the other |
volume, pan | How loud, and where |
after, at, beat | When it starts |
A chord has room for eight notes. With no synth of your own (playArp, startArp) an arpeggio plays on a chip sound. Every synth has arp and startArp too: lead.arp("Cm7").
A pattern, as text or by name
A pattern says which note of the chord each step plays, so an arpeggio can have a rhythm of its own.
setTempo(126)
const lead = makeSynth("pluck")
function onMouseDown() {
lead.arp("Am", "whole", { step: "sixteenth", pattern: "1 - 3 2 _ 1 3 5^" }) // written out
}
function onKeyDown() {
lead.arp("Am", "whole", { step: "sixteenth", pattern: "trance" }) // or one by name
}| In a pattern | What it means |
|---|---|
| A number | That note of the chord, counted from 1 at the lowest. Past the top it carries on an octave up |
- | A rest |
_ | Hold the last note through this step |
' after a note | An octave up. , is an octave down |
^ after a note | An accent |
:3 after a note | Its own gate, in tenths |
~ before a note | Slide into it |
The patterns by name are bounce, octaves, root-fifth, thumb, converge, diverge, offbeat, gallop, trance and acid.
A synth can have a pattern of its own, which every arpeggio of it follows unless it says another:
const lead = makeSynth("pluck")
lead.arpPattern = "trance" // a name, the steps as text, or a list such as [1, 0, 3, 2], where 0 is a rest
function onMouseDown() {
lead.arp("Am", "whole", { step: "sixteenth" })
}It reads back as its text, and lead.arpPattern = "off" takes it away. lead.arpStep() is the step that is sounding, counted from 0, for drawing it (arp_step()).
An arpeggio that stays
startArp gives one that sounds until its stop(). change moves it to another chord without starting the note again, which is how a chip tune moved through its chords.
const chords = ["Am", "F", "C", "G"]
let arp = null
let turn = 0
function onMouseDown() {
if (!arp) arp = startArp(chords[0], { volume: 0.5 })
else arp.change(chords[++turn % 4])
}
function onKeyDown() {
if (arp) arp.stop()
arp = null
}
function draw() {
background("black")
text(arp ? chords[turn % 4] : "Click for a chord", width / 2, height / 2, "lime", 48, "centre")
}Each click moves to the next chord, and a key stops it. A change can be asked for ahead of time: arp.change("F", { after: 2 }) (arp.change("F", after=2)).
A musical arpeggio that stays changes chord under your hands, from the next step, and keeps its place in the pattern:
setTempo(138)
const lead = makeSynth("pluck")
const chords = ["Am", "F", "C", "G"]
let chord = null
let turn = 0
function onMouseDown() {
if (!chord) chord = lead.startArp(chords[0], { step: "sixteenth", pattern: "trance" })
else chord.change(chords[++turn % 4]) // from the next step
}
function onKeyDown(key) {
if (!chord) return
if (key === "g") chord.gate = 0.3 // gate, octaves, swing, accent, pattern, order, step and speed change as it plays too
else { chord.stop(); chord = null }
}A whole chip tune
Four chords from one voice, over a chip bass and chip drums. Click or tap to start it, and again to stop.
const chords = ["Am", "F", "C", "G"]
const bar = 1.6 // seconds each chord lasts
let began = null // when it started, on the sound clock
let chord = null // the arpeggio, while it sounds
function onMouseDown() {
if (began !== null) {
began = null
return stopSound()
}
began = soundTime()
// One note that never starts again: only its chord changes, every bar, for sixteen bars.
chord = startArp(chords[0], { speed: 50, order: "up", volume: 0.5 })
for (let i = 1; i < 16; i++) chord.change(chords[i % 4], { after: i * bar })
makeSynth("chipBass").playNotes("A1:4 F1:4 C2:4 G1:4", bar / 4, { repeat: 4 })
makeSynth("chipKick").playNotes("C3 - - - C3 - - -", bar / 8, { repeat: 16 })
makeSynth("chipSnare").playNotes("- - A3 - - - A3 -", bar / 8, { repeat: 16 })
makeSynth("chipHat").playNotes("C5 C5", bar / 8, { repeat: 64, volume: 0.3 })
}
function draw() {
background("black")
const bars = began === null ? 16 : (soundTime() - began) / bar
if (bars >= 16) {
if (began !== null) chord.stop()
began = null
text("Click to play", width / 2, height / 2, "lime", 28, "centre")
return
}
// The chord that is sounding, and a block for each of the four.
const now = Math.floor(bars) % 4
text(chords[now], width / 2, height / 2 - 30, "lime", 72, "centre")
for (let i = 0; i < 4; i++) rect(width / 2 - 110 + i * 56, height / 2 + 40, 48, 16, i === now ? "lime" : "darkgreen")
}Step macros
A macro is a short list of steps that every note of a synth plays through, 50 a second, each step changing the wave, the pitch, the pulse width, the cutoff or the volume. It is how a sound chip made a drum from one voice: a burst of noise, then a tone, then noise again.
const snare = makeSynth("chip", { decay: 0.16, sustain: 0, macro: [
{ wave: "noise", pitch: 24 }, // noise, tuned high
{ wave: "square" }, // then the note itself
{ pitch: -5, volume: 0.9 }, // five semitones lower
{ wave: "noise", pitch: 18, volume: 0.6 },
] })
function onMouseDown() {
snare.play("A3")
}A step has any of these, and the rest stay as they were:
| In a step | What it is |
|---|---|
wave | "sine", "triangle", "saw", "pulse", "square" or "noise" |
pitch | Semitones from the note |
width | The pulse width |
cutoff | Semitones from the note |
volume | 0 to 1 |
The last step holds. The setting macroRate is the steps a second (macro_rate), and setting macro to nothing takes a macro away: snare.macro = null (snare.macro = None).
For steps that go round while the note is held, give the steps with loop:
const buzz = makeSynth("chip", { macro: { steps: [{ width: 0.1 }, { width: 0.3 }, { width: 0.5 }, { width: 0.3 }], loop: true, slide: true } })slide moves the width, the cutoff and the volume smoothly between steps. chipKick, chipSnare, chipHat and chipBass are made this way.
Related
- Notes, chords and tunes: the chords an arpeggio can play
- Songs: a song of synths' own has chords as arpeggios too