mirror of
https://github.com/onyx-and-iris/voicemeeter.git
synced 2024-11-21 04:10:56 +00:00
package module moved into root of repository.
example in readme updated. level pooler implemented, runs in its own goroutine. Remote type now exported observers example updated.
This commit is contained in:
parent
f16bed893f
commit
70d69f5599
@ -41,7 +41,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/onyx-and-iris/voicemeeter-api-go/voicemeeter"
|
||||
"github.com/onyx-and-iris/voicemeeter-api-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -112,6 +112,21 @@ func mdirty() bool {
|
||||
return int(res) == 1
|
||||
}
|
||||
|
||||
func ldirty(k *kind) bool {
|
||||
_levelCache.stripLevelsBuff = make([]float32, (2*k.physIn)+(8*k.virtIn))
|
||||
_levelCache.busLevelsBuff = make([]float32, 8*k.numBus())
|
||||
|
||||
for i := 0; i < (2*k.physIn)+(8*k.virtIn); i++ {
|
||||
_levelCache.stripLevelsBuff[i] = float32(getLevel(_levelCache.stripMode, i))
|
||||
_levelCache.stripComp[i] = _levelCache.stripLevelsBuff[i] == _levelCache.stripLevels[i]
|
||||
}
|
||||
for i := 0; i < 8*k.numBus(); i++ {
|
||||
_levelCache.busLevelsBuff[i] = float32(getLevel(3, i))
|
||||
_levelCache.busComp[i] = _levelCache.busLevelsBuff[i] == _levelCache.busLevels[i]
|
||||
}
|
||||
return !(allTrue(_levelCache.stripComp, (2*k.physIn)+(8*k.virtIn)) && allTrue(_levelCache.busComp, 8*k.numBus()))
|
||||
}
|
||||
|
||||
// getVMType returns the type of Voicemeeter, as a string
|
||||
func getVMType() string {
|
||||
var type_ uint64
|
@ -253,13 +253,13 @@ func (bm *busMode) GetRearOnly() bool {
|
||||
|
||||
func newBusLevels(i int, k *kind) levels {
|
||||
init := i * 8
|
||||
return levels{iRemote{fmt.Sprintf("bus[%d]", i), i}, k, init, 8}
|
||||
return levels{iRemote{fmt.Sprintf("bus[%d]", i), i}, k, init, 8, "bus"}
|
||||
}
|
||||
|
||||
func (l *levels) All() []float32 {
|
||||
var levels []float32
|
||||
for i := l.init; i < l.init+l.offset; i++ {
|
||||
levels = append(levels, l.convertLevel(getLevel(3, i)))
|
||||
levels = append(levels, l.convertLevel(_levelCache.busLevels[i]))
|
||||
}
|
||||
return levels
|
||||
}
|
@ -2,37 +2,45 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/onyx-and-iris/voicemeeter-api-go/voicemeeter"
|
||||
"github.com/onyx-and-iris/voicemeeter-api-go"
|
||||
)
|
||||
|
||||
type observer struct {
|
||||
i int
|
||||
vm *voicemeeter.Remote
|
||||
}
|
||||
|
||||
func (o observer) OnUpdate(subject string) {
|
||||
fmt.Println(o.i, subject)
|
||||
if strings.Compare(subject, "pdirty") == 0 {
|
||||
fmt.Println("pdirty!")
|
||||
}
|
||||
if strings.Compare(subject, "mdirty") == 0 {
|
||||
fmt.Println("mdirty!")
|
||||
}
|
||||
if strings.Compare(subject, "ldirty") == 0 {
|
||||
fmt.Printf("%v %v %v %v %v %v %v %v\n",
|
||||
o.vm.Bus[0].Levels().IsDirty(),
|
||||
o.vm.Bus[1].Levels().IsDirty(),
|
||||
o.vm.Bus[2].Levels().IsDirty(),
|
||||
o.vm.Bus[3].Levels().IsDirty(),
|
||||
o.vm.Bus[4].Levels().IsDirty(),
|
||||
o.vm.Bus[5].Levels().IsDirty(),
|
||||
o.vm.Bus[6].Levels().IsDirty(),
|
||||
o.vm.Bus[7].Levels().IsDirty(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
vmRem := voicemeeter.GetRemote("banana")
|
||||
vmRem := voicemeeter.GetRemote("potato")
|
||||
vmRem.Login()
|
||||
|
||||
o := observer{1}
|
||||
o2 := observer{2}
|
||||
o3 := observer{3}
|
||||
o4 := observer{4}
|
||||
o := observer{vmRem}
|
||||
vmRem.Register(o)
|
||||
vmRem.Register(o2)
|
||||
vmRem.Register(o3)
|
||||
vmRem.Register(o4)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
vmRem.Deregister(o2)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
time.Sleep(30 * time.Second)
|
||||
vmRem.Deregister(o)
|
||||
|
||||
vmRem.Logout()
|
||||
}
|
||||
|
4
go.mod
4
go.mod
@ -3,8 +3,8 @@ module github.com/onyx-and-iris/voicemeeter-api-go
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/stretchr/testify v1.7.5
|
||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664
|
||||
github.com/stretchr/testify v1.8.0
|
||||
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d
|
||||
)
|
||||
|
||||
require (
|
||||
|
8
go.sum
8
go.sum
@ -6,10 +6,10 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q=
|
||||
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664 h1:wEZYwx+kK+KlZ0hpvP2Ls1Xr4+RWnlzGFwPP0aiDjIU=
|
||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I=
|
||||
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@ -2,7 +2,6 @@ package voicemeeter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// iRemote provides a set of common forwarding methods
|
||||
@ -68,18 +67,3 @@ func (ir *iRemote) setter_string(p, v string) {
|
||||
param := fmt.Sprintf("%s.%s", ir.identifier(), p)
|
||||
setParameterString(param, v)
|
||||
}
|
||||
|
||||
type levels struct {
|
||||
iRemote
|
||||
k *kind
|
||||
init int
|
||||
offset int
|
||||
}
|
||||
|
||||
func (l *levels) convertLevel(i float32) float32 {
|
||||
if i > 0 {
|
||||
val := 20 * math.Log10(float64(i))
|
||||
return float32(val)
|
||||
}
|
||||
return -200.0
|
||||
}
|
45
levels.go
Normal file
45
levels.go
Normal file
@ -0,0 +1,45 @@
|
||||
package voicemeeter
|
||||
|
||||
import "math"
|
||||
|
||||
// levels
|
||||
type levels struct {
|
||||
iRemote
|
||||
k *kind
|
||||
init int
|
||||
offset int
|
||||
id string
|
||||
}
|
||||
|
||||
func (l *levels) convertLevel(i float32) float32 {
|
||||
if i > 0 {
|
||||
val := 20 * math.Log10(float64(i))
|
||||
return float32(roundFloat(float64(val), 1))
|
||||
}
|
||||
return -200.0
|
||||
}
|
||||
|
||||
var _levelCache *levelCache
|
||||
|
||||
// levelCache defines level slices used by the pooler to track updates
|
||||
type levelCache struct {
|
||||
stripMode int
|
||||
stripLevels []float32
|
||||
busLevels []float32
|
||||
stripLevelsBuff []float32
|
||||
busLevelsBuff []float32
|
||||
stripComp []bool
|
||||
busComp []bool
|
||||
}
|
||||
|
||||
// newLevelCache returns a levelCache struct address
|
||||
func newLevelCache(k *kind) *levelCache {
|
||||
stripLevels := make([]float32, (2*k.physIn)+(8*k.virtIn))
|
||||
busLevels := make([]float32, 8*k.numBus())
|
||||
stripComp := make([]bool, (2*k.physIn)+(8*k.virtIn))
|
||||
busComp := make([]bool, 8*k.numBus())
|
||||
if _levelCache == nil {
|
||||
_levelCache = &levelCache{stripMode: 0, stripLevels: stripLevels, busLevels: busLevels, stripComp: stripComp, busComp: busComp}
|
||||
}
|
||||
return _levelCache
|
||||
}
|
@ -43,15 +43,18 @@ func (p *publisher) notify(subject string) {
|
||||
// pooler continuously polls the dirty paramters
|
||||
// it is expected to be run in a goroutine
|
||||
type pooler struct {
|
||||
k *kind
|
||||
run bool
|
||||
publisher
|
||||
}
|
||||
|
||||
func newPooler() *pooler {
|
||||
func newPooler(k *kind) *pooler {
|
||||
p := &pooler{
|
||||
k: k,
|
||||
run: true,
|
||||
}
|
||||
go p.runner()
|
||||
go p.levels()
|
||||
return p
|
||||
}
|
||||
|
||||
@ -66,3 +69,16 @@ func (p *pooler) runner() {
|
||||
time.Sleep(33 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pooler) levels() {
|
||||
_levelCache = newLevelCache(p.k)
|
||||
|
||||
for p.run {
|
||||
if ldirty(p.k) {
|
||||
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")
|
||||
}
|
||||
time.Sleep(33 * time.Millisecond)
|
||||
}
|
||||
}
|
@ -5,9 +5,8 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// A remote type represents the API for a kind,
|
||||
// comprised of slices representing each member
|
||||
type remote struct {
|
||||
// A Remote type represents the API for a kind
|
||||
type Remote struct {
|
||||
kind *kind
|
||||
Strip []t_strip
|
||||
Bus []t_bus
|
||||
@ -21,53 +20,53 @@ type remote struct {
|
||||
}
|
||||
|
||||
// String implements the fmt.stringer interface
|
||||
func (r *remote) String() string {
|
||||
func (r *Remote) String() string {
|
||||
return fmt.Sprintf("Voicemeeter %s", r.kind)
|
||||
}
|
||||
|
||||
// Login logs into the API
|
||||
// then it intializes the pooler
|
||||
func (r *remote) Login() {
|
||||
r.pooler = newPooler()
|
||||
func (r *Remote) Login() {
|
||||
login(r.kind.name)
|
||||
r.pooler = newPooler(r.kind)
|
||||
}
|
||||
|
||||
// Logout logs out of the API
|
||||
// it also terminates the pooler
|
||||
func (r *remote) Logout() {
|
||||
func (r *Remote) Logout() {
|
||||
r.pooler.run = false
|
||||
logout()
|
||||
}
|
||||
|
||||
func (r *remote) Type() string {
|
||||
func (r *Remote) Type() string {
|
||||
return getVMType()
|
||||
}
|
||||
|
||||
func (r *remote) Version() string {
|
||||
func (r *Remote) Version() string {
|
||||
return getVersion()
|
||||
}
|
||||
|
||||
// Pdirty returns true iff a parameter value has changed
|
||||
func (r *remote) Pdirty() bool {
|
||||
func (r *Remote) Pdirty() bool {
|
||||
return pdirty()
|
||||
}
|
||||
|
||||
// Mdirty returns true iff a macrobutton value has changed
|
||||
func (r *remote) Mdirty() bool {
|
||||
func (r *Remote) Mdirty() bool {
|
||||
return mdirty()
|
||||
}
|
||||
|
||||
func (r *remote) SendText(script string) {
|
||||
func (r *Remote) SendText(script string) {
|
||||
setParametersMulti(script)
|
||||
}
|
||||
|
||||
// Register forwards the register method to Pooler
|
||||
func (r *remote) Register(o observer) {
|
||||
func (r *Remote) Register(o observer) {
|
||||
r.pooler.Register(o)
|
||||
}
|
||||
|
||||
// Register forwards the deregister method to Pooler
|
||||
func (r *remote) Deregister(o observer) {
|
||||
func (r *Remote) Deregister(o observer) {
|
||||
r.pooler.Deregister(o)
|
||||
}
|
||||
|
||||
@ -81,7 +80,7 @@ type remoteBuilder interface {
|
||||
makeDevice() remoteBuilder
|
||||
makeRecorder() remoteBuilder
|
||||
Build() remoteBuilder
|
||||
Get() *remote
|
||||
Get() *Remote
|
||||
}
|
||||
|
||||
// directory is responsible for directing the genericBuilder
|
||||
@ -100,13 +99,13 @@ func (d *director) Construct() {
|
||||
}
|
||||
|
||||
// Get forwards the Get method to the builder
|
||||
func (d *director) Get() *remote {
|
||||
func (d *director) Get() *Remote {
|
||||
return d.builder.Get()
|
||||
}
|
||||
|
||||
type genericBuilder struct {
|
||||
k *kind
|
||||
r remote
|
||||
r Remote
|
||||
}
|
||||
|
||||
func (b *genericBuilder) setKind() remoteBuilder {
|
||||
@ -157,28 +156,28 @@ func (b *genericBuilder) makeButton() remoteBuilder {
|
||||
return b
|
||||
}
|
||||
|
||||
// makeCommand makes a Command type and assigns it to remote.Command
|
||||
// makeCommand makes a command type and assigns it to remote.Command
|
||||
func (b *genericBuilder) makeCommand() remoteBuilder {
|
||||
fmt.Println("building command")
|
||||
b.r.Command = newCommand()
|
||||
return b
|
||||
}
|
||||
|
||||
// makeVban makes a Vban type and assigns it to remote.Vban
|
||||
// makeVban makes a vban type and assigns it to remote.Vban
|
||||
func (b *genericBuilder) makeVban() remoteBuilder {
|
||||
fmt.Println("building vban")
|
||||
b.r.Vban = newVban(b.k)
|
||||
return b
|
||||
}
|
||||
|
||||
// makeVban makes a Vban type and assigns it to remote.Vban
|
||||
// makeDevice makes a device type and assigns it to remote.Device
|
||||
func (b *genericBuilder) makeDevice() remoteBuilder {
|
||||
fmt.Println("building device")
|
||||
b.r.Device = newDevice()
|
||||
return b
|
||||
}
|
||||
|
||||
// makeRecorder makes a recorder type and assigns it to remote.Vban
|
||||
// makeRecorder makes a recorder type and assigns it to remote.Recorder
|
||||
func (b *genericBuilder) makeRecorder() remoteBuilder {
|
||||
fmt.Println("building recorder")
|
||||
b.r.Recorder = newRecorder()
|
||||
@ -186,7 +185,7 @@ func (b *genericBuilder) makeRecorder() remoteBuilder {
|
||||
}
|
||||
|
||||
// Get returns a fully constructed remote type for a kind
|
||||
func (b *genericBuilder) Get() *remote {
|
||||
func (b *genericBuilder) Get() *Remote {
|
||||
return &b.r
|
||||
}
|
||||
|
||||
@ -217,9 +216,9 @@ func (potb *potatoBuilder) Build() remoteBuilder {
|
||||
return potb.setKind().makeStrip().makeBus().makeButton().makeCommand().makeVban().makeDevice().makeRecorder()
|
||||
}
|
||||
|
||||
// GetRemote returns a remote type for a kind
|
||||
// GetRemote returns a Remote type for a kind
|
||||
// this is the interface entry point
|
||||
func GetRemote(kindId string) *remote {
|
||||
func GetRemote(kindId string) *Remote {
|
||||
_kind, ok := kindMap[kindId]
|
||||
if !ok {
|
||||
err := fmt.Errorf("unknown Voicemeeter kind '%s'", kindId)
|
||||
@ -230,11 +229,11 @@ func GetRemote(kindId string) *remote {
|
||||
director := director{}
|
||||
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()
|
@ -256,29 +256,42 @@ func newStripLevels(i int, k *kind) levels {
|
||||
init = (k.physIn * 2) + ((i - k.physIn) * 8)
|
||||
os = 8
|
||||
}
|
||||
return levels{iRemote{fmt.Sprintf("strip[%d]", i), i}, k, init, os}
|
||||
return levels{iRemote{fmt.Sprintf("strip[%d]", i), i}, k, init, os, "strip"}
|
||||
}
|
||||
|
||||
func (l *levels) PreFader() []float32 {
|
||||
_levelCache.stripMode = 0
|
||||
var levels []float32
|
||||
for i := l.init; i < l.init+l.offset; i++ {
|
||||
levels = append(levels, l.convertLevel(getLevel(0, i)))
|
||||
levels = append(levels, l.convertLevel(_levelCache.stripLevels[i]))
|
||||
}
|
||||
return levels
|
||||
}
|
||||
|
||||
func (l *levels) PostFader() []float32 {
|
||||
_levelCache.stripMode = 1
|
||||
var levels []float32
|
||||
for i := l.init; i < l.init+l.offset; i++ {
|
||||
levels = append(levels, l.convertLevel(getLevel(1, i)))
|
||||
levels = append(levels, l.convertLevel(_levelCache.stripLevels[i]))
|
||||
}
|
||||
return levels
|
||||
}
|
||||
|
||||
func (l *levels) PostMute() []float32 {
|
||||
_levelCache.stripMode = 2
|
||||
var levels []float32
|
||||
for i := l.init; i < l.init+l.offset; i++ {
|
||||
levels = append(levels, l.convertLevel(getLevel(2, i)))
|
||||
levels = append(levels, l.convertLevel(_levelCache.stripLevels[i]))
|
||||
}
|
||||
return levels
|
||||
}
|
||||
|
||||
func (l *levels) IsDirty() bool {
|
||||
var vals []bool
|
||||
if l.id == "strip" {
|
||||
vals = _levelCache.stripComp[l.init : l.init+l.offset]
|
||||
} else if l.id == "bus" {
|
||||
vals = _levelCache.busComp[l.init : l.init+l.offset]
|
||||
}
|
||||
return !allTrue(vals, l.offset)
|
||||
}
|
@ -5,7 +5,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/onyx-and-iris/voicemeeter-api-go/voicemeeter"
|
||||
"github.com/onyx-and-iris/voicemeeter-api-go"
|
||||
)
|
||||
|
||||
var (
|
||||
|
24
util.go
Normal file
24
util.go
Normal file
@ -0,0 +1,24 @@
|
||||
package voicemeeter
|
||||
|
||||
import "math"
|
||||
|
||||
// allTrue accepts a boolean slice and evaluates if all elements are True
|
||||
func allTrue(s []bool, sz int) bool {
|
||||
for i := 0; i < sz; i++ {
|
||||
if !s[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func update(s1 []float32, s2 []float32, sz int) {
|
||||
for i := 0; i < sz; i++ {
|
||||
s1[i] = s2[i]
|
||||
}
|
||||
}
|
||||
|
||||
func roundFloat(val float64, precision uint) float64 {
|
||||
ratio := math.Pow(10, float64(precision))
|
||||
return math.Round(val*ratio) / ratio
|
||||
}
|
Loading…
Reference in New Issue
Block a user