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) # Dependency directories (remove the comment below to include it)
# vendor/ # 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] ## [Unreleased]
- [x] - [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
## [1.7.0] - 2022-09-14 ## [1.7.0] - 2022-09-14
### Added ### Added
- voicemeeter.NewRemote now accepts a delay int argument (milliseconds). - voicemeeter.NewRemote now accepts a delay int argument (milliseconds).
- vm.Sync() can now be used to force the dirty parameters to clear. - vm.Sync() can now be used to force the dirty parameters to clear.
### Changed ### Changed
- higher level methods/functions now accept/return float64 - higher level methods/functions now accept/return float64
- tests updated to reflect changes. - tests updated to reflect changes.
## [1.5.0] - 2022-09-07 ## [1.5.0] - 2022-09-07
### Changed ### Changed
- changes to error handling. - changes to error handling.
- functions that wrap capi calls now return error types. - functions that wrap capi calls now return error types.
- higher level functions print error messages - higher level functions print error messages
## [1.4.0] - 2022-08-22 ## [1.4.0] - 2022-08-22
### Added ### Added
- midi type, supports midi devices - midi type, supports midi devices
- midi updates added to the pooler - midi updates added to the pooler
- event type, supports toggling event updates through EventAdd() and EventRemove() methods. - event type, supports toggling event updates through EventAdd() and EventRemove() methods.
- Forwarder methods for get/set float/string parameters added to Remote type - Forwarder methods for get/set float/string parameters added to Remote type
- Midi, Events sections added to README. - Midi, Events sections added to README.
### Changed ### Changed
- macrobutton updates moved into its own goroutine - macrobutton updates moved into its own goroutine
- observer example updated to include midi updates - observer example updated to include midi updates
- level updates are now disabled by default, should be enabled explicitly - level updates are now disabled by default, should be enabled explicitly
## [1.2.0] - 2022-07-10 ## [1.2.0] - 2022-07-10
### Added ### Added
- docstrings added to types, methods and functions - docstrings added to types, methods and functions
- version retractions added to go.mod - version retractions added to go.mod
### Changed ### Changed
- Entry method renamed from GetRemote to NewRemote - Entry method renamed from GetRemote to NewRemote
- Readme updated to reflect latest changes - Readme updated to reflect latest changes
## [1.1.0] - 2022-06-30 ## [1.1.0] - 2022-06-30
### Added ### 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
- Fixed bug with identifier in outputs struct. - Fixed bug with identifier in outputs struct.
### Changed ### Changed
- Package files moved into root of repository. - Package files moved into root of repository.
- Remote struct now exported type - Remote struct now exported type
## [1.0.0] - 2022-06-30 ## [1.0.0] - 2022-06-30
### Added ### Added
- recorder, device structs implemented - recorder, device structs implemented
- gainlayers field in strip struct implemented - gainlayers field in strip struct implemented
- levels field in strip, bus structs implemented - levels field in strip, bus structs implemented
- pooler ratelimit set at 33ms - pooler ratelimit set at 33ms
## [0.0.3] - 2022-06-25 ## [0.0.3] - 2022-06-25
### Added ### Added
- pre-commit.ps1 added for use with git hook - pre-commit.ps1 added for use with git hook
- unit tests for factory functions added - unit tests for factory functions added
- vban parameter methods added - vban parameter methods added
- support for observers added. publisher/observer structs defined - support for observers added. publisher/observer structs defined
- Pooler struct added, pdirty, mdirty now updated continously in a goroutine - Pooler struct added, pdirty, mdirty now updated continously in a goroutine
### Changed ### Changed
- NewRemote factory method now uses director, builder types to create Remote types. - NewRemote factory method now uses director, builder types to create Remote types.
- cdll renamed to path - cdll renamed to path
- test suite now using testify/assert - test suite now using testify/assert
## [0.0.2] - 2022-06-23 ## [0.0.2] - 2022-06-23
### Added ### Added
- physicalStrip, virtualStrip, physicalBus and virtualBus types defined. - physicalStrip, virtualStrip, physicalBus and virtualBus types defined.
- factory methods for strip, bus now cast return values to interface types. - factory methods for strip, bus now cast return values to interface types.
- parameter methods added to strip, bus types. - parameter methods added to strip, bus types.
- command struct implemented - command struct implemented
- bus, vban unit tests added - bus, vban unit tests added
### Changed ### Changed
- strip, bus slices in remote type defined as interface slice types. - strip, bus slices in remote type defined as interface slice types.
- bindings in base now prepended with vm. - bindings in base now prepended with vm.
- vban fields added to kind structs - vban fields added to kind structs
## [0.0.1] - 2022-06-22 ## [0.0.1] - 2022-06-22
### Added ### Added
- interface entry point defined in remote - interface entry point defined in remote
- some base functions are exported through forwarding methods in Remote type (Login, Logout etc) - some base functions are exported through forwarding methods in Remote type (Login, Logout etc)
- wrapper around the CAPI defined in base - wrapper around the CAPI defined in base
- path helper functions defined in cdll - path helper functions defined in cdll
- kind structs defined in kinds. These describe the layout for each version. - kind structs defined in kinds. These describe the layout for each version.
- channel, strip, bus structs getter/setter procedures defined. - channel, strip, bus structs getter/setter procedures defined.
- button struct fully implemented. - button struct fully implemented.
- initial test commit - 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) For an outline of past/future changes refer to: [CHANGELOG](CHANGELOG.md)
## Tested against ## Tested against
- Basic 1.1.1.1 - Basic 1.0.8.4
- Banana 2.1.1.1 - Banana 2.0.6.4
- Potato 3.1.1.1 - Potato 3.0.2.4
## Requirements ## Requirements
- [Voicemeeter](https://voicemeeter.com/) - [Voicemeeter](https://voicemeeter.com/)
- Go 1.18 or greater - Go 1.18 or greater
## Installation ## Installation
Initialize your own module then `go get` #### GO GET
``` Install voicemeeter-api-go package from your console to download the latest version.
go mod init github.com/x/y
go get github.com/onyx-and-iris/voicemeeter/v2 `go get github.com/onyx-and-iris/voicemeeter-api-go`
```
or add it to your `go.mod` file.
## `Use` ## `Use`
@ -35,7 +38,7 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/onyx-and-iris/voicemeeter/v2" "github.com/onyx-and-iris/voicemeeter-api-go"
) )
func main() { func main() {
@ -47,11 +50,11 @@ func main() {
vm.Strip[0].SetLabel("rode podmic") vm.Strip[0].SetLabel("rode podmic")
vm.Strip[0].SetMute(true) 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) { func vmConnect() (*voicemeeter.Remote, error) {
vm, err := voicemeeter.NewRemote("banana", 20) vm, err := voicemeeter.NewRemote("banana", 15)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -65,33 +68,21 @@ func vmConnect() (*voicemeeter.Remote, error) {
} }
``` ```
## `voicemeeter.NewRemote(<kindId>, <delay>, opts ...Option)` ## `voicemeeter.NewRemote(<kindId>, <delay>)`
### `kindId` ### `kindId`
Pass the kind of Voicemeeter as an argument. kindId may be: Pass the kind of Voicemeeter as an argument. kindId may be:
- `basic` - `basic`
- `banana` - `banana`
- `potato` - `potato`
### `delay` ### `delay`
Pass a delay in milliseconds to force the getters to wait for dirty parameters to clear. Pass a delay in milliseconds to force the getters to wait for dirty parameters to clear.
Useful if not listening for event updates. Useful if not running callbacks.
### `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))`
## `Remote Type` ## `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)` #### `vm.Register(o observer)`
register an observer type as an observer register an object as an observer
#### `vm.Deregister(o 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()` #### `vm.Pdirty()`
@ -189,121 +180,50 @@ Use this to force dirty parameters to clear after a delay in milliseconds.
The following methods are available The following methods are available
- `Mute() bool` - `GetMute() bool`
- `SetMute(val bool)` - `SetMute(val bool)`
- `Mono() bool` - `GetMono() bool`
- `SetMono(val bool)` - `SetMono(val bool)`
- `Solo() bool` - `GetSolo() bool`
- `SetSolo(val bool)` - `SetSolo(val bool)`
- `Limit() int` - `GetLimit() int`
- `SetLimit(val int)` from -40 to 12 - `SetLimit(val int)` from -40 to 12
- `Label() string` - `GetLabel() string`
- `SetLabel(val string)` - `SetLabel(val string)`
- `Gain() float64` - `GetGain() float64`
- `SetGain(val float64)` from -60.0 to 12.0 - `SetGain(val float64)` from -60.0 to 12.0
- `Mc() bool` - `GetMc() bool`
- `SetMc(val bool)` - `SetMc(val bool)`
- `Audibility() float64` - `GetComp() float64`
- `SetAudibility(val float64)` from 0.0 to 10.0 - `SetComp(val float64)` from 0.0 to 10.0
- `A1() bool - A5() bool` - `GetGate() float64`
- `SetA1(val bool) - SetA5(val bool)` - `SetGate(val float64)` from 0.0 to 10.0
- `B1() bool - B3() bool` - `GetAudibility() float64`
- `SetB1(val bool) bool - SetB3(val bool) bool` - `SetAudibility(val float64)` from 0.0 to 10.0
- `AppGain(name string, gain float64)` - `GetA1() bool - GetA5() bool`
- `AppMute(name string, val bool)` - `SetA1(val bool) - SetA5(val bool)`
- `AppGain(name string, gain float64)`
- `AppMute(name string, val bool)`
example: example:
```go ```go
vm.Strip[3].SetGain(3.7) 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[4].SetA1(true)
vm.Strip[5].AppGain("Spotify", 0.5) vm.Strip[5].AppGain("Spotify", 0.5)
vm.Strip[5].AppMute("Spotify", true) vm.Strip[5].AppMute("Spotify", true)
``` ```
##### Comp ##### Gainlayers
- `vm.Strip[i].Comp()` - `vm.Strip[i].GainLayer()[j]`
The following methods are available The following methods are available
- `Knob() float64` - `Get() float64`
- `SetKnob(val float64)` from 0.0 to 10.0 - `Set(val float64)`
- `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)`
example: example:
@ -313,13 +233,13 @@ vm.Strip[6].GainLayer()[3].Set(-13.6)
##### Levels ##### Levels
- `vm.Strip[i].Levels()` - `vm.Strip[i].Levels()`
The following methods are available The following methods are available
- `PreFader() []float64` - `PreFader() []float64`
- `PostFader() []float64` - `PostFader() []float64`
- `PostMute() []float64` - `PostMute() []float64`
example: example:
@ -331,53 +251,53 @@ fmt.Println(vm.Strip[5].Levels().PreFader())
The following methods are available The following methods are available
- `String() string` - `String() string`
- `Mute() bool` - `GetMute() bool`
- `SetMute(val bool)` - `SetMute(val bool)`
- `Mono() bool` - `GetEq() bool`
- `SetMono(val bool)` - `SetEq(val bool)`
- `Label() string` - `GetMono() bool`
- `SetLabel(val string)` - `SetMono(val bool)`
- `Gain() float64` - `GetLabel() string`
- `SetGain(val float64)` from -60.0 to 12.0 - `SetLabel(val string)`
- `GetGain() float64`
example: - `SetGain(val float64)` from -60.0 to 12.0
```go ```go
vm.Bus[3].SetEq(true) vm.Bus[3].SetEq(true)
fmt.Println(vm.Bus[0].Label()) fmt.Println(vm.Bus[0].GetLabel())
``` ```
##### Modes ##### Modes
- `vm.Bus[i].Mode()` - `vm.Bus[i].Mode()`
The following methods are available The following methods are available
- `SetNormal(val bool)` - `SetNormal(val bool)`
- `Normal() bool` - `GetNormal() bool`
- `SetAmix(val bool)` - `SetAmix(val bool)`
- `Amix() bool` - `GetAmix() bool`
- `SetBmix(val bool)` - `SetBmix(val bool)`
- `Bmix() bool` - `GetBmix() bool`
- `SetRepeat(val bool)` - `SetRepeat(val bool)`
- `Repeat() bool` - `GetRepeat() bool`
- `SetComposite(val bool)` - `SetComposite(val bool)`
- `Composite() bool` - `GetComposite() bool`
- `SetTvMix(val bool)` - `SetTvMix(val bool)`
- `TvMix() bool` - `GetTvMix() bool`
- `SetUpMix21(val bool)` - `SetUpMix21(val bool)`
- `UpMix21() bool` - `GetUpMix21() bool`
- `SetUpMix41(val bool)` - `SetUpMix41(val bool)`
- `UpMix41() bool` - `GetUpMix41() bool`
- `SetUpMix61(val bool)` - `SetUpMix61(val bool)`
- `UpMix61() bool` - `GetUpMix61() bool`
- `SetCenterOnly(val bool)` - `SetCenterOnly(val bool)`
- `CenterOnly() bool` - `GetCenterOnly() bool`
- `SetLfeOnly(val bool)` - `SetLfeOnly(val bool)`
- `LfeOnly() bool` - `GetLfeOnly() bool`
- `SetRearOnly(val bool)` - `SetRearOnly(val bool)`
- `RearOnly() bool` - `GetRearOnly() bool`
example: example:
@ -388,11 +308,11 @@ vm.Bus[4].Mode().SetCenterOnly(true)
##### Levels ##### Levels
- `vm.Bus[i].Levels()` - `vm.Bus[i].Levels()`
The following methods are available The following methods are available
- `All() []float64` - `All() []float64`
example: example:
@ -402,28 +322,10 @@ fmt.Println(vm.Bus[1].Levels().All())
### Strip | Bus ### Strip | Bus
##### EQ
- `vm.Strip[i].Eq()` `vm.Bus[i].Eq()`
The following methods are available. The following methods are available.
- `On() bool` - `FadeTo(target float64, time_ int)`: float, int
- `SetOn(val bool)` - `FadeBy(change float64, time_ int)`: float, int
- `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
Modify gain to or by the selected amount in db over a time interval in ms. 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 The following methods are available
- `State() bool` - `GetState() bool`
- `SetState(val bool)` - `SetState(val bool)`
- `StateOnly() bool` - `GetStateOnly() bool`
- `SetStateOnly(val bool)` - `SetStateOnly(val bool)`
- `Trigger() bool` - `GetTrigger() bool`
- `SetTrigger(val bool)` - `SetTrigger(val bool)`
example: example:
```go ```go
vm.Button[37].SetState(true) vm.Button[37].SetState(true)
fmt.Println(vm.Button[64].StateOnly()) fmt.Println(vm.Button[64].GetStateOnly())
``` ```
### Command ### Command
The following methods are available The following methods are available
- `Show()` Show Voicemeeter GUI if it's hidden - `Show()` Show Voicemeeter GUI if it's hidden
- `Hide()` Hide Voicemeeter GUI if it's shown - `Hide()` Hide Voicemeeter GUI if it's shown
- `Shutdown()` Shuts down the GUI - `Shutdown()` Shuts down the GUI
- `Restart()` Restart the audio engine - `Restart()` Restart the audio engine
- `Lock(val bool)` Lock the Voicemeeter GUI - `Lock(val bool)` Lock the Voicemeeter GUI
example: example:
@ -471,32 +373,32 @@ vm.Command.Show()
### VBAN ### 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 ##### Instream | Outstream
- `vm.Vban.InStream[i]` `vm.Vban.OutStream[i]` - `vm.Vban.InStream` `vm.Vban.OutStream`
The following methods are available The following methods are available
- `On() bool` - `GetOn() bool`
- `SetOn(val bool)` - `SetOn(val bool)`
- `Name() string` - `GetName() string`
- `SetName(val string)` - `SetName(val string)`
- `Ip() string` - `GetIp() string`
- `SetIp(val string)` - `SetIp(val string)`
- `Port() int` - `GetPort() int`
- `SetPort(val int)` from 1024 to 65535 - `SetPort(val int)` from 1024 to 65535
- `Sr() int` - `GetSr() int`
- `SetSr(val int)` (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000) - `SetSr(val int)` (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000)
- `Channel() int` - `GetChannel() int`
- `SetChannel(val int)` from 1 to 8 - `SetChannel(val int)` from 1 to 8
- `Bit() int` - `GetBit() int`
- `SetBit(val int)` 16 or 24 - `SetBit(val int)` 16 or 24
- `Quality() int` - `GetQuality() int`
- `SetQuality(val int)` from 0 to 4 - `SetQuality(val int)` from 0 to 4
- `Route() int` - `GetRoute() int`
- `SetRoute(val int)` from 0 to 8 - `SetRoute(val int)` from 0 to 8
example: example:
@ -515,10 +417,10 @@ vm.Vban.OutStream[3].SetBit(24)
The following methods are available The following methods are available
- `Ins()` - `Ins()`
- `Outs()` - `Outs()`
- `Input(val int)` - `Input(val int)`
- `Output(val int)` - `Output(val int)`
example: example:
@ -532,13 +434,13 @@ for i := 0; i < int(vm.Device.Ins()); i++ {
The following methods are available The following methods are available
- `Play()` - `Play()`
- `Stop()` - `Stop()`
- `Pause()` - `Pause()`
- `Replay()` - `Replay()`
- `Record()` - `Record()`
- `Ff()` - `Ff()`
- `Rew()` - `Rew()`
example: example:
@ -557,9 +459,9 @@ vm.Recorder.SetB2(false)
The following methods are available The following methods are available
- `Channel()` returns the current midi channel - `Channel()` returns the current midi channel
- `Current()` returns the most recently pressed midi button - `Current()` returns the most recently pressed midi button
- `Get(<button>)` returns the value in cache for the midi button - `Get(<button>)` returns the value in cache for the midi button
example: 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: By default level updates are disabled. Any event may be enabled or disabled. The following events exist:
- `pdirty` parameter updates - `pdirty` parameter updates
- `mdirty` macrobutton updates - `mdirty` macrobutton updates
- `midi` midi updates - `midi` midi updates
- `ldirty` level updates - `ldirty` level updates
example: example:
```go ```go
events := []string{"ldirty", "mdirty", "pdirty"} vm.EventAdd("ldirty")
vm.EventAdd(events...) vm.EventRemove("pdirty")
vm.EventRemove(events...)
``` ```
### Run tests ### Run tests
@ -597,4 +497,4 @@ go test ./...
### Official Documentation ### 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 ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"math" "math"
"runtime"
"strings" "strings"
"syscall" "syscall"
"time" "time"
"unsafe" "unsafe"
log "github.com/sirupsen/logrus"
) )
var ( var (
@ -47,30 +43,18 @@ var (
// login logs into the API, // login logs into the API,
// attempts to launch Voicemeeter if it's not running, // attempts to launch Voicemeeter if it's not running,
// initializes dirty parameters. // initializes dirty parameters.
func login(kindId string, timeout, bits int) error { func login(kindId string) error {
res, _, _ := vmLogin.Call() res, _, _ := vmLogin.Call()
if res == 1 { if res == 1 {
runVoicemeeter(kindId, bits) runVoicemeeter(kindId)
time.Sleep(time.Second)
} else if res != 0 { } else if res != 0 {
err := fmt.Errorf("VBVMR_Login returned %d", res) err := fmt.Errorf("VBVMR_Login returned %d", res)
return err return err
} }
fmt.Printf("Logged into Voicemeeter %s\n", kindId)
var ver_s string for pdirty() || mdirty() {
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
}
} }
if err != nil {
return errors.New("timeout logging into the API")
}
clear()
return nil return nil
} }
@ -79,28 +63,24 @@ func login(kindId string, timeout, bits int) error {
func logout(kindId string) error { func logout(kindId string) error {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
res, _, _ := vmLogout.Call() res, _, _ := vmLogout.Call()
if int32(res) != 0 { if res != 0 {
err := fmt.Errorf("VBVMR_Logout returned %d", int32(res)) err := fmt.Errorf("VBVMR_Logout returned %d", res)
return err return err
} }
log.Infof("Logged out of Voicemeeter %s", kindMap[kindId]) fmt.Printf("Logged out of Voicemeeter %s\n", kindId)
return nil return nil
} }
// runVoicemeeter attempts to launch a Voicemeeter GUI of a kind. // 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{ vals := map[string]uint64{
"basic": 1, "basic": 1,
"banana": 2, "banana": 2,
"potato": 3, "potato": 3,
} }
val := vals[kindId] res, _, _ := vmRunvm.Call(uintptr(vals[kindId]))
if strings.Contains(runtime.GOARCH, "64") && bits == 64 { if res != 0 {
val += 3 err := fmt.Errorf("VBVMR_RunVoicemeeter returned %d", res)
}
res, _, _ := vmRunvm.Call(uintptr(val))
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_RunVoicemeeter returned %d", int32(res))
return err return err
} }
return nil return nil
@ -110,9 +90,8 @@ func runVoicemeeter(kindId string, bits int) error {
func getVersion() (string, error) { func getVersion() (string, error) {
var ver uint64 var ver uint64
res, _, _ := vmGetvmVersion.Call(uintptr(unsafe.Pointer(&ver))) res, _, _ := vmGetvmVersion.Call(uintptr(unsafe.Pointer(&ver)))
if int32(res) != 0 { if res != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterVersion returned %d", int32(res)) err := fmt.Errorf("VBVMR_GetVoicemeeterVersion returned %d", res)
log.Error(err.Error())
return "", err return "", err
} }
v1 := (ver & 0xFF000000) >> 24 v1 := (ver & 0xFF000000) >> 24
@ -123,61 +102,33 @@ func getVersion() (string, error) {
} }
// pdirty returns true iff a parameter value has changed // pdirty returns true iff a parameter value has changed
func pdirty() (bool, error) { func pdirty() bool {
res, _, _ := vmPdirty.Call() res, _, _ := vmPdirty.Call()
if int32(res) < 0 { return int(res) == 1
err := fmt.Errorf("VBVMR_IsParametersDirty returned %d", int32(res))
log.Error(err.Error())
return false, err
}
return int32(res) == 1, nil
} }
// mdirty returns true iff a macrobutton value has changed // mdirty returns true iff a macrobutton value has changed
func mdirty() (bool, error) { func mdirty() bool {
res, _, _ := vmMdirty.Call() res, _, _ := vmMdirty.Call()
if int32(res) < 0 { return int(res) == 1
err := fmt.Errorf("VBVMR_MacroButton_IsDirty returned %d", int32(res))
log.Error(err.Error())
return false, err
}
return int32(res) == 1, nil
} }
// ldirty returns true iff a level value has changed // 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.stripLevelsBuff = make([]float64, (2*k.PhysIn)+(8*k.VirtIn))
_levelCache.busLevelsBuff = make([]float64, 8*k.NumBus()) _levelCache.busLevelsBuff = make([]float64, 8*k.NumBus())
for i := 0; i < (2*k.PhysIn)+(8*k.VirtIn); i++ { for i := 0; i < (2*k.PhysIn)+(8*k.VirtIn); i++ {
val, err := getLevel(_levelCache.stripMode, i) val, _ := getLevel(_levelCache.stripMode, i)
if err != nil {
log.Error(err.Error())
return false, err
}
_levelCache.stripLevelsBuff[i] = val _levelCache.stripLevelsBuff[i] = val
_levelCache.stripComp[i] = _levelCache.stripLevelsBuff[i] == _levelCache.stripLevels[i] _levelCache.stripComp[i] = _levelCache.stripLevelsBuff[i] == _levelCache.stripLevels[i]
} }
for i := 0; i < 8*k.NumBus(); i++ { for i := 0; i < 8*k.NumBus(); i++ {
val, err := getLevel(3, i) val, _ := getLevel(3, i)
if err != nil {
log.Error(err.Error())
return false, err
}
_levelCache.busLevelsBuff[i] = val _levelCache.busLevelsBuff[i] = val
_levelCache.busComp[i] = _levelCache.busLevelsBuff[i] == _levelCache.busLevels[i] _levelCache.busComp[i] = _levelCache.busLevelsBuff[i] == _levelCache.busLevels[i]
} }
return !(allTrue(_levelCache.stripComp, (2*k.PhysIn)+(8*k.VirtIn)) && allTrue(_levelCache.busComp, 8*k.NumBus())), nil return !(allTrue(_levelCache.stripComp, (2*k.PhysIn)+(8*k.VirtIn)) && allTrue(_levelCache.busComp, 8*k.NumBus()))
}
func clear() {
for {
pdirty, _ := pdirty()
mdirty, _ := mdirty()
if !(pdirty || mdirty) {
break
}
}
} }
// getVMType returns the type of Voicemeeter, as a string // getVMType returns the type of Voicemeeter, as a string
@ -186,8 +137,8 @@ func getVMType() (string, error) {
res, _, _ := vmGetvmType.Call( res, _, _ := vmGetvmType.Call(
uintptr(unsafe.Pointer(&type_)), uintptr(unsafe.Pointer(&type_)),
) )
if int32(res) != 0 { if res != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterType returned %d", int32(res)) err := fmt.Errorf("VBVMR_GetVoicemeeterType returned %d", res)
return "", err return "", err
} }
vals := map[uint64]string{ vals := map[uint64]string{
@ -202,7 +153,8 @@ func getVMType() (string, error) {
func getParameterFloat(name string) (float64, error) { func getParameterFloat(name string) (float64, error) {
if vmsync { if vmsync {
time.Sleep(time.Duration(vmdelay) * time.Millisecond) time.Sleep(time.Duration(vmdelay) * time.Millisecond)
clear() for pdirty() || mdirty() {
}
} }
var value float32 var value float32
b := append([]byte(name), 0) b := append([]byte(name), 0)
@ -210,8 +162,8 @@ func getParameterFloat(name string) (float64, error) {
uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&b[0])),
uintptr(unsafe.Pointer(&value)), uintptr(unsafe.Pointer(&value)),
) )
if int32(res) != 0 { if res != 0 {
err := fmt.Errorf("VBVMR_GetParameterFloat returned %d", int32(res)) err := fmt.Errorf("VBVMR_GetParameterFloat returned %d", res)
return 0, err return 0, err
} }
return math.Round(float64(value)*10) / 10, nil 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(unsafe.Pointer(&b1[0])),
uintptr(b2), uintptr(b2),
) )
if int32(res) != 0 { if res != 0 {
err := fmt.Errorf("VBVMR_SetParameterFloat returned %d", int32(res)) err := fmt.Errorf("VBVMR_SetParameterFloat returned %d", res)
return err return err
} }
return nil return nil
@ -236,7 +188,8 @@ func setParameterFloat(name string, value float64) error {
func getParameterString(name string) (string, error) { func getParameterString(name string) (string, error) {
if vmsync { if vmsync {
time.Sleep(time.Duration(vmdelay) * time.Millisecond) time.Sleep(time.Duration(vmdelay) * time.Millisecond)
clear() for pdirty() || mdirty() {
}
} }
b1 := append([]byte(name), 0) b1 := append([]byte(name), 0)
var b2 [512]byte var b2 [512]byte
@ -244,8 +197,8 @@ func getParameterString(name string) (string, error) {
uintptr(unsafe.Pointer(&b1[0])), uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])), uintptr(unsafe.Pointer(&b2[0])),
) )
if int32(res) != 0 { if res != 0 {
err := fmt.Errorf("VBVMR_GetParameterStringA returned %d", int32(res)) err := fmt.Errorf("VBVMR_GetParameterStringA returned %d", res)
return "", err return "", err
} }
str := bytes.Trim(b2[:], "\x00") str := bytes.Trim(b2[:], "\x00")
@ -260,8 +213,8 @@ func setParameterString(name, value string) error {
uintptr(unsafe.Pointer(&b1[0])), uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])), uintptr(unsafe.Pointer(&b2[0])),
) )
if int32(res) != 0 { if res != 0 {
err := fmt.Errorf("VBVMR_SetParameterStringA returned %d", int32(res)) err := fmt.Errorf("VBVMR_SetParameterStringA returned %d", res)
return err return err
} }
return nil return nil
@ -273,8 +226,8 @@ func setParametersMulti(script string) error {
res, _, _ := vmSetParameters.Call( res, _, _ := vmSetParameters.Call(
uintptr(unsafe.Pointer(&b1[0])), uintptr(unsafe.Pointer(&b1[0])),
) )
if int32(res) != 0 { if res != 0 {
err := fmt.Errorf("VBVMR_SetParameters returned %d", int32(res)) err := fmt.Errorf("VBVMR_SetParameters returned %d", res)
return err return err
} }
return nil return nil
@ -284,7 +237,8 @@ func setParametersMulti(script string) error {
func getMacroStatus(id, mode int) (float64, error) { func getMacroStatus(id, mode int) (float64, error) {
if vmsync { if vmsync {
time.Sleep(time.Duration(vmdelay) * time.Millisecond) time.Sleep(time.Duration(vmdelay) * time.Millisecond)
clear() for pdirty() || mdirty() {
}
} }
var state float32 var state float32
res, _, _ := vmGetMacroStatus.Call( res, _, _ := vmGetMacroStatus.Call(
@ -292,8 +246,8 @@ func getMacroStatus(id, mode int) (float64, error) {
uintptr(unsafe.Pointer(&state)), uintptr(unsafe.Pointer(&state)),
uintptr(mode), uintptr(mode),
) )
if int32(res) != 0 { if res != 0 {
err := fmt.Errorf("VBVMR_MacroButton_GetStatus returned %d", int32(res)) err := fmt.Errorf("VBVMR_MacroButton_GetStatus returned %d", res)
return 0, err return 0, err
} }
return float64(state), nil return float64(state), nil
@ -306,8 +260,8 @@ func setMacroStatus(id, state, mode int) error {
uintptr(state), uintptr(state),
uintptr(mode), uintptr(mode),
) )
if int32(res) != 0 { if res != 0 {
err := fmt.Errorf("VBVMR_MacroButton_SetStatus returned %d", int32(res)) err := fmt.Errorf("VBVMR_MacroButton_SetStatus returned %d", res)
return err return err
} }
return nil return nil
@ -336,8 +290,8 @@ func getDeviceDescription(i int, dir string) (string, uint64, string, error) {
uintptr(unsafe.Pointer(&b1[0])), uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])), uintptr(unsafe.Pointer(&b2[0])),
) )
if int32(res) != 0 { if res != 0 {
err := fmt.Errorf("VBVMR_Input_GetDeviceDescA returned %d", int32(res)) err := fmt.Errorf("VBVMR_Input_GetDeviceDescA returned %d", res)
return "", 0, "", err return "", 0, "", err
} }
} else { } else {
@ -347,8 +301,8 @@ func getDeviceDescription(i int, dir string) (string, uint64, string, error) {
uintptr(unsafe.Pointer(&b1[0])), uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])), uintptr(unsafe.Pointer(&b2[0])),
) )
if int32(res) != 0 { if res != 0 {
err := fmt.Errorf("VBVMR_Output_GetDeviceDescA returned %d", int32(res)) err := fmt.Errorf("VBVMR_Output_GetDeviceDescA returned %d", res)
return "", 0, "", err 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] // getLevel returns a single level value of type type_ for channel[i]
func getLevel(type_, i int) (float64, error) { func getLevel(type_, i int) (float64, error) {
var val float32 var val float64
res, _, _ := vmGetLevelFloat.Call( res, _, _ := vmGetLevelFloat.Call(
uintptr(type_), uintptr(type_),
uintptr(i), uintptr(i),
uintptr(unsafe.Pointer(&val)), uintptr(unsafe.Pointer(&val)),
) )
if int32(res) != 0 { if res != 0 {
err := fmt.Errorf("VBVMR_GetLevel returned %d", int32(res)) err := fmt.Errorf("VBVMR_GetLevel returned %d", res)
return 0, err return 0, err
} }
return float64(val), nil return val, nil
} }
// getMidiMessage gets midi channel, pitch and velocity for a single midi input // getMidiMessage gets midi channel, pitch and velocity for a single midi input
func getMidiMessage() (bool, error) { func getMidiMessage() bool {
var midi = newMidi() var midi = newMidi()
var b1 [1024]byte var b1 [1024]byte
res, _, _ := vmGetMidiMessage.Call( res, _, _ := vmGetMidiMessage.Call(
uintptr(unsafe.Pointer(&b1[0])), uintptr(unsafe.Pointer(&b1[0])),
uintptr(1024), uintptr(1024),
) )
x := int32(res) x := int(res)
if x < 0 { if x < 0 {
if x == -2 { err := fmt.Errorf("VBVMR_GetMidiMessage returned %d", res)
err := fmt.Errorf("VBVMR_GetMidiMessage returned %d", int32(res)) if err != nil {
if err != nil { fmt.Println(err)
log.Error(err)
return false, err
}
} }
return false
} }
msg := bytes.Trim(b1[:], "\x00") msg := bytes.Trim(b1[:], "\x00")
if len(msg) > 0 { if len(msg) > 0 {
@ -405,5 +357,5 @@ func getMidiMessage() (bool, error) {
midi.cache[pitch] = vel 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 // iBus defines the interface bus types must satisfy
type iBus interface { type iBus interface {
String() string String() string
Mute() bool GetMute() bool
SetMute(val bool) SetMute(val bool)
Mono() bool GetEq() bool
SetEq(val bool)
GetMono() bool
SetMono(val bool) SetMono(val bool)
Label() string GetLabel() string
SetLabel(val string) SetLabel(val string)
Gain() float64 GetGain() float64
SetGain(val float64) SetGain(val float64)
Eq() *eQ Mode() iBusMode
Mode() *busMode
Levels() *levels Levels() *levels
FadeTo(target float32, time_ int) FadeTo(target float32, time_ int)
FadeBy(change float32, time_ int) FadeBy(change float32, time_ int)
@ -26,13 +27,12 @@ type iBus interface {
// bus represents a bus channel // bus represents a bus channel
type bus struct { type bus struct {
iRemote iRemote
eQ *eQ mode busMode
mode *busMode levels
levels *levels
} }
// Mute returns the value of the Mute parameter // GetMute returns the value of the Mute parameter
func (b *bus) Mute() bool { func (b *bus) GetMute() bool {
return b.getter_bool("Mute") return b.getter_bool("Mute")
} }
@ -41,8 +41,18 @@ func (b *bus) SetMute(val bool) {
b.setter_bool("Mute", val) b.setter_bool("Mute", val)
} }
// Mono returns the value of the Mute parameter // GetEq returns the value of the Eq.On parameter
func (b *bus) Mono() bool { 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") return b.getter_bool("Mono")
} }
@ -51,8 +61,8 @@ func (b *bus) SetMono(val bool) {
b.setter_bool("Mono", val) b.setter_bool("Mono", val)
} }
// Label returns the value of the MC parameter // GetLabel returns the value of the MC parameter
func (b *bus) Label() string { func (b *bus) GetLabel() string {
return b.getter_string("Label") return b.getter_string("Label")
} }
@ -61,8 +71,8 @@ func (b *bus) SetLabel(val string) {
b.setter_string("Label", val) b.setter_string("Label", val)
} }
// Gain returns the value of the Gain parameter // GetGain returns the value of the Gain parameter
func (b *bus) Gain() float64 { func (b *bus) GetGain() float64 {
return b.getter_float("Gain") return b.getter_float("Gain")
} }
@ -71,19 +81,14 @@ func (b *bus) SetGain(val float64) {
b.setter_float("Gain", val) 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 // Mode returns address of a busMode struct
func (b *bus) Mode() *busMode { func (b *bus) Mode() iBusMode {
return b.mode return &b.mode
} }
// Levels returns the levels field // Levels returns the levels field
func (b *bus) Levels() *levels { 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_ // 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) time.Sleep(time.Millisecond)
} }
// PhysicalBus represents a single physical bus // physicalBus represents a single physical bus
type PhysicalBus struct { type physicalBus struct {
bus 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 { func newPhysicalBus(i int, k *kind) iBus {
e := newEq(fmt.Sprintf("bus[%d].EQ", i), i)
b := newBusMode(i) b := newBusMode(i)
l := newBusLevels(i, k) 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 // String implements the fmt.stringer interface
func (p *PhysicalBus) String() string { func (p *physicalBus) String() string {
return fmt.Sprintf("PhysicalBus%d", p.index) return fmt.Sprintf("PhysicalBus%d", p.index)
} }
// VirtualBus represents a single virtual bus // virtualBus represents a single virtual bus
type VirtualBus struct { type virtualBus struct {
bus 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 { func newVirtualBus(i int, k *kind) iBus {
e := newEq(fmt.Sprintf("bus[%d].EQ", i), i)
b := newBusMode(i) b := newBusMode(i)
l := newBusLevels(i, k) l := newBusLevels(i, k)
vb := VirtualBus{bus{iRemote{fmt.Sprintf("bus[%d]", i), i}, e, b, l}} vb := virtualBus{bus{iRemote{fmt.Sprintf("bus[%d]", i), i}, b, l}}
return &vb return iBus(&vb)
} }
// String implements the fmt.stringer interface // String implements the fmt.stringer interface
func (v *VirtualBus) String() string { func (v *virtualBus) String() string {
return fmt.Sprintf("VirtualBus%d", v.index) 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 // busMode offers methods for getting/setting bus mode states
type busMode struct { type busMode struct {
iRemote iRemote
} }
// newBusMode returns a busMode struct // newBusMode returns a busMode struct
func newBusMode(i int) *busMode { func newBusMode(i int) busMode {
return &busMode{iRemote{fmt.Sprintf("bus[%d].mode", i), i}} return busMode{iRemote{fmt.Sprintf("bus[%d].mode", i), i}}
} }
// Normal gets the value of the Mode.Normal parameter // GetNormal gets the value of the Mode.Normal parameter
func (bm *busMode) Normal() bool { func (bm *busMode) GetNormal() bool {
return bm.getter_bool("Normal") return bm.getter_bool("Normal")
} }
@ -157,8 +188,8 @@ func (bm *busMode) SetNormal(val bool) {
bm.setter_bool("Normal", val) bm.setter_bool("Normal", val)
} }
// Amix gets the value of the Mode.Amix parameter // GetAmix gets the value of the Mode.Amix parameter
func (bm *busMode) Amix() bool { func (bm *busMode) GetAmix() bool {
return bm.getter_bool("Amix") return bm.getter_bool("Amix")
} }
@ -167,8 +198,8 @@ func (bm *busMode) SetAmix(val bool) {
bm.setter_bool("Amix", val) bm.setter_bool("Amix", val)
} }
// Bmix gets the value of the Mode.Bmix parameter // GetBmix gets the value of the Mode.Bmix parameter
func (bm *busMode) Bmix() bool { func (bm *busMode) GetBmix() bool {
return bm.getter_bool("Bmix") return bm.getter_bool("Bmix")
} }
@ -177,8 +208,8 @@ func (bm *busMode) SetBmix(val bool) {
bm.setter_bool("Bmix", val) bm.setter_bool("Bmix", val)
} }
// Repeat gets the value of the Mode.Repeat parameter // GetRepeat gets the value of the Mode.Repeat parameter
func (bm *busMode) Repeat() bool { func (bm *busMode) GetRepeat() bool {
return bm.getter_bool("Repeat") return bm.getter_bool("Repeat")
} }
@ -187,8 +218,8 @@ func (bm *busMode) SetRepeat(val bool) {
bm.setter_bool("Repeat", val) bm.setter_bool("Repeat", val)
} }
// Composite gets the value of the Mode.Composite parameter // GetComposite gets the value of the Mode.Composite parameter
func (bm *busMode) Composite() bool { func (bm *busMode) GetComposite() bool {
return bm.getter_bool("Composite") return bm.getter_bool("Composite")
} }
@ -197,8 +228,8 @@ func (bm *busMode) SetComposite(val bool) {
bm.setter_bool("Composite", val) bm.setter_bool("Composite", val)
} }
// TvMix gets the value of the Mode.TvMix parameter // GetTvMix gets the value of the Mode.TvMix parameter
func (bm *busMode) TvMix() bool { func (bm *busMode) GetTvMix() bool {
return bm.getter_bool("TvMix") return bm.getter_bool("TvMix")
} }
@ -207,8 +238,8 @@ func (bm *busMode) SetTvMix(val bool) {
bm.setter_bool("TvMix", val) bm.setter_bool("TvMix", val)
} }
// UpMix21 gets the value of the Mode.UpMix21 parameter // GetUpMix21 gets the value of the Mode.UpMix21 parameter
func (bm *busMode) UpMix21() bool { func (bm *busMode) GetUpMix21() bool {
return bm.getter_bool("UpMix21") return bm.getter_bool("UpMix21")
} }
@ -217,8 +248,8 @@ func (bm *busMode) SetUpMix21(val bool) {
bm.setter_bool("UpMix21", val) bm.setter_bool("UpMix21", val)
} }
// UpMix41 gets the value of the Mode.UpMix41 parameter // GetUpMix41 gets the value of the Mode.UpMix41 parameter
func (bm *busMode) UpMix41() bool { func (bm *busMode) GetUpMix41() bool {
return bm.getter_bool("UpMix41") return bm.getter_bool("UpMix41")
} }
@ -227,8 +258,8 @@ func (bm *busMode) SetUpMix41(val bool) {
bm.setter_bool("UpMix41", val) bm.setter_bool("UpMix41", val)
} }
// UpMix61 gets the value of the Mode.UpMix61 parameter // GetUpMix61 gets the value of the Mode.UpMix61 parameter
func (bm *busMode) UpMix61() bool { func (bm *busMode) GetUpMix61() bool {
return bm.getter_bool("UpMix61") return bm.getter_bool("UpMix61")
} }
@ -237,8 +268,8 @@ func (bm *busMode) SetUpMix61(val bool) {
bm.setter_bool("UpMix61", val) bm.setter_bool("UpMix61", val)
} }
// CenterOnly gets the value of the Mode.CenterOnly parameter // GetCenterOnly gets the value of the Mode.CenterOnly parameter
func (bm *busMode) CenterOnly() bool { func (bm *busMode) GetCenterOnly() bool {
return bm.getter_bool("CenterOnly") return bm.getter_bool("CenterOnly")
} }
@ -247,8 +278,8 @@ func (bm *busMode) SetCenterOnly(val bool) {
bm.setter_bool("CenterOnly", val) bm.setter_bool("CenterOnly", val)
} }
// LfeOnly gets the value of the Mode.LFE parameter // GetLfeOnly gets the value of the Mode.LFE parameter
func (bm *busMode) LfeOnly() bool { func (bm *busMode) GetLfeOnly() bool {
return bm.getter_bool("LfeOnly") return bm.getter_bool("LfeOnly")
} }
@ -257,8 +288,8 @@ func (bm *busMode) SetLfeOnly(val bool) {
bm.setter_bool("LfeOnly", val) bm.setter_bool("LfeOnly", val)
} }
// RearOnly gets the value of the Mode.RearOnly parameter // GetRearOnly gets the value of the Mode.RearOnly parameter
func (bm *busMode) RearOnly() bool { func (bm *busMode) GetRearOnly() bool {
return bm.getter_bool("RearOnly") return bm.getter_bool("RearOnly")
} }
@ -268,16 +299,16 @@ func (bm *busMode) SetRearOnly(val bool) {
} }
// newBusLevels represents the levels field for a channel // 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 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 // All returns the level values for a bus
func (l *levels) All() []float64 { func (l *levels) All() []float64 {
levels := make([]float64, l.offset) var levels []float64
for i := range levels { for i := l.init; i < l.init+l.offset; i++ {
levels[i] = convertLevel(_levelCache.busLevels[l.init+i]) levels = append(levels, convertLevel(_levelCache.busLevels[i]))
} }
return levels return levels
} }

View File

@ -37,8 +37,8 @@ func (m *button) String() string {
return fmt.Sprintf("MacroButton%d", m.index) return fmt.Sprintf("MacroButton%d", m.index)
} }
// State returns the value of the State parameter // GetState returns the value of the State parameter
func (m *button) State() bool { func (m *button) GetState() bool {
return m.getter(1) return m.getter(1)
} }
@ -47,8 +47,8 @@ func (m *button) SetState(val bool) {
m.setter(val, 1) m.setter(val, 1)
} }
// StateOnly returns the value of the StateOnly parameter // GetStateOnly returns the value of the StateOnly parameter
func (m *button) StateOnly() bool { func (m *button) GetStateOnly() bool {
return m.getter(2) return m.getter(2)
} }
@ -57,8 +57,8 @@ func (m *button) SetStateOnly(val bool) {
m.setter(val, 2) m.setter(val, 2)
} }
// Trigger returns the value of the Trigger parameter // GetTrigger returns the value of the Trigger parameter
func (m *button) Trigger() bool { func (m *button) GetTrigger() bool {
return m.getter(2) 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 go 1.19
require ( require (
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0 github.com/onyx-and-iris/voicemeeter-api-go v1.7.0
github.com/sirupsen/logrus v1.9.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 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 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg=
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg= 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-api-go v1.7.0 h1:WA1RGrfaWBOavgY2uoJltzy/rAgvs62PM3qzR+5/jt8=
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/go.mod h1:zAdBhHXQ9n37CUbLizbOPmAutyZI8Ncqeu5e9u1Fy14=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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= 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-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/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=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

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

View File

@ -1,12 +1,11 @@
module obs module main
go 1.18 go 1.18
require ( require (
github.com/BurntSushi/toml v1.2.0 github.com/BurntSushi/toml v1.2.0
github.com/andreykaipov/goobs v0.10.0 github.com/andreykaipov/goobs v0.10.0
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0 github.com/onyx-and-iris/voicemeeter-api-go v1.7.0
github.com/sirupsen/logrus v1.9.0
) )
require ( require (
@ -14,5 +13,5 @@ require (
github.com/gorilla/websocket v1.5.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/logutils v1.0.0 // indirect
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // 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/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 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= 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 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 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 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 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 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 h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= 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-api-go v1.7.0 h1:WA1RGrfaWBOavgY2uoJltzy/rAgvs62PM3qzR+5/jt8=
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/go.mod h1:zAdBhHXQ9n37CUbLizbOPmAutyZI8Ncqeu5e9u1Fy14=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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= 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-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/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=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

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

View File

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

View File

@ -1,39 +1,25 @@
## About ## About
A Voicemeeter CLI, offers ability to toggle, get and set parameters. A simple voicemeeter-cli program. 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))
## Use ## Use
Commands that begin with `!` will toggle a parameter, commands that contain `=` will set a parameter, all other commands will get a value. 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 the following optional flags: You may pass an optional flag -delay to set a delay on the getters. Defaults to 15ms.
- -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).
for example: 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: Expected output:
``` ```
time="<timestamp>" level=info msg="Logged into Voicemeeter Potato v3.1.1.1" Logged into Voicemeeter potato
Setting strip[0].mute=0 Running command strip[0].mute=0
strip[0].mute: 0.00 Value of strip[0].mute is: 0
Toggling strip[0].mute Toggling strip[0].mute
strip[0].mute: 1.00 Value of strip[0].mute is: 1
Setting bus[0].gain=-8.8 Running command bus[0].gain=-8.8
Setting command.lock=1 Running command command.lock=1
time="<timestamp>" level=info msg="Logged out of Voicemeeter Potato"
``` ```
If running in interactive mode enter `Q`, to exit.

View File

@ -1,121 +1,33 @@
package main package main
import ( import (
"bufio"
"flag" "flag"
"fmt" "fmt"
"os" "log"
"strings" "strings"
log "github.com/sirupsen/logrus" "github.com/onyx-and-iris/voicemeeter-api-go"
"github.com/onyx-and-iris/voicemeeter/v2"
) )
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() { func main() {
var ( kindId := flag.String("kind", "banana", "kind of voicemeeter")
kind string delay := flag.Int("delay", 15, "delay between commands")
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)")
flag.Parse() flag.Parse()
if help { vm, err := vmConnect(kindId, delay)
flag.Usage()
return
}
if loglevel >= int(log.PanicLevel) && loglevel <= int(log.TraceLevel) {
log.SetLevel(log.Level(loglevel))
}
vm, err := vmConnect(kind, delay)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
defer vm.Logout() defer vm.Logout()
if interactive { err = run_commands(vm)
interactiveMode(vm) if err != nil {
return fmt.Println(err)
}
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())
}
} }
} }
func usage() { func vmConnect(kindId *string, delay *int) (*voicemeeter.Remote, error) {
fmt.Println("usage: ./vm-cli.exe [-h] [-i] [-k] [-l] [-d] [-v]\n" + vm, err := voicemeeter.NewRemote(*kindId, *delay)
"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)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -128,86 +40,38 @@ func vmConnect(kind string, delay int) (*voicemeeter.Remote, error) {
return vm, nil return vm, nil
} }
func interactiveMode(vm *voicemeeter.Remote) error { func run_commands(vm *voicemeeter.Remote) error {
fmt.Println("Interactive mode enabled. Enter 'Q' to exit.") for _, arg := range flag.Args() {
if arg[0] == '!' {
scanner := bufio.NewScanner(os.Stdin) val, err := vm.GetFloat(arg[1:])
fmt.Printf(">> ") if err != nil {
for scanner.Scan() { err = fmt.Errorf("unable to toggle %s", arg[1:])
input := scanner.Text() return err
if strings.ToUpper(input) == "Q" { }
break vm.SetFloat(arg[1:], 1-val)
} fmt.Println("Toggling", arg[1:])
} else {
for _, cmd := range strings.Split(input, " ") { if strings.Contains(arg, "=") {
if err := parse(vm, cmd); err != nil { fmt.Println("Running command", arg)
log.Error(err.Error()) 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 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 go 1.18
retract (
// package files moved into root of repository
[v1.0.0, v1.1.0]
)
require ( require (
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.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 ( 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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 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.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 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 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-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

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

View File

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

View File

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

View File

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

497
strip.go
View File

@ -3,51 +3,37 @@ package voicemeeter
import ( import (
"fmt" "fmt"
"time" "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 { type iStrip interface {
String() string String() string
Mute() bool GetMute() bool
SetMute(val bool) SetMute(val bool)
Mono() bool GetMono() bool
SetMono(val bool) SetMono(val bool)
Solo() bool GetSolo() bool
SetSolo(val bool) SetSolo(val bool)
Limit() int GetLimit() int
SetLimit(val int) SetLimit(val int)
Label() string GetLabel() string
SetLabel(val string) SetLabel(val string)
Gain() float64 GetGain() float64
SetGain(val float64) SetGain(val float64)
Mc() bool GetMc() bool
SetMc(val bool) SetMc(val bool)
Audibility() float64 GetComp() float64
SetComp(val float64)
GetGate() float64
SetGate(val float64)
GetAudibility() float64
SetAudibility(val float64) SetAudibility(val float64)
PanX() float64 GainLayer() []gainLayer
SetPanX(val float64) Levels() *levels
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)
FadeTo(target float64, time_ int) FadeTo(target float64, time_ int)
FadeBy(change float64, time_ int) FadeBy(change float64, time_ int)
AppGain(name string, gain float64) AppGain(name string, gain float64)
AppMute(name string, val bool) AppMute(name string, val bool)
Eq() *eQ
Comp() *comp
Gate() *gate
Denoiser() *denoiser
GainLayer() []gainLayer
Levels() *levels
iOutputs iOutputs
} }
@ -55,16 +41,12 @@ type iStrip interface {
type strip struct { type strip struct {
iRemote iRemote
outputs outputs
eQ *eQ
comp *comp
gate *gate
denoiser *denoiser
gainLayer []gainLayer gainLayer []gainLayer
levels *levels levels
} }
// Mute returns the value of the Mute parameter // GetMute returns the value of the Mute parameter
func (s *strip) Mute() bool { func (s *strip) GetMute() bool {
return s.getter_bool("Mute") return s.getter_bool("Mute")
} }
@ -73,8 +55,8 @@ func (s *strip) SetMute(val bool) {
s.setter_bool("Mute", val) s.setter_bool("Mute", val)
} }
// Mono returns the value of the Mono parameter // GetMono returns the value of the Mono parameter
func (s *strip) Mono() bool { func (s *strip) GetMono() bool {
return s.getter_bool("Mono") return s.getter_bool("Mono")
} }
@ -83,8 +65,8 @@ func (s *strip) SetMono(val bool) {
s.setter_bool("Mono", val) s.setter_bool("Mono", val)
} }
// Solo returns the value of the Solo parameter // GetSolo returns the value of the Solo parameter
func (s *strip) Solo() bool { func (s *strip) GetSolo() bool {
return s.getter_bool("Solo") return s.getter_bool("Solo")
} }
@ -93,8 +75,8 @@ func (s *strip) SetSolo(val bool) {
s.setter_bool("Solo", val) s.setter_bool("Solo", val)
} }
// Limit returns the value of the Limit parameter // GetLimit returns the value of the Limit parameter
func (s *strip) Limit() int { func (s *strip) GetLimit() int {
return s.getter_int("Limit") return s.getter_int("Limit")
} }
@ -103,8 +85,8 @@ func (s *strip) SetLimit(val int) {
s.setter_int("Limit", val) s.setter_int("Limit", val)
} }
// Label returns the value of the Label parameter // GetLabel returns the value of the Label parameter
func (s *strip) Label() string { func (s *strip) GetLabel() string {
return s.getter_string("Label") return s.getter_string("Label")
} }
@ -113,8 +95,8 @@ func (s *strip) SetLabel(val string) {
s.setter_string("Label", val) s.setter_string("Label", val)
} }
// Gain returns the value of the Gain parameter // GetGain returns the value of the Gain parameter
func (s *strip) Gain() float64 { func (s *strip) GetGain() float64 {
return s.getter_float("Gain") return s.getter_float("Gain")
} }
@ -123,31 +105,6 @@ func (s *strip) SetGain(val float64) {
s.setter_float("Gain", val) 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 // GainLayer returns the gainlayer field
func (s *strip) GainLayer() []gainLayer { func (s *strip) GainLayer() []gainLayer {
return s.gainLayer return s.gainLayer
@ -155,7 +112,7 @@ func (s *strip) GainLayer() []gainLayer {
// Levels returns the levels field // Levels returns the levels field
func (s *strip) Levels() *levels { 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_ // 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) time.Sleep(time.Millisecond)
} }
// PhysicalStrip represents a single physical strip // physicalStrip represents a single physical strip
type PhysicalStrip struct { type physicalStrip struct {
strip strip
} }
// newPhysicalStrip returns a PhysicalStrip type // newPhysicalStrip returns a physicalStrip type cast to an iStrip
func newPhysicalStrip(i int, k *kind) iStrip { func newPhysicalStrip(i int, k *kind) iStrip {
o := newOutputs(fmt.Sprintf("strip[%d]", i), i) 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) gl := make([]gainLayer, 8)
for j := 0; j < 8; j++ { for j := 0; j < 8; j++ {
gl[j] = newGainLayer(i, j) gl[j] = newGainLayer(i, j)
} }
l := newStripLevels(i, k) l := newStripLevels(i, k)
ps := PhysicalStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, e, c, g, d, gl, l}} ps := physicalStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, gl, l}}
return &ps return iStrip(&ps)
} }
// String implements fmt.stringer interface // String implements fmt.stringer interface
func (p *PhysicalStrip) String() string { func (p *physicalStrip) String() string {
return fmt.Sprintf("PhysicalStrip%d", p.index) return fmt.Sprintf("PhysicalStrip%d", p.index)
} }
// Audibility returns the value of the Audibility parameter // GetComp returns the value of the Comp parameter
func (p *PhysicalStrip) Audibility() float64 { 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") return p.getter_float("Audibility")
} }
// SetAudibility sets the value of the Audibility parameter // 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) p.setter_float("Audibility", val)
} }
// Mc logs a warning reason invalid parameter // GetMc panics reason invalid parameter
// it always returns zero value func (p *physicalStrip) GetMc() bool {
func (p *PhysicalStrip) Mc() bool { panic("invalid parameter MC for physicalStrip")
log.Warn("invalid parameter MC for physicalStrip")
return false
} }
// SetMc logs a warning reason invalid parameter // SetMc panics reason invalid parameter
func (p *PhysicalStrip) SetMc(val bool) { func (p *physicalStrip) SetMc(val bool) {
log.Warn("invalid parameter MC for physicalStrip") panic("invalid parameter MC for physicalStrip")
} }
// Comp returns the comp field // virtualStrip represents a single virtual strip
func (p *PhysicalStrip) Comp() *comp { type virtualStrip struct {
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 {
strip strip
} }
// newVirtualStrip returns a VirtualStrip type // newVirtualStrip returns a virtualStrip type cast to an iStrip
func newVirtualStrip(i int, k *kind) iStrip { func newVirtualStrip(i int, k *kind) iStrip {
o := newOutputs(fmt.Sprintf("strip[%d]", i), i) 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) gl := make([]gainLayer, 8)
for j := 0; j < 8; j++ { for j := 0; j < 8; j++ {
gl[j] = newGainLayer(i, j) gl[j] = newGainLayer(i, j)
} }
l := newStripLevels(i, k) l := newStripLevels(i, k)
vs := VirtualStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, e, c, g, d, gl, l}} vs := virtualStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, gl, l}}
return &vs return iStrip(&vs)
} }
// String implements fmt.stringer interface // String implements fmt.stringer interface
func (v *VirtualStrip) String() string { func (v *virtualStrip) String() string {
return fmt.Sprintf("VirtualStrip%d", v.index) return fmt.Sprintf("VirtualStrip%d", v.index)
} }
// Comp returns the comp field // GetMc returns the value of the MC parameter
func (v *VirtualStrip) Comp() *comp { func (v *virtualStrip) GetMc() bool {
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 {
return v.getter_bool("MC") return v.getter_bool("MC")
} }
// SetMc sets the value of the MC parameter // 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) v.setter_bool("MC", val)
} }
// ColorX logs a warning reason invalid parameter // GetComp panics reason invalid parameter
// it always returns zero value func (v *virtualStrip) GetComp() float64 {
func (v *VirtualStrip) ColorX() float64 { panic("invalid parameter Comp for virtualStrip")
log.Warn("invalid parameter ColorX for virtualStrip")
return 0
} }
// SetColorX logs a warning reason invalid parameter // SetComp panics reason invalid parameter
func (v *VirtualStrip) SetColorX(val float64) { func (v *virtualStrip) SetComp(val float64) {
log.Warn("invalid parameter ColorX for virtualStrip") panic("invalid parameter Comp for virtualStrip")
} }
// ColorY logs a warning reason invalid parameter // GetGate panics reason invalid parameter
// it always returns zero value func (v *virtualStrip) GetGate() float64 {
func (v *VirtualStrip) ColorY() float64 { panic("invalid parameter Gate for virtualStrip")
log.Warn("invalid parameter ColorY for virtualStrip")
return 0
} }
// SetColorY logs a warning reason invalid parameter // SetGate panics reason invalid parameter
func (v *VirtualStrip) SetColorY(val float64) { func (v *virtualStrip) SetGate(val float64) {
log.Warn("invalid parameter ColorY for virtualStrip") panic("invalid parameter Gate for virtualStrip")
} }
// FxX logs a warning reason invalid parameter // GetAudibility panics reason invalid parameter
// it always returns zero value func (v *virtualStrip) GetAudibility() float64 {
func (v *VirtualStrip) FxX() float64 { panic("invalid parameter Audibility for virtualStrip")
log.Warn("invalid parameter FxX for virtualStrip")
return 0
} }
// SetFxX logs a warning reason invalid parameter // SetAudibility panics reason invalid parameter
func (v *VirtualStrip) SetFxX(val float64) { func (v *virtualStrip) SetAudibility(val float64) {
log.Warn("invalid parameter SetFxX for virtualStrip") panic("invalid parameter Audibility 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")
} }
// AppGain sets the gain in db by val for the app matching name. // 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))) 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 // gainLayer represents the 8 gainlayers for a single strip
type gainLayer struct { type gainLayer struct {
iRemote iRemote
@ -582,7 +289,7 @@ func (gl *gainLayer) Set(val float64) {
} }
// newStripLevels returns a levels struct // newStripLevels returns a levels struct
func newStripLevels(i int, k *kind) *levels { func newStripLevels(i int, k *kind) levels {
var init int var init int
var os int var os int
if i < k.PhysIn { if i < k.PhysIn {
@ -592,15 +299,15 @@ func newStripLevels(i int, k *kind) *levels {
init = (k.PhysIn * 2) + ((i - k.PhysIn) * 8) init = (k.PhysIn * 2) + ((i - k.PhysIn) * 8)
os = 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 // PreFader returns the level values for this strip, PREFADER mode
func (l *levels) PreFader() []float64 { func (l *levels) PreFader() []float64 {
_levelCache.stripMode = 0 _levelCache.stripMode = 0
levels := make([]float64, l.offset) var levels []float64
for i := range levels { for i := l.init; i < l.init+l.offset; i++ {
levels[i] = convertLevel(_levelCache.stripLevels[l.init+i]) levels = append(levels, convertLevel(_levelCache.stripLevels[i]))
} }
return levels return levels
} }
@ -608,9 +315,9 @@ func (l *levels) PreFader() []float64 {
// PostFader returns the level values for this strip, POSTFADER mode // PostFader returns the level values for this strip, POSTFADER mode
func (l *levels) PostFader() []float64 { func (l *levels) PostFader() []float64 {
_levelCache.stripMode = 1 _levelCache.stripMode = 1
levels := make([]float64, l.offset) var levels []float64
for i := range levels { for i := l.init; i < l.init+l.offset; i++ {
levels[i] = convertLevel(_levelCache.stripLevels[l.init+i]) levels = append(levels, convertLevel(_levelCache.stripLevels[i]))
} }
return levels return levels
} }
@ -618,9 +325,9 @@ func (l *levels) PostFader() []float64 {
// PostMute returns the level values for this strip, POSTMUTE mode // PostMute returns the level values for this strip, POSTMUTE mode
func (l *levels) PostMute() []float64 { func (l *levels) PostMute() []float64 {
_levelCache.stripMode = 2 _levelCache.stripMode = 2
levels := make([]float64, l.offset) var levels []float64
for i := range levels { for i := l.init; i < l.init+l.offset; i++ {
levels[i] = convertLevel(_levelCache.stripLevels[l.init+i]) levels = append(levels, convertLevel(_levelCache.stripLevels[i]))
} }
return levels return levels
} }

View File

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

View File

@ -9,39 +9,39 @@ import (
func TestStrip0Mute(t *testing.T) { func TestStrip0Mute(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vm.Strip[0].SetMute(true) vm.Strip[0].SetMute(true)
t.Run("Should return true when Strip[0].SetMute(true)", func(t *testing.T) { t.Run("Should return true when SetMute(true)", func(t *testing.T) {
assert.True(t, vm.Strip[0].Mute()) assert.True(t, vm.Strip[0].GetMute())
}) })
vm.Strip[0].SetMute(false) vm.Strip[0].SetMute(false)
t.Run("Should return false when Strip[0].SetMute(false)", func(t *testing.T) { t.Run("Should return false when SetMute(false)", func(t *testing.T) {
assert.False(t, vm.Strip[0].Mute()) assert.False(t, vm.Strip[0].GetMute())
}) })
} }
func TestStrip3A1(t *testing.T) { func TestStrip3A1(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vm.Strip[3].SetA1(true) vm.Strip[3].SetA1(true)
t.Run("Should return true when Strip[3].SetA1(true)", func(t *testing.T) { t.Run("Should return true when SetA1(true)", func(t *testing.T) {
assert.True(t, vm.Strip[3].A1()) assert.True(t, vm.Strip[3].GetA1())
}) })
vm.Strip[3].SetA1(false) vm.Strip[3].SetA1(false)
t.Run("Should return false when Strip[3].SetA1(false)", func(t *testing.T) { t.Run("Should return false when SetA1(false)", func(t *testing.T) {
assert.False(t, vm.Strip[3].A1()) assert.False(t, vm.Strip[3].GetA1())
}) })
} }
func TestStrip2Limit(t *testing.T) { func TestStrip2Limit(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vm.Strip[2].SetLimit(-8) vm.Strip[2].SetLimit(-8)
t.Run("Should return -8 when Strip[2].SetLimit(-8)", func(t *testing.T) { t.Run("Should return -8 when SetLimit(-8)", func(t *testing.T) {
assert.Equal(t, vm.Strip[2].Limit(), -8) assert.Equal(t, vm.Strip[2].GetLimit(), -8)
}) })
vm.Strip[2].SetLimit(-32) vm.Strip[2].SetLimit(-32)
t.Run("Should return -32 when Strip[2].SetLimit(-32)", func(t *testing.T) { t.Run("Should return -32 when SetLimit(-8)", func(t *testing.T) {
assert.Equal(t, vm.Strip[2].Limit(), -32) assert.Equal(t, vm.Strip[2].GetLimit(), -32)
}) })
} }
@ -49,210 +49,162 @@ func TestStrip2Limit(t *testing.T) {
func TestStrip4Label(t *testing.T) { func TestStrip4Label(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vm.Strip[4].SetLabel("test0") vm.Strip[4].SetLabel("test0")
t.Run("Should return test0 when Strip[4].SetLabel('test0')", func(t *testing.T) { t.Run("Should return test0 when SetLimit('test0')", func(t *testing.T) {
assert.Equal(t, "test0", vm.Strip[4].Label()) assert.Equal(t, "test0", vm.Strip[4].GetLabel())
}) })
vm.Strip[4].SetLabel("test1") vm.Strip[4].SetLabel("test1")
t.Run("Should return test1 when Strip[4].SetLabel('test1')", func(t *testing.T) { t.Run("Should return test1 when SetLimit('test1')", func(t *testing.T) {
assert.Equal(t, "test1", vm.Strip[4].Label()) assert.Equal(t, "test1", vm.Strip[4].GetLabel())
}) })
} }
func TestStrip4Gain(t *testing.T) { func TestStrip5Gain(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vm.Strip[4].SetGain(-20.8) vm.Strip[4].SetGain(-20.8)
t.Run("Should return -20.8 when Strip[4].SetGain(-20.8)", func(t *testing.T) { t.Run("Should return -20.8 when SetGain(-20.8)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].Gain(), -20.8) assert.Equal(t, vm.Strip[4].GetGain(), -20.8)
}) })
vm.Strip[4].SetGain(-3.6) vm.Strip[4].SetGain(-3.6)
t.Run("Should return -3.6 when Strip[4].SetGain(-3.6)", func(t *testing.T) { t.Run("Should return -3.6 when SetGain(-3.6)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].Gain(), -3.6) assert.Equal(t, vm.Strip[4].GetGain(), -3.6)
}) })
} }
func TestStrip4CompKnob(t *testing.T) { func TestStrip3Comp(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vm.Strip[4].Comp().SetKnob(8.1) vm.Strip[4].SetComp(8.1)
t.Run("Should return 8.1 when Strip[4].Comp().SetKnob(8.1)", func(t *testing.T) { t.Run("Should return 8.1 when SetGain(8.1)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].Comp().Knob(), 8.1) assert.Equal(t, vm.Strip[4].GetComp(), 8.1)
}) })
vm.Strip[4].Comp().SetKnob(1.6) vm.Strip[4].SetComp(1.6)
t.Run("Should return 1.6 when Strip[4].Comp().SetKnob(1.6)", func(t *testing.T) { t.Run("Should return 1.6 when SetGain(1.6)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].Comp().Knob(), 1.6) assert.Equal(t, vm.Strip[4].GetComp(), 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)
}) })
} }
func TestStrip5Mc(t *testing.T) { func TestStrip5Mc(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vm.Strip[5].SetMc(true) vm.Strip[5].SetMc(true)
t.Run("Should return true when Strip[5].SetMc(true)", func(t *testing.T) { t.Run("Should return true when SetMc(true)", func(t *testing.T) {
assert.True(t, vm.Strip[5].Mc()) assert.True(t, vm.Strip[5].GetMc())
}) })
vm.Strip[5].SetMc(false) vm.Strip[5].SetMc(false)
t.Run("Should return false when Strip[5].SetMc(false)", func(t *testing.T) { t.Run("Should return false when SetMc(false)", func(t *testing.T) {
assert.False(t, vm.Strip[5].Mc()) assert.False(t, vm.Strip[5].GetMc())
}) })
} }
func TestStrip2GainLayer3(t *testing.T) { func TestStrip2GainLayer3(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vm.Strip[2].GainLayer()[3].Set(-18.3) 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) assert.Equal(t, vm.Strip[2].GainLayer()[3].Get(), -18.3)
}) })
vm.Strip[2].GainLayer()[3].Set(-25.6) 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) 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") //t.Skip("skipping test")
vm.Bus[3].Eq().SetOn(true) vm.Bus[3].SetEq(true)
t.Run("Should return true when Bus[3].Eq().SetOn(true)", func(t *testing.T) { t.Run("Should return true when SetEq(true)", func(t *testing.T) {
assert.True(t, vm.Bus[3].Eq().On()) assert.True(t, vm.Bus[3].GetEq())
}) })
vm.Bus[3].Eq().SetOn(false) vm.Bus[3].SetEq(false)
t.Run("Should return false when Bus[3].SetEq(false)", func(t *testing.T) { t.Run("Should return false when SetEq(false)", func(t *testing.T) {
assert.False(t, vm.Bus[3].Eq().On()) assert.False(t, vm.Bus[3].GetEq())
}) })
} }
func TestBus4Label(t *testing.T) { func TestBus4Label(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vm.Bus[4].SetLabel("test0") vm.Bus[4].SetLabel("test0")
t.Run("Should return test0 when Bus[4].SetLabel('test0')", func(t *testing.T) { t.Run("Should return test0 when SetEq('test0')", func(t *testing.T) {
assert.Equal(t, "test0", vm.Bus[4].Label()) assert.Equal(t, "test0", vm.Bus[4].GetLabel())
}) })
vm.Bus[4].SetLabel("test1") vm.Bus[4].SetLabel("test1")
t.Run("Should return test1 when Bus[4].SetLabel('test1')", func(t *testing.T) { t.Run("Should return test1 when SetEq('test1')", func(t *testing.T) {
assert.Equal(t, "test1", vm.Bus[4].Label()) assert.Equal(t, "test1", vm.Bus[4].GetLabel())
}) })
} }
func TestBus3ModeAmix(t *testing.T) { func TestBus3ModeAmix(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vm.Bus[3].Mode().SetAmix(true) vm.Bus[3].Mode().SetAmix(true)
t.Run("Should return true when Bus[3].Mode().SetAmix(true)", func(t *testing.T) { t.Run("Should return true when Mode().SetAmix(true)", func(t *testing.T) {
assert.True(t, vm.Bus[3].Mode().Amix()) assert.True(t, vm.Bus[3].Mode().GetAmix())
}) })
} }
func TestVbanInStream0On(t *testing.T) { func TestVbanInStream0On(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vm.Vban.InStream[0].SetOn(true) vm.Vban.InStream[0].SetOn(true)
t.Run("Should return true when Vban.InStream[0].SetOn(true)", func(t *testing.T) { t.Run("Should return true when SetOn(true)", func(t *testing.T) {
assert.True(t, vm.Vban.InStream[0].On()) assert.True(t, vm.Vban.InStream[0].GetOn())
}) })
vm.Vban.InStream[0].SetOn(false) vm.Vban.InStream[0].SetOn(false)
t.Run("Should return false when Vban.InStream[0].SetOn(false)", func(t *testing.T) { t.Run("Should return false when SetOn(false)", func(t *testing.T) {
assert.False(t, vm.Vban.InStream[0].On()) assert.False(t, vm.Vban.InStream[0].GetOn())
}) })
} }
func TestVbanOutStream6On(t *testing.T) { func TestVbanOutStream6On(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vm.Vban.OutStream[6].SetOn(true) vm.Vban.OutStream[6].SetOn(true)
t.Run("Should return true when Vban.OutStream[6].SetOn(true)", func(t *testing.T) { t.Run("Should return true when SetOn(true)", func(t *testing.T) {
assert.True(t, vm.Vban.OutStream[6].On()) assert.True(t, vm.Vban.OutStream[6].GetOn())
}) })
vm.Vban.OutStream[6].SetOn(false) vm.Vban.OutStream[6].SetOn(false)
t.Run("Should return false when Vban.OutStream[6].SetOn(false)", func(t *testing.T) { t.Run("Should return false when SetOn(false)", func(t *testing.T) {
assert.False(t, vm.Vban.OutStream[6].On()) assert.False(t, vm.Vban.OutStream[6].GetOn())
}) })
} }
func TestVbanOutStream3Name(t *testing.T) { func TestVbanOutStream3Name(t *testing.T) {
//t.Skip("skipping test") t.Skip("skipping test")
vm.Vban.OutStream[3].SetName("test0") vm.Vban.OutStream[3].SetName("test0")
t.Run("Should return test0 when Vban.OutStream[3].SetName('test0')", func(t *testing.T) { t.Run("Should return test0 when SetName('test0')", func(t *testing.T) {
assert.Equal(t, "test0", vm.Vban.OutStream[3].Name()) assert.Equal(t, "test0", vm.Vban.OutStream[3].GetName())
}) })
vm.Vban.OutStream[3].SetName("test1") vm.Vban.OutStream[3].SetName("test1")
t.Run("Should return test1 when Vban.OutStream[3].SetName('test1')", func(t *testing.T) { t.Run("Should return test1 when SetName('test1')", func(t *testing.T) {
assert.Equal(t, "test1", vm.Vban.OutStream[3].Name()) assert.Equal(t, "test1", vm.Vban.OutStream[3].GetName())
}) })
} }
func TestVbanInStream4Bit(t *testing.T) { func TestVbanInStream4Bit(t *testing.T) {
//t.Skip("skipping test") t.Skip("skipping test")
vm.Vban.InStream[4].SetBit(16) t.Run("Should panic when instream SetBit(16)", func(t *testing.T) {
t.Run("Should log 'bit is readonly for vban instreams' when instream Vban.InStream[4].SetBit(16)", func(t *testing.T) { defer func() {
assert.Contains(t, logstring.String(), "bit is readonly for vban instreams") if r := recover(); r == nil {
t.Error("expected panic")
}
}()
vm.Vban.InStream[4].SetBit(16)
}) })
} }
func TestVbanOutStream4Bit(t *testing.T) { func TestVbanOutStream4Bit(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vm.Vban.OutStream[4].SetBit(16) vm.Vban.OutStream[4].SetBit(16)
t.Run("Should return 16 when Vban.OutStream[4].SetBit(16)", func(t *testing.T) { t.Run("Should return 16 when SetBit(16)", func(t *testing.T) {
assert.Equal(t, vm.Vban.OutStream[4].Bit(), 16) assert.Equal(t, vm.Vban.OutStream[4].GetBit(), 16)
}) })
vm.Vban.OutStream[4].SetBit(24) vm.Vban.OutStream[4].SetBit(24)
t.Run("Should return 24 when Vban.OutStream[4].SetBit(24)", func(t *testing.T) { t.Run("Should return 24 when SetBit(24)", func(t *testing.T) {
assert.Equal(t, vm.Vban.OutStream[4].Bit(), 24) 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 // convertLevel performs the necessary math for a channel level
func convertLevel(i float64) float64 { func convertLevel(i float64) float64 {
if i > 0 { if i > 0 {
val := 20 * math.Log10(i) val := 20 * math.Log10(float64(i))
return roundFloat(val, 1) return float64(roundFloat(float64(val), 1))
} }
return -200.0 return -200.0
} }

129
vban.go
View File

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