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:
ak47 committed 2026-11-04 19:56:38 -05:00
1 parent 622d2f96a1
commit ac431dd514
9 files changed
+1370 -11

No files matched your search

+25 -8
View File
@@ -31,6 +31,7 @@ type configuredSpeaker struct {
mu sync.RWMutex
update sync.Mutex
active configuredVoice
remote *remoteSpeechVoice
socket string
settings config.Settings
closed bool
@@ -70,10 +71,15 @@ func (speaker *configuredSpeaker) UpdateConfiguration(ctx context.Context, setti
}
hardware.settings = settings
hardware.mu.Unlock()
speaker.retire_remote_voice(settings)
return nil
}
if software, ok := current.(*echoSpeaker); ok && settings.SpeechServerFactory != "hardwarefactory" {
return software.UpdateConfiguration(ctx, settings)
if err := software.UpdateConfiguration(ctx, settings); err != nil {
return err
}
speaker.retire_remote_voice(settings)
return nil
}
var candidate configuredVoice
if settings.SpeechServerFactory == "hardwarefactory" {
@@ -99,8 +105,8 @@ func (speaker *configuredSpeaker) UpdateConfiguration(ctx context.Context, setti
}
}
speaker.active = candidate
speaker.settings = settings
speaker.mu.Unlock()
speaker.retire_remote_voice(settings)
// The new backend is committed. A retired connection's cleanup error must
// not report this successful swap as a rejected preferences transaction.
if current != nil {
@@ -182,23 +188,34 @@ func (speaker *configuredSpeaker) speak_uninterruptible(ctx context.Context, tex
func (speaker *configuredSpeaker) Cancel(ctx context.Context) error {
speaker.mu.RLock()
voice := speaker.active
remote := speaker.remote
speaker.mu.RUnlock()
if voice == nil {
return nil
var result error
if voice != nil {
result = voice.Cancel(ctx)
}
return voice.Cancel(ctx)
if remote != nil {
result = errors.Join(result, remote.Cancel(ctx))
}
return result
}
func (speaker *configuredSpeaker) Close(ctx context.Context) error {
speaker.update.Lock()
defer speaker.update.Unlock()
speaker.mu.Lock()
voice := speaker.active
remote := speaker.remote
speaker.active, speaker.closed = nil, true
speaker.remote = nil
speaker.mu.Unlock()
if voice == nil {
return nil
var result error
if remote != nil {
result = remote.Close(ctx)
}
return voice.Close(ctx)
if voice == nil {
return result
}
return errors.Join(result, voice.Close(ctx))
}
type hardwareSpeaker struct {