Compare commits
No commits in common. "acb051d7a36ca0e27bb85d9c791dfd84f173b787" and "30a50b775fe10624f41fb1462236b0332ab32108" have entirely different histories.
acb051d7a3
...
30a50b775f
11
.drone.yml
11
.drone.yml
@ -47,17 +47,6 @@ 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:
|
||||
|
11
api/alarm.go
11
api/alarm.go
@ -33,17 +33,6 @@ 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()
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
|
||||
func declareAlarmsRoutes(cfg *config.Config, db *reveil.LevelDBStorage, resetTimer func(), router *gin.RouterGroup) {
|
||||
router.GET("/alarms/next", func(c *gin.Context) {
|
||||
alarm, err := reveil.GetNextAlarm(cfg, db)
|
||||
alarm, err := reveil.GetNextAlarm(db)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
@ -200,8 +200,8 @@ func declareAlarmsRoutes(cfg *config.Config, db *reveil.LevelDBStorage, resetTim
|
||||
|
||||
repeatedAlarmsRoutes.GET("", func(c *gin.Context) {
|
||||
alarm := c.MustGet("alarm").(*reveil.AlarmRepeated)
|
||||
alarm.FillExcepts(cfg, db)
|
||||
alarm.NextTime = alarm.GetNextOccurence(cfg, db)
|
||||
alarm.FillExcepts(db)
|
||||
alarm.NextTime = alarm.GetNextOccurence(db)
|
||||
|
||||
c.JSON(http.StatusOK, alarm)
|
||||
})
|
||||
|
3
app.go
3
app.go
@ -64,7 +64,6 @@ func (app *App) Start() {
|
||||
Handler: app.router,
|
||||
}
|
||||
|
||||
log.Println("Current timezone:", app.cfg.Timezone.String())
|
||||
app.ResetTimer()
|
||||
|
||||
log.Printf("Ready, listening on %s\n", app.cfg.Bind)
|
||||
@ -79,7 +78,7 @@ func (app *App) ResetTimer() {
|
||||
app.nextAlarm = nil
|
||||
}
|
||||
|
||||
if na, err := reveil.GetNextAlarm(app.cfg, app.db); err == nil && na != nil {
|
||||
if na, err := reveil.GetNextAlarm(app.db); err == nil && na != nil {
|
||||
app.nextAlarm = time.AfterFunc(time.Until(*na), func() {
|
||||
app.nextAlarm = nil
|
||||
reveil.RemoveOldAlarmsSingle(app.db)
|
||||
|
@ -17,7 +17,6 @@ func (c *Config) declareFlags() {
|
||||
flag.StringVar(&c.ActionsDir, "actions-dir", c.ActionsDir, "Path to the directory containing the actions")
|
||||
flag.StringVar(&c.RoutinesDir, "routines-dir", c.RoutinesDir, "Path to the directory containing the routines")
|
||||
flag.IntVar(&c.SampleRate, "samplerate", c.SampleRate, "Samplerate for unifying output stream")
|
||||
flag.Var(&c.Timezone, "timezone", "Timezone to use when dealing with times")
|
||||
|
||||
// Others flags are declared in some other files when they need specials configurations
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ type Config struct {
|
||||
ActionsDir string
|
||||
RoutinesDir string
|
||||
|
||||
Timezone Timezone
|
||||
SampleRate int
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@ package config
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type JWTSecretKey []byte
|
||||
@ -43,33 +42,3 @@ func (i *URL) Set(value string) error {
|
||||
i.URL = u
|
||||
return nil
|
||||
}
|
||||
|
||||
type Timezone struct {
|
||||
tz *time.Location
|
||||
}
|
||||
|
||||
func (tz *Timezone) GetLocation() *time.Location {
|
||||
if tz.tz != nil {
|
||||
return tz.tz
|
||||
} else {
|
||||
return time.Local
|
||||
}
|
||||
}
|
||||
|
||||
func (tz *Timezone) String() string {
|
||||
if tz.tz != nil {
|
||||
return tz.tz.String()
|
||||
} else {
|
||||
return time.Local.String()
|
||||
}
|
||||
}
|
||||
|
||||
func (tz *Timezone) Set(value string) error {
|
||||
newtz, err := time.LoadLocation(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tz.tz = newtz
|
||||
return nil
|
||||
}
|
||||
|
@ -7,10 +7,10 @@ import (
|
||||
)
|
||||
|
||||
// FromEnv analyzes all the environment variables to find each one
|
||||
// starting by REVEIL_
|
||||
// starting by GUSTUS_
|
||||
func (c *Config) FromEnv() error {
|
||||
for _, line := range os.Environ() {
|
||||
if strings.HasPrefix(line, "REVEIL_") {
|
||||
if strings.HasPrefix(line, "GUSTUS_") {
|
||||
err := c.parseLine(line)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in environment (%q): %w", line, err)
|
||||
|
10
go.mod
10
go.mod
@ -3,12 +3,13 @@ module git.nemunai.re/nemunaire/reveil
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/faiface/beep v1.1.0
|
||||
github.com/faiface/beep v0.0.0-00010101000000-000000000000
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/ebitengine/purego v0.0.0-20220907032450-cf3e27c364c7 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
@ -16,7 +17,7 @@ require (
|
||||
github.com/goccy/go-json v0.9.7 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/hajimehoshi/go-mp3 v0.3.0 // indirect
|
||||
github.com/hajimehoshi/oto v0.7.1 // indirect
|
||||
github.com/hajimehoshi/oto/v2 v2.4.0-alpha.4 // indirect
|
||||
github.com/icza/bitio v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
@ -29,12 +30,11 @@ require (
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 // indirect
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067 // indirect
|
||||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6 // indirect
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
|
||||
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/faiface/beep => github.com/MarkKremer/beep v1.0.3-0.20221013180303-756ceb286755
|
||||
|
13
go.sum
13
go.sum
@ -1,11 +1,13 @@
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/MarkKremer/beep v1.0.3-0.20221013180303-756ceb286755 h1:rkuKNEd+Izze/hA44R1kzPs9BXa544xXeufys8x0fkc=
|
||||
github.com/MarkKremer/beep v1.0.3-0.20221013180303-756ceb286755/go.mod h1:PWWzyIlbyHQjQ/gJzGiMyDAvjo/t5L8TC8qkyX8UfWs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/faiface/beep v1.1.0 h1:A2gWP6xf5Rh7RG/p9/VAW2jRSDEGQm5sbOb38sf5d4c=
|
||||
github.com/faiface/beep v1.1.0/go.mod h1:6I8p6kK2q4opL/eWb+kAkk38ehnTunWeToJB+s51sT4=
|
||||
github.com/ebitengine/purego v0.0.0-20220907032450-cf3e27c364c7 h1:tmSauY5l3s/Cp5n+cEiG1epUR2AejmdHeMJMycMFxb0=
|
||||
github.com/ebitengine/purego v0.0.0-20220907032450-cf3e27c364c7/go.mod h1:Eh8I3yvknDYZeCuXH9kRNaPuHEwvXDCk378o9xszmHg=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
|
||||
@ -37,8 +39,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
||||
github.com/hajimehoshi/go-mp3 v0.3.0 h1:fTM5DXjp/DL2G74HHAs/aBGiS9Tg7wnp+jkU38bHy4g=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.0/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
||||
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
|
||||
github.com/hajimehoshi/oto v0.7.1 h1:I7maFPz5MBCwiutOrz++DLdbr4rTzBsbBuV2VpgU9kk=
|
||||
github.com/hajimehoshi/oto v0.7.1/go.mod h1:wovJ8WWMfFKvP587mhHgot/MBr4DnNy9m6EepeVGnos=
|
||||
github.com/hajimehoshi/oto/v2 v2.4.0-alpha.4 h1:m29xzbn3Pv5MgvgjMPs7m28uhUgVt3B3AIGjQLgkqUI=
|
||||
github.com/hajimehoshi/oto/v2 v2.4.0-alpha.4/go.mod h1:OdGUICBjy7upAjvqqacbB63XIuYR3fqXZ7kYtlVYJgQ=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/icza/bitio v1.0.0 h1:squ/m1SHyFeCA6+6Gyol1AxV9nmPPlJFT8c2vKdj3U8=
|
||||
@ -101,12 +103,9 @@ github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 h1:idBdZTd9UioThJp8KpM/rTSinK/ChZFBE43/WtIy8zg=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067 h1:KYGJGHOQy8oSi1fDlSpcZF0+juKwk/hEMv5SiwHogR0=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6 h1:vyLBGJPIl9ZYbcQFM2USFmJBK6KI+t+z6jL0lbwjrnc=
|
||||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"git.nemunai.re/nemunaire/reveil/config"
|
||||
)
|
||||
|
||||
type Date time.Time
|
||||
@ -40,7 +38,7 @@ func (h *Hour) UnmarshalJSON(src []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetNextAlarm(cfg *config.Config, db *LevelDBStorage) (*time.Time, error) {
|
||||
func GetNextAlarm(db *LevelDBStorage) (*time.Time, error) {
|
||||
alarmsRepeated, err := GetAlarmsRepeated(db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -48,7 +46,7 @@ func GetNextAlarm(cfg *config.Config, db *LevelDBStorage) (*time.Time, error) {
|
||||
|
||||
var closestAlarm *time.Time
|
||||
for _, alarm := range alarmsRepeated {
|
||||
next := alarm.GetNextOccurence(cfg, db)
|
||||
next := alarm.GetNextOccurence(db)
|
||||
if next != nil && (closestAlarm == nil || closestAlarm.After(*next)) {
|
||||
closestAlarm = next
|
||||
}
|
||||
@ -94,7 +92,7 @@ type AlarmRepeated struct {
|
||||
NextTime *time.Time `json:"next_time,omitempty"`
|
||||
}
|
||||
|
||||
func (a *AlarmRepeated) FillExcepts(cfg *config.Config, db *LevelDBStorage) error {
|
||||
func (a *AlarmRepeated) FillExcepts(db *LevelDBStorage) error {
|
||||
if a.IgnoreExceptions {
|
||||
return nil
|
||||
}
|
||||
@ -114,7 +112,7 @@ func (a *AlarmRepeated) FillExcepts(cfg *config.Config, db *LevelDBStorage) erro
|
||||
end := time.Time(*exception.End).AddDate(0, 0, 1)
|
||||
for t := time.Time(*exception.Start); end.After(t); t = t.AddDate(0, 0, 1) {
|
||||
if t.Weekday() == a.Weekday {
|
||||
a.Excepts = append(a.Excepts, time.Date(t.Year(), t.Month(), t.Day(), time.Time(*a.StartTime).Hour(), time.Time(*a.StartTime).Minute(), time.Time(*a.StartTime).Second(), 0, cfg.Timezone.GetLocation()))
|
||||
a.Excepts = append(a.Excepts, time.Date(t.Year(), t.Month(), t.Day(), time.Time(*a.StartTime).Hour(), time.Time(*a.StartTime).Minute(), time.Time(*a.StartTime).Second(), 0, now.Location()))
|
||||
t.AddDate(0, 0, 6)
|
||||
}
|
||||
}
|
||||
@ -125,14 +123,14 @@ func (a *AlarmRepeated) FillExcepts(cfg *config.Config, db *LevelDBStorage) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AlarmRepeated) GetNextOccurence(cfg *config.Config, db *LevelDBStorage) *time.Time {
|
||||
func (a *AlarmRepeated) GetNextOccurence(db *LevelDBStorage) *time.Time {
|
||||
if len(a.Excepts) == 0 {
|
||||
a.FillExcepts(cfg, db)
|
||||
a.FillExcepts(db)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
today := time.Date(now.Year(), now.Month(), now.Day(), time.Time(*a.StartTime).Hour(), time.Time(*a.StartTime).Minute(), time.Time(*a.StartTime).Second(), 0, cfg.Timezone.GetLocation())
|
||||
today := time.Date(now.Year(), now.Month(), now.Day(), time.Time(*a.StartTime).Hour(), time.Time(*a.StartTime).Minute(), time.Time(*a.StartTime).Second(), 0, now.Location())
|
||||
if now.After(today) {
|
||||
today = today.AddDate(0, 0, 1)
|
||||
}
|
||||
|
163
player/player.go
163
player/player.go
@ -6,12 +6,19 @@ 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"
|
||||
)
|
||||
@ -22,8 +29,8 @@ type Player struct {
|
||||
Playlist []string
|
||||
MaxRunTime time.Duration
|
||||
Stopper chan bool
|
||||
currentCmd *exec.Cmd
|
||||
currentCmdCh chan bool
|
||||
|
||||
sampleRate beep.SampleRate
|
||||
|
||||
claironTime time.Duration
|
||||
claironFile string
|
||||
@ -31,7 +38,7 @@ type Player struct {
|
||||
ntick int64
|
||||
hasClaironed bool
|
||||
launched time.Time
|
||||
volume uint16
|
||||
volume *effects.Volume
|
||||
dontUpdateVolume bool
|
||||
reverseOrder bool
|
||||
playedItem int
|
||||
@ -42,10 +49,6 @@ func WakeUp(cfg *config.Config) (err error) {
|
||||
return fmt.Errorf("Unable to start the player: a player is already running")
|
||||
}
|
||||
|
||||
seed := time.Now().Unix()
|
||||
seed -= seed % 172800
|
||||
rand.Seed(seed)
|
||||
|
||||
CommonPlayer, err = NewPlayer(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -64,11 +67,10 @@ func NewPlayer(cfg *config.Config) (*Player, error) {
|
||||
|
||||
p := Player{
|
||||
Stopper: make(chan bool, 1),
|
||||
currentCmdCh: make(chan bool, 1),
|
||||
MaxRunTime: settings.MaxRunTime * time.Minute,
|
||||
sampleRate: beep.SampleRate(cfg.SampleRate),
|
||||
claironTime: settings.GongInterval * time.Minute,
|
||||
claironFile: reveil.CurrentGongPath(cfg),
|
||||
reverseOrder: int(time.Now().Unix()/86400)%2 == 0,
|
||||
}
|
||||
|
||||
// Load our track list
|
||||
@ -98,53 +100,59 @@ func NewPlayer(cfg *config.Config) (*Player, error) {
|
||||
return &p, 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
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("Running paplay ", filepath)
|
||||
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)
|
||||
}
|
||||
|
||||
err = p.currentCmd.Wait()
|
||||
p.currentCmdCh <- true
|
||||
if err != nil {
|
||||
fd.Close()
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Player) WakeUp() {
|
||||
log.Println("RUN WAKEUP FUNC")
|
||||
|
||||
log.Println("Playlist in use:", strings.Join(p.Playlist, " ; "))
|
||||
|
||||
// Prepare sound player
|
||||
p.volume = 3500
|
||||
|
||||
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)
|
||||
|
||||
p.currentCmdCh <- true
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-p.currentCmdCh:
|
||||
// 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
|
||||
p.SetVolume(65535)
|
||||
_, sample, format, err := loadFile(p.claironFile)
|
||||
if err == nil {
|
||||
p.volume.Volume = 0.1
|
||||
p.dontUpdateVolume = true
|
||||
go p.playFile(p.claironFile)
|
||||
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 = 3500 + uint16(math.Log(1+float64(p.ntick)/8)*9500)
|
||||
p.SetVolume(p.volume)
|
||||
p.volume.Volume = -2 - math.Log(5/float64(p.ntick))/3
|
||||
|
||||
if p.reverseOrder {
|
||||
p.playedItem -= 1
|
||||
@ -158,25 +166,54 @@ loop:
|
||||
p.playedItem = len(p.Playlist) - 1
|
||||
}
|
||||
|
||||
log.Println("Next track: ", p.Playlist[p.playedItem])
|
||||
go p.playFile(p.Playlist[p.playedItem])
|
||||
// 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
|
||||
}
|
||||
|
||||
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)
|
||||
// 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)
|
||||
|
||||
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)
|
||||
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-p.Stopper:
|
||||
log.Println("Stopper activated")
|
||||
break loop
|
||||
|
||||
case <-maxRun:
|
||||
log.Println("Max run time exhausted")
|
||||
break loop
|
||||
|
||||
case <-ticker.C:
|
||||
p.ntick += 1
|
||||
if !p.dontUpdateVolume {
|
||||
p.volume.Volume = -2 - math.Log(5/float64(p.ntick))/3
|
||||
}
|
||||
case <-interrupt:
|
||||
break loop
|
||||
}
|
||||
@ -186,47 +223,31 @@ loop:
|
||||
|
||||
// Calm down music
|
||||
loopcalm:
|
||||
for i := 0; i < 128 && p.volume >= 15000; i += 1 {
|
||||
timer := time.NewTimer(40 * time.Millisecond)
|
||||
|
||||
p.volume -= 256
|
||||
p.SetVolume(p.volume)
|
||||
for i := 0; i < 2000; i += 1 {
|
||||
p.volume.Volume -= 0.001
|
||||
|
||||
timer := time.NewTimer(4 * time.Millisecond)
|
||||
select {
|
||||
case <-p.Stopper:
|
||||
log.Println("Hard stop received...")
|
||||
timer.Stop()
|
||||
p.volume = 0
|
||||
p.volume.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
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Player) NextTrack() {
|
||||
if p.currentCmd != nil && p.currentCmd.Process != nil {
|
||||
p.currentCmd.Process.Kill()
|
||||
// TODO: find a better way to deallocate the card
|
||||
os.Exit(42)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -1,22 +0,0 @@
|
||||
<script>
|
||||
import {
|
||||
Toast,
|
||||
ToastBody,
|
||||
ToastHeader,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import { ToastsStore } from '$lib/stores/toasts';
|
||||
</script>
|
||||
|
||||
<div class="toast-container position-absolute top-0 end-0 p-3">
|
||||
{#each $ToastsStore.toasts as toast}
|
||||
<Toast>
|
||||
<ToastHeader toggle={toast.close} icon={toast.color}>
|
||||
{#if toast.title}{toast.title}{:else}Réveil{/if}
|
||||
</ToastHeader>
|
||||
<ToastBody>
|
||||
{toast.msg}
|
||||
</ToastBody>
|
||||
</Toast>
|
||||
{/each}
|
||||
</div>
|
@ -8,14 +8,6 @@
|
||||
|
||||
import Header from '$lib/components/Header.svelte';
|
||||
import Toaster from '$lib/components/Toaster.svelte';
|
||||
import { ToastsStore } from '$lib/stores/toasts';
|
||||
|
||||
window.onunhandledrejection = (e) => {
|
||||
ToastsStore.addErrorToast({
|
||||
message: e.reason,
|
||||
timeout: 7500,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"short_name": "Réveil",
|
||||
"name": "Réveil",
|
||||
"short_name": "Gustus",
|
||||
"name": "Gustus",
|
||||
"version": "0.1",
|
||||
"author": "nemucorp",
|
||||
"start_url": "/",
|
||||
@ -12,9 +12,9 @@
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"background_color": "#e83e8c",
|
||||
"background_color": "#d62a49",
|
||||
"display": "standalone",
|
||||
"scope": "/",
|
||||
"theme_color": "#ffffff",
|
||||
"description": "Quand est-ce qu'on se lève ?"
|
||||
"description": "Retrouvez facilement toutes vos recettes préférées"
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user