marlinbox/marlinbox.go

274 lines
4.9 KiB
Go

package marlinbox
import (
"encoding/json"
"log"
"os"
"time"
evdev "github.com/gvalkov/golang-evdev"
"github.com/hajimehoshi/go-mp3"
"github.com/hajimehoshi/oto/v2"
)
type MarlinBox struct {
DeviceName string `json:"devicename"`
DevicePath string `json:"devicepath,omitempty"`
ConfigPath string `json:"configpath,omitempty"`
CurrentID string `json:"-"`
Volume float64 `json:"-"`
NextSong bool `json:"-"`
Stop bool `json:"-"`
Playlist []*PlayCard `json:"playlist,omitempty"`
ControlCards []*ControlCard `json:"controlcards,omitempty"`
Device *evdev.InputDevice `json:"-"`
CurrentPlayCard *PlayCard `json:"-"`
Player oto.Player `json:"-"`
PlayerContext *oto.Context `json:"-"`
}
type RFIDCard struct {
ID string `json:"id"`
}
type PlayCard struct {
RFIDCard
File []string `json:"file"`
}
type ControlCard struct {
RFIDCard
Function string `json:"function,omitempty"`
}
var KeyMap = map[uint16]string{
2: "1",
3: "2",
4: "3",
5: "4",
6: "5",
7: "6",
8: "7",
9: "8",
10: "9",
11: "0",
28: "ENTER",
}
func New(path string) *MarlinBox {
var mb *MarlinBox
f, err := os.ReadFile(path)
if err != nil {
panic(err)
}
err = json.Unmarshal(f, &mb)
if err != nil {
panic(err)
}
if mb.Volume == 0 {
mb.Volume = 1
}
if mb.DeviceName == "" {
panic("No DeviceName given")
}
devices, err := evdev.ListInputDevices()
if err != nil {
log.Panicf("Error listing Input Devices: %s", err)
}
for _, dev := range devices {
if dev.Name == mb.DeviceName {
mb.DevicePath = dev.Fn
}
}
if mb.DevicePath == "" {
panic("Device not found")
}
mb.ConfigPath = path
return mb
}
func (mb *MarlinBox) Run() {
var err error
mb.Device, err = evdev.Open(mb.DevicePath)
if err != nil {
panic(err)
}
mb.Device.Grab()
for {
events, err := mb.Device.Read()
if err != nil {
log.Println(err)
}
for _, ev := range events {
if ev.Value != 0x1 {
continue
}
val, ok := KeyMap[ev.Code]
if !ok {
continue
}
if val == "ENTER" || len(mb.CurrentID) == 9 {
if val != "ENTER" {
mb.CurrentID += val
}
mb.GetCurrentCard()
mb.CurrentID = ""
continue
}
mb.CurrentID += val
}
}
}
func (mb *MarlinBox) GetCurrentCard() {
for _, c := range mb.ControlCards {
if mb.CurrentID == c.ID {
switch c.Function {
case "stop":
if mb.Player != nil && mb.Player.IsPlaying() {
mb.Stop = true
}
return
case "vol+":
if mb.Volume < 1.0 {
mb.Volume = mb.Volume + 0.2
return
}
case "vol-":
if mb.Volume >= 0.2 {
mb.Volume = mb.Volume - 0.2
return
}
case "nextSong":
if mb.Player != nil {
mb.NextSong = true
return
}
return
default:
return
}
}
}
for _, c := range mb.Playlist {
if mb.CurrentID == c.ID {
if mb.CurrentPlayCard != nil {
if mb.CurrentID == mb.CurrentPlayCard.ID {
return
}
}
mb.CurrentPlayCard = c
if mb.PlayerContext != nil {
mb.Player.Pause()
}
mb.Play()
return
}
}
mb.Playlist = append(mb.Playlist, &PlayCard{
RFIDCard: RFIDCard{
ID: mb.CurrentID,
},
})
config, err := json.MarshalIndent(&mb, "", " ")
if err != nil {
log.Printf("\nCould not marshal output '%+v': %s", config, err)
}
jsonFile, err := os.Create(mb.ConfigPath)
if err != nil {
log.Printf("\nCould not create json-File '%s': %s", mb.ConfigPath, err)
}
_, err = jsonFile.Write(config)
if err != nil {
log.Printf("\nCould not write json-File '%s': %s", mb.ConfigPath, err)
}
}
func (mb *MarlinBox) Play() {
go func() {
var ready chan struct{}
if mb.CurrentPlayCard == nil {
return
}
playingID := mb.CurrentPlayCard.ID
for _, file := range mb.CurrentPlayCard.File {
if mb.Stop {
break
}
if mb.CurrentPlayCard == nil || playingID != mb.CurrentPlayCard.ID {
return
}
f, err := os.Open(file)
if err != nil {
log.Println(err)
return
}
defer f.Close()
d, err := mp3.NewDecoder(f)
if err != nil {
log.Println(err)
return
}
mb.PlayerContext, ready, err = oto.NewContext(d.SampleRate(), 2, 2)
if err != nil {
log.Println(err)
return
}
<-ready
mb.Player = mb.PlayerContext.NewPlayer(d)
defer mb.Player.Close()
mb.Player.SetVolume(mb.Volume)
mb.Player.Play()
for {
if mb.Stop {
break
}
if mb.CurrentPlayCard != nil && mb.CurrentPlayCard.ID != playingID {
break
}
if mb.NextSong {
mb.NextSong = false
break
}
if mb.Volume != mb.Player.Volume() {
mb.Player.SetVolume(mb.Volume)
}
time.Sleep(time.Second)
if !mb.Player.IsPlaying() {
break
}
}
mb.Player.Pause()
mb.Player.Close()
mb.PlayerContext.Suspend()
}
mb.Stop = false
mb.CurrentPlayCard = nil
}()
}