Add isolated Remote speech synthesis and typed rendering
Preserve literal whitespace and resolved spelling/language state across complete Remote speech assemblies. Isolate cancellation from local status and echo, retire handles on configuration changes or rendering failure, and cover metadata preparation cancellation with private SSIP fixtures. Verified focused application and backend race tests, ten repetitions of isolated-client/rendering cases, shared speech regressions, affected-package vet, reader build and Tentacles checks. Incoming mailbox integration, local priority and live audible acceptance remain pending.
This commit is contained in:
1 parent
622d2f96a1
commit
ac431dd514
9 files changed
+1370
-11
No files matched your search
@@ -138,6 +138,120 @@ warnings remain in place.
|
||||
|
||||
## Verification and remaining work
|
||||
|
||||
### Isolated synthesis prerequisite
|
||||
|
||||
The configured software backend now has an internal `open_remote_voice`
|
||||
primitive. It lazily creates one independent Speech Dispatcher speaking/control
|
||||
pair using the last successfully committed default voice settings. Ordinary
|
||||
reader startup creates no additional connections. Hardware and unconnected or
|
||||
closed backends reject acquisition; another acquisition is rejected until the
|
||||
previous handle has finished closing. This is not a lease authorizer and is not
|
||||
attached to the production incoming mailbox.
|
||||
|
||||
The handle offers plain-text `SpeakAndWait`, scoped `Cancel`, and idempotent
|
||||
`Close`. Speech remains outstanding through END/CANCEL, rather than just queue
|
||||
acknowledgement. Caller cancellation affects only the isolated client. Acquisition
|
||||
context cancellation after a successful open does not retire that handle: a
|
||||
future lease owner must explicitly close it on revoke, policy loss and shutdown.
|
||||
Core user interruption cancels both local output and this separate client.
|
||||
|
||||
A successful settings commit retires the old handle; it never silently adopts
|
||||
the replacement configuration. Failed preparation preserves it. Closing an old
|
||||
handle cannot cancel its replacement. Configuration updates now also retain the
|
||||
committed settings snapshot when reusing a software backend, so subsequent
|
||||
acquisition uses the current module and default voice rather than startup values.
|
||||
|
||||
Private Unix-socket SSIP tests keep local status and custom echo queued while
|
||||
remote cancellation, caller cancellation and handle closure run. They check
|
||||
independent client identities, END/CANCEL pacing, connection reuse, exclusive
|
||||
ownership, configuration success/failure and stale cleanup. These model protocol
|
||||
ownership; they do not establish real Speech Dispatcher scheduling, audible
|
||||
cancellation, or priority arbitration. The original plain-text primitive retains
|
||||
ordinary normalization. The typed renderer below adds whitespace and
|
||||
character/language handling; pause and NVDA priority still need stream policy.
|
||||
|
||||
No command invokes this primitive yet. The rendering prerequisite below adds
|
||||
typed utterances; binding them to the bounded reader mailbox still requires
|
||||
delivery-time lease/focus checks and local priority. Keep mailbox budgets until
|
||||
delivery/discard and retire queued work on cancellation. No live Remote speech
|
||||
acceptance test is available from these prerequisites alone.
|
||||
|
||||
### Literal utterance rendering prerequisite
|
||||
|
||||
`prepare_remote_utterance` now validates a complete speak assembly, retaining its
|
||||
single priority for a future stream owner. It rejects non-speak effects, mixed
|
||||
priorities and assemblies beyond the existing 64-frame/256-KiB limits. It joins
|
||||
adjacent text with the same language/spelling state across IPC fragment
|
||||
boundaries without adding separators. An end marker or metadata change creates
|
||||
a synthesis boundary; repeated empty end markers produce no speech. It neither
|
||||
owns nor releases mailbox budget and cannot authorize presentation.
|
||||
|
||||
The isolated handle's `SpeakUtteranceAndWait` renders that prepared sequence
|
||||
through a new bounded `speech.Client.SpeakFragmentsAndWait` path. Every literal
|
||||
fragment is checked before backend I/O. Invalid UTF-8, NUL, C0 controls other than
|
||||
tab/CR/LF, DEL and invalid language tokens reject the whole sequence without a
|
||||
spoken prefix. Spaces and tabs remain intact; CR, LF and CRLF become SSIP CRLF
|
||||
line boundaries. Every dot-prefixed line is escaped, including a lone dot.
|
||||
`SSML_MODE off` makes markup and command-looking text literal data. This changes
|
||||
no ordinary local-text normalization.
|
||||
|
||||
Each fragment uses `SPELLING on/off` and its resolved language. Before rendering,
|
||||
the client reads its effective default language, output module and symbolic voice
|
||||
category; unsupported/malformed defaults fail closed. It restores the original
|
||||
module after every language change, because Speech Dispatcher language mappings
|
||||
may change modules. Remote language overrides clear a configured named voice and
|
||||
use the captured symbolic category. If a named voice has no category, explicit
|
||||
remote languages use SSIP category MALE1; default-language parts restore the
|
||||
configured voice name. Unknown categories without a configured named voice are
|
||||
rejected. The accepted module/default-language tokens are limited to 64 ASCII
|
||||
letters, digits, hyphens or underscores. Backend acceptance of a language request
|
||||
does not establish that the module can synthesize that language.
|
||||
|
||||
The speaking connection remains owned through each END/CANCEL and the complete
|
||||
sequence. Success restores default voice/language and spelling off. A backend
|
||||
failure or cancellation discards the remaining suffix and retires the isolated
|
||||
handle, cancelling only its client. A replacement requires explicit acquisition;
|
||||
there is no automatic retry or replay. Atomic IPC assembly does not make several
|
||||
backend fragments atomic: a later backend rejection can follow an already-spoken
|
||||
earlier fragment.
|
||||
|
||||
Only one typed assembly can render at a time. Scoped cancellation cancels its
|
||||
context as well as outstanding synthesis, so a cancel during a stalled metadata
|
||||
query cannot be lost before the first SPEAK. It never waits for metadata I/O in
|
||||
the caller. The renderer completes cleanup before returning ownership.
|
||||
|
||||
This implementation follows the [SSIP speech, spelling and language
|
||||
contract](https://github.com/brailcom/speechd/blob/master/doc/ssip.texi) and checks
|
||||
default-query/module-switch behavior against the upstream
|
||||
[parser](https://github.com/brailcom/speechd/blob/master/src/server/parse.c) and
|
||||
[settings implementation](https://github.com/brailcom/speechd/blob/master/src/server/set.c).
|
||||
It makes no NVDA priority or pause decision. A production owner still has to
|
||||
apply priority once, recheck original-lease/focus/mode policy, yield to local
|
||||
output, process cancel/pause while synthesis is outstanding, and retain mailbox
|
||||
budget until completion/discard. The source Remote command remains relay-only
|
||||
for incoming speech; no active or installed reader was replaced.
|
||||
|
||||
### Automated evidence
|
||||
|
||||
For the isolated synthesis prerequisite, focused application/backend race checks
|
||||
passed, including ten repetitions of the new cases. Existing speech regression
|
||||
checks, affected-package vet, a separate reader build and Tentacles `make check`
|
||||
also passed. No installed or active reader was replaced, and no audible or
|
||||
physical-input acceptance was performed.
|
||||
|
||||
The literal rendering increment passed the complete speech-backend race suite
|
||||
and ten repetitions of the isolated-client/rendering race cases. Private SSIP
|
||||
fixtures cover exact whitespace, mixed CR/LF, dot-only/command-looking lines,
|
||||
markup as literal text, repeated-character spelling, language/module/default
|
||||
restoration, named-voice fallback, long split text, end boundaries, assembly
|
||||
limits, invalid later fragments, unavailable defaults, rejected settings and
|
||||
remote/caller/configuration cancellation with local status/echo retained. The
|
||||
stalled-metadata cancellation case also rejects overlapping typed assemblies
|
||||
and verifies that neither a prefix nor suffix reaches synthesis. The
|
||||
existing speech regression harness, affected-package vet, separate reader build
|
||||
and Tentacles `make check` passed. These are protocol fixtures, without real
|
||||
Speech Dispatcher, audio, physical input or Braille acceptance.
|
||||
|
||||
Run from this checkout:
|
||||
|
||||
```sh
|
||||
@@ -172,8 +286,8 @@ host/wire/both-SDK race suites (host: about 127 seconds), focused application
|
||||
speech/Remote/Tentacle race checks, affected-package vet, a separate reader
|
||||
build, the existing speech regression harness and Tentacles `make check`.
|
||||
|
||||
Next: add incoming speech ownership, delivery-time policy, local priority and
|
||||
remote-only cancellation. Explicit status detachment is implemented; physical
|
||||
Next: bind the isolated renderer to incoming speech ownership, delivery-time
|
||||
policy, local priority and stream cancellation. Explicit status detachment is implemented; physical
|
||||
input capture/release and ordinary reading while connected still need acceptance.
|
||||
Review the remaining output producers before assigning exportable origins.
|
||||
Atomic IPC assembly is implemented; it does not establish backend
|
||||
|
||||
Reference in new issue
Block a user