# Badinerie, Orchestral Suite No. 2 in B minor, BWV 1067, by Johann Sebastian Bach - https://lab.glossema.dev/play/badinerie/code # It plays the notes of https://lab.glossema.dev/play/badinerie, tone for tone. # A solo flute over the continuo in 2/4, one unbroken line from the upbeat to the final B. # Bach's flute part, as the Bach-Gesellschaft edition prints it, is written below bar by bar # and read as it plays. The continuo taps on every downbeat, the ornaments sound just before # their notes, each half is played twice, and a fermata closes the piece. { # A thirty-second lasts 60 ms: the quarter at 125 beats a minute :local unit 60ms # The pitch of each note in Hz: the flute's, then the continuo's, in the octave from C3 to B3 :local hz { "F#4"=370; "G4"=392; "A4"=440; "A#4"=466; "B4"=494; "C5"=523; "C#5"=554; "D5"=587; "D#5"=622; "E5"=659; "F5"=698; "F#5"=740; "G5"=784; "G#5"=831; "A5"=880; "B5"=988; "C#6"=1109; "D6"=1175; "C#3"=139; "D3"=147; "E3"=165; "F#3"=185; "G3"=196; "G#3"=208; "A3"=220; "A#3"=233; "B3"=247 } # How long each note value sounds, the value in thirty-seconds: an eighth (4) sounds # for 200 ms of its 240 ms slot, and the rest of the slot is silence :local tone {"1"=50ms; "2"=105ms; "4"=200ms; "8"=430ms; "10"=560ms; "12"=680ms} # The eight ornaments, each a short tone just before its note, listed by part and bar: # the onset of that note, then the note the ornament sounds. Six trills, each sounded as # its upper neighbour, and the two small printed graces (bar 17 of B and the close of B) :local ornaments { "A 8"={"0"; "E5"}; "A 10"={"0"; "G#5"}; "A 15"={"12"; "A5"}; "B 11"={"12"; "F#5"}; "B 14"={"0"; "G5"}; "B 16"={"0"; "F#5"}; "B 17"={"0"; "A5"}; "B close 1"={"0"; "C#5"} } # The fermata holds the last bar for 1260 ms, its B sounding to the end :local fermata 1260ms # The flute part, bar by bar: each note is its name, then onset/value in thirty-seconds. # A bar that opens on a downbeat first names the continuo's note under it. :local music { "A upbeat"={"B5 0/4 D6 4/2 B5 6/2"}; "A"={ "D3: F#5 0/4 B5 4/2 F#5 6/2 D5 8/4 F#5 12/2 D5 14/2"; "B3: B4 0/8 F#4 8/2 B4 10/2 D5 12/2 B4 14/2"; "E3: C#5 0/2 B4 2/2 C#5 4/2 B4 6/2 A#4 8/2 C#5 10/2 E5 12/2 C#5 14/2"; "B2: D5 0/4 B4 4/4 B5 8/4 D6 12/2 B5 14/2"; "D3: F#5 0/4 B5 4/2 F#5 6/2 D5 8/4 F#5 12/2 D5 14/2"; "B3: B4 0/8 D5 8/4 D5 12/4"; "G#3: D5 0/4 D5 4/4 B5 8/4 D5 12/4"; "A3: D5 0/4 C#5 4/4 F#5 8/4 F#5 12/4"; "B3: F#5 0/4 F#5 4/4 D6 8/4 F#5 12/4"; "C#4: F#5 0/4 F5 4/4 C#5 8/2 F#5 10/2 A5 12/2 F#5 14/2"; "B3: G#5 0/2 F#5 2/2 G#5 4/2 F#5 6/2 F5 8/2 G#5 10/2 B5 12/2 G#5 14/2"; "F#3: A5 0/2 G#5 2/2 A5 4/2 G#5 6/2 F#5 8/2 A5 10/2 F#5 12/2 F5 14/2"; "D3: F#5 0/2 B5 2/2 F#5 4/2 F5 6/2 F#5 8/2 C#6 10/2 F#5 12/2 F5 14/2"; "B2: F#5 0/2 D6 2/2 F#5 4/2 F5 6/2 F#5 8/2 D6 10/2 C#6 12/2 B5 14/2"; "A3: C#6 0/2 A5 2/2 G#5 4/2 F#5 6/2 A5 8/4 G#5 12/4" }; "A close"={"F#3: F#5 0/8"}; "B upbeat"={"F#5 0/4 A5 4/2 F#5 6/2"}; "B"={ "A2: C#5 0/4 F#5 4/2 C#5 6/2 A4 8/4 C#5 12/2 A4 14/2"; "F#3: F#4 0/8 C5 8/4 B4 12/4"; "B2: E5 0/4 D#5 4/2 F#5 6/2 A5 8/4 G5 12/2 F#5 14/2"; "E3: G5 0/4 E5 4/4 G5 8/4 B5 12/2 G5 14/2"; "C#3: E5 0/4 G5 4/2 E5 6/2 C#5 8/4 E5 12/2 C#5 14/2"; "G3: A4 0/10 D5 10/2 F#5 12/2 D5 14/2"; "G3: E5 0/2 D5 2/2 E5 4/2 D5 6/2 C#5 8/2 E5 10/2 G5 12/2 E5 14/2"; "D3: F#5 0/2 E5 2/2 F#5 4/2 E5 6/2 D5 8/2 F#5 10/2 D5 12/2 C#5 14/2"; "B2: D5 0/2 G5 2/2 D5 4/2 C#5 6/2 D5 8/2 A5 10/2 D5 12/2 C#5 14/2"; "G2: D5 0/2 B5 2/2 D5 4/2 C#5 6/2 D5 8/2 B5 10/2 A5 12/2 G5 14/2"; "F#3: A5 0/2 F#5 2/2 E5 4/2 D5 6/2 F#5 8/4 E5 12/4"; "D3: D5 0/8 F#5 8/4 F#5 12/4"; "F#3: F#5 0/4 F#5 4/4 D6 8/4 F#5 12/4"; "A3: F#5 0/4 E5 4/4 E5 8/4 E5 12/4"; "A#3: E5 0/4 E5 4/4 C#6 8/4 E5 12/4"; "B3: E5 0/4 D5 4/4 B5 8/4 D6 12/2 B5 14/2"; "E3: G5 0/12 B5 12/1 A5 13/1 G5 14/1 F#5 15/1"; "E3: E5 0/12 G5 12/1 F#5 13/1 E5 14/1 D5 15/1"; "E3: C5 0/2 E5 2/2 G5 4/2 E5 6/2 C5 8/2 B4 10/2 C5 12/2 B4 14/2"; "E3: A#4 0/4 F#4 4/4 G4 8/4 F#4 12/4"; "F#3: B4 0/4 A#4 4/2 C#5 6/2 E5 8/4 D5 12/2 C#5 14/2"; "B3: D5 0/4 B4 4/1 C#5 5/1 D5 6/1 E5 7/1 F#5 8/4 D5 12/2 F#5 14/2"; "D3: B5 0/4 F#5 4/4 E5 8/2 D5 10/2 C#5 12/2 D5 14/2" }; "B close"={"B2: B4 0/8"} } # share: one flute note, with the continuo's tap and its ornament paid out of its slot. # A tap or an ornament (0 when the note has none) sounds 25 ms and takes 30, # and the flute note has the rest. :local share do={ :local spent 0ms :if ($tap > 0) do={ :beep frequency=$tap length=25ms :delay 30ms :set spent 30ms } :if ($ornament > 0) do={ :beep frequency=$ornament length=25ms :delay 30ms :set spent ($spent + 30ms) } :beep frequency=$note length=($length - $spent) :delay ($slot - $spent) } # The form: each half twice, and every pass its upbeat, its bars and its close :foreach title,half in={"First half"="A"; "Second half"="B"} do={ :foreach pass in={$title; "$title again"} do={ :put $pass :foreach part in={"$half upbeat"; $half; "$half close"} do={ :local bar 0 :foreach text in=($music->$part) do={ :set bar ($bar + 1) # the bar's ornament, if it has one: the onset of its note and its pitch :local ornamentAt "" :local ornamentHz 0 :foreach where,mark in=$ornaments do={ :if ($where = "$part $bar") do={ :set ornamentAt ($mark->0) :set ornamentHz ($hz->($mark->1)) } } # the continuo's note, if the bar names one, moved into the octave from C3 to B3 :local continuo 0 :local notes $text :local colon [:find ($text . ":") ":"] :if ($colon < [:len $text]) do={ :set continuo ($hz->([:pick $text 0 ($colon - 1)] . "3")) :set notes [:pick $text ($colon + 2) [:len $text]] } # then the flute's notes, one at a time, each followed by two spaces :set notes ($notes . " ") :local start 0 :while ($start < [:len $notes]) do={ :local space [:find $notes " " $start] :local slash [:find $notes "/" $start] :local gap [:find $notes " " $start] :local name [:pick $notes $start $space] :local onset [:pick $notes ($space + 1) $slash] :local value [:pick $notes ($slash + 1) $gap] :set start ($gap + 2) # the continuo taps on the downbeat :local tap 0 :if ($onset = "0") do={ :set tap $continuo } # the ornament, if this note has one :local ornament 0 :if ($onset = $ornamentAt) do={ :set ornament $ornamentHz } # the note's tone and slot; on the last pass the close is the fermata :local length ($tone->$value) :local slot ($unit * [:tonum $value]) :if (($pass = "Second half again") && ($part = "B close")) do={ :set length $fermata :set slot $fermata } $share tap=$tap ornament=$ornament note=($hz->$name) length=$length slot=$slot } } } } } }