30 Commits

Author SHA1 Message Date
30cacfac91 Merge pull request #9 from mdugas/dev
Adding BUS Sel and MonitorOnSel
2026-04-12 15:39:37 +01:00
Mathieu Dugas
2364ea13aa Adding BUS Select and MonitorOnSel 2026-04-12 00:31:34 -04:00
d7aa02e516 Merge pull request #8 from onyx-and-iris/dependabot/go_modules/github.com/sirupsen/logrus-1.9.1
Bump github.com/sirupsen/logrus from 1.9.0 to 1.9.1
2026-03-30 00:46:29 +01:00
dependabot[bot]
b4f0fa4449 Bump github.com/sirupsen/logrus from 1.9.0 to 1.9.1
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.9.0 to 1.9.1.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.9.0...v1.9.1)

---
updated-dependencies:
- dependency-name: github.com/sirupsen/logrus
  dependency-version: 1.9.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-29 23:46:01 +00:00
21d61d30e6 Merge pull request #7 from onyx-and-iris/dependabot/go_modules/examples/hotkeys/github.com/sirupsen/logrus-1.9.1
Bump github.com/sirupsen/logrus from 1.9.0 to 1.9.1 in /examples/hotkeys
2026-03-30 00:45:03 +01:00
dependabot[bot]
7b71086d7e Bump github.com/sirupsen/logrus from 1.9.0 to 1.9.1 in /examples/hotkeys
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.9.0 to 1.9.1.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.9.0...v1.9.1)

---
updated-dependencies:
- dependency-name: github.com/sirupsen/logrus
  dependency-version: 1.9.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-29 23:44:51 +00:00
943a921889 Merge pull request #6 from onyx-and-iris/dependabot/go_modules/examples/obs/github.com/sirupsen/logrus-1.9.1
Bump github.com/sirupsen/logrus from 1.9.0 to 1.9.1 in /examples/obs
2026-03-30 00:42:19 +01:00
dependabot[bot]
2f07a93c4e Bump github.com/sirupsen/logrus from 1.9.0 to 1.9.1 in /examples/obs
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.9.0 to 1.9.1.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.9.0...v1.9.1)

---
updated-dependencies:
- dependency-name: github.com/sirupsen/logrus
  dependency-version: 1.9.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-29 23:41:53 +00:00
