From c4f00a3dbd8254dec87c10e6556adbcceebce521 Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Thu, 8 Dec 2022 10:50:00 +0000 Subject: [PATCH] pooler now accepts channels done channels used to clean up pooler if GUI goes down InitPooler() added to Remote type, useful for reinitiating the Pooler. For this reason Pooler now defined as singleton type. --- publisher.go | 146 +++++++++++++++++++++++++++++++++------------------ remote.go | 39 +++++++------- 2 files changed, 115 insertions(+), 70 deletions(-) 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