The Machines · Runtime stack

seedbuzzer

A reckoner seed: buzzer, a tune from the entry line — machines/seeds/seedbuzzer.shoddy

the seedbuzzer machine's icon

Summary

> 1 "T140 O5 CDEC CDEC EFG EFG" BUZZPLAY

and the mill (the running program) plays it.

Not a resource, which makes this the odd one out among the seeds that reach a device. (A seed is the file that bridges one machine's words into the reckoner, Shoddy's calculator.) A window and a file are resources: opened, held and shut. A sound is fired. SoundQueue takes a channel number — one of eight channels that always exist — and returns as soon as the notes are queued. So there is nothing to bind, nothing to name and nothing to CLOSE. The resource table does not come into it.

Why It's Useful

What did have to change, and where. buzzer's MML parser could abort — end the whole session — five different ways, and its note-name reader two more. (MML is Music Macro Language, a small text notation for tunes.) A bridged word may not end the session. And there was no guarded builtin to build on, because the parse is not in the runtime — it is in the machine.

So buzzer grew the tree's first machine-level guarded twins. A guarded word reports a failure instead of aborting. TryMmlNotes and TryNoteMidi answer a Result, a value that is either the answer or an error. MmlNotes and NoteMidi are those answers with the Err turned back into the abort it always was. TryReadFile and ReadFile have that shape in the runtime; this brings it up to a machine for the first time.

That was worth rewriting a working parser for. The alternative was a validator walking the string a second time. That is two statements of the same rules, and two things to drift apart. Threading a Result through the recursion leaves exactly one. The new parser reads longer, and all of the extra is unwrapping. That is what carrying a failure through a recursive descent — a parser built from functions that call each other — costs in a language with no exceptions.

And one runtime addition, for the one sound fault nothing could check in advance. SoundQueue refuses more than five minutes queued ahead on a channel. Until SoundQueued existed, there was no way to ask what was already in front of you. A word that measured its own score and still could not see the queue would be guessing. The abort's own message says to feed long scores incrementally, which is the one thing that was impossible without it.

A score is measured before it is committed. BUZZPLAY parses the score, sums the note durations, and adds what is already queued. If the total is too long it refuses the whole thing before a single note goes out, because a tune half queued is a tune that has already started. BUZZLENGTH is the same measurement on its own, so a session can check a score without playing it.

The silence is not an error. With no audio device, every sound word does nothing rather than failing. So BUZZPLAY answers the same at a prompt with speakers and at one without. That is buzzer's own contract and this seed keeps it: a session is told when its music is wrong, and never when the machine it is running on has no sound.

User's Guide

> "A4" BUZZFREQ
x: 440

> "T120 CDEF" BUZZLENGTH
x: 2000                       ' four quarter notes, two seconds

> 1 "T140 O5 CDEC CDEC EFG EFG" BUZZPLAY
> 1 "sine" BUZZWAVE
> 1 0.5 BUZZGAIN

> 1 BUZZQUEUED
x: 5820                       ' still playing

> 1 "CDEZ" BUZZPLAY
BUZZPLAY: MML: UNEXPECTED 'Z' AT POSITION 4

> 1 BUZZSTOP

The MML here is the subset GW-BASIC used (an early home-computer language):

Word Reference

WordDescription
BUZZPLAY ( ch mml -- )Play an MML tune on channel 1 to 8. Returns as soon as the tune is queued.
BUZZTONE ( ch hz ms -- )Queue one tone of hz hertz for ms milliseconds. A frequency of 0 is a rest.
BUZZNOTE ( ch name ms -- )Queue one named note — "C4", "F#3", "Bb5" — for ms milliseconds. C4 is middle C.
BUZZFREQ ( name -- hz )The frequency of a named note in hertz, and nothing is played. "A4" is 440.
BUZZLENGTH ( mml -- ms )How long that tune would take, without playing a note of it. The way to check a score before committing it.
BUZZQUEUED ( ch -- ms )How far ahead that channel is already queued, and 0 once it has drained. BUZZLENGTH measures a tune; this measures what is in front of it.
BUZZSTOP ( ch -- )Stop that channel at once and throw away whatever it had queued.
BUZZGAIN ( ch v -- )How loud that channel is, from 0 to 1. Sticky until set again.
BUZZWAVE ( ch name -- )The channel's waveform: "square", "triangle" or "sine". Sticky until set again.

Who Uses It

UserHow
halifaxThe calculator's sound words — a tune typed at the prompt, on a machine with speakers or without.

A mill claims this seed by folding RckSeedBuzzer over its reckoner state, which is all halifax does.

The Machines It Uses

MachineWhy
buzzerThe domain this seed bridges — and, for it, the machine grew TryMmlNotes and TryNoteMidi so that a malformed score could be reported rather than fatal.
cuttleThe Cell type the arguments cross the bridge as.
reckonerRckReg and the argument readers every registered word is built from.
seqFolding the note list to a duration, and Pair for the multi-argument words.