Compare commits

..

No commits in common. "dev" and "v1.8.0" have entirely different histories.
dev ... v1.8.0

29 changed files with 900 additions and 1774 deletions

4
.gitignore vendored
View File

@ -19,7 +19,3 @@ config.toml
# Dependency directories (remove the comment below to include it)
# vendor/
# work files
go.work
go.work.sum

View File

@ -9,171 +9,121 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
## [Unreleased]
- [x]
## [2.1.0] - 2024-07-01
### Added
- Added a configurable login timeout in seconds (defaults to 2).
- Option function added for overriding the type of Voicemeeter GUI runVoicemeeter() will launch.
- Explanation of Option functions added to README.
### Changed
- runVoicemeeter() now launches x64 GUIs for all kinds if on a 64 bit system.
- this can be overridden to force 32 bit GUI using voicemeeter.WithBits(32) Option function
## [2.0.0] - 2022-10-25
V2 introduces some breaking changes.
### Changed
- Removed Get prefix from getters in Bus, Strip, Vban, Button and Output types.
- Pooler now communicates event updates over a channel.
- strip.comp now references comp struct type. (see readme for changes in setting comp parameters)
- strip.gate now references gate struct type. (see readme for changes in setting gate parameters)
- strip.eq, bus.eq now reference eQ struct type. (see readme for changes in setting eq parameters)
- All examples and tests have been updated to reflect the changes.
### Added
- denoiser type to strip types.
- XY parameters added to strip type
- extra logging added to getters/setters in iRemote type.
- InitPooler to Remote type in case the Pooler needs reinitiating. (perhaps the GUI closed unexpectedly)
### Fixed
- Functions that wrap CAPI calls in base.go now return correct error values.
## [1.11.0] - 2022-10-10
### Fixed
- type error in getLevel
## [1.8.0] - 2022-09-17
### Added
- vm-cli example added + example README
- Fade, App methods added to project README
- [x]
## [1.7.0] - 2022-09-14
### Added
- voicemeeter.NewRemote now accepts a delay int argument (milliseconds).
- vm.Sync() can now be used to force the dirty parameters to clear.
- voicemeeter.NewRemote now accepts a delay int argument (milliseconds).
- vm.Sync() can now be used to force the dirty parameters to clear.
### Changed
- higher level methods/functions now accept/return float64
- tests updated to reflect changes.
- higher level methods/functions now accept/return float64
- tests updated to reflect changes.
## [1.5.0] - 2022-09-07
### Changed
- changes to error handling.
- functions that wrap capi calls now return error types.
- higher level functions print error messages
- changes to error handling.
- functions that wrap capi calls now return error types.
- higher level functions print error messages
## [1.4.0] - 2022-08-22
### Added
- midi type, supports midi devices
- midi updates added to the pooler
- event type, supports toggling event updates through EventAdd() and EventRemove() methods.
- Forwarder methods for get/set float/string parameters added to Remote type
- Midi, Events sections added to README.
- midi type, supports midi devices
- midi updates added to the pooler
- event type, supports toggling event updates through EventAdd() and EventRemove() methods.
- Forwarder methods for get/set float/string parameters added to Remote type
- Midi, Events sections added to README.
### Changed
- macrobutton updates moved into its own goroutine
- observer example updated to include midi updates
- level updates are now disabled by default, should be enabled explicitly
- macrobutton updates moved into its own goroutine
- observer example updated to include midi updates
- level updates are now disabled by default, should be enabled explicitly
## [1.2.0] - 2022-07-10
### Added
- docstrings added to types, methods and functions
- version retractions added to go.mod
- docstrings added to types, methods and functions
- version retractions added to go.mod
### Changed
- Entry method renamed from GetRemote to NewRemote
- Readme updated to reflect latest changes
- Entry method renamed from GetRemote to NewRemote
- Readme updated to reflect latest changes
## [1.1.0] - 2022-06-30
### Added
- Level updates implemented in Pooler struct. Runs in its own goroutine.
- Level updates implemented in Pooler struct. Runs in its own goroutine.
### Fixed
- Fixed bug with identifier in outputs struct.
- Fixed bug with identifier in outputs struct.
### Changed
- Package files moved into root of repository.
- Remote struct now exported type
- Package files moved into root of repository.
- Remote struct now exported type
## [1.0.0] - 2022-06-30
### Added
- recorder, device structs implemented
- gainlayers field in strip struct implemented
- levels field in strip, bus structs implemented
- pooler ratelimit set at 33ms
- recorder, device structs implemented
- gainlayers field in strip struct implemented
- levels field in strip, bus structs implemented
- pooler ratelimit set at 33ms
## [0.0.3] - 2022-06-25
### Added
- pre-commit.ps1 added for use with git hook
- unit tests for factory functions added
- vban parameter methods added
- support for observers added. publisher/observer structs defined
- Pooler struct added, pdirty, mdirty now updated continously in a goroutine
- pre-commit.ps1 added for use with git hook
- unit tests for factory functions added
- vban parameter methods added
- support for observers added. publisher/observer structs defined
- Pooler struct added, pdirty, mdirty now updated continously in a goroutine
### Changed
- NewRemote factory method now uses director, builder types to create Remote types.
- cdll renamed to path
- test suite now using testify/assert
- NewRemote factory method now uses director, builder types to create Remote types.
- cdll renamed to path
- test suite now using testify/assert
## [0.0.2] - 2022-06-23
### Added
- physicalStrip, virtualStrip, physicalBus and virtualBus types defined.
- factory methods for strip, bus now cast return values to interface types.
- parameter methods added to strip, bus types.
- command struct implemented
- bus, vban unit tests added
- physicalStrip, virtualStrip, physicalBus and virtualBus types defined.
- factory methods for strip, bus now cast return values to interface types.
- parameter methods added to strip, bus types.
- command struct implemented
- bus, vban unit tests added
### Changed
- strip, bus slices in remote type defined as interface slice types.
- bindings in base now prepended with vm.
- vban fields added to kind structs
- strip, bus slices in remote type defined as interface slice types.
- bindings in base now prepended with vm.
- vban fields added to kind structs
## [0.0.1] - 2022-06-22
### Added
- interface entry point defined in remote
- some base functions are exported through forwarding methods in Remote type (Login, Logout etc)
- wrapper around the CAPI defined in base
- path helper functions defined in cdll
- kind structs defined in kinds. These describe the layout for each version.
- channel, strip, bus structs getter/setter procedures defined.
- button struct fully implemented.
- initial test commit
- interface entry point defined in remote
- some base functions are exported through forwarding methods in Remote type (Login, Logout etc)
- wrapper around the CAPI defined in base
- path helper functions defined in cdll
- kind structs defined in kinds. These describe the layout for each version.
- channel, strip, bus structs getter/setter procedures defined.
- button struct fully implemented.
- initial test commit

412
README.md
View File

