// // 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 mixer import ( "container/list" "fmt" // "log" "net" "sync" // "github.com/scgolang/osc" ) type Channel uint8 type EventType int const ( EventFaderChange EventType = iota EventMute ) func (et EventType) String() string { switch et { case EventFaderChange: return "fader-change" case EventMute: return "mute" default: return "unknown" } } type FaderLevel float32 const ( 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("%3f", fl) switch fl { case FaderLevelMax: return val + " (max)" case FaderLevel0db: return val + " (0db)" case FaderLevelOff: return val + " (off)" default: return val } } type Mute int8 const ( MuteUnknown = Mute(-1) MuteUnmuted = Mute(0) MuteMuted = Mute(1) ) func (m Mute) String() string { switch m { case MuteUnmuted: return "unmuted" case MuteMuted: return "muted" default: return "unknown" } } type Event struct { Channel Channel Type EventType Level FaderLevel Mute Mute } func (e Event) String() string { return fmt.Sprintf("Event(%s) for channel %d: level=%s, muted=%s", e.Type, e.Channel, e.Level, e.Mute) } type subscriber struct { publish chan<- Event unsubscribe <-chan struct{} } type Mixer struct { config Config client *net.UDPConn clientLock sync.Mutex subscribersLock sync.Mutex subscribers map[Channel]*list.List } func NewMixer(c Config) (*Mixer, error) { if c.Port == "" { c.Port = "10023" } m := &Mixer{config: c} server, err := net.ResolveUDPAddr("udp", c.Host+":"+c.Port) if err != nil { return nil, err } m.client, err = net.DialUDP("udp", nil, server) if err != nil { return nil, err } m.subscribers = make(map[Channel]*list.List) 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) publishEvent(ev Event) { m.subscribersLock.Lock() defer m.subscribersLock.Unlock() subs, exists := m.subscribers[ev.Channel] if exists && subs != nil { var next *list.Element for entry := subs.Front(); entry != nil; entry = next { next = entry.Next() sub, ok := (entry.Value).(subscriber) if !ok { panic(fmt.Sprintf("mixer: subscriber list element value has wrong type: %T", entry.Value)) } select { case <-sub.unsubscribe: // log.Printf("mixer: removing subscriber '%v', because it has unsubscribed", sub.publish) close(sub.publish) subs.Remove(entry) default: select { case sub.publish <- ev: default: // log.Printf("mixer: removing subscriber '%v', because it is not responding", sub.publish) close(sub.publish) subs.Remove(entry) } } } } } // 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) // } // } // }() return nil } func (m *Mixer) Shutdown() error { if m.client != nil { m.client.Close() m.client = nil } m.subscribersLock.Lock() defer m.subscribersLock.Unlock() for _, subs := range m.subscribers { var next *list.Element for entry := subs.Front(); entry != nil; entry = next { next = entry.Next() sub, ok := (entry.Value).(subscriber) if !ok { panic(fmt.Sprintf("mixer: subscriber list element value has wrong type: %T", entry.Value)) } close(sub.publish) subs.Remove(entry) } } return nil } 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 nil // m.sendMidiMessage([]byte{CC_MUTE, channel, value}) } func (m *Mixer) Mute(ch Channel) error { return m.sendMute(byte(ch), 0x7F) } func (m *Mixer) Unmute(ch Channel) error { return m.sendMute(byte(ch), 0x00) } func (m *Mixer) SetLevel(ch Channel, level FaderLevel) error { if level > FaderLevelMax { level = FaderLevelMax } return nil // m.sendMidiMessage([]byte{CC_FADER, byte(ch), byte(level)}) } func (m *Mixer) Subscribe(ch Channel, out chan<- Event) chan<- struct{} { m.subscribersLock.Lock() defer m.subscribersLock.Unlock() subs, exists := m.subscribers[ch] if !exists { subs = list.New() m.subscribers[ch] = subs } // log.Printf("mixer: subscribing '%v' to events for channel: %v", out, ch) unsubscribe := make(chan struct{}) subs.PushBack(subscriber{publish: out, unsubscribe: unsubscribe}) return unsubscribe }