// // 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" "errors" "fmt" // "log" "strings" "github.com/scgolang/midi" ) // TODO: make this configurable const ( CC_MUTE = byte(0xB1) CC_FADER = byte(0xB0) ) type Channel uint8 type EventType int const ( EventFaderChange = iota EventMute ) func (et EventType) String() string { switch et { case EventFaderChange: return "fader-change" case EventMute: return "mute" default: return "unknown" } } type FaderLevel uint8 // TODO: make the values configurable const ( FaderLevelUnknown = FaderLevel(0xFF) FaderLevelMax = FaderLevel(0x7F) FaderLevel0db = FaderLevel(0x60) FaderLevelOff = FaderLevel(0x00) ) func (fl FaderLevel) String() string { if fl > FaderLevelMax { return "unknown" } val := fmt.Sprintf("%3d", 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 = -1 MuteUnmuted = 0 MuteMuted = 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 Mixer struct { DevIn *midi.Device DevOut *midi.Device 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 } // TODO: add support for DevIn == DevOut m := &Mixer{} if m.DevIn, err = openDevice(devices, c.DevIn); err != nil { return nil, err } m.DevIn.QueueSize = 100 if m.DevOut, err = openDevice(devices, c.DevOut); err != nil { return nil, err } m.subscribers = make(map[Channel]*list.List) return m, nil } func (m *Mixer) sendEvent(ev Event) { subs, exists := m.subscribers[ev.Channel] if exists && subs != nil { var next *list.Element for sub := subs.Front(); sub != nil; sub = next { next = sub.Next() ch, ok := (sub.Value).(chan<- Event) if !ok { panic(fmt.Sprintf("mixer: subscriber list element value has wrong type: %T", sub.Value)) } select { case ch <- ev: default: // subscriber is not respoding... // log.Printf("mixer: removing subscriber '%v', because it is not responding", ch) close(ch) subs.Remove(sub) } } } } 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.sendEvent(ev) } func (m *Mixer) Init() error { ch, err := m.DevIn.Packets() if err != nil { return err } go func() { for { // TODO: handle Errors (reopen the device!) m.handleMidiPacket(<-ch) } }() return nil } func (m *Mixer) Shutdown() error { if m.DevIn != nil { m.DevIn.Close() } if m.DevOut != nil { m.DevOut.Close() } // TODO: also close all subscribed channels // terminate go-routine started by Init() return nil } func (m *Mixer) sendMute(channel byte, value byte) error { n, err := m.DevOut.Write([]byte{CC_MUTE, channel, value}) if err != nil { // reopen device? return err } if n != 3 { return errors.New("sending mute command failed.") } return nil } 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 } n, err := m.DevOut.Write([]byte{CC_FADER, byte(ch), byte(level)}) if err != nil { // reopen device? return err } if n != 3 { return errors.New("setting fader level failed.") } return nil } func (m *Mixer) Subscribe(ch Channel, out chan<- Event) { 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) subs.PushBack(out) }