RouterOS beeper player
Super Mario Bros, remastered
Every part of the tune is written down once, and a playlist calls them in the game’s order, counting each strain as it comes round. It plays the same notes as the notes page, tone for tone, with the bass and the grace notes slipped in on one speaker.
1# Super Mario Bros. ground theme (overworld), Koji Kondo, 1985 - remastered for one beeper - https://lab.glossema.dev/play/mario-remastered/code2# It plays the notes of https://lab.glossema.dev/play/mario-remastered, tone for tone.3# Each part is written once: its lead, note by note, and the bass voice's notes on beats 1 and 3.4# The form line is the playlist: the intro call, the A strain twice, the B strain twice, the C strain5# (closing on the intro call), the A strain twice more, the D strain twice (the second ending on the arrival).6# One speaker sounds every voice: a bass stroke, a grace or an answer takes its time out of the slot7# of the note or rest it sits in, never out of the bar.8{9 # The pitch of each note the tune sounds, in Hz10 :local hz {11 "C3"=131; "D3"=147; "F3"=175; "G3"=196; "G#3"=208; "B3"=247;12 "C4"=262; "E4"=330; "G4"=392; "G#4"=415; "A4"=440; "A#4"=466; "B4"=494;13 "C5"=523; "D5"=587; "D#5"=622; "E5"=659; "F5"=698; "F#5"=740; "G5"=784; "A5"=880; "B5"=988;14 "C6"=1047; "F6"=139715 }1617 # Note values: e eighth, q quarter, d dotted quarter, h half, t a triplet note (three to a half).18 # The slot each takes in ms, how long a note sounds in it, and how long when a rest comes next.19 :local slots {"e"=150; "q"=300; "d"=450; "h"=600; "t"=200}20 :local full {"e"=120; "q"=270; "d"=405; "h"=540; "t"=180}21 :local light {"e"=90; "d"=270; "h"=360}2223 # One slot of the lead, shared by the voices: the bass stroke, then a grace or an answer, each24 # paid out of the slot; the note, or the rest, takes what is left of it.25 :local share do={26 :local spent 027 :if ([:len $bass] > 0) do={28 :beep frequency=($hz->$bass) length=30ms29 :delay 35ms30 :set spent ($spent + 35)31 }32 :if ([:len $grace] > 0) do={33 :beep frequency=($hz->$grace) length=22ms34 :delay 22ms35 :set spent ($spent + 22)36 }37 :if ([:len $answer] > 0) do={38 :beep frequency=($hz->$answer) length=50ms39 :delay 60ms40 :set spent ($spent + 60)41 }42 :if ($note != "-") do={ :beep frequency=($hz->$note) length=(($length - $spent) * 1ms) }43 :delay (($slot - $spent) * 1ms)44 }4546 # The parts, each written once. The lead, a bar to a line: note and value, a dash for a rest,47 # and after a rest the second voice's note that answers in it. The bass: the bass voice's note48 # struck on beat 1 and on beat 3 of each bar, a dash where it does not strike there.49 :local parts {50 "intro"={51 "lead"={52 "E5:e"; "E5:q"; "E5:q"; "C5:e"; "E5:q";53 "G5:h"; "G4:h"54 };55 "bass"={"D3"; "-"; "G4"; "G3"}56 };57 "A"={58 "lead"={59 "C5:d"; "G4:d"; "E4:d";60 "A4:q"; "B4:q"; "A#4:e"; "A4:e"; "-:e";61 "G4:t"; "E5:t"; "G5:t"; "A5:e"; "-:e"; "F5:e"; "G5:q";62 "E5:q"; "C5:e"; "D5:e"; "B4:d"63 };64 "bass"={"G3"; "-"; "-"; "-"; "E3"; "F4"; "-"; "B3"}65 };66 "B-opening"={67 "lead"={68 "-:q"; "G5:e"; "F#5:e"; "F5:e"; "D#5:e"; "-:e"; "E5:e";69 "-:e"; "G#4:e"; "A4:e"; "C5:e"; "-:e"; "A4:e"; "C5:e"; "D5:e"70 };71 "bass"={"C3"; "-"; "F3"; "C4"}72 };73 "B-high"={74 "lead"={75 "-:q"; "G5:e"; "F#5:e"; "F5:e"; "D#5:e"; "-:e"; "E5:e";76 "-:e"; "C6:e"; "-:e"; "C6:e"; "C6:h"77 };78 "bass"={"C3"; "-"; "-"; "G5"}79 };80 "B-close"={81 "lead"={82 "-:q"; "D#5:d"; "D5:d";83 "C5:h"; "-:h"84 };85 "bass"={"C3"; "-"; "C4"; "G3"}86 };87 "C-opening"={88 "lead"={89 "C5:e"; "C5:q"; "C5:q"; "C5:e"; "D5:e"; "-:e";90 "E5:e"; "C5:e"; "-:e"; "A4:e"; "G4:h"91 };92 "bass"={"G#2"; "-"; "G3"; "-"}93 };94 "C-answer"={95 "lead"={96 "C5:e"; "C5:e"; "-:e"; "C5:e"; "-:e"; "C5:e"; "D5:e"; "E5:d";97 "-:d"; "-:d"98 };99 "bass"={"G#2"; "-"; "G3"; "-"}100 };101 "D-opening"={102 "lead"={103 "E5:e"; "C5:q"; "G4:d"; "G#4:q";104 "A4:e"; "F5:e"; "-:e"; "F5:e"; "A4:h"105 };106 "bass"={"C3"; "G3"; "F3"; "C4"}107 };108 "D-climb"={109 "lead"={110 "B4:t"; "A5:t"; "A5:t"; "A5:t"; "G5:t"; "F5:t";111 "E5:e"; "C5:e"; "-:e"; "A4:e"; "G4:h"112 };113 "bass"={"D3"; "G3"; "G3"; "C4"}114 };115 "D-close"={116 "lead"={117 "B4:e"; "F5:e"; "-:e"; "F5:e"; "F5:t"; "E5:t"; "D5:t";118 "C5:h"; "-:h:C4"119 };120 "bass"={"G3"; "G3"; "C4"; "C3"}121 }122 }123124 # Graces, 22ms, by part and pass: each in front of the first note of its name.125 # On its fourth pass the A strain puts the octave above in front of its first C5 and its first F5.126 :local graces {127 "intro 1"={"G5"="F#5"};128 "B-opening 1"={"G5"="A5"};129 "B-high 1"={"C6"="B5"};130 "B-high 2"={"C6"="B5"};131 "C-answer 1"={"E5"="D#5"};132 "A 4"={"C5"="C6"; "F5"="F6"}133 }134135 # The playlist: the form line, part by part136 :local form {137 "intro"; "A"; "A";138 "B-opening"; "B-high"; "B-opening"; "B-close"; "B-opening"; "B-high"; "B-opening"; "B-close";139 "C-opening"; "C-answer"; "C-opening"; "intro";140 "A"; "A";141 "D-opening"; "D-climb"; "D-opening"; "D-close"; "D-opening"; "D-climb"; "D-opening"; "D-close"142 }143144 # The strains, by the part each one begins with, as the terminal names them145 :local strains {146 "intro"="Intro call"; "A"="A strain"; "B-opening"="B strain"; "C-opening"="C strain"; "D-opening"="D strain"147 }148149 # Where a strain begins: at the first part, and after each part that closes one (the intro150 # call, an A, B-close, D-close). Count how many times each strain plays.151 :local titles [:toarray ""]152 :local plays [:toarray ""]153 :foreach title in=$strains do={ :set ($plays->$title) 0 }154 :local begins true155 :foreach part in=$form do={156 :local title ""157 :if ($begins) do={158 :set title ($strains->$part)159 :set ($plays->$title) (($plays->$title) + 1)160 }161 :set titles ($titles, $title)162 :set begins (($part = "intro") || ($part = "A") || ($part = "B-close") || ($part = "D-close"))163 }164165 # Play the form, part by part166 :local heard [:toarray ""]167 :foreach title in=$strains do={ :set ($heard->$title) 0 }168 :local passes [:toarray ""]169 :foreach part in=$form do={ :set ($passes->$part) 0 }170 :local last ([:len $form] - 1)171 :for i from=0 to=$last do={172 :local part ($form->$i)173 :local lead ($parts->$part->"lead")174 :local bass ($parts->$part->"bass")175 # The graces this pass of the part sounds176 :set ($passes->$part) (($passes->$part) + 1)177 :local pass ($part . " " . ($passes->$part))178 :local pending [:toarray ""]179 :foreach which,notes in=$graces do={180 :if ($which = $pass) do={ :set pending $notes }181 }182 # The last part ends on the arrival: its closing rest is not played183 :local end ([:len $lead] - 1)184 :if ($i = $last) do={ :set end ($end - 1) }185 # Each strain is announced as it begins, with its count when it plays more than once186 :local title ($titles->$i)187 :if ($title != "") do={188 :set ($heard->$title) (($heard->$title) + 1)189 :if (($plays->$title) > 1) do={190 :set title ($title . " " . ($heard->$title) . " of " . ($plays->$title))191 }192 :put $title193 }194 :local onset 0195 :for k from=0 to=$end do={196 # One step of the lead: a note or a dash, its value, and the answer after a rest197 :local step ($lead->$k)198 :local colon [:find $step ":"]199 :local note [:pick $step 0 $colon]200 :local value [:pick $step ($colon + 1)]201 :local answer ""202 :if ([:len $step] > ($colon + 3)) do={ :set answer [:pick $step ($colon + 3) [:len $step]] }203 # A note sounds light when a rest comes next, in this part or at the start of the next204 :local next ""205 :if ($k < ([:len $lead] - 1)) do={206 :set next ($lead->($k + 1))207 } else={208 :set next ($parts->($form->($i + 1))->"lead"->0)209 }210 :local length ($full->$value)211 :if ([:pick $next 0 1] = "-") do={ :set length ($light->$value) }212 :local slot ($slots->$value)213 # On beats 1 and 3, where a step begins, the bass strikes, moved into the third octave;214 # never in front of a triplet note215 :local stroke ""216 :if ((($onset % 600) = 0) && ($value != "t")) do={217 :local struck ($bass->($onset / 600))218 :if ($struck != "-") do={ :set stroke ([:pick $struck 0 ([:len $struck] - 1)] . "3") }219 }220 # A grace sounds once, in front of the first note of its name221 :local grace ""222 :foreach name,sound in=$pending do={223 :if ($name = $note) do={ :set grace $sound }224 }225 :if ([:len $grace] > 0) do={ :set ($pending->$note) "" }226 # The arrival: the last C5 held 1500ms after the 35ms of its bass stroke227 :if (($i = $last) && ($k = $end)) do={228 :set length (35 + 1500)229 :set slot (35 + 1500)230 }231 $share hz=$hz note=$note bass=$stroke grace=$grace answer=$answer length=$length slot=$slot232 :set onset ($onset + ($slots->$value))233 }234 }235}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.