Compare commits

..

3 Commits

Author SHA1 Message Date
045a18c3c8 CI: Publish binary on tag
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/tag Build is passing
2022-12-06 16:41:49 +01:00
5ff2f0730f api: Implement next track button 2022-12-06 16:41:38 +01:00
fc660f49a7 Use paplay 2022-12-06 16:41:28 +01:00
3 changed files with 106 additions and 110 deletions

View File

@ -47,6 +47,17 @@ steps:
event:
- tag
- name: gitea release
image: plugins/gitea-release
settings:
api_key:
from_secret: gitea_api_key
base_url: https://git.nemunai.re/
files: deploy/reveil-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
when:
event:
- tag
- name: docker
image: plugins/docker
settings:

View File

@ -33,6 +33,17 @@ func declareAlarmRoutes(cfg *config.Config, router *gin.RouterGroup) {
c.JSON(http.StatusOK, true)
})
router.POST("/alarm/next", func(c *gin.Context) {
if player.CommonPlayer == nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "No player currently playing"})
return
} else {
player.CommonPlayer.NextTrack()
}
c.JSON(http.StatusOK, true)
})
router.DELETE("/alarm", func(c *gin.Context) {
if player.CommonPlayer != nil {
err := player.CommonPlayer.Stop()

View File

@ -6,19 +6,12 @@ import (
"math"
"math/rand"
"os"
"os/exec"
"os/signal"
"path"
"strings"
"syscall"
"time"
"github.com/faiface/beep"
"github.com/faiface/beep/effects"
"github.com/faiface/beep/flac"
"github.com/faiface/beep/mp3"
"github.com/faiface/beep/speaker"
"github.com/faiface/beep/wav"
"git.nemunai.re/nemunaire/reveil/config"
"git.nemunai.re/nemunaire/reveil/model"
)
@ -26,11 +19,11 @@ import (
var CommonPlayer *Player
type Player struct {
Playlist []string
MaxRunTime time.Duration
Stopper chan bool
sampleRate beep.SampleRate
Playlist []string
MaxRunTime time.Duration
Stopper chan bool
currentCmd *exec.Cmd
currentCmdCh chan bool
claironTime time.Duration
claironFile string
@ -38,7 +31,7 @@ type Player struct {
ntick int64
hasClaironed bool
launched time.Time
volume *effects.Volume
volume uint16
dontUpdateVolume bool
reverseOrder bool
playedItem int
@ -66,11 +59,11 @@ func NewPlayer(cfg *config.Config) (*Player, error) {
}
p := Player{
Stopper: make(chan bool, 1),
MaxRunTime: settings.MaxRunTime * time.Minute,
sampleRate: beep.SampleRate(cfg.SampleRate),
claironTime: settings.GongInterval * time.Minute,
claironFile: reveil.CurrentGongPath(cfg),
Stopper: make(chan bool, 1),
currentCmdCh: make(chan bool, 1),
MaxRunTime: settings.MaxRunTime * time.Minute,
claironTime: settings.GongInterval * time.Minute,
claironFile: reveil.CurrentGongPath(cfg),
}
// Load our track list
@ -100,95 +93,27 @@ func NewPlayer(cfg *config.Config) (*Player, error) {
return &p, nil
}
func loadFile(filepath string) (name string, s beep.StreamSeekCloser, format beep.Format, err error) {
var fd *os.File
name = path.Base(filepath)
fd, err = os.Open(filepath)
if err != nil {
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
return
}
switch strings.ToLower(path.Ext(filepath)) {
case ".flac":
s, format, err = flac.Decode(fd)
case ".mp3":
s, format, err = mp3.Decode(fd)
default:
s, format, err = wav.Decode(fd)
}
log.Println("Running paplay ", filepath)
if err != nil {
fd.Close()
return
}
err = p.currentCmd.Wait()
p.currentCmdCh <- true
return
}
func (p *Player) WakeUp() {
log.Println("RUN WAKEUP FUNC")
log.Println("Playlist in use:", strings.Join(p.Playlist, " ; "))
// Create infinite stream
stream := beep.Iterate(func() beep.Streamer {
if !p.hasClaironed && time.Since(p.launched) >= p.claironTime {
log.Println("clairon time!")
p.claironTime += p.claironTime / 2
_, sample, format, err := loadFile(p.claironFile)
if err == nil {
p.volume.Volume = 0.1
p.dontUpdateVolume = true
if format.SampleRate != p.sampleRate {
return beep.Resample(3, format.SampleRate, p.sampleRate, sample)
} else {
return sample
}
} else {
log.Println("Error loading clairon:", err)
}
}
p.dontUpdateVolume = false
p.volume.Volume = -2 - math.Log(5/float64(p.ntick))/3
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
}
// Load our current item
_, sample, format, err := loadFile(p.Playlist[p.playedItem])
if err != nil {
log.Println("Error loading audio file %s: %s", p.Playlist[p.playedItem], err.Error())
return nil
}
// Resample if needed
log.Println("playing list item:", p.playedItem, "/", len(p.Playlist), ":", p.Playlist[p.playedItem])
if format.SampleRate != p.sampleRate {
return beep.Resample(3, format.SampleRate, p.sampleRate, sample)
} else {
return sample
}
})
// Prepare sound player
log.Println("Initializing sound player...")
speaker.Init(p.sampleRate, p.sampleRate.N(time.Second/10))
defer speaker.Close()
p.volume = &effects.Volume{stream, 10, -2, false}
speaker.Play(p.volume)
p.volume = 3500
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
@ -200,20 +125,53 @@ func (p *Player) WakeUp() {
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, syscall.SIGHUP)
p.currentCmdCh <- true
loop:
for {
select {
case <-p.Stopper:
log.Println("Stopper activated")
break loop
case <-maxRun:
log.Println("Max run time exhausted")
break loop
case <-p.currentCmdCh:
if !p.hasClaironed && time.Since(p.launched) >= p.claironTime {
log.Println("clairon time!")
p.claironTime += p.claironTime / 2
p.SetVolume(65535)
p.dontUpdateVolume = true
go p.playFile(p.claironFile)
} else {
p.dontUpdateVolume = false
p.volume = 3500 + uint16(math.Log(1+float64(p.ntick)/8)*9500)
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.Volume = -2 - math.Log(5/float64(p.ntick))/3
p.volume = 3500 + uint16(math.Log(1+float64(p.ntick)/8)*9500)
p.SetVolume(p.volume)
}
case <-p.Stopper:
log.Println("Stopper activated")
break loop
case <-maxRun:
log.Println("Max run time exhausted")
break loop
case <-interrupt:
break loop
}
@ -223,31 +181,47 @@ loop:
// Calm down music
loopcalm:
for i := 0; i < 2000; i += 1 {
p.volume.Volume -= 0.001
for i := 0; i < 128 && p.volume >= 15000; i += 1 {
timer := time.NewTimer(40 * time.Millisecond)
p.volume -= 256
p.SetVolume(p.volume)
timer := time.NewTimer(4 * time.Millisecond)
select {
case <-p.Stopper:
log.Println("Hard stop received...")
timer.Stop()
p.volume.Volume = 0
p.volume = 0
break loopcalm
case <-timer.C:
break
}
}
if p.currentCmd != nil && p.currentCmd.Process != nil {
p.currentCmd.Process.Kill()
}
p.SetVolume(65535)
if p == CommonPlayer {
log.Println("Destoying common player")
CommonPlayer = nil
// TODO: find a better way to deallocate the card
os.Exit(42)
}
}
func (p *Player) NextTrack() {
if p.currentCmd != nil && p.currentCmd.Process != nil {
p.currentCmd.Process.Kill()
}
}
func (p *Player) SetVolume(volume uint16) error {
cmd := exec.Command("amixer", "-D", "pulse", "set", "Master", fmt.Sprintf("%d", volume))
return cmd.Run()
}
func (p *Player) Stop() error {
log.Println("Trying to stop the player")
p.Stopper <- true