Standard library · Encoding and data

audio/music

Notes, scales, chords and tunings, Persian dastgah among them.

import audio.music

This package is the arithmetic of music rather than of sound: what a note is, which notes belong together, and what frequency each one wants. It makes no sound itself, which audio/synth does.

Notes are MIDI numbers, so 60 is middle C and 69 is the A tuned to 440. NoteToFreq turns one into a frequency in equal temperament, and JustFreq and PythagoreanRatio are there when equal temperament is not what you want.

Things to know

  • Scales and chords come back as vectors of semitone steps, which the caller frees.
  • NoteName returns a string the caller frees. ParseNote reads one back and accepts flats and either case, so it round-trips its own output.
  • Persian music does not fit twelve semitones. A dastgah is therefore given in cents, not semitone steps: the third of Shur sits at 350 cents, between a minor and a major third, where no piano key is. NoteToFreqCents is how you ask for it, and KoronCents and SoriCents are the accidentals that get you there.
  • The seven dastgah here are the frequencies of their tonic rows. A real performance moves between gusheh within a dastgah, which is a matter of melody rather than of tuning.

Constants and variables

A4Note

const A4Note: i32 = 69
The MIDI number of the A above middle C, 69.
music-Note.salam
import audio.music
import math
import mem

func main:
    println music.A4Note, music.A4Freq
    println music.NoteToFreq(69), music.NoteToFreq(81), music.NoteToFreq(57)
    println math.Abs(music.NoteToFreq(60) - 261.6255653005986) < 0.0001
    println music.FreqToNote(440.0), music.FreqToNote(261.63), music.FreqToNote(0.0)
    println music.Octave(60), music.PitchClass(61), music.PitchClass(-1)
    println music.Transpose(60, 7), music.Interval(60, 67)
    println music.Cents(440.0, 880.0), music.FreqToCents(440.0, 69)
    println music.NoteToFreqCents(69, 1200.0)

    each n in [60, 61, 69, 127]:
        name := music.NoteName(n)
        println name, music.ParseNote(name) == n
        mem.Free(name as void*)
    end
    println music.ParseNote("c4"), music.ParseNote("Db4")
    println music.ParseNote("x9"), music.ParseNote(""), music.ParseNote("C")
    println music.BeatSeconds(120.0), music.BeatSeconds(0.0)
end
Output69 440 440 880 220 true 69 60 0 4 1 11 67 7 1200 0 880 C4 true C#4 true A4 true G9 true 60 61 -1 -1 -1 0.5 0

A4Freq

const A4Freq: f64 = 440
The frequency that note is tuned to, 440 Hz.

The example for this item is shown with the ones above: music-Note.salam

Scale numbers Scale…

Which scale to ask Scale or ScaleNotes for.
NameValue
ScaleMajor0
ScaleMinor1
ScaleHarmonicMinor2
ScaleMelodicMinor3
ScalePentatonic4
ScalePentatonicMinor5
ScaleBlues6
ScaleChromatic7
ScaleDorian8
ScalePhrygian9
ScaleLydian10
ScaleMixolydian11
ScaleLocrian12
ScaleWholeTone13
music-Scale.salam
import audio.music

func main:
    mut maj := music.Scale(music.ScaleMajor)
    println maj.len(), maj.get(0), maj.get(6)
    maj.free()
    mut minor := music.Scale(music.ScaleMinor)
    println minor.get(2), minor.get(5)
    minor.free()
    mut chrom := music.Scale(music.ScaleChromatic)
    println chrom.len()
    chrom.free()
    mut pent := music.Scale(music.ScalePentatonic)
    println pent.len()
    pent.free()
    mut blues := music.Scale(music.ScaleBlues)
    println blues.len(), blues.get(3)
    blues.free()
    mut dor := music.Scale(music.ScaleDorian)
    println dor.get(2), dor.get(6)
    dor.free()

    mut run := music.ScaleNotes(60, music.ScaleMajor, 8)
    println run.len(), run.get(0), run.get(7)
    run.free()

    mut tri := music.ChordNotes(60, music.ChordMajor)
    println tri.len(), tri.get(1), tri.get(2)
    tri.free()
    mut m7 := music.ChordNotes(60, music.ChordMinor7)
    println m7.len(), m7.get(3)
    m7.free()
    mut dim := music.Chord(music.ChordDiminished)
    println dim.get(2)
    dim.free()
    mut sus := music.Chord(music.ChordSus4)
    println sus.get(1)
    sus.free()
    mut aug := music.Chord(music.ChordAugmented)
    println aug.get(2)
    aug.free()
    mut dom := music.Chord(music.ChordDominant7)
    println dom.get(3)
    dom.free()
end
Output7 0 11 3 8 12 5 6 6 3 10 8 60 72 3 64 67 4 70 6 5 8 10

Chord numbers Chord…

Which chord to ask Chord or ChordNotes for.
NameValue
ChordMajor0
ChordMinor1
ChordDiminished2
ChordAugmented3
ChordSus24
ChordSus45
ChordMajor76
ChordMinor77
ChordDominant78
ChordDiminished79

The example for this item is shown with the ones above: music-Scale.salam

Dastgah numbers Dastgah…

