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
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user