@ -1,28 +1,31 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/onyx-and-iris/voicemeeter.svg)](https://pkg.go.dev/github.com/onyx-and-iris/voicemeeter/v2)
[![Go Reference](https://pkg.go.dev/badge/github.com/onyx-and-iris/voicemeeter-api-go.svg)](https://pkg.go.dev/github.com/onyx-and-iris/voicemeeter-api-go)
# A Go Wrapper for the Voicemeeter API
# A Go Wrapper for Voicemeeter API
This package offers a Go interface for the Voicemeeter Remote C API.
For an outline of past/future changes refer to: [CHANGELOG](CHANGELOG.md)
## Tested against
- Basic 1.1.1.1
- Banana 2.1.1.1
- Potato 3.1.1.1
- Basic 1.0.8.4
- Banana 2.0.6.4
- Potato 3.0.2.4
## Requirements
- [Voicemeeter](https://voicemeeter.com/)
- Go 1.18 or greater
- [Voicemeeter](https://voicemeeter.com/)
- Go 1.18 or greater
## Installation
Initialize your own module then `go get`
#### GO GET
```
go mod init github.com/x/y
go get github.com/onyx-and-iris/voicemeeter/v2
```
Install voicemeeter-api-go package from your console to download the latest version.
`go get github.com/onyx-and-iris/voicemeeter-api-go`
or add it to your `go.mod` file.
## `Use`
@ -35,7 +38,7 @@ import (
"fmt"
"log"
"github.com/onyx-and-iris/voicemeeter/v2"
"github.com/onyx-and-iris/voicemeeter-api-go"
)
func main() {
@ -47,11 +50,11 @@ func main() {
vm.Strip[0].SetLabel("rode podmic")
vm.Strip[0].SetMute(true)
fmt.Printf("Strip 0 (%s) mute was set to %v\n", vm.Strip[0].Label(), vm.Strip[0].Mute())
fmt.Printf("Strip 0 (%s) mute was set to %v\n", vm.Strip[0].GetLabel(), vm.Strip[0].GetMute())
}
func vmConnect() (*voicemeeter.Remote, error) {
vm, err := voicemeeter.NewRemote("banana", 20)
vm, err := voicemeeter.NewRemote("banana", 15)
if err != nil {
return nil, err
}
@ -65,33 +68,21 @@ func vmConnect() (*voicemeeter.Remote, error) {
}
```
## `voicemeeter.NewRemote(<kindId>, <delay>, opts ...Option)`
## `voicemeeter.NewRemote(<kindId>, <delay>)`
### `kindId`
Pass the kind of Voicemeeter as an argument. kindId may be:
- `basic`
- `banana`
- `potato`
- `basic`
- `banana`
- `potato`
### `delay`
Pass a delay in milliseconds to force the getters to wait for dirty parameters to clear.
Useful if not listening for event updates.
### `voicemeeter.WithTimeout(timeout int)`
Set a login timeout, defaults to 2 seconds. For example to set it to 1s:
`voicemeeter.NewRemote("banana", 20, voicemeeter.WithTimeout(1))`
### `voicemeeter.WithBits(bits int)`
Override the type of Voicemeeter GUI to launch on 64 bit systems. For example, to force 32 bit GUI:
`voicemeeter.NewRemote("banana", 20, voicemeeter.WithBits(32))`
Useful if not running callbacks.
## `Remote Type`
@ -157,19 +148,19 @@ sets many parameters in script format eg. ("Strip[0].Mute=1;Bus[3].Gain=3.6")
#### `vm.Register(o observer)`
register an observer type as an observer
register an object as an observer
#### `vm.Deregister(o observer)`
deregister an observer type as an observer
deregister an object as an observer
#### `vm.EventAdd(<events>)`
#### `vm.EventAdd(<event>)`
adds a single or multiple events to the pooler. Accepts a string or slice of strings.
adds an event to the pooler eg. vm.EventAdd("ldirty")
#### `vm.EventRemove(<events>)`
#### `vm.EventRemove(<event>)`
removes a single or multiple events from the pooler. Accepts a string or slice of strings.
removes an event to the pooler eg. vm.EventRemove("pdirty")
#### `vm.Pdirty()`
@ -189,121 +180,50 @@ Use this to force dirty parameters to clear after a delay in milliseconds.
The following methods are available
- `Mute() bool`
- `SetMute(val bool)`
- `Mono() bool`
- `SetMono(val bool)`
- `Solo() bool`
- `SetSolo(val bool)`
- `Limit() int`
- `SetLimit(val int)` from -40 to 12
- `Label() string`
- `SetLabel(val string)`
- `Gain() float64`
- `SetGain(val float64)` from -60.0 to 12.0
- `Mc() bool`
- `SetMc(val bool)`
- `Audibility() float64`
- `SetAudibility(val float64)` from 0.0 to 10.0
- `A1() bool - A5() bool`
- `SetA1(val bool) - SetA5(val bool)`
- `B1() bool - B3() bool`
- `SetB1(val bool) bool - SetB3(val bool) bool`
- `AppGain(name string, gain float64)`
- `AppMute(name string, val bool)`
- `GetMute() bool`
- `SetMute(val bool)`
- `GetMono() bool`
- `SetMono(val bool)`
- `GetSolo() bool`
- `SetSolo(val bool)`
- `GetLimit() int`
- `SetLimit(val int)` from -40 to 12
- `GetLabel() string`
- `SetLabel(val string)`
- `GetGain() float64`
- `SetGain(val float64)` from -60.0 to 12.0
- `GetMc() bool`
- `SetMc(val bool)`
- `GetComp() float64`
- `SetComp(val float64)` from 0.0 to 10.0
- `GetGate() float64`
- `SetGate(val float64)` from 0.0 to 10.0
- `GetAudibility() float64`
- `SetAudibility(val float64)` from 0.0 to 10.0
- `GetA1() bool - GetA5() bool`
- `SetA1(val bool) - SetA5(val bool)`
- `AppGain(name string, gain float64)`
- `AppMute(name string, val bool)`
example:
```go
vm.Strip[3].SetGain(3.7)
fmt.Println(vm.Strip[0].Label())
fmt.Println(vm.Strip[0].GetLabel())
vm.Strip[4].SetA1(true)
vm.Strip[5].AppGain("Spotify", 0.5)
vm.Strip[5].AppMute("Spotify", true)
```
##### Comp
##### Gainlayers
- `vm.Strip[i].Comp()`
- `vm.Strip[i].GainLayer()[j]`
The following methods are available
- `Knob() float64`
- `SetKnob(val float64)` from 0.0 to 10.0
- `GainIn() float64`
- `SetGainIn(val float64)` from -24.0 to 24.0
- `Ratio() float64`
- `SetRatio(val float64)` from 1.0 to 8.0
- `Threshold() float64`
- `SetThreshold(val float64)` from -40.0 to -3.0
- `Attack() float64`
- `SetAttack(val float64)` from 0.0 to 200.0
- `Release() float64`
- `SetRelease(val float64)` from 0.0 to 5000.0
- `Knee() float64`
- `SetKnee(val float64)` from 0.0 to 1.0
- `GainOut() float64`
- `SetGainOut(val float64)` from -24.0 to 24.0
- `MakeUp() bool`
- `SetMakeUp(val bool)`
example:
```go
vm.Strip[3].Comp().SetRatio(3.5)
```
##### Gate
- `vm.Strip[i].Gate()`
The following methods are available
- `Knob() float64`
- `SetKnob(val float64)` from 0.0 to 10.0
- `Threshold() float64`
- `SetThreshold(val float64)` from -60.0 to -10.0
- `Damping() float64`
- `SetDamping(val float64)` from -60.0 to -10.0
- `BPSidechain() int`
- `SetBPSidechain(val int)` from 100 to 4000
- `Attack() float64`
- `SetAttack(val float64)` from 0.0 to 1000.0
- `Hold() float64`
- `SetHold(val float64)` from 0.0 to 5000.0
- `Release() float64`
- `SetRelease(val float64)` from 0.0 to 5000.0
example:
```go
fmt.Println(vm.Strip[4].Gate().Attack())
```
##### Denoiser
- `vm.Strip[i].Denoiser()`
The following methods are available
- `Knob() float64`
- `SetKnob(val float64)` from 0.0 to 10.0
example:
```go
vm.Strip[1].Denoiser().SetKnob(4.2)
```
##### Gainlayer
- `vm.Strip[i].Gainlayer()[j]`
The following methods are available
- `Get() float64`
- `Set(val float64)`
- `Get() float64`
- `Set(val float64)`
example:
@ -313,13 +233,13 @@ vm.Strip[6].GainLayer()[3].Set(-13.6)
##### Levels
- `vm.Strip[i].Levels()`
- `vm.Strip[i].Levels()`
The following methods are available
- `PreFader() []float64`
- `PostFader() []float64`
- `PostMute() []float64`
- `PreFader() []float64`
- `PostFader() []float64`
- `PostMute() []float64`
example:
@ -331,53 +251,53 @@ fmt.Println(vm.Strip[5].Levels().PreFader())
The following methods are available
- `String() string`
- `Mute() bool`
- `SetMute(val bool)`
- `Mono() bool`
- `SetMono(val bool)`
- `Label() string`
- `SetLabel(val string)`
- `Gain() float64`
- `SetGain(val float64)` from -60.0 to 12.0
example:
- `String() string`
- `GetMute() bool`
- `SetMute(val bool)`
- `GetEq() bool`
- `SetEq(val bool)`
- `GetMono() bool`
- `SetMono(val bool)`
- `GetLabel() string`
- `SetLabel(val string)`
- `GetGain() float64`
- `SetGain(val float64)` from -60.0 to 12.0
```go
vm.Bus[3].SetEq(true)
fmt.Println(vm.Bus[0].Label())
fmt.Println(vm.Bus[0].GetLabel())
```
##### Modes
- `vm.Bus[i].Mode()`
- `vm.Bus[i].Mode()`
The following methods are available
- `SetNormal(val bool)`
- `Normal() bool`
- `SetAmix(val bool)`
- `Amix() bool`
- `SetBmix(val bool)`
- `Bmix() bool`
- `SetRepeat(val bool)`
- `Repeat() bool`
- `SetComposite(val bool)`
- `Composite() bool`
- `SetTvMix(val bool)`
- `TvMix() bool`
- `SetUpMix21(val bool)`
- `UpMix21() bool`
- `SetUpMix41(val bool)`
- `UpMix41() bool`
- `SetUpMix61(val bool)`
- `UpMix61() bool`
- `SetCenterOnly(val bool)`
- `CenterOnly() bool`
- `SetLfeOnly(val bool)`
- `LfeOnly() bool`
- `SetRearOnly(val bool)`
- `RearOnly() bool`
- `SetNormal(val bool)`
- `GetNormal() bool`
- `SetAmix(val bool)`
- `GetAmix() bool`
- `SetBmix(val bool)`
- `GetBmix() bool`
- `SetRepeat(val bool)`
- `GetRepeat() bool`
- `SetComposite(val bool)`
- `GetComposite() bool`
- `SetTvMix(val bool)`
- `GetTvMix() bool`
- `SetUpMix21(val bool)`
- `GetUpMix21() bool`
- `SetUpMix41(val bool)`
- `GetUpMix41() bool`
- `SetUpMix61(val bool)`
- `GetUpMix61() bool`
- `SetCenterOnly(val bool)`
- `GetCenterOnly() bool`
- `SetLfeOnly(val bool)`
- `GetLfeOnly() bool`
- `SetRearOnly(val bool)`
- `GetRearOnly() bool`
example:
@ -388,11 +308,11 @@ vm.Bus[4].Mode().SetCenterOnly(true)
##### Levels
- `vm.Bus[i].Levels()`
- `vm.Bus[i].Levels()`
The following methods are available
- `All() []float64`
- `All() []float64`
example:
@ -402,28 +322,10 @@ fmt.Println(vm.Bus[1].Levels().All())
### Strip | Bus
##### EQ
- `vm.Strip[i].Eq()` `vm.Bus[i].Eq()`
The following methods are available.
- `On() bool`
- `SetOn(val bool)`
- `Ab() bool`
- `SetAb(val bool)`
example:
```go
vm.Strip[1].Eq().SetOn(true)
fmt.Println(vm.Bus[3].Eq().Ab())
```
The following methods are available.
- `FadeTo(target float64, time_ int)`: float, int
- `FadeBy(change float64, time_ int)`: float, int
- `FadeTo(target float64, time_ int)`: float, int
- `FadeBy(change float64, time_ int)`: float, int
Modify gain to or by the selected amount in db over a time interval in ms.
@ -438,29 +340,29 @@ vm.Bus[3].FadeTo(-12.8, 500)
The following methods are available
- `State() bool`
- `SetState(val bool)`
- `StateOnly() bool`
- `SetStateOnly(val bool)`
- `Trigger() bool`
- `SetTrigger(val bool)`
- `GetState() bool`
- `SetState(val bool)`
- `GetStateOnly() bool`
- `SetStateOnly(val bool)`
- `GetTrigger() bool`
- `SetTrigger(val bool)`
example:
```go
vm.Button[37].SetState(true)
fmt.Println(vm.Button[64].StateOnly())
fmt.Println(vm.Button[64].GetStateOnly())
```
### Command
The following methods are available
- `Show()` Show Voicemeeter GUI if it's hidden
- `Hide()` Hide Voicemeeter GUI if it's shown
- `Shutdown()` Shuts down the GUI
- `Restart()` Restart the audio engine
- `Lock(val bool)` Lock the Voicemeeter GUI
- `Show()` Show Voicemeeter GUI if it's hidden
- `Hide()` Hide Voicemeeter GUI if it's shown
- `Shutdown()` Shuts down the GUI
- `Restart()` Restart the audio engine
- `Lock(val bool)` Lock the Voicemeeter GUI
example:
@ -471,32 +373,32 @@ vm.Command.Show()
### VBAN
- `vm.Vban.Enable()` `vm.Vban.Disable()` Turn VBAN on or off
- `vm.Vban.Enable()` `vm.Vban.Disable()` Turn VBAN on or off
##### Instream | Outstream
- `vm.Vban.InStream[i]` `vm.Vban.OutStream[i]`
- `vm.Vban.InStream` `vm.Vban.OutStream`
The following methods are available
- `On() bool`
- `SetOn(val bool)`
- `Name() string`
- `SetName(val string)`
- `Ip() string`
- `SetIp(val string)`
- `Port() int`
- `SetPort(val int)` from 1024 to 65535
- `Sr() int`
- `SetSr(val int)` (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000)
- `Channel() int`
- `SetChannel(val int)` from 1 to 8
- `Bit() int`
- `SetBit(val int)` 16 or 24
- `Quality() int`
- `SetQuality(val int)` from 0 to 4
- `Route() int`
- `SetRoute(val int)` from 0 to 8
- `GetOn() bool`
- `SetOn(val bool)`
- `GetName() string`
- `SetName(val string)`
- `GetIp() string`
- `SetIp(val string)`
- `GetPort() int`
- `SetPort(val int)` from 1024 to 65535
- `GetSr() int`
- `SetSr(val int)` (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000)
- `GetChannel() int`
- `SetChannel(val int)` from 1 to 8
- `GetBit() int`
- `SetBit(val int)` 16 or 24
- `GetQuality() int`
- `SetQuality(val int)` from 0 to 4
- `GetRoute() int`
- `SetRoute(val int)` from 0 to 8
example:
@ -515,10 +417,10 @@ vm.Vban.OutStream[3].SetBit(24)
The following methods are available
- `Ins()`
- `Outs()`
- `Input(val int)`
- `Output(val int)`
- `Ins()`
- `Outs()`
- `Input(val int)`
- `Output(val int)`
example:
@ -532,13 +434,13 @@ for i := 0; i < int(vm.Device.Ins()); i++ {
The following methods are available
- `Play()`
- `Stop()`
- `Pause()`
- `Replay()`
- `Record()`
- `Ff()`
- `Rew()`
- `Play()`
- `Stop()`
- `Pause()`
- `Replay()`
- `Record()`
- `Ff()`
- `Rew()`
example:
@ -557,9 +459,9 @@ vm.Recorder.SetB2(false)
The following methods are available
- `Channel()` returns the current midi channel
- `Current()` returns the most recently pressed midi button
- `Get(<button>)` returns the value in cache for the midi button
- `Channel()` returns the current midi channel
- `Current()` returns the most recently pressed midi button
- `Get(<button>)` returns the value in cache for the midi button
example:
@ -572,19 +474,17 @@ var val = vm.Midi.Get(current)
By default level updates are disabled. Any event may be enabled or disabled. The following events exist:
- `pdirty` parameter updates
- `mdirty` macrobutton updates
- `midi` midi updates
- `ldirty` level updates
- `pdirty` parameter updates
- `mdirty` macrobutton updates
- `midi` midi updates
- `ldirty` level updates
example:
```go
events := []string{"ldirty", "mdirty", "pdirty"}
vm.EventAdd("ldirty")
vm.EventAdd(events...)
vm.EventRemove(events...)
vm.EventRemove("pdirty")
```
### Run tests
@ -597,4 +497,4 @@ go test ./...
### Official Documentation
- [Voicemeeter Remote C API](https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/main/VoicemeeterRemoteAPI.pdf)
- [Voicemeeter Remote C API](https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/main/VoicemeeterRemoteAPI.pdf)

166
base.go
View File

@ -2,16 +2,12 @@ package voicemeeter
import (
"bytes"
"errors"
"fmt"
"math"
"runtime"
"strings"
"syscall"
"time"
"unsafe"
log "github.com/sirupsen/logrus"
)
var (
@ -47,30 +43,18 @@ var (
// login logs into the API,
// attempts to launch Voicemeeter if it's not running,
// initializes dirty parameters.
func login(kindId string, timeout, bits int) error {
func login(kindId string) error {
res, _, _ := vmLogin.Call()
if res == 1 {
runVoicemeeter(kindId, bits)
runVoicemeeter(kindId)
time.Sleep(time.Second)
} else if res != 0 {
err := fmt.Errorf("VBVMR_Login returned %d", res)
return err
}
var ver_s string
start := time.Now()
var err error
for time.Since(start).Seconds() < float64(timeout) {
time.Sleep(time.Duration(100) * time.Millisecond)
if ver_s, err = getVersion(); err == nil {
log.Infof("Logged into Voicemeeter %s v%s", kindMap[kindId], ver_s)
log.Debugf("Log in time: %.2f", time.Since(start).Seconds())
break
}
fmt.Printf("Logged into Voicemeeter %s\n", kindId)
for pdirty() || mdirty() {
}
if err != nil {
return errors.New("timeout logging into the API")
}
clear()
return nil
}
@ -79,28 +63,24 @@ func login(kindId string, timeout, bits int) error {
func logout(kindId string) error {
time.Sleep(100 * time.Millisecond)
res, _, _ := vmLogout.Call()
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_Logout returned %d", int32(res))
if res != 0 {
err := fmt.Errorf("VBVMR_Logout returned %d", res)
return err
}
log.Infof("Logged out of Voicemeeter %s", kindMap[kindId])
fmt.Printf("Logged out of Voicemeeter %s\n", kindId)
return nil
}
// runVoicemeeter attempts to launch a Voicemeeter GUI of a kind.
func runVoicemeeter(kindId string, bits int) error {
func runVoicemeeter(kindId string) error {
vals := map[string]uint64{
"basic": 1,
"banana": 2,
"potato": 3,
}
val := vals[kindId]
if strings.Contains(runtime.GOARCH, "64") && bits == 64 {
val += 3
}
res, _, _ := vmRunvm.Call(uintptr(val))
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_RunVoicemeeter returned %d", int32(res))
res, _, _ := vmRunvm.Call(uintptr(vals[kindId]))
if res != 0 {
err := fmt.Errorf("VBVMR_RunVoicemeeter returned %d", res)
return err
}
return nil
@ -110,9 +90,8 @@ func runVoicemeeter(kindId string, bits int) error {
func getVersion() (string, error) {
var ver uint64
res, _, _ := vmGetvmVersion.Call(uintptr(unsafe.Pointer(&ver)))
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterVersion returned %d", int32(res))
log.Error(err.Error())
if res != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterVersion returned %d", res)
return "", err
}
v1 := (ver & 0xFF000000) >> 24
@ -123,61 +102,33 @@ func getVersion() (string, error) {
}
// pdirty returns true iff a parameter value has changed
func pdirty() (bool, error) {
func pdirty() bool {
res, _, _ := vmPdirty.Call()
if int32(res) < 0 {
err := fmt.Errorf("VBVMR_IsParametersDirty returned %d", int32(res))
log.Error(err.Error())
return false, err
}
return int32(res) == 1, nil
return int(res) == 1
}
// mdirty returns true iff a macrobutton value has changed
func mdirty() (bool, error) {
func mdirty() bool {
res, _, _ := vmMdirty.Call()
if int32(res) < 0 {
err := fmt.Errorf("VBVMR_MacroButton_IsDirty returned %d", int32(res))
log.Error(err.Error())
return false, err
}
return int32(res) == 1, nil
return int(res) == 1
}
// ldirty returns true iff a level value has changed
func ldirty(k *kind) (bool, error) {
func ldirty(k *kind) bool {
_levelCache.stripLevelsBuff = make([]float64, (2*k.PhysIn)+(8*k.VirtIn))
_levelCache.busLevelsBuff = make([]float64, 8*k.NumBus())
for i := 0; i < (2*k.PhysIn)+(8*k.VirtIn); i++ {
val, err := getLevel(_levelCache.stripMode, i)
if err != nil {
log.Error(err.Error())
return false, err
}
val, _ := getLevel(_levelCache.stripMode, i)
_levelCache.stripLevelsBuff[i] = val
_levelCache.stripComp[i] = _levelCache.stripLevelsBuff[i] == _levelCache.stripLevels[i]
}
for i := 0; i < 8*k.NumBus(); i++ {
val, err := getLevel(3, i)
if err != nil {
log.Error(err.Error())
return false, err
}
val, _ := getLevel(3, i)
_levelCache.busLevelsBuff[i] = val
_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())), nil
}
func clear() {
for {
pdirty, _ := pdirty()
mdirty, _ := mdirty()
if !(pdirty || mdirty) {
break
}
}
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
@ -186,8 +137,8 @@ func getVMType() (string, error) {
res, _, _ := vmGetvmType.Call(
uintptr(unsafe.Pointer(&type_)),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterType returned %d", int32(res))
if res != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterType returned %d", res)
return "", err
}
vals := map[uint64]string{
@ -202,7 +153,8 @@ func getVMType() (string, error) {
func getParameterFloat(name string) (float64, error) {
if vmsync {
time.Sleep(time.Duration(vmdelay) * time.Millisecond)
clear()
for pdirty() || mdirty() {
}
}
var value float32
b := append([]byte(name), 0)
@ -210,8 +162,8 @@ func getParameterFloat(name string) (float64, error) {
uintptr(unsafe.Pointer(&b[0])),
uintptr(unsafe.Pointer(&value)),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetParameterFloat returned %d", int32(res))
if res != 0 {
err := fmt.Errorf("VBVMR_GetParameterFloat returned %d", res)
return 0, err
}
return math.Round(float64(value)*10) / 10, nil
@ -225,8 +177,8 @@ func setParameterFloat(name string, value float64) error {
uintptr(unsafe.Pointer(&b1[0])),
uintptr(b2),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_SetParameterFloat returned %d", int32(res))
if res != 0 {
err := fmt.Errorf("VBVMR_SetParameterFloat returned %d", res)
return err
}
return nil
@ -236,7 +188,8 @@ func setParameterFloat(name string, value float64) error {
func getParameterString(name string) (string, error) {
if vmsync {
time.Sleep(time.Duration(vmdelay) * time.Millisecond)
clear()
for pdirty() || mdirty() {
}
}
b1 := append([]byte(name), 0)
var b2 [512]byte
@ -244,8 +197,8 @@ func getParameterString(name string) (string, error) {
uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetParameterStringA returned %d", int32(res))
if res != 0 {
err := fmt.Errorf("VBVMR_GetParameterStringA returned %d", res)
return "", err
}
str := bytes.Trim(b2[:], "\x00")
@ -260,8 +213,8 @@ func setParameterString(name, value string) error {
uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_SetParameterStringA returned %d", int32(res))
if res != 0 {
err := fmt.Errorf("VBVMR_SetParameterStringA returned %d", res)
return err
}
return nil
@ -273,8 +226,8 @@ func setParametersMulti(script string) error {
res, _, _ := vmSetParameters.Call(
uintptr(unsafe.Pointer(&b1[0])),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_SetParameters returned %d", int32(res))
if res != 0 {
err := fmt.Errorf("VBVMR_SetParameters returned %d", res)
return err
}
return nil
@ -284,7 +237,8 @@ func setParametersMulti(script string) error {
func getMacroStatus(id, mode int) (float64, error) {
if vmsync {
time.Sleep(time.Duration(vmdelay) * time.Millisecond)
clear()
for pdirty() || mdirty() {
}
}
var state float32
res, _, _ := vmGetMacroStatus.Call(
@ -292,8 +246,8 @@ func getMacroStatus(id, mode int) (float64, error) {
uintptr(unsafe.Pointer(&state)),
uintptr(mode),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_MacroButton_GetStatus returned %d", int32(res))
if res != 0 {
err := fmt.Errorf("VBVMR_MacroButton_GetStatus returned %d", res)
return 0, err
}
return float64(state), nil
@ -306,8 +260,8 @@ func setMacroStatus(id, state, mode int) error {
uintptr(state),
uintptr(mode),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_MacroButton_SetStatus returned %d", int32(res))
if res != 0 {
err := fmt.Errorf("VBVMR_MacroButton_SetStatus returned %d", res)
return err
}
return nil
@ -336,8 +290,8 @@ func getDeviceDescription(i int, dir string) (string, uint64, string, error) {
uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_Input_GetDeviceDescA returned %d", int32(res))
if res != 0 {
err := fmt.Errorf("VBVMR_Input_GetDeviceDescA returned %d", res)
return "", 0, "", err
}
} else {
@ -347,8 +301,8 @@ func getDeviceDescription(i int, dir string) (string, uint64, string, error) {
uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_Output_GetDeviceDescA returned %d", int32(res))
if res != 0 {
err := fmt.Errorf("VBVMR_Output_GetDeviceDescA returned %d", res)
return "", 0, "", err
}
}
@ -359,36 +313,34 @@ func getDeviceDescription(i int, dir string) (string, uint64, string, error) {
// getLevel returns a single level value of type type_ for channel[i]
func getLevel(type_, i int) (float64, error) {
var val float32
var val float64
res, _, _ := vmGetLevelFloat.Call(
uintptr(type_),
uintptr(i),
uintptr(unsafe.Pointer(&val)),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetLevel returned %d", int32(res))
if res != 0 {
err := fmt.Errorf("VBVMR_GetLevel returned %d", res)
return 0, err
}
return float64(val), nil
return val, nil
}
// getMidiMessage gets midi channel, pitch and velocity for a single midi input
func getMidiMessage() (bool, error) {
func getMidiMessage() bool {
var midi = newMidi()
var b1 [1024]byte
res, _, _ := vmGetMidiMessage.Call(
uintptr(unsafe.Pointer(&b1[0])),
uintptr(1024),
)
x := int32(res)
x := int(res)
if x < 0 {
if x == -2 {
err := fmt.Errorf("VBVMR_GetMidiMessage returned %d", int32(res))
if err != nil {
log.Error(err)
return false, err
}
err := fmt.Errorf("VBVMR_GetMidiMessage returned %d", res)
if err != nil {
fmt.Println(err)
}
return false
}
msg := bytes.Trim(b1[:], "\x00")
if len(msg) > 0 {
@ -405,5 +357,5 @@ func getMidiMessage() (bool, error) {
midi.cache[pitch] = vel
}
}
return len(msg) > 0, nil
return len(msg) > 0
}

171
bus.go
View File

@ -8,16 +8,17 @@ import (
// iBus defines the interface bus types must satisfy
type iBus interface {
String() string
Mute() bool
GetMute() bool
SetMute(val bool)
Mono() bool
GetEq() bool
SetEq(val bool)
GetMono() bool
SetMono(val bool)
Label() string
GetLabel() string
SetLabel(val string)
Gain() float64
GetGain() float64
SetGain(val float64)
Eq() *eQ
Mode() *busMode
Mode() iBusMode
Levels() *levels
FadeTo(target float32, time_ int)
FadeBy(change float32, time_ int)
@ -26,13 +27,12 @@ type iBus interface {
// bus represents a bus channel
type bus struct {
iRemote
eQ *eQ
mode *busMode
levels *levels
mode busMode
levels
}
// Mute returns the value of the Mute parameter
func (b *bus) Mute() bool {
// GetMute returns the value of the Mute parameter
func (b *bus) GetMute() bool {
return b.getter_bool("Mute")
}
@ -41,8 +41,18 @@ func (b *bus) SetMute(val bool) {
b.setter_bool("Mute", val)
}
// Mono returns the value of the Mute parameter
func (b *bus) Mono() bool {
// GetEq returns the value of the Eq.On parameter
func (b *bus) GetEq() bool {
return b.getter_bool("Eq.On")
}
// SetEq sets the value of the Eq.On parameter
func (b *bus) SetEq(val bool) {
b.setter_bool("Eq.On", val)
}
// GetMono returns the value of the Mute parameter
func (b *bus) GetMono() bool {
return b.getter_bool("Mono")
}
@ -51,8 +61,8 @@ func (b *bus) SetMono(val bool) {
b.setter_bool("Mono", val)
}
// Label returns the value of the MC parameter
func (b *bus) Label() string {
// GetLabel returns the value of the MC parameter
func (b *bus) GetLabel() string {
return b.getter_string("Label")
}
@ -61,8 +71,8 @@ func (b *bus) SetLabel(val string) {
b.setter_string("Label", val)
}
// Gain returns the value of the Gain parameter
func (b *bus) Gain() float64 {
// GetGain returns the value of the Gain parameter
func (b *bus) GetGain() float64 {
return b.getter_float("Gain")
}
@ -71,19 +81,14 @@ func (b *bus) SetGain(val float64) {
b.setter_float("Gain", val)
}
// Eq returns the eQ field
func (b *bus) Eq() *eQ {
return b.eQ
}
// Mode returns address of a busMode struct
func (b *bus) Mode() *busMode {
return b.mode
func (b *bus) Mode() iBusMode {
return &b.mode
}
// Levels returns the levels field
func (b *bus) Levels() *levels {
return b.levels
return &b.levels
}
// FadeTo sets the value of gain to target over at time interval of time_
@ -98,57 +103,83 @@ func (b *bus) FadeBy(change float32, time_ int) {
time.Sleep(time.Millisecond)
}
// PhysicalBus represents a single physical bus
type PhysicalBus struct {
// physicalBus represents a single physical bus
type physicalBus struct {
bus
}
// newPhysicalBus returns a PhysicalBus type cast to an iBus
// newPhysicalBus returns a physicalBus type cast to an iBus
func newPhysicalBus(i int, k *kind) iBus {
e := newEq(fmt.Sprintf("bus[%d].EQ", i), i)
b := newBusMode(i)
l := newBusLevels(i, k)
pb := PhysicalBus{bus{iRemote{fmt.Sprintf("bus[%d]", i), i}, e, b, l}}
pb := physicalBus{bus{iRemote{fmt.Sprintf("bus[%d]", i), i}, b, l}}
return &pb
return iBus(&pb)
}
// String implements the fmt.stringer interface
func (p *PhysicalBus) String() string {
func (p *physicalBus) String() string {
return fmt.Sprintf("PhysicalBus%d", p.index)
}
// VirtualBus represents a single virtual bus
type VirtualBus struct {
// virtualBus represents a single virtual bus
type virtualBus struct {
bus
}
// newVirtualBus returns a VirtualBus type cast to an iBus
// newVirtualBus returns a virtualBus type cast to an iBus
func newVirtualBus(i int, k *kind) iBus {
e := newEq(fmt.Sprintf("bus[%d].EQ", i), i)
b := newBusMode(i)
l := newBusLevels(i, k)
vb := VirtualBus{bus{iRemote{fmt.Sprintf("bus[%d]", i), i}, e, b, l}}
return &vb
vb := virtualBus{bus{iRemote{fmt.Sprintf("bus[%d]", i), i}, b, l}}
return iBus(&vb)
}
// String implements the fmt.stringer interface
func (v *VirtualBus) String() string {
func (v *virtualBus) String() string {
return fmt.Sprintf("VirtualBus%d", v.index)
}
// iBusMode defines the interface busMode type must satisfy
type iBusMode interface {
SetNormal(val bool)
GetNormal() bool
SetAmix(val bool)
GetAmix() bool
SetBmix(val bool)
GetBmix() bool
SetRepeat(val bool)
GetRepeat() bool
SetComposite(val bool)
GetComposite() bool
SetTvMix(val bool)
GetTvMix() bool
SetUpMix21(val bool)
GetUpMix21() bool
SetUpMix41(val bool)
GetUpMix41() bool
SetUpMix61(val bool)
GetUpMix61() bool
SetCenterOnly(val bool)
GetCenterOnly() bool
SetLfeOnly(val bool)
GetLfeOnly() bool
SetRearOnly(val bool)
GetRearOnly() bool
}
// busMode offers methods for getting/setting bus mode states
type busMode struct {
iRemote
}
// newBusMode returns a busMode struct
func newBusMode(i int) *busMode {
return &busMode{iRemote{fmt.Sprintf("bus[%d].mode", i), i}}
func newBusMode(i int) busMode {
return busMode{iRemote{fmt.Sprintf("bus[%d].mode", i), i}}
}
// Normal gets the value of the Mode.Normal parameter
func (bm *busMode) Normal() bool {
// GetNormal gets the value of the Mode.Normal parameter
func (bm *busMode) GetNormal() bool {
return bm.getter_bool("Normal")
}
@ -157,8 +188,8 @@ func (bm *busMode) SetNormal(val bool) {
bm.setter_bool("Normal", val)
}
// Amix gets the value of the Mode.Amix parameter
func (bm *busMode) Amix() bool {
// GetAmix gets the value of the Mode.Amix parameter
func (bm *busMode) GetAmix() bool {
return bm.getter_bool("Amix")
}
@ -167,8 +198,8 @@ func (bm *busMode) SetAmix(val bool) {
bm.setter_bool("Amix", val)
}
// Bmix gets the value of the Mode.Bmix parameter
func (bm *busMode) Bmix() bool {
// GetBmix gets the value of the Mode.Bmix parameter
func (bm *busMode) GetBmix() bool {
return bm.getter_bool("Bmix")
}
@ -177,8 +208,8 @@ func (bm *busMode) SetBmix(val bool) {
bm.setter_bool("Bmix", val)
}
// Repeat gets the value of the Mode.Repeat parameter
func (bm *busMode) Repeat() bool {
// GetRepeat gets the value of the Mode.Repeat parameter
func (bm *busMode) GetRepeat() bool {
return bm.getter_bool("Repeat")
}
@ -187,8 +218,8 @@ func (bm *busMode) SetRepeat(val bool) {
bm.setter_bool("Repeat", val)
}
// Composite gets the value of the Mode.Composite parameter
func (bm *busMode) Composite() bool {
// GetComposite gets the value of the Mode.Composite parameter
func (bm *busMode) GetComposite() bool {
return bm.getter_bool("Composite")
}
@ -197,8 +228,8 @@ func (bm *busMode) SetComposite(val bool) {
bm.setter_bool("Composite", val)
}
// TvMix gets the value of the Mode.TvMix parameter
func (bm *busMode) TvMix() bool {
// GetTvMix gets the value of the Mode.TvMix parameter
func (bm *busMode) GetTvMix() bool {
return bm.getter_bool("TvMix")
}
@ -207,8 +238,8 @@ func (bm *busMode) SetTvMix(val bool) {
bm.setter_bool("TvMix", val)
}
// UpMix21 gets the value of the Mode.UpMix21 parameter
func (bm *busMode) UpMix21() bool {
// GetUpMix21 gets the value of the Mode.UpMix21 parameter
func (bm *busMode) GetUpMix21() bool {
return bm.getter_bool("UpMix21")
}
@ -217,8 +248,8 @@ func (bm *busMode) SetUpMix21(val bool) {
bm.setter_bool("UpMix21", val)
}
// UpMix41 gets the value of the Mode.UpMix41 parameter
func (bm *busMode) UpMix41() bool {
// GetUpMix41 gets the value of the Mode.UpMix41 parameter
func (bm *busMode) GetUpMix41() bool {
return bm.getter_bool("UpMix41")
}
@ -227,8 +258,8 @@ func (bm *busMode) SetUpMix41(val bool) {
bm.setter_bool("UpMix41", val)
}
// UpMix61 gets the value of the Mode.UpMix61 parameter
func (bm *busMode) UpMix61() bool {
// GetUpMix61 gets the value of the Mode.UpMix61 parameter
func (bm *busMode) GetUpMix61() bool {
return bm.getter_bool("UpMix61")
}
@ -237,8 +268,8 @@ func (bm *busMode) SetUpMix61(val bool) {
bm.setter_bool("UpMix61", val)
}
// CenterOnly gets the value of the Mode.CenterOnly parameter
func (bm *busMode) CenterOnly() bool {
// GetCenterOnly gets the value of the Mode.CenterOnly parameter
func (bm *busMode) GetCenterOnly() bool {
return bm.getter_bool("CenterOnly")
}
@ -247,8 +278,8 @@ func (bm *busMode) SetCenterOnly(val bool) {
bm.setter_bool("CenterOnly", val)
}
// LfeOnly gets the value of the Mode.LFE parameter
func (bm *busMode) LfeOnly() bool {
// GetLfeOnly gets the value of the Mode.LFE parameter
func (bm *busMode) GetLfeOnly() bool {
return bm.getter_bool("LfeOnly")
}
@ -257,8 +288,8 @@ func (bm *busMode) SetLfeOnly(val bool) {
bm.setter_bool("LfeOnly", val)
}
// RearOnly gets the value of the Mode.RearOnly parameter
func (bm *busMode) RearOnly() bool {
// GetRearOnly gets the value of the Mode.RearOnly parameter
func (bm *busMode) GetRearOnly() bool {
return bm.getter_bool("RearOnly")
}
@ -268,16 +299,16 @@ func (bm *busMode) SetRearOnly(val bool) {
}
// newBusLevels represents the levels field for a channel
func newBusLevels(i int, k *kind) *levels {
func newBusLevels(i int, k *kind) levels {
init := i * 8
return &levels{iRemote{fmt.Sprintf("bus[%d]", i), i}, k, init, 8, "bus"}
return levels{iRemote{fmt.Sprintf("bus[%d]", i), i}, k, init, 8, "bus"}
}
// All returns the level values for a bus
func (l *levels) All() []float64 {
levels := make([]float64, l.offset)
for i := range levels {
levels[i] = convertLevel(_levelCache.busLevels[l.init+i])
var levels []float64
for i := l.init; i < l.init+l.offset; i++ {
levels = append(levels, convertLevel(_levelCache.busLevels[i]))
}
return levels
}

View File

@ -37,8 +37,8 @@ func (m *button) String() string {
return fmt.Sprintf("MacroButton%d", m.index)
}
// State returns the value of the State parameter
func (m *button) State() bool {
// GetState returns the value of the State parameter
func (m *button) GetState() bool {
return m.getter(1)
}
@ -47,8 +47,8 @@ func (m *button) SetState(val bool) {
m.setter(val, 1)
}
// StateOnly returns the value of the StateOnly parameter
func (m *button) StateOnly() bool {
// GetStateOnly returns the value of the StateOnly parameter
func (m *button) GetStateOnly() bool {
return m.getter(2)
}
@ -57,8 +57,8 @@ func (m *button) SetStateOnly(val bool) {
m.setter(val, 2)
}
// Trigger returns the value of the Trigger parameter
func (m *button) Trigger() bool {
// GetTrigger returns the value of the Trigger parameter
func (m *button) GetTrigger() bool {
return m.getter(2)
}

25
eq.go
View File

@ -1,25 +0,0 @@
package voicemeeter
type eQ struct {
iRemote
}
func newEq(id string, i int) *eQ {
return &eQ{iRemote{id, i}}
}
func (e *eQ) On() bool {
return e.getter_bool("on")
}
func (e *eQ) SetOn(val bool) {
e.setter_bool("on", val)
}
func (e *eQ) Ab() bool {
return e.getter_bool("AB")
}
func (e *eQ) SetAb(val bool) {
e.setter_bool("AB", val)
}

View File

@ -1,15 +0,0 @@
package voicemeeter
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetEq(t *testing.T) {
//t.Skip("skipping test")
__e := newEq("strip[0].EQ", 0)
t.Run("Should return an eQ type", func(t *testing.T) {
assert.NotNil(t, __e)
})
}

View File

@ -1,11 +1,10 @@
module hotkeys
module main
go 1.19
require (
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0
github.com/sirupsen/logrus v1.9.0
github.com/onyx-and-iris/voicemeeter-api-go v1.7.0
)
require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
require golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d // indirect

View File

@ -1,19 +1,10 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg=
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg=
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0 h1:AXem+OMeKDuJd2KoLpzHEU70Rx2057p4XKgiOJkXCIo=
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0/go.mod h1:ULRO0N2Wg7Ymj7CEg4TI7CJobx9yVEZ5Lbh7oWi7woA=
github.com/onyx-and-iris/voicemeeter-api-go v1.7.0 h1:WA1RGrfaWBOavgY2uoJltzy/rAgvs62PM3qzR+5/jt8=
github.com/onyx-and-iris/voicemeeter-api-go v1.7.0/go.mod h1:zAdBhHXQ9n37CUbLizbOPmAutyZI8Ncqeu5e9u1Fy14=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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=
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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@ -2,17 +2,12 @@ package main
import (
"fmt"
log "github.com/sirupsen/logrus"
"log"
"github.com/eiannone/keyboard"
"github.com/onyx-and-iris/voicemeeter/v2"
"github.com/onyx-and-iris/voicemeeter-api-go"
)
func init() {
log.SetLevel(log.InfoLevel)
}
func main() {
if err := keyboard.Open(); err != nil {
log.Fatal(err)
@ -39,9 +34,9 @@ Loop:
case '0':
fmt.Printf("Logged into Voicemeeter %s, version %s\n", vm.Type(), vm.Version())
case '1':
vm.Strip[0].SetMute(!vm.Strip[0].Mute())
vm.Strip[0].SetMute(!vm.Strip[0].GetMute())
case '2':
if vm.Strip[3].Gain() == -12.8 {
if vm.Strip[3].GetGain() == -12.8 {
vm.Strip[3].FadeBy(-8.3, 500)
} else {
vm.Strip[3].FadeTo(-12.8, 500)

View File

@ -1,12 +1,11 @@
module obs
module main
go 1.18
require (
github.com/BurntSushi/toml v1.2.0
github.com/andreykaipov/goobs v0.10.0
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0
github.com/sirupsen/logrus v1.9.0
github.com/onyx-and-iris/voicemeeter-api-go v1.7.0
)
require (
@ -14,5 +13,5 @@ require (
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d // indirect
)

View File

@ -4,26 +4,17 @@ github.com/andreykaipov/goobs v0.10.0 h1:wa4CxbYu/NqwUmx5E4/baDqYRYEmfHwg2T23RAg
github.com/andreykaipov/goobs v0.10.0/go.mod h1:EqG73Uu/4npyhXIWWszgRelNkEeIz+d0slUT6NKWYs4=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0 h1:AXem+OMeKDuJd2KoLpzHEU70Rx2057p4XKgiOJkXCIo=
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0/go.mod h1:ULRO0N2Wg7Ymj7CEg4TI7CJobx9yVEZ5Lbh7oWi7woA=
github.com/onyx-and-iris/voicemeeter-api-go v1.7.0 h1:WA1RGrfaWBOavgY2uoJltzy/rAgvs62PM3qzR+5/jt8=
github.com/onyx-and-iris/voicemeeter-api-go v1.7.0/go.mod h1:zAdBhHXQ9n37CUbLizbOPmAutyZI8Ncqeu5e9u1Fy14=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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=
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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@ -2,12 +2,11 @@ package main
import (
"fmt"
"os"
"log"
"time"
"os"
log "github.com/sirupsen/logrus"
"github.com/onyx-and-iris/voicemeeter/v2"
"github.com/onyx-and-iris/voicemeeter-api-go"
"github.com/andreykaipov/goobs"
"github.com/andreykaipov/goobs/api/events"
@ -42,10 +41,6 @@ func onEnd(vm *voicemeeter.Remote) {
vm.Vban.InStream[0].SetOn(false)
}
func init() {
log.SetLevel(log.InfoLevel)
}
func main() {
vm, err := vmConnect()
if err != nil {
@ -97,12 +92,12 @@ func vmConnect() (*voicemeeter.Remote, error) {
return vm, nil
}
func obsConnect() (*goobs.Client, error) {
func obsConnect() (*goobs.Client, error) {
type (
connection struct {
Host string
Port int
Password string
Host string
Port int
Password string
}
config struct {
@ -112,8 +107,7 @@ func obsConnect() (*goobs.Client, error) {
f := "config.toml"
if _, err := os.Stat(f); err != nil {
err := fmt.Errorf("unable to locate %s", f)
return nil, err
f = "./config.toml"
}
var c config

View File

@ -2,58 +2,60 @@ package main
import (
"fmt"
"log"
"time"
log "github.com/sirupsen/logrus"
"github.com/onyx-and-iris/voicemeeter/v2"
"github.com/onyx-and-iris/voicemeeter-api-go"
)
// observer represents a single receiver of updates
type observer struct {
vm *voicemeeter.Remote
events chan string
vm *voicemeeter.Remote
}
// newObserver returns an observer type
// newObserver add ldirty events to the eventlist and returns an observer type
func newObserver(vm *voicemeeter.Remote) *observer {
return &observer{vm, make(chan string)}
vm.EventAdd("ldirty")
return &observer{vm}
}
// Listen registers the observer channel and listens for updates.
func (o observer) Listen() {
o.vm.Register(o.events)
// register registers this observer to receive updates
func (o observer) register() {
o.vm.Register(o)
}
for s := range o.events {
switch s {
case "pdirty", "mdirty":
fmt.Println(s)
case "midi":
var current = o.vm.Midi.Current()
var val = o.vm.Midi.Get(current)
fmt.Printf("Value of midi button %d: %d\n", current, val)
case "ldirty":
for _, bus := range o.vm.Bus {
if bus.Levels().IsDirty() {
fmt.Println(bus, bus.Levels().All())
}
}
}
// deregister deregisters this observer to receive updates
func (o observer) deregister() {
o.vm.Deregister(o)
}
// OnUpdate satisfies the observer interface defined in publisher.go
// for each event type an action is triggered when the event occurs.
func (o observer) OnUpdate(subject string) {
if subject == "pdirty" {
fmt.Println("pdirty!")
} else if subject == "mdirty" {
fmt.Println("mdirty!")
} else if subject == "midi" {
var current = o.vm.Midi.Current()
var val = o.vm.Midi.Get(current)
fmt.Printf("Value of midi button %d: %d\n", current, val)
} else if subject == "ldirty" {
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 init() {
log.SetLevel(log.InfoLevel)
}
// runObserver initiates a single observer and
// starts its Listen() function in a goroutine.
func runObserver(vm *voicemeeter.Remote) {
o := newObserver(vm)
go o.Listen()
}
// main connects to Voiceemeter and runs a single observer for 30 seconds.
// main connects to Voiceemeter, registers observer for updates
// runs updates for 30 seconds and then deregisters observer.
func main() {
vm, err := vmConnect()
if err != nil {
@ -61,15 +63,15 @@ func main() {
}
defer vm.Logout()
runObserver(vm)
time.Sleep(time.Duration(30) * time.Second)
o := newObserver(vm)
o.register()
time.Sleep(30 * time.Second)
o.deregister()
}
// vmConnect connects to Voicemeeter potato and logs into the API
// it also add ldirty to event updates.
func vmConnect() (*voicemeeter.Remote, error) {
vm, err := voicemeeter.NewRemote("basic", 0)
vm, err := voicemeeter.NewRemote("potato", 0)
if err != nil {
return nil, err
}
@ -78,7 +80,6 @@ func vmConnect() (*voicemeeter.Remote, error) {
if err != nil {
return nil, err
}
vm.EventAdd("ldirty")
return vm, nil
}

View File

@ -1,39 +1,25 @@
## About
A Voicemeeter CLI, offers ability to toggle, get and set parameters.
## Install
First build and install it with `go install` (skip this step if using binary from [Releases](https://github.com/onyx-and-iris/voicemeeter/releases))
A simple voicemeeter-cli program. Offers ability to toggle, get and set parameters.
## Use
Commands that begin with `!` will toggle a parameter, commands that contain `=` will set a parameter, all other commands will get a value.
You may pass the following optional flags:
- -h: Print the help message
- -i: Enable interactive mode
- -k: The kind of Voicemeeter GUI to launch, defaults to Banana
- -l: Log level (0 up to 6), defaults to 3, Warn Level
- -d: Set the delay between commands, defaults to 20ms
- -v: Enable extra console output (toggle and set messages).
Toggle with `!` prefix, get by excluding `=` and set by including `=`. Mix and match arguments.
You may pass an optional flag -kind to set the kind of Voicemeeter. Defaults to banana.
You may pass an optional flag -delay to set a delay on the getters. Defaults to 15ms.
for example:
`vm-cli.exe -v -l=4 -k=potato strip[0].mute=0 strip[0].mute !strip[0].mute strip[0].mute bus[0].gain=-8.8 command.lock=1`
`go run .\main.go -kind=potato -delay=18 strip[0].mute=0 strip[0].mute !strip[0].mute strip[0].mute bus[0].gain=-8.8 command.lock=1`
Expected output:
```
time="<timestamp>" level=info msg="Logged into Voicemeeter Potato v3.1.1.1"
Setting strip[0].mute=0
strip[0].mute: 0.00
Logged into Voicemeeter potato
Running command strip[0].mute=0
Value of strip[0].mute is: 0
Toggling strip[0].mute
strip[0].mute: 1.00
Setting bus[0].gain=-8.8
Setting command.lock=1
time="<timestamp>" level=info msg="Logged out of Voicemeeter Potato"
Value of strip[0].mute is: 1
Running command bus[0].gain=-8.8
Running command command.lock=1
```
If running in interactive mode enter `Q`, to exit.

View File

@ -1,121 +1,33 @@
package main
import (
"bufio"
"flag"
"fmt"
"os"
"log"
"strings"
log "github.com/sirupsen/logrus"
"github.com/onyx-and-iris/voicemeeter/v2"
"github.com/onyx-and-iris/voicemeeter-api-go"
)
const (
FLOAT = iota
STRING
)
type result struct {
kind int
stringParam string
floatParam float64
}
type verbosePrinter struct {
verbose bool
}
func newVerbosePrinter() *verbosePrinter {
return &verbosePrinter{}
}
func (v *verbosePrinter) printf(format string, a ...interface{}) {
if v.verbose {
fmt.Printf(format, a...)
}
}
var (
vPrinter *verbosePrinter
)
func init() {
vPrinter = newVerbosePrinter()
}
func main() {
var (
kind string
delay int
interactive bool
loglevel int
help bool
)
flag.Usage = usage
flag.BoolVar(&help, "help", false, "print the help message")
flag.BoolVar(&help, "h", false, "print the help message (shorthand)")
flag.StringVar(&kind, "kind", "banana", "kind of voicemeeter")
flag.StringVar(&kind, "k", "banana", "kind of voicemeeter (shorthand)")
flag.IntVar(&delay, "delay", 20, "delay between commands")
flag.IntVar(&delay, "d", 20, "delay between commands (shorthand)")
flag.BoolVar(&interactive, "interactive", false, "toggle interactive mode")
flag.BoolVar(&interactive, "i", false, "toggle interactive mode (shorthand)")
flag.IntVar(&loglevel, "loglevel", int(log.WarnLevel), "set the log level")
flag.IntVar(&loglevel, "l", int(log.WarnLevel), "set the log level (shorthand)")
flag.BoolVar(&vPrinter.verbose, "verbose", false, "enable extra console output (toggle and set messages)")
flag.BoolVar(&vPrinter.verbose, "v", false, "enable extra console output (toggle and set messages) (shorthand)")
kindId := flag.String("kind", "banana", "kind of voicemeeter")
delay := flag.Int("delay", 15, "delay between commands")
flag.Parse()
if help {
flag.Usage()
return
}
if loglevel >= int(log.PanicLevel) && loglevel <= int(log.TraceLevel) {
log.SetLevel(log.Level(loglevel))
}
vm, err := vmConnect(kind, delay)
vm, err := vmConnect(kindId, delay)
if err != nil {
log.Fatal(err)
}
defer vm.Logout()
if interactive {
interactiveMode(vm)
return
}
args := flag.Args()
if len(args) == 0 {
flag.Usage()
return
}
for _, arg := range args {
if err := parse(vm, arg); err != nil {
log.Error(err.Error())
}
err = run_commands(vm)
if err != nil {
fmt.Println(err)
}
}
func usage() {
fmt.Println("usage: ./vm-cli.exe [-h] [-i] [-k] [-l] [-d] [-v]\n" +
"Where:\n" +
"\th: Print the help message\n" +
"\ti: Enable interactive mode\n" +
"\tk: The kind of Voicemeeter GUI to launch, defaults to Banana\n" +
"\tl: Log level 0 up to 6, (defaults to 3, Warn Level)\n" +
"\td: Set the delay between commands (defaults to 20ms)\n" +
"\tv: Enable extra console output (toggle and set messages).")
}
func vmConnect(kind string, delay int) (*voicemeeter.Remote, error) {
vm, err := voicemeeter.NewRemote(kind, delay)
func vmConnect(kindId *string, delay *int) (*voicemeeter.Remote, error) {
vm, err := voicemeeter.NewRemote(*kindId, *delay)
if err != nil {
return nil, err
}
@ -128,86 +40,38 @@ func vmConnect(kind string, delay int) (*voicemeeter.Remote, error) {
return vm, nil
}
func interactiveMode(vm *voicemeeter.Remote) error {
fmt.Println("Interactive mode enabled. Enter 'Q' to exit.")
scanner := bufio.NewScanner(os.Stdin)
fmt.Printf(">> ")
for scanner.Scan() {
input := scanner.Text()
if strings.ToUpper(input) == "Q" {
break
}
for _, cmd := range strings.Split(input, " ") {
if err := parse(vm, cmd); err != nil {
log.Error(err.Error())
func run_commands(vm *voicemeeter.Remote) error {
for _, arg := range flag.Args() {
if arg[0] == '!' {
val, err := vm.GetFloat(arg[1:])
if err != nil {
err = fmt.Errorf("unable to toggle %s", arg[1:])
return err
}
vm.SetFloat(arg[1:], 1-val)
fmt.Println("Toggling", arg[1:])
} else {
if strings.Contains(arg, "=") {
fmt.Println("Running command", arg)
err := vm.SendText(arg)
if err != nil {
err = fmt.Errorf("unable to set %s", arg)
return err
}
} else {
val_f, err := vm.GetFloat(arg)
if err != nil {
val_s, err := vm.GetString(arg)
if err != nil {
err = fmt.Errorf("unable to get %s", arg)
return err
}
fmt.Println("Value of", arg, "is:", val_s)
} else {
fmt.Println("Value of", arg, "is:", val_f)
}
}
}
fmt.Printf(">> ")
}
if scanner.Err() != nil {
return scanner.Err()
}
return nil
}
func parse(vm *voicemeeter.Remote, cmd string) error {
if cmd[0] == '!' {
if err := toggleCmd(vm, cmd[1:]); err != nil {
return err
}
} else if strings.Contains(cmd, "=") {
if err := setCmd(vm, cmd); err != nil {
return err
}
} else {
r := result{kind: FLOAT}
if err := getCmd(vm, cmd, &r); err != nil {
return err
}
switch r.kind {
case FLOAT:
fmt.Printf("%s: %.2f\n", cmd, r.floatParam)
case STRING:
fmt.Printf("%s: %s\n", cmd, r.stringParam)
}
}
return nil
}
func toggleCmd(vm *voicemeeter.Remote, cmd string) error {
r := result{kind: FLOAT}
if err := getCmd(vm, cmd, &r); err != nil {
return err
}
if r.kind == FLOAT && (r.floatParam == 0 || r.floatParam == 1) {
vPrinter.printf("Toggling %s\n", cmd)
vm.SetFloat(cmd, 1-r.floatParam)
} else {
log.Warnf("%s does not appear to be a boolean parameter", cmd)
}
return nil
}
func setCmd(vm *voicemeeter.Remote, cmd string) error {
if err := vm.SendText(cmd); err != nil {
err = fmt.Errorf("unable to set %s", cmd)
return err
}
vPrinter.printf("Setting %s\n", cmd)
return nil
}
func getCmd(vm *voicemeeter.Remote, cmd string, r *result) error {
if val, err := vm.GetFloat(cmd); err == nil {
r.floatParam = val
} else if val, err := vm.GetString(cmd); err == nil {
r.kind = STRING
r.stringParam = val
} else {
err := fmt.Errorf("unknown parameter '%s'", cmd)
return err
}
return nil
}

10
go.mod
View File

@ -1,11 +1,15 @@
module github.com/onyx-and-iris/voicemeeter/v2
module github.com/onyx-and-iris/voicemeeter-api-go
go 1.18
retract (
// package files moved into root of repository
[v1.0.0, v1.1.0]
)
require (
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.0
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d
)
require (

7
go.sum
View File

@ -3,16 +3,13 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
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.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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=

View File

@ -2,12 +2,10 @@ package voicemeeter
import (
"fmt"
log "github.com/sirupsen/logrus"
)
// iRemote provides an interface between higher types and functions that
// wrap CAPI calls
// iRemote provides an interface between higher methods and lower functions
// expected to be embedded
type iRemote struct {
_identifier string
index int
@ -21,7 +19,6 @@ func (ir *iRemote) identifier() string {
// getter_bool returns the value of a boolean parameter
func (ir *iRemote) getter_bool(p string) bool {
param := fmt.Sprintf("%s.%s", ir.identifier(), p)
log.Debug("getter_bool::", param)
val, err := getParameterFloat(param)
if err != nil {
fmt.Println(err)
@ -38,7 +35,6 @@ func (ir *iRemote) setter_bool(p string, v bool) {
} else {
value = 0
}
log.Debug("setter_bool::", param, "=", v)
err := setParameterFloat(param, float64(value))
if err != nil {
fmt.Println(err)
@ -48,7 +44,6 @@ func (ir *iRemote) setter_bool(p string, v bool) {
// getter_int returns the value of an int parameter p
func (ir *iRemote) getter_int(p string) int {
param := fmt.Sprintf("%s.%s", ir.identifier(), p)
log.Debug("getter_int::", param)
val, err := getParameterFloat(param)
if err != nil {
fmt.Println(err)
@ -59,22 +54,15 @@ func (ir *iRemote) getter_int(p string) int {
// setter_int sets the value v of an int parameter p
func (ir *iRemote) setter_int(p string, v int) {
param := fmt.Sprintf("%s.%s", ir.identifier(), p)
log.Debug("setter_int::", param, "=", v)
err := setParameterFloat(param, float64(v))
if err != nil {
fmt.Println(err)
}
}
// getter_float returns the value of a float parameter p
// getter_float returns the value of an int parameter p
func (ir *iRemote) getter_float(p string) float64 {
var param string
if p != "" {
param = fmt.Sprintf("%s.%s", ir.identifier(), p)
} else {
param = ir.identifier()
}
log.Debug("getter_float::", param)
param := fmt.Sprintf("%s.%s", ir.identifier(), p)
val, err := getParameterFloat(param)
if err != nil {
fmt.Println(err)
@ -82,15 +70,9 @@ func (ir *iRemote) getter_float(p string) float64 {
return val
}
// setter_float sets the value v of an float parameter p
// setter_float sets the value v of an int parameter p
func (ir *iRemote) setter_float(p string, v float64) {
var param string
if p != "" {
param = fmt.Sprintf("%s.%s", ir.identifier(), p)
} else {
param = ir.identifier()
}
log.Debug("setter_float::", param, "=", v)
param := fmt.Sprintf("%s.%s", ir.identifier(), p)
err := setParameterFloat(param, float64(v))
if err != nil {
fmt.Println(err)
@ -100,7 +82,6 @@ func (ir *iRemote) setter_float(p string, v float64) {
// getter_string returns the value of a string parameter p
func (ir *iRemote) getter_string(p string) string {
param := fmt.Sprintf("%s.%s", ir.identifier(), p)
log.Debug("getter_string::", param)
val, err := getParameterString(param)
if err != nil {
fmt.Println(err)
@ -111,7 +92,6 @@ func (ir *iRemote) getter_string(p string) string {
// setter_string sets the value v of a string parameter p
func (ir *iRemote) setter_string(p, v string) {
param := fmt.Sprintf("%s.%s", ir.identifier(), p)
log.Debug("setter_string::", param, "=", v)
err := setParameterString(param, v)
if err != nil {
fmt.Println(err)

View File

@ -2,21 +2,21 @@ package voicemeeter
// iOutputs defines the interface outputs type must satisfy
type iOutputs interface {
A1() bool
GetA1() bool
SetA1(val bool)
A2() bool
GetA2() bool
SetA2(val bool)
A3() bool
GetA3() bool
SetA3(val bool)
A4() bool
GetA4() bool
SetA4(val bool)
A5() bool
GetA5() bool
SetA5(val bool)
B1() bool
GetB1() bool
SetB1(val bool)
B2() bool
GetB2() bool
SetB2(val bool)
B3() bool
GetB3() bool
SetB3(val bool)
}
@ -32,8 +32,8 @@ func newOutputs(id string, i int) outputs {
return o
}
// A1 returns the value of the A1 parameter
func (o *outputs) A1() bool {
// GetA1 returns the value of the A1 parameter
func (o *outputs) GetA1() bool {
return o.getter_bool("A1")
}
@ -42,8 +42,8 @@ func (o *outputs) SetA1(val bool) {
o.setter_bool("A1", val)
}
// A2 returns the value of the A2 parameter
func (o *outputs) A2() bool {
// GetA2 returns the value of the A2 parameter
func (o *outputs) GetA2() bool {
return o.getter_bool("A2")
}
@ -52,8 +52,8 @@ func (o *outputs) SetA2(val bool) {
o.setter_bool("A2", val)
}
// A3 returns the value of the A3 parameter
func (o *outputs) A3() bool {
// GetA3 returns the value of the A3 parameter
func (o *outputs) GetA3() bool {
return o.getter_bool("A3")
}
@ -62,8 +62,8 @@ func (o *outputs) SetA3(val bool) {
o.setter_bool("A3", val)
}
// A4 returns the value of the A4 parameter
func (o *outputs) A4() bool {
// GetA4 returns the value of the A4 parameter
func (o *outputs) GetA4() bool {
return o.getter_bool("A4")
}
@ -72,8 +72,8 @@ func (o *outputs) SetA4(val bool) {
o.setter_bool("A4", val)
}
// A5 returns the value of the A5 parameter
func (o *outputs) A5() bool {
// GetA5 returns the value of the A5 parameter
func (o *outputs) GetA5() bool {
return o.getter_bool("A5")
}
@ -82,8 +82,8 @@ func (o *outputs) SetA5(val bool) {
o.setter_bool("A5", val)
}
// B1 returns the value of the B1 parameter
func (o *outputs) B1() bool {
// GetB1 returns the value of the B1 parameter
func (o *outputs) GetB1() bool {
return o.getter_bool("B1")
}
@ -92,8 +92,8 @@ func (o *outputs) SetB1(val bool) {
o.setter_bool("B1", val)
}
// B2 returns the value of the B2 parameter
func (o *outputs) B2() bool {
// GetB2 returns the value of the B2 parameter
func (o *outputs) GetB2() bool {
return o.getter_bool("B2")
}
@ -102,8 +102,8 @@ func (o *outputs) SetB2(val bool) {
o.setter_bool("B2", val)
}
// B3 returns the value of the B3 parameter
func (o *outputs) B3() bool {
// GetB3 returns the value of the B3 parameter
func (o *outputs) GetB3() bool {
return o.getter_bool("B3")
}

View File

@ -2,18 +2,42 @@ package voicemeeter
import (
"time"
log "github.com/sirupsen/logrus"
)
// publisher defines the list of observer channels
type publisher struct {
observers []chan string
// observer defines the interface any registered observers must satisfy
type observer interface {
OnUpdate(subject string)
}
// Register adds an observer channel to the channelList
func (p *publisher) Register(channel chan string) {
p.observers = append(p.observers, channel)
// publisher defines methods that support observers
type publisher struct {
observerList []observer
}
// 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)
}
}
type event struct {
@ -27,64 +51,47 @@ func newEvent() *event {
return &event{true, true, true, false}
}
func (e *event) Add(events ...string) {
for _, event := range events {
switch event {
case "pdirty":
e.pdirty = true
case "mdirty":
e.mdirty = true
case "midi":
e.midi = true
case "ldirty":
e.ldirty = true
}
log.Info(event, " added to the pooler")
func (e *event) Add(ev string) {
switch ev {
case "pdirty":
e.pdirty = true
case "mdirty":
e.mdirty = true
case "midi":
e.midi = true
case "ldirty":
e.ldirty = true
}
}
func (e *event) Remove(events ...string) {
for _, event := range events {
switch event {
case "pdirty":
e.pdirty = false
case "mdirty":
e.mdirty = false
case "midi":
e.midi = false
case "ldirty":
e.ldirty = false
}
log.Info(event, " removed from the pooler")
func (e *event) Remove(ev string) {
switch ev {
case "pdirty":
e.pdirty = false
case "mdirty":
e.mdirty = false
case "midi":
e.midi = false
case "ldirty":
e.ldirty = false
}
}
var p *pooler
// pooler continuously polls the dirty parameters
// pooler continuously polls the dirty paramters
// it is expected to be run in a goroutine
type pooler struct {
k *kind
run bool
event *event
pdirtyDone chan bool
mdirtyDone chan bool
midiDone chan bool
ldirtyDone chan bool
k *kind
run bool
event *event
publisher
}
func newPooler(k *kind) *pooler {
p = &pooler{
k: k,
run: true,
event: newEvent(),
pdirtyDone: make(chan bool),
mdirtyDone: make(chan bool),
midiDone: make(chan bool),
ldirtyDone: make(chan bool),
p := &pooler{
k: k,
run: true,
event: newEvent(),
}
go p.done()
go p.parameters()
go p.macrobuttons()
go p.midi()
@ -92,46 +99,10 @@ func newPooler(k *kind) *pooler {
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 {
pdirty, err := pdirty()
if err != nil {
close(p.pdirtyDone)
break
}
if p.event.pdirty && pdirty {
for _, ch := range p.observers {
ch <- "pdirty"
}
if p.event.pdirty && pdirty() {
p.notify("pdirty")
}
time.Sleep(33 * time.Millisecond)
}
@ -139,15 +110,8 @@ func (p *pooler) parameters() {
func (p *pooler) macrobuttons() {
for p.run {
mdirty, err := mdirty()
if err != nil {
close(p.mdirtyDone)
break
}
if p.event.mdirty && mdirty {
for _, ch := range p.observers {
ch <- "mdirty"
}
if p.event.mdirty && mdirty() {
p.notify("mdirty")
}
time.Sleep(33 * time.Millisecond)
}
@ -155,15 +119,8 @@ func (p *pooler) macrobuttons() {
func (p *pooler) midi() {
for p.run {
midi, err := getMidiMessage()
if err != nil {
close(p.midiDone)
break
}
if p.event.midi && midi {
for _, ch := range p.observers {
ch <- "midi"
}
if p.event.midi && getMidiMessage() {
p.notify("midi")
}
time.Sleep(33 * time.Millisecond)
}
@ -173,17 +130,10 @@ func (p *pooler) levels() {
_levelCache = newLevelCache(p.k)
for p.run {
ldirty, err := ldirty(p.k)
if err != nil {
close(p.ldirtyDone)
break
}
if p.event.ldirty && ldirty {
if p.event.ldirty && 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())
for _, ch := range p.observers {
ch <- "ldirty"
}
p.notify("ldirty")
}
time.Sleep(33 * time.Millisecond)
}

View File

@ -51,13 +51,3 @@ func (r *recorder) Rew() {
func (r *recorder) Loop(val bool) {
r.setter_bool("Mode.Loop", val)
}
// Gain returns the value of the Gain parameter
func (r *recorder) Gain() float64 {
return r.getter_float("Gain")
}
// SetGain sets the value of the Gain parameter
func (r *recorder) SetGain(val float64) {
r.setter_float("Gain", val)
}

167
remote.go
View File

@ -2,13 +2,10 @@ package voicemeeter
import (
"fmt"
"os"
"time"
log "github.com/sirupsen/logrus"
)
// Remote represents the API for a kind
// A Remote type represents the API for a kind
type Remote struct {
Kind *kind
Strip []iStrip
@ -20,9 +17,7 @@ type Remote struct {
Recorder *recorder
Midi *midi_t
pooler *pooler
timeout int
bits int
pooler *pooler
}
// String implements the fmt.stringer interface
@ -33,11 +28,11 @@ func (r *Remote) String() string {
// Login logs into the API
// then it intializes the pooler
func (r *Remote) Login() error {
err := login(r.Kind.Name, r.timeout, r.bits)
err := login(r.Kind.Name)
if err != nil {
return err
}
r.InitPooler()
r.pooler = newPooler(r.Kind)
return nil
}
@ -52,20 +47,6 @@ func (r *Remote) Logout() error {
return nil
}
// InitPooler initiates the Pooler
func (r *Remote) InitPooler() {
r.pooler = newPooler(r.Kind)
}
// Run launches the Voicemeeter GUI for a kind.
func (r *Remote) Run(kindId string) error {
err := runVoicemeeter(kindId, r.bits)
if err != nil {
return err
}
return nil
}
// Type returns the type of Voicemeeter (basic, banana, potato)
func (r *Remote) Type() string {
val, err := getVMType()
@ -85,24 +66,23 @@ func (r *Remote) Version() string {
}
// Pdirty returns true iff a parameter value has changed
func (r *Remote) Pdirty() (bool, error) {
pdirty, err := pdirty()
return pdirty, err
func (r *Remote) Pdirty() bool {
return pdirty()
}
// Mdirty returns true iff a macrobutton value has changed
func (r *Remote) Mdirty() (bool, error) {
mdirty, err := mdirty()
return mdirty, err
func (r *Remote) Mdirty() bool {
return mdirty()
}
// Sync is a helper method that waits for dirty parameters to clear
func (r *Remote) Sync() {
time.Sleep(time.Duration(vmdelay) * time.Millisecond)
clear()
for r.Pdirty() || r.Mdirty() {
}
}
// GetFloat gets a float parameter value
// Gets a float parameter value
func (r *Remote) GetFloat(name string) (float64, error) {
val, err := getParameterFloat(name)
if err != nil {
@ -111,7 +91,7 @@ func (r *Remote) GetFloat(name string) (float64, error) {
return val, nil
}
// SetFloat sets a float paramter value
// Sets a float paramter value
func (r *Remote) SetFloat(name string, value float64) error {
err := setParameterFloat(name, value)
if err != nil {
@ -120,7 +100,7 @@ func (r *Remote) SetFloat(name string, value float64) error {
return nil
}
// GetString gets a string parameter value
// Gets a string parameter value
func (r *Remote) GetString(name string) (string, error) {
val, err := getParameterString(name)
if err != nil {
@ -129,7 +109,7 @@ func (r *Remote) GetString(name string) (string, error) {
return val, nil
}
// SetString sets a string parameter value
// Sets a string paramter value
func (r *Remote) SetString(name, value string) error {
err := setParameterString(name, value)
if err != nil {
@ -148,18 +128,23 @@ func (r *Remote) SendText(script string) error {
}
// Register forwards the register method to Pooler
func (r *Remote) Register(channel chan string) {
r.pooler.Register(channel)
func (r *Remote) Register(o observer) {
r.pooler.Register(o)
}
// EventAdd adds events to the Pooler
func (r *Remote) EventAdd(events ...string) {
r.pooler.event.Add(events...)
// Deregister forwards the deregister method to Pooler
func (r *Remote) Deregister(o observer) {
r.pooler.Deregister(o)
}
// EventRemove removes events from the Pooler
func (r *Remote) EventRemove(events ...string) {
r.pooler.event.Remove(events...)
// EventAdd adds an event to the Pooler
func (r *Remote) EventAdd(event string) {
r.pooler.event.Add(event)
}
// EventRemove removes an event from the Pooler
func (r *Remote) EventRemove(event string) {
r.pooler.event.Remove(event)
}
// remoteBuilder defines the interface builder types must satisfy
@ -173,12 +158,11 @@ type remoteBuilder interface {
makeDevice() remoteBuilder
makeRecorder() remoteBuilder
makeMidi() remoteBuilder
setDefaults() remoteBuilder
Build() remoteBuilder
Get() *Remote
}
// director is responsible for directing the genericBuilder
// directory is responsible for directing the genericBuilder
type director struct {
builder remoteBuilder
}
@ -213,88 +197,81 @@ func (b *genericBuilder) setKind() remoteBuilder {
// makeStrip makes a strip slice and assigns it to remote.Strip
// []iStrip comprises of both physical and virtual strip types
func (b *genericBuilder) makeStrip() remoteBuilder {
log.Debug("building strip")
strip := make([]iStrip, b.k.NumStrip())
fmt.Println("building strip")
_strip := make([]iStrip, b.k.NumStrip())
for i := 0; i < b.k.NumStrip(); i++ {
if i < b.k.PhysIn {
strip[i] = newPhysicalStrip(i, b.k)
_strip[i] = newPhysicalStrip(i, b.k)
} else {
strip[i] = newVirtualStrip(i, b.k)
_strip[i] = newVirtualStrip(i, b.k)
}
}
b.r.Strip = strip
b.r.Strip = _strip
return b
}
// makeBus makes a bus slice and assigns it to remote.Bus
// []t_bus comprises of both physical and virtual bus types
func (b *genericBuilder) makeBus() remoteBuilder {
log.Debug("building bus")
bus := make([]iBus, b.k.NumBus())
fmt.Println("building bus")
_bus := make([]iBus, b.k.NumBus())
for i := 0; i < b.k.NumBus(); i++ {
if i < b.k.PhysOut {
bus[i] = newPhysicalBus(i, b.k)
_bus[i] = newPhysicalBus(i, b.k)
} else {
bus[i] = newVirtualBus(i, b.k)
_bus[i] = newVirtualBus(i, b.k)
}
}
b.r.Bus = bus
b.r.Bus = _bus
return b
}
// makeButton makes a button slice and assigns it to remote.Button
func (b *genericBuilder) makeButton() remoteBuilder {
log.Debug("building button")
button := make([]button, 80)
fmt.Println("building button")
_button := make([]button, 80)
for i := 0; i < 80; i++ {
button[i] = newButton(i)
_button[i] = newButton(i)
}
b.r.Button = button
b.r.Button = _button
return b
}
// makeCommand makes a command type and assigns it to remote.Command
func (b *genericBuilder) makeCommand() remoteBuilder {
log.Debug("building command")
fmt.Println("building command")
b.r.Command = newCommand()
return b
}
// makeVban makes a vban type and assigns it to remote.Vban
func (b *genericBuilder) makeVban() remoteBuilder {
log.Debug("building vban")
fmt.Println("building vban")
b.r.Vban = newVban(b.k)
return b
}
// makeDevice makes a device type and assigns it to remote.Device
func (b *genericBuilder) makeDevice() remoteBuilder {
log.Debug("building device")
fmt.Println("building device")
b.r.Device = newDevice()
return b
}
// makeRecorder makes a recorder type and assigns it to remote.Recorder
func (b *genericBuilder) makeRecorder() remoteBuilder {
log.Debug("building recorder")
fmt.Println("building recorder")
b.r.Recorder = newRecorder()
return b
}
// makeMidi makes a midi type and assigns it to remote.Midi
func (b *genericBuilder) makeMidi() remoteBuilder {
log.Debug("building midi")
fmt.Println("building midi")
b.r.Midi = newMidi()
return b
}
// setDefaults sets defaults for optional members
func (b *genericBuilder) setDefaults() remoteBuilder {
b.r.bits = 64
b.r.timeout = 2
return b
}
// Get returns a fully constructed remote type for a kind
func (b *genericBuilder) Get() *Remote {
return &b.r
@ -314,8 +291,7 @@ func (basb *genericBuilder) Build() remoteBuilder {
makeCommand().
makeVban().
makeDevice().
makeMidi().
setDefaults()
makeMidi()
}
// bananaBuilder represents a builder specific to banana type
@ -333,8 +309,7 @@ func (banb *bananaBuilder) Build() remoteBuilder {
makeVban().
makeDevice().
makeRecorder().
makeMidi().
setDefaults()
makeMidi()
}
// potatoBuilder represents a builder specific to potato type
@ -352,8 +327,7 @@ func (potb *potatoBuilder) Build() remoteBuilder {
makeVban().
makeDevice().
makeRecorder().
makeMidi().
setDefaults()
makeMidi()
}
var (
@ -361,31 +335,10 @@ var (
vmdelay int
)
type Option func(*Remote)
func WithTimeout(timeout int) Option {
return func(r *Remote) {
r.timeout = timeout
}
}
func WithBits(bits int) Option {
return func(r *Remote) {
if bits == 32 || bits == 64 {
r.bits = bits
}
}
}
func init() {
log.SetOutput(os.Stdout)
log.SetLevel(log.WarnLevel)
}
// NewRemote returns a Remote type for a kind
// this is the interface entry point
func NewRemote(kindId string, delay int, opts ...Option) (*Remote, error) {
kind, ok := kindMap[kindId]
func NewRemote(kindId string, delay int) (*Remote, error) {
_kind, ok := kindMap[kindId]
if !ok {
err := fmt.Errorf("unknown Voicemeeter kind '%s'", kindId)
return nil, err
@ -398,20 +351,14 @@ func NewRemote(kindId string, delay int, opts ...Option) (*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()
r := director.Get()
for _, opt := range opts {
opt(r)
}
return r, nil
return director.Get(), nil
}

497
strip.go
View File

@ -3,51 +3,37 @@ package voicemeeter
import (
"fmt"
"time"
log "github.com/sirupsen/logrus"
)
// iStrip defines the interface strip types must satisfy
// iStrip defines the interface bus types must satisfy
type iStrip interface {
String() string
Mute() bool
GetMute() bool
SetMute(val bool)
Mono() bool
GetMono() bool
SetMono(val bool)
Solo() bool
GetSolo() bool
SetSolo(val bool)
Limit() int
GetLimit() int
SetLimit(val int)
Label() string
GetLabel() string
SetLabel(val string)
Gain() float64
GetGain() float64
SetGain(val float64)
Mc() bool
GetMc() bool
SetMc(val bool)
Audibility() float64
GetComp() float64
SetComp(val float64)
GetGate() float64
SetGate(val float64)
GetAudibility() float64
SetAudibility(val float64)
PanX() float64
SetPanX(val float64)
PanY() float64
SetPanY(val float64)
ColorX() float64
SetColorX(val float64)
ColorY() float64
SetColorY(val float64)
FxX() float64
SetFxX(val float64)
FxY() float64
SetFxY(val float64)
GainLayer() []gainLayer
Levels() *levels
FadeTo(target float64, time_ int)
FadeBy(change float64, time_ int)
AppGain(name string, gain float64)
AppMute(name string, val bool)
Eq() *eQ
Comp() *comp
Gate() *gate
Denoiser() *denoiser
GainLayer() []gainLayer
Levels() *levels
iOutputs
}
@ -55,16 +41,12 @@ type iStrip interface {
type strip struct {
iRemote
outputs
eQ *eQ
comp *comp
gate *gate
denoiser *denoiser
gainLayer []gainLayer
levels *levels
levels
}
// Mute returns the value of the Mute parameter
func (s *strip) Mute() bool {
// GetMute returns the value of the Mute parameter
func (s *strip) GetMute() bool {
return s.getter_bool("Mute")
}
@ -73,8 +55,8 @@ func (s *strip) SetMute(val bool) {
s.setter_bool("Mute", val)
}
// Mono returns the value of the Mono parameter
func (s *strip) Mono() bool {
// GetMono returns the value of the Mono parameter
func (s *strip) GetMono() bool {
return s.getter_bool("Mono")
}
@ -83,8 +65,8 @@ func (s *strip) SetMono(val bool) {
s.setter_bool("Mono", val)
}
// Solo returns the value of the Solo parameter
func (s *strip) Solo() bool {
// GetSolo returns the value of the Solo parameter
func (s *strip) GetSolo() bool {
return s.getter_bool("Solo")
}
@ -93,8 +75,8 @@ func (s *strip) SetSolo(val bool) {
s.setter_bool("Solo", val)
}
// Limit returns the value of the Limit parameter
func (s *strip) Limit() int {
// GetLimit returns the value of the Limit parameter
func (s *strip) GetLimit() int {
return s.getter_int("Limit")
}
@ -103,8 +85,8 @@ func (s *strip) SetLimit(val int) {
s.setter_int("Limit", val)
}
// Label returns the value of the Label parameter
func (s *strip) Label() string {
// GetLabel returns the value of the Label parameter
func (s *strip) GetLabel() string {
return s.getter_string("Label")
}
@ -113,8 +95,8 @@ func (s *strip) SetLabel(val string) {
s.setter_string("Label", val)
}
// Gain returns the value of the Gain parameter
func (s *strip) Gain() float64 {
// GetGain returns the value of the Gain parameter
func (s *strip) GetGain() float64 {
return s.getter_float("Gain")
}
@ -123,31 +105,6 @@ func (s *strip) SetGain(val float64) {
s.setter_float("Gain", val)
}
// PanX returns the value of the Pan_X parameter
func (s *strip) PanX() float64 {
return s.getter_float("Pan_x")
}
// SetPanX sets the value of the Pan_X parameter
func (s *strip) SetPanX(val float64) {
s.setter_float("Pan_x", val)
}
// PanY returns the value of the Pan_Y parameter
func (s *strip) PanY() float64 {
return s.getter_float("Pan_y")
}
// SetPanY sets the value of the Pan_Y parameter
func (s *strip) SetPanY(val float64) {
s.setter_float("Pan_y", val)
}
// Eq returns the eQ field
func (s *strip) Eq() *eQ {
return s.eQ
}
// GainLayer returns the gainlayer field
func (s *strip) GainLayer() []gainLayer {
return s.gainLayer
@ -155,7 +112,7 @@ func (s *strip) GainLayer() []gainLayer {
// Levels returns the levels field
func (s *strip) Levels() *levels {
return s.levels
return &s.levels
}
// FadeTo sets the value of gain to target over at time interval of time_
@ -170,218 +127,128 @@ func (s *strip) FadeBy(change float64, time_ int) {
time.Sleep(time.Millisecond)
}
// PhysicalStrip represents a single physical strip
type PhysicalStrip struct {
// physicalStrip represents a single physical strip
type physicalStrip struct {
strip
}
// newPhysicalStrip returns a PhysicalStrip type
// newPhysicalStrip returns a physicalStrip type cast to an iStrip
func newPhysicalStrip(i int, k *kind) iStrip {
o := newOutputs(fmt.Sprintf("strip[%d]", i), i)
e := newEq(fmt.Sprintf("strip[%d].EQ", i), i)
c := newComp(i)
g := newGate(i)
d := newDenoiser(i)
gl := make([]gainLayer, 8)
for j := 0; j < 8; j++ {
gl[j] = newGainLayer(i, j)
}
l := newStripLevels(i, k)
ps := PhysicalStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, e, c, g, d, gl, l}}
return &ps
ps := physicalStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, gl, l}}
return iStrip(&ps)
}
// String implements fmt.stringer interface
func (p *PhysicalStrip) String() string {
func (p *physicalStrip) String() string {
return fmt.Sprintf("PhysicalStrip%d", p.index)
}
// Audibility returns the value of the Audibility parameter
func (p *PhysicalStrip) Audibility() float64 {
// GetComp returns the value of the Comp parameter
func (p *physicalStrip) GetComp() float64 {
return p.getter_float("Comp")
}
// SetComp sets the value of the Comp parameter
func (p *physicalStrip) SetComp(val float64) {
p.setter_float("Comp", val)
}
// GetGate returns the value of the Gate parameter
func (p *physicalStrip) GetGate() float64 {
return p.getter_float("Gate")
}
// SetGate sets the value of the Gate parameter
func (p *physicalStrip) SetGate(val float64) {
p.setter_float("Gate", val)
}
// GetAudibility returns the value of the Audibility parameter
func (p *physicalStrip) GetAudibility() float64 {
return p.getter_float("Audibility")
}
// SetAudibility sets the value of the Audibility parameter
func (p *PhysicalStrip) SetAudibility(val float64) {
func (p *physicalStrip) SetAudibility(val float64) {
p.setter_float("Audibility", val)
}
// Mc logs a warning reason invalid parameter
// it always returns zero value
func (p *PhysicalStrip) Mc() bool {
log.Warn("invalid parameter MC for physicalStrip")
return false
// GetMc panics reason invalid parameter
func (p *physicalStrip) GetMc() bool {
panic("invalid parameter MC for physicalStrip")
}
// SetMc logs a warning reason invalid parameter
func (p *PhysicalStrip) SetMc(val bool) {
log.Warn("invalid parameter MC for physicalStrip")
// SetMc panics reason invalid parameter
func (p *physicalStrip) SetMc(val bool) {
panic("invalid parameter MC for physicalStrip")
}
// Comp returns the comp field
func (p *PhysicalStrip) Comp() *comp {
return p.comp
}
// Gate returns the gate field
func (p *PhysicalStrip) Gate() *gate {
return p.gate
}
// Denoiser returns the denoiser field
func (p *PhysicalStrip) Denoiser() *denoiser {
return p.denoiser
}
// ColorX returns the value of the Color_X parameter
func (p *PhysicalStrip) ColorX() float64 {
return p.getter_float("Color_x")
}
// SetColorX sets the value of the Color_X parameter
func (p *PhysicalStrip) SetColorX(val float64) {
p.setter_float("Color_x", val)
}
// ColorY returns the value of the Color_Y parameter
func (p *PhysicalStrip) ColorY() float64 {
return p.getter_float("Color_y")
}
// SetColorY sets the value of the Color_Y parameter
func (p *PhysicalStrip) SetColorY(val float64) {
p.setter_float("Color_y", val)
}
// FxX returns the value of the Color_X parameter
func (p *PhysicalStrip) FxX() float64 {
return p.getter_float("fx_x")
}
// SetFxX sets the value of the Color_X parameter
func (p *PhysicalStrip) SetFxX(val float64) {
p.setter_float("fx_x", val)
}
// FxY returns the value of the Color_Y parameter
func (p *PhysicalStrip) FxY() float64 {
return p.getter_float("fx_y")
}
// SetFxY sets the value of the Color_Y parameter
func (p *PhysicalStrip) SetFxY(val float64) {
p.setter_float("fx_y", val)
}
// VirtualStrip represents a single virtual strip
type VirtualStrip struct {
// virtualStrip represents a single virtual strip
type virtualStrip struct {
strip
}
// newVirtualStrip returns a VirtualStrip type
// newVirtualStrip returns a virtualStrip type cast to an iStrip
func newVirtualStrip(i int, k *kind) iStrip {
o := newOutputs(fmt.Sprintf("strip[%d]", i), i)
e := newEq(fmt.Sprintf("strip[%d].EQ", i), i)
c := newComp(i)
g := newGate(i)
d := newDenoiser(i)
gl := make([]gainLayer, 8)
for j := 0; j < 8; j++ {
gl[j] = newGainLayer(i, j)
}
l := newStripLevels(i, k)
vs := VirtualStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, e, c, g, d, gl, l}}
return &vs
vs := virtualStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, gl, l}}
return iStrip(&vs)
}
// String implements fmt.stringer interface
func (v *VirtualStrip) String() string {
func (v *virtualStrip) String() string {
return fmt.Sprintf("VirtualStrip%d", v.index)
}
// Comp returns the comp field
func (v *VirtualStrip) Comp() *comp {
return v.comp
}
// Gate returns the gate field
func (v *VirtualStrip) Gate() *gate {
return v.gate
}
// Denoiser returns the denoiser field
func (v *VirtualStrip) Denoiser() *denoiser {
return v.denoiser
}
// Mc returns the value of the MC parameter
func (v *VirtualStrip) Mc() bool {
// GetMc returns the value of the MC parameter
func (v *virtualStrip) GetMc() bool {
return v.getter_bool("MC")
}
// SetMc sets the value of the MC parameter
func (v *VirtualStrip) SetMc(val bool) {
func (v *virtualStrip) SetMc(val bool) {
v.setter_bool("MC", val)
}
// ColorX logs a warning reason invalid parameter
// it always returns zero value
func (v *VirtualStrip) ColorX() float64 {
log.Warn("invalid parameter ColorX for virtualStrip")
return 0
// GetComp panics reason invalid parameter
func (v *virtualStrip) GetComp() float64 {
panic("invalid parameter Comp for virtualStrip")
}
// SetColorX logs a warning reason invalid parameter
func (v *VirtualStrip) SetColorX(val float64) {
log.Warn("invalid parameter ColorX for virtualStrip")
// SetComp panics reason invalid parameter
func (v *virtualStrip) SetComp(val float64) {
panic("invalid parameter Comp for virtualStrip")
}
// ColorY logs a warning reason invalid parameter
// it always returns zero value
func (v *VirtualStrip) ColorY() float64 {
log.Warn("invalid parameter ColorY for virtualStrip")
return 0
// GetGate panics reason invalid parameter
func (v *virtualStrip) GetGate() float64 {
panic("invalid parameter Gate for virtualStrip")
}
// SetColorY logs a warning reason invalid parameter
func (v *VirtualStrip) SetColorY(val float64) {
log.Warn("invalid parameter ColorY for virtualStrip")
// SetGate panics reason invalid parameter
func (v *virtualStrip) SetGate(val float64) {
panic("invalid parameter Gate for virtualStrip")
}
// FxX logs a warning reason invalid parameter
// it always returns zero value
func (v *VirtualStrip) FxX() float64 {
log.Warn("invalid parameter FxX for virtualStrip")
return 0
// GetAudibility panics reason invalid parameter
func (v *virtualStrip) GetAudibility() float64 {
panic("invalid parameter Audibility for virtualStrip")
}
// SetFxX logs a warning reason invalid parameter
func (v *VirtualStrip) SetFxX(val float64) {
log.Warn("invalid parameter SetFxX for virtualStrip")
}
// FxY logs a warning reason invalid parameter
// it always returns zero value
func (v *VirtualStrip) FxY() float64 {
log.Warn("invalid parameter FxY for virtualStrip")
return 0
}
// SetFxY logs a warning reason invalid parameter
func (v *VirtualStrip) SetFxY(val float64) {
log.Warn("invalid parameter SetFxY for virtualStrip")
}
// Audibility logs a warning reason invalid parameter
// it always returns zero value
func (v *VirtualStrip) Audibility() float64 {
log.Warn("invalid parameter Audibility for virtualStrip")
return 0
}
// SetAudibility logs a warning reason invalid parameter
func (v *VirtualStrip) SetAudibility(val float64) {
log.Warn("invalid parameter Audibility for virtualStrip")
// SetAudibility panics reason invalid parameter
func (v *virtualStrip) SetAudibility(val float64) {
panic("invalid parameter Audibility for virtualStrip")
}
// AppGain sets the gain in db by val for the app matching name.
@ -400,166 +267,6 @@ func (v *strip) AppMute(name string, val bool) {
v.setter_string("AppMute", fmt.Sprintf("(\"%s\", %f)", name, float64(value)))
}
type denoiser struct {
iRemote
}
func newDenoiser(i int) *denoiser {
return &denoiser{iRemote{fmt.Sprintf("strip[%d].denoiser", i), i}}
}
func (d *denoiser) Knob() float64 {
return d.getter_float("")
}
func (d *denoiser) SetKnob(val float64) {
d.setter_float("", val)
}
type comp struct {
iRemote
}
func newComp(i int) *comp {
return &comp{iRemote{fmt.Sprintf("strip[%d].comp", i), i}}
}
func (c *comp) Knob() float64 {
return c.getter_float("")
}
func (c *comp) SetKnob(val float64) {
c.setter_float("", val)
}
func (c *comp) GainIn() float64 {
return c.getter_float("GainIn")
}
func (c *comp) SetGainIn(val float64) {
c.setter_float("GainIn", val)
}
func (c *comp) Ratio() float64 {
return c.getter_float("Ratio")
}
func (c *comp) SetRatio(val float64) {
c.setter_float("Ratio", val)
}
func (c *comp) Threshold() float64 {
return c.getter_float("Threshold")
}
func (c *comp) SetThreshold(val float64) {
c.setter_float("Threshold", val)
}
func (c *comp) Attack() float64 {
return c.getter_float("Attack")
}
func (c *comp) SetAttack(val float64) {
c.setter_float("Attack", val)
}
func (c *comp) Release() float64 {
return c.getter_float("Release")
}
func (c *comp) SetRelease(val float64) {
c.setter_float("Release", val)
}
func (c *comp) Knee() float64 {
return c.getter_float("Knee")
}
func (c *comp) SetKnee(val float64) {
c.setter_float("Knee", val)
}
func (c *comp) GainOut() float64 {
return c.getter_float("GainOut")
}
func (c *comp) SetGainOut(val float64) {
c.setter_float("GainOut", val)
}
func (c *comp) MakeUp() bool {
return c.getter_bool("MakeUp")
}
func (c *comp) SetMakeUp(val bool) {
c.setter_bool("MakeUp", val)
}
type gate struct {
iRemote
}
func newGate(i int) *gate {
return &gate{iRemote{fmt.Sprintf("strip[%d].gate", i), i}}
}
func (g *gate) Knob() float64 {
return g.getter_float("")
}
func (g *gate) SetKnob(val float64) {
g.setter_float("", val)
}
func (g *gate) Threshold() float64 {
return g.getter_float("Threshold")
}
func (g *gate) SetThreshold(val float64) {
g.setter_float("Threshold", val)
}
func (g *gate) Damping() float64 {
return g.getter_float("Damping")
}
func (g *gate) SetDamping(val float64) {
g.setter_float("Damping", val)
}
func (g *gate) BPSidechain() int {
return g.getter_int("BPSidechain")
}
func (g *gate) SetBPSidechain(val int) {
g.setter_int("BPSidechain", val)
}
func (g *gate) Attack() float64 {
return g.getter_float("Attack")
}
func (g *gate) SetAttack(val float64) {
g.setter_float("Attack", val)
}
func (g *gate) Hold() float64 {
return g.getter_float("Hold")
}
func (g *gate) SetHold(val float64) {
g.setter_float("Hold", val)
}
func (g *gate) Release() float64 {
return g.getter_float("Release")
}
func (g *gate) SetRelease(val float64) {
g.setter_float("Release", val)
}
// gainLayer represents the 8 gainlayers for a single strip
type gainLayer struct {
iRemote
@ -582,7 +289,7 @@ func (gl *gainLayer) Set(val float64) {
}
// newStripLevels returns a levels struct
func newStripLevels(i int, k *kind) *levels {
func newStripLevels(i int, k *kind) levels {
var init int
var os int
if i < k.PhysIn {
@ -592,15 +299,15 @@ 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, "strip"}
return levels{iRemote{fmt.Sprintf("strip[%d]", i), i}, k, init, os, "strip"}
}
// PreFader returns the level values for this strip, PREFADER mode
func (l *levels) PreFader() []float64 {
_levelCache.stripMode = 0
levels := make([]float64, l.offset)
for i := range levels {
levels[i] = convertLevel(_levelCache.stripLevels[l.init+i])
var levels []float64
for i := l.init; i < l.init+l.offset; i++ {
levels = append(levels, convertLevel(_levelCache.stripLevels[i]))
}
return levels
}
@ -608,9 +315,9 @@ func (l *levels) PreFader() []float64 {
// PostFader returns the level values for this strip, POSTFADER mode
func (l *levels) PostFader() []float64 {
_levelCache.stripMode = 1
levels := make([]float64, l.offset)
for i := range levels {
levels[i] = convertLevel(_levelCache.stripLevels[l.init+i])
var levels []float64
for i := l.init; i < l.init+l.offset; i++ {
levels = append(levels, convertLevel(_levelCache.stripLevels[i]))
}
return levels
}
@ -618,9 +325,9 @@ func (l *levels) PostFader() []float64 {
// PostMute returns the level values for this strip, POSTMUTE mode
func (l *levels) PostMute() []float64 {
_levelCache.stripMode = 2
levels := make([]float64, l.offset)
for i := range levels {
levels[i] = convertLevel(_levelCache.stripLevels[l.init+i])
var levels []float64
for i := l.init; i < l.init+l.offset; i++ {
levels = append(levels, convertLevel(_levelCache.stripLevels[i]))
}
return levels
}

View File

@ -1,17 +1,14 @@
package voicemeeter_test
import (
"bytes"
"log"
"testing"
log "github.com/sirupsen/logrus"
"github.com/onyx-and-iris/voicemeeter/v2"
"github.com/onyx-and-iris/voicemeeter-api-go"
)
var (
logstring bytes.Buffer
vm, err = voicemeeter.NewRemote("potato", 30)
vm, err = voicemeeter.NewRemote("potato", 30)
)
func TestMain(m *testing.M) {
@ -25,7 +22,5 @@ func TestMain(m *testing.M) {
}
defer vm.Logout()
log.SetOutput(&logstring)
m.Run()
}

View File

@ -9,39 +9,39 @@ import (
func TestStrip0Mute(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[0].SetMute(true)
t.Run("Should return true when Strip[0].SetMute(true)", func(t *testing.T) {
assert.True(t, vm.Strip[0].Mute())
t.Run("Should return true when SetMute(true)", func(t *testing.T) {
assert.True(t, vm.Strip[0].GetMute())
})
vm.Strip[0].SetMute(false)
t.Run("Should return false when Strip[0].SetMute(false)", func(t *testing.T) {
assert.False(t, vm.Strip[0].Mute())
t.Run("Should return false when SetMute(false)", func(t *testing.T) {
assert.False(t, vm.Strip[0].GetMute())
})
}
func TestStrip3A1(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[3].SetA1(true)
t.Run("Should return true when Strip[3].SetA1(true)", func(t *testing.T) {
assert.True(t, vm.Strip[3].A1())
t.Run("Should return true when SetA1(true)", func(t *testing.T) {
assert.True(t, vm.Strip[3].GetA1())
})
vm.Strip[3].SetA1(false)
t.Run("Should return false when Strip[3].SetA1(false)", func(t *testing.T) {
assert.False(t, vm.Strip[3].A1())
t.Run("Should return false when SetA1(false)", func(t *testing.T) {
assert.False(t, vm.Strip[3].GetA1())
})
}
func TestStrip2Limit(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[2].SetLimit(-8)
t.Run("Should return -8 when Strip[2].SetLimit(-8)", func(t *testing.T) {
assert.Equal(t, vm.Strip[2].Limit(), -8)
t.Run("Should return -8 when SetLimit(-8)", func(t *testing.T) {
assert.Equal(t, vm.Strip[2].GetLimit(), -8)
})
vm.Strip[2].SetLimit(-32)
t.Run("Should return -32 when Strip[2].SetLimit(-32)", func(t *testing.T) {
assert.Equal(t, vm.Strip[2].Limit(), -32)
t.Run("Should return -32 when SetLimit(-8)", func(t *testing.T) {
assert.Equal(t, vm.Strip[2].GetLimit(), -32)
})
}
@ -49,210 +49,162 @@ func TestStrip2Limit(t *testing.T) {
func TestStrip4Label(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[4].SetLabel("test0")
t.Run("Should return test0 when Strip[4].SetLabel('test0')", func(t *testing.T) {
assert.Equal(t, "test0", vm.Strip[4].Label())
t.Run("Should return test0 when SetLimit('test0')", func(t *testing.T) {
assert.Equal(t, "test0", vm.Strip[4].GetLabel())
})
vm.Strip[4].SetLabel("test1")
t.Run("Should return test1 when Strip[4].SetLabel('test1')", func(t *testing.T) {
assert.Equal(t, "test1", vm.Strip[4].Label())
t.Run("Should return test1 when SetLimit('test1')", func(t *testing.T) {
assert.Equal(t, "test1", vm.Strip[4].GetLabel())
})
}
func TestStrip4Gain(t *testing.T) {
func TestStrip5Gain(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[4].SetGain(-20.8)
t.Run("Should return -20.8 when Strip[4].SetGain(-20.8)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].Gain(), -20.8)
t.Run("Should return -20.8 when SetGain(-20.8)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].GetGain(), -20.8)
})
vm.Strip[4].SetGain(-3.6)
t.Run("Should return -3.6 when Strip[4].SetGain(-3.6)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].Gain(), -3.6)
t.Run("Should return -3.6 when SetGain(-3.6)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].GetGain(), -3.6)
})
}
func TestStrip4CompKnob(t *testing.T) {
func TestStrip3Comp(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[4].Comp().SetKnob(8.1)
t.Run("Should return 8.1 when Strip[4].Comp().SetKnob(8.1)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].Comp().Knob(), 8.1)
vm.Strip[4].SetComp(8.1)
t.Run("Should return 8.1 when SetGain(8.1)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].GetComp(), 8.1)
})
vm.Strip[4].Comp().SetKnob(1.6)
t.Run("Should return 1.6 when Strip[4].Comp().SetKnob(1.6)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].Comp().Knob(), 1.6)
})
}
func TestStrip0CompGainIn(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[0].Comp().SetGainIn(3.4)
t.Run("Should return 3.4 when Strip[0].Comp().SetGainIn(3.4)", func(t *testing.T) {
assert.Equal(t, vm.Strip[0].Comp().GainIn(), 3.4)
})
vm.Strip[0].Comp().SetGainIn(-19.3)
t.Run("Should return -19.3 when Strip[0].Comp().SetGainIn(-19.3)", func(t *testing.T) {
assert.Equal(t, vm.Strip[0].Comp().GainIn(), -19.3)
})
}
func TestStrip3GateKnob(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[3].Gate().SetKnob(8.1)
t.Run("Should return 8.1 when Strip[3].Gate().SetKnob(8.1)", func(t *testing.T) {
assert.Equal(t, vm.Strip[3].Gate().Knob(), 8.1)
})
vm.Strip[3].Gate().SetKnob(1.6)
t.Run("Should return 1.6 when Strip[3].Gate().SetKnob(1.6)", func(t *testing.T) {
assert.Equal(t, vm.Strip[3].Gate().Knob(), 1.6)
})
}
func TestStrip0CompAttack(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[0].Comp().SetAttack(3.4)
t.Run("Should return 3.4 when Strip[0].Comp().SetAttack(3.4)", func(t *testing.T) {
assert.Equal(t, vm.Strip[0].Comp().Attack(), 3.4)
})
vm.Strip[0].Comp().SetAttack(190.3)
t.Run("Should return 190.3 when Strip[0].Comp().SetAttack(190.3)", func(t *testing.T) {
assert.Equal(t, vm.Strip[0].Comp().Attack(), 190.3)
})
}
func TestStrip4Denoiser(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[4].Denoiser().SetKnob(2.1)
t.Run("Should return 2.1 when Strip[4].Denoiser().SetKnob(2.1)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].Denoiser().Knob(), 2.1)
})
vm.Strip[4].Denoiser().SetKnob(5.6)
t.Run("Should return 5.6 when Strip[4].Denoiser().SetKnob(5.6)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].Denoiser().Knob(), 5.6)
vm.Strip[4].SetComp(1.6)
t.Run("Should return 1.6 when SetGain(1.6)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].GetComp(), 1.6)
})
}
func TestStrip5Mc(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[5].SetMc(true)
t.Run("Should return true when Strip[5].SetMc(true)", func(t *testing.T) {
assert.True(t, vm.Strip[5].Mc())
t.Run("Should return true when SetMc(true)", func(t *testing.T) {
assert.True(t, vm.Strip[5].GetMc())
})
vm.Strip[5].SetMc(false)
t.Run("Should return false when Strip[5].SetMc(false)", func(t *testing.T) {
assert.False(t, vm.Strip[5].Mc())
t.Run("Should return false when SetMc(false)", func(t *testing.T) {
assert.False(t, vm.Strip[5].GetMc())
})
}
func TestStrip2GainLayer3(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[2].GainLayer()[3].Set(-18.3)
t.Run("Should return -18.3 when Strip[2].GainLayer()[3].Set(-18.3)", func(t *testing.T) {
t.Run("Should return -18.3 when SetMc(true)", func(t *testing.T) {
assert.Equal(t, vm.Strip[2].GainLayer()[3].Get(), -18.3)
})
vm.Strip[2].GainLayer()[3].Set(-25.6)
t.Run("Should return -25.6 when Strip[2].GainLayer()[3].Set(-25.6)", func(t *testing.T) {
t.Run("Should return -25.6 when SetMc(true)", func(t *testing.T) {
assert.Equal(t, vm.Strip[2].GainLayer()[3].Get(), -25.6)
})
}
func TestBus3EqOn(t *testing.T) {
func TestBus3Eq(t *testing.T) {
//t.Skip("skipping test")
vm.Bus[3].Eq().SetOn(true)
t.Run("Should return true when Bus[3].Eq().SetOn(true)", func(t *testing.T) {
assert.True(t, vm.Bus[3].Eq().On())
vm.Bus[3].SetEq(true)
t.Run("Should return true when SetEq(true)", func(t *testing.T) {
assert.True(t, vm.Bus[3].GetEq())
})
vm.Bus[3].Eq().SetOn(false)
t.Run("Should return false when Bus[3].SetEq(false)", func(t *testing.T) {
assert.False(t, vm.Bus[3].Eq().On())
vm.Bus[3].SetEq(false)
t.Run("Should return false when SetEq(false)", func(t *testing.T) {
assert.False(t, vm.Bus[3].GetEq())
})
}
func TestBus4Label(t *testing.T) {
//t.Skip("skipping test")
vm.Bus[4].SetLabel("test0")
t.Run("Should return test0 when Bus[4].SetLabel('test0')", func(t *testing.T) {
assert.Equal(t, "test0", vm.Bus[4].Label())
t.Run("Should return test0 when SetEq('test0')", func(t *testing.T) {
assert.Equal(t, "test0", vm.Bus[4].GetLabel())
})
vm.Bus[4].SetLabel("test1")
t.Run("Should return test1 when Bus[4].SetLabel('test1')", func(t *testing.T) {
assert.Equal(t, "test1", vm.Bus[4].Label())
t.Run("Should return test1 when SetEq('test1')", func(t *testing.T) {
assert.Equal(t, "test1", vm.Bus[4].GetLabel())
})
}
func TestBus3ModeAmix(t *testing.T) {
//t.Skip("skipping test")
vm.Bus[3].Mode().SetAmix(true)
t.Run("Should return true when Bus[3].Mode().SetAmix(true)", func(t *testing.T) {
assert.True(t, vm.Bus[3].Mode().Amix())
t.Run("Should return true when Mode().SetAmix(true)", func(t *testing.T) {
assert.True(t, vm.Bus[3].Mode().GetAmix())
})
}
func TestVbanInStream0On(t *testing.T) {
//t.Skip("skipping test")
vm.Vban.InStream[0].SetOn(true)
t.Run("Should return true when Vban.InStream[0].SetOn(true)", func(t *testing.T) {
assert.True(t, vm.Vban.InStream[0].On())
t.Run("Should return true when SetOn(true)", func(t *testing.T) {
assert.True(t, vm.Vban.InStream[0].GetOn())
})
vm.Vban.InStream[0].SetOn(false)
t.Run("Should return false when Vban.InStream[0].SetOn(false)", func(t *testing.T) {
assert.False(t, vm.Vban.InStream[0].On())
t.Run("Should return false when SetOn(false)", func(t *testing.T) {
assert.False(t, vm.Vban.InStream[0].GetOn())
})
}
func TestVbanOutStream6On(t *testing.T) {
//t.Skip("skipping test")
vm.Vban.OutStream[6].SetOn(true)
t.Run("Should return true when Vban.OutStream[6].SetOn(true)", func(t *testing.T) {
assert.True(t, vm.Vban.OutStream[6].On())
t.Run("Should return true when SetOn(true)", func(t *testing.T) {
assert.True(t, vm.Vban.OutStream[6].GetOn())
})
vm.Vban.OutStream[6].SetOn(false)
t.Run("Should return false when Vban.OutStream[6].SetOn(false)", func(t *testing.T) {
assert.False(t, vm.Vban.OutStream[6].On())
t.Run("Should return false when SetOn(false)", func(t *testing.T) {
assert.False(t, vm.Vban.OutStream[6].GetOn())
})
}
func TestVbanOutStream3Name(t *testing.T) {
//t.Skip("skipping test")
t.Skip("skipping test")
vm.Vban.OutStream[3].SetName("test0")
t.Run("Should return test0 when Vban.OutStream[3].SetName('test0')", func(t *testing.T) {
assert.Equal(t, "test0", vm.Vban.OutStream[3].Name())
t.Run("Should return test0 when SetName('test0')", func(t *testing.T) {
assert.Equal(t, "test0", vm.Vban.OutStream[3].GetName())
})
vm.Vban.OutStream[3].SetName("test1")
t.Run("Should return test1 when Vban.OutStream[3].SetName('test1')", func(t *testing.T) {
assert.Equal(t, "test1", vm.Vban.OutStream[3].Name())
t.Run("Should return test1 when SetName('test1')", func(t *testing.T) {
assert.Equal(t, "test1", vm.Vban.OutStream[3].GetName())
})
}
func TestVbanInStream4Bit(t *testing.T) {
//t.Skip("skipping test")
vm.Vban.InStream[4].SetBit(16)
t.Run("Should log 'bit is readonly for vban instreams' when instream Vban.InStream[4].SetBit(16)", func(t *testing.T) {
assert.Contains(t, logstring.String(), "bit is readonly for vban instreams")
t.Skip("skipping test")
t.Run("Should panic when instream SetBit(16)", func(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Error("expected panic")
}
}()
vm.Vban.InStream[4].SetBit(16)
})
}
func TestVbanOutStream4Bit(t *testing.T) {
//t.Skip("skipping test")
vm.Vban.OutStream[4].SetBit(16)
t.Run("Should return 16 when Vban.OutStream[4].SetBit(16)", func(t *testing.T) {
assert.Equal(t, vm.Vban.OutStream[4].Bit(), 16)
t.Run("Should return 16 when SetBit(16)", func(t *testing.T) {
assert.Equal(t, vm.Vban.OutStream[4].GetBit(), 16)
})
vm.Vban.OutStream[4].SetBit(24)
t.Run("Should return 24 when Vban.OutStream[4].SetBit(24)", func(t *testing.T) {
assert.Equal(t, vm.Vban.OutStream[4].Bit(), 24)
t.Run("Should return 24 when SetBit(24)", func(t *testing.T) {
assert.Equal(t, vm.Vban.OutStream[4].GetBit(), 24)
})
}

View File

@ -28,8 +28,8 @@ func roundFloat(val float64, precision uint) float64 {
// convertLevel performs the necessary math for a channel level
func convertLevel(i float64) float64 {
if i > 0 {
val := 20 * math.Log10(i)
return roundFloat(val, 1)
val := 20 * math.Log10(float64(i))
return float64(roundFloat(float64(val), 1))
}
return -200.0
}

129
vban.go
View File

@ -1,99 +1,95 @@
package voicemeeter
import (
"fmt"
log "github.com/sirupsen/logrus"
)
import "fmt"
// iVban defines the interface vban types must satisfy
type iVban interface {
On() bool
GetOn() bool
SetOn(val bool)
Name() string
GetName() string
SetName(val string)
Ip() string
GetIp() string
SetIp(val string)
Port() int
GetPort() int
SetPort(val int)
Sr() int
GetSr() int
SetSr(val int)
Channel() int
GetChannel() int
SetChannel(val int)
Bit() int
GetBit() int
SetBit(val int)
Quality() int
GetQuality() int
SetQuality(val int)
Route() int
GetRoute() int
SetRoute(val int)
}
type stream struct {
type vbanStream struct {
iRemote
}
// On returns the value of the On parameter
func (v *stream) On() bool {
// GetOn returns the value of the On parameter
func (v *vbanStream) GetOn() bool {
return v.getter_bool("On")
}
// SetOn sets the value of the On parameter
func (v *stream) SetOn(val bool) {
func (v *vbanStream) SetOn(val bool) {
v.setter_bool("On", val)
}
// Name returns the value of the Name parameter
func (v *stream) Name() string {
// GetName returns the value of the Name parameter
func (v *vbanStream) GetName() string {
return v.getter_string("Name")
}
// SetLabel sets the value of the Name parameter
func (v *stream) SetName(val string) {
func (v *vbanStream) SetName(val string) {
v.setter_string("Name", val)
}
// Ip returns the value of the Ip parameter
func (v *stream) Ip() string {
// GetIp returns the value of the Ip parameter
func (v *vbanStream) GetIp() string {
return v.getter_string("Ip")
}
// SetIp sets the value of the Ip parameter
func (v *stream) SetIp(val string) {
func (v *vbanStream) SetIp(val string) {
v.setter_string("Ip", val)
}
// Port returns the value of the Port parameter
func (v *stream) Port() int {
// GetPort returns the value of the Port parameter
func (v *vbanStream) GetPort() int {
return v.getter_int("Port")
}
// SetPort sets the value of the Port parameter
func (v *stream) SetPort(val int) {
func (v *vbanStream) SetPort(val int) {
v.setter_int("Port", val)
}
// Sr returns the value of the Sr parameter
func (v *stream) Sr() int {
// GetSr returns the value of the Sr parameter
func (v *vbanStream) GetSr() int {
return v.getter_int("Sr")
}
// SetSr sets the value of the Sr parameter
func (v *stream) SetSr(val int) {
func (v *vbanStream) SetSr(val int) {
v.setter_int("Sr", val)
}
// Channel returns the value of the Channel parameter
func (v *stream) Channel() int {
// GetChannel returns the value of the Channel parameter
func (v *vbanStream) GetChannel() int {
return v.getter_int("Channel")
}
// SetChannel sets the value of the Channel parameter
func (v *stream) SetChannel(val int) {
func (v *vbanStream) SetChannel(val int) {
v.setter_int("Channel", val)
}
// Bit returns the value of the Bit parameter
func (v *stream) Bit() int {
// GetBit returns the value of the Bit parameter
func (v *vbanStream) GetBit() int {
val := v.getter_int("Bit")
if val == 1 {
return 16
@ -102,70 +98,69 @@ func (v *stream) Bit() int {
}
// SetBit sets the value of the Bit parameter
func (v *stream) SetBit(val int) {
func (v *vbanStream) SetBit(val int) {
switch val {
case 16:
val = 1
case 24:
val = 2
default:
log.Warn("expected value 16 or 24")
return
panic("expected value 16 or 24")
}
v.setter_int("Bit", val)
}
// Quality returns the value of the Quality parameter
func (v *stream) Quality() int {
// GetQuality returns the value of the Quality parameter
func (v *vbanStream) GetQuality() int {
return v.getter_int("Quality")
}
// SetQuality sets the value of the Quality parameter
func (v *stream) SetQuality(val int) {
func (v *vbanStream) SetQuality(val int) {
v.setter_int("Quality", val)
}
// Route returns the value of the Route parameter
func (v *stream) Route() int {
// GetRoute returns the value of the Route parameter
func (v *vbanStream) GetRoute() int {
return v.getter_int("Route")
}
// SetRoute sets the value of the Route parameter
func (v *stream) SetRoute(val int) {
func (v *vbanStream) SetRoute(val int) {
v.setter_int("Route", val)
}
type VbanInstream struct {
stream
type vbanInStream struct {
vbanStream
}
func newVbanInStream(i int) iVban {
vbi := VbanInstream{stream{iRemote{fmt.Sprintf("vban.instream[%d]", i), i}}}
return &vbi
vbi := vbanInStream{vbanStream{iRemote{fmt.Sprintf("vban.instream[%d]", i), i}}}
return iVban(&vbi)
}
// SetSr logs a warning reason read only
func (vbi *VbanInstream) SetSr(val int) {
log.Warn("SR is readonly for vban instreams")
// SetSr panics reason read only
func (vbi *vbanInStream) SetSr(val int) {
panic("SR is readonly for vban instreams")
}
// SetChannel logs a warning reason read only
func (vbi *VbanInstream) SetChannel(val int) {
log.Warn("channel is readonly for vban instreams")
// SetChannel panics reason read only
func (vbi *vbanInStream) SetChannel(val int) {
panic("channel is readonly for vban instreams")
}
// SetBit logs a warning reason read only
func (vbi *VbanInstream) SetBit(val int) {
log.Warn("bit is readonly for vban instreams")
// SetBit panics reason read only
func (vbi *vbanInStream) SetBit(val int) {
panic("bit is readonly for vban instreams")
}
type VbanOutStream struct {
stream
type vbanOutStream struct {
vbanStream
}
func newVbanOutStream(i int) iVban {
vbo := VbanOutStream{stream{iRemote{fmt.Sprintf("vban.outstream[%d]", i), i}}}
return &vbo
vbo := vbanOutStream{vbanStream{iRemote{fmt.Sprintf("vban.outstream[%d]", i), i}}}
return iVban(&vbo)
}
type vban struct {
@ -174,17 +169,17 @@ type vban struct {
}
func newVban(k *kind) *vban {
vbanIn := make([]iVban, k.VbanIn)
_vbanIn := make([]iVban, k.VbanIn)
for i := 0; i < k.VbanIn; i++ {
vbanIn[i] = newVbanInStream(i)
_vbanIn[i] = newVbanInStream(i)
}
vbanOut := make([]iVban, k.VbanOut)
_vbanOut := make([]iVban, k.VbanOut)
for i := 0; i < k.VbanOut; i++ {
vbanOut[i] = newVbanOutStream(i)
_vbanOut[i] = newVbanOutStream(i)
}
return &vban{
InStream: vbanIn,
OutStream: vbanOut,
InStream: _vbanIn,
OutStream: _vbanOut,
}
}