Attempt to make audio and speech discovery more robust.

This commit is contained in:
Storm Dragon committed 2026-11-03 13:21:50 -05:00
1 parent a27f274099
commit dc945ff392
21 files changed
+1314 -60

No files matched your search

+56 -13
View File
@@ -43,8 +43,20 @@ type Rejection struct{ Code string }
func (r *Rejection) Error() string { return "Speech Dispatcher rejected a command: " + r.Code }
type wire struct {
conn net.Conn
reader *bufio.Reader
conn net.Conn
reader *bufio.Reader
stopContext func() bool
}
func (w *wire) detach_context() {
if w.stopContext != nil {
w.stopContext()
}
}
func (w *wire) close() {
w.detach_context()
_ = w.conn.Close()
}
func (w *wire) reply() ([]string, error) {
@@ -102,11 +114,14 @@ func connect_options(options Options) (*wire, error) {
if err != nil {
return nil, err
}
return connect_endpoint(endpoint, options.autospawn, automatic_server_address(options.Socket))
return connect_endpoint(options.context, endpoint, options.autospawn, automatic_server_address(options.Socket))
}
func connect_endpoint(endpoint serverAddress, autospawn, systemFallback bool) (*wire, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*commandTimeout)
func connect_endpoint(parent context.Context, endpoint serverAddress, autospawn, systemFallback bool) (*wire, error) {
if parent == nil {
parent = context.Background()
}
ctx, cancel := context.WithTimeout(parent, 3*commandTimeout)
defer cancel()
dialer := &net.Dialer{Timeout: commandTimeout}
dial := serverDialer(dialer.DialContext)
@@ -123,7 +138,7 @@ func connect_endpoint(endpoint serverAddress, autospawn, systemFallback bool) (*
if err != nil {
return nil, err
}
return &wire{conn: conn, reader: bufio.NewReaderSize(conn, 4096)}, nil
return &wire{conn: conn, reader: bufio.NewReaderSize(conn, 4096), stopContext: context.AfterFunc(parent, func() { _ = conn.Close() })}, nil
}
type Client struct {
@@ -132,11 +147,13 @@ type Client struct {
sendMu sync.Mutex
controlMu sync.Mutex
closeOnce sync.Once
options Options
}
type Options struct {
// Only Prepare enables startup during its prerequisite selection queries.
autospawn bool
context context.Context
Socket string
Rate int
Pitch *int
@@ -174,15 +191,15 @@ func Open(options Options) (*Client, error) {
if err != nil {
return nil, err
}
data, err := connect_endpoint(endpoint, true, automatic_server_address(options.Socket))
data, err := connect_endpoint(options.context, endpoint, true, automatic_server_address(options.Socket))
if err != nil {
return nil, fmt.Errorf("connect Speech Dispatcher at %s: %w", endpoint.address, err)
}
client := &Client{data: data}
client := &Client{data: data, options: options}
fail := func(err error) (*Client, error) {
data.conn.Close()
data.close()
if client.control != nil {
client.control.conn.Close()
client.control.close()
}
return nil, err
}
@@ -229,13 +246,31 @@ func Open(options Options) (*Client, error) {
if endpoint.network == "unix" {
endpoint.address = data.conn.RemoteAddr().String()
}
client.control, err = connect_endpoint(endpoint, false, false)
client.control, err = connect_endpoint(options.context, endpoint, false, false)
if err != nil {
return fail(err)
}
if _, err := client.control.command(`SET SELF CLIENT_NAME "fenrir:fenrir:control"`); err != nil {
return fail(err)
}
data.detach_context()
client.control.detach_context()
if options.context != nil && options.context.Err() != nil {
client.abort()
return nil, options.context.Err()
}
client.options.context = nil
return client, nil
}
// Reopen reconnects only the selected server and synthesis options.
func (c *Client) Reopen(ctx context.Context) (Transport, error) {
options := c.options
options.context = ctx
client, err := Open(options)
if err != nil {
return nil, err
}
return client, nil
}
@@ -315,8 +350,16 @@ func (c *Client) Cancel() error {
func (c *Client) Close() {
c.closeOnce.Do(func() {
// Closing the data socket releases a blocked sender before cancellation.
c.data.conn.Close()
c.data.close()
c.Cancel()
c.control.conn.Close()
c.control.close()
})
}
// abort releases blocked initialization commands without another SSIP request.
func (c *Client) abort() {
_ = c.data.conn.Close()
if c.control != nil {
_ = c.control.conn.Close()
}
}