// // dolmetschctl // // // Copyright (C) 2019 Christian Pointner // // This file is part of dolmetschctl. // // dolmetschctl is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. // // dolmetschctl is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with dolmetschctl. If not, see . // package controller import ( "container/list" "errors" "fmt" // "log" "strings" "sync" "github.com/scgolang/midi" ) const ( BUTTON_ON = byte(0x90) BUTTON_OFF = byte(0x80) NOTE_OFFSET_BUTTON = byte(0x00) NUM_BUTTONS = 4 LED_CC = byte(0xB0) NOTE_OFFSET_LED = byte(0x00) NUM_LEDS = 4 LED_OFF = byte(0x00) LED_ON = byte(0x01) LED_TOGGLE = byte(0x02) LED_BLINK_MIN = byte(0x03) LED_BLINK_MAX = byte(0x7f) ) type EventType int const ( ButtonPressed EventType = iota ButtonReleased ) func (e EventType) String() string { switch e { case ButtonPressed: return "button pressed" case ButtonReleased: return "button released" default: return "unknown" } } type Event struct { Button byte Type EventType } type subscriber struct { publish chan<- Event unsubscribe <-chan struct{} } type Controller struct { Dev *midi.Device subscribersLock sync.Mutex subscribers list.List } func openDevice(devices []*midi.Device, prefix string) (d *midi.Device, err error) { for _, device := range devices { if strings.HasPrefix(device.Name, prefix) { d = device } } if d == nil { return nil, errors.New("could not find device with prefix: " + prefix) } return d, d.Open() } func NewController(c Config) (*Controller, error) { devices, err := midi.Devices() if err != nil { return nil, err } ctrl := &Controller{} if ctrl.Dev, err = openDevice(devices, c.Dev); err != nil { return nil, err } ctrl.Dev.QueueSize = 100 return ctrl, nil } func (c *Controller) publishEvent(ev Event) { c.subscribersLock.Lock() defer c.subscribersLock.Unlock() var next *list.Element for entry := c.subscribers.Front(); entry != nil; entry = next { next = entry.Next() sub, ok := (entry.Value).(subscriber) if !ok { panic(fmt.Sprintf("controller: subscriber list element value has wrong type: %T", entry.Value)) } select { case <-sub.unsubscribe: // log.Printf("controller: removing subscriber '%v', because it has unsubscribed", sub.publish) close(sub.publish) c.subscribers.Remove(entry) default: select { case sub.publish <- ev: default: // log.Printf("controller: removing subscriber '%v', because it is not responding", sub.publish) close(sub.publish) c.subscribers.Remove(entry) } } } } func (c *Controller) handleMidiPacket(p midi.Packet) { ev := Event{Button: p.Data[1]} switch p.Data[0] { case BUTTON_ON: ev.Type = ButtonPressed case BUTTON_OFF: ev.Type = ButtonReleased default: // ignore all other events return } c.publishEvent(ev) } func (c *Controller) Init() error { ch, err := c.Dev.Packets() if err != nil { return err } go func() { for { p := <-ch if p.Err != nil { c.Shutdown() } c.handleMidiPacket(p) } }() return nil } func (c *Controller) Shutdown() error { if c.Dev != nil { c.Dev.Close() c.Dev = nil } c.subscribersLock.Lock() defer c.subscribersLock.Unlock() for entry := c.subscribers.Front(); entry != nil; entry = entry.Next() { sub, ok := (entry.Value).(subscriber) if !ok { panic(fmt.Sprintf("controller: subscriber list element value has wrong type: %T", entry.Value)) } close(sub.publish) } c.subscribers.Init() return nil } func (c *Controller) setLed(num byte, value byte) error { if num >= NUM_LEDS { return fmt.Errorf("this controller has only %d leds.", NUM_LEDS) } n, err := c.Dev.Write([]byte{LED_CC, NOTE_OFFSET_LED + num, value}) if err != nil { // reopen device? return err } if n != 3 { return errors.New("sending LED command failed.") } return nil } func (c *Controller) LedOff(num byte) error { return c.setLed(num, LED_OFF) } func (c *Controller) LedOn(num byte) error { return c.setLed(num, LED_ON) } func (c *Controller) LedToggle(num byte) error { return c.setLed(num, LED_TOGGLE) } func (c *Controller) LedBlink(num, wait byte) error { if (wait + LED_BLINK_MIN) > LED_BLINK_MAX { return fmt.Errorf("blink wait must be between 0 and %d.", LED_BLINK_MAX-LED_BLINK_MIN) } return c.setLed(num, wait+LED_BLINK_MIN) } func (c *Controller) Subscribe(out chan<- Event) chan<- struct{} { c.subscribersLock.Lock() defer c.subscribersLock.Unlock() // log.Printf("controller: subscribing '%v' to events", out) unsubscribe := make(chan struct{}) c.subscribers.PushBack(subscriber{publish: out, unsubscribe: unsubscribe}) return unsubscribe }