reveil/player/player.go

283 lines
6.0 KiB
Go
Raw Permalink Normal View History

2022-10-14 18:08:03 +00:00
package player
import (
"fmt"
"log"
"math"
"math/rand"
"os"
2022-12-06 15:41:28 +00:00
"os/exec"
2022-10-14 18:08:03 +00:00
"os/signal"
"strings"
"syscall"
"time"
"git.nemunai.re/nemunaire/reveil/config"
"git.nemunai.re/nemunaire/reveil/model"
)
var CommonPlayer *Player
type Player struct {
2022-12-06 15:41:28 +00:00
Playlist []string
MaxRunTime time.Duration
Stopper chan bool
currentCmd *exec.Cmd
currentCmdCh chan bool
2022-10-14 18:08:03 +00:00
2022-12-08 16:49:15 +00:00
weatherTime time.Duration
weatherAction *reveil.Action
2022-10-14 18:08:03 +00:00
claironTime time.Duration
claironFile string
2022-12-08 16:49:42 +00:00
endRoutines []*reveil.Routine
2022-10-14 18:08:03 +00:00
ntick int64
2022-12-08 16:49:15 +00:00
hasSpokeWeather bool
2022-10-14 18:08:03 +00:00
launched time.Time
2022-12-06 15:41:28 +00:00
volume uint16
2022-10-14 18:08:03 +00:00
dontUpdateVolume bool
reverseOrder bool
playedItem int
}
2022-12-08 16:49:42 +00:00
func WakeUp(cfg *config.Config, routine []reveil.Identifier) (err error) {
2022-10-14 18:08:03 +00:00
if CommonPlayer != nil {
return fmt.Errorf("Unable to start the player: a player is already running")
}
2022-12-06 20:01:54 +00:00
seed := time.Now().Unix()
seed -= seed % 172800
rand.Seed(seed)
2022-12-08 16:49:42 +00:00
CommonPlayer, err = NewPlayer(cfg, routine)
2022-10-14 18:08:03 +00:00
if err != nil {
return err
}
2022-12-08 16:49:42 +00:00
go CommonPlayer.WakeUp(cfg)
2022-10-14 18:08:03 +00:00
return nil
}
2022-12-08 16:49:42 +00:00
func NewPlayer(cfg *config.Config, routines []reveil.Identifier) (*Player, error) {
2022-10-14 18:08:03 +00:00
// Load our settings
settings, err := reveil.ReadSettings(cfg.SettingsFile)
if err != nil {
return nil, fmt.Errorf("Unable to read settings: %w", err)
}
2022-12-08 16:49:15 +00:00
// Load weather action
wact, err := reveil.LoadAction(cfg, settings.WeatherAction)
if err != nil {
log.Println("Unable to load weather action:", err.Error())
}
2022-10-14 18:08:03 +00:00
p := Player{
2022-12-08 16:49:15 +00:00
Stopper: make(chan bool, 1),
currentCmdCh: make(chan bool, 1),
MaxRunTime: settings.MaxRunTime * time.Minute,
weatherTime: settings.WeatherDelay * time.Minute,
weatherAction: wact,
claironTime: settings.GongInterval * time.Minute,
claironFile: reveil.CurrentGongPath(cfg),
reverseOrder: int(time.Now().Unix()/86400)%2 == 0,
2022-10-14 18:08:03 +00:00
}
2022-12-08 16:49:42 +00:00
// Load routines
for _, routine := range routines {
r, err := reveil.LoadRoutineFromId(routine, cfg)
if err != nil {
log.Printf("Unable to load routine %x: %s", routine, err.Error())
continue
}
p.endRoutines = append(p.endRoutines, r)
}
2022-10-14 18:08:03 +00:00
// Load our track list
tracks, err := reveil.LoadTracks(cfg)
if err != nil {
return nil, fmt.Errorf("Unable to load tracks: %w", err)
}
// Creating playlist
log.Println("Loading playlist...")
for _, track := range tracks {
if !track.Enabled {
continue
}
p.Playlist = append(p.Playlist, track.Path)
}
log.Println("Shuffling playlist...")
// Shuffle the playlist
2022-12-11 21:25:11 +00:00
rand.Shuffle(len(p.Playlist), func(i, j int) {
p.Playlist[i], p.Playlist[j] = p.Playlist[j], p.Playlist[i]
2022-10-14 18:08:03 +00:00
})
return &p, nil
}
func (p *Player) launchAction(cfg *config.Config, a *reveil.Action) (err error) {
settings, err := reveil.ReadSettings(cfg.SettingsFile)
if err != nil {
return fmt.Errorf("unable to read settings: %w", err)
}
p.currentCmd, err = a.Launch(settings)
2022-12-08 16:49:15 +00:00
log.Println("Running action ", a.Name)
err = p.currentCmd.Wait()
p.currentCmdCh <- true
return
}
2022-12-06 15:41:28 +00:00
func (p *Player) playFile(filepath string) (err error) {
p.currentCmd = exec.Command("paplay", filepath)
if err = p.currentCmd.Start(); err != nil {
log.Println("Running paplay err: ", err.Error())
p.currentCmdCh <- true
2022-10-14 18:08:03 +00:00
return
}
2022-12-06 15:41:28 +00:00
log.Println("Running paplay ", filepath)
2022-10-14 18:08:03 +00:00
2022-12-06 15:41:28 +00:00
err = p.currentCmd.Wait()
p.currentCmdCh <- true
2022-10-14 18:08:03 +00:00
return
}
2022-12-08 16:49:42 +00:00
func (p *Player) WakeUp(cfg *config.Config) {
2022-10-14 18:08:03 +00:00
log.Println("Playlist in use:", strings.Join(p.Playlist, " ; "))
// Prepare sound player
2022-12-06 15:41:28 +00:00
p.volume = 3500
2022-10-14 18:08:03 +00:00
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
p.launched = time.Now()
// Prepare graceful shutdown
maxRun := time.After(p.MaxRunTime)
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, syscall.SIGHUP)
2022-12-06 15:41:28 +00:00
p.currentCmdCh <- true
2022-10-14 18:08:03 +00:00
loop:
for {
select {
2022-12-06 15:41:28 +00:00
case <-p.currentCmdCh:
2022-12-09 09:35:43 +00:00
if time.Since(p.launched) >= p.claironTime {
2022-12-06 15:41:28 +00:00
log.Println("clairon time!")
p.claironTime += p.claironTime / 2
p.SetVolume(65535)
p.dontUpdateVolume = true
go p.playFile(p.claironFile)
2022-12-08 16:49:15 +00:00
} else if p.weatherAction != nil && !p.hasSpokeWeather && time.Since(p.launched) >= p.weatherTime {
log.Println("weather time!")
p.dontUpdateVolume = true
2022-12-09 09:35:43 +00:00
p.hasSpokeWeather = true
go p.launchAction(cfg, p.weatherAction)
2022-12-06 15:41:28 +00:00
} else {
p.dontUpdateVolume = false
2023-11-04 08:50:59 +00:00
p.volume = uint16(math.Log(1+float64(p.ntick)/8) * 9500)
2022-12-06 15:41:28 +00:00
p.SetVolume(p.volume)
if p.reverseOrder {
p.playedItem -= 1
} else {
p.playedItem += 1
}
if p.playedItem >= len(p.Playlist) {
p.playedItem = 0
} else if p.playedItem < 0 {
p.playedItem = len(p.Playlist) - 1
}
log.Println("Next track: ", p.Playlist[p.playedItem])
go p.playFile(p.Playlist[p.playedItem])
}
case <-ticker.C:
p.ntick += 1
if !p.dontUpdateVolume {
p.volume = 3500 + uint16(math.Log(1+float64(p.ntick)/8)*9500)
p.SetVolume(p.volume)
}
2022-10-14 18:08:03 +00:00
case <-p.Stopper:
log.Println("Stopper activated")
break loop
2022-12-06 15:41:28 +00:00
2022-10-14 18:08:03 +00:00
case <-maxRun:
log.Println("Max run time exhausted")
break loop
2022-12-06 15:41:28 +00:00
2022-10-14 18:08:03 +00:00
case <-interrupt:
break loop
}
}
log.Println("Stopping the player...")
// Calm down music
loopcalm:
for i := 0; i < 128 && p.volume >= 768; i += 1 {
2022-12-06 15:41:28 +00:00
timer := time.NewTimer(40 * time.Millisecond)
2024-03-02 14:35:11 +00:00
p.volume -= 768
2022-12-06 15:41:28 +00:00
p.SetVolume(p.volume)
2022-10-14 18:08:03 +00:00
select {
case <-p.Stopper:
log.Println("Hard stop received...")
timer.Stop()
2022-12-06 15:41:28 +00:00
p.volume = 0
2022-10-14 18:08:03 +00:00
break loopcalm
case <-timer.C:
break
}
}
2022-12-06 15:41:28 +00:00
if p.currentCmd != nil && p.currentCmd.Process != nil {
p.currentCmd.Process.Kill()
}
p.SetVolume(65535)
2022-10-14 18:08:03 +00:00
if p == CommonPlayer {
log.Println("Destoying common player")
CommonPlayer = nil
2022-12-06 15:41:28 +00:00
}
2022-12-08 16:49:42 +00:00
// TODO: Start Routine if any
for _, r := range p.endRoutines {
go r.Launch(cfg)
}
2022-12-06 15:41:28 +00:00
}
2022-10-14 18:08:03 +00:00
2022-12-06 15:41:28 +00:00
func (p *Player) NextTrack() {
if p.currentCmd != nil && p.currentCmd.Process != nil {
p.currentCmd.Process.Kill()
2022-10-14 18:08:03 +00:00
}
}
2022-12-06 15:41:28 +00:00
func (p *Player) SetVolume(volume uint16) error {
cmd := exec.Command("amixer", "-D", "pulse", "set", "Master", fmt.Sprintf("%d", volume))
return cmd.Run()
}
2022-10-14 18:08:03 +00:00
func (p *Player) Stop() error {
log.Println("Trying to stop the player")
p.Stopper <- true
return nil
}