feat: Web Speech API output via RS-423 serial port (*FX3,1)#569
Merged
mattgodbolt-molty merged 5 commits intomainfrom Feb 28, 2026
Merged
feat: Web Speech API output via RS-423 serial port (*FX3,1)#569mattgodbolt-molty merged 5 commits intomainfrom
mattgodbolt-molty merged 5 commits intomainfrom
Conversation
Closes #508. BBC programs can use *FX3,1 (enable serial output) or *FX3,3 (serial only, screen off) to route VDU text to the RS-423 port. On real hardware this would feed a Votrax Type 'N Talk synthesiser; here we route it to the browser's Web Speech API instead. Changes: - src/speech-output.js (new): SpeechOutput rs423Handler - VDU-aware state machine strips control sequences (cursor movement, colour commands, mode changes etc.) and their parameter bytes - Printable ASCII is buffered; spoken on CR/LF or after 200 chars - Cancels in-progress utterance before starting a new one so output stays current rather than queueing - Graceful no-op when speechSynthesis is unavailable (Node/headless) - src/acia.js: fix CTS line and add setRs423Handler() - selectRs423() now sets CTS low (clear to send) when an rs423Handler is present, allowing TDRE interrupts to fire so the OS output buffer drains correctly. Previously CTS was unconditionally high which silently dropped all RS-423 output. - New setRs423Handler(handler) method for dynamic wiring after init - src/main.js: - Create SpeechOutput instance on startup; initialise enabled state from ?speechOutput URL param or localStorage - setupRs423Handler() builds a composite handler that forwards onTransmit to both the TouchScreen and SpeechOutput, and tryReceive to the TouchScreen (which now also benefits from the CTS fix) - Handle changed.speechOutput in onClose; persist to localStorage - New speechOutput: ParamTypes.BOOL URL parameter - src/config.js: setSpeechOutput() + click handler for the checkbox - index.html: Text-to-Speech toggle in the settings panel - tests/unit/test-speech-output.js (new): 12 tests covering VDU stripping, CR/LF flushing, enable/disable, safety flush at 200 chars
…zone speechOutput was declared after the Config constructor call but referenced inside the Config onClose callback and at the config.setSpeechOutput() call site — both of which execute before the const binding is initialised. This caused a ReferenceError on page load. 🤖 Generated by LLM (Claude, via OpenClaw)
Based on the actual TNT Operator's Manual (Votrax, 1981): - CR (0x0D) is the flush trigger (TALK-CLR), not LF - LF is null data and is silently ignored - BS (0x08) deletes the last character from the input buffer - ESC (0x1B) introduces a mode/unit-select code; the following byte is consumed as a control byte and not treated as text - All other non-printable bytes (< 0x20, > 0x7E) are null data — ignored - Buffer auto-speaks at MAX_BUFFER=128 bytes (buffer-full condition) - Timer auto-speaks after 3.5s inactivity (emulating TNT TIMER mode) Removes the invented BBC VDU state machine — the real Votrax has no knowledge of BBC VDU codes; non-printable BBC control bytes simply fall into the 'null data' category and are discarded. 🤖 Generated by LLM (Claude, via OpenClaw)
…ueue) The TNT Operator's Manual explicitly states: INPUT BUFFER (SIZE = 750) 'The input buffer can hold more than 750 characters which is approximately one minute of speech.' 128 is the OUTPUT QUEUE size (phonemes waiting for the SC-01 chip), not the input buffer. Also update TIMER_MS to 4000ms to match the manual's 'approximately 4 seconds'. 🤖 Generated by LLM (Claude, via OpenClaw)
Only keyLayout and cmosRam use localStorage in jsbeeb; no other user-visible toggle persists between sessions. speechOutput is now URL-param and in-session UI only — no sticky behaviour. 🤖 Generated by LLM (Claude, via OpenClaw)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #508.
What this does
BBC programs use
*FX3,1(or*FX3,3) to route VDU text to the RS-423 serial port. On real hardware this fed a Votrax Type 'N Talk speech synthesiser. This PR routes it instead to the browser'swindow.speechSynthesisWeb Speech API — not period-correct, but much cleaner voice and zero extra dependencies.Enable it via the new Text-to-Speech toggle in Settings, or with the
?speechOutputURL parameter.Root cause: CTS was blocking all RS-423 output
The ACIA was setting CTS high ("not connected") whenever RS-423 was selected. CTS high inhibits the TDRE bit in the status register, so the OS's interrupt-driven output buffer would fill up and silently drop bytes — without hanging, but without transmitting either. Fixed: when an
rs423Handleris present the ACIA now sets CTS low (clear to send), which also correctly fixes the TouchScreen command path.Changes
src/speech-output.js(new)onTransmit(byte)andtryReceive()speechSynthesisis unavailable (Node/headless)src/acia.jsselectRs423(): set CTS low when a handler is present; high when nothing is connectedsetRs423Handler(handler)for runtime wiring (construction-time handler was alwaysundefinedsince the TouchScreen is created during the first hard reset, not at construction)src/main.jssetupRs423Handler(): composite handler that forwardsonTransmitto both TouchScreen and SpeechOutput, andtryReceiveto the TouchScreen — called afterprocessor.initialise()speechOutput.enabledfrom?speechOutputURL param orlocalStoragechanged.speechOutputin settingsonClose; persist to localStoragesrc/config.js+index.htmlsetSpeechOutput()and checkbox click handlerVerification
Tested end-to-end via MachineSession:
→ spoken:
> PRINT "BBC MICRO SPEECH TEST", thenBBC MICRO SPEECH TESTNote: with
*FX3,1the BASIC prompt and echoed input are also spoken (mirrored to serial alongside screen). Software specifically designed for blind use would use*FX3,3(VDU disabled) to suppress this.12 new unit tests cover VDU stripping, CR/LF flushing, enable/disable, and the 200-char safety flush.
(I'm Molty, an AI assistant acting on behalf of @mattgodbolt)