RouterOS beeper player
Badinerie
The flute part from the Bach-Gesellschaft print sits in the program as text, bar by bar, and nested loops read it out: each half twice, the continuo’s tap on every downbeat, the ornaments from a small table, the fermata at the close. It plays the same notes as the notes page, tone for tone.
1# Badinerie, Orchestral Suite No. 2 in B minor, BWV 1067, by Johann Sebastian Bach - https://lab.glossema.dev/play/badinerie/code2# It plays the notes of https://lab.glossema.dev/play/badinerie, tone for tone.3# A solo flute over the continuo in 2/4, one unbroken line from the upbeat to the final B.4# Bach's flute part, as the Bach-Gesellschaft edition prints it, is written below bar by bar5# and read as it plays. The continuo taps on every downbeat, the ornaments sound just before6# their notes, each half is played twice, and a fermata closes the piece.7{8 # A thirty-second lasts 60 ms: the quarter at 125 beats a minute9 :local unit 60ms1011 # The pitch of each note in Hz: the flute's, then the continuo's, in the octave from C3 to B312 :local hz {13 "F#4"=370; "G4"=392; "A4"=440; "A#4"=466; "B4"=494; "C5"=523; "C#5"=554; "D5"=587; "D#5"=622;14 "E5"=659; "F5"=698; "F#5"=740; "G5"=784; "G#5"=831; "A5"=880; "B5"=988; "C#6"=1109; "D6"=1175;15 "C#3"=139; "D3"=147; "E3"=165; "F#3"=185; "G3"=196; "G#3"=208; "A3"=220; "A#3"=233; "B3"=24716 }1718 # How long each note value sounds, the value in thirty-seconds: an eighth (4) sounds19 # for 200 ms of its 240 ms slot, and the rest of the slot is silence20 :local tone {"1"=50ms; "2"=105ms; "4"=200ms; "8"=430ms; "10"=560ms; "12"=680ms}2122 # The eight ornaments, each a short tone just before its note, listed by part and bar:23 # the onset of that note, then the note the ornament sounds. Six trills, each sounded as24 # its upper neighbour, and the two small printed graces (bar 17 of B and the close of B)25 :local ornaments {26 "A 8"={"0"; "E5"}; "A 10"={"0"; "G#5"}; "A 15"={"12"; "A5"}; "B 11"={"12"; "F#5"};27 "B 14"={"0"; "G5"}; "B 16"={"0"; "F#5"}; "B 17"={"0"; "A5"}; "B close 1"={"0"; "C#5"}28 }2930 # The fermata holds the last bar for 1260 ms, its B sounding to the end31 :local fermata 1260ms3233 # The flute part, bar by bar: each note is its name, then onset/value in thirty-seconds.34 # A bar that opens on a downbeat first names the continuo's note under it.35 :local music {36 "A upbeat"={"B5 0/4 D6 4/2 B5 6/2"};37 "A"={38 "D3: F#5 0/4 B5 4/2 F#5 6/2 D5 8/4 F#5 12/2 D5 14/2";39 "B3: B4 0/8 F#4 8/2 B4 10/2 D5 12/2 B4 14/2";40 "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";41 "B2: D5 0/4 B4 4/4 B5 8/4 D6 12/2 B5 14/2";42 "D3: F#5 0/4 B5 4/2 F#5 6/2 D5 8/4 F#5 12/2 D5 14/2";43 "B3: B4 0/8 D5 8/4 D5 12/4";44 "G#3: D5 0/4 D5 4/4 B5 8/4 D5 12/4";45 "A3: D5 0/4 C#5 4/4 F#5 8/4 F#5 12/4";46 "B3: F#5 0/4 F#5 4/4 D6 8/4 F#5 12/4";47 "C#4: F#5 0/4 F5 4/4 C#5 8/2 F#5 10/2 A5 12/2 F#5 14/2";48 "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";49 "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";50 "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";51 "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";52 "A3: C#6 0/2 A5 2/2 G#5 4/2 F#5 6/2 A5 8/4 G#5 12/4"53 };54 "A close"={"F#3: F#5 0/8"};55 "B upbeat"={"F#5 0/4 A5 4/2 F#5 6/2"};56 "B"={57 "A2: C#5 0/4 F#5 4/2 C#5 6/2 A4 8/4 C#5 12/2 A4 14/2";58 "F#3: F#4 0/8 C5 8/4 B4 12/4";59 "B2: E5 0/4 D#5 4/2 F#5 6/2 A5 8/4 G5 12/2 F#5 14/2";60 "E3: G5 0/4 E5 4/4 G5 8/4 B5 12/2 G5 14/2";61 "C#3: E5 0/4 G5 4/2 E5 6/2 C#5 8/4 E5 12/2 C#5 14/2";62 "G3: A4 0/10 D5 10/2 F#5 12/2 D5 14/2";63 "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";64 "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";65 "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";66 "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";67 "F#3: A5 0/2 F#5 2/2 E5 4/2 D5 6/2 F#5 8/4 E5 12/4";68 "D3: D5 0/8 F#5 8/4 F#5 12/4";69 "F#3: F#5 0/4 F#5 4/4 D6 8/4 F#5 12/4";70 "A3: F#5 0/4 E5 4/4 E5 8/4 E5 12/4";71 "A#3: E5 0/4 E5 4/4 C#6 8/4 E5 12/4";72 "B3: E5 0/4 D5 4/4 B5 8/4 D6 12/2 B5 14/2";73 "E3: G5 0/12 B5 12/1 A5 13/1 G5 14/1 F#5 15/1";74 "E3: E5 0/12 G5 12/1 F#5 13/1 E5 14/1 D5 15/1";75 "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";76 "E3: A#4 0/4 F#4 4/4 G4 8/4 F#4 12/4";77 "F#3: B4 0/4 A#4 4/2 C#5 6/2 E5 8/4 D5 12/2 C#5 14/2";78 "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";79 "D3: B5 0/4 F#5 4/4 E5 8/2 D5 10/2 C#5 12/2 D5 14/2"80 };81 "B close"={"B2: B4 0/8"}82 }8384 # share: one flute note, with the continuo's tap and its ornament paid out of its slot.85 # A tap or an ornament (0 when the note has none) sounds 25 ms and takes 30,86 # and the flute note has the rest.87 :local share do={88 :local spent 0ms89 :if ($tap > 0) do={90 :beep frequency=$tap length=25ms91 :delay 30ms92 :set spent 30ms93 }94 :if ($ornament > 0) do={95 :beep frequency=$ornament length=25ms96 :delay 30ms97 :set spent ($spent + 30ms)98 }99 :beep frequency=$note length=($length - $spent)100 :delay ($slot - $spent)101 }102103 # The form: each half twice, and every pass its upbeat, its bars and its close104 :foreach title,half in={"First half"="A"; "Second half"="B"} do={105 :foreach pass in={$title; "$title again"} do={106 :put $pass107 :foreach part in={"$half upbeat"; $half; "$half close"} do={108 :local bar 0109 :foreach text in=($music->$part) do={110 :set bar ($bar + 1)111 # the bar's ornament, if it has one: the onset of its note and its pitch112 :local ornamentAt ""113 :local ornamentHz 0114 :foreach where,mark in=$ornaments do={115 :if ($where = "$part $bar") do={116 :set ornamentAt ($mark->0)117 :set ornamentHz ($hz->($mark->1))118 }119 }120 # the continuo's note, if the bar names one, moved into the octave from C3 to B3121 :local continuo 0122 :local notes $text123 :local colon [:find ($text . ":") ":"]124 :if ($colon < [:len $text]) do={125 :set continuo ($hz->([:pick $text 0 ($colon - 1)] . "3"))126 :set notes [:pick $text ($colon + 2) [:len $text]]127 }128 # then the flute's notes, one at a time, each followed by two spaces129 :set notes ($notes . " ")130 :local start 0131 :while ($start < [:len $notes]) do={132 :local space [:find $notes " " $start]133 :local slash [:find $notes "/" $start]134 :local gap [:find $notes " " $start]135 :local name [:pick $notes $start $space]136 :local onset [:pick $notes ($space + 1) $slash]137 :local value [:pick $notes ($slash + 1) $gap]138 :set start ($gap + 2)139 # the continuo taps on the downbeat140 :local tap 0141 :if ($onset = "0") do={ :set tap $continuo }142 # the ornament, if this note has one143 :local ornament 0144 :if ($onset = $ornamentAt) do={ :set ornament $ornamentHz }145 # the note's tone and slot; on the last pass the close is the fermata146 :local length ($tone->$value)147 :local slot ($unit * [:tonum $value])148 :if (($pass = "Second half again") && ($part = "B close")) do={149 :set length $fermata150 :set slot $fermata151 }152 $share tap=$tap ornament=$ornament note=($hz->$name) length=$length slot=$slot153 }154 }155 }156 }157 }158}On the router: paste the script into a terminal, or upload the file and run it.
Script output
Write your own
Ask for a tune you may arrange: your own, one out of copyright, or one you are licensed to use.
Have a MIDI file? altucor’s converter turns it into a script.
How it works
One command sounds one tone: :beep frequency=<hertz> length=<time>; the beeper plays one note at a time.
:delay is the clock. A beep starts the tone and returns at once, so every note is a beep followed by a delay of the same length and a small gap.
Most tunes here are written out note by note, a beep and a delay each; some also come as code, with loops, tables and functions, that plays the very same notes. Download any of these and run it in a terminal.