spdif: Handle interface sample rate changes
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing

This commit is contained in:
nemunaire 2024-11-21 17:51:45 +01:00
parent bb2f432a4b
commit 84d594d695

View File

@ -3,16 +3,21 @@ package spdif
import ( import (
"fmt" "fmt"
"io" "io"
"log"
"os" "os"
"os/exec" "os/exec"
"path" "path"
"strconv"
"time"
"git.nemunai.re/nemunaire/hathoris/alsacontrol"
"git.nemunai.re/nemunaire/hathoris/sources" "git.nemunai.re/nemunaire/hathoris/sources"
) )
type SPDIFSource struct { type SPDIFSource struct {
processRec *exec.Cmd processRec *exec.Cmd
processPlay *exec.Cmd processPlay *exec.Cmd
endChan chan bool
DeviceIn string DeviceIn string
DeviceOut string DeviceOut string
Bitrate int64 Bitrate int64
@ -28,14 +33,18 @@ func init() {
idfile := path.Join(thisdir, "id") idfile := path.Join(thisdir, "id")
if fd, err := os.Open(idfile); err == nil { if fd, err := os.Open(idfile); err == nil {
if cnt, err := io.ReadAll(fd); err == nil && string(cnt) == "imxspdif\n" { if cnt, err := io.ReadAll(fd); err == nil && string(cnt) == "imxspdif\n" {
sr, err := getCardSampleRate("imxspdif")
if err == nil {
sources.SoundSources["imxspdif"] = &SPDIFSource{ sources.SoundSources["imxspdif"] = &SPDIFSource{
endChan: make(chan bool, 1),
DeviceIn: "imxspdif", DeviceIn: "imxspdif",
DeviceOut: "is31ap2121", DeviceOut: "is31ap2121",
Bitrate: 48000, Bitrate: sr,
Channels: 2, Channels: 2,
Format: "S24_LE", Format: "S24_LE",
} }
} }
}
fd.Close() fd.Close()
} }
} }
@ -93,6 +102,8 @@ func (s *SPDIFSource) Enable() error {
return err return err
} }
go s.watchBitrate()
go func() { go func() {
err := s.processRec.Wait() err := s.processRec.Wait()
if err != nil { if err != nil {
@ -113,6 +124,57 @@ func (s *SPDIFSource) Disable() error {
if s.processPlay != nil && s.processPlay.Process != nil { if s.processPlay != nil && s.processPlay.Process != nil {
s.processPlay.Process.Kill() s.processPlay.Process.Kill()
} }
s.endChan <- true
return nil return nil
} }
func getCardSampleRate(cardId string) (sr int64, err error) {
cc, err := alsa.ParseAmixerContent("hw:" + cardId)
if err != nil {
return 0, fmt.Errorf("unable to parse amixer content: %w", err)
}
for _, c := range cc {
if len(c.Values) == 1 {
val, err := strconv.Atoi(c.Values[0])
if c.Name == "RX Sample Rate" && err == nil && val > 0 {
return int64(val), nil
}
}
}
return 0, fmt.Errorf("unable to find 'RX Sample Rate' control value")
}
func (s *SPDIFSource) watchBitrate() {
ticker := time.NewTicker(time.Second)
loop:
for {
select {
case <-ticker.C:
sr, err := getCardSampleRate(s.DeviceIn)
if err == nil && s.Bitrate/10 != sr/10 {
log.Printf("[SPDIF] Sample rate changes from %d to %d Hz", s.Bitrate, sr)
s.Bitrate = sr
s.Disable()
// Wait process exited
for {
if s.processPlay == nil && s.processRec == nil {
break
}
time.Sleep(100 * time.Millisecond)
}
s.Enable()
}
case <-s.endChan:
break loop
}
}
ticker.Stop()
}