Files

69 lines
2.3 KiB
Go

// Fenrir console screen reader.
// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
package sound
import (
"context"
"fmt"
"os"
"time"
"github.com/jfreymuth/pulse"
"github.com/jfreymuth/pulse/proto"
)
func open_pulse(s *Service, _ string) error {
options := []pulse.ClientOption{pulse.ClientApplicationName("Fenrir sounds"), pulse.ClientTimeout(500 * time.Millisecond)}
// Try the invoking user's session, then the bundled PipeWire helper's
// socket and the system-wide service, unless PULSE_SERVER selects a server.
options = append(options, pulse.ClientServerString(Server(os.Getenv("PULSE_SERVER"), os.Getenv("SUDO_UID"))))
client, err := pulse.NewClient(options...)
if err != nil {
return fmt.Errorf("connect sound server: %w", err)
}
stream, err := client.NewPlayback(pulse.Float32Reader(s.read_samples), pulse.PlaybackStereo, pulse.PlaybackSampleRate(sampleRate), pulse.PlaybackLatency(0.02), pulse.PlaybackMediaName("Fenrir sound cues"))
if err != nil {
client.Close()
return err
}
s.closeBackend = func() { stream.Close(); client.Close() }
// Drain even after the final reader callback reports EndOfData. The
// library's Drain helper skips idle streams, whose buffered tail may
// still be audible on the server.
s.drainBackend = func(_ context.Context) error {
return client.RawRequest(&proto.DrainPlaybackStream{StreamIndex: stream.StreamIndex()}, nil)
}
stream.Start()
s.workers.Go(func() {
tick := time.NewTicker(time.Second)
defer tick.Stop()
for {
select {
case <-s.done:
return
case <-tick.C:
if stream.Closed() {
s.errors <- fmt.Errorf("sound server connection closed")
return
}
}
}
})
return nil
}