Compare commits

..

108 Commits
v1.1.0 ... dev

Author SHA1 Message Date
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
01906b6810 update changelog 2022-12-15 00:00:49 +00:00
4e3597075e fix test names 2022-12-15 00:00:38 +00:00
2a9b925623 add Run() to Remote type. Launches Voicemeeter GUI
pooler no longer defined as singleton
2022-12-15 00:00:20 +00:00
68e9654502 add i to init, a little clearer to read 2022-12-14 23:58:44 +00:00
4b69952fd5 md fix in readme, reword in changelog 2022-12-09 01:17:16 +00:00
632cd9049f run o.Listen() in goroutine.
run main thread for 30 seconds
2022-12-09 00:55:18 +00:00
a846c59e72 reword in changelog 2022-12-09 00:54:53 +00:00
26765f554c refactor level methods for strip/bus types 2022-12-09 00:54:40 +00:00
134d016e10 tested against versions updated
changes to Strip[i] Comp, Gate and Denoiser added.

changes to Strip|Bus Eq added
2022-12-08 19:46:56 +00:00
18aef9e74b docstring fix 2022-12-08 19:45:18 +00:00
ca62f9ff3c docstring fix
var rename in makeStrip, makeBus
2022-12-08 19:45:02 +00:00
95963ead14 export PhysicalStrip, VirtualStrip types
export PhysicalBus, VirtualBus types
export VbanInstream, VbanOutStream types

These can now be used by consumer for type assertions.

Strip[i].Denoiser() now references a struct type.
This will be easier to extend if the api changes in future.
2022-12-08 19:44:06 +00:00
cd830abb78 Get prefix removed from getters in Button type 2022-12-08 10:56:12 +00:00
c6c6bfd2ee CHANGELOG, README updated to reflect changes. 2022-12-08 10:53:50 +00:00
7f2646ca6e tests/examples updated with v2 changes
getCmd in vm-cli always prints value.
2022-12-08 10:52:15 +00:00
c4f00a3dbd pooler now accepts channels
done channels used to clean up pooler if GUI goes down

InitPooler() added to Remote type, useful for reinitiating the Pooler.
For this reason Pooler now defined as singleton type.
2022-12-08 10:50:00 +00:00
e01efb22eb debug logging added to getters/setters in iRemote 2022-12-08 10:27:34 +00:00
62b4956279 return values for CAPI calls fixed.
Polling functions now return error types
2022-12-08 10:26:19 +00:00
4c54d924b3 Get prefix removed from getters 2022-12-08 10:24:52 +00:00
b116f04f51 Get prefix removed from getters
True for Bus, Strip, Vban and Output types.

Bus[i].Eq() and Strip[i].Eq() now return pointers to eQ structs.
This makes it easier to extend Eq types in future if desired.

Strip[i].Comp()  now return pointer to comp struct
Strip[i].Gain() now return pointer to gain struct
This is to support the new API features in Potato 3.0.2.8

Removed casting in factory function return statements.
Since types are satisfied implicitly.

eQ struct type tests added
2022-12-08 10:23:35 +00:00
onyx-and-iris
38973810d7 add variadic example in events section 2022-10-16 17:24:51 +01:00
onyx-and-iris
be6a49e3bc event Add() and Remove() now variadic.
in strip, vban, log warning then return zero value instead of panic.

update readme
2022-10-10 18:51:30 +01:00
onyx-and-iris
76ed3320d3 when parsing, notify of errors but do not exit
return scanner.err if not nil
2022-10-06 11:31:39 +01:00
onyx-and-iris
8f630d52e7 upd dep files for hotkeys, obs examples 2022-10-05 21:35:13 +01:00
onyx-and-iris
174db162c8 add verbosePrinter to vm-cli example
add info exit from interactive in README
2022-10-05 21:34:59 +01:00
onyx-and-iris
c7a265fb33 fix bug in togglecmd 2022-10-05 13:12:12 +01:00
onyx-and-iris
8bb8336599 interactive mode added to vm-cli example
toggle, get and set refactored into separate functions

flag vars moved from global  scope

-i flag added to README
2022-10-04 20:56:38 +01:00
onyx-and-iris
5b3f10c4b8 print bus level values in observer example 2022-10-04 20:18:48 +01:00
onyx-and-iris
7022c5dacf fix type error in getLevel 2022-10-04 20:18:26 +01:00
onyx-and-iris
5bb7eb9668 remove logrus from vm-cli 2022-09-29 19:04:32 +01:00
onyx-and-iris
5e5a82f198 upd -v flag description 2022-09-29 18:31:15 +01:00
onyx-and-iris
d8de82b90e add note to install in vm-cli 2022-09-29 18:27:26 +01:00
onyx-and-iris
5b4592f484 upd dep ver for obs, hotkeys examples 2022-09-29 18:25:23 +01:00
onyx-and-iris
b2dd96abc4 add install to vm-cli readme 2022-09-29 18:17:23 +01:00
onyx-and-iris
6605d6e62d set log level to info in examples
add verbose flag to vm-cli example

update readme for vm-cli example
2022-09-29 18:05:21 +01:00
onyx-and-iris
3ab5daa61c update install instructions in readme. 2022-09-29 18:04:13 +01:00
onyx-and-iris
0d06098af8 now using logrus package
logrus added to go.mod
2022-09-29 18:02:11 +01:00
onyx-and-iris
9df10d939d clean up installation in readme 2022-09-23 18:17:56 +01:00
onyx-and-iris
d82e6a39f5 fix package name in readme 2022-09-22 23:24:54 +01:00
onyx-and-iris
10d7cea523 Merge branch 'dev' of https://github.com/onyx-and-iris/voicemeeter-api-go into dev 2022-09-22 22:55:24 +01:00
onyx-and-iris
8a9b5f9a97 rename dir structure to match package name
update examples, readme
2022-09-22 22:55:08 +01:00
norm
8a611e39aa change func and var names 2022-09-21 07:30:14 +01:00
norm
ff56dd16f5 upd readme with new default delay 2022-09-19 03:05:02 +01:00
norm
440c3b8eec increae default delay to 20 2022-09-19 03:03:51 +01:00
onyx-and-iris
a5f653f569 add error message if config not found 2022-09-18 20:57:59 +01:00
onyx-and-iris
73893209a1 upd dependency ver 2022-09-18 05:53:08 +01:00
onyx-and-iris
303f1a871c md fix 2022-09-18 05:45:09 +01:00
onyx-and-iris
2c6baf20e4 vm-cli reworked.
now accepts -kind and -delay flags.

arg parsing moved into separate run_commands function.

README added to vm-cli example
2022-09-18 05:39:42 +01:00
onyx-and-iris
c05bf500ee obs example now reads conn info from toml.
vm, obs connect logic moved into separate functions.

README added for obs example.
2022-09-18 05:38:22 +01:00
onyx-and-iris
2533f1c162 refactor observer example.
newObserver factory method added
2022-09-18 05:37:10 +01:00
onyx-and-iris
21fb2ad597 refactor example in readme 2022-09-18 05:36:24 +01:00
onyx-and-iris
1284c92680 refactor hotkeys example. 2022-09-18 05:35:53 +01:00
onyx-and-iris
a87c11099c config.toml added to gitignore 2022-09-18 05:35:27 +01:00
onyx-and-iris
00f66e8f9e GetFloat, SetFloat now return error type on error
GetString, SetString now return error type on error

SendText now returns error type on error

