diff --git a/.gitignore b/.gitignore index 66fd13c..d642699 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ # Dependency directories (remove the comment below to include it) # vendor/ + +*.mp3 diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..85890bd --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "time" + + marlinbox "github.com/itsscb/marlin-box" +) + +func main() { + mb := marlinbox.New("playlist.json") + mb.Run() + time.Sleep(time.Minute * 5) +} diff --git a/cmd/playlist.json b/cmd/playlist.json new file mode 100644 index 0000000..4485c98 --- /dev/null +++ b/cmd/playlist.json @@ -0,0 +1,28 @@ +{ +"devicename":"Sycreader RFID Technology Co., Ltd SYC ID&IC USB Reader", +"volume": 1.0, +"playlist": [ + { + "id": "0002693373", + "file": "./music/ACDC - Back In Black.mp3" + }, + { + "id": "0011415256", + "file": "./music/ACDC - TNT.mp3" + }, + { + "id": "0011394336", + "file": "./music/ACDC - Thunderstruck.mp3" + } + ], +"controlcards": [ + { + "id": "0011462127", + "function": "vol+" + }, + { + "id": "0011394129", + "function": "vol-" + } + ] +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1b90ea4 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module marlinbox + +go 1.18 + +require ( + github.com/gvalkov/golang-evdev v0.0.0-20220815104727-7e27d6ce89b6 + github.com/hajimehoshi/go-mp3 v0.3.4 + github.com/hajimehoshi/oto/v2 v2.3.1 +) + +require golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4fe170f --- /dev/null +++ b/go.sum @@ -0,0 +1,8 @@ +github.com/gvalkov/golang-evdev v0.0.0-20220815104727-7e27d6ce89b6 h1:K9b8efT9f1NkITNgNAm2A1LuoamhG4pAhXVjz5Sfa5Q= +github.com/gvalkov/golang-evdev v0.0.0-20220815104727-7e27d6ce89b6/go.mod h1:SAzVFKCRezozJTGavF3GX8MBUruETCqzivVLYiywouA= +github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68= +github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo= +github.com/hajimehoshi/oto/v2 v2.3.1 h1:qrLKpNus2UfD674oxckKjNJmesp9hMh7u7QCrStB3Rc= +github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo= +golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e h1:NHvCuwuS43lGnYhten69ZWqi2QOj/CiDNcKbVqwVoew= +golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/marlinbox.go b/marlinbox.go new file mode 100644 index 0000000..5c66e77 --- /dev/null +++ b/marlinbox.go @@ -0,0 +1,215 @@ +package marlinbox + +import ( + "encoding/json" + "errors" + "fmt" + "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"` + CurrentID string + Volume float64 `json:"volume,omitempty"` + Playlist []*PlayCard `json:"playlist,omitempty"` + ControlCards []*ControlCard `json:"controlcards,omitempty"` + Device *evdev.InputDevice + CurrentPlayCard *PlayCard + Player oto.Player + PlayerContext *oto.Context +} + +type RFIDCard struct { + ID string `json:"id"` +} + +type PlayCard struct { + RFIDCard + File string `json:"file,omitempty"` +} +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() + + for _, dev := range devices { + if dev.Name == mb.DeviceName { + mb.DevicePath = dev.Fn + } + } + + if mb.DevicePath == "" { + panic("Device not found") + } + + return mb +} + +func (mb *MarlinBox) Run() { + var err error + mb.Device, err = evdev.Open(mb.DevicePath) + if err != nil { + panic(err) + } + mb.Device.Grab() + + go func() { + 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" { + err := mb.GetCurrentCard() + if err != nil { + log.Println(err) + } + fmt.Println(mb.CurrentPlayCard) + mb.CurrentID = "" + // err = + // if err != nil { + // log.Println(err) + // panic(err) + // } + continue + } + + mb.CurrentID += val + } + } + }() +} + +func (mb *MarlinBox) GetCurrentCard() error { + fmt.Println(mb.ControlCards) + for _, c := range mb.ControlCards { + if mb.CurrentID == c.ID { + switch c.Function { + case "vol+": + fmt.Println("vol+") + if mb.Volume < 1.0 { + mb.Volume = mb.Volume + 0.2 + } + case "vol-": + fmt.Println("vol-") + if mb.Volume > 0 { + mb.Volume = mb.Volume - 0.2 + } + default: + return nil + } + return nil + } + } + for _, c := range mb.Playlist { + if mb.CurrentID == c.ID { + mb.CurrentPlayCard = c + if mb.PlayerContext != nil { + mb.Player.Reset() + } + mb.Play() + return nil + } + } + return errors.New("Card not found: " + mb.CurrentID) +} + +func (mb *MarlinBox) Play() { + go func() { + var err error + var ready chan struct{} + playingID := mb.CurrentPlayCard.ID + f, err := os.Open(mb.CurrentPlayCard.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.CurrentPlayCard.ID != playingID { + break + } + if mb.Volume != mb.Player.Volume() { + mb.Player.SetVolume(mb.Volume) + } + time.Sleep(time.Second) + if !mb.Player.IsPlaying() { + break + } + } + }() + +}