audio/synth
Making sound from nothing: oscillators, envelopes and voices.
import audio.synth
Everything else in this family reads sound that already exists. This package makes it. Six waveforms, an envelope to shape a note, three colours of noise, and four ways of building a voice that sounds like something rather than like a test tone.
The quickest way in is Tone for a bare waveform or ToneEnvelope for a note that starts and stops the way an instrument does. To play a phrase, fill a vector of Note and hand it to RenderNotes, which mixes and pans them into one clip.
Things to know
- An oscillator carries its own phase, so stepping it with
OscNextin a loop gives a continuous tone.WaveValueis the stateless form. - A note lasts longer than you asked:
ADSRTotaladds the release onto the hold, and that is the lengthToneEnvelopegives you. Chorddivides by the number of notes, so three notes together do not clip.RenderNotesdoes not, so give its notes a gain that leaves room.- For which notes to play, see audio/music, which also carries the Persian dastgah; to play a file of notes, see audio/midi.
Types
Osc
struct Osc| Name | Type | Description |
|---|---|---|
wave | int = 0 | Which waveform it makes. |
freq | f64 = 440 | Its pitch in hertz. |
phase | f64 = 0 | Where in the cycle it is, from 0 to 1. |
duty | f64 = 0.5 | How much of a pulse cycle is high. |
rate | int = 44100 | The sample rate it is stepping at. |
import audio
import audio.synth
import math
func main:
mut o := synth.NewOsc(synth.WaveSine, 440.0, 44100)
println o.wave, o.freq, o.rate, o.phase
first := synth.OscNext(o)
println math.Abs(first) < 0.0001, o.phase > 0.0
println synth.WaveValue(synth.WaveSine, 0.0, 0.5)
println synth.WaveValue(synth.WaveSaw, 0.5, 0.5)
println synth.WaveValue(synth.WaveSquare, 0.25, 0.5), synth.WaveValue(synth.WaveSquare, 0.75, 0.5)
println synth.WaveValue(synth.WaveTriangle, 0.25, 0.5)
println synth.WaveValue(synth.WavePulse, 0.1, 0.25)
t := synth.Tone(440.0, 0.05, 22050, synth.WaveSine)
println t.frames, t.rate, audio.Peak(t) > 0.9
e := synth.ADSR { attack = 0.1, decay = 0.1, sustain = 0.5, release = 0.2 }
println e.attack, e.sustain
println synth.ADSRAt(e, 0.0, 1.0), synth.ADSRAt(e, 0.05, 1.0), synth.ADSRAt(e, 0.1, 1.0)
println synth.ADSRAt(e, 0.2, 1.0), synth.ADSRAt(e, 0.5, 1.0)
println synth.ADSRAt(e, 1.2, 1.0) < 0.0001, synth.ADSRAt(e, 5.0, 1.0)
println synth.ADSRTotal(e, 1.0)
shaped := synth.ToneEnvelope(440.0, 0.05, 22050, synth.WaveSine, e, 0.8)
println audio.Sample(shaped, 0, 0), audio.Peak(shaped) <= 0.81
mut d := t
audio.Free(d)
mut d2 := shaped
audio.Free(d2)
endADSR
struct ADSR| Name | Type | Description |
|---|---|---|
attack | f64 = 0.01 | Seconds from silence to full. |
decay | f64 = 0.05 | Seconds from full down to the sustain level. |
sustain | f64 = 0.7 | The level a held note rests at. |
release | f64 = 0.1 | Seconds from the sustain level back to silence. |
The example for this item is shown with the ones above: synth-Osc.salam
Note
struct Note| Name | Type | Description |
|---|---|---|
note | int = 60 | The MIDI note number. |
start | f64 = 0 | When it starts, in seconds. |
length | f64 = 0.5 | How long it is held, in seconds. |
gain | f64 = 0.5 | How loud it is. |
wave | int = 0 | Which waveform plays it. |
pan | f64 = 0.5 | Where it sits between the speakers, 0 left to 1 right. |
import audio
import audio.music
import audio.synth
func main:
mut freqs := music.DastgahFreqs(57, music.DastgahShur)
ch := synth.Chord(freqs, 0.05, 22050, synth.WaveSine)
println ch.frames, audio.Peak(ch) > 0.1, audio.IsClipped(ch)
freqs.free()
wn := synth.NoiseWhite(0.05, 22050)
pn := synth.NoisePink(0.05, 22050)
bn := synth.NoiseBrown(0.05, 22050)
println audio.RMS(wn) > 0.3, audio.RMS(pn) > 0.01, audio.RMS(bn) > 0.001
fm := synth.FMTone(220.0, 2.0, 3.0, 0.05, 22050)
println fm.frames, audio.Peak(fm) > 0.5
add := synth.Additive(220.0, 6, 0.6, 0.05, 22050)
println add.frames, audio.Peak(add) > 0.1
ks := synth.KarplusStrong(220.0, 0.05, 22050, 0.99)
println ks.frames, audio.Peak(ks) > 0.1
one := synth.NoteAt(60, 0.0, 0.1, 0.4, synth.WaveSine)
println one.note, one.start, one.length, one.gain, one.wave
mut phrase := Vector {} as Vector<synth.Note>
mut scale := music.ScaleNotes(60, music.ScaleMajor, 4)
mut k := 0
until k < scale.len():
phrase.push(synth.NoteAt(scale.get(k), (k as f64) * 0.05, 0.04, 0.4, synth.WaveSine))
k += 1
end
scale.free()
e := synth.ADSR { attack = 0.005, decay = 0.01, sustain = 0.6, release = 0.02 }
song := synth.RenderNotes(phrase, 22050, 2, e)
println song.channels, song.frames > 0, audio.Peak(song) > 0.1, audio.IsClipped(song)
phrase.free()
nothing := synth.RenderNotes(Vector {} as Vector<synth.Note>, 22050, 1, e)
println audio.IsEmpty(nothing)
mut a := ch
audio.Free(a)
mut b := wn
audio.Free(b)
mut c := pn
audio.Free(c)
mut d := bn
audio.Free(d)
mut f := fm
audio.Free(f)
mut g := add
audio.Free(g)
mut h := ks
audio.Free(h)
mut i := song
audio.Free(i)
endConstants and variables
Waveform numbers Wave…
| Name | Value |
|---|---|
WaveSine | 0 |
WaveSquare | 1 |
WaveSaw | 2 |
WaveTriangle | 3 |
WaveNoise | 4 |
WavePulse | 5 |
The example for this item is shown with the ones above: synth-Osc.salam
Functions
NewOsc
func NewOsc(wave: int, freq: f64, rate: int): OscThe example for this item is shown with the ones above: synth-Osc.salam
OscNext
func OscNext(o &: Osc): f64The example for this item is shown with the ones above: synth-Osc.salam
WaveValue
func WaveValue(wave: int, phase: f64, duty: f64): f64The example for this item is shown with the ones above: synth-Osc.salam
ADSRAt
func ADSRAt(e: ADSR, t: f64, held: f64): f64The example for this item is shown with the ones above: synth-Osc.salam
ADSRTotal
func ADSRTotal(e: ADSR, held: f64): f64The example for this item is shown with the ones above: synth-Osc.salam
Tone
func Tone(freq: f64, seconds: f64, rate: int, wave: int): audio.ClipThe example for this item is shown with the ones above: synth-Osc.salam
ToneEnvelope
func ToneEnvelope(freq: f64, seconds: f64, rate: int, wave: int, e: ADSR, gain: f64): audio.ClipThe example for this item is shown with the ones above: synth-Osc.salam
Chord
func Chord(freqs: Vector<f64>, seconds: f64, rate: int, wave: int): audio.ClipThe example for this item is shown with the ones above: synth-Voice.salam
NoiseWhite
func NoiseWhite(seconds: f64, rate: int): audio.ClipThe example for this item is shown with the ones above: synth-Voice.salam
NoisePink
func NoisePink(seconds: f64, rate: int): audio.ClipThe example for this item is shown with the ones above: synth-Voice.salam
NoiseBrown
func NoiseBrown(seconds: f64, rate: int): audio.ClipThe example for this item is shown with the ones above: synth-Voice.salam
FMTone
func FMTone(carrier: f64, ratio: f64, index: f64, seconds: f64, rate: int): audio.ClipThe example for this item is shown with the ones above: synth-Voice.salam
Additive
func Additive(freq: f64, partials: int, decay: f64, seconds: f64, rate: int): audio.ClipThe example for this item is shown with the ones above: synth-Voice.salam
KarplusStrong
func KarplusStrong(freq: f64, seconds: f64, rate: int, damping: f64): audio.ClipThe example for this item is shown with the ones above: synth-Voice.salam
RenderNotes
func RenderNotes(notes: Vector<Note>, rate: int, channels: int, e: ADSR): audio.ClipThe example for this item is shown with the ones above: synth-Voice.salam
NoteAt
func NoteAt(note: int, start: f64, length: f64, gain: f64, wave: int): NoteThe example for this item is shown with the ones above: synth-Voice.salam