dfe3cf5771 Merge pull request #5 from onyx-and-iris/dependabot/go_modules/examples/obs/github.com/buger/jsonparser-1.1.2
Bump github.com/buger/jsonparser from 1.1.1 to 1.1.2 in /examples/obs
2026-03-29 23:30:29 +01:00
dependabot[bot]
e8dc89b4f6 Bump github.com/buger/jsonparser from 1.1.1 to 1.1.2 in /examples/obs
Bumps [github.com/buger/jsonparser](https://github.com/buger/jsonparser) from 1.1.1 to 1.1.2.
- [Release notes](https://github.com/buger/jsonparser/releases)
- [Commits](https://github.com/buger/jsonparser/compare/v1.1.1...v1.1.2)

---
updated-dependencies:
- dependency-name: github.com/buger/jsonparser
  dependency-version: 1.1.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-29 22:29:51 +00:00
0bfc1e62ba define custom usage handler. 2024-07-02 10:54:27 +01:00
6211531f87 fix expected output 2024-07-02 06:57:58 +01:00
c9eaa76f62 fix expected output 2024-07-02 06:55:30 +01:00
1b9d633217 add help dialogue
add guard for toggleCmd to prevent unsafe gain changes.

use logrus for control of log levels.

add an input prompt for interactive mode
2024-07-02 06:50:28 +01:00
359c2d61b5 reword 2024-07-01 07:32:41 +01:00
e586478729 fix WithTimeout name in README
move it above WithBits
2024-07-01 07:23:40 +01:00
6512b35155 added Option function sections to README. 2024-07-01 07:20:52 +01:00
5aabd0a343 added 2.1.0 section to CHANGELOG 2024-07-01 07:20:30 +01:00
0558e8f81d login() is now tested for up to {Remote}.timeout seconds
if timeout exceeded return an error

runVoicemeeter() now promotes types to x64bit on 64-bit OS unless overridden.

Option functions WithTimeout() and WithBits() added.
2024-07-01 07:20:05 +01:00
07018d1703 upd link for documentation 2024-01-03 09:30:57 +00:00
73627ddbf1 add go.work files to gitignore 2023-12-05 11:25:53 +00:00
dd55dd8fdf Merge pull request #4 from Francisco46018/dev
Add gain to Recorder
2023-10-04 15:02:08 +01:00
FranciscoFilipe
00d0be8055 Add gain to Recorder 2023-09-09 22:57:45 +01:00
418d04a08e fix docstrings 2022-12-15 11:27:39 +00:00
8038670203 update link to docs in readme 2022-12-15 11:26:10 +00:00
3319a8c4f4 upd pkg.go.dev badge in readme 2022-12-15 01:02:24 +00:00
b777025f88 upd installation in readme 2022-12-15 00:52:29 +00:00
93d5e2db67 upd docstring 2022-12-15 00:43:19 +00:00
a23a9f8598 update docstrings in observer example 2022-12-15 00:41:57 +00:00
195ee326a0 update go.mod files 2022-12-15 00:25:55 +00:00
16 changed files with 531 additions and 371 deletions

4
.gitignore vendored
View File

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

View File

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

360
README.md
View File

@@ -1,21 +1,19 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/onyx-and-iris/voicemeeter.svg)](https://pkg.go.dev/github.com/onyx-and-iris/voicemeeter) [![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)
# A Go Wrapper for Voicemeeter API # A Go Wrapper for the Voicemeeter API
This package offers a Go interface for the Voicemeeter Remote C API.
For an outline of past/future changes refer to: [CHANGELOG](CHANGELOG.md) For an outline of past/future changes refer to: [CHANGELOG](CHANGELOG.md)
## Tested against ## Tested against
- Basic 1.0.8.8 - Basic 1.1.1.1
- Banana 2.0.6.8 - Banana 2.1.1.1
- Potato 3.0.2.8 - Potato 3.1.1.1
## Requirements ## Requirements
- [Voicemeeter](https://voicemeeter.com/) - [Voicemeeter](https://voicemeeter.com/)
- Go 1.18 or greater - Go 1.18 or greater
## Installation ## Installation
@@ -23,7 +21,7 @@ Initialize your own module then `go get`
``` ```
go mod init github.com/x/y go mod init github.com/x/y
go get github.com/onyx-and-iris/voicemeeter go get github.com/onyx-and-iris/voicemeeter/v2
``` ```
## `Use` ## `Use`
@@ -67,15 +65,15 @@ func vmConnect() (*voicemeeter.Remote, error) {
} }
``` ```
## `voicemeeter.NewRemote(<kindId>, <delay>)` ## `voicemeeter.NewRemote(<kindId>, <delay>, opts ...Option)`
### `kindId` ### `kindId`
Pass the kind of Voicemeeter as an argument. kindId may be: Pass the kind of Voicemeeter as an argument. kindId may be:
- `basic` - `basic`
- `banana` - `banana`
- `potato` - `potato`
### `delay` ### `delay`
@@ -83,6 +81,18 @@ Pass a delay in milliseconds to force the getters to wait for dirty parameters t
Useful if not listening for event updates. 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))`
## `Remote Type` ## `Remote Type`
#### `vm.Strip` #### `vm.Strip`
@@ -179,28 +189,28 @@ Use this to force dirty parameters to clear after a delay in milliseconds.
The following methods are available The following methods are available
- `Mute() bool` - `Mute() bool`
- `SetMute(val bool)` - `SetMute(val bool)`
- `Mono() bool` - `Mono() bool`
- `SetMono(val bool)` - `SetMono(val bool)`
- `Solo() bool` - `Solo() bool`
- `SetSolo(val bool)` - `SetSolo(val bool)`
- `Limit() int` - `Limit() int`
- `SetLimit(val int)` from -40 to 12 - `SetLimit(val int)` from -40 to 12
- `Label() string` - `Label() string`
- `SetLabel(val string)` - `SetLabel(val string)`
- `Gain() float64` - `Gain() float64`
- `SetGain(val float64)` from -60.0 to 12.0 - `SetGain(val float64)` from -60.0 to 12.0
- `Mc() bool` - `Mc() bool`
- `SetMc(val bool)` - `SetMc(val bool)`
- `Audibility() float64` - `Audibility() float64`
- `SetAudibility(val float64)` from 0.0 to 10.0 - `SetAudibility(val float64)` from 0.0 to 10.0
- `A1() bool - A5() bool` - `A1() bool - A5() bool`
- `SetA1(val bool) - SetA5(val bool)` - `SetA1(val bool) - SetA5(val bool)`
- `B1() bool - B3() bool` - `B1() bool - B3() bool`
- `SetB1(val bool) bool - SetB3(val bool) bool` - `SetB1(val bool) bool - SetB3(val bool) bool`
- `AppGain(name string, gain float64)` - `AppGain(name string, gain float64)`
- `AppMute(name string, val bool)` - `AppMute(name string, val bool)`
example: example:
@@ -215,28 +225,28 @@ vm.Strip[5].AppMute("Spotify", true)
##### Comp ##### Comp
- `vm.Strip[i].Comp()` - `vm.Strip[i].Comp()`
The following methods are available The following methods are available
- `Knob() float64` - `Knob() float64`
- `SetKnob(val float64)` from 0.0 to 10.0 - `SetKnob(val float64)` from 0.0 to 10.0
- `GainIn() float64` - `GainIn() float64`
- `SetGainIn(val float64)` from -24.0 to 24.0 - `SetGainIn(val float64)` from -24.0 to 24.0
- `Ratio() float64` - `Ratio() float64`
- `SetRatio(val float64)` from 1.0 to 8.0 - `SetRatio(val float64)` from 1.0 to 8.0
- `Threshold() float64` - `Threshold() float64`
- `SetThreshold(val float64)` from -40.0 to -3.0 - `SetThreshold(val float64)` from -40.0 to -3.0
- `Attack() float64` - `Attack() float64`
- `SetAttack(val float64)` from 0.0 to 200.0 - `SetAttack(val float64)` from 0.0 to 200.0
- `Release() float64` - `Release() float64`
- `SetRelease(val float64)` from 0.0 to 5000.0 - `SetRelease(val float64)` from 0.0 to 5000.0
- `Knee() float64` - `Knee() float64`
- `SetKnee(val float64)` from 0.0 to 1.0 - `SetKnee(val float64)` from 0.0 to 1.0
- `GainOut() float64` - `GainOut() float64`
- `SetGainOut(val float64)` from -24.0 to 24.0 - `SetGainOut(val float64)` from -24.0 to 24.0
- `MakeUp() bool` - `MakeUp() bool`
- `SetMakeUp(val bool)` - `SetMakeUp(val bool)`
example: example:
@@ -246,24 +256,24 @@ vm.Strip[3].Comp().SetRatio(3.5)
##### Gate ##### Gate
- `vm.Strip[i].Gate()` - `vm.Strip[i].Gate()`
The following methods are available The following methods are available
- `Knob() float64` - `Knob() float64`
- `SetKnob(val float64)` from 0.0 to 10.0 - `SetKnob(val float64)` from 0.0 to 10.0
- `Threshold() float64` - `Threshold() float64`
- `SetThreshold(val float64)` from -60.0 to -10.0 - `SetThreshold(val float64)` from -60.0 to -10.0
- `Damping() float64` - `Damping() float64`
- `SetDamping(val float64)` from -60.0 to -10.0 - `SetDamping(val float64)` from -60.0 to -10.0
- `BPSidechain() int` - `BPSidechain() int`
- `SetBPSidechain(val int)` from 100 to 4000 - `SetBPSidechain(val int)` from 100 to 4000
- `Attack() float64` - `Attack() float64`
- `SetAttack(val float64)` from 0.0 to 1000.0 - `SetAttack(val float64)` from 0.0 to 1000.0
- `Hold() float64` - `Hold() float64`
- `SetHold(val float64)` from 0.0 to 5000.0 - `SetHold(val float64)` from 0.0 to 5000.0
- `Release() float64` - `Release() float64`
- `SetRelease(val float64)` from 0.0 to 5000.0 - `SetRelease(val float64)` from 0.0 to 5000.0
example: example:
@@ -273,12 +283,12 @@ fmt.Println(vm.Strip[4].Gate().Attack())
##### Denoiser ##### Denoiser
- `vm.Strip[i].Denoiser()` - `vm.Strip[i].Denoiser()`
The following methods are available The following methods are available
- `Knob() float64` - `Knob() float64`
- `SetKnob(val float64)` from 0.0 to 10.0 - `SetKnob(val float64)` from 0.0 to 10.0
example: example:
@@ -288,12 +298,12 @@ vm.Strip[1].Denoiser().SetKnob(4.2)
##### Gainlayer ##### Gainlayer
- `vm.Strip[i].Gainlayer()[j]` - `vm.Strip[i].Gainlayer()[j]`
The following methods are available The following methods are available
- `Get() float64` - `Get() float64`
- `Set(val float64)` - `Set(val float64)`
example: example:
@@ -303,13 +313,13 @@ vm.Strip[6].GainLayer()[3].Set(-13.6)
##### Levels ##### Levels
- `vm.Strip[i].Levels()` - `vm.Strip[i].Levels()`
The following methods are available The following methods are available
- `PreFader() []float64` - `PreFader() []float64`
- `PostFader() []float64` - `PostFader() []float64`
- `PostMute() []float64` - `PostMute() []float64`
example: example:
@@ -321,15 +331,19 @@ fmt.Println(vm.Strip[5].Levels().PreFader())
The following methods are available The following methods are available
- `String() string` - `String() string`
- `Mute() bool` - `Mute() bool`
- `SetMute(val bool)` - `SetMute(val bool)`
- `Mono() bool` - `Mono() bool`
- `SetMono(val bool)` - `SetMono(val bool)`
- `Label() string` - `Sel() bool`
- `SetLabel(val string)` - `SetSel(val bool)`
- `Gain() float64` - `Monitor() bool`
- `SetGain(val float64)` from -60.0 to 12.0 - `SetMonitor(val bool)`
- `Label() string`
- `SetLabel(val string)`
- `Gain() float64`
- `SetGain(val float64)` from -60.0 to 12.0
example: example:
@@ -340,34 +354,34 @@ fmt.Println(vm.Bus[0].Label())
##### Modes ##### Modes
- `vm.Bus[i].Mode()` - `vm.Bus[i].Mode()`
The following methods are available The following methods are available
- `SetNormal(val bool)` - `SetNormal(val bool)`
- `Normal() bool` - `Normal() bool`
- `SetAmix(val bool)` - `SetAmix(val bool)`
- `Amix() bool` - `Amix() bool`
- `SetBmix(val bool)` - `SetBmix(val bool)`
- `Bmix() bool` - `Bmix() bool`
- `SetRepeat(val bool)` - `SetRepeat(val bool)`
- `Repeat() bool` - `Repeat() bool`
- `SetComposite(val bool)` - `SetComposite(val bool)`
- `Composite() bool` - `Composite() bool`
- `SetTvMix(val bool)` - `SetTvMix(val bool)`
- `TvMix() bool` - `TvMix() bool`
- `SetUpMix21(val bool)` - `SetUpMix21(val bool)`
- `UpMix21() bool` - `UpMix21() bool`
- `SetUpMix41(val bool)` - `SetUpMix41(val bool)`
- `UpMix41() bool` - `UpMix41() bool`
- `SetUpMix61(val bool)` - `SetUpMix61(val bool)`
- `UpMix61() bool` - `UpMix61() bool`
- `SetCenterOnly(val bool)` - `SetCenterOnly(val bool)`
- `CenterOnly() bool` - `CenterOnly() bool`
- `SetLfeOnly(val bool)` - `SetLfeOnly(val bool)`
- `LfeOnly() bool` - `LfeOnly() bool`
- `SetRearOnly(val bool)` - `SetRearOnly(val bool)`
- `RearOnly() bool` - `RearOnly() bool`
example: example:
@@ -378,11 +392,11 @@ vm.Bus[4].Mode().SetCenterOnly(true)
##### Levels ##### Levels
- `vm.Bus[i].Levels()` - `vm.Bus[i].Levels()`
The following methods are available The following methods are available
- `All() []float64` - `All() []float64`
example: example:
@@ -394,14 +408,14 @@ fmt.Println(vm.Bus[1].Levels().All())
##### EQ ##### EQ
- `vm.Strip[i].Eq()` `vm.Bus[i].Eq()` - `vm.Strip[i].Eq()` `vm.Bus[i].Eq()`
The following methods are available. The following methods are available.
- `On() bool` - `On() bool`
- `SetOn(val bool)` - `SetOn(val bool)`
- `Ab() bool` - `Ab() bool`
- `SetAb(val bool)` - `SetAb(val bool)`
example: example:
@@ -412,8 +426,8 @@ fmt.Println(vm.Bus[3].Eq().Ab())
The following methods are available. The following methods are available.
- `FadeTo(target float64, time_ int)`: float, int - `FadeTo(target float64, time_ int)`: float, int
- `FadeBy(change float64, time_ int)`: float, int - `FadeBy(change float64, time_ int)`: float, int
Modify gain to or by the selected amount in db over a time interval in ms. Modify gain to or by the selected amount in db over a time interval in ms.
@@ -428,12 +442,12 @@ vm.Bus[3].FadeTo(-12.8, 500)
The following methods are available The following methods are available
- `State() bool` - `State() bool`
- `SetState(val bool)` - `SetState(val bool)`
- `StateOnly() bool` - `StateOnly() bool`
- `SetStateOnly(val bool)` - `SetStateOnly(val bool)`
- `Trigger() bool` - `Trigger() bool`
- `SetTrigger(val bool)` - `SetTrigger(val bool)`
example: example:
@@ -446,11 +460,11 @@ fmt.Println(vm.Button[64].StateOnly())
The following methods are available The following methods are available
- `Show()` Show Voicemeeter GUI if it's hidden - `Show()` Show Voicemeeter GUI if it's hidden
- `Hide()` Hide Voicemeeter GUI if it's shown - `Hide()` Hide Voicemeeter GUI if it's shown
- `Shutdown()` Shuts down the GUI - `Shutdown()` Shuts down the GUI
- `Restart()` Restart the audio engine - `Restart()` Restart the audio engine
- `Lock(val bool)` Lock the Voicemeeter GUI - `Lock(val bool)` Lock the Voicemeeter GUI
example: example:
@@ -461,32 +475,32 @@ vm.Command.Show()
### VBAN ### VBAN
- `vm.Vban.Enable()` `vm.Vban.Disable()` Turn VBAN on or off - `vm.Vban.Enable()` `vm.Vban.Disable()` Turn VBAN on or off
##### Instream | Outstream ##### Instream | Outstream
- `vm.Vban.InStream[i]` `vm.Vban.OutStream[i]` - `vm.Vban.InStream[i]` `vm.Vban.OutStream[i]`
The following methods are available The following methods are available
- `On() bool` - `On() bool`
- `SetOn(val bool)` - `SetOn(val bool)`
- `Name() string` - `Name() string`
- `SetName(val string)` - `SetName(val string)`
- `Ip() string` - `Ip() string`
- `SetIp(val string)` - `SetIp(val string)`
- `Port() int` - `Port() int`
- `SetPort(val int)` from 1024 to 65535 - `SetPort(val int)` from 1024 to 65535
- `Sr() int` - `Sr() int`
- `SetSr(val int)` (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000) - `SetSr(val int)` (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000)
- `Channel() int` - `Channel() int`
- `SetChannel(val int)` from 1 to 8 - `SetChannel(val int)` from 1 to 8
- `Bit() int` - `Bit() int`
- `SetBit(val int)` 16 or 24 - `SetBit(val int)` 16 or 24
- `Quality() int` - `Quality() int`
- `SetQuality(val int)` from 0 to 4 - `SetQuality(val int)` from 0 to 4
- `Route() int` - `Route() int`
- `SetRoute(val int)` from 0 to 8 - `SetRoute(val int)` from 0 to 8
example: example:
@@ -505,10 +519,10 @@ vm.Vban.OutStream[3].SetBit(24)
The following methods are available The following methods are available
- `Ins()` - `Ins()`
- `Outs()` - `Outs()`
- `Input(val int)` - `Input(val int)`
- `Output(val int)` - `Output(val int)`
example: example:
@@ -522,13 +536,13 @@ for i := 0; i < int(vm.Device.Ins()); i++ {
The following methods are available The following methods are available
- `Play()` - `Play()`
- `Stop()` - `Stop()`
- `Pause()` - `Pause()`
- `Replay()` - `Replay()`
- `Record()` - `Record()`
- `Ff()` - `Ff()`
- `Rew()` - `Rew()`
example: example:
@@ -547,9 +561,9 @@ vm.Recorder.SetB2(false)
The following methods are available The following methods are available
- `Channel()` returns the current midi channel - `Channel()` returns the current midi channel
- `Current()` returns the most recently pressed midi button - `Current()` returns the most recently pressed midi button
- `Get(<button>)` returns the value in cache for the midi button - `Get(<button>)` returns the value in cache for the midi button
example: example:
@@ -562,10 +576,10 @@ var val = vm.Midi.Get(current)
By default level updates are disabled. Any event may be enabled or disabled. The following events exist: By default level updates are disabled. Any event may be enabled or disabled. The following events exist:
- `pdirty` parameter updates - `pdirty` parameter updates
- `mdirty` macrobutton updates - `mdirty` macrobutton updates
- `midi` midi updates - `midi` midi updates
- `ldirty` level updates - `ldirty` level updates
example: example:
@@ -587,4 +601,4 @@ go test ./...
### Official Documentation ### Official Documentation
- [Voicemeeter Remote C API](https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/main/VoicemeeterRemoteAPI.pdf) - [Voicemeeter Remote C API](https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/main/VoicemeeterRemoteAPI.pdf)

34
base.go
View File

@@ -2,8 +2,10 @@ package voicemeeter
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"math" "math"
"runtime"
"strings" "strings"
"syscall" "syscall"
"time" "time"
@@ -45,16 +47,29 @@ var (
// login logs into the API, // login logs into the API,
// attempts to launch Voicemeeter if it's not running, // attempts to launch Voicemeeter if it's not running,
// initializes dirty parameters. // initializes dirty parameters.
func login(kindId string) error { func login(kindId string, timeout, bits int) error {
res, _, _ := vmLogin.Call() res, _, _ := vmLogin.Call()
if res == 1 { if res == 1 {
runVoicemeeter(kindId) runVoicemeeter(kindId, bits)
time.Sleep(time.Second)
} else if res != 0 { } else if res != 0 {
err := fmt.Errorf("VBVMR_Login returned %d", res) err := fmt.Errorf("VBVMR_Login returned %d", res)
return err return err
} }
log.Info("Logged into Voicemeeter ", kindId)
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
}
}
if err != nil {
return errors.New("timeout logging into the API")
}
clear() clear()
return nil return nil
} }
@@ -68,18 +83,22 @@ func logout(kindId string) error {
err := fmt.Errorf("VBVMR_Logout returned %d", int32(res)) err := fmt.Errorf("VBVMR_Logout returned %d", int32(res))
return err return err
} }
log.Info("Logged out of Voicemeeter ", kindId) log.Infof("Logged out of Voicemeeter %s", kindMap[kindId])
return nil return nil
} }
// runVoicemeeter attempts to launch a Voicemeeter GUI of a kind. // runVoicemeeter attempts to launch a Voicemeeter GUI of a kind.
func runVoicemeeter(kindId string) error { func runVoicemeeter(kindId string, bits int) error {
vals := map[string]uint64{ vals := map[string]uint64{
"basic": 1, "basic": 1,
"banana": 2, "banana": 2,
"potato": 3, "potato": 3,
} }
res, _, _ := vmRunvm.Call(uintptr(vals[kindId])) val := vals[kindId]
if strings.Contains(runtime.GOARCH, "64") && bits == 64 {
val += 3
}
res, _, _ := vmRunvm.Call(uintptr(val))
if int32(res) != 0 { if int32(res) != 0 {
err := fmt.Errorf("VBVMR_RunVoicemeeter returned %d", int32(res)) err := fmt.Errorf("VBVMR_RunVoicemeeter returned %d", int32(res))
return err return err
@@ -93,6 +112,7 @@ func getVersion() (string, error) {
res, _, _ := vmGetvmVersion.Call(uintptr(unsafe.Pointer(&ver))) res, _, _ := vmGetvmVersion.Call(uintptr(unsafe.Pointer(&ver)))
if int32(res) != 0 { if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterVersion returned %d", int32(res)) err := fmt.Errorf("VBVMR_GetVoicemeeterVersion returned %d", int32(res))
log.Error(err.Error())
return "", err return "", err
} }
v1 := (ver & 0xFF000000) >> 24 v1 := (ver & 0xFF000000) >> 24

24
bus.go
View File

@@ -12,6 +12,10 @@ type iBus interface {
SetMute(val bool) SetMute(val bool)
Mono() bool Mono() bool
SetMono(val bool) SetMono(val bool)
Sel() bool
SetSel(val bool)
Monitor() bool
SetMonitor(val bool)
Label() string Label() string
SetLabel(val string) SetLabel(val string)
Gain() float64 Gain() float64
@@ -51,6 +55,26 @@ func (b *bus) SetMono(val bool) {
b.setter_bool("Mono", val) b.setter_bool("Mono", val)
} }
// Sel returns the value of the Sel parameter
func (b *bus) Sel() bool {
return b.getter_bool("Sel")
}
// SetSel sets the alue of the Sel parameter
func (b *bus) SetSel(val bool) {
b.setter_bool("Sel", val)
}
// Monitor returns the value of the Monitor parameter
func (b *bus) Monitor() bool {
return b.getter_bool("Monitor")
}
// SetMonitor sets the alue of the Monitor parameter
func (b *bus) SetMonitor(val bool) {
b.setter_bool("Monitor", val)
}
// Label returns the value of the MC parameter // Label returns the value of the MC parameter
func (b *bus) Label() string { func (b *bus) Label() string {
return b.getter_string("Label") return b.getter_string("Label")

View File

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

View File

@@ -3,12 +3,12 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg= github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg=
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg= github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg=
github.com/onyx-and-iris/voicemeeter v1.10.1 h1:kpPN/cTYe1JG2aLUD+/m/bMUbp7wXrqrk1fL+sV8yko= github.com/onyx-and-iris/voicemeeter/v2 v2.0.0 h1:AXem+OMeKDuJd2KoLpzHEU70Rx2057p4XKgiOJkXCIo=
github.com/onyx-and-iris/voicemeeter v1.10.1/go.mod h1:LayUoN/MWSqKXSOGQ7AcLvwoefsL+zQ9CjncLs3WqsU= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.1 h1:Ou41VVR3nMWWmTiEUnj0OlsgOSCUFgsPAOl6jRIcVtQ=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.1/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=

View File

@@ -1,16 +1,16 @@
module main module obs
go 1.18 go 1.18
require ( require (
github.com/BurntSushi/toml v1.2.0 github.com/BurntSushi/toml v1.2.0
github.com/andreykaipov/goobs v0.10.0 github.com/andreykaipov/goobs v0.10.0
github.com/onyx-and-iris/voicemeeter v1.10.1 github.com/onyx-and-iris/voicemeeter/v2 v2.0.0
github.com/sirupsen/logrus v1.9.0 github.com/sirupsen/logrus v1.9.1
) )
require ( require (
github.com/buger/jsonparser v1.1.1 // indirect github.com/buger/jsonparser v1.1.2 // indirect
github.com/gorilla/websocket v1.5.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/logutils v1.0.0 // indirect
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect

View File

@@ -2,8 +2,8 @@ github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 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 h1:wa4CxbYu/NqwUmx5E4/baDqYRYEmfHwg2T23RAg3jlU=
github.com/andreykaipov/goobs v0.10.0/go.mod h1:EqG73Uu/4npyhXIWWszgRelNkEeIz+d0slUT6NKWYs4= github.com/andreykaipov/goobs v0.10.0/go.mod h1:EqG73Uu/4npyhXIWWszgRelNkEeIz+d0slUT6NKWYs4=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/buger/jsonparser v1.1.2/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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -13,12 +13,12 @@ github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
github.com/onyx-and-iris/voicemeeter v1.10.1 h1:kpPN/cTYe1JG2aLUD+/m/bMUbp7wXrqrk1fL+sV8yko= github.com/onyx-and-iris/voicemeeter/v2 v2.0.0 h1:AXem+OMeKDuJd2KoLpzHEU70Rx2057p4XKgiOJkXCIo=
github.com/onyx-and-iris/voicemeeter v1.10.1/go.mod h1:LayUoN/MWSqKXSOGQ7AcLvwoefsL+zQ9CjncLs3WqsU= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.1 h1:Ou41VVR3nMWWmTiEUnj0OlsgOSCUFgsPAOl6jRIcVtQ=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.1/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=

View File

@@ -20,8 +20,7 @@ func newObserver(vm *voicemeeter.Remote) *observer {
return &observer{vm, make(chan string)} return &observer{vm, make(chan string)}
} }
// OnUpdate satisfies the observer interface defined in publisher.go // Listen registers the observer channel and listens for updates.
// for each event type an action is triggered when the event occurs.
func (o observer) Listen() { func (o observer) Listen() {
o.vm.Register(o.events) o.vm.Register(o.events)
@@ -47,13 +46,14 @@ func init() {
log.SetLevel(log.InfoLevel) log.SetLevel(log.InfoLevel)
} }
// runObserver initiates a single observer and
// starts its Listen() function in a goroutine.
func runObserver(vm *voicemeeter.Remote) { func runObserver(vm *voicemeeter.Remote) {
o := newObserver(vm) o := newObserver(vm)
go o.Listen() go o.Listen()
} }
// main connects to Voiceemeter, registers observer for updates // main connects to Voiceemeter and runs a single observer for 30 seconds.
// runs updates for 30 seconds and then deregisters observer.
func main() { func main() {
vm, err := vmConnect() vm, err := vmConnect()
if err != nil { if err != nil {
@@ -67,6 +67,7 @@ func main() {
} }
// vmConnect connects to Voicemeeter potato and logs into the API // vmConnect connects to Voicemeeter potato and logs into the API
// it also add ldirty to event updates.
func vmConnect() (*voicemeeter.Remote, error) { func vmConnect() (*voicemeeter.Remote, error) {
vm, err := voicemeeter.NewRemote("basic", 0) vm, err := voicemeeter.NewRemote("basic", 0)
if err != nil { if err != nil {

View File

@@ -1,6 +1,6 @@
## About ## About
A simple voicemeeter-cli program. Offers ability to toggle, get and set parameters. A Voicemeeter CLI, offers ability to toggle, get and set parameters.
## Install ## Install
@@ -8,28 +8,32 @@ First build and install it with `go install` (skip this step if using binary fro
## Use ## Use
Toggle with `!` prefix, get by excluding `=` and set by including `=`. Mix and match arguments. 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: You may pass the following optional flags:
- -v: (-verbose) to toggle console output. - -h: Print the help message
- -i: (-interactive) to toggle interactive mode. - -i: Enable interactive mode
- -k: (-kind) to set the kind of Voicemeeter. Defaults to banana. - -k: The kind of Voicemeeter GUI to launch, defaults to Banana
- -d: (-delay) to set a delay on the getters. Defaults to 20ms. - -l: Log level (0 up to 6), defaults to 3, Warn Level
- -d: Set the delay between commands, defaults to 20ms
- -v: Enable extra console output (toggle and set messages).
for example: for example:
`vm-cli.exe -v -k=potato -d=25 strip[0].mute=0 strip[0].mute !strip[0].mute strip[0].mute bus[0].gain=-8.8 command.lock=1` `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: Expected output:
``` ```
Running command strip[0].mute=0 time="<timestamp>" level=info msg="Logged into Voicemeeter Potato v3.1.1.1"
Value of strip[0].mute is: 0 Setting strip[0].mute=0
strip[0].mute: 0.00
Toggling strip[0].mute Toggling strip[0].mute
Value of strip[0].mute is: 1 strip[0].mute: 1.00
Running command bus[0].gain=-8.8 Setting bus[0].gain=-8.8
Running command command.lock=1 Setting command.lock=1
time="<timestamp>" level=info msg="Logged out of Voicemeeter Potato"
``` ```
If running in interactive mode enter `q`, `quit` or `<Enter>` to exit. If running in interactive mode enter `Q`, to exit.