Which Persian dastgah to ask for. Each is a row of cent offsets, not semitones.
NameValue
DastgahShur0
DastgahMahur1
DastgahHomayoun2
DastgahSegah3
DastgahChahargah4
DastgahNava5
DastgahRastPanjgah6
music-Dastgah.salam
import audio.music

func main:
    mut shur := music.Dastgah(music.DastgahShur)
    println shur.len(), shur.get(0), shur.get(2), shur.get(6)
    shur.free()
    mut mahur := music.Dastgah(music.DastgahMahur)
    println mahur.get(2)
    mahur.free()
    mut homayoun := music.Dastgah(music.DastgahHomayoun)
    println homayoun.get(1)
    homayoun.free()
    mut segah := music.Dastgah(music.DastgahSegah)
    println segah.get(2)
    segah.free()

    println music.KoronCents, music.SoriCents

    mut freqs := music.DastgahFreqs(60, music.DastgahShur)
    println freqs.len()
    println freqs.get(0) == music.NoteToFreq(60)
    println freqs.get(2) > music.NoteToFreq(63), freqs.get(2) < music.NoteToFreq(64)
    freqs.free()

    println music.JustRatio(0), music.JustRatio(7), music.JustRatio(4)
    println music.PythagoreanRatio(0), music.PythagoreanRatio(7), music.PythagoreanRatio(2)
    println music.JustFreq(69, 7)
end
Output7 0 350 1000 400 150 350 -50 50 7 true true true 1 1.5 1.25 1 1.5 1.125 660

KoronCents

const KoronCents: f64 = -50
The cents a koron lowers a note by, about half a semitone.

The example for this item is shown with the ones above: music-Dastgah.salam

SoriCents

const SoriCents: f64 = 50
The cents a sori raises a note by.

The example for this item is shown with the ones above: music-Dastgah.salam

Functions

NoteToFreq

func NoteToFreq(note: int): f64
The frequency of a MIDI note number in equal temperament.

The example for this item is shown with the ones above: music-Note.salam

NoteToFreqCents

func NoteToFreqCents(note: int, cents: f64): f64
The same, moved by so many cents, which is how a quarter tone is asked for.

The example for this item is shown with the ones above: music-Note.salam

FreqToNote

func FreqToNote(freq: f64): int
The nearest MIDI note to a frequency.

The example for this item is shown with the ones above: music-Note.salam

FreqToCents

func FreqToCents(freq: f64, note: int): f64
How many cents a frequency sits away from a note, positive when sharp.

The example for this item is shown with the ones above: music-Note.salam

Octave

func Octave(note: int): int
Which octave a note is in, where middle C is 4.

The example for this item is shown with the ones above: music-Note.salam

PitchClass

func PitchClass(note: int): int
Which of the twelve pitches a note is, 0 for C.

The example for this item is shown with the ones above: music-Note.salam

Transpose

func Transpose(note: int, semitones: int): int
A note moved by so many semitones.

The example for this item is shown with the ones above: music-Note.salam

Interval

func Interval(low: int, high: int): int
How many semitones apart two notes are.

The example for this item is shown with the ones above: music-Note.salam

NoteName

func NoteName(note: int): str
The name of a note, such as C#4. The caller frees it.

The example for this item is shown with the ones above: music-Note.salam

ParseNote

func ParseNote(name: str): int
The note a name means, accepting flats and either case, or -1 when it is not a note.

The example for this item is shown with the ones above: music-Note.salam

Scale

func Scale(kind: int): Vector<int>
The semitone steps of a scale, as a vector the caller frees.

The example for this item is shown with the ones above: music-Scale.salam

ScaleNotes

func ScaleNotes(root: int, kind: int, count: int): Vector<int>
A run of notes in a scale from a root, climbing through octaves as it runs out of degrees.

The example for this item is shown with the ones above: music-Scale.salam

Chord

func Chord(kind: int): Vector<int>
The semitone steps of a chord.

The example for this item is shown with the ones above: music-Scale.salam

ChordNotes

func ChordNotes(root: int, kind: int): Vector<int>
The notes of a chord on a root.

The example for this item is shown with the ones above: music-Scale.salam

Dastgah

func Dastgah(kind: int): Vector<f64>
The cent offsets of a Persian dastgah. Cents rather than semitones, because these modes do not fall on the piano.

The example for this item is shown with the ones above: music-Dastgah.salam

DastgahFreqs

func DastgahFreqs(root: int, kind: int): Vector<f64>
The frequencies of a dastgah from a root note.

The example for this item is shown with the ones above: music-Dastgah.salam

JustRatio

func JustRatio(degree: int): f64
The just intonation ratio for a semitone degree.

The example for this item is shown with the ones above: music-Dastgah.salam

JustFreq

func JustFreq(root: int, degree: int): f64
A frequency in just intonation from a root.

The example for this item is shown with the ones above: music-Dastgah.salam

PythagoreanRatio

func PythagoreanRatio(degree: int): f64
The Pythagorean ratio for a semitone degree.

The example for this item is shown with the ones above: music-Dastgah.salam

Cents

func Cents(a: f64, b: f64): f64
How many cents apart two frequencies are.

The example for this item is shown with the ones above: music-Note.salam

BeatSeconds

func BeatSeconds(bpm: f64): f64
How long one beat lasts at a tempo.

The example for this item is shown with the ones above: music-Note.salam