remote_test updated.
2022-09-18 05:35:04 +01:00
onyx-and-iris
e16a51c0da Fade and App methods added to readme. 2022-09-17 03:47:54 +01:00
onyx-and-iris
8a03904ab8 add readme to vm-cli example 2022-09-17 03:21:04 +01:00
onyx-and-iris
5fb7f5e4db remove redundant files 2022-09-17 03:13:50 +01:00
onyx-and-iris
915e3a9609 add vm-cli example 2022-09-17 03:11:53 +01:00
onyx-and-iris
0a7b8e0662 fix whitespace 2022-09-14 21:17:20 +01:00
onyx-and-iris
c32e3995db add hotkeys example 2022-09-14 21:16:29 +01:00
onyx-and-iris
07218472df obs example updated 2022-09-14 16:43:04 +01:00
onyx-and-iris
db7826dab6 reword in readme 2022-09-14 16:11:34 +01:00
onyx-and-iris
32780f11e4 changelog, readme updated to reflect changes
minor bump.
2022-09-14 16:05:49 +01:00
onyx-and-iris
8ca545b1c4 NewRemote now accepts delay param
getters clear dirty params sync enabled.

examples and tests updated
2022-09-14 16:05:15 +01:00
onyx-and-iris
a4b84f289e higher methods/functions now accept/return float64
update tests

update readme, changelog
2022-09-14 00:58:05 +01:00
onyx-and-iris
67cb8509b4 Update README.md 2022-09-07 22:32:37 +01:00
onyx-and-iris
e4de1d49cb upd go.mod for obs example 2022-09-07 21:10:42 +01:00
onyx-and-iris
50d045d823 fix ver in changelog 2022-09-07 21:04:14 +01:00
onyx-and-iris
6ed4e38dae changes to error handling
readme, changelog updated to reflect changes

version bump
2022-09-07 20:59:55 +01:00
onyx-and-iris
505b5969a2 go fmt all files.
add midi event toggle to the pooler
2022-08-23 14:03:07 +01:00
onyx-and-iris
7f992a1a87 only check subject once per update 2022-08-23 03:58:49 +01:00
onyx-and-iris
fa8e9f3e76 obs example added. 2022-08-23 03:53:51 +01:00
onyx-and-iris
7a79555cb8 use log.Fatal during setup procedures.
update readme example
2022-08-23 03:35:34 +01:00
onyx-and-iris
6fabc43998 return err from NewRemote\
pass Kind.Name to logout

update examples/tests to reflect changes
2022-08-23 03:16:43 +01:00
onyx-and-iris
3fd08ff606 fix deregister in observer example 2022-08-22 22:54:38 +01:00
onyx-and-iris
7744971a10 changelog updated to reflect changes
minor version bump
2022-08-22 22:40:08 +01:00
onyx-and-iris
82bbcd06b1 tested against version updated.
Get/Set Float/String, EventAdd() + EventRemote() added to README

Midi and Events sections added to README.
2022-08-22 22:39:35 +01:00
onyx-and-iris
cba2ac85ec observer example updated with midi event.
ldirty now enabled explicitly in observer example.
2022-08-22 22:34:32 +01:00
onyx-and-iris
dd895daffb float/string getter/setter tests added
test helper added
2022-08-22 22:34:03 +01:00
onyx-and-iris
87a05d81e4 getter/setter method forwarders added to Remote
EventAdd() and EventRemote() added to Remote

method chaining in builder types split across lines.
2022-08-22 22:33:22 +01:00
onyx-and-iris
3ea4aee863 event type added for toggline evet subscriptions.
level updates now disabled by default.

each event updater runs in its own goroutine.
2022-08-22 22:29:30 +01:00
onyx-and-iris
69476ffcd9 getMidiMessage implemented
midi type added.
2022-08-22 22:28:11 +01:00
onyx-and-iris
76e6d3cba7 Remote Kind field now exported.
Kind fields/methods now exported

vmRem renamed vm in examples/tests. prefer short variable name.

minor version bump
2022-07-18 16:23:15 +01:00
onyx-and-iris
1af67bb219 Update strip.go
fix typo in docstrings
2022-07-12 17:33:50 +01:00
onyx-and-iris
14264d0818 docstrings added to remote 2022-07-10 23:35:56 +01:00
onyx-and-iris
1efac19b12 docstrings added to functions, types and methods
CHANGELOG first update

pre-commit updated to look in root of repo.

version retraction added to go.mod

README updated to reflect changes
2022-07-10 23:08:14 +01:00
onyx-and-iris
36522cf80c reword in readme 2022-07-09 19:31:28 +01:00
onyx-and-iris
119a996afd upd ver in readme 2022-07-09 19:26:45 +01:00
41 changed files with 2940 additions and 972 deletions

9
.gitignore vendored
View File

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

View File

@ -9,4 +9,171 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
## [Unreleased]
- [x]
- [x]
## [2.1.0] - 2024-07-01
### Added
- Added a configurable login timeout in seconds (defaults to 2).
- Option function added for overriding the type of Voicemeeter GUI runVoicemeeter() will launch.
- Explanation of Option functions added to README.
### Changed
- runVoicemeeter() now launches x64 GUIs for all kinds if on a 64 bit system.
- this can be overridden to force 32 bit GUI using voicemeeter.WithBits(32) Option function
## [2.0.0] - 2022-10-25
V2 introduces some breaking changes.
### Changed
- Removed Get prefix from getters in Bus, Strip, Vban, Button and Output types.
- Pooler now communicates event updates over a channel.
- strip.comp now references comp struct type. (see readme for changes in setting comp parameters)
- strip.gate now references gate struct type. (see readme for changes in setting gate parameters)
- strip.eq, bus.eq now reference eQ struct type. (see readme for changes in setting eq parameters)
- All examples and tests have been updated to reflect the changes.
### Added
- denoiser type to strip types.
- XY parameters added to strip type
- extra logging added to getters/setters in iRemote type.
- InitPooler to Remote type in case the Pooler needs reinitiating. (perhaps the GUI closed unexpectedly)
### Fixed
- Functions that wrap CAPI calls in base.go now return correct error values.
## [1.11.0] - 2022-10-10
### Fixed
- type error in getLevel
## [1.8.0] - 2022-09-17
### Added
- vm-cli example added + example README
- Fade, App methods added to project README
## [1.7.0] - 2022-09-14
### 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

589
README.md
View File

@ -1,35 +1,28 @@
[![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)
[![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
This package offers a Go interface for the Voicemeeter Remote C API.
# A Go Wrapper for the Voicemeeter API
For an outline of past/future changes refer to: [CHANGELOG](CHANGELOG.md)
## Tested against
- Basic 1.0.8.2
- Banana 2.0.6.2
- Potato 3.0.2.2
- Basic 1.1.1.1
- Banana 2.1.1.1
- Potato 3.1.1.1
## Requirements
- [Voicemeeter](https://voicemeeter.com/)
- Go 1.18 or greater
- [Voicemeeter](https://voicemeeter.com/)
- Go 1.18 or greater
## Installation
#### GO.MOD
Initialize your own module then `go get`
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`
```
go mod init github.com/x/y
go get github.com/onyx-and-iris/voicemeeter/v2
```
## `Use`
@ -40,348 +33,558 @@ package main
import (
"fmt"
"log"
"github.com/onyx-and-iris/voicemeeter-api-go"
"github.com/onyx-and-iris/voicemeeter/v2"
)
func main() {
kindId := "banana"
vmRem := voicemeeter.GetRemote(kindId)
vm, err := vmConnect()
if err != nil {
log.Fatal(err)
}
defer vm.Logout()
vmRem.Login()
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.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())
func vmConnect() (*voicemeeter.Remote, error) {
vm, err := voicemeeter.NewRemote("banana", 20)
if err != nil {
return nil, err
}
vmRem.Logout()
err = vm.Login()
if err != nil {
return nil, err
}
return vm, nil
}
```
## `kindId`
## `voicemeeter.NewRemote(<kindId>, <delay>, opts ...Option)`
### `kindId`
Pass the kind of Voicemeeter as an argument. kindId may be:
- `basic`
- `banana`
- `potato`
- `basic`
- `banana`
- `potato`
### `delay`
Pass a delay in milliseconds to force the getters to wait for dirty parameters to clear.
Useful if not listening for event updates.
### `voicemeeter.WithTimeout(timeout int)`
Set a login timeout, defaults to 2 seconds. For example to set it to 1s:
`voicemeeter.NewRemote("banana", 20, voicemeeter.WithTimeout(1))`
### `voicemeeter.WithBits(bits int)`
Override the type of Voicemeeter GUI to launch on 64 bit systems. For example, to force 32 bit GUI:
`voicemeeter.NewRemote("banana", 20, voicemeeter.WithBits(32))`
## `Remote Type`
#### `vmRem.Strip`
#### `vm.Strip`
[]t_strip slice containing both physicalStrip and virtualStrip types
#### `vmRem.Bus`
#### `vm.Bus`
[]t_bus slice containing both physicalBus and virtualBus types
#### `vmRem.Button`
#### `vm.Button`
[]button slice containing button types, one for each macrobutton
#### `vmRem.Command`
#### `vm.Command`
pointer to command type, represents action type functions
#### `vmRem.Vban`
#### `vm.Vban`
pointer to vban type, containing both vbanInStream and vbanOutStream slices
#### `vmRem.Device`
#### `vm.Device`
pointer to device type, represents physical input/output hardware devices
#### `vmRem.Recorder`
#### `vm.Recorder`
pointer to recorder type, represents the recorder
#### `vmRem.Type()`
#### `vm.Midi`
pointer to midi type, represents a connected midi device
#### `vm.Type()`
returns the type of Voicemeeter as a string
#### `vmRem.Version()`
#### `vm.Version()`
returns the version of Voicemeeter as a string
#### `vmRem.SendText(<script>)`
#### `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>)`
sets many parameters in script format eg. ("Strip[0].Mute=1;Bus[3].Gain=3.6")
#### `vmRem.Register(o observer)`
#### `vm.Register(o observer)`
register an object as an observer
register an observer type as an observer
#### `vmRem.Deregister(o observer)`
#### `vm.Deregister(o observer)`
deregister an object as an observer
deregister an observer type as an observer
#### `vmRem.Pdirty()`
#### `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()`
returns True iff a GUI parameter has changed
#### `vmRem.Mdirty()`
#### `vm.Mdirty()`
returns True iff a macrobutton paramter has changed
returns True iff a macrobutton parameter has changed
#### `vm.Sync()`
Use this to force dirty parameters to clear after a delay in milliseconds.
## `Available commands`
### Strip
The following functions are available
The following methods are available
- `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)`
- `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)`
example:
```go
vmRem.Strip[3].SetGain(3.7)
fmt.Println(vmRem.Strip[0].GetLabel())
vmRem.Strip[4].SetA1(true)
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)
```
##### Gainlayers
##### Comp
- `vmRem.Strip[i].GainLayer()[j]`
- `vm.Strip[i].Comp()`
The following functions are available
The following methods are available
- `Get() float64`
- `Set(val float32)`
- `Knob() float64`
- `SetKnob(val float64)` from 0.0 to 10.0
- `GainIn() float64`
- `SetGainIn(val float64)` from -24.0 to 24.0
- `Ratio() float64`
- `SetRatio(val float64)` from 1.0 to 8.0
- `Threshold() float64`
- `SetThreshold(val float64)` from -40.0 to -3.0
- `Attack() float64`
- `SetAttack(val float64)` from 0.0 to 200.0
- `Release() float64`
- `SetRelease(val float64)` from 0.0 to 5000.0
- `Knee() float64`
- `SetKnee(val float64)` from 0.0 to 1.0
- `GainOut() float64`
- `SetGainOut(val float64)` from -24.0 to 24.0
- `MakeUp() bool`
- `SetMakeUp(val bool)`
example:
```go
vmRem.Strip[6].GainLayer()[3].Set(-13.6)
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)
```
##### Levels
- `vmRem.Strip[i].Levels()`
- `vm.Strip[i].Levels()`
The following functions are available
The following methods are available
- `PreFader() []float32`
- `PostFader() []float32`
- `PostMute() []float32`
- `PreFader() []float64`
- `PostFader() []float64`
- `PostMute() []float64`
example:
```go
fmt.Println(vmRem.Strip[5].Levels().PreFader())
fmt.Println(vm.Strip[5].Levels().PreFader())
```
### Bus
The following functions are available
The following methods are available
- `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
- `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:
```go
vmRem.Bus[3].SetEq(true)
fmt.Println(vmRem.Bus[0].GetLabel())
vm.Bus[3].SetEq(true)
fmt.Println(vm.Bus[0].Label())
```
##### Modes
- `vmRem.Bus[i].Mode()`
- `vm.Bus[i].Mode()`
The following functions are available
The following methods are available
- `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`
- `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`
example:
```go
vmRem.Bus[3].Mode().SetAmix(true)
vmRem.Bus[4].Mode().SetCenterOnly(true)
vm.Bus[3].Mode().SetAmix(true)
vm.Bus[4].Mode().SetCenterOnly(true)
```
##### Levels
- `vmRem.Bus[i].Levels()`
- `vm.Bus[i].Levels()`
The following functions are available
The following methods are available
- `All() []float32`
- `All() []float64`
example:
```go
fmt.Println(vmRem.Bus[1].Levels().All())
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)
```
### Button
The following functions are available
The following methods are available
- `GetState() bool`
- `SetState(val bool)`
- `GetStateOnly() bool`
- `SetStateOnly(val bool)`
- `GetTrigger() bool`
- `SetTrigger(val bool)`
- `State() bool`
- `SetState(val bool)`
- `StateOnly() bool`
- `SetStateOnly(val bool)`
- `Trigger() bool`
- `SetTrigger(val bool)`
example:
```go
vmRem.Button[37].SetState(true)
fmt.Println(vmRem.Button[64].GetStateOnly())
vm.Button[37].SetState(true)
fmt.Println(vm.Button[64].StateOnly())
```
### Command
The following functions are available
The following methods are available
- `Show()` Show Voicemeeter GUI if it's hidden
- `Hide()` Hide Voicemeeter GUI if it's shown
- `Shutdown()` Shuts down the GUI
- `Restart()` Restart the audio engine
- `Lock(val bool)` Lock the Voicemeeter GUI
- `Show()` Show Voicemeeter GUI if it's hidden
- `Hide()` Hide Voicemeeter GUI if it's shown
- `Shutdown()` Shuts down the GUI
- `Restart()` Restart the audio engine
- `Lock(val bool)` Lock the Voicemeeter GUI
example:
```go
vmRem.Command.Restart()
vmRem.Command.Show()
vm.Command.Restart()
vm.Command.Show()
```
### VBAN
- `vmRem.Vban.Enable()` `vmRem.Vban.Disable()` Turn VBAN on or off
- `vm.Vban.Enable()` `vm.Vban.Disable()` Turn VBAN on or off
##### Instream | Outstream
- `vmRem.Vban.InStream` `vmRem.Vban.OutStream`
- `vm.Vban.InStream[i]` `vm.Vban.OutStream[i]`
The following functions are available
The following methods are available
- `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
- `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
example:
```go
# turn VBAN on
vmRem.Vban.Enable()
vm.Vban.Enable()
// turn on vban instream 0
vmRem.Vban.InStream[0].SetOn(true)
vm.Vban.InStream[0].SetOn(true)
// set bit property for outstream 3 to 24
vmRem.Vban.OutStream[3].SetBit(24)
vm.Vban.OutStream[3].SetBit(24)
```
### Device
The following functions are available
The following methods 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(vmRem.Device.Ins()); i++ {
fmt.Println(vmRem.Device.Input(i))
for i := 0; i < int(vm.Device.Ins()); i++ {
fmt.Println(vm.Device.Input(i))
}
```
### Recorder
The following functions are available
The following methods are available
- `Play()`
- `Stop()`
- `Pause()`
- `Replay()`
- `Record()`
- `Ff()`
- `Rew()`
- `Play()`
- `Stop()`
- `Pause()`
- `Replay()`
- `Record()`
- `Ff()`
- `Rew()`
example:
```go
vmRem.Recorder.Play()
vmRem.Recorder.Stop()
vm.Recorder.Play()
vm.Recorder.Stop()
# Enable loop play
vmRem.Recorder.Loop(true)
vm.Recorder.Loop(true)
# Disable recorder out channel B2
vmRem.Recorder.SetB2(false)
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...)
```
### Run tests
@ -394,4 +597,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,13 +2,16 @@ package voicemeeter
import (
"bytes"
"errors"
"fmt"
"math"
"os"
"runtime"
"strings"
"syscall"
"time"
"unsafe"
log "github.com/sirupsen/logrus"
)
var (
@ -37,222 +40,281 @@ 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,
// then attempts to launch Voicemeeter if it's not running.
func login(kindId string) {
// attempts to launch Voicemeeter if it's not running,
// initializes dirty parameters.
func login(kindId string, timeout, bits int) error {
res, _, _ := vmLogin.Call()
if res == 1 {
runVoicemeeter(kindId)
time.Sleep(time.Second)
runVoicemeeter(kindId, bits)
} else if res != 0 {
err := fmt.Errorf("VBVMR_Login returned %d", res)
fmt.Println(err)
os.Exit(1)
return err
}
fmt.Println("Logged into API")
for pdirty() || mdirty() {
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()
return nil
}
// logout logs out of the API,
// delayed for 100ms to allow final operation to complete.
func logout() {
func logout(kindId string) error {
time.Sleep(100 * time.Millisecond)
res, _, _ := vmLogout.Call()
if res != 0 {
err := fmt.Errorf("VBVMR_Logout returned %d", res)
fmt.Println(err)
os.Exit(1)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_Logout returned %d", int32(res))
return err
}
fmt.Println("Logged out of API")
log.Infof("Logged out of Voicemeeter %s", kindMap[kindId])
return nil
}
// runVoicemeeter attempts to launch a Voicemeeter GUI of a kind.
func runVoicemeeter(kindId string) {
func runVoicemeeter(kindId string, bits int) error {
vals := map[string]uint64{
"basic": 1,
"banana": 2,
"potato": 3,
}
res, _, _ := vmRunvm.Call(uintptr(vals[kindId]))
if res != 0 {
err := fmt.Errorf("VBVMR_RunVoicemeeter returned %d", res)
fmt.Println(err)
os.Exit(1)
val := vals[kindId]
if strings.Contains(runtime.GOARCH, "64") && bits == 64 {
val += 3
}
res, _, _ := vmRunvm.Call(uintptr(val))
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_RunVoicemeeter returned %d", int32(res))
return err
}
return nil
}
// getVersion returns the version of Voicemeeter as a string
func getVersion() string {
func getVersion() (string, error) {
var ver uint64
res, _, _ := vmGetvmVersion.Call(uintptr(unsafe.Pointer(&ver)))
if res != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterVersion returned %d", res)
fmt.Println(err)
os.Exit(1)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterVersion returned %d", int32(res))
log.Error(err.Error())
return "", err
}
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)
return fmt.Sprintf("%d.%d.%d.%d", v1, v2, v3, v4), nil
}
// pdirty returns true iff a parameter value has changed
func pdirty() bool {
func pdirty() (bool, error) {
res, _, _ := vmPdirty.Call()
return int(res) == 1
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
}
// mdirty returns true iff a macrobutton value has changed
func mdirty() bool {
func mdirty() (bool, error) {
res, _, _ := vmMdirty.Call()
return int(res) == 1
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
}
func ldirty(k *kind) bool {
_levelCache.stripLevelsBuff = make([]float32, (2*k.physIn)+(8*k.virtIn))
_levelCache.busLevelsBuff = make([]float32, 8*k.numBus())
// 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())
for i := 0; i < (2*k.physIn)+(8*k.virtIn); i++ {
_levelCache.stripLevelsBuff[i] = float32(getLevel(_levelCache.stripMode, i))
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
_levelCache.stripComp[i] = _levelCache.stripLevelsBuff[i] == _levelCache.stripLevels[i]
}
for i := 0; i < 8*k.numBus(); i++ {
_levelCache.busLevelsBuff[i] = float32(getLevel(3, 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
_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()))
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
}
}
}
// getVMType returns the type of Voicemeeter, as a string
func getVMType() string {
func getVMType() (string, error) {
var type_ uint64
res, _, _ := vmGetvmType.Call(
uintptr(unsafe.Pointer(&type_)),
)
if res != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterType returned %d", res)
fmt.Println(err)
os.Exit(1)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterType returned %d", int32(res))
return "", err
}
vals := map[uint64]string{
1: "basic",
2: "banana",
3: "potato",
}
return vals[type_]
return vals[type_], nil
}
// getParameterFloat gets the value of a float parameter
func getParameterFloat(name string) float64 {
func getParameterFloat(name string) (float64, error) {
if vmsync {
time.Sleep(time.Duration(vmdelay) * time.Millisecond)
clear()
}
var value float32
b := append([]byte(name), 0)
res, _, _ := vmGetParamFloat.Call(
uintptr(unsafe.Pointer(&b[0])),
uintptr(unsafe.Pointer(&value)),
)
if res != 0 {
err := fmt.Errorf("VBVMR_GetParameterFloat returned %d", res)
fmt.Println(err)
os.Exit(1)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetParameterFloat returned %d", int32(res))
return 0, err
}
return math.Round(float64(value)*10) / 10
return math.Round(float64(value)*10) / 10, nil
}
// setParameterFloat sets the value of a float parameter
func setParameterFloat(name string, value float32) {
func setParameterFloat(name string, value float64) error {
b1 := append([]byte(name), 0)
b2 := math.Float32bits(value)
b2 := math.Float32bits(float32(value))
res, _, _ := vmSetParamFloat.Call(
uintptr(unsafe.Pointer(&b1[0])),
uintptr(b2),
)
if res != 0 {
err := fmt.Errorf("VBVMR_SetParameterFloat returned %d", res)
fmt.Println(err)
os.Exit(1)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_SetParameterFloat returned %d", int32(res))
return err
}
return nil
}
// getParameterString gets the value of a string parameter
func getParameterString(name string) string {
func getParameterString(name string) (string, error) {
if vmsync {
time.Sleep(time.Duration(vmdelay) * time.Millisecond)
clear()
}
b1 := append([]byte(name), 0)
var b2 [512]byte
res, _, _ := vmGetParamString.Call(
uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])),
)
if res != 0 {
err := fmt.Errorf("VBVMR_GetParameterStringA returned %d", res)
fmt.Println(err)
os.Exit(1)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetParameterStringA returned %d", int32(res))
return "", err
}
str := bytes.Trim(b2[:], "\x00")
return string(str)
return string(str), nil
}
// getParameterString sets the value of a string parameter
func setParameterString(name, value string) {
// setParameterString sets the value of a string parameter
func setParameterString(name, value string) error {
b1 := append([]byte(name), 0)
b2 := append([]byte(value), 0)
res, _, _ := vmSetParamString.Call(
uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])),
)
if res != 0 {
err := fmt.Errorf("VBVMR_SetParameterStringA returned %d", res)
fmt.Println(err)
os.Exit(1)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_SetParameterStringA returned %d", int32(res))
return err
}
return nil
}
// setParametersMulti sets multiple parameters with a script
func setParametersMulti(script string) {
func setParametersMulti(script string) error {
b1 := append([]byte(script), 0)
res, _, _ := vmSetParameters.Call(
uintptr(unsafe.Pointer(&b1[0])),
)
if res != 0 {
err := fmt.Errorf("VBVMR_SetParameters returned %d", res)
fmt.Println(err)
os.Exit(1)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_SetParameters returned %d", int32(res))
return err
}
return nil
}
// getMacroStatus gets a macrobutton value
func getMacroStatus(id, mode int) float32 {
func getMacroStatus(id, mode int) (float64, error) {
if vmsync {
time.Sleep(time.Duration(vmdelay) * time.Millisecond)
clear()
}
var state float32
res, _, _ := vmGetMacroStatus.Call(
uintptr(id),
uintptr(unsafe.Pointer(&state)),
uintptr(mode),
)
if res != 0 {
err := fmt.Errorf("VBVMR_MacroButton_GetStatus returned %d", res)
fmt.Println(err)
os.Exit(1)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_MacroButton_GetStatus returned %d", int32(res))
return 0, err
}
return state
return float64(state), nil
}
// setMacroStatus sets a macrobutton value
func setMacroStatus(id, state, mode int) {
func setMacroStatus(id, state, mode int) error {
res, _, _ := vmSetMacroStatus.Call(
uintptr(id),
uintptr(state),
uintptr(mode),
)
if res != 0 {
err := fmt.Errorf("VBVMR_MacroButton_SetStatus returned %d", res)
fmt.Println(err)
os.Exit(1)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_MacroButton_SetStatus returned %d", int32(res))
return err
}
return nil
}
func get_num_devices(dir string) uint64 {
// getNumDevices returns the number of hardware input/output devices
func getNumDevices(dir string) uint64 {
if strings.Compare(dir, "in") == 0 {
res, _, _ := vmGetDevNumIn.Call()
return uint64(res)
@ -262,7 +324,8 @@ func get_num_devices(dir string) uint64 {
}
}
func get_device_description(i int, dir string) (string, uint64, string) {
// getDeviceDescription returns name, driver type and hwid for a given device
func getDeviceDescription(i int, dir string) (string, uint64, string, error) {
var t_ uint64
var b1 [512]byte
var b2 [512]byte
@ -273,10 +336,9 @@ func get_device_description(i int, dir string) (string, uint64, string) {
uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])),
)
if res != 0 {
err := fmt.Errorf("VBVMR_Input_GetDeviceDescA returned %d", res)
fmt.Println(err)
os.Exit(1)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_Input_GetDeviceDescA returned %d", int32(res))
return "", 0, "", err
}
} else {
res, _, _ := vmGetDevDescOut.Call(
@ -285,29 +347,63 @@ func get_device_description(i int, dir string) (string, uint64, string) {
uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])),
)
if res != 0 {
err := fmt.Errorf("VBVMR_Output_GetDeviceDescA returned %d", res)
fmt.Println(err)
os.Exit(1)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_Output_GetDeviceDescA returned %d", int32(res))
return "", 0, "", err
}
}
name := bytes.Trim(b1[:], "\x00")
hwid := bytes.Trim(b2[:], "\x00")
return string(name), t_, string(hwid)
return string(name), t_, string(hwid), nil
}
// getLevel returns a single level value of type type_ for channel[i]
func getLevel(type_, i int) float32 {
func getLevel(type_, i int) (float64, error) {
var val float32
res, _, _ := vmGetLevelFloat.Call(
uintptr(type_),
uintptr(i),
uintptr(unsafe.Pointer(&val)),
)
if res != 0 {
err := fmt.Errorf("VBVMR_GetLevel returned %d", res)
fmt.Println(err)
os.Exit(1)
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetLevel returned %d", int32(res))
return 0, err
}
return val
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
}

228
bus.go
View File

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

View File

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

View File

@ -1,9 +1,11 @@
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}}
}
@ -29,9 +31,8 @@ func (c *command) Restart() {
}
// Lock locks or unlocks the Voiceemeter GUI
// it accepts a boolean value
func (c *command) Lock(val bool) {
var value float32
var value float64
if val {
value = 1
} else {

View File

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

25
eq.go Normal file
View File

@ -0,0 +1,25 @@
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)
}

15
eq_test.go Normal file
View File

@ -0,0 +1,15 @@
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)
})
}

11
examples/hotkeys/go.mod Normal file
View File

@ -0,0 +1,11 @@
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

19
examples/hotkeys/go.sum Normal file
View File

@ -0,0 +1,19 @@
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=

71
examples/hotkeys/main.go Normal file
View File

@ -0,0 +1,71 @@
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
}

19
examples/obs/README.md Normal file
View File

@ -0,0 +1,19 @@
## 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"
```

18
examples/obs/go.mod Normal file
View File

@ -0,0 +1,18 @@
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
)

29
examples/obs/go.sum Normal file
View File

@ -0,0 +1,29 @@
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=

131
examples/obs/main.go Normal file
View File

@ -0,0 +1,131 @@
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
}

84
examples/observer/main.go Normal file
View File

@ -0,0 +1,84 @@
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

@ -1,46 +0,0 @@
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()
}

39
examples/vm-cli/README.md Normal file
View File

@ -0,0 +1,39 @@
## 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.

213
examples/vm-cli/main.go Normal file
View File

@ -0,0 +1,213 @@
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,10 +1,11 @@
module github.com/onyx-and-iris/voicemeeter-api-go
module github.com/onyx-and-iris/voicemeeter/v2
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-20220708085239-5a0f0661e09d
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8
)
require (

7
go.sum
View File

@ -3,13 +3,16 @@ 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-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I=
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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 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=

22
helper_test.go Normal file
View File

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

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

28
midi.go Normal file
View File

@ -0,0 +1,28 @@
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,35 +1,39 @@
package voicemeeter
type t_outputs interface {
GetA1() bool
// iOutputs defines the interface outputs type must satisfy
type iOutputs interface {
A1() bool
SetA1(val bool)
GetA2() bool
A2() bool
SetA2(val bool)
GetA3() bool
A3() bool
SetA3(val bool)
GetA4() bool
A4() bool
SetA4(val bool)
GetA5() bool
A5() bool
SetA5(val bool)
GetB1() bool
B1() bool
SetB1(val bool)
GetB2() bool
B2() bool
SetB2(val bool)
GetB3() bool
B3() 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
}
// GetA1 returns the value of the A1 parameter
func (o *outputs) GetA1() bool {
// A1 returns the value of the A1 parameter
func (o *outputs) A1() bool {
return o.getter_bool("A1")
}
@ -38,8 +42,8 @@ func (o *outputs) SetA1(val bool) {
o.setter_bool("A1", val)
}
// GetA2 returns the value of the A2 parameter
func (o *outputs) GetA2() bool {
// A2 returns the value of the A2 parameter
func (o *outputs) A2() bool {
return o.getter_bool("A2")
}
@ -48,8 +52,8 @@ func (o *outputs) SetA2(val bool) {
o.setter_bool("A2", val)
}
// GetA3 returns the value of the A3 parameter
func (o *outputs) GetA3() bool {
// A3 returns the value of the A3 parameter
func (o *outputs) A3() bool {
return o.getter_bool("A3")
}
@ -58,8 +62,8 @@ func (o *outputs) SetA3(val bool) {
o.setter_bool("A3", val)
}
// GetA4 returns the value of the A4 parameter
func (o *outputs) GetA4() bool {
// A4 returns the value of the A4 parameter
func (o *outputs) A4() bool {
return o.getter_bool("A4")
}
@ -68,8 +72,8 @@ func (o *outputs) SetA4(val bool) {
o.setter_bool("A4", val)
}
// GetA5 returns the value of the A5 parameter
func (o *outputs) GetA5() bool {
// A5 returns the value of the A5 parameter
func (o *outputs) A5() bool {
return o.getter_bool("A5")
}
@ -78,8 +82,8 @@ func (o *outputs) SetA5(val bool) {
o.setter_bool("A5", val)
}
// GetB1 returns the value of the B1 parameter
func (o *outputs) GetB1() bool {
// B1 returns the value of the B1 parameter
func (o *outputs) B1() bool {
return o.getter_bool("B1")
}
@ -88,8 +92,8 @@ func (o *outputs) SetB1(val bool) {
o.setter_bool("B1", val)
}
// GetB2 returns the value of the B2 parameter
func (o *outputs) GetB2() bool {
// B2 returns the value of the B2 parameter
func (o *outputs) B2() bool {
return o.getter_bool("B2")
}
@ -98,8 +102,8 @@ func (o *outputs) SetB2(val bool) {
o.setter_bool("B2", val)
}
// GetB3 returns the value of the B3 parameter
func (o *outputs) GetB3() bool {
// B3 returns the value of the B3 parameter
func (o *outputs) B3() bool {
return o.getter_bool("B3")
}

View File

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

View File

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

View File

@ -1,10 +1,12 @@
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}
@ -49,3 +51,13 @@ 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,73 +3,166 @@ package voicemeeter
import (
"fmt"
"os"
"time"
log "github.com/sirupsen/logrus"
)
// A Remote type represents the API for a kind
// Remote represents the API for a kind
type Remote struct {
kind *kind
Strip []t_strip
Bus []t_bus
Kind *kind
Strip []iStrip
Bus []iBus
Button []button
Command *command
Vban *vban
Device *device
Recorder *recorder
Midi *midi_t
pooler *pooler
pooler *pooler
timeout int
bits int
}
// 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() {
login(r.kind.name)
r.pooler = newPooler(r.kind)
func (r *Remote) Login() error {
err := login(r.Kind.Name, r.timeout, r.bits)
if err != nil {
return err
}
r.InitPooler()
return nil
}
// Logout logs out of the API
// it also terminates the pooler
func (r *Remote) Logout() {
func (r *Remote) Logout() error {
r.pooler.run = false
logout()
err := logout(r.Kind.Name)
if err != nil {
return err
}
return nil
}
// InitPooler initiates the Pooler
func (r *Remote) InitPooler() {
r.pooler = newPooler(r.Kind)
}
// Run launches the Voicemeeter GUI for a kind.
func (r *Remote) Run(kindId string) error {
err := runVoicemeeter(kindId, r.bits)
if err != nil {
return err
}
return nil
}
// Type returns the type of Voicemeeter (basic, banana, potato)
func (r *Remote) Type() string {
return getVMType()
val, err := getVMType()
if err != nil {
fmt.Println(err)
}
return val
}
// Version returns the version of Voicemeeter as a string
func (r *Remote) Version() string {
return getVersion()
val, err := getVersion()
if err != nil {
fmt.Println(err)
}
return val
}
// Pdirty returns true iff a parameter value has changed
func (r *Remote) Pdirty() bool {
return pdirty()
func (r *Remote) Pdirty() (bool, error) {
pdirty, err := pdirty()
return pdirty, err
}
// Mdirty returns true iff a macrobutton value has changed
func (r *Remote) Mdirty() bool {
return mdirty()
func (r *Remote) Mdirty() (bool, error) {
mdirty, err := mdirty()
return mdirty, err
}
func (r *Remote) SendText(script string) {
setParametersMulti(script)
// 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
}
// Register forwards the register method to Pooler
func (r *Remote) Register(o observer) {
r.pooler.Register(o)
func (r *Remote) Register(channel chan string) {
r.pooler.Register(channel)
}
// Register forwards the deregister method to Pooler
func (r *Remote) Deregister(o observer) {
r.pooler.Deregister(o)
// EventAdd adds events to the Pooler
func (r *Remote) EventAdd(events ...string) {
r.pooler.event.Add(events...)
}
// 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
@ -79,11 +172,13 @@ type remoteBuilder interface {
makeVban() remoteBuilder
makeDevice() remoteBuilder
makeRecorder() remoteBuilder
makeMidi() remoteBuilder
setDefaults() remoteBuilder
Build() remoteBuilder
Get() *Remote
}
// directory is responsible for directing the genericBuilder
// director is responsible for directing the genericBuilder
type director struct {
builder remoteBuilder
}
@ -103,138 +198,220 @@ 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
// []t_strip comprises of both physical and virtual strip types
// []iStrip comprises of both physical and virtual strip types
func (b *genericBuilder) makeStrip() remoteBuilder {
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)
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)
} 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 {
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)
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)
} 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 {
fmt.Println("building button")
_button := make([]button, 80)
log.Debug("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 {
fmt.Println("building command")
log.Debug("building command")
b.r.Command = newCommand()
return b
}
// makeVban makes a vban type and assigns it to remote.Vban
func (b *genericBuilder) makeVban() remoteBuilder {
fmt.Println("building vban")
log.Debug("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 {
fmt.Println("building device")
log.Debug("building device")
b.r.Device = newDevice()
return b
}
// makeRecorder makes a recorder type and assigns it to remote.Recorder
func (b *genericBuilder) makeRecorder() remoteBuilder {
fmt.Println("building recorder")
log.Debug("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()
return basb.setKind().
makeStrip().
makeBus().
makeButton().
makeCommand().
makeVban().
makeDevice().
makeMidi().
setDefaults()
}
// 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()
return banb.setKind().
makeStrip().
makeBus().
makeButton().
makeCommand().
makeVban().
makeDevice().
makeRecorder().
makeMidi().
setDefaults()
}
// 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()
return potb.setKind().
makeStrip().
makeBus().
makeButton().
makeCommand().
makeVban().
makeDevice().
makeRecorder().
makeMidi().
setDefaults()
}
// GetRemote returns a Remote type for a kind
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
// this is the interface entry point
func GetRemote(kindId string) *Remote {
_kind, ok := kindMap[kindId]
func NewRemote(kindId string, delay int, opts ...Option) (*Remote, error) {
kind, ok := kindMap[kindId]
if !ok {
err := fmt.Errorf("unknown Voicemeeter kind '%s'", kindId)
fmt.Println(err)
os.Exit(1)
return nil, err
}
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()
return director.Get()
r := director.Get()
for _, opt := range opts {
opt(r)
}
return r, nil
}

View File

@ -8,7 +8,7 @@ import (
func TestGetBasicRemote(t *testing.T) {
//t.Skip("skipping test")
__rem := GetRemote("basic")
__rem, _ := NewRemote("basic", 0)
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 := GetRemote("banana")
__rem, _ := NewRemote("banana", 0)
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 := GetRemote("potato")
__rem, _ := NewRemote("potato", 0)
t.Run("Should return a remote basic type", func(t *testing.T) {
assert.NotNil(t, __rem)
})
@ -83,3 +83,25 @@ 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,45 +2,69 @@ package voicemeeter
import (
"fmt"
"time"
log "github.com/sirupsen/logrus"
)
type t_strip interface {
// iStrip defines the interface strip types must satisfy
type iStrip interface {
String() string
GetMute() bool
Mute() bool
SetMute(val bool)
GetMono() bool
Mono() bool
SetMono(val bool)
GetSolo() bool
Solo() bool
SetSolo(val bool)
GetLimit() int
Limit() int
SetLimit(val int)
GetLabel() string
Label() string
SetLabel(val string)
GetGain() float64
SetGain(val float32)
GetMc() bool
Gain() float64
SetGain(val float64)
Mc() bool
SetMc(val bool)
GetComp() float64
SetComp(val float32)
GetGate() float64
SetGate(val float32)
GetAudibility() float64
SetAudibility(val float32)
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
GainLayer() []gainLayer
Levels() *levels
t_outputs
iOutputs
}
// strip represents a strip channel
type strip struct {
iRemote
outputs
eQ *eQ
comp *comp
gate *gate
denoiser *denoiser
gainLayer []gainLayer
levels
levels *levels
}
// GetMute returns the value of the Mute parameter
func (s *strip) GetMute() bool {
// Mute returns the value of the Mute parameter
func (s *strip) Mute() bool {
return s.getter_bool("Mute")
}
@ -49,8 +73,8 @@ func (s *strip) SetMute(val bool) {
s.setter_bool("Mute", val)
}
// GetMono returns the value of the Mono parameter
func (s *strip) GetMono() bool {
// Mono returns the value of the Mono parameter
func (s *strip) Mono() bool {
return s.getter_bool("Mono")
}
@ -59,8 +83,8 @@ func (s *strip) SetMono(val bool) {
s.setter_bool("Mono", val)
}
// GetSolo returns the value of the Solo parameter
func (s *strip) GetSolo() bool {
// Solo returns the value of the Solo parameter
func (s *strip) Solo() bool {
return s.getter_bool("Solo")
}
@ -69,8 +93,8 @@ func (s *strip) SetSolo(val bool) {
s.setter_bool("Solo", val)
}
// GetLimit returns the value of the Limit parameter
func (s *strip) GetLimit() int {
// Limit returns the value of the Limit parameter
func (s *strip) Limit() int {
return s.getter_int("Limit")
}
@ -79,8 +103,8 @@ func (s *strip) SetLimit(val int) {
s.setter_int("Limit", val)
}
// GetLabel returns the value of the Label parameter
func (s *strip) GetLabel() string {
// Label returns the value of the Label parameter
func (s *strip) Label() string {
return s.getter_string("Label")
}
@ -89,209 +113,514 @@ func (s *strip) SetLabel(val string) {
s.setter_string("Label", val)
}
// GetGain returns the value of the Gain parameter
func (s *strip) GetGain() float64 {
// Gain returns the value of the Gain parameter
func (s *strip) Gain() float64 {
return s.getter_float("Gain")
}
// SetGain sets the value of the Gain parameter
func (s *strip) SetGain(val float32) {
func (s *strip) SetGain(val float64) {
s.setter_float("Gain", val)
}
// PanX returns the value of the Pan_X parameter
func (s *strip) PanX() float64 {
return s.getter_float("Pan_x")
}
// SetPanX sets the value of the Pan_X parameter
func (s *strip) SetPanX(val float64) {
s.setter_float("Pan_x", val)
}
// PanY returns the value of the Pan_Y parameter
func (s *strip) PanY() float64 {
return s.getter_float("Pan_y")
}
// SetPanY sets the value of the Pan_Y parameter
func (s *strip) SetPanY(val float64) {
s.setter_float("Pan_y", val)
}
// Eq returns the eQ field
func (s *strip) Eq() *eQ {
return s.eQ
}
// GainLayer returns the gainlayer field
func (s *strip) GainLayer() []gainLayer {
return s.gainLayer
}
// Levels returns the gainlayer field
// Levels returns the levels field
func (s *strip) Levels() *levels {
return &s.levels
return s.levels
}
type physicalStrip struct {
// 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 {
strip
}
func newPhysicalStrip(i int, k *kind) t_strip {
// newPhysicalStrip returns a PhysicalStrip type
func newPhysicalStrip(i int, k *kind) iStrip {
o := newOutputs(fmt.Sprintf("strip[%d]", i), i)
e := newEq(fmt.Sprintf("strip[%d].EQ", i), i)
c := newComp(i)
g := newGate(i)
d := newDenoiser(i)
gl := make([]gainLayer, 8)
for j := 0; j < 8; j++ {
gl[j] = newGainLayer(i, j)
}
l := newStripLevels(i, k)
ps := physicalStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, gl, l}}
return t_strip(&ps)
ps := PhysicalStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, e, c, g, d, gl, l}}
return &ps
}
// implement fmt.stringer interface in fmt
func (p *physicalStrip) String() string {
// String implements fmt.stringer interface
func (p *PhysicalStrip) String() string {
return fmt.Sprintf("PhysicalStrip%d", p.index)
}
// 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 {
// Audibility returns the value of the Audibility parameter
func (p *PhysicalStrip) Audibility() float64 {
return p.getter_float("Audibility")
}
// SetAudibility sets the value of the Audibility parameter
func (p *physicalStrip) SetAudibility(val float32) {
func (p *PhysicalStrip) SetAudibility(val float64) {
p.setter_float("Audibility", val)
}
// GetMc panics reason invalid parameter
func (p *physicalStrip) GetMc() bool {
panic("invalid parameter MC for physicalStrip")
// 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
}
// SetMc panics reason invalid parameter
func (p *physicalStrip) SetMc(val 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")
}
type virtualStrip struct {
// 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 {
strip
}
func newVirtualStrip(i int, k *kind) t_strip {
// newVirtualStrip returns a VirtualStrip type
func newVirtualStrip(i int, k *kind) iStrip {
o := newOutputs(fmt.Sprintf("strip[%d]", i), i)
e := newEq(fmt.Sprintf("strip[%d].EQ", i), i)
c := newComp(i)
g := newGate(i)
d := newDenoiser(i)
gl := make([]gainLayer, 8)
for j := 0; j < 8; j++ {
gl[j] = newGainLayer(i, j)
}
l := newStripLevels(i, k)
vs := virtualStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, gl, l}}
return t_strip(&vs)
vs := VirtualStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, e, c, g, d, gl, l}}
return &vs
}
// implement fmt.stringer interface in fmt
func (v *virtualStrip) String() string {
// String implements fmt.stringer interface
func (v *VirtualStrip) String() string {
return fmt.Sprintf("VirtualStrip%d", v.index)
}
// GetMc returns the value of the MC parameter
func (v *virtualStrip) GetMc() bool {
// 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 {
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)
}
// GetComp panics reason invalid parameter
func (v *virtualStrip) GetComp() float64 {
panic("invalid parameter Comp for virtualStrip")
// 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
}
// SetComp panics reason invalid parameter
func (v *virtualStrip) SetComp(val float32) {
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")
}
// GetGate panics reason invalid parameter
func (v *virtualStrip) GetGate() float64 {
panic("invalid parameter Gate 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
}
// SetGate panics reason invalid parameter
func (v *virtualStrip) SetGate(val float32) {
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")
}
// GetAudibility panics reason invalid parameter
func (v *virtualStrip) GetAudibility() float64 {
panic("invalid parameter Audibility 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
}
// SetAudibility panics reason invalid parameter
func (v *virtualStrip) SetAudibility(val float32) {
panic("invalid parameter Audibility for virtualStrip")
// SetFxX logs a warning reason invalid parameter
func (v *VirtualStrip) SetFxX(val float64) {
log.Warn("invalid parameter SetFxX for virtualStrip")
}
// FxY logs a warning reason invalid parameter
// it always returns zero value
func (v *VirtualStrip) FxY() float64 {
log.Warn("invalid parameter FxY for virtualStrip")
return 0
}
// SetFxY logs a warning reason invalid parameter
func (v *VirtualStrip) SetFxY(val float64) {
log.Warn("invalid parameter SetFxY for virtualStrip")
}
// Audibility logs a warning reason invalid parameter
// it always returns zero value
func (v *VirtualStrip) Audibility() float64 {
log.Warn("invalid parameter Audibility for virtualStrip")
return 0
}
// SetAudibility logs a warning reason invalid parameter
func (v *VirtualStrip) SetAudibility(val float64) {
log.Warn("invalid parameter Audibility for virtualStrip")
}
// 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))
}
func (gl *gainLayer) Set(val float32) {
// Set sets the gain value for a single gainlayer
func (gl *gainLayer) Set(val float64) {
gl.setter_float(fmt.Sprintf("gainlayer[%d]", gl.index), val)
}
func newStripLevels(i int, k *kind) levels {
// newStripLevels returns a levels struct
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"}
}
func (l *levels) PreFader() []float32 {
// PreFader returns the level values for this strip, PREFADER mode
func (l *levels) PreFader() []float64 {
_levelCache.stripMode = 0
var levels []float32
for i := l.init; i < l.init+l.offset; i++ {
levels = append(levels, l.convertLevel(_levelCache.stripLevels[i]))
levels := make([]float64, l.offset)
for i := range levels {
levels[i] = convertLevel(_levelCache.stripLevels[l.init+i])
}
return levels
}
func (l *levels) PostFader() []float32 {
// PostFader returns the level values for this strip, POSTFADER mode
func (l *levels) PostFader() []float64 {
_levelCache.stripMode = 1
var levels []float32
for i := l.init; i < l.init+l.offset; i++ {
levels = append(levels, l.convertLevel(_levelCache.stripLevels[i]))
levels := make([]float64, l.offset)
for i := range levels {
levels[i] = convertLevel(_levelCache.stripLevels[l.init+i])
}
return levels
}
func (l *levels) PostMute() []float32 {
// PostMute returns the level values for this strip, POSTMUTE mode
func (l *levels) PostMute() []float64 {
_levelCache.stripMode = 2
var levels []float32
for i := l.init; i < l.init+l.offset; i++ {
levels = append(levels, l.convertLevel(_levelCache.stripLevels[i]))
levels := make([]float64, l.offset)
for i := range levels {
levels[i] = convertLevel(_levelCache.stripLevels[l.init+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,26 +1,31 @@
package voicemeeter_test
import (
"os"
"bytes"
"testing"
"time"
"github.com/onyx-and-iris/voicemeeter-api-go"
log "github.com/sirupsen/logrus"
"github.com/onyx-and-iris/voicemeeter/v2"
)
var (
vmRem = voicemeeter.GetRemote("potato")
logstring bytes.Buffer
vm, err = voicemeeter.NewRemote("potato", 30)
)
func TestMain(m *testing.M) {
vmRem.Login()
code := m.Run()
vmRem.Logout()
os.Exit(code)
}
func sync() {
time.Sleep(30 * time.Millisecond)
for vmRem.Pdirty() || vmRem.Mdirty() {
if err != nil {
log.Fatal(err)
}
err = vm.Login()
if err != nil {
log.Fatal(err)
}
defer vm.Logout()
log.SetOutput(&logstring)
m.Run()
}

View File

@ -8,232 +8,251 @@ import (
func TestStrip0Mute(t *testing.T) {
//t.Skip("skipping test")
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(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(false)
sync()
t.Run("Should return false when SetMute(false)", func(t *testing.T) {
assert.False(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())
})
}
func TestStrip3A1(t *testing.T) {
//t.Skip("skipping test")
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(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(false)
sync()
t.Run("Should return false when SetA1(false)", func(t *testing.T) {
assert.False(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())
})
}
func TestStrip2Limit(t *testing.T) {
//t.Skip("skipping test")
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(-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(-32)
sync()
t.Run("Should return -32 when SetLimit(-8)", func(t *testing.T) {
assert.Equal(t, vmRem.Strip[2].GetLimit(), -32)
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)
})
}
func TestStrip4Label(t *testing.T) {
//t.Skip("skipping test")
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("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("test1")
sync()
t.Run("Should return test1 when SetLimit('test1')", func(t *testing.T) {
assert.Equal(t, "test1", 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())
})
}
func TestStrip5Gain(t *testing.T) {
func TestStrip4Gain(t *testing.T) {
//t.Skip("skipping test")
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(-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(-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)
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)
})
}
func TestStrip3Comp(t *testing.T) {
func TestStrip4CompKnob(t *testing.T) {
//t.Skip("skipping test")
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(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(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)
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)
})
}
func TestStrip5Mc(t *testing.T) {
//t.Skip("skipping test")
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(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(false)
sync()
t.Run("Should return false when SetMc(false)", func(t *testing.T) {
assert.False(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())
})
}
func TestStrip2GainLayer3(t *testing.T) {
//t.Skip("skipping test")
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(-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(-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)
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)
})
}
func TestBus3Eq(t *testing.T) {
func TestBus3EqOn(t *testing.T) {
//t.Skip("skipping test")
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(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(false)
sync()
t.Run("Should return false when SetEq(false)", func(t *testing.T) {
assert.False(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())
})
}
func TestBus4Label(t *testing.T) {
//t.Skip("skipping test")
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("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("test1")
sync()
t.Run("Should return test1 when SetEq('test1')", func(t *testing.T) {
assert.Equal(t, "test1", 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())
})
}
func TestBus3ModeAmix(t *testing.T) {
//t.Skip("skipping test")
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())
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())
})
}
func TestVbanInStream0On(t *testing.T) {
//t.Skip("skipping test")
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(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(false)
sync()
t.Run("Should return false when SetOn(false)", func(t *testing.T) {
assert.False(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())
})
}
func TestVbanOutStream6On(t *testing.T) {
//t.Skip("skipping test")
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(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(false)
sync()
t.Run("Should return false when SetOn(false)", func(t *testing.T) {
assert.False(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())
})
}
func TestVbanOutStream3Name(t *testing.T) {
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())
//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())
})
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())
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())
})
}
func TestVbanInStream4Bit(t *testing.T) {
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)
//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")
})
}
func TestVbanOutStream4Bit(t *testing.T) {
//t.Skip("skipping test")
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(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(24)
sync()
t.Run("Should return 24 when SetBit(24)", func(t *testing.T) {
assert.Equal(t, vmRem.Vban.OutStream[4].GetBit(), 24)
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)
})
}

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,13 +12,24 @@ func allTrue(s []bool, sz int) bool {
return true
}
func update(s1 []float32, s2 []float32, sz int) {
// update copies the contents of one float slice into another
func update(s1 []float64, s2 []float64, 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
}

41
util_test.go Normal file
View File

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