diff options
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | Makefile | 45 | ||||
-rw-r--r-- | cmd/dolmetschctld/dolmetschctld.go | 2 | ||||
-rw-r--r-- | cmd/dolmetschctld/statemachine.go | 4 | ||||
-rw-r--r-- | cmd/dolmetschctld/telnet.go | 2 | ||||
-rw-r--r-- | go.mod | 2 | ||||
-rw-r--r-- | go.sum | 4 | ||||
-rw-r--r-- | pkg/mixer/config.go | 4 | ||||
-rw-r--r-- | pkg/mixer/mixer.go | 288 |
9 files changed, 189 insertions, 166 deletions
@@ -8,8 +8,8 @@ app/*.o app/*.d app/*.d.* app/config.h -cmd/dolmetschctl/dolmetschctl -cmd/dolmetschctld/dolmetschctld +dolmetschctl +dolmetschctld firmware/*.a firmware/*.o firmware/*.d diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a557e34 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +## +## dolmetschctl +## +## +## Copyright (C) 2019 Christian Pointner <equinox@spreadspace.org> +## +## 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 <http://www.gnu.org/licenses/>. +## + +GOCMD := go +ifdef GOROOT +GOCMD = $(GOROOT)/bin/go +endif + +all: dolmetschctl dolmetschctld +.PHONY: vet format dolmetschctl dolmetschctld clean + +vet: + $(GOCMD) vet ./... + +format: + $(GOCMD) fmt ./... + +dolmetschctl: + $(GOCMD) build -o dolmetschctl ./cmd/dolmetschctl + +dolmetschctld: + $(GOCMD) build -o dolmetschctld ./cmd/dolmetschctld + +clean: + rm -f dolmetschctl + rm -f dolmetschctld diff --git a/cmd/dolmetschctld/dolmetschctld.go b/cmd/dolmetschctld/dolmetschctld.go index 38f71ad..7105ae1 100644 --- a/cmd/dolmetschctld/dolmetschctld.go +++ b/cmd/dolmetschctld/dolmetschctld.go @@ -44,7 +44,7 @@ func main() { // ************************************* // * initialize core - m, err := mixer.NewMixer(mixer.Config{DevIn: "ESI MIDIMATE eX MIDI 2", DevOut: "ESI MIDIMATE eX MIDI 1"}) + m, err := mixer.NewMixer(mixer.Config{Host: "192.168.48.232"}) if err != nil { log.Printf("Error opening the mixer: %v", err) os.Exit(1) diff --git a/cmd/dolmetschctld/statemachine.go b/cmd/dolmetschctld/statemachine.go index 3e92f00..15f0e41 100644 --- a/cmd/dolmetschctld/statemachine.go +++ b/cmd/dolmetschctld/statemachine.go @@ -133,7 +133,7 @@ func (sm *StateMachine) publishState() { sm.stateSubscribers.Remove(entry) default: select { - case sub.publish <- types.FullState{sm.state, sm.original2InterpreterRatio, sm.language}: + case sub.publish <- types.FullState{State: sm.state, Ratio: sm.original2InterpreterRatio, Language: sm.language}: default: // subscriber is not responding... log.Printf("statemachine: removing subscriber '%v', because it is not responding", sub.publish) @@ -292,7 +292,7 @@ func (sm *StateMachine) run() { case req := <-sm.getOriginal2InterpreterRatioCh: req.resCh <- sm.original2InterpreterRatio case req := <-sm.getStateCh: - req.resCh <- getStateRes{types.FullState{sm.state, sm.original2InterpreterRatio, sm.language}} + req.resCh <- getStateRes{types.FullState{State: sm.state, Ratio: sm.original2InterpreterRatio, Language: sm.language}} case ev := <-sm.mixerEventCh: sm.handleMixerEvent(ev) sm.reconcile(false) diff --git a/cmd/dolmetschctld/telnet.go b/cmd/dolmetschctld/telnet.go index 5618ebe..55c3bf0 100644 --- a/cmd/dolmetschctld/telnet.go +++ b/cmd/dolmetschctld/telnet.go @@ -78,7 +78,7 @@ func telnetCmdState(c *telgo.Client, args []string, sm *StateMachine) bool { } state, ratio, lang := sm.GetState() c.Sayln("current state: %s", state) - c.Sayln("current ratio: %s", ratio) + c.Sayln("current ratio: %f", ratio) c.Sayln("current language: %s", lang) return false } @@ -4,7 +4,9 @@ go 1.13 require ( github.com/gorilla/websocket v1.4.1 + github.com/imdario/go-ulid v0.0.0-20180116185620-aeb52bf96595 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/scgolang/midi v0.5.0 + github.com/scgolang/osc v0.11.1 github.com/spreadspace/telgo v0.0.0-20170609015223-7277b0d8090e ) @@ -1,8 +1,12 @@ github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/imdario/go-ulid v0.0.0-20180116185620-aeb52bf96595 h1:8MKHx/6AMMFGslqvr37RF7zktr3eJmY1z2FKdq3Zo/o= +github.com/imdario/go-ulid v0.0.0-20180116185620-aeb52bf96595/go.mod h1:ugPCasYVpR6Cf8xlF0vkZdVKntj7zTgo9pLR4Si7Boo= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/scgolang/midi v0.5.0 h1:HsSMHIbgCAZlmjloOgRZeLQgBXi8eWXw3FZJzm3eEkE= github.com/scgolang/midi v0.5.0/go.mod h1:ur2Ay6ad9oWPNFuGq4oQ6u75uZIjZfDdhozbGHnTcfQ= +github.com/scgolang/osc v0.11.1 h1:o2+nXrQrlyEAoFcgZ2zk6p5iI6ht+NgiSKaGQBpvWbU= +github.com/scgolang/osc v0.11.1/go.mod h1:fu5QITvJ5w2pzKXJBmyVTF89ZycPN4bS4cOHJErpR2A= github.com/spreadspace/telgo v0.0.0-20170609015223-7277b0d8090e h1:lhFaE8l5AfSEzH4OW+9BUAo+BuYLe21zuHwWX1YEZHw= github.com/spreadspace/telgo v0.0.0-20170609015223-7277b0d8090e/go.mod h1:kvvhiAI5nvwI4SZrVH9vRa3MTjkcYm3tElmmh+bYuEI= diff --git a/pkg/mixer/config.go b/pkg/mixer/config.go index bc0a292..e25ba2e 100644 --- a/pkg/mixer/config.go +++ b/pkg/mixer/config.go @@ -23,6 +23,6 @@ package mixer type Config struct { - DevIn string `json:"dev-in" yaml:"dev-in" toml:"dev-in"` - DevOut string `json:"dev-out" yaml:"dev-out" toml:"dev-out"` + Host string `json:"host" yaml:"host" toml:"host"` + Port string `json:"port" yaml:"port" toml:"port"` } diff --git a/pkg/mixer/mixer.go b/pkg/mixer/mixer.go index afa1534..8782765 100644 --- a/pkg/mixer/mixer.go +++ b/pkg/mixer/mixer.go @@ -24,20 +24,11 @@ package mixer import ( "container/list" - "errors" "fmt" - "log" - "strings" + // "log" + "net" "sync" - "time" - - "github.com/scgolang/midi" -) - -// TODO: make this configurable -const ( - CC_MUTE = byte(0xB1) - CC_FADER = byte(0xB0) + // "github.com/scgolang/osc" ) type Channel uint8 @@ -59,21 +50,20 @@ func (et EventType) String() string { } } -type FaderLevel uint8 +type FaderLevel float32 -// TODO: make the values configurable const ( - FaderLevelUnknown = FaderLevel(0xFF) - FaderLevelMax = FaderLevel(0x7F) - FaderLevel0db = FaderLevel(0x60) - FaderLevelOff = FaderLevel(0x00) + FaderLevelUnknown = FaderLevel(-1.0) + FaderLevelMax = FaderLevel(1.00) + FaderLevel0db = FaderLevel(0.75) + FaderLevelOff = FaderLevel(0.00) ) func (fl FaderLevel) String() string { if fl > FaderLevelMax { return "unknown" } - val := fmt.Sprintf("%3d", fl) + val := fmt.Sprintf("%3f", fl) switch fl { case FaderLevelMax: return val + " (max)" @@ -123,38 +113,24 @@ type subscriber struct { type Mixer struct { config Config - devIn *midi.Device - devOutLock sync.Mutex - devOut *midi.Device + client *net.UDPConn + clientLock sync.Mutex subscribersLock sync.Mutex subscribers map[Channel]*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 NewMixer(c Config) (*Mixer, error) { - devices, err := midi.Devices() - if err != nil { - return nil, err + if c.Port == "" { + c.Port = "10023" } - - // TODO: add support for devIn == devOut m := &Mixer{config: c} - if m.devIn, err = openDevice(devices, c.DevIn); err != nil { + + server, err := net.ResolveUDPAddr("udp", c.Host+":"+c.Port) + if err != nil { return nil, err } - m.devIn.QueueSize = 100 - if m.devOut, err = openDevice(devices, c.DevOut); err != nil { + m.client, err = net.DialUDP("udp", nil, server) + if err != nil { return nil, err } @@ -162,56 +138,56 @@ func NewMixer(c Config) (*Mixer, error) { return m, nil } -func (m *Mixer) reopenInput() { - for { - time.Sleep(time.Second) - - devices, err := midi.Devices() - if err != nil { - log.Printf("mixer: error listing midi devices: %v, retrying...", err) - continue - } - - if m.devIn, err = openDevice(devices, m.config.DevIn); err != nil { - log.Printf("mixer: error re-opening midi input device: %v, retrying...", err) - continue - } - - if err = m.Init(); err != nil { - log.Printf("mixer: error re-initializing midi input device: %v, retrying...", err) - - continue - } - - log.Printf("mixer: successfully re-initialized midi input device") - break - } -} - -func (m *Mixer) reopenOutput() { - for { - time.Sleep(time.Second) - - devices, err := midi.Devices() - if err != nil { - log.Printf("mixer: error listing midi devices: %v, retrying...", err) - continue - } - - newOut, err := openDevice(devices, m.config.DevOut) - if err != nil { - log.Printf("mixer: error re-opening midi output device: %v, retrying...", err) - continue - } - - log.Printf("mixer: successfully re-opened midi output device") - - m.devOutLock.Lock() - defer m.devOutLock.Unlock() - m.devOut = newOut - break - } -} +// func (m *Mixer) reopenInput() { +// for { +// time.Sleep(time.Second) + +// devices, err := midi.Devices() +// if err != nil { +// log.Printf("mixer: error listing midi devices: %v, retrying...", err) +// continue +// } + +// if m.devIn, err = openDevice(devices, m.config.DevIn); err != nil { +// log.Printf("mixer: error re-opening midi input device: %v, retrying...", err) +// continue +// } + +// if err = m.Init(); err != nil { +// log.Printf("mixer: error re-initializing midi input device: %v, retrying...", err) + +// continue +// } + +// log.Printf("mixer: successfully re-initialized midi input device") +// break +// } +// } + +// func (m *Mixer) reopenOutput() { +// for { +// time.Sleep(time.Second) + +// devices, err := midi.Devices() +// if err != nil { +// log.Printf("mixer: error listing midi devices: %v, retrying...", err) +// continue +// } + +// newOut, err := openDevice(devices, m.config.DevOut) +// if err != nil { +// log.Printf("mixer: error re-opening midi output device: %v, retrying...", err) +// continue +// } + +// log.Printf("mixer: successfully re-opened midi output device") + +// m.devOutLock.Lock() +// defer m.devOutLock.Unlock() +// m.devOut = newOut +// break +// } +// } func (m *Mixer) publishEvent(ev Event) { m.subscribersLock.Lock() @@ -246,57 +222,53 @@ func (m *Mixer) publishEvent(ev Event) { } } -func (m *Mixer) handleMidiPacket(p midi.Packet) { - ev := Event{Level: FaderLevelUnknown, Mute: MuteUnknown} - ev.Channel = Channel(p.Data[1]) - switch p.Data[0] { - case CC_FADER: - ev.Type = EventFaderChange - ev.Level = FaderLevel(p.Data[2]) - case CC_MUTE: - ev.Type = EventMute - ev.Mute = MuteUnmuted - if p.Data[2] > 0 { - ev.Mute = MuteMuted - } - default: - return - } - m.publishEvent(ev) -} +// func (m *Mixer) handleMidiPacket(p midi.Packet) { +// ev := Event{Level: FaderLevelUnknown, Mute: MuteUnknown} +// ev.Channel = Channel(p.Data[1]) +// switch p.Data[0] { +// case CC_FADER: +// ev.Type = EventFaderChange +// ev.Level = FaderLevel(p.Data[2]) +// case CC_MUTE: +// ev.Type = EventMute +// ev.Mute = MuteUnmuted +// if p.Data[2] > 0 { +// ev.Mute = MuteMuted +// } +// default: +// return +// } +// m.publishEvent(ev) +// } func (m *Mixer) Init() error { - ch, err := m.devIn.Packets() - if err != nil { - return err - } - - go func() { - for { - ps := <-ch - for _, p := range ps { - if p.Err != nil { - log.Printf("mixer: got fatal error from midi input device: %v, trying to reopen it...", p.Err) - m.devIn.Close() - m.devIn = nil - go m.reopenInput() - return - } - m.handleMidiPacket(p) - } - } - }() + // ch, err := m.devIn.Packets() + // if err != nil { + // return err + // } + + // go func() { + // for { + // ps := <-ch + // for _, p := range ps { + // if p.Err != nil { + // log.Printf("mixer: got fatal error from midi input device: %v, trying to reopen it...", p.Err) + // // m.devIn.Close() + // // m.devIn = nil + // // go m.reopenInput() + // // return + // } + // // m.handleMidiPacket(p) + // } + // } + // }() return nil } func (m *Mixer) Shutdown() error { - if m.devIn != nil { - m.devIn.Close() - m.devIn = nil - } - if m.devOut != nil { - m.devOut.Close() - m.devOut = nil + if m.client != nil { + m.client.Close() + m.client = nil } m.subscribersLock.Lock() @@ -319,30 +291,30 @@ func (m *Mixer) Shutdown() error { return nil } -func (m *Mixer) sendMidiMessage(msg []byte) error { - m.devOutLock.Lock() - defer m.devOutLock.Unlock() - - if m.devOut == nil { - return errors.New("mixer: output device is not ready.") - } - - n, err := m.devOut.Write(msg) - if err != nil { - log.Printf("mixer: got fatal error from midi output device: %v, trying to reopen it...", err) - m.devOut.Close() - m.devOut = nil - go m.reopenOutput() - return err - } - if n != len(msg) { - return errors.New("sending midi message failed: short write") - } +func (m *Mixer) sendOSCMessage(msg []byte) error { + m.clientLock.Lock() + defer m.clientLock.Unlock() + + // if m.devOut == nil { + // return errors.New("mixer: output device is not ready.") + // } + + // n, err := m.devOut.Write(msg) + // if err != nil { + // log.Printf("mixer: got fatal error from midi output device: %v, trying to reopen it...", err) + // m.devOut.Close() + // m.devOut = nil + // go m.reopenOutput() + // return err + // } + // if n != len(msg) { + // return errors.New("sending midi message failed: short write") + // } return nil } func (m *Mixer) sendMute(channel byte, value byte) error { - return m.sendMidiMessage([]byte{CC_MUTE, channel, value}) + return nil // m.sendMidiMessage([]byte{CC_MUTE, channel, value}) } func (m *Mixer) Mute(ch Channel) error { @@ -357,7 +329,7 @@ func (m *Mixer) SetLevel(ch Channel, level FaderLevel) error { if level > FaderLevelMax { level = FaderLevelMax } - return m.sendMidiMessage([]byte{CC_FADER, byte(ch), byte(level)}) + return nil // m.sendMidiMessage([]byte{CC_FADER, byte(ch), byte(level)}) } func (m *Mixer) Subscribe(ch Channel, out chan<- Event) chan<- struct{} { |