Add callbacks, crescendo sound and better configs
This commit is contained in:
@@ -1,17 +1,28 @@
|
||||
package player
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CrescendoConfig holds volume ramp settings.
|
||||
type CrescendoConfig struct {
|
||||
Enabled bool
|
||||
StartPct int // 0-100
|
||||
EndPct int // 0-100
|
||||
DurationS int // seconds to ramp
|
||||
}
|
||||
|
||||
// Player handles alarm sound playback by shelling out to system audio tools.
|
||||
type Player struct {
|
||||
mu sync.Mutex
|
||||
stopCh chan struct{}
|
||||
doneCh chan struct{}
|
||||
mu sync.Mutex
|
||||
stopCh chan struct{}
|
||||
doneCh chan struct{}
|
||||
crescStopCh chan struct{}
|
||||
crescDoneCh chan struct{}
|
||||
}
|
||||
|
||||
func New() *Player {
|
||||
@@ -60,6 +71,11 @@ func findPlayer() (string, func(string) []string) {
|
||||
|
||||
// PlayLoop starts playing a sound file in a loop until Stop() is called.
|
||||
func (p *Player) PlayLoop(path string) {
|
||||
p.PlayLoopWithCrescendo(path, CrescendoConfig{})
|
||||
}
|
||||
|
||||
// PlayLoopWithCrescendo starts playing with optional volume ramp.
|
||||
func (p *Player) PlayLoopWithCrescendo(path string, cresc CrescendoConfig) {
|
||||
p.Stop() // kill any previous playback and wait for it to finish
|
||||
|
||||
p.mu.Lock()
|
||||
@@ -69,6 +85,19 @@ func (p *Player) PlayLoop(path string) {
|
||||
doneCh := p.doneCh
|
||||
p.mu.Unlock()
|
||||
|
||||
// Start crescendo if enabled and pactl is available
|
||||
if cresc.Enabled && cresc.DurationS > 0 {
|
||||
if _, err := exec.LookPath("pactl"); err == nil {
|
||||
p.mu.Lock()
|
||||
p.crescStopCh = make(chan struct{})
|
||||
p.crescDoneCh = make(chan struct{})
|
||||
crescStopCh := p.crescStopCh
|
||||
crescDoneCh := p.crescDoneCh
|
||||
p.mu.Unlock()
|
||||
go p.crescendoLoop(cresc, crescStopCh, crescDoneCh)
|
||||
}
|
||||
}
|
||||
|
||||
resolved := resolveSound(path)
|
||||
if resolved == "" {
|
||||
go p.bellLoop(stopCh, doneCh)
|
||||
@@ -146,15 +175,74 @@ func (p *Player) bellLoop(stopCh, doneCh chan struct{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// crescendoLoop gradually raises PulseAudio volume from StartPct to EndPct over DurationS seconds.
|
||||
func (p *Player) crescendoLoop(cfg CrescendoConfig, stopCh, doneCh chan struct{}) {
|
||||
defer close(doneCh)
|
||||
|
||||
startPct := cfg.StartPct
|
||||
endPct := cfg.EndPct
|
||||
if startPct < 0 {
|
||||
startPct = 0
|
||||
}
|
||||
if endPct > 150 { // allow some overdrive but not ridiculous
|
||||
endPct = 150
|
||||
}
|
||||
if startPct >= endPct {
|
||||
return
|
||||
}
|
||||
|
||||
// Set initial volume
|
||||
setVolume(startPct)
|
||||
|
||||
steps := cfg.DurationS
|
||||
if steps < 1 {
|
||||
steps = 1
|
||||
}
|
||||
stepSize := float64(endPct-startPct) / float64(steps)
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
currentPct := float64(startPct)
|
||||
for i := 0; i < steps; i++ {
|
||||
select {
|
||||
case <-stopCh:
|
||||
return
|
||||
case <-ticker.C:
|
||||
currentPct += stepSize
|
||||
setVolume(int(currentPct))
|
||||
}
|
||||
}
|
||||
// Ensure we hit the target
|
||||
setVolume(endPct)
|
||||
}
|
||||
|
||||
// setVolume sets the default PulseAudio sink volume to the given percentage.
|
||||
func setVolume(pct int) {
|
||||
_ = exec.Command("pactl", "set-sink-volume", "@DEFAULT_SINK@", fmt.Sprintf("%d%%", pct)).Run()
|
||||
}
|
||||
|
||||
// Stop stops any currently playing sound and waits for the playback goroutine to exit.
|
||||
func (p *Player) Stop() {
|
||||
p.mu.Lock()
|
||||
stopCh := p.stopCh
|
||||
doneCh := p.doneCh
|
||||
crescStopCh := p.crescStopCh
|
||||
crescDoneCh := p.crescDoneCh
|
||||
p.stopCh = nil
|
||||
p.doneCh = nil
|
||||
p.crescStopCh = nil
|
||||
p.crescDoneCh = nil
|
||||
p.mu.Unlock()
|
||||
|
||||
// Stop crescendo
|
||||
if crescStopCh != nil {
|
||||
close(crescStopCh)
|
||||
}
|
||||
if crescDoneCh != nil {
|
||||
<-crescDoneCh
|
||||
}
|
||||
|
||||
// Stop audio
|
||||
if stopCh != nil {
|
||||
close(stopCh)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user