View File

@@ -4,26 +4,36 @@ import (
"bufio" "bufio"
"flag" "flag"
"fmt" "fmt"
"log"
"os" "os"
"strings" "strings"
log "github.com/sirupsen/logrus"
"github.com/onyx-and-iris/voicemeeter/v2" "github.com/onyx-and-iris/voicemeeter/v2"
) )
type ( const (
verbosePrinter struct { FLOAT = iota
verbose bool STRING
}
) )
type result struct {
kind int
stringParam string
floatParam float64
}
type verbosePrinter struct {
verbose bool
}
func newVerbosePrinter() *verbosePrinter { func newVerbosePrinter() *verbosePrinter {
return &verbosePrinter{} return &verbosePrinter{}
} }
func (v *verbosePrinter) printf(format string, a ...interface{}) { func (v *verbosePrinter) printf(format string, a ...interface{}) {
if v.verbose { if v.verbose {
fmt.Printf(format+"\n", a...) fmt.Printf(format, a...)
} }
} }
@@ -40,28 +50,68 @@ func main() {
kind string kind string
delay int delay int
interactive bool 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, "kind", "banana", "kind of voicemeeter")
flag.StringVar(&kind, "k", "banana", "kind of voicemeeter (shorthand)") flag.StringVar(&kind, "k", "banana", "kind of voicemeeter (shorthand)")
flag.IntVar(&delay, "delay", 20, "delay between commands") flag.IntVar(&delay, "delay", 20, "delay between commands")
flag.IntVar(&delay, "d", 20, "delay between commands (shorthand)") flag.IntVar(&delay, "d", 20, "delay between commands (shorthand)")
flag.BoolVar(&vPrinter.verbose, "verbose", false, "toggle console output")
flag.BoolVar(&vPrinter.verbose, "v", false, "toggle console output (shorthand)")
flag.BoolVar(&interactive, "interactive", false, "toggle interactive mode") flag.BoolVar(&interactive, "interactive", false, "toggle interactive mode")
flag.BoolVar(&interactive, "i", false, "toggle interactive mode (shorthand)") flag.BoolVar(&interactive, "i", false, "toggle interactive mode (shorthand)")
flag.IntVar(&loglevel, "loglevel", int(log.WarnLevel), "set the log level")
flag.IntVar(&loglevel, "l", int(log.WarnLevel), "set the log level (shorthand)")
flag.BoolVar(&vPrinter.verbose, "verbose", false, "enable extra console output (toggle and set messages)")
flag.BoolVar(&vPrinter.verbose, "v", false, "enable extra console output (toggle and set messages) (shorthand)")
flag.Parse() flag.Parse()
if help {
flag.Usage()
return
}
if loglevel >= int(log.PanicLevel) && loglevel <= int(log.TraceLevel) {
log.SetLevel(log.Level(loglevel))
}
vm, err := vmConnect(kind, delay) vm, err := vmConnect(kind, delay)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
defer vm.Logout() defer vm.Logout()
err = runCommands(vm, interactive) if interactive {
if err != nil { interactiveMode(vm)
fmt.Println(err) 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) { func vmConnect(kind string, delay int) (*voicemeeter.Remote, error) {
@@ -78,38 +128,23 @@ func vmConnect(kind string, delay int) (*voicemeeter.Remote, error) {
return vm, nil return vm, nil
} }
func runCommands(vm *voicemeeter.Remote, interactive bool) error {
if interactive {
return interactiveMode(vm)
}
args := flag.Args()
if len(args) == 0 {
err := fmt.Errorf("must provide some commands to run")
return err
}
for _, arg := range args {
err := parse(vm, arg)
if err != nil {
vPrinter.printf(err.Error())
}
}
return nil
}
func interactiveMode(vm *voicemeeter.Remote) error { func interactiveMode(vm *voicemeeter.Remote) error {
vPrinter.printf("running in interactive mode... waiting for input") fmt.Println("Interactive mode enabled. Enter 'Q' to exit.")
scanner := bufio.NewScanner(os.Stdin) scanner := bufio.NewScanner(os.Stdin)
fmt.Printf(">> ")
for scanner.Scan() { for scanner.Scan() {
input := scanner.Text() input := scanner.Text()
if input == "q" || input == "quit" || input == "" { if strings.ToUpper(input) == "Q" {
return nil break
} }
for _, cmd := range strings.Split(input, " ") { for _, cmd := range strings.Split(input, " ") {
err := parse(vm, cmd) if err := parse(vm, cmd); err != nil {
if err != nil { log.Error(err.Error())
vPrinter.printf(err.Error())
} }
} }
fmt.Printf(">> ")
} }
if scanner.Err() != nil { if scanner.Err() != nil {
return scanner.Err() return scanner.Err()
@@ -119,58 +154,60 @@ func interactiveMode(vm *voicemeeter.Remote) error {
func parse(vm *voicemeeter.Remote, cmd string) error { func parse(vm *voicemeeter.Remote, cmd string) error {
if cmd[0] == '!' { if cmd[0] == '!' {
err := toggleCmd(vm, cmd[1:]) if err := toggleCmd(vm, cmd[1:]); err != nil {
if err != nil { return err
}
} else if strings.Contains(cmd, "=") {
if err := setCmd(vm, cmd); err != nil {
return err return err
} }
} else { } else {
if strings.Contains(cmd, "=") { r := result{kind: FLOAT}
err := setCmd(vm, cmd) if err := getCmd(vm, cmd, &r); err != nil {
if err != nil { return err
return err }
} switch r.kind {
} else { case FLOAT:
err := getCmd(vm, cmd) fmt.Printf("%s: %.2f\n", cmd, r.floatParam)
if err != nil { case STRING:
return err fmt.Printf("%s: %s\n", cmd, r.stringParam)
}
} }
} }
return nil return nil
} }
func toggleCmd(vm *voicemeeter.Remote, cmd string) error { func toggleCmd(vm *voicemeeter.Remote, cmd string) error {
val, err := vm.GetFloat(cmd) r := result{kind: FLOAT}
if err != nil { if err := getCmd(vm, cmd, &r); err != nil {
err = fmt.Errorf("unable to toggle %s", cmd)
return err return err
} }
vm.SetFloat(cmd, 1-val) if r.kind == FLOAT && (r.floatParam == 0 || r.floatParam == 1) {
vPrinter.printf("Toggling %s", cmd) 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 return nil
} }
func setCmd(vm *voicemeeter.Remote, cmd string) error { func setCmd(vm *voicemeeter.Remote, cmd string) error {
vPrinter.printf("Running command %s", cmd) if err := vm.SendText(cmd); err != nil {
err := vm.SendText(cmd)
if err != nil {
err = fmt.Errorf("unable to set %s", cmd) err = fmt.Errorf("unable to set %s", cmd)
return err return err
} }
vPrinter.printf("Setting %s\n", cmd)
return nil
}
func getCmd(vm *voicemeeter.Remote, cmd string, r *result) error {
if val, err := vm.GetFloat(cmd); err == nil {
r.floatParam = val
} else if val, err := vm.GetString(cmd); err == nil {
r.kind = STRING
r.stringParam = val
} else {
err := fmt.Errorf("unknown parameter '%s'", cmd)
return err
}
return nil return nil
} }
func getCmd(vm *voicemeeter.Remote, cmd string) error {
valF, err := vm.GetFloat(cmd)
if err != nil {
valS, err := vm.GetString(cmd)
if err != nil {
err = fmt.Errorf("unable to get %s", cmd)
return err
}
fmt.Printf("Value of %s is: %s", cmd, valS)
} else {
fmt.Printf("Value of %s is: %v", cmd, valF)
}
return nil
}

2
go.mod
View File

@@ -3,7 +3,7 @@ module github.com/onyx-and-iris/voicemeeter/v2
go 1.18 go 1.18
require ( require (
github.com/sirupsen/logrus v1.9.0 github.com/sirupsen/logrus v1.9.1
github.com/stretchr/testify v1.8.0 github.com/stretchr/testify v1.8.0
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8
) )

4
go.sum
View File

@@ -3,8 +3,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.1 h1:Ou41VVR3nMWWmTiEUnj0OlsgOSCUFgsPAOl6jRIcVtQ=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.1/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=

View File

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

View File

@@ -20,7 +20,9 @@ type Remote struct {
Recorder *recorder Recorder *recorder
Midi *midi_t Midi *midi_t
pooler *pooler pooler *pooler
timeout int
bits int
} }
// String implements the fmt.stringer interface // String implements the fmt.stringer interface
@@ -31,7 +33,7 @@ func (r *Remote) String() string {
// Login logs into the API // Login logs into the API
// then it intializes the pooler // then it intializes the pooler
func (r *Remote) Login() error { func (r *Remote) Login() error {
err := login(r.Kind.Name) err := login(r.Kind.Name, r.timeout, r.bits)
if err != nil { if err != nil {
return err return err
} }
@@ -57,12 +59,10 @@ func (r *Remote) InitPooler() {
// Run launches the Voicemeeter GUI for a kind. // Run launches the Voicemeeter GUI for a kind.
func (r *Remote) Run(kindId string) error { func (r *Remote) Run(kindId string) error {
err := runVoicemeeter(kindId) err := runVoicemeeter(kindId, r.bits)
if err != nil { if err != nil {
return err return err
} }
time.Sleep(time.Second)
clear()
return nil return nil
} }
@@ -173,6 +173,7 @@ type remoteBuilder interface {
makeDevice() remoteBuilder makeDevice() remoteBuilder
makeRecorder() remoteBuilder makeRecorder() remoteBuilder
makeMidi() remoteBuilder makeMidi() remoteBuilder
setDefaults() remoteBuilder
Build() remoteBuilder Build() remoteBuilder
Get() *Remote Get() *Remote
} }
@@ -212,7 +213,7 @@ func (b *genericBuilder) setKind() remoteBuilder {
// makeStrip makes a strip slice and assigns it to remote.Strip // makeStrip makes a strip slice and assigns it to remote.Strip
// []iStrip comprises of both physical and virtual strip types // []iStrip comprises of both physical and virtual strip types
func (b *genericBuilder) makeStrip() remoteBuilder { func (b *genericBuilder) makeStrip() remoteBuilder {
log.Info("building strip") log.Debug("building strip")
strip := make([]iStrip, b.k.NumStrip()) strip := make([]iStrip, b.k.NumStrip())
for i := 0; i < b.k.NumStrip(); i++ { for i := 0; i < b.k.NumStrip(); i++ {
if i < b.k.PhysIn { if i < b.k.PhysIn {
@@ -228,7 +229,7 @@ func (b *genericBuilder) makeStrip() remoteBuilder {
// makeBus makes a bus slice and assigns it to remote.Bus // makeBus makes a bus slice and assigns it to remote.Bus
// []t_bus comprises of both physical and virtual bus types // []t_bus comprises of both physical and virtual bus types
func (b *genericBuilder) makeBus() remoteBuilder { func (b *genericBuilder) makeBus() remoteBuilder {
log.Info("building bus") log.Debug("building bus")
bus := make([]iBus, b.k.NumBus()) bus := make([]iBus, b.k.NumBus())
for i := 0; i < b.k.NumBus(); i++ { for i := 0; i < b.k.NumBus(); i++ {
if i < b.k.PhysOut { if i < b.k.PhysOut {
@@ -243,7 +244,7 @@ func (b *genericBuilder) makeBus() remoteBuilder {
// makeButton makes a button slice and assigns it to remote.Button // makeButton makes a button slice and assigns it to remote.Button
func (b *genericBuilder) makeButton() remoteBuilder { func (b *genericBuilder) makeButton() remoteBuilder {
log.Info("building button") log.Debug("building button")
button := make([]button, 80) button := make([]button, 80)
for i := 0; i < 80; i++ { for i := 0; i < 80; i++ {
button[i] = newButton(i) button[i] = newButton(i)
@@ -254,39 +255,46 @@ func (b *genericBuilder) makeButton() remoteBuilder {
// makeCommand makes a command type and assigns it to remote.Command // makeCommand makes a command type and assigns it to remote.Command
func (b *genericBuilder) makeCommand() remoteBuilder { func (b *genericBuilder) makeCommand() remoteBuilder {
log.Info("building command") log.Debug("building command")
b.r.Command = newCommand() b.r.Command = newCommand()
return b return b
} }
// makeVban makes a vban type and assigns it to remote.Vban // makeVban makes a vban type and assigns it to remote.Vban
func (b *genericBuilder) makeVban() remoteBuilder { func (b *genericBuilder) makeVban() remoteBuilder {
log.Info("building vban") log.Debug("building vban")
b.r.Vban = newVban(b.k) b.r.Vban = newVban(b.k)
return b return b
} }
// makeDevice makes a device type and assigns it to remote.Device // makeDevice makes a device type and assigns it to remote.Device
func (b *genericBuilder) makeDevice() remoteBuilder { func (b *genericBuilder) makeDevice() remoteBuilder {
log.Info("building device") log.Debug("building device")
b.r.Device = newDevice() b.r.Device = newDevice()
return b return b
} }
// makeRecorder makes a recorder type and assigns it to remote.Recorder // makeRecorder makes a recorder type and assigns it to remote.Recorder
func (b *genericBuilder) makeRecorder() remoteBuilder { func (b *genericBuilder) makeRecorder() remoteBuilder {
log.Info("building recorder") log.Debug("building recorder")
b.r.Recorder = newRecorder() b.r.Recorder = newRecorder()
return b return b
} }
// makeMidi makes a midi type and assigns it to remote.Midi // makeMidi makes a midi type and assigns it to remote.Midi
func (b *genericBuilder) makeMidi() remoteBuilder { func (b *genericBuilder) makeMidi() remoteBuilder {
log.Info("building midi") log.Debug("building midi")
b.r.Midi = newMidi() b.r.Midi = newMidi()
return b return b
} }
// setDefaults sets defaults for optional members
func (b *genericBuilder) setDefaults() remoteBuilder {
b.r.bits = 64
b.r.timeout = 2
return b
}
// Get returns a fully constructed remote type for a kind // Get returns a fully constructed remote type for a kind
func (b *genericBuilder) Get() *Remote { func (b *genericBuilder) Get() *Remote {
return &b.r return &b.r
@@ -306,7 +314,8 @@ func (basb *genericBuilder) Build() remoteBuilder {
makeCommand(). makeCommand().
makeVban(). makeVban().
makeDevice(). makeDevice().
makeMidi() makeMidi().
setDefaults()
} }
// bananaBuilder represents a builder specific to banana type // bananaBuilder represents a builder specific to banana type
@@ -324,7 +333,8 @@ func (banb *bananaBuilder) Build() remoteBuilder {
makeVban(). makeVban().
makeDevice(). makeDevice().
makeRecorder(). makeRecorder().
makeMidi() makeMidi().
setDefaults()
} }
// potatoBuilder represents a builder specific to potato type // potatoBuilder represents a builder specific to potato type
@@ -342,7 +352,8 @@ func (potb *potatoBuilder) Build() remoteBuilder {
makeVban(). makeVban().
makeDevice(). makeDevice().
makeRecorder(). makeRecorder().
makeMidi() makeMidi().
setDefaults()
} }
var ( var (
@@ -350,6 +361,22 @@ var (
vmdelay int vmdelay int
) )
type Option func(*Remote)
func WithTimeout(timeout int) Option {
return func(r *Remote) {
r.timeout = timeout
}
}
func WithBits(bits int) Option {
return func(r *Remote) {
if bits == 32 || bits == 64 {
r.bits = bits
}
}
}
func init() { func init() {
log.SetOutput(os.Stdout) log.SetOutput(os.Stdout)
log.SetLevel(log.WarnLevel) log.SetLevel(log.WarnLevel)
@@ -357,7 +384,7 @@ func init() {
// NewRemote returns a Remote type for a kind // NewRemote returns a Remote type for a kind
// this is the interface entry point // this is the interface entry point
func NewRemote(kindId string, delay int) (*Remote, error) { func NewRemote(kindId string, delay int, opts ...Option) (*Remote, error) {
kind, ok := kindMap[kindId] kind, ok := kindMap[kindId]
if !ok { if !ok {
err := fmt.Errorf("unknown Voicemeeter kind '%s'", kindId) err := fmt.Errorf("unknown Voicemeeter kind '%s'", kindId)
@@ -380,5 +407,11 @@ func NewRemote(kindId string, delay int) (*Remote, error) {
director.SetBuilder(&potatoBuilder{genericBuilder{kind, Remote{}}}) director.SetBuilder(&potatoBuilder{genericBuilder{kind, Remote{}}})
} }
director.Construct() director.Construct()
return director.Get(), nil r := director.Get()
for _, opt := range opts {
opt(r)
}
return r, nil
} }