Tutorial
Playing your hardware, and letting your hardware play you
So far everything has happened inside swync. Now let’s play with some hardware.
Open the settings panel. Under MIDI there are two lists — what you can send to, and what you can read from — each with a number beside it.
You won’t need to come back here often. When you type midiout(" the editor offers the same
list, which is usually easier than remembering how a vendor spelled things.
midiout goes where an instrument would go.
play([c2;q, c2, g2, c2], midiout("deluge"))
Any part of the name will do, and case doesn’t matter — "deluge" finds Deluge MIDI 1.
The device number works too.
play([c2;q, c2, g2, c2], midiout(2))
A second argument is the channel, 1 to 16.
play([c2;q, c2, g2, c2], midiout("deluge", 3))
If the port isn’t plugged in, the problems panel says so but won’t prevent a compile. Everything else in your program still runs.
Three lanes work here. vel: 0 to 1 like everything else in the language.
play([c2;q, c2, g2, c2], midiout("deluge"), vel: [1, .5, .8, .5])
legato shortens or lengthens the note, as it does for an instrument.
play([c2;q, c2, g2, c2], midiout("deluge"), legato: .25) // staccato
And chan overrides the channel per note.
play([36;e, 42, 38, 42], midiout("digitakt"), chan: [1, 10, 1, 10])
midiin goes where the pattern would go.
fn lead(n) = saw(n.m2h) * perc(0.01, 0.4)
play(midiin("keystation"), lead)
Play the keyboard. Your instrument sounds.
Because a.f(b) is f(a, b), this reads the other way round too:
midiin("keystation").play(lead)
By default it listens on every channel. A second argument narrows it.
play(midiin("keystation", 2), lead)
Give your instrument a parameter called vel and it gets the velocity of each note, 0 to 1.
fn lead(n, vel = 0.7) = saw(n.m2h) * vel * perc(0.01, 0.4)
play(midiin("keystation"), lead)
An instrument without a vel parameter never sees it, so everything you’ve already written
still plays.
dur is how long a note lasts, and a key you’re still holding down doesn’t have a length
yet. So on a keyboard dur is the longest a note could be — which is a long time.
Write it in the last slot of env as usual, and the note will release when you let go:
fn pad(n) = saw(n.m2h) * env(0.5, 0.3, 0.7, 2, dur)
But don’t do arithmetic on it. dur/2 is fine for a written pattern and about thirty seconds
on a keyboard. Say the number you mean instead.
Lanes work here too, and a keyboard has no length, so playn and a rate are refused rather
than quietly ignored.
cc reads one controller as a signal you can use anywhere.
fn lead(n) = lowpass(saw(n.m2h), cc("push", 74), 1) * perc(0.01, 0.4)
That’s controller 74, and it reads 0 to 1. A filter cutoff wants hertz, so say the range once where the knob is named rather than doing the arithmetic every time you use it.
cc("push", 74, 1, 200, 5000)
That’s device, number, channel, range. The channel is 1 unless you say otherwise.
bend is the pitch wheel. It rests at zero rather than at the bottom, so you can add it
straight to a note.
fn lead(n) = saw((n + bend("keys", 1, -2, 2)).m2h) * perc(0.01, 0.4)
Two semitones either way. aftertouch is how hard you’re leaning on the keys, 0 to 1, and
takes a range the same way.
fn pad(n) = saw(n.m2h) * (0.4 + aftertouch("keys") * 0.6)
All three are smoothed, so a knob won’t zipper when you turn it.
If you’re sending notes to a synth, its arpeggiator and delays probably want to be in time
with them. midiclock sends it the transport.
midiclock("deluge")
play(bass, midiout("deluge"), vel: [1, .6, .8])
It starts and stops with the transport, and follows your tempo wherever you drag it. Write it once per port.