Compare commits

..

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

41 changed files with 973 additions and 2941 deletions

9
.gitignore vendored
View File

@ -1,8 +1,5 @@
# quick tests
quick
#config
config.toml
quick.go
# Binaries for programs and plugins
*.exe
@ -19,7 +16,3 @@ config.toml
# Dependency directories (remove the comment below to include it)
# vendor/
# work files
go.work
go.work.sum

View File

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

589
README.md
View File

@ -1,28 +1,35 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/onyx-and-iris/voicemeeter.svg)](https://pkg.go.dev/github.com/onyx-and-iris/voicemeeter/v2)
[![Go Reference](https://pkg.go.dev/badge/github.com/onyx-and-iris/voicemeeter-api-go.svg)](https://pkg.go.dev/github.com/onyx-and-iris/voicemeeter-api-go)
# A Go Wrapper for the Voicemeeter API
# A Go Wrapper for Voicemeeter API
This package offers a Go interface for the Voicemeeter Remote C API.
For an outline of past/future changes refer to: [CHANGELOG](CHANGELOG.md)
## Tested against
- Basic 1.1.1.1
- Banana 2.1.1.1
- Potato 3.1.1.1
- Basic 1.0.8.2
- Banana 2.0.6.2
- Potato 3.0.2.2
## Requirements
- [Voicemeeter](https://voicemeeter.com/)
- Go 1.18 or greater
- [Voicemeeter](https://voicemeeter.com/)
- Go 1.18 or greater
## Installation
Initialize your own module then `go get`
#### GO.MOD
```
go mod init github.com/x/y
go get github.com/onyx-and-iris/voicemeeter/v2
```
Add to your `go.mod` file:
`require github.com/onyx-and-iris/voicemeeter-api-go v1.0.2`
#### GO GET
Install voicemeeter-api-go package from your console
`go get github.com/onyx-and-iris/voicemeeter-api-go`
## `Use`
@ -33,558 +40,348 @@ package main
import (
"fmt"
"log"
"github.com/onyx-and-iris/voicemeeter/v2"
"github.com/onyx-and-iris/voicemeeter-api-go"
)
func main() {
vm, err := vmConnect()
if err != nil {
log.Fatal(err)
}
defer vm.Logout()
kindId := "banana"
vmRem := voicemeeter.GetRemote(kindId)
vm.Strip[0].SetLabel("rode podmic")
vm.Strip[0].SetMute(true)
fmt.Printf("Strip 0 (%s) mute was set to %v\n", vm.Strip[0].Label(), vm.Strip[0].Mute())
}
vmRem.Login()
func vmConnect() (*voicemeeter.Remote, error) {
vm, err := voicemeeter.NewRemote("banana", 20)
if err != nil {
return nil, err
}
vmRem.Strip[0].SetLabel("rode podmic")
vmRem.Strip[0].SetMute(true)
fmt.Printf("Strip 0 (%s) mute was set to %v\n", vmRem.Strip[0].GetLabel(), vmRem.Strip[0].GetMute())
err = vm.Login()
if err != nil {
return nil, err
}
return vm, nil
vmRem.Logout()
}
```
## `voicemeeter.NewRemote(<kindId>, <delay>, opts ...Option)`
### `kindId`
## `kindId`
Pass the kind of Voicemeeter as an argument. kindId may be:
- `basic`
- `banana`
- `potato`
### `delay`
Pass a delay in milliseconds to force the getters to wait for dirty parameters to clear.
Useful if not listening for event updates.
### `voicemeeter.WithTimeout(timeout int)`
Set a login timeout, defaults to 2 seconds. For example to set it to 1s:
`voicemeeter.NewRemote("banana", 20, voicemeeter.WithTimeout(1))`
### `voicemeeter.WithBits(bits int)`
Override the type of Voicemeeter GUI to launch on 64 bit systems. For example, to force 32 bit GUI:
`voicemeeter.NewRemote("banana", 20, voicemeeter.WithBits(32))`
- `basic`
- `banana`
- `potato`
## `Remote Type`
#### `vm.Strip`
#### `vmRem.Strip`
[]t_strip slice containing both physicalStrip and virtualStrip types
#### `vm.Bus`
#### `vmRem.Bus`
[]t_bus slice containing both physicalBus and virtualBus types
#### `vm.Button`
#### `vmRem.Button`
[]button slice containing button types, one for each macrobutton
#### `vm.Command`
#### `vmRem.Command`
pointer to command type, represents action type functions
#### `vm.Vban`
#### `vmRem.Vban`
pointer to vban type, containing both vbanInStream and vbanOutStream slices
#### `vm.Device`
#### `vmRem.Device`
pointer to device type, represents physical input/output hardware devices
#### `vm.Recorder`
#### `vmRem.Recorder`
pointer to recorder type, represents the recorder
#### `vm.Midi`
pointer to midi type, represents a connected midi device
#### `vm.Type()`
#### `vmRem.Type()`
returns the type of Voicemeeter as a string
#### `vm.Version()`
#### `vmRem.Version()`
returns the version of Voicemeeter as a string
#### `vm.GetFloat(<param>)`
gets a float parameter value
#### `vm.SetFloat(<param>, <value>)`
sets a float parameter value eg. vm.SetFloat("strip[0].mute", 1)
#### `vm.GetString(<param>)`
gets a string parameter value
#### `vm.SetString(<param>, <value>)`
sets a string parameter value eg. vm.SetString("strip[0].label", "podmic")
#### `vm.SendText(<script>)`
#### `vmRem.SendText(<script>)`
sets many parameters in script format eg. ("Strip[0].Mute=1;Bus[3].Gain=3.6")
#### `vm.Register(o observer)`
#### `vmRem.Register(o observer)`
register an observer type as an observer
register an object as an observer
#### `vm.Deregister(o observer)`
#### `vmRem.Deregister(o observer)`
deregister an observer type as an observer
deregister an object as an observer
#### `vm.EventAdd(<events>)`
adds a single or multiple events to the pooler. Accepts a string or slice of strings.
#### `vm.EventRemove(<events>)`
removes a single or multiple events from the pooler. Accepts a string or slice of strings.
#### `vm.Pdirty()`
#### `vmRem.Pdirty()`
returns True iff a GUI parameter has changed
#### `vm.Mdirty()`
#### `vmRem.Mdirty()`
returns True iff a macrobutton parameter has changed
#### `vm.Sync()`
Use this to force dirty parameters to clear after a delay in milliseconds.
returns True iff a macrobutton paramter has changed
## `Available commands`
### Strip
The following methods are available
The following functions are available
- `Mute() bool`
- `SetMute(val bool)`
- `Mono() bool`
- `SetMono(val bool)`
- `Solo() bool`
- `SetSolo(val bool)`
- `Limit() int`
- `SetLimit(val int)` from -40 to 12
- `Label() string`
- `SetLabel(val string)`
- `Gain() float64`
- `SetGain(val float64)` from -60.0 to 12.0
- `Mc() bool`
- `SetMc(val bool)`
- `Audibility() float64`
- `SetAudibility(val float64)` from 0.0 to 10.0
- `A1() bool - A5() bool`
- `SetA1(val bool) - SetA5(val bool)`
- `B1() bool - B3() bool`
- `SetB1(val bool) bool - SetB3(val bool) bool`
- `AppGain(name string, gain float64)`
- `AppMute(name string, val bool)`
- `GetMute() bool`
- `SetMute(val bool)`
- `GetMono() bool`
- `SetMono(val bool)`
- `GetSolo() bool`
- `SetSolo(val bool)`
- `GetLimit() int`
- `SetLimit(val int)` from -40 to 12
- `GetLabel() string`
- `SetLabel(val string)`
- `GetGain() float64`
- `SetGain(val float32)` from -60.0 to 12.0
- `GetMc() bool`
- `SetMc(val bool)`
- `GetComp() float64`
- `SetComp(val float32)` from 0.0 to 10.0
- `GetGate() float64`
- `SetGate(val float32)` from 0.0 to 10.0
- `GetAudibility() float64`
- `SetAudibility(val float32)` from 0.0 to 10.0
- `GetA1() bool - GetA5() bool`
- `SetA1(val bool) - SetA5(val bool)`
example:
```go
vm.Strip[3].SetGain(3.7)
fmt.Println(vm.Strip[0].Label())
vm.Strip[4].SetA1(true)
vm.Strip[5].AppGain("Spotify", 0.5)
vm.Strip[5].AppMute("Spotify", true)
vmRem.Strip[3].SetGain(3.7)
fmt.Println(vmRem.Strip[0].GetLabel())
vmRem.Strip[4].SetA1(true)
```
##### Comp
##### Gainlayers
- `vm.Strip[i].Comp()`
- `vmRem.Strip[i].GainLayer()[j]`
The following methods are available
The following functions are available
- `Knob() float64`
- `SetKnob(val float64)` from 0.0 to 10.0
- `GainIn() float64`
- `SetGainIn(val float64)` from -24.0 to 24.0
- `Ratio() float64`
- `SetRatio(val float64)` from 1.0 to 8.0
- `Threshold() float64`
- `SetThreshold(val float64)` from -40.0 to -3.0
- `Attack() float64`
- `SetAttack(val float64)` from 0.0 to 200.0
- `Release() float64`
- `SetRelease(val float64)` from 0.0 to 5000.0
- `Knee() float64`
- `SetKnee(val float64)` from 0.0 to 1.0
- `GainOut() float64`
- `SetGainOut(val float64)` from -24.0 to 24.0
- `MakeUp() bool`
- `SetMakeUp(val bool)`
- `Get() float64`
- `Set(val float32)`
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:
```go
vm.Strip[6].GainLayer()[3].Set(-13.6)
vmRem.Strip[6].GainLayer()[3].Set(-13.6)
```
##### Levels
- `vm.Strip[i].Levels()`
- `vmRem.Strip[i].Levels()`
The following methods are available
The following functions are available
- `PreFader() []float64`
- `PostFader() []float64`
- `PostMute() []float64`
- `PreFader() []float32`
- `PostFader() []float32`
- `PostMute() []float32`
example:
```go
fmt.Println(vm.Strip[5].Levels().PreFader())
fmt.Println(vmRem.Strip[5].Levels().PreFader())
```
### Bus
The following methods are available
The following functions are available
- `String() string`
- `Mute() bool`
- `SetMute(val bool)`
- `Mono() bool`
- `SetMono(val bool)`
- `Label() string`
- `SetLabel(val string)`
- `Gain() float64`
- `SetGain(val float64)` from -60.0 to 12.0
example:
- `String() string`
- `GetMute() bool`
- `SetMute(val bool)`
- `GetEq() bool`
- `SetEq(val bool)`
- `GetMono() bool`
- `SetMono(val bool)`
- `GetLabel() string`
- `SetLabel(val string)`
- `GetGain() float64`
- `SetGain(val float32)` from -60.0 to 12.0
```go
vm.Bus[3].SetEq(true)
fmt.Println(vm.Bus[0].Label())
vmRem.Bus[3].SetEq(true)
fmt.Println(vmRem.Bus[0].GetLabel())
```
##### Modes
- `vm.Bus[i].Mode()`
- `vmRem.Bus[i].Mode()`
The following methods are available
The following functions are available
- `SetNormal(val bool)`
- `Normal() bool`
- `SetAmix(val bool)`
- `Amix() bool`
- `SetBmix(val bool)`
- `Bmix() bool`
- `SetRepeat(val bool)`
- `Repeat() bool`
- `SetComposite(val bool)`
- `Composite() bool`
- `SetTvMix(val bool)`
- `TvMix() bool`
- `SetUpMix21(val bool)`
- `UpMix21() bool`
- `SetUpMix41(val bool)`
- `UpMix41() bool`
- `SetUpMix61(val bool)`
- `UpMix61() bool`
- `SetCenterOnly(val bool)`
- `CenterOnly() bool`
- `SetLfeOnly(val bool)`
- `LfeOnly() bool`
- `SetRearOnly(val bool)`
- `RearOnly() bool`
- `SetNormal(val bool)`
- `GetNormal() bool`
- `SetAmix(val bool)`
- `GetAmix() bool`
- `SetBmix(val bool)`
- `GetBmix() bool`
- `SetRepeat(val bool)`
- `GetRepeat() bool`
- `SetComposite(val bool)`
- `GetComposite() bool`
- `SetTvMix(val bool)`
- `GetTvMix() bool`
- `SetUpMix21(val bool)`
- `GetUpMix21() bool`
- `SetUpMix41(val bool)`
- `GetUpMix41() bool`
- `SetUpMix61(val bool)`
- `GetUpMix61() bool`
- `SetCenterOnly(val bool)`
- `GetCenterOnly() bool`
- `SetLfeOnly(val bool)`
- `GetLfeOnly() bool`
- `SetRearOnly(val bool)`
- `GetRearOnly() bool`
example:
```go
vm.Bus[3].Mode().SetAmix(true)
vm.Bus[4].Mode().SetCenterOnly(true)
vmRem.Bus[3].Mode().SetAmix(true)
vmRem.Bus[4].Mode().SetCenterOnly(true)
```
##### Levels
- `vm.Bus[i].Levels()`
- `vmRem.Bus[i].Levels()`
The following methods are available
The following functions are available
- `All() []float64`
- `All() []float32`
example:
```go
fmt.Println(vm.Bus[1].Levels().All())
```
### Strip | Bus
##### EQ
- `vm.Strip[i].Eq()` `vm.Bus[i].Eq()`
The following methods are available.
- `On() bool`
- `SetOn(val bool)`
- `Ab() bool`
- `SetAb(val bool)`
example:
```go
vm.Strip[1].Eq().SetOn(true)
fmt.Println(vm.Bus[3].Eq().Ab())
```
The following methods are available.
- `FadeTo(target float64, time_ int)`: float, int
- `FadeBy(change float64, time_ int)`: float, int
Modify gain to or by the selected amount in db over a time interval in ms.
example:
```go
vm.Strip[3].FadeBy(-8.3, 500)
vm.Bus[3].FadeTo(-12.8, 500)
fmt.Println(vmRem.Bus[1].Levels().All())
```
### Button
The following methods are available
The following functions are available
- `State() bool`
- `SetState(val bool)`
- `StateOnly() bool`
- `SetStateOnly(val bool)`
- `Trigger() bool`
- `SetTrigger(val bool)`
- `GetState() bool`
- `SetState(val bool)`
- `GetStateOnly() bool`
- `SetStateOnly(val bool)`
- `GetTrigger() bool`
- `SetTrigger(val bool)`
example:
```go
vm.Button[37].SetState(true)
fmt.Println(vm.Button[64].StateOnly())
vmRem.Button[37].SetState(true)
fmt.Println(vmRem.Button[64].GetStateOnly())
```
### Command
The following methods are available
The following functions are available
- `Show()` Show Voicemeeter GUI if it's hidden
- `Hide()` Hide Voicemeeter GUI if it's shown
- `Shutdown()` Shuts down the GUI
- `Restart()` Restart the audio engine
- `Lock(val bool)` Lock the Voicemeeter GUI
- `Show()` Show Voicemeeter GUI if it's hidden
- `Hide()` Hide Voicemeeter GUI if it's shown
- `Shutdown()` Shuts down the GUI
- `Restart()` Restart the audio engine
- `Lock(val bool)` Lock the Voicemeeter GUI
example:
```go
vm.Command.Restart()
vm.Command.Show()
vmRem.Command.Restart()
vmRem.Command.Show()
```
### VBAN
- `vm.Vban.Enable()` `vm.Vban.Disable()` Turn VBAN on or off
- `vmRem.Vban.Enable()` `vmRem.Vban.Disable()` Turn VBAN on or off
##### Instream | Outstream
- `vm.Vban.InStream[i]` `vm.Vban.OutStream[i]`
- `vmRem.Vban.InStream` `vmRem.Vban.OutStream`
The following methods are available
The following functions are available
- `On() bool`
- `SetOn(val bool)`
- `Name() string`
- `SetName(val string)`
- `Ip() string`
- `SetIp(val string)`
- `Port() int`
- `SetPort(val int)` from 1024 to 65535
- `Sr() int`
- `SetSr(val int)` (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000)
- `Channel() int`
- `SetChannel(val int)` from 1 to 8
- `Bit() int`
- `SetBit(val int)` 16 or 24
- `Quality() int`
- `SetQuality(val int)` from 0 to 4
- `Route() int`
- `SetRoute(val int)` from 0 to 8
- `GetOn() bool`
- `SetOn(val bool)`
- `GetName() string`
- `SetName(val string)`
- `GetIp() string`
- `SetIp(val string)`
- `GetPort() int`
- `SetPort(val int)` from 1024 to 65535
- `GetSr() int`
- `SetSr(val int)` (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000)
- `GetChannel() int`
- `SetChannel(val int)` from 1 to 8
- `GetBit() int`
- `SetBit(val int)` 16 or 24
- `GetQuality() int`
- `SetQuality(val int)` from 0 to 4
- `GetRoute() int`
- `SetRoute(val int)` from 0 to 8
example:
```go
# turn VBAN on
vm.Vban.Enable()
vmRem.Vban.Enable()
// turn on vban instream 0
vm.Vban.InStream[0].SetOn(true)
vmRem.Vban.InStream[0].SetOn(true)
// set bit property for outstream 3 to 24
vm.Vban.OutStream[3].SetBit(24)
vmRem.Vban.OutStream[3].SetBit(24)
```
### Device
The following methods are available
The following functions are available
- `Ins()`
- `Outs()`
- `Input(val int)`
- `Output(val int)`
- `Ins`
- `Outs`
- `Input(val int)`
- `Output(val int)`
example:
```go
for i := 0; i < int(vm.Device.Ins()); i++ {
fmt.Println(vm.Device.Input(i))
for i := 0; i < int(vmRem.Device.Ins()); i++ {
fmt.Println(vmRem.Device.Input(i))
}
```
### Recorder
The following methods are available
The following functions are available
- `Play()`
- `Stop()`
- `Pause()`
- `Replay()`
- `Record()`
- `Ff()`
- `Rew()`
- `Play()`
- `Stop()`
- `Pause()`
- `Replay()`
- `Record()`
- `Ff()`
- `Rew()`
example:
```go
vm.Recorder.Play()
vm.Recorder.Stop()
vmRem.Recorder.Play()
vmRem.Recorder.Stop()
# Enable loop play
vm.Recorder.Loop(true)
vmRem.Recorder.Loop(true)
# Disable recorder out channel B2
vm.Recorder.SetB2(false)
```
### Midi
The following methods are available
- `Channel()` returns the current midi channel
- `Current()` returns the most recently pressed midi button
- `Get(<button>)` returns the value in cache for the midi button
example:
```go
var current = vm.Midi.Current()
var val = vm.Midi.Get(current)
```
### Events
By default level updates are disabled. Any event may be enabled or disabled. The following events exist:
- `pdirty` parameter updates
- `mdirty` macrobutton updates
- `midi` midi updates
- `ldirty` level updates
example:
```go
events := []string{"ldirty", "mdirty", "pdirty"}
vm.EventAdd(events...)
vm.EventRemove(events...)
vmRem.Recorder.SetB2(false)
```
### Run tests
@ -597,4 +394,4 @@ go test ./...
### Official Documentation
- [Voicemeeter Remote C API](https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/main/VoicemeeterRemoteAPI.pdf)
- [Voicemeeter Remote C API](https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/main/VoicemeeterRemoteAPI.pdf)

300
base.go
View File

@ -2,16 +2,13 @@ package voicemeeter
import (
"bytes"
"errors"
"fmt"
"math"
"runtime"
"os"
"strings"
"syscall"
"time"
"unsafe"
log "github.com/sirupsen/logrus"
)
var (
@ -40,281 +37,222 @@ var (
vmMdirty = mod.NewProc("VBVMR_MacroButton_IsDirty")
vmGetMacroStatus = mod.NewProc("VBVMR_MacroButton_GetStatus")
vmSetMacroStatus = mod.NewProc("VBVMR_MacroButton_SetStatus")
vmGetMidiMessage = mod.NewProc("VBVMR_GetMidiMessage")
)
// login logs into the API,
// attempts to launch Voicemeeter if it's not running,
// initializes dirty parameters.
func login(kindId string, timeout, bits int) error {
// then attempts to launch Voicemeeter if it's not running.
func login(kindId string) {
res, _, _ := vmLogin.Call()
if res == 1 {
runVoicemeeter(kindId, bits)
runVoicemeeter(kindId)
time.Sleep(time.Second)
} else if res != 0 {
err := fmt.Errorf("VBVMR_Login returned %d", res)
return err
fmt.Println(err)
os.Exit(1)
}
var ver_s string
start := time.Now()
var err error
for time.Since(start).Seconds() < float64(timeout) {
time.Sleep(time.Duration(100) * time.Millisecond)
if ver_s, err = getVersion(); err == nil {
log.Infof("Logged into Voicemeeter %s v%s", kindMap[kindId], ver_s)
log.Debugf("Log in time: %.2f", time.Since(start).Seconds())
break
}
fmt.Println("Logged into API")
for pdirty() || mdirty() {
}
if err != nil {
return errors.New("timeout logging into the API")
}
clear()
return nil
}
// logout logs out of the API,
// delayed for 100ms to allow final operation to complete.
func logout(kindId string) error {
func logout() {
time.Sleep(100 * time.Millisecond)
res, _, _ := vmLogout.Call()
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_Logout returned %d", int32(res))
return err
if res != 0 {
err := fmt.Errorf("VBVMR_Logout returned %d", res)
fmt.Println(err)
os.Exit(1)
}
log.Infof("Logged out of Voicemeeter %s", kindMap[kindId])
return nil
fmt.Println("Logged out of API")
}
// runVoicemeeter attempts to launch a Voicemeeter GUI of a kind.
func runVoicemeeter(kindId string, bits int) error {
func runVoicemeeter(kindId string) {
vals := map[string]uint64{
"basic": 1,
"banana": 2,
"potato": 3,
}
val := vals[kindId]
if strings.Contains(runtime.GOARCH, "64") && bits == 64 {
val += 3
res, _, _ := vmRunvm.Call(uintptr(vals[kindId]))
if res != 0 {
err := fmt.Errorf("VBVMR_RunVoicemeeter returned %d", res)
fmt.Println(err)
os.Exit(1)
}
res, _, _ := vmRunvm.Call(uintptr(val))
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_RunVoicemeeter returned %d", int32(res))
return err
}
return nil
}
// getVersion returns the version of Voicemeeter as a string
func getVersion() (string, error) {
func getVersion() string {
var ver uint64
res, _, _ := vmGetvmVersion.Call(uintptr(unsafe.Pointer(&ver)))
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterVersion returned %d", int32(res))
log.Error(err.Error())
return "", err
if res != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterVersion returned %d", res)
fmt.Println(err)
os.Exit(1)
}
v1 := (ver & 0xFF000000) >> 24
v2 := (ver & 0x00FF0000) >> 16
v3 := (ver & 0x0000FF00) >> 8
v4 := ver & 0x000000FF
return fmt.Sprintf("%d.%d.%d.%d", v1, v2, v3, v4), nil
return fmt.Sprintf("%d.%d.%d.%d", v1, v2, v3, v4)
}
// pdirty returns true iff a parameter value has changed
func pdirty() (bool, error) {
func pdirty() bool {
res, _, _ := vmPdirty.Call()
if int32(res) < 0 {
err := fmt.Errorf("VBVMR_IsParametersDirty returned %d", int32(res))
log.Error(err.Error())
return false, err
}
return int32(res) == 1, nil
return int(res) == 1
}
// mdirty returns true iff a macrobutton value has changed
func mdirty() (bool, error) {
func mdirty() bool {
res, _, _ := vmMdirty.Call()
if int32(res) < 0 {
err := fmt.Errorf("VBVMR_MacroButton_IsDirty returned %d", int32(res))
log.Error(err.Error())
return false, err
}
return int32(res) == 1, nil
return int(res) == 1
}
// ldirty returns true iff a level value has changed
func ldirty(k *kind) (bool, error) {
_levelCache.stripLevelsBuff = make([]float64, (2*k.PhysIn)+(8*k.VirtIn))
_levelCache.busLevelsBuff = make([]float64, 8*k.NumBus())
func ldirty(k *kind) bool {
_levelCache.stripLevelsBuff = make([]float32, (2*k.physIn)+(8*k.virtIn))
_levelCache.busLevelsBuff = make([]float32, 8*k.numBus())
for i := 0; i < (2*k.PhysIn)+(8*k.VirtIn); i++ {
val, err := getLevel(_levelCache.stripMode, i)
if err != nil {
log.Error(err.Error())
return false, err
}
_levelCache.stripLevelsBuff[i] = val
for i := 0; i < (2*k.physIn)+(8*k.virtIn); i++ {
_levelCache.stripLevelsBuff[i] = float32(getLevel(_levelCache.stripMode, i))
_levelCache.stripComp[i] = _levelCache.stripLevelsBuff[i] == _levelCache.stripLevels[i]
}
for i := 0; i < 8*k.NumBus(); i++ {
val, err := getLevel(3, i)
if err != nil {
log.Error(err.Error())
return false, err
}
_levelCache.busLevelsBuff[i] = val
for i := 0; i < 8*k.numBus(); i++ {
_levelCache.busLevelsBuff[i] = float32(getLevel(3, i))
_levelCache.busComp[i] = _levelCache.busLevelsBuff[i] == _levelCache.busLevels[i]
}
return !(allTrue(_levelCache.stripComp, (2*k.PhysIn)+(8*k.VirtIn)) && allTrue(_levelCache.busComp, 8*k.NumBus())), nil
}
func clear() {
for {
pdirty, _ := pdirty()
mdirty, _ := mdirty()
if !(pdirty || mdirty) {
break
}
}
return !(allTrue(_levelCache.stripComp, (2*k.physIn)+(8*k.virtIn)) && allTrue(_levelCache.busComp, 8*k.numBus()))
}
// getVMType returns the type of Voicemeeter, as a string
func getVMType() (string, error) {
func getVMType() string {
var type_ uint64
res, _, _ := vmGetvmType.Call(
uintptr(unsafe.Pointer(&type_)),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterType returned %d", int32(res))
return "", err
if res != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterType returned %d", res)
fmt.Println(err)
os.Exit(1)
}
vals := map[uint64]string{
1: "basic",
2: "banana",
3: "potato",
}
return vals[type_], nil
return vals[type_]
}
// getParameterFloat gets the value of a float parameter
func getParameterFloat(name string) (float64, error) {
if vmsync {
time.Sleep(time.Duration(vmdelay) * time.Millisecond)
clear()
}
func getParameterFloat(name string) float64 {
var value float32
b := append([]byte(name), 0)
res, _, _ := vmGetParamFloat.Call(
uintptr(unsafe.Pointer(&b[0])),
uintptr(unsafe.Pointer(&value)),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetParameterFloat returned %d", int32(res))
return 0, err
if res != 0 {
err := fmt.Errorf("VBVMR_GetParameterFloat returned %d", res)
fmt.Println(err)
os.Exit(1)
}
return math.Round(float64(value)*10) / 10, nil
return math.Round(float64(value)*10) / 10
}
// setParameterFloat sets the value of a float parameter
func setParameterFloat(name string, value float64) error {
func setParameterFloat(name string, value float32) {
b1 := append([]byte(name), 0)
b2 := math.Float32bits(float32(value))
b2 := math.Float32bits(value)
res, _, _ := vmSetParamFloat.Call(
uintptr(unsafe.Pointer(&b1[0])),
uintptr(b2),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_SetParameterFloat returned %d", int32(res))
return err
if res != 0 {
err := fmt.Errorf("VBVMR_SetParameterFloat returned %d", res)
fmt.Println(err)
os.Exit(1)
}
return nil
}
// getParameterString gets the value of a string parameter
func getParameterString(name string) (string, error) {
if vmsync {
time.Sleep(time.Duration(vmdelay) * time.Millisecond)
clear()
}
func getParameterString(name string) string {
b1 := append([]byte(name), 0)
var b2 [512]byte
res, _, _ := vmGetParamString.Call(
uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetParameterStringA returned %d", int32(res))
return "", err
if res != 0 {
err := fmt.Errorf("VBVMR_GetParameterStringA returned %d", res)
fmt.Println(err)
os.Exit(1)
}
str := bytes.Trim(b2[:], "\x00")
return string(str), nil
return string(str)
}
// setParameterString sets the value of a string parameter
func setParameterString(name, value string) error {
// getParameterString sets the value of a string parameter
func setParameterString(name, value string) {
b1 := append([]byte(name), 0)
b2 := append([]byte(value), 0)
res, _, _ := vmSetParamString.Call(
uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_SetParameterStringA returned %d", int32(res))
return err
if res != 0 {
err := fmt.Errorf("VBVMR_SetParameterStringA returned %d", res)
fmt.Println(err)
os.Exit(1)
}
return nil
}
// setParametersMulti sets multiple parameters with a script
func setParametersMulti(script string) error {
func setParametersMulti(script string) {
b1 := append([]byte(script), 0)
res, _, _ := vmSetParameters.Call(
uintptr(unsafe.Pointer(&b1[0])),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_SetParameters returned %d", int32(res))
return err
if res != 0 {
err := fmt.Errorf("VBVMR_SetParameters returned %d", res)
fmt.Println(err)
os.Exit(1)
}
return nil
}
// getMacroStatus gets a macrobutton value
func getMacroStatus(id, mode int) (float64, error) {
if vmsync {
time.Sleep(time.Duration(vmdelay) * time.Millisecond)
clear()
}
func getMacroStatus(id, mode int) float32 {
var state float32
res, _, _ := vmGetMacroStatus.Call(
uintptr(id),
uintptr(unsafe.Pointer(&state)),
uintptr(mode),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_MacroButton_GetStatus returned %d", int32(res))
return 0, err
if res != 0 {
err := fmt.Errorf("VBVMR_MacroButton_GetStatus returned %d", res)
fmt.Println(err)
os.Exit(1)
}
return float64(state), nil
return state
}
// setMacroStatus sets a macrobutton value
func setMacroStatus(id, state, mode int) error {
func setMacroStatus(id, state, mode int) {
res, _, _ := vmSetMacroStatus.Call(
uintptr(id),
uintptr(state),
uintptr(mode),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_MacroButton_SetStatus returned %d", int32(res))
return err
if res != 0 {
err := fmt.Errorf("VBVMR_MacroButton_SetStatus returned %d", res)
fmt.Println(err)
os.Exit(1)
}
return nil
}
// getNumDevices returns the number of hardware input/output devices
func getNumDevices(dir string) uint64 {
func get_num_devices(dir string) uint64 {
if strings.Compare(dir, "in") == 0 {
res, _, _ := vmGetDevNumIn.Call()
return uint64(res)
@ -324,8 +262,7 @@ func getNumDevices(dir string) uint64 {
}
}
// getDeviceDescription returns name, driver type and hwid for a given device
func getDeviceDescription(i int, dir string) (string, uint64, string, error) {
func get_device_description(i int, dir string) (string, uint64, string) {
var t_ uint64
var b1 [512]byte
var b2 [512]byte
@ -336,9 +273,10 @@ func getDeviceDescription(i int, dir string) (string, uint64, string, error) {
uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_Input_GetDeviceDescA returned %d", int32(res))
return "", 0, "", err
if res != 0 {
err := fmt.Errorf("VBVMR_Input_GetDeviceDescA returned %d", res)
fmt.Println(err)
os.Exit(1)
}
} else {
res, _, _ := vmGetDevDescOut.Call(
@ -347,63 +285,29 @@ func getDeviceDescription(i int, dir string) (string, uint64, string, error) {
uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_Output_GetDeviceDescA returned %d", int32(res))
return "", 0, "", err
if res != 0 {
err := fmt.Errorf("VBVMR_Output_GetDeviceDescA returned %d", res)
fmt.Println(err)
os.Exit(1)
}
}
name := bytes.Trim(b1[:], "\x00")
hwid := bytes.Trim(b2[:], "\x00")
return string(name), t_, string(hwid), nil
return string(name), t_, string(hwid)
}
// getLevel returns a single level value of type type_ for channel[i]
func getLevel(type_, i int) (float64, error) {
func getLevel(type_, i int) float32 {
var val float32
res, _, _ := vmGetLevelFloat.Call(
uintptr(type_),
uintptr(i),
uintptr(unsafe.Pointer(&val)),
)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetLevel returned %d", int32(res))
return 0, err
if res != 0 {
err := fmt.Errorf("VBVMR_GetLevel returned %d", res)
fmt.Println(err)
os.Exit(1)
}
return float64(val), nil
}
// getMidiMessage gets midi channel, pitch and velocity for a single midi input
func getMidiMessage() (bool, error) {
var midi = newMidi()
var b1 [1024]byte
res, _, _ := vmGetMidiMessage.Call(
uintptr(unsafe.Pointer(&b1[0])),
uintptr(1024),
)
x := int32(res)
if x < 0 {
if x == -2 {
err := fmt.Errorf("VBVMR_GetMidiMessage returned %d", int32(res))
if err != nil {
log.Error(err)
return false, err
}
}
}
msg := bytes.Trim(b1[:], "\x00")
if len(msg) > 0 {
for i := 0; i < len(msg)%3; i++ {
msg = append(msg, 0)
}
for i := 0; i < len(msg); i += 3 {
var ch = int(msg[i])
var pitch = int(msg[i+1])
var vel = int(msg[i+2])
midi.channel = ch
midi.current = pitch
midi.cache[pitch] = vel
}
}
return len(msg) > 0, nil
return val
}

230
bus.go
View File

@ -2,37 +2,33 @@ package voicemeeter
import (
"fmt"
"time"
)
// iBus defines the interface bus types must satisfy
type iBus interface {
type t_bus interface {
String() string
Mute() bool
GetMute() bool
SetMute(val bool)
Mono() bool
GetEq() bool
SetEq(val bool)
GetMono() bool
SetMono(val bool)
Label() string
GetLabel() string
SetLabel(val string)
Gain() float64
SetGain(val float64)
Eq() *eQ
Mode() *busMode
GetGain() float64
SetGain(val float32)
Mode() t_busMode
Levels() *levels
FadeTo(target float32, time_ int)
FadeBy(change float32, time_ int)
}
// bus represents a bus channel
type bus struct {
iRemote
eQ *eQ
mode *busMode
levels *levels
mode busMode
levels
}
// Mute returns the value of the Mute parameter
func (b *bus) Mute() bool {
// GetMute returns the value of the Mute parameter
func (b *bus) GetMute() bool {
return b.getter_bool("Mute")
}
@ -41,8 +37,18 @@ func (b *bus) SetMute(val bool) {
b.setter_bool("Mute", val)
}
// Mono returns the value of the Mute parameter
func (b *bus) Mono() bool {
// GetEq returns the value of the Eq.On parameter
func (b *bus) GetEq() bool {
return b.getter_bool("Eq.On")
}
// SetEq sets the value of the Eq.On parameter
func (b *bus) SetEq(val bool) {
b.setter_bool("Eq.On", val)
}
// GetMono returns the value of the Mute parameter
func (b *bus) GetMono() bool {
return b.getter_bool("Mono")
}
@ -51,8 +57,8 @@ func (b *bus) SetMono(val bool) {
b.setter_bool("Mono", val)
}
// Label returns the value of the MC parameter
func (b *bus) Label() string {
// GetLabel returns the value of the MC parameter
func (b *bus) GetLabel() string {
return b.getter_string("Label")
}
@ -61,223 +67,199 @@ func (b *bus) SetLabel(val string) {
b.setter_string("Label", val)
}
// Gain returns the value of the Gain parameter
func (b *bus) Gain() float64 {
// GetGain returns the value of the Gain parameter
func (b *bus) GetGain() float64 {
return b.getter_float("Gain")
}
// SetGain sets the value of the Gain parameter
func (b *bus) SetGain(val float64) {
func (b *bus) SetGain(val float32) {
b.setter_float("Gain", val)
}
// Eq returns the eQ field
func (b *bus) Eq() *eQ {
return b.eQ
}
// Mode returns address of a busMode struct
func (b *bus) Mode() *busMode {
return b.mode
func (b *bus) Mode() t_busMode {
return &b.mode
}
// Levels returns the levels field
// Levels returns the gainlayer field
func (b *bus) Levels() *levels {
return b.levels
return &b.levels
}
// FadeTo sets the value of gain to target over at time interval of time_
func (b *bus) FadeTo(target float32, time_ int) {
b.setter_string("FadeTo", fmt.Sprintf("(\"%f\", %d)", target, time_))
time.Sleep(time.Millisecond)
}
// FadeBy adjusts the value of gain by change over a time interval of time_
func (b *bus) FadeBy(change float32, time_ int) {
b.setter_string("FadeBy", fmt.Sprintf("(\"%f\", %d)", change, time_))
time.Sleep(time.Millisecond)
}
// PhysicalBus represents a single physical bus
type PhysicalBus struct {
type physicalBus struct {
bus
}
// newPhysicalBus returns a PhysicalBus type cast to an iBus
func newPhysicalBus(i int, k *kind) iBus {
e := newEq(fmt.Sprintf("bus[%d].EQ", i), i)
func newPhysicalBus(i int, k *kind) t_bus {
b := newBusMode(i)
l := newBusLevels(i, k)
pb := PhysicalBus{bus{iRemote{fmt.Sprintf("bus[%d]", i), i}, e, b, l}}
pb := physicalBus{bus{iRemote{fmt.Sprintf("bus[%d]", i), i}, b, l}}
return &pb
return t_bus(&pb)
}
// String implements the fmt.stringer interface
func (p *PhysicalBus) String() string {
func (p *physicalBus) String() string {
return fmt.Sprintf("PhysicalBus%d", p.index)
}
// VirtualBus represents a single virtual bus
type VirtualBus struct {
type virtualBus struct {
bus
}
// newVirtualBus returns a VirtualBus type cast to an iBus
func newVirtualBus(i int, k *kind) iBus {
e := newEq(fmt.Sprintf("bus[%d].EQ", i), i)
func newVirtualBus(i int, k *kind) t_bus {
b := newBusMode(i)
l := newBusLevels(i, k)
vb := VirtualBus{bus{iRemote{fmt.Sprintf("bus[%d]", i), i}, e, b, l}}
return &vb
vb := virtualBus{bus{iRemote{fmt.Sprintf("bus[%d]", i), i}, b, l}}
return t_bus(&vb)
}
// String implements the fmt.stringer interface
func (v *VirtualBus) String() string {
func (v *virtualBus) String() string {
return fmt.Sprintf("VirtualBus%d", v.index)
}
// busMode offers methods for getting/setting bus mode states
type t_busMode 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
}
type busMode struct {
iRemote
}
// newBusMode returns a busMode struct
func newBusMode(i int) *busMode {
return &busMode{iRemote{fmt.Sprintf("bus[%d].mode", i), i}}
func newBusMode(i int) busMode {
return busMode{iRemote{fmt.Sprintf("bus[%d].mode", i), i}}
}
// Normal gets the value of the Mode.Normal parameter
func (bm *busMode) Normal() bool {
return bm.getter_bool("Normal")
}
// SetNormal sets the value of the Mode.Normal parameter
func (bm *busMode) SetNormal(val bool) {
bm.setter_bool("Normal", val)
}
// Amix gets the value of the Mode.Amix parameter
func (bm *busMode) Amix() bool {
return bm.getter_bool("Amix")
func (bm *busMode) GetNormal() bool {
return bm.getter_bool("Normal")
}
// SetAmix sets the value of the Mode.Amix parameter
func (bm *busMode) SetAmix(val bool) {
bm.setter_bool("Amix", val)
}
// Bmix gets the value of the Mode.Bmix parameter
func (bm *busMode) Bmix() bool {
return bm.getter_bool("Bmix")
func (bm *busMode) GetAmix() bool {
return bm.getter_bool("Amix")
}
// SetBmix sets the value of the Mode.Bmix parameter
func (bm *busMode) SetBmix(val bool) {
bm.setter_bool("Bmix", val)
}
// Repeat gets the value of the Mode.Repeat parameter
func (bm *busMode) Repeat() bool {
return bm.getter_bool("Repeat")
func (bm *busMode) GetBmix() bool {
return bm.getter_bool("Bmix")
}
// SetRepeat sets the value of the Mode.Repeat parameter
func (bm *busMode) SetRepeat(val bool) {
bm.setter_bool("Repeat", val)
}
// Composite gets the value of the Mode.Composite parameter
func (bm *busMode) Composite() bool {
return bm.getter_bool("Composite")
func (bm *busMode) GetRepeat() bool {
return bm.getter_bool("Repeat")
}
// SetComposite sets the value of the Mode.Composite parameter
func (bm *busMode) SetComposite(val bool) {
bm.setter_bool("Composite", val)
}
// TvMix gets the value of the Mode.TvMix parameter
func (bm *busMode) TvMix() bool {
return bm.getter_bool("TvMix")
func (bm *busMode) GetComposite() bool {
return bm.getter_bool("Composite")
}
// SetTvMix sets the value of the Mode.TvMix parameter
func (bm *busMode) SetTvMix(val bool) {
bm.setter_bool("TvMix", val)
}
// UpMix21 gets the value of the Mode.UpMix21 parameter
func (bm *busMode) UpMix21() bool {
return bm.getter_bool("UpMix21")
func (bm *busMode) GetTvMix() bool {
return bm.getter_bool("TvMix")
}
// SetUpMix21 sets the value of the Mode.UpMix21 parameter
func (bm *busMode) SetUpMix21(val bool) {
bm.setter_bool("UpMix21", val)
}
// UpMix41 gets the value of the Mode.UpMix41 parameter
func (bm *busMode) UpMix41() bool {
return bm.getter_bool("UpMix41")
func (bm *busMode) GetUpMix21() bool {
return bm.getter_bool("UpMix21")
}
// SetUpMix41 sets the value of the Mode.UpMix41 parameter
func (bm *busMode) SetUpMix41(val bool) {
bm.setter_bool("UpMix41", val)
}
// UpMix61 gets the value of the Mode.UpMix61 parameter
func (bm *busMode) UpMix61() bool {
return bm.getter_bool("UpMix61")
func (bm *busMode) GetUpMix41() bool {
return bm.getter_bool("UpMix41")
}
// SetUpMix61 sets the value of the Mode.UpMix61 parameter
func (bm *busMode) SetUpMix61(val bool) {
bm.setter_bool("UpMix61", val)
}
// CenterOnly gets the value of the Mode.CenterOnly parameter
func (bm *busMode) CenterOnly() bool {
return bm.getter_bool("CenterOnly")
func (bm *busMode) GetUpMix61() bool {
return bm.getter_bool("UpMix61")
}
// SetCenterOnly sets the value of the Mode.CenterOnly parameter
func (bm *busMode) SetCenterOnly(val bool) {
bm.setter_bool("CenterOnly", val)
}
// LfeOnly gets the value of the Mode.LFE parameter
func (bm *busMode) LfeOnly() bool {
return bm.getter_bool("LfeOnly")
func (bm *busMode) GetCenterOnly() bool {
return bm.getter_bool("CenterOnly")
}
// SetLfeOnly sets the value of the Mode.LFE parameter
func (bm *busMode) SetLfeOnly(val bool) {
bm.setter_bool("LfeOnly", val)
}
// RearOnly gets the value of the Mode.RearOnly parameter
func (bm *busMode) RearOnly() bool {
return bm.getter_bool("RearOnly")
func (bm *busMode) GetLfeOnly() bool {
return bm.getter_bool("LfeOnly")
}
// SetRearOnly sets the value of the Mode.RearOnly parameter
func (bm *busMode) SetRearOnly(val bool) {
bm.setter_bool("RearOnly", val)
}
// newBusLevels represents the levels field for a channel
func newBusLevels(i int, k *kind) *levels {
init := i * 8
return &levels{iRemote{fmt.Sprintf("bus[%d]", i), i}, k, init, 8, "bus"}
func (bm *busMode) GetRearOnly() bool {
return bm.getter_bool("RearOnly")
}
// All returns the level values for a bus
func (l *levels) All() []float64 {
levels := make([]float64, l.offset)
for i := range levels {
levels[i] = convertLevel(_levelCache.busLevels[l.init+i])
func newBusLevels(i int, k *kind) levels {
init := i * 8
return levels{iRemote{fmt.Sprintf("bus[%d]", i), i}, k, init, 8, "bus"}
}
func (l *levels) All() []float32 {
var levels []float32
for i := l.init; i < l.init+l.offset; i++ {
levels = append(levels, l.convertLevel(_levelCache.busLevels[i]))
}
return levels
}

View File

@ -2,7 +2,7 @@ package voicemeeter
import "fmt"
// button represents a single macrobuttton
// custom strip type, struct forwarding channel
type button struct {
index int
}
@ -14,11 +14,7 @@ func newButton(i int) button {
// getter returns the value of a macrobutton parameter
func (m *button) getter(mode int) bool {
val, err := getMacroStatus(m.index, mode)
if err != nil {
fmt.Println(err)
}
return val == 1
return getMacroStatus(m.index, mode) == 1
}
// setter sets the value of a macrobutton parameter
@ -37,8 +33,8 @@ func (m *button) String() string {
return fmt.Sprintf("MacroButton%d", m.index)
}
// State returns the value of the State parameter
func (m *button) State() bool {
// GetState returns the value of the State parameter
func (m *button) GetState() bool {
return m.getter(1)
}
@ -47,8 +43,8 @@ func (m *button) SetState(val bool) {
m.setter(val, 1)
}
// StateOnly returns the value of the StateOnly parameter
func (m *button) StateOnly() bool {
// GetStateOnly returns the value of the StateOnly parameter
func (m *button) GetStateOnly() bool {
return m.getter(2)
}
@ -57,8 +53,8 @@ func (m *button) SetStateOnly(val bool) {
m.setter(val, 2)
}
// Trigger returns the value of the Trigger parameter
func (m *button) Trigger() bool {
// GetTrigger returns the value of the Trigger parameter
func (m *button) GetTrigger() bool {
return m.getter(2)
}

View File

@ -1,11 +1,9 @@
package voicemeeter
// command represents command (action) type parameters
type command struct {
iRemote
}
// newCommand returns a pointer to a command type
func newCommand() *command {
return &command{iRemote{"command", 0}}
}
@ -31,8 +29,9 @@ func (c *command) Restart() {
}
// Lock locks or unlocks the Voiceemeter GUI
// it accepts a boolean value
func (c *command) Lock(val bool) {
var value float64
var value float32
if val {
value = 1
} else {

View File

@ -1,7 +1,5 @@
package voicemeeter
import "fmt"
type devDesc struct {
Name, Type, Hwid string
}
@ -15,19 +13,16 @@ func newDevice() *device {
// Ins returns the total number of physical input devices
func (d *device) Ins() int {
return int(getNumDevices("in"))
return int(get_num_devices("in"))
}
// Ins returns the total number of physical input devices
func (d *device) Outs() int {
return int(getNumDevices("out"))
return int(get_num_devices("out"))
}
func (d *device) Input(i int) devDesc {
n, t_, id, err := getDeviceDescription(i, "in")
if err != nil {
fmt.Println(err)
}
n, t_, id := get_device_description(i, "in")
vals := map[uint64]string{
1: "mme",
3: "wdm",
@ -38,10 +33,7 @@ func (d *device) Input(i int) devDesc {
}
func (d *device) Output(i int) devDesc {
n, t_, id, err := getDeviceDescription(i, "out")
if err != nil {
fmt.Println(err)
}
n, t_, id := get_device_description(i, "out")
vals := map[uint64]string{
1: "mme",
3: "wdm",

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 +0,0 @@
module hotkeys
go 1.19
require (
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0
github.com/sirupsen/logrus v1.9.0
)
require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect

View File

@ -1,19 +0,0 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg=
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg=
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0 h1:AXem+OMeKDuJd2KoLpzHEU70Rx2057p4XKgiOJkXCIo=
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0/go.mod h1:ULRO0N2Wg7Ymj7CEg4TI7CJobx9yVEZ5Lbh7oWi7woA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@ -1,71 +0,0 @@
package main
import (
"fmt"
log "github.com/sirupsen/logrus"
"github.com/eiannone/keyboard"
"github.com/onyx-and-iris/voicemeeter/v2"
)
func init() {
log.SetLevel(log.InfoLevel)
}
func main() {
if err := keyboard.Open(); err != nil {
log.Fatal(err)
}
defer func() {
_ = keyboard.Close()
}()
vm, err := vmConnect()
if err != nil {
log.Fatal(err)
}
defer vm.Logout()
fmt.Println("Press ESC to quit")
Loop:
for {
char, key, err := keyboard.GetKey()
if err != nil {
panic(err)
}
switch char {
case '0':
fmt.Printf("Logged into Voicemeeter %s, version %s\n", vm.Type(), vm.Version())
case '1':
vm.Strip[0].SetMute(!vm.Strip[0].Mute())
case '2':
if vm.Strip[3].Gain() == -12.8 {
vm.Strip[3].FadeBy(-8.3, 500)
} else {
vm.Strip[3].FadeTo(-12.8, 500)
}
case '3':
vm.Strip[5].AppMute("Spotify", true)
default:
if key == keyboard.KeyEsc {
break Loop
}
}
}
}
func vmConnect() (*voicemeeter.Remote, error) {
vm, err := voicemeeter.NewRemote("potato", 0)
if err != nil {
return nil, err
}
err = vm.Login()
if err != nil {
return nil, err
}
return vm, nil
}

View File

@ -1,19 +0,0 @@
## Requirements
- [OBS Studio](https://obsproject.com/)
- [GOOBS Go Client for Websocket v5](https://github.com/andreykaipov/goobs)
## About
A simple demonstration showing how to sync OBS scene switches to Voicemeeter states. The script assumes you have connection info saved in
a config file named `config.toml` placed next to `main.go`. It also assumes you have scenes named `START` `BRB` `END` and `LIVE`.
A valid `config.toml` file might look like this:
```toml
[connection]
Host="localhost"
Port=4455
Password="mystrongpass"
```

View File

@ -1,18 +0,0 @@
module obs
go 1.18
require (
github.com/BurntSushi/toml v1.2.0
github.com/andreykaipov/goobs v0.10.0
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0
github.com/sirupsen/logrus v1.9.0
)
require (
github.com/buger/jsonparser v1.1.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
)

View File

@ -1,29 +0,0 @@
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/andreykaipov/goobs v0.10.0 h1:wa4CxbYu/NqwUmx5E4/baDqYRYEmfHwg2T23RAg3jlU=
github.com/andreykaipov/goobs v0.10.0/go.mod h1:EqG73Uu/4npyhXIWWszgRelNkEeIz+d0slUT6NKWYs4=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0 h1:AXem+OMeKDuJd2KoLpzHEU70Rx2057p4XKgiOJkXCIo=
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0/go.mod h1:ULRO0N2Wg7Ymj7CEg4TI7CJobx9yVEZ5Lbh7oWi7woA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@ -1,131 +0,0 @@
package main
import (
"fmt"
"os"
"time"
log "github.com/sirupsen/logrus"
"github.com/onyx-and-iris/voicemeeter/v2"
"github.com/andreykaipov/goobs"
"github.com/andreykaipov/goobs/api/events"
"github.com/BurntSushi/toml"
)
func onStart(vm *voicemeeter.Remote) {
vm.Strip[0].SetMute(true)
vm.Strip[1].SetB1(true)
vm.Strip[2].SetB1(true)
}
func onBrb(vm *voicemeeter.Remote) {
vm.Strip[7].FadeTo(0, 500)
vm.Bus[0].SetMute(true)
}
func onLive(vm *voicemeeter.Remote) {
vm.Strip[0].SetMute(false)
vm.Strip[7].FadeTo(-6, 500)
vm.Strip[7].SetA3(true)
vm.Vban.InStream[0].SetOn(true)
}
func onEnd(vm *voicemeeter.Remote) {
vm.Strip[0].SetMute(true)
vm.Strip[1].SetMute(true)
vm.Strip[1].SetB1(false)
vm.Strip[2].SetMute(true)
vm.Strip[2].SetB1(false)
vm.Vban.InStream[0].SetOn(false)
}
func init() {
log.SetLevel(log.InfoLevel)
}
func main() {
vm, err := vmConnect()
if err != nil {
log.Fatal(err)
}
defer vm.Logout()
obs, err := obsConnect()
if err != nil {
log.Fatal(err)
}
defer obs.Disconnect()
version, _ := obs.General.GetVersion()
fmt.Printf("OBS Studio version: %s\n", version.ObsVersion)
fmt.Printf("Websocket server version: %s\n", version.ObsWebSocketVersion)
go obs.Listen(func(event any) {
switch e := event.(type) {
case *events.CurrentProgramSceneChanged:
fmt.Printf("Switched to scene %s\n", e.SceneName)
switch e.SceneName {
case "START":
onStart(vm)
case "BRB":
onBrb(vm)
case "LIVE":
onLive(vm)
case "END":
onEnd(vm)
}
}
})
time.Sleep(30 * time.Second)
}
func vmConnect() (*voicemeeter.Remote, error) {
vm, err := voicemeeter.NewRemote("potato", 0)
if err != nil {
return nil, err
}
err = vm.Login()
if err != nil {
return nil, err
}
return vm, nil
}
func obsConnect() (*goobs.Client, error) {
type (
connection struct {
Host string
Port int
Password string
}
config struct {
Connection map[string]connection
}
)
f := "config.toml"
if _, err := os.Stat(f); err != nil {
err := fmt.Errorf("unable to locate %s", f)
return nil, err
}
var c config
_, err := toml.DecodeFile(f, &c.Connection)
if err != nil {
return nil, err
}
conn := c.Connection["connection"]
obs, err := goobs.New(fmt.Sprintf("%s:%d", conn.Host, conn.Port), goobs.WithPassword(conn.Password))
if err != nil {
return nil, err
}
return obs, nil
}

View File

@ -1,84 +0,0 @@
package main
import (
"fmt"
"time"
log "github.com/sirupsen/logrus"
"github.com/onyx-and-iris/voicemeeter/v2"
)
// observer represents a single receiver of updates
type observer struct {
vm *voicemeeter.Remote
events chan string
}
// newObserver returns an observer type
func newObserver(vm *voicemeeter.Remote) *observer {
return &observer{vm, make(chan string)}
}
// Listen registers the observer channel and listens for updates.
func (o observer) Listen() {
o.vm.Register(o.events)
for s := range o.events {
switch s {
case "pdirty", "mdirty":
fmt.Println(s)
case "midi":
var current = o.vm.Midi.Current()
var val = o.vm.Midi.Get(current)
fmt.Printf("Value of midi button %d: %d\n", current, val)
case "ldirty":
for _, bus := range o.vm.Bus {
if bus.Levels().IsDirty() {
fmt.Println(bus, bus.Levels().All())
}
}
}
}
}
func init() {
log.SetLevel(log.InfoLevel)
}
// runObserver initiates a single observer and
// starts its Listen() function in a goroutine.
func runObserver(vm *voicemeeter.Remote) {
o := newObserver(vm)
go o.Listen()
}
// main connects to Voiceemeter and runs a single observer for 30 seconds.
func main() {
vm, err := vmConnect()
if err != nil {
log.Fatal(err)
}
defer vm.Logout()
runObserver(vm)
time.Sleep(time.Duration(30) * time.Second)
}
// vmConnect connects to Voicemeeter potato and logs into the API
// it also add ldirty to event updates.
func vmConnect() (*voicemeeter.Remote, error) {
vm, err := voicemeeter.NewRemote("basic", 0)
if err != nil {
return nil, err
}
err = vm.Login()
if err != nil {
return nil, err
}
vm.EventAdd("ldirty")
return vm, nil
}

View File

@ -0,0 +1,46 @@
package main
import (
"fmt"
"strings"
"time"
"github.com/onyx-and-iris/voicemeeter-api-go"
)
type observer struct {
vm *voicemeeter.Remote
}
func (o observer) OnUpdate(subject string) {
if strings.Compare(subject, "pdirty") == 0 {
fmt.Println("pdirty!")
}
if strings.Compare(subject, "mdirty") == 0 {
fmt.Println("mdirty!")
}
if strings.Compare(subject, "ldirty") == 0 {
fmt.Printf("%v %v %v %v %v %v %v %v\n",
o.vm.Bus[0].Levels().IsDirty(),
o.vm.Bus[1].Levels().IsDirty(),
o.vm.Bus[2].Levels().IsDirty(),
o.vm.Bus[3].Levels().IsDirty(),
o.vm.Bus[4].Levels().IsDirty(),
o.vm.Bus[5].Levels().IsDirty(),
o.vm.Bus[6].Levels().IsDirty(),
o.vm.Bus[7].Levels().IsDirty(),
)
}
}
func main() {
vmRem := voicemeeter.GetRemote("potato")
vmRem.Login()
o := observer{vmRem}
vmRem.Register(o)
time.Sleep(30 * time.Second)
vmRem.Deregister(o)
vmRem.Logout()
}

View File

@ -1,39 +0,0 @@
## About
A Voicemeeter CLI, offers ability to toggle, get and set parameters.
## Install
First build and install it with `go install` (skip this step if using binary from [Releases](https://github.com/onyx-and-iris/voicemeeter/releases))
## Use
Commands that begin with `!` will toggle a parameter, commands that contain `=` will set a parameter, all other commands will get a value.
You may pass the following optional flags:
- -h: Print the help message
- -i: Enable interactive mode
- -k: The kind of Voicemeeter GUI to launch, defaults to Banana
- -l: Log level (0 up to 6), defaults to 3, Warn Level
- -d: Set the delay between commands, defaults to 20ms
- -v: Enable extra console output (toggle and set messages).
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`
Expected output:
```
time="<timestamp>" level=info msg="Logged into Voicemeeter Potato v3.1.1.1"
Setting strip[0].mute=0
strip[0].mute: 0.00
Toggling strip[0].mute
strip[0].mute: 1.00
Setting bus[0].gain=-8.8
Setting command.lock=1
time="<timestamp>" level=info msg="Logged out of Voicemeeter Potato"
```
If running in interactive mode enter `Q`, to exit.

View File

@ -1,213 +0,0 @@
package main
import (
"bufio"
"flag"
"fmt"
"os"
"strings"
log "github.com/sirupsen/logrus"
"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() {
var (
kind string
delay int
interactive bool
loglevel int
help bool
)
flag.Usage = usage
flag.BoolVar(&help, "help", false, "print the help message")
flag.BoolVar(&help, "h", false, "print the help message (shorthand)")
flag.StringVar(&kind, "kind", "banana", "kind of voicemeeter")
flag.StringVar(&kind, "k", "banana", "kind of voicemeeter (shorthand)")
flag.IntVar(&delay, "delay", 20, "delay between commands")
flag.IntVar(&delay, "d", 20, "delay between commands (shorthand)")
flag.BoolVar(&interactive, "interactive", false, "toggle interactive mode")
flag.BoolVar(&interactive, "i", false, "toggle interactive mode (shorthand)")
flag.IntVar(&loglevel, "loglevel", int(log.WarnLevel), "set the log level")
flag.IntVar(&loglevel, "l", int(log.WarnLevel), "set the log level (shorthand)")
flag.BoolVar(&vPrinter.verbose, "verbose", false, "enable extra console output (toggle and set messages)")
flag.BoolVar(&vPrinter.verbose, "v", false, "enable extra console output (toggle and set messages) (shorthand)")
flag.Parse()
if help {
flag.Usage()
return
}
if loglevel >= int(log.PanicLevel) && loglevel <= int(log.TraceLevel) {
log.SetLevel(log.Level(loglevel))
}
vm, err := vmConnect(kind, delay)
if err != nil {
log.Fatal(err)
}
defer vm.Logout()
if interactive {
interactiveMode(vm)
return
}
args := flag.Args()
if len(args) == 0 {
flag.Usage()
return
}
for _, arg := range args {
if err := parse(vm, arg); err != nil {
log.Error(err.Error())
}
}
}
func usage() {
fmt.Println("usage: ./vm-cli.exe [-h] [-i] [-k] [-l] [-d] [-v]\n" +
"Where:\n" +
"\th: Print the help message\n" +
"\ti: Enable interactive mode\n" +
"\tk: The kind of Voicemeeter GUI to launch, defaults to Banana\n" +
"\tl: Log level 0 up to 6, (defaults to 3, Warn Level)\n" +
"\td: Set the delay between commands (defaults to 20ms)\n" +
"\tv: Enable extra console output (toggle and set messages).")
}
func vmConnect(kind string, delay int) (*voicemeeter.Remote, error) {
vm, err := voicemeeter.NewRemote(kind, delay)
if err != nil {
return nil, err
}
err = vm.Login()
if err != nil {
return nil, err
}
return vm, nil
}
func interactiveMode(vm *voicemeeter.Remote) error {
fmt.Println("Interactive mode enabled. Enter 'Q' to exit.")
scanner := bufio.NewScanner(os.Stdin)
fmt.Printf(">> ")
for scanner.Scan() {
input := scanner.Text()
if strings.ToUpper(input) == "Q" {
break
}
for _, cmd := range strings.Split(input, " ") {
if err := parse(vm, cmd); err != nil {
log.Error(err.Error())
}
}
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
}

5
go.mod
View File

@ -1,11 +1,10 @@
module github.com/onyx-and-iris/voicemeeter/v2
module github.com/onyx-and-iris/voicemeeter-api-go
go 1.18
require (
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.0
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d
)
require (

7
go.sum
View File

@ -3,16 +3,13 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I=
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,22 +0,0 @@
package voicemeeter
import (
"log"
"os"
"testing"
)
var (
vm, err = NewRemote("potato", 30)
)
func TestMain(m *testing.M) {
if err != nil {
log.Fatal(err)
}
vm.Login()
code := m.Run()
vm.Logout()
os.Exit(code)
}

View File

@ -2,18 +2,14 @@ package voicemeeter
import (
"fmt"
log "github.com/sirupsen/logrus"
)
// iRemote provides an interface between higher types and functions that
// wrap CAPI calls
// iRemote provides a set of common forwarding methods
type iRemote struct {
_identifier string
index int
}
// identifier returns a string identifier
func (ir *iRemote) identifier() string {
return ir._identifier
}
@ -21,12 +17,7 @@ func (ir *iRemote) identifier() string {
// getter_bool returns the value of a boolean parameter
func (ir *iRemote) getter_bool(p string) bool {
param := fmt.Sprintf("%s.%s", ir.identifier(), p)
log.Debug("getter_bool::", param)
val, err := getParameterFloat(param)
if err != nil {
fmt.Println(err)
}
return val == 1
return getParameterFloat(param) == 1
}
// setter_bool sets the value of a boolean parameter
@ -38,82 +29,41 @@ func (ir *iRemote) setter_bool(p string, v bool) {
} else {
value = 0
}
log.Debug("setter_bool::", param, "=", v)
err := setParameterFloat(param, float64(value))
if err != nil {
fmt.Println(err)
}
setParameterFloat(param, float32(value))
}
// getter_int returns the value of an int parameter p
func (ir *iRemote) getter_int(p string) int {
param := fmt.Sprintf("%s.%s", ir.identifier(), p)
log.Debug("getter_int::", param)
val, err := getParameterFloat(param)
if err != nil {
fmt.Println(err)
}
return int(val)
return int(getParameterFloat(param))
}
// setter_int sets the value v of an int parameter p
func (ir *iRemote) setter_int(p string, v int) {
param := fmt.Sprintf("%s.%s", ir.identifier(), p)
log.Debug("setter_int::", param, "=", v)
err := setParameterFloat(param, float64(v))
if err != nil {
fmt.Println(err)
}
setParameterFloat(param, float32(v))
}
// getter_float returns the value of a float parameter p
// getter_float returns the value of an int parameter p
func (ir *iRemote) getter_float(p string) float64 {
var param string
if p != "" {
param = fmt.Sprintf("%s.%s", ir.identifier(), p)
} else {
param = ir.identifier()
}
log.Debug("getter_float::", param)
val, err := getParameterFloat(param)
if err != nil {
fmt.Println(err)
}
return val
param := fmt.Sprintf("%s.%s", ir.identifier(), p)
return getParameterFloat(param)
}
// setter_float sets the value v of an float parameter p
func (ir *iRemote) setter_float(p string, v float64) {
var param string
if p != "" {
param = fmt.Sprintf("%s.%s", ir.identifier(), p)
} else {
param = ir.identifier()
}
log.Debug("setter_float::", param, "=", v)
err := setParameterFloat(param, float64(v))
if err != nil {
fmt.Println(err)
}
// setter_float sets the value v of an int parameter p
func (ir *iRemote) setter_float(p string, v float32) {
param := fmt.Sprintf("%s.%s", ir.identifier(), p)
setParameterFloat(param, float32(v))
}
// getter_string returns the value of a string parameter p
func (ir *iRemote) getter_string(p string) string {
param := fmt.Sprintf("%s.%s", ir.identifier(), p)
log.Debug("getter_string::", param)
val, err := getParameterString(param)
if err != nil {
fmt.Println(err)
}
return val
return getParameterString(param)
}
// setter_string sets the value v of a string parameter p
func (ir *iRemote) setter_string(p, v string) {
param := fmt.Sprintf("%s.%s", ir.identifier(), p)
log.Debug("setter_string::", param, "=", v)
err := setParameterString(param, v)
if err != nil {
fmt.Println(err)
}
setParameterString(param, v)
}

View File

@ -9,25 +9,25 @@ var basic, banana, potato *kind
// A kind represents a Voicemeeter kinds layout
type kind struct {
Name string
PhysIn, VirtIn, PhysOut, VirtOut, VbanIn, VbanOut int
name string
physIn, virtIn, physOut, virtOut, vbanIn, vbanOut int
}
// numStrip returns the total number of strips for a kind
func (k *kind) NumStrip() int {
n := k.PhysIn + k.VirtIn
func (k *kind) numStrip() int {
n := k.physIn + k.virtIn
return n
}
// numBus returns the total number of buses for a kind
func (k *kind) NumBus() int {
n := k.PhysOut + k.VirtOut
func (k *kind) numBus() int {
n := k.physOut + k.virtOut
return n
}
// String implements the fmt.stringer interface
func (k *kind) String() string {
return fmt.Sprintf("%s%s", strings.ToUpper(k.Name[:1]), k.Name[1:])
return fmt.Sprintf("%s%s", strings.ToUpper(k.name[:1]), k.name[1:])
}
// newBasicKind returns a basic kind struct address

View File

@ -1,6 +1,8 @@
package voicemeeter
// levels represents the levels field for a channel
import "math"
// levels
type levels struct {
iRemote
k *kind
@ -9,15 +11,12 @@ type levels struct {
id string
}
// returns true if any levels value for a strip/bus have been updated
func (l *levels) IsDirty() bool {
var vals []bool
if l.id == "strip" {
vals = _levelCache.stripComp[l.init : l.init+l.offset]
} else if l.id == "bus" {
vals = _levelCache.busComp[l.init : l.init+l.offset]
func (l *levels) convertLevel(i float32) float32 {
if i > 0 {
val := 20 * math.Log10(float64(i))
return float32(roundFloat(float64(val), 1))
}
return !allTrue(vals, l.offset)
return -200.0
}
var _levelCache *levelCache
@ -25,20 +24,20 @@ var _levelCache *levelCache
// levelCache defines level slices used by the pooler to track updates
type levelCache struct {
stripMode int
stripLevels []float64
busLevels []float64
stripLevelsBuff []float64
busLevelsBuff []float64
stripLevels []float32
busLevels []float32
stripLevelsBuff []float32
busLevelsBuff []float32
stripComp []bool
busComp []bool
}
// newLevelCache returns a levelCache struct address
func newLevelCache(k *kind) *levelCache {
stripLevels := make([]float64, (2*k.PhysIn)+(8*k.VirtIn))
busLevels := make([]float64, 8*k.NumBus())
stripComp := make([]bool, (2*k.PhysIn)+(8*k.VirtIn))
busComp := make([]bool, 8*k.NumBus())
stripLevels := make([]float32, (2*k.physIn)+(8*k.virtIn))
busLevels := make([]float32, 8*k.numBus())
stripComp := make([]bool, (2*k.physIn)+(8*k.virtIn))
busComp := make([]bool, 8*k.numBus())
if _levelCache == nil {
_levelCache = &levelCache{stripMode: 0, stripLevels: stripLevels, busLevels: busLevels, stripComp: stripComp, busComp: busComp}
}

28
midi.go
View File

@ -1,28 +0,0 @@
package voicemeeter
var midi *midi_t
type midi_t struct {
channel int
current int
cache map[int]int
}
func newMidi() *midi_t {
if midi == nil {
midi = &midi_t{0, 0, map[int]int{}}
}
return midi
}
func (m *midi_t) Channel() int {
return m.channel
}
func (m *midi_t) Current() int {
return m.current
}
func (m *midi_t) Get(key int) int {
return m.cache[key]
}

View File

@ -1,39 +1,35 @@
package voicemeeter
// iOutputs defines the interface outputs type must satisfy
type iOutputs interface {
A1() bool
type t_outputs interface {
GetA1() bool
SetA1(val bool)
A2() bool
GetA2() bool
SetA2(val bool)
A3() bool
GetA3() bool
SetA3(val bool)
A4() bool
GetA4() bool
SetA4(val bool)
A5() bool
GetA5() bool
SetA5(val bool)
B1() bool
GetB1() bool
SetB1(val bool)
B2() bool
GetB2() bool
SetB2(val bool)
B3() bool
GetB3() bool
SetB3(val bool)
}
// outputs represents the outputs field (A1 - A5, B1 - B3)
// expected to be embedded
type outputs struct {
iRemote
}
// newOutputs returns an outputs type
func newOutputs(id string, i int) outputs {
o := outputs{iRemote{id, i}}
return o
}
// A1 returns the value of the A1 parameter
func (o *outputs) A1() bool {
// GetA1 returns the value of the A1 parameter
func (o *outputs) GetA1() bool {
return o.getter_bool("A1")
}
@ -42,8 +38,8 @@ func (o *outputs) SetA1(val bool) {
o.setter_bool("A1", val)
}
// A2 returns the value of the A2 parameter
func (o *outputs) A2() bool {
// GetA2 returns the value of the A2 parameter
func (o *outputs) GetA2() bool {
return o.getter_bool("A2")
}
@ -52,8 +48,8 @@ func (o *outputs) SetA2(val bool) {
o.setter_bool("A2", val)
}
// A3 returns the value of the A3 parameter
func (o *outputs) A3() bool {
// GetA3 returns the value of the A3 parameter
func (o *outputs) GetA3() bool {
return o.getter_bool("A3")
}
@ -62,8 +58,8 @@ func (o *outputs) SetA3(val bool) {
o.setter_bool("A3", val)
}
// A4 returns the value of the A4 parameter
func (o *outputs) A4() bool {
// GetA4 returns the value of the A4 parameter
func (o *outputs) GetA4() bool {
return o.getter_bool("A4")
}
@ -72,8 +68,8 @@ func (o *outputs) SetA4(val bool) {
o.setter_bool("A4", val)
}
// A5 returns the value of the A5 parameter
func (o *outputs) A5() bool {
// GetA5 returns the value of the A5 parameter
func (o *outputs) GetA5() bool {
return o.getter_bool("A5")
}
@ -82,8 +78,8 @@ func (o *outputs) SetA5(val bool) {
o.setter_bool("A5", val)
}
// B1 returns the value of the B1 parameter
func (o *outputs) B1() bool {
// GetB1 returns the value of the B1 parameter
func (o *outputs) GetB1() bool {
return o.getter_bool("B1")
}
@ -92,8 +88,8 @@ func (o *outputs) SetB1(val bool) {
o.setter_bool("B1", val)
}
// B2 returns the value of the B2 parameter
func (o *outputs) B2() bool {
// GetB2 returns the value of the B2 parameter
func (o *outputs) GetB2() bool {
return o.getter_bool("B2")
}
@ -102,8 +98,8 @@ func (o *outputs) SetB2(val bool) {
o.setter_bool("B2", val)
}
// B3 returns the value of the B3 parameter
func (o *outputs) B3() bool {
// GetB3 returns the value of the B3 parameter
func (o *outputs) GetB3() bool {
return o.getter_bool("B3")
}

View File

@ -3,7 +3,7 @@ package voicemeeter
import (
"errors"
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"strings"
@ -49,7 +49,8 @@ func dllPath() (string, error) {
func getDllPath() string {
path, err := dllPath()
if err != nil {
log.Fatal(err)
fmt.Println(err)
os.Exit(1)
}
return path
}

View File

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

View File

@ -1,12 +1,10 @@
package voicemeeter
// recorder represents the recorder
type recorder struct {
iRemote
outputs
}
// newRecorder returns an address to a recorder struct
func newRecorder() *recorder {
o := newOutputs("recorder", 0)
return &recorder{iRemote{"recorder", 0}, o}
@ -51,13 +49,3 @@ func (r *recorder) Rew() {
func (r *recorder) Loop(val bool) {
r.setter_bool("Mode.Loop", val)
}
// Gain returns the value of the Gain parameter
func (r *recorder) Gain() float64 {
return r.getter_float("Gain")
}
// SetGain sets the value of the Gain parameter
func (r *recorder) SetGain(val float64) {
r.setter_float("Gain", val)
}

301
remote.go
View File

@ -3,166 +3,73 @@ package voicemeeter
import (
"fmt"
"os"
"time"
log "github.com/sirupsen/logrus"
)
// Remote represents the API for a kind
// A Remote type represents the API for a kind
type Remote struct {
Kind *kind
Strip []iStrip
Bus []iBus
kind *kind
Strip []t_strip
Bus []t_bus
Button []button
Command *command
Vban *vban
Device *device
Recorder *recorder
Midi *midi_t
pooler *pooler
timeout int
bits int
pooler *pooler
}
// String implements the fmt.stringer interface
func (r *Remote) String() string {
return fmt.Sprintf("Voicemeeter %s", r.Kind)
return fmt.Sprintf("Voicemeeter %s", r.kind)
}
// Login logs into the API
// then it intializes the pooler
func (r *Remote) Login() error {
err := login(r.Kind.Name, r.timeout, r.bits)
if err != nil {
return err
}
r.InitPooler()
return nil
func (r *Remote) Login() {
login(r.kind.name)
r.pooler = newPooler(r.kind)
}
// Logout logs out of the API
// it also terminates the pooler
func (r *Remote) Logout() error {
func (r *Remote) Logout() {
r.pooler.run = false
err := logout(r.Kind.Name)
if err != nil {
return err
}
return nil
logout()
}
// InitPooler initiates the Pooler
func (r *Remote) InitPooler() {
r.pooler = newPooler(r.Kind)
}
// Run launches the Voicemeeter GUI for a kind.
func (r *Remote) Run(kindId string) error {
err := runVoicemeeter(kindId, r.bits)
if err != nil {
return err
}
return nil
}
// Type returns the type of Voicemeeter (basic, banana, potato)
func (r *Remote) Type() string {
val, err := getVMType()
if err != nil {
fmt.Println(err)
}
return val
return getVMType()
}
// Version returns the version of Voicemeeter as a string
func (r *Remote) Version() string {
val, err := getVersion()
if err != nil {
fmt.Println(err)
}
return val
return getVersion()
}
// Pdirty returns true iff a parameter value has changed
func (r *Remote) Pdirty() (bool, error) {
pdirty, err := pdirty()
return pdirty, err
func (r *Remote) Pdirty() bool {
return pdirty()
}
// Mdirty returns true iff a macrobutton value has changed
func (r *Remote) Mdirty() (bool, error) {
mdirty, err := mdirty()
return mdirty, err
func (r *Remote) Mdirty() bool {
return mdirty()
}
// Sync is a helper method that waits for dirty parameters to clear
func (r *Remote) Sync() {
time.Sleep(time.Duration(vmdelay) * time.Millisecond)
clear()
}
// GetFloat gets a float parameter value
func (r *Remote) GetFloat(name string) (float64, error) {
val, err := getParameterFloat(name)
if err != nil {
return 0, err
}
return val, nil
}
// SetFloat sets a float paramter value
func (r *Remote) SetFloat(name string, value float64) error {
err := setParameterFloat(name, value)
if err != nil {
return err
}
return nil
}
// GetString gets a string parameter value
func (r *Remote) GetString(name string) (string, error) {
val, err := getParameterString(name)
if err != nil {
return "", err
}
return val, nil
}
// SetString sets a string parameter value
func (r *Remote) SetString(name, value string) error {
err := setParameterString(name, value)
if err != nil {
return err
}
return nil
}
// SendText sets multiple parameters by script
func (r *Remote) SendText(script string) error {
err := setParametersMulti(script)
if err != nil {
return err
}
return nil
func (r *Remote) SendText(script string) {
setParametersMulti(script)
}
// Register forwards the register method to Pooler
func (r *Remote) Register(channel chan string) {
r.pooler.Register(channel)
func (r *Remote) Register(o observer) {
r.pooler.Register(o)
}
// EventAdd adds events to the Pooler
func (r *Remote) EventAdd(events ...string) {
r.pooler.event.Add(events...)
// Register forwards the deregister method to Pooler
func (r *Remote) Deregister(o observer) {
r.pooler.Deregister(o)
}
// EventRemove removes events from the Pooler
func (r *Remote) EventRemove(events ...string) {
r.pooler.event.Remove(events...)
}
// remoteBuilder defines the interface builder types must satisfy
type remoteBuilder interface {
setKind() remoteBuilder
makeStrip() remoteBuilder
@ -172,13 +79,11 @@ type remoteBuilder interface {
makeVban() remoteBuilder
makeDevice() remoteBuilder
makeRecorder() remoteBuilder
makeMidi() remoteBuilder
setDefaults() remoteBuilder
Build() remoteBuilder
Get() *Remote
}
// director is responsible for directing the genericBuilder
// directory is responsible for directing the genericBuilder
type director struct {
builder remoteBuilder
}
@ -198,220 +103,138 @@ func (d *director) Get() *Remote {
return d.builder.Get()
}
// genericBuilder represents a generic builder type
type genericBuilder struct {
k *kind
r Remote
}
// setKind sets the kind for a builder of a kind
func (b *genericBuilder) setKind() remoteBuilder {
b.r.Kind = b.k
b.r.kind = b.k
return b
}
// makeStrip makes a strip slice and assigns it to remote.Strip
// []iStrip comprises of both physical and virtual strip types
// []t_strip comprises of both physical and virtual strip types
func (b *genericBuilder) makeStrip() remoteBuilder {
log.Debug("building strip")
strip := make([]iStrip, b.k.NumStrip())
for i := 0; i < b.k.NumStrip(); i++ {
if i < b.k.PhysIn {
strip[i] = newPhysicalStrip(i, b.k)
fmt.Println("building strip")
_strip := make([]t_strip, b.k.numStrip())
for i := 0; i < b.k.numStrip(); i++ {
if i < b.k.physIn {
_strip[i] = newPhysicalStrip(i, b.k)
} else {
strip[i] = newVirtualStrip(i, b.k)
_strip[i] = newVirtualStrip(i, b.k)
}
}
b.r.Strip = strip
b.r.Strip = _strip
return b
}
// makeBus makes a bus slice and assigns it to remote.Bus
// []t_bus comprises of both physical and virtual bus types
func (b *genericBuilder) makeBus() remoteBuilder {
log.Debug("building bus")
bus := make([]iBus, b.k.NumBus())
for i := 0; i < b.k.NumBus(); i++ {
if i < b.k.PhysOut {
bus[i] = newPhysicalBus(i, b.k)
fmt.Println("building bus")
_bus := make([]t_bus, b.k.numBus())
for i := 0; i < b.k.numBus(); i++ {
if i < b.k.physOut {
_bus[i] = newPhysicalBus(i, b.k)
} else {
bus[i] = newVirtualBus(i, b.k)
_bus[i] = newVirtualBus(i, b.k)
}
}
b.r.Bus = bus
b.r.Bus = _bus
return b
}
// makeButton makes a button slice and assigns it to remote.Button
func (b *genericBuilder) makeButton() remoteBuilder {
log.Debug("building button")
button := make([]button, 80)
fmt.Println("building button")
_button := make([]button, 80)
for i := 0; i < 80; i++ {
button[i] = newButton(i)
_button[i] = newButton(i)
}
b.r.Button = button
b.r.Button = _button
return b
}
// makeCommand makes a command type and assigns it to remote.Command
func (b *genericBuilder) makeCommand() remoteBuilder {
log.Debug("building command")
fmt.Println("building command")
b.r.Command = newCommand()
return b
}
// makeVban makes a vban type and assigns it to remote.Vban
func (b *genericBuilder) makeVban() remoteBuilder {
log.Debug("building vban")
fmt.Println("building vban")
b.r.Vban = newVban(b.k)
return b
}
// makeDevice makes a device type and assigns it to remote.Device
func (b *genericBuilder) makeDevice() remoteBuilder {
log.Debug("building device")
fmt.Println("building device")
b.r.Device = newDevice()
return b
}
// makeRecorder makes a recorder type and assigns it to remote.Recorder
func (b *genericBuilder) makeRecorder() remoteBuilder {
log.Debug("building recorder")
fmt.Println("building recorder")
b.r.Recorder = newRecorder()
return b
}
// makeMidi makes a midi type and assigns it to remote.Midi
func (b *genericBuilder) makeMidi() remoteBuilder {
log.Debug("building midi")
b.r.Midi = newMidi()
return b
}
// setDefaults sets defaults for optional members
func (b *genericBuilder) setDefaults() remoteBuilder {
b.r.bits = 64
b.r.timeout = 2
return b
}
// Get returns a fully constructed remote type for a kind
func (b *genericBuilder) Get() *Remote {
return &b.r
}
// basicBuilder represents a builder specific to basic type
type basicBuilder struct {
genericBuilder
}
// Build defines the steps required to build a basic type
func (basb *genericBuilder) Build() remoteBuilder {
return basb.setKind().
makeStrip().
makeBus().
makeButton().
makeCommand().
makeVban().
makeDevice().
makeMidi().
setDefaults()
return basb.setKind().makeStrip().makeBus().makeButton().makeCommand().makeVban().makeDevice()
}
// bananaBuilder represents a builder specific to banana type
type bananaBuilder struct {
genericBuilder
}
// Build defines the steps required to build a banana type
func (banb *bananaBuilder) Build() remoteBuilder {
return banb.setKind().
makeStrip().
makeBus().
makeButton().
makeCommand().
makeVban().
makeDevice().
makeRecorder().
makeMidi().
setDefaults()
return banb.setKind().makeStrip().makeBus().makeButton().makeCommand().makeVban().makeDevice().makeRecorder()
}
// potatoBuilder represents a builder specific to potato type
type potatoBuilder struct {
genericBuilder
}
// Build defines the steps required to build a potato type
func (potb *potatoBuilder) Build() remoteBuilder {
return potb.setKind().
makeStrip().
makeBus().
makeButton().
makeCommand().
makeVban().
makeDevice().
makeRecorder().
makeMidi().
setDefaults()
return potb.setKind().makeStrip().makeBus().makeButton().makeCommand().makeVban().makeDevice().makeRecorder()
}
var (
vmsync bool
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
// GetRemote returns a Remote type for a kind
// this is the interface entry point
func NewRemote(kindId string, delay int, opts ...Option) (*Remote, error) {
kind, ok := kindMap[kindId]
func GetRemote(kindId string) *Remote {
_kind, ok := kindMap[kindId]
if !ok {
err := fmt.Errorf("unknown Voicemeeter kind '%s'", kindId)
return nil, err
fmt.Println(err)
os.Exit(1)
}
if delay < 0 {
err := fmt.Errorf("invalid delay value. should be >= 0")
return nil, err
}
vmsync = delay > 0
vmdelay = delay
director := director{}
switch kind.Name {
switch _kind.name {
case "basic":
director.SetBuilder(&basicBuilder{genericBuilder{kind, Remote{}}})
director.SetBuilder(&basicBuilder{genericBuilder{_kind, Remote{}}})
case "banana":
director.SetBuilder(&bananaBuilder{genericBuilder{kind, Remote{}}})
director.SetBuilder(&bananaBuilder{genericBuilder{_kind, Remote{}}})
case "potato":
director.SetBuilder(&potatoBuilder{genericBuilder{kind, Remote{}}})
director.SetBuilder(&potatoBuilder{genericBuilder{_kind, Remote{}}})
}
director.Construct()
r := director.Get()
for _, opt := range opts {
opt(r)
}
return r, nil
return director.Get()
}

View File

@ -8,7 +8,7 @@ import (
func TestGetBasicRemote(t *testing.T) {
//t.Skip("skipping test")
__rem, _ := NewRemote("basic", 0)
__rem := GetRemote("basic")
t.Run("Should return a remote basic type", func(t *testing.T) {
assert.NotNil(t, __rem)
})
@ -34,7 +34,7 @@ func TestGetBasicRemote(t *testing.T) {
func TestGetBananaRemote(t *testing.T) {
//t.Skip("skipping test")
__rem, _ := NewRemote("banana", 0)
__rem := GetRemote("banana")
t.Run("Should return a remote banana type", func(t *testing.T) {
assert.NotNil(t, __rem)
})
@ -60,7 +60,7 @@ func TestGetBananaRemote(t *testing.T) {
func TestGetPotatoRemote(t *testing.T) {
//t.Skip("skipping test")
__rem, _ := NewRemote("potato", 0)
__rem := GetRemote("potato")
t.Run("Should return a remote basic type", func(t *testing.T) {
assert.NotNil(t, __rem)
})
@ -83,25 +83,3 @@ func TestGetPotatoRemote(t *testing.T) {
assert.NotNil(t, __rem.Recorder)
})
}
func TestSetAndGetFloatParameter(t *testing.T) {
//t.Skip("skipping test")
var param = "strip[0].mute"
var exp = float64(1)
vm.SetFloat(param, 1)
t.Run("Should get a float parameter", func(t *testing.T) {
val, _ := vm.GetFloat(param)
assert.Equal(t, exp, val)
})
}
func TestSetAndGetStringParameter(t *testing.T) {
//t.Skip("skipping test")
var param = "strip[0].label"
var exp = "test0"
vm.SetString(param, exp)
t.Run("Should get a string parameter", func(t *testing.T) {
val, _ := vm.GetString(param)
assert.Equal(t, exp, val)
})
}

571
strip.go
View File

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

View File

@ -1,31 +1,26 @@
package voicemeeter_test
import (
"bytes"
"os"
"testing"
"time"
log "github.com/sirupsen/logrus"
"github.com/onyx-and-iris/voicemeeter/v2"
"github.com/onyx-and-iris/voicemeeter-api-go"
)
var (
logstring bytes.Buffer
vm, err = voicemeeter.NewRemote("potato", 30)
vmRem = voicemeeter.GetRemote("potato")
)
func TestMain(m *testing.M) {
if err != nil {
log.Fatal(err)
}
err = vm.Login()
if err != nil {
log.Fatal(err)
}
defer vm.Logout()
log.SetOutput(&logstring)
m.Run()
vmRem.Login()
code := m.Run()
vmRem.Logout()
os.Exit(code)
}
func sync() {
time.Sleep(30 * time.Millisecond)
for vmRem.Pdirty() || vmRem.Mdirty() {
}
}

View File

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

View File

@ -1,6 +1,6 @@
Function RunTests {
$run_int_tests = "go clean -testcache; go test -v ."
$run_ext_tests = "go clean -testcache; go test -v .\tests\"
$run_int_tests = "go clean -testcache; go test -v .\voicemeeter\"
Invoke-Expression $run_ext_tests
Invoke-Expression $run_int_tests

13
util.go
View File

@ -12,24 +12,13 @@ func allTrue(s []bool, sz int) bool {
return true
}
// update copies the contents of one float slice into another
func update(s1 []float64, s2 []float64, sz int) {
func update(s1 []float32, s2 []float32, sz int) {
for i := 0; i < sz; i++ {
s1[i] = s2[i]
}
}
// roundFloat rounds a float value to a given precision
func roundFloat(val float64, precision uint) float64 {
ratio := math.Pow(10, float64(precision))
return math.Round(val*ratio) / ratio
}
// convertLevel performs the necessary math for a channel level
func convertLevel(i float64) float64 {
if i > 0 {
val := 20 * math.Log10(i)
return roundFloat(val, 1)
}
return -200.0
}

View File

@ -1,41 +0,0 @@
package voicemeeter
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestAllTrue(t *testing.T) {
//t.Skip("skipping test")
s := []bool{true, true, true, true, true, true}
t.Run("Should return true", func(t *testing.T) {
assert.True(t, allTrue(s, len(s)))
})
s = []bool{true, true, true, true, false, true}
t.Run("Should return false", func(t *testing.T) {
assert.False(t, allTrue(s, len(s)))
})
}
func TestUpdate(t *testing.T) {
//t.Skip("skipping test")
s1 := []float64{3.6, 8.7, 1.8, 18.2}
s2 := make([]float64, len(s1))
update(s2, s1, len(s1))
t.Run("Should return true", func(t *testing.T) {
assert.Equal(t, s1, s2)
})
}
func TestConvertLevel(t *testing.T) {
//t.Skip("skipping test")
res := convertLevel(0.02)
t.Run("Should be equal", func(t *testing.T) {
assert.Equal(t, float64(-34), res)
})
res = convertLevel(-0.02)
t.Run("Should be equal", func(t *testing.T) {
assert.Equal(t, float64(-200), res)
})
}

144
vban.go
View File

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