package main import ( "bytes" "context" "log" "os" "os/exec" "strconv" "sync" "syscall" "text/template" "time" "github.com/faiface/beep" "github.com/fhs/gompd/v2/mpd" "github.com/kataras/go-events" ) type MPD struct { file int f beep.Format } var ( mpdcf context.CancelFunc mpdw *mpd.Watcher mpdc *mpd.Client mpd_mutex *sync.Mutex mpd_plcf context.CancelFunc ) func init() { log.Println("mpd.go loading..") f, err := os.Create(config.GetString("mpd.config")) if err != nil { log.Fatal(err) } t, err := template.New("mpd.tmpl").ParseFiles("tmpl/mpd.tmpl") if err != nil { log.Fatal(err) } err = t.Execute(f, config.GetStringMapString("mpd")) if err != nil { log.Fatal(err) } f.Close() pidstr, err := os.ReadFile(config.GetString("mpd.pid")) switch err { case os.ErrNotExist: log.Println("Pidfile not found, doing nothing") case nil: pid, _ := strconv.Atoi(string(bytes.TrimSpace(pidstr))) proc, err := os.FindProcess(pid) switch err { case nil: log.Println(proc.Kill()) case os.ErrProcessDone: log.Println("Pid alreadt finished, doing nothing.") default: log.Fatal(err) } default: log.Fatal(err) } ctx, cancel := context.WithCancel(context.Background()) mpdcf = cancel cmd := exec.CommandContext( ctx, config.GetString("mpd.cmd"), "--no-daemon", config.GetString("mpd.config"), "-v", ) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr go func() { err := cmd.Run() if err != nil { log.Println(err) } cancel() }() // wait for mpd to start. time.Sleep(2 * time.Second) mpdc, err = mpd.Dial("unix", config.GetString("mpd.sock")) if err != nil { log.Fatal(err) } mpdc.Repeat(true) mpdc.Random(true) err = mpdc.EnableOutput(0) if err != nil { log.Fatal(err) } mpdw, err = mpd.NewWatcher("unix", config.GetString("mpd.sock"), "") if err != nil { log.Fatal(err) } go func() { for { select { case e := <-mpdw.Event: ev.Emit(events.EventName(e)) case err := <-mpdw.Error: log.Println(err) return } } }() log.Println("mpd.go done.") } func NewMPD() (*MPD, error) { out := new(MPD) f, err := syscall.Open(config.GetString("mpd.fifo"), syscall.O_CREAT|syscall.O_RDONLY|syscall.O_CLOEXEC|syscall.O_NONBLOCK, 0644) if err != nil { return nil, err } out.f = beep.Format{ SampleRate: beep.SampleRate(sampleRate), NumChannels: channels, Precision: 2, } out.file = f return out, nil } func (m *MPD) Err() error { return nil } func (m *MPD) Stream(samples [][2]float64) (n int, ok bool) { tmp := make([]byte, m.f.NumChannels+2) for i := range samples { dn, err := syscall.Read(m.file, tmp) if dn == len(tmp) { samples[i], _ = m.f.DecodeSigned(tmp) ok = true } if err != nil { samples[i] = [2]float64{} ok = true break } } return len(samples), ok }