diff --git a/publisher.go b/publisher.go index 1c0ade0..de20080 100644 --- a/publisher.go +++ b/publisher.go @@ -6,40 +6,14 @@ import ( log "github.com/sirupsen/logrus" ) -// observer defines the interface any registered observers must satisfy -type observer interface { - OnUpdate(subject string) -} - -// publisher defines methods that support observers +// publisher defines the list of observer channels type publisher struct { - observerList []observer + observers []chan string } -// Register adds an observer to observerList -func (p *publisher) Register(o observer) { - p.observerList = append(p.observerList, o) -} - -// Deregister removes an observer from observerList -func (p *publisher) Deregister(o observer) { - var indexToRemove int - - for i, observer := range p.observerList { - if observer == o { - indexToRemove = i - break - } - } - - p.observerList = append(p.observerList[:indexToRemove], p.observerList[indexToRemove+1:]...) -} - -// notify updates observers of any changes -func (p *publisher) notify(subject string) { - for _, observer := range p.observerList { - observer.OnUpdate(subject) - } +// Register adds an observer channel to the channelList +func (p *publisher) Register(channel chan string) { + p.observers = append(p.observers, channel) } type event struct { @@ -85,32 +59,81 @@ func (e *event) Remove(events ...string) { } } -// pooler continuously polls the dirty paramters +var p *pooler + +// pooler continuously polls the dirty parameters // it is expected to be run in a goroutine type pooler struct { - k *kind - run bool - event *event + k *kind + run bool + event *event + pdirtyDone chan bool + mdirtyDone chan bool + midiDone chan bool + ldirtyDone chan bool publisher } func newPooler(k *kind) *pooler { - p := &pooler{ - k: k, - run: true, - event: newEvent(), + if p == nil { + p = &pooler{ + k: k, + run: true, + event: newEvent(), + pdirtyDone: make(chan bool), + mdirtyDone: make(chan bool), + midiDone: make(chan bool), + ldirtyDone: make(chan bool), + } + go p.done() + go p.parameters() + go p.macrobuttons() + go p.midi() + go p.levels() } - go p.parameters() - go p.macrobuttons() - go p.midi() - go p.levels() return p } +func (p *pooler) done() { + for { + select { + case _, ok := <-p.pdirtyDone: + if !ok { + p.pdirtyDone = nil + } + case _, ok := <-p.mdirtyDone: + if !ok { + p.mdirtyDone = nil + } + case _, ok := <-p.midiDone: + if !ok { + p.midiDone = nil + } + case _, ok := <-p.ldirtyDone: + if !ok { + p.ldirtyDone = nil + } + } + if p.pdirtyDone == nil && p.mdirtyDone == nil && p.midiDone == nil && p.ldirtyDone == nil { + for _, ch := range p.observers { + close(ch) + } + break + } + } +} + func (p *pooler) parameters() { for p.run { - if p.event.pdirty && pdirty() { - p.notify("pdirty") + pdirty, err := pdirty() + if err != nil { + close(p.pdirtyDone) + break + } + if p.event.pdirty && pdirty { + for _, ch := range p.observers { + ch <- "pdirty" + } } time.Sleep(33 * time.Millisecond) } @@ -118,8 +141,15 @@ func (p *pooler) parameters() { func (p *pooler) macrobuttons() { for p.run { - if p.event.mdirty && mdirty() { - p.notify("mdirty") + mdirty, err := mdirty() + if err != nil { + close(p.mdirtyDone) + break + } + if p.event.mdirty && mdirty { + for _, ch := range p.observers { + ch <- "mdirty" + } } time.Sleep(33 * time.Millisecond) } @@ -127,8 +157,15 @@ func (p *pooler) macrobuttons() { func (p *pooler) midi() { for p.run { - if p.event.midi && getMidiMessage() { - p.notify("midi") + midi, err := getMidiMessage() + if err != nil { + close(p.midiDone) + break + } + if p.event.midi && midi { + for _, ch := range p.observers { + ch <- "midi" + } } time.Sleep(33 * time.Millisecond) } @@ -138,10 +175,17 @@ func (p *pooler) levels() { _levelCache = newLevelCache(p.k) for p.run { - if p.event.ldirty && ldirty(p.k) { + ldirty, err := ldirty(p.k) + if err != nil { + close(p.ldirtyDone) + break + } + if p.event.ldirty && ldirty { update(_levelCache.stripLevels, _levelCache.stripLevelsBuff, (2*p.k.PhysIn)+(8*p.k.VirtIn)) update(_levelCache.busLevels, _levelCache.busLevelsBuff, 8*p.k.NumBus()) - p.notify("ldirty") + for _, ch := range p.observers { + ch <- "ldirty" + } } time.Sleep(33 * time.Millisecond) } diff --git a/remote.go b/remote.go index b809f86..eab6b8b 100644 --- a/remote.go +++ b/remote.go @@ -35,7 +35,7 @@ func (r *Remote) Login() error { if err != nil { return err } - r.pooler = newPooler(r.Kind) + r.InitPooler() return nil } @@ -50,6 +50,11 @@ func (r *Remote) Logout() error { return nil } +// InitPooler initiates the Pooler +func (r *Remote) InitPooler() { + r.pooler = newPooler(r.Kind) +} + // Type returns the type of Voicemeeter (basic, banana, potato) func (r *Remote) Type() string { val, err := getVMType() @@ -69,20 +74,21 @@ func (r *Remote) Version() string { } // Pdirty returns true iff a parameter value has changed -func (r *Remote) Pdirty() bool { - return pdirty() +func (r *Remote) Pdirty() (bool, error) { + pdirty, err := pdirty() + return pdirty, err } // Mdirty returns true iff a macrobutton value has changed -func (r *Remote) Mdirty() bool { - return mdirty() +func (r *Remote) Mdirty() (bool, error) { + mdirty, err := mdirty() + return mdirty, err } // Sync is a helper method that waits for dirty parameters to clear func (r *Remote) Sync() { time.Sleep(time.Duration(vmdelay) * time.Millisecond) - for r.Pdirty() || r.Mdirty() { - } + clear() } // GetFloat gets a float parameter value @@ -131,13 +137,8 @@ func (r *Remote) SendText(script string) error { } // Register forwards the register method to Pooler -func (r *Remote) Register(o observer) { - r.pooler.Register(o) -} - -// Deregister forwards the deregister method to Pooler -func (r *Remote) Deregister(o observer) { - r.pooler.Deregister(o) +func (r *Remote) Register(channel chan string) { + r.pooler.Register(channel) } // EventAdd adds events to the Pooler @@ -346,7 +347,7 @@ func init() { // NewRemote returns a Remote type for a kind // this is the interface entry point func NewRemote(kindId string, delay int) (*Remote, error) { - _kind, ok := kindMap[kindId] + kind, ok := kindMap[kindId] if !ok { err := fmt.Errorf("unknown Voicemeeter kind '%s'", kindId) return nil, err @@ -359,13 +360,13 @@ func NewRemote(kindId string, delay int) (*Remote, error) { vmdelay = delay director := director{} - switch _kind.Name { + switch kind.Name { case "basic": - director.SetBuilder(&basicBuilder{genericBuilder{_kind, Remote{}}}) + director.SetBuilder(&basicBuilder{genericBuilder{kind, Remote{}}}) case "banana": - director.SetBuilder(&bananaBuilder{genericBuilder{_kind, Remote{}}}) + director.SetBuilder(&bananaBuilder{genericBuilder{kind, Remote{}}}) case "potato": - director.SetBuilder(&potatoBuilder{genericBuilder{_kind, Remote{}}}) + director.SetBuilder(&potatoBuilder{genericBuilder{kind, Remote{}}}) } director.Construct() return director.Get(), nil