274 lines
4.9 KiB
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
|
|
}()
|
|
|
|
}
|