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

View File

@ -9,4 +9,171 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
## [Unreleased] ## [Unreleased]
- [x] - [x]
## [2.1.0] - 2024-07-01
### Added
- Added a configurable login timeout in seconds (defaults to 2).
- Option function added for overriding the type of Voicemeeter GUI runVoicemeeter() will launch.
- Explanation of Option functions added to README.
### Changed
- runVoicemeeter() now launches x64 GUIs for all kinds if on a 64 bit system.
- this can be overridden to force 32 bit GUI using voicemeeter.WithBits(32) Option function
## [2.0.0] - 2022-10-25
V2 introduces some breaking changes.
### Changed
- Removed Get prefix from getters in Bus, Strip, Vban, Button and Output types.
- Pooler now communicates event updates over a channel.
- strip.comp now references comp struct type. (see readme for changes in setting comp parameters)
- strip.gate now references gate struct type. (see readme for changes in setting gate parameters)
- strip.eq, bus.eq now reference eQ struct type. (see readme for changes in setting eq parameters)
- All examples and tests have been updated to reflect the changes.
### Added
- denoiser type to strip types.
- XY parameters added to strip type
- extra logging added to getters/setters in iRemote type.
- InitPooler to Remote type in case the Pooler needs reinitiating. (perhaps the GUI closed unexpectedly)
### Fixed
- Functions that wrap CAPI calls in base.go now return correct error values.
## [1.11.0] - 2022-10-10
### Fixed
- type error in getLevel
## [1.8.0] - 2022-09-17
### Added
- vm-cli example added + example README
- Fade, App methods added to project README
## [1.7.0] - 2022-09-14
### 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 # A Go Wrapper for the Voicemeeter API
This package offers a Go interface for the Voicemeeter Remote C API.
For an outline of past/future changes refer to: [CHANGELOG](CHANGELOG.md) For an outline of past/future changes refer to: [CHANGELOG](CHANGELOG.md)
## Tested against ## Tested against
- Basic 1.0.8.2 - Basic 1.1.1.1
- Banana 2.0.6.2 - Banana 2.1.1.1
- Potato 3.0.2.2 - Potato 3.1.1.1
## Requirements ## Requirements
- [Voicemeeter](https://voicemeeter.com/) - [Voicemeeter](https://voicemeeter.com/)
- Go 1.18 or greater - Go 1.18 or greater
## Installation ## Installation
#### GO.MOD Initialize your own module then `go get`
Add to your `go.mod` file: ```
go mod init github.com/x/y
`require github.com/onyx-and-iris/voicemeeter-api-go v1.0.2` go get github.com/onyx-and-iris/voicemeeter/v2
```
#### GO GET
Install voicemeeter-api-go package from your console
`go get github.com/onyx-and-iris/voicemeeter-api-go`
## `Use` ## `Use`
@ -40,348 +33,558 @@ package main
import ( import (
"fmt" "fmt"
"log"
"github.com/onyx-and-iris/voicemeeter-api-go" "github.com/onyx-and-iris/voicemeeter/v2"
) )
func main() { func main() {
kindId := "banana" vm, err := vmConnect()
vmRem := voicemeeter.GetRemote(kindId) 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") func vmConnect() (*voicemeeter.Remote, error) {
vmRem.Strip[0].SetMute(true) vm, err := voicemeeter.NewRemote("banana", 20)
fmt.Printf("Strip 0 (%s) mute was set to %v\n", vmRem.Strip[0].GetLabel(), vmRem.Strip[0].GetMute()) 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: Pass the kind of Voicemeeter as an argument. kindId may be:
- `basic` - `basic`
- `banana` - `banana`
- `potato` - `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` ## `Remote Type`
#### `vmRem.Strip` #### `vm.Strip`
[]t_strip slice containing both physicalStrip and virtualStrip types []t_strip slice containing both physicalStrip and virtualStrip types
#### `vmRem.Bus` #### `vm.Bus`
[]t_bus slice containing both physicalBus and virtualBus types []t_bus slice containing both physicalBus and virtualBus types
#### `vmRem.Button` #### `vm.Button`
[]button slice containing button types, one for each macrobutton []button slice containing button types, one for each macrobutton
#### `vmRem.Command` #### `vm.Command`
pointer to command type, represents action type functions pointer to command type, represents action type functions
#### `vmRem.Vban` #### `vm.Vban`
pointer to vban type, containing both vbanInStream and vbanOutStream slices pointer to vban type, containing both vbanInStream and vbanOutStream slices
#### `vmRem.Device` #### `vm.Device`
pointer to device type, represents physical input/output hardware devices pointer to device type, represents physical input/output hardware devices
#### `vmRem.Recorder` #### `vm.Recorder`
pointer to recorder type, represents the 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 returns the type of Voicemeeter as a string
#### `vmRem.Version()` #### `vm.Version()`
returns the version of Voicemeeter as a string 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") 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 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` ## `Available commands`
### Strip ### Strip
The following functions are available The following methods are available
- `GetMute() bool` - `Mute() bool`
- `SetMute(val bool)` - `SetMute(val bool)`
- `GetMono() bool` - `Mono() bool`
- `SetMono(val bool)` - `SetMono(val bool)`
- `GetSolo() bool` - `Solo() bool`
- `SetSolo(val bool)` - `SetSolo(val bool)`
- `GetLimit() int` - `Limit() int`
- `SetLimit(val int)` from -40 to 12 - `SetLimit(val int)` from -40 to 12
- `GetLabel() string` - `Label() string`
- `SetLabel(val string)` - `SetLabel(val string)`
- `GetGain() float64` - `Gain() float64`
- `SetGain(val float32)` from -60.0 to 12.0 - `SetGain(val float64)` from -60.0 to 12.0
- `GetMc() bool` - `Mc() bool`
- `SetMc(val bool)` - `SetMc(val bool)`
- `GetComp() float64` - `Audibility() float64`
- `SetComp(val float32)` from 0.0 to 10.0 - `SetAudibility(val float64)` from 0.0 to 10.0
- `GetGate() float64` - `A1() bool - A5() bool`
- `SetGate(val float32)` from 0.0 to 10.0 - `SetA1(val bool) - SetA5(val bool)`
- `GetAudibility() float64` - `B1() bool - B3() bool`
- `SetAudibility(val float32)` from 0.0 to 10.0 - `SetB1(val bool) bool - SetB3(val bool) bool`
- `GetA1() bool - GetA5() bool` - `AppGain(name string, gain float64)`
- `SetA1(val bool) - SetA5(val bool)` - `AppMute(name string, val bool)`
example: example:
```go ```go
vmRem.Strip[3].SetGain(3.7) vm.Strip[3].SetGain(3.7)
fmt.Println(vmRem.Strip[0].GetLabel()) fmt.Println(vm.Strip[0].Label())
vmRem.Strip[4].SetA1(true) 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` - `Knob() float64`
- `Set(val float32)` - `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: example:
```go ```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 ##### Levels
- `vmRem.Strip[i].Levels()` - `vm.Strip[i].Levels()`
The following functions are available The following methods are available
- `PreFader() []float32` - `PreFader() []float64`
- `PostFader() []float32` - `PostFader() []float64`
- `PostMute() []float32` - `PostMute() []float64`
example: example:
```go ```go
fmt.Println(vmRem.Strip[5].Levels().PreFader()) fmt.Println(vm.Strip[5].Levels().PreFader())
``` ```
### Bus ### Bus
The following functions are available The following methods are available
- `String() string` - `String() string`
- `GetMute() bool` - `Mute() bool`
- `SetMute(val bool)` - `SetMute(val bool)`
- `GetEq() bool` - `Mono() bool`
- `SetEq(val bool)` - `SetMono(val bool)`
- `GetMono() bool` - `Label() string`
- `SetMono(val bool)` - `SetLabel(val string)`
- `GetLabel() string` - `Gain() float64`
- `SetLabel(val string)` - `SetGain(val float64)` from -60.0 to 12.0
- `GetGain() float64`
- `SetGain(val float32)` from -60.0 to 12.0 example:
```go ```go
vmRem.Bus[3].SetEq(true) vm.Bus[3].SetEq(true)
fmt.Println(vmRem.Bus[0].GetLabel()) fmt.Println(vm.Bus[0].Label())
``` ```
##### Modes ##### Modes
- `vmRem.Bus[i].Mode()` - `vm.Bus[i].Mode()`
The following functions are available The following methods are available
- `SetNormal(val bool)` - `SetNormal(val bool)`
- `GetNormal() bool` - `Normal() bool`
- `SetAmix(val bool)` - `SetAmix(val bool)`
- `GetAmix() bool` - `Amix() bool`
- `SetBmix(val bool)` - `SetBmix(val bool)`
- `GetBmix() bool` - `Bmix() bool`
- `SetRepeat(val bool)` - `SetRepeat(val bool)`
- `GetRepeat() bool` - `Repeat() bool`
- `SetComposite(val bool)` - `SetComposite(val bool)`
- `GetComposite() bool` - `Composite() bool`
- `SetTvMix(val bool)` - `SetTvMix(val bool)`
- `GetTvMix() bool` - `TvMix() bool`
- `SetUpMix21(val bool)` - `SetUpMix21(val bool)`
- `GetUpMix21() bool` - `UpMix21() bool`
- `SetUpMix41(val bool)` - `SetUpMix41(val bool)`
- `GetUpMix41() bool` - `UpMix41() bool`
- `SetUpMix61(val bool)` - `SetUpMix61(val bool)`
- `GetUpMix61() bool` - `UpMix61() bool`
- `SetCenterOnly(val bool)` - `SetCenterOnly(val bool)`
- `GetCenterOnly() bool` - `CenterOnly() bool`
- `SetLfeOnly(val bool)` - `SetLfeOnly(val bool)`
- `GetLfeOnly() bool` - `LfeOnly() bool`
- `SetRearOnly(val bool)` - `SetRearOnly(val bool)`
- `GetRearOnly() bool` - `RearOnly() bool`
example: example:
```go ```go
vmRem.Bus[3].Mode().SetAmix(true) vm.Bus[3].Mode().SetAmix(true)
vmRem.Bus[4].Mode().SetCenterOnly(true) vm.Bus[4].Mode().SetCenterOnly(true)
``` ```
##### Levels ##### Levels
- `vmRem.Bus[i].Levels()` - `vm.Bus[i].Levels()`
The following functions are available The following methods are available
- `All() []float32` - `All() []float64`
example: example:
```go ```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 ### Button
The following functions are available The following methods are available
- `GetState() bool` - `State() bool`
- `SetState(val bool)` - `SetState(val bool)`
- `GetStateOnly() bool` - `StateOnly() bool`
- `SetStateOnly(val bool)` - `SetStateOnly(val bool)`
- `GetTrigger() bool` - `Trigger() bool`
- `SetTrigger(val bool)` - `SetTrigger(val bool)`
example: example:
```go ```go
vmRem.Button[37].SetState(true) vm.Button[37].SetState(true)
fmt.Println(vmRem.Button[64].GetStateOnly()) fmt.Println(vm.Button[64].StateOnly())
``` ```
### Command ### Command
The following functions are available The following methods are available
- `Show()` Show Voicemeeter GUI if it's hidden - `Show()` Show Voicemeeter GUI if it's hidden
- `Hide()` Hide Voicemeeter GUI if it's shown - `Hide()` Hide Voicemeeter GUI if it's shown
- `Shutdown()` Shuts down the GUI - `Shutdown()` Shuts down the GUI
- `Restart()` Restart the audio engine - `Restart()` Restart the audio engine
- `Lock(val bool)` Lock the Voicemeeter GUI - `Lock(val bool)` Lock the Voicemeeter GUI
example: example:
```go ```go
vmRem.Command.Restart() vm.Command.Restart()
vmRem.Command.Show() vm.Command.Show()
``` ```
### VBAN ### 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 ##### 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` - `On() bool`
- `SetOn(val bool)` - `SetOn(val bool)`
- `GetName() string` - `Name() string`
- `SetName(val string)` - `SetName(val string)`
- `GetIp() string` - `Ip() string`
- `SetIp(val string)` - `SetIp(val string)`
- `GetPort() int` - `Port() int`
- `SetPort(val int)` from 1024 to 65535 - `SetPort(val int)` from 1024 to 65535
- `GetSr() int` - `Sr() int`
- `SetSr(val int)` (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000) - `SetSr(val int)` (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000)
- `GetChannel() int` - `Channel() int`
- `SetChannel(val int)` from 1 to 8 - `SetChannel(val int)` from 1 to 8
- `GetBit() int` - `Bit() int`
- `SetBit(val int)` 16 or 24 - `SetBit(val int)` 16 or 24
- `GetQuality() int` - `Quality() int`
- `SetQuality(val int)` from 0 to 4 - `SetQuality(val int)` from 0 to 4
- `GetRoute() int` - `Route() int`
- `SetRoute(val int)` from 0 to 8 - `SetRoute(val int)` from 0 to 8
example: example:
```go ```go
# turn VBAN on # turn VBAN on
vmRem.Vban.Enable() vm.Vban.Enable()
// turn on vban instream 0 // 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 // set bit property for outstream 3 to 24
vmRem.Vban.OutStream[3].SetBit(24) vm.Vban.OutStream[3].SetBit(24)
``` ```
### Device ### Device
The following functions are available The following methods are available
- `Ins` - `Ins()`
- `Outs` - `Outs()`
- `Input(val int)` - `Input(val int)`
- `Output(val int)` - `Output(val int)`
example: example:
```go ```go
for i := 0; i < int(vmRem.Device.Ins()); i++ { for i := 0; i < int(vm.Device.Ins()); i++ {
fmt.Println(vmRem.Device.Input(i)) fmt.Println(vm.Device.Input(i))
} }
``` ```
### Recorder ### Recorder
The following functions are available The following methods are available
- `Play()` - `Play()`
- `Stop()` - `Stop()`
- `Pause()` - `Pause()`
- `Replay()` - `Replay()`
- `Record()` - `Record()`
- `Ff()` - `Ff()`
- `Rew()` - `Rew()`
example: example:
```go ```go
vmRem.Recorder.Play() vm.Recorder.Play()
vmRem.Recorder.Stop() vm.Recorder.Stop()
# Enable loop play # Enable loop play
vmRem.Recorder.Loop(true) vm.Recorder.Loop(true)
# Disable recorder out channel B2 # 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 ### Run tests
@ -394,4 +597,4 @@ go test ./...
### Official Documentation ### Official Documentation
- [Voicemeeter Remote C API](https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/main/VoicemeeterRemoteAPI.pdf) - [Voicemeeter Remote C API](https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/main/VoicemeeterRemoteAPI.pdf)

300
base.go
View File

@ -2,13 +2,16 @@ package voicemeeter
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"math" "math"
"os" "runtime"
"strings" "strings"
"syscall" "syscall"
"time" "time"
"unsafe" "unsafe"
log "github.com/sirupsen/logrus"
) )
var ( var (
@ -37,222 +40,281 @@ var (
vmMdirty = mod.NewProc("VBVMR_MacroButton_IsDirty") vmMdirty = mod.NewProc("VBVMR_MacroButton_IsDirty")
vmGetMacroStatus = mod.NewProc("VBVMR_MacroButton_GetStatus") vmGetMacroStatus = mod.NewProc("VBVMR_MacroButton_GetStatus")
vmSetMacroStatus = mod.NewProc("VBVMR_MacroButton_SetStatus") vmSetMacroStatus = mod.NewProc("VBVMR_MacroButton_SetStatus")
vmGetMidiMessage = mod.NewProc("VBVMR_GetMidiMessage")
) )
// login logs into the API, // login logs into the API,
// then attempts to launch Voicemeeter if it's not running. // attempts to launch Voicemeeter if it's not running,
func login(kindId string) { // initializes dirty parameters.
func login(kindId string, timeout, bits int) error {
res, _, _ := vmLogin.Call() res, _, _ := vmLogin.Call()
if res == 1 { if res == 1 {
runVoicemeeter(kindId) runVoicemeeter(kindId, bits)
time.Sleep(time.Second)
} else if res != 0 { } else if res != 0 {
err := fmt.Errorf("VBVMR_Login returned %d", res) err := fmt.Errorf("VBVMR_Login returned %d", res)
fmt.Println(err) return err
os.Exit(1)
} }
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, // logout logs out of the API,
// delayed for 100ms to allow final operation to complete. // delayed for 100ms to allow final operation to complete.
func logout() { func logout(kindId string) error {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
res, _, _ := vmLogout.Call() res, _, _ := vmLogout.Call()
if res != 0 { if int32(res) != 0 {
err := fmt.Errorf("VBVMR_Logout returned %d", res) err := fmt.Errorf("VBVMR_Logout returned %d", int32(res))
fmt.Println(err) return err
os.Exit(1)
} }
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. // 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{ vals := map[string]uint64{
"basic": 1, "basic": 1,
"banana": 2, "banana": 2,
"potato": 3, "potato": 3,
} }
res, _, _ := vmRunvm.Call(uintptr(vals[kindId])) val := vals[kindId]
if res != 0 { if strings.Contains(runtime.GOARCH, "64") && bits == 64 {
err := fmt.Errorf("VBVMR_RunVoicemeeter returned %d", res) val += 3
fmt.Println(err)
os.Exit(1)
} }
res, _, _ := vmRunvm.Call(uintptr(val))
if int32(res) != 0 {
err := fmt.Errorf("VBVMR_RunVoicemeeter returned %d", int32(res))
return err
}
return nil
} }
// getVersion returns the version of Voicemeeter as a string // getVersion returns the version of Voicemeeter as a string
func getVersion() string { func getVersion() (string, error) {
var ver uint64 var ver uint64
res, _, _ := vmGetvmVersion.Call(uintptr(unsafe.Pointer(&ver))) res, _, _ := vmGetvmVersion.Call(uintptr(unsafe.Pointer(&ver)))
if res != 0 { if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterVersion returned %d", res) err := fmt.Errorf("VBVMR_GetVoicemeeterVersion returned %d", int32(res))
fmt.Println(err) log.Error(err.Error())
os.Exit(1) return "", err
} }
v1 := (ver & 0xFF000000) >> 24 v1 := (ver & 0xFF000000) >> 24
v2 := (ver & 0x00FF0000) >> 16 v2 := (ver & 0x00FF0000) >> 16
v3 := (ver & 0x0000FF00) >> 8 v3 := (ver & 0x0000FF00) >> 8
v4 := ver & 0x000000FF 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 // pdirty returns true iff a parameter value has changed
func pdirty() bool { func pdirty() (bool, error) {
res, _, _ := vmPdirty.Call() 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 // mdirty returns true iff a macrobutton value has changed
func mdirty() bool { func mdirty() (bool, error) {
res, _, _ := vmMdirty.Call() 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 { // ldirty returns true iff a level value has changed
_levelCache.stripLevelsBuff = make([]float32, (2*k.physIn)+(8*k.virtIn)) func ldirty(k *kind) (bool, error) {
_levelCache.busLevelsBuff = make([]float32, 8*k.numBus()) _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++ { for i := 0; i < (2*k.PhysIn)+(8*k.VirtIn); i++ {
_levelCache.stripLevelsBuff[i] = float32(getLevel(_levelCache.stripMode, 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] _levelCache.stripComp[i] = _levelCache.stripLevelsBuff[i] == _levelCache.stripLevels[i]
} }
for i := 0; i < 8*k.numBus(); i++ { for i := 0; i < 8*k.NumBus(); i++ {
_levelCache.busLevelsBuff[i] = float32(getLevel(3, 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] _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 // getVMType returns the type of Voicemeeter, as a string
func getVMType() string { func getVMType() (string, error) {
var type_ uint64 var type_ uint64
res, _, _ := vmGetvmType.Call( res, _, _ := vmGetvmType.Call(
uintptr(unsafe.Pointer(&type_)), uintptr(unsafe.Pointer(&type_)),
) )
if res != 0 { if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetVoicemeeterType returned %d", res) err := fmt.Errorf("VBVMR_GetVoicemeeterType returned %d", int32(res))
fmt.Println(err) return "", err
os.Exit(1)
} }
vals := map[uint64]string{ vals := map[uint64]string{
1: "basic", 1: "basic",
2: "banana", 2: "banana",
3: "potato", 3: "potato",
} }
return vals[type_] return vals[type_], nil
} }
// getParameterFloat gets the value of a float parameter // 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 var value float32
b := append([]byte(name), 0) b := append([]byte(name), 0)
res, _, _ := vmGetParamFloat.Call( res, _, _ := vmGetParamFloat.Call(
uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&b[0])),
uintptr(unsafe.Pointer(&value)), uintptr(unsafe.Pointer(&value)),
) )
if res != 0 { if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetParameterFloat returned %d", res) err := fmt.Errorf("VBVMR_GetParameterFloat returned %d", int32(res))
fmt.Println(err) return 0, err
os.Exit(1)
} }
return math.Round(float64(value)*10) / 10 return math.Round(float64(value)*10) / 10, nil
} }
// setParameterFloat sets the value of a float parameter // 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) b1 := append([]byte(name), 0)
b2 := math.Float32bits(value) b2 := math.Float32bits(float32(value))
res, _, _ := vmSetParamFloat.Call( res, _, _ := vmSetParamFloat.Call(
uintptr(unsafe.Pointer(&b1[0])), uintptr(unsafe.Pointer(&b1[0])),
uintptr(b2), uintptr(b2),
) )
if res != 0 { if int32(res) != 0 {
err := fmt.Errorf("VBVMR_SetParameterFloat returned %d", res) err := fmt.Errorf("VBVMR_SetParameterFloat returned %d", int32(res))
fmt.Println(err) return err
os.Exit(1)
} }
return nil
} }
// getParameterString gets the value of a string parameter // 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) b1 := append([]byte(name), 0)
var b2 [512]byte var b2 [512]byte
res, _, _ := vmGetParamString.Call( res, _, _ := vmGetParamString.Call(
uintptr(unsafe.Pointer(&b1[0])), uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])), uintptr(unsafe.Pointer(&b2[0])),
) )
if res != 0 { if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetParameterStringA returned %d", res) err := fmt.Errorf("VBVMR_GetParameterStringA returned %d", int32(res))
fmt.Println(err) return "", err
os.Exit(1)
} }
str := bytes.Trim(b2[:], "\x00") str := bytes.Trim(b2[:], "\x00")
return string(str) return string(str), nil
} }
// getParameterString sets the value of a string parameter // setParameterString sets the value of a string parameter
func setParameterString(name, value string) { func setParameterString(name, value string) error {
b1 := append([]byte(name), 0) b1 := append([]byte(name), 0)
b2 := append([]byte(value), 0) b2 := append([]byte(value), 0)
res, _, _ := vmSetParamString.Call( res, _, _ := vmSetParamString.Call(
uintptr(unsafe.Pointer(&b1[0])), uintptr(unsafe.Pointer(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])), uintptr(unsafe.Pointer(&b2[0])),
) )
if res != 0 { if int32(res) != 0 {
err := fmt.Errorf("VBVMR_SetParameterStringA returned %d", res) err := fmt.Errorf("VBVMR_SetParameterStringA returned %d", int32(res))
fmt.Println(err) return err
os.Exit(1)
} }
return nil
} }
// setParametersMulti sets multiple parameters with a script // setParametersMulti sets multiple parameters with a script
func setParametersMulti(script string) { func setParametersMulti(script string) error {
b1 := append([]byte(script), 0) b1 := append([]byte(script), 0)
res, _, _ := vmSetParameters.Call( res, _, _ := vmSetParameters.Call(
uintptr(unsafe.Pointer(&b1[0])), uintptr(unsafe.Pointer(&b1[0])),
) )
if res != 0 { if int32(res) != 0 {
err := fmt.Errorf("VBVMR_SetParameters returned %d", res) err := fmt.Errorf("VBVMR_SetParameters returned %d", int32(res))
fmt.Println(err) return err
os.Exit(1)
} }
return nil
} }
// getMacroStatus gets a macrobutton value // 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 var state float32
res, _, _ := vmGetMacroStatus.Call( res, _, _ := vmGetMacroStatus.Call(
uintptr(id), uintptr(id),
uintptr(unsafe.Pointer(&state)), uintptr(unsafe.Pointer(&state)),
uintptr(mode), uintptr(mode),
) )
if res != 0 { if int32(res) != 0 {
err := fmt.Errorf("VBVMR_MacroButton_GetStatus returned %d", res) err := fmt.Errorf("VBVMR_MacroButton_GetStatus returned %d", int32(res))
fmt.Println(err) return 0, err
os.Exit(1)
} }
return state return float64(state), nil
} }
// setMacroStatus sets a macrobutton value // setMacroStatus sets a macrobutton value
func setMacroStatus(id, state, mode int) { func setMacroStatus(id, state, mode int) error {
res, _, _ := vmSetMacroStatus.Call( res, _, _ := vmSetMacroStatus.Call(
uintptr(id), uintptr(id),
uintptr(state), uintptr(state),
uintptr(mode), uintptr(mode),
) )
if res != 0 { if int32(res) != 0 {
err := fmt.Errorf("VBVMR_MacroButton_SetStatus returned %d", res) err := fmt.Errorf("VBVMR_MacroButton_SetStatus returned %d", int32(res))
fmt.Println(err) return err
os.Exit(1)
} }
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 { if strings.Compare(dir, "in") == 0 {
res, _, _ := vmGetDevNumIn.Call() res, _, _ := vmGetDevNumIn.Call()
return uint64(res) 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 t_ uint64
var b1 [512]byte var b1 [512]byte
var b2 [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(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])), uintptr(unsafe.Pointer(&b2[0])),
) )
if res != 0 { if int32(res) != 0 {
err := fmt.Errorf("VBVMR_Input_GetDeviceDescA returned %d", res) err := fmt.Errorf("VBVMR_Input_GetDeviceDescA returned %d", int32(res))
fmt.Println(err) return "", 0, "", err
os.Exit(1)
} }
} else { } else {
res, _, _ := vmGetDevDescOut.Call( 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(&b1[0])),
uintptr(unsafe.Pointer(&b2[0])), uintptr(unsafe.Pointer(&b2[0])),
) )
if res != 0 { if int32(res) != 0 {
err := fmt.Errorf("VBVMR_Output_GetDeviceDescA returned %d", res) err := fmt.Errorf("VBVMR_Output_GetDeviceDescA returned %d", int32(res))
fmt.Println(err) return "", 0, "", err
os.Exit(1)
} }
} }
name := bytes.Trim(b1[:], "\x00") name := bytes.Trim(b1[:], "\x00")
hwid := bytes.Trim(b2[:], "\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] // 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 var val float32
res, _, _ := vmGetLevelFloat.Call( res, _, _ := vmGetLevelFloat.Call(
uintptr(type_), uintptr(type_),
uintptr(i), uintptr(i),
uintptr(unsafe.Pointer(&val)), uintptr(unsafe.Pointer(&val)),
) )
if res != 0 { if int32(res) != 0 {
err := fmt.Errorf("VBVMR_GetLevel returned %d", res) err := fmt.Errorf("VBVMR_GetLevel returned %d", int32(res))
fmt.Println(err) return 0, err
os.Exit(1)
} }
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 ( import (
"fmt" "fmt"
"time"
) )
type t_bus interface { // iBus defines the interface bus types must satisfy
type iBus interface {
String() string String() string
GetMute() bool Mute() bool
SetMute(val bool) SetMute(val bool)
GetEq() bool Mono() bool
SetEq(val bool)
GetMono() bool
SetMono(val bool) SetMono(val bool)
GetLabel() string Label() string
SetLabel(val string) SetLabel(val string)
GetGain() float64 Gain() float64
SetGain(val float32) SetGain(val float64)
Mode() t_busMode Eq() *eQ
Mode() *busMode
Levels() *levels Levels() *levels
FadeTo(target float32, time_ int)
FadeBy(change float32, time_ int)
} }
// bus represents a bus channel // bus represents a bus channel
type bus struct { type bus struct {
iRemote iRemote
mode busMode eQ *eQ
levels mode *busMode
levels *levels
} }
// GetMute returns the value of the Mute parameter // Mute returns the value of the Mute parameter
func (b *bus) GetMute() bool { func (b *bus) Mute() bool {
return b.getter_bool("Mute") return b.getter_bool("Mute")
} }
@ -37,18 +41,8 @@ func (b *bus) SetMute(val bool) {
b.setter_bool("Mute", val) b.setter_bool("Mute", val)
} }
// GetEq returns the value of the Eq.On parameter // Mono returns the value of the Mute parameter
func (b *bus) GetEq() bool { func (b *bus) Mono() bool {
return b.getter_bool("Eq.On")
}
// SetEq sets the value of the Eq.On parameter
func (b *bus) SetEq(val bool) {
b.setter_bool("Eq.On", val)
}
// GetMono returns the value of the Mute parameter
func (b *bus) GetMono() bool {
return b.getter_bool("Mono") return b.getter_bool("Mono")
} }
@ -57,8 +51,8 @@ func (b *bus) SetMono(val bool) {
b.setter_bool("Mono", val) b.setter_bool("Mono", val)
} }
// GetLabel returns the value of the MC parameter // Label returns the value of the MC parameter
func (b *bus) GetLabel() string { func (b *bus) Label() string {
return b.getter_string("Label") return b.getter_string("Label")
} }
@ -67,199 +61,223 @@ func (b *bus) SetLabel(val string) {
b.setter_string("Label", val) b.setter_string("Label", val)
} }
// GetGain returns the value of the Gain parameter // Gain returns the value of the Gain parameter
func (b *bus) GetGain() float64 { func (b *bus) Gain() float64 {
return b.getter_float("Gain") return b.getter_float("Gain")
} }
// SetGain sets the value of the Gain parameter // 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) b.setter_float("Gain", val)
} }
// Eq returns the eQ field
func (b *bus) Eq() *eQ {
return b.eQ
}
// Mode returns address of a busMode struct // Mode returns address of a busMode struct
func (b *bus) Mode() t_busMode { func (b *bus) Mode() *busMode {
return &b.mode return b.mode
} }
// Levels returns the gainlayer field // Levels returns the levels field
func (b *bus) Levels() *levels { 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 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) b := newBusMode(i)
l := newBusLevels(i, k) 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 // String implements the fmt.stringer interface
func (p *physicalBus) String() string { func (p *PhysicalBus) String() string {
return fmt.Sprintf("PhysicalBus%d", p.index) return fmt.Sprintf("PhysicalBus%d", p.index)
} }
type virtualBus struct { // VirtualBus represents a single virtual bus
type VirtualBus struct {
bus 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) b := newBusMode(i)
l := newBusLevels(i, k) l := newBusLevels(i, k)
vb := virtualBus{bus{iRemote{fmt.Sprintf("bus[%d]", i), i}, b, l}} vb := VirtualBus{bus{iRemote{fmt.Sprintf("bus[%d]", i), i}, e, b, l}}
return t_bus(&vb) return &vb
} }
// String implements the fmt.stringer interface // String implements the fmt.stringer interface
func (v *virtualBus) String() string { func (v *VirtualBus) String() string {
return fmt.Sprintf("VirtualBus%d", v.index) return fmt.Sprintf("VirtualBus%d", v.index)
} }
type t_busMode interface { // busMode offers methods for getting/setting bus mode states
SetNormal(val bool)
GetNormal() bool
SetAmix(val bool)
GetAmix() bool
SetBmix(val bool)
GetBmix() bool
SetRepeat(val bool)
GetRepeat() bool
SetComposite(val bool)
GetComposite() bool
SetTvMix(val bool)
GetTvMix() bool
SetUpMix21(val bool)
GetUpMix21() bool
SetUpMix41(val bool)
GetUpMix41() bool
SetUpMix61(val bool)
GetUpMix61() bool
SetCenterOnly(val bool)
GetCenterOnly() bool
SetLfeOnly(val bool)
GetLfeOnly() bool
SetRearOnly(val bool)
GetRearOnly() bool
}
type busMode struct { type busMode struct {
iRemote iRemote
} }
func newBusMode(i int) busMode { // newBusMode returns a busMode struct
return busMode{iRemote{fmt.Sprintf("bus[%d].mode", i), i}} func newBusMode(i int) *busMode {
return &busMode{iRemote{fmt.Sprintf("bus[%d].mode", i), i}}
} }
// Normal gets the value of the Mode.Normal parameter
func (bm *busMode) Normal() bool {
return bm.getter_bool("Normal")
}
// SetNormal sets the value of the Mode.Normal parameter
func (bm *busMode) SetNormal(val bool) { func (bm *busMode) SetNormal(val bool) {
bm.setter_bool("Normal", val) bm.setter_bool("Normal", val)
} }
func (bm *busMode) GetNormal() bool { // Amix gets the value of the Mode.Amix parameter
return bm.getter_bool("Normal") 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) { func (bm *busMode) SetAmix(val bool) {
bm.setter_bool("Amix", val) bm.setter_bool("Amix", val)
} }
func (bm *busMode) GetAmix() bool { // Bmix gets the value of the Mode.Bmix parameter
return bm.getter_bool("Amix") 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) { func (bm *busMode) SetBmix(val bool) {
bm.setter_bool("Bmix", val) bm.setter_bool("Bmix", val)
} }
func (bm *busMode) GetBmix() bool { // Repeat gets the value of the Mode.Repeat parameter
return bm.getter_bool("Bmix") 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) { func (bm *busMode) SetRepeat(val bool) {
bm.setter_bool("Repeat", val) bm.setter_bool("Repeat", val)
} }
func (bm *busMode) GetRepeat() bool { // Composite gets the value of the Mode.Composite parameter
return bm.getter_bool("Repeat") 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) { func (bm *busMode) SetComposite(val bool) {
bm.setter_bool("Composite", val) bm.setter_bool("Composite", val)
} }
func (bm *busMode) GetComposite() bool { // TvMix gets the value of the Mode.TvMix parameter
return bm.getter_bool("Composite") 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) { func (bm *busMode) SetTvMix(val bool) {
bm.setter_bool("TvMix", val) bm.setter_bool("TvMix", val)
} }
func (bm *busMode) GetTvMix() bool { // UpMix21 gets the value of the Mode.UpMix21 parameter
return bm.getter_bool("TvMix") 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) { func (bm *busMode) SetUpMix21(val bool) {
bm.setter_bool("UpMix21", val) bm.setter_bool("UpMix21", val)
} }
func (bm *busMode) GetUpMix21() bool { // UpMix41 gets the value of the Mode.UpMix41 parameter
return bm.getter_bool("UpMix21") 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) { func (bm *busMode) SetUpMix41(val bool) {
bm.setter_bool("UpMix41", val) bm.setter_bool("UpMix41", val)
} }
func (bm *busMode) GetUpMix41() bool { // UpMix61 gets the value of the Mode.UpMix61 parameter
return bm.getter_bool("UpMix41") 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) { func (bm *busMode) SetUpMix61(val bool) {
bm.setter_bool("UpMix61", val) bm.setter_bool("UpMix61", val)
} }
func (bm *busMode) GetUpMix61() bool { // CenterOnly gets the value of the Mode.CenterOnly parameter
return bm.getter_bool("UpMix61") 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) { func (bm *busMode) SetCenterOnly(val bool) {
bm.setter_bool("CenterOnly", val) bm.setter_bool("CenterOnly", val)
} }
func (bm *busMode) GetCenterOnly() bool { // LfeOnly gets the value of the Mode.LFE parameter
return bm.getter_bool("CenterOnly") 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) { func (bm *busMode) SetLfeOnly(val bool) {
bm.setter_bool("LfeOnly", val) bm.setter_bool("LfeOnly", val)
} }
func (bm *busMode) GetLfeOnly() bool { // RearOnly gets the value of the Mode.RearOnly parameter
return bm.getter_bool("LfeOnly") 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) { func (bm *busMode) SetRearOnly(val bool) {
bm.setter_bool("RearOnly", val) bm.setter_bool("RearOnly", val)
} }
func (bm *busMode) GetRearOnly() bool { // newBusLevels represents the levels field for a channel
return bm.getter_bool("RearOnly") func newBusLevels(i int, k *kind) *levels {
}
func newBusLevels(i int, k *kind) levels {
init := i * 8 init := i * 8
return levels{iRemote{fmt.Sprintf("bus[%d]", i), i}, k, init, 8, "bus"} return &levels{iRemote{fmt.Sprintf("bus[%d]", i), i}, k, init, 8, "bus"}
} }
func (l *levels) All() []float32 { // All returns the level values for a bus
var levels []float32 func (l *levels) All() []float64 {
for i := l.init; i < l.init+l.offset; i++ { levels := make([]float64, l.offset)
levels = append(levels, l.convertLevel(_levelCache.busLevels[i])) for i := range levels {
levels[i] = convertLevel(_levelCache.busLevels[l.init+i])
} }
return levels return levels
} }

View File

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

View File

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

View File

@ -1,5 +1,7 @@
package voicemeeter package voicemeeter
import "fmt"
type devDesc struct { type devDesc struct {
Name, Type, Hwid string Name, Type, Hwid string
} }
@ -13,16 +15,19 @@ func newDevice() *device {
// Ins returns the total number of physical input devices // Ins returns the total number of physical input devices
func (d *device) Ins() int { func (d *device) Ins() int {
return int(get_num_devices("in")) return int(getNumDevices("in"))
} }
// Ins returns the total number of physical input devices // Ins returns the total number of physical input devices
func (d *device) Outs() int { func (d *device) Outs() int {
return int(get_num_devices("out")) return int(getNumDevices("out"))
} }
func (d *device) Input(i int) devDesc { 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{ vals := map[uint64]string{
1: "mme", 1: "mme",
3: "wdm", 3: "wdm",
@ -33,7 +38,10 @@ func (d *device) Input(i int) devDesc {
} }
func (d *device) Output(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{ vals := map[uint64]string{
1: "mme", 1: "mme",
3: "wdm", 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 go 1.18
require ( require (
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.0 github.com/stretchr/testify v1.8.0
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8
) )
require ( 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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

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 ( import (
"fmt" "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 { type iRemote struct {
_identifier string _identifier string
index int index int
} }
// identifier returns a string identifier
func (ir *iRemote) identifier() string { func (ir *iRemote) identifier() string {
return ir._identifier return ir._identifier
} }
@ -17,7 +21,12 @@ func (ir *iRemote) identifier() string {
// getter_bool returns the value of a boolean parameter // getter_bool returns the value of a boolean parameter
func (ir *iRemote) getter_bool(p string) bool { func (ir *iRemote) getter_bool(p string) bool {
param := fmt.Sprintf("%s.%s", ir.identifier(), p) param := fmt.Sprintf("%s.%s", ir.identifier(), p)
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 // setter_bool sets the value of a boolean parameter
@ -29,41 +38,82 @@ func (ir *iRemote) setter_bool(p string, v bool) {
} else { } else {
value = 0 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 // getter_int returns the value of an int parameter p
func (ir *iRemote) getter_int(p string) int { func (ir *iRemote) getter_int(p string) int {
param := fmt.Sprintf("%s.%s", ir.identifier(), p) param := fmt.Sprintf("%s.%s", ir.identifier(), p)
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 // setter_int sets the value v of an int parameter p
func (ir *iRemote) setter_int(p string, v int) { func (ir *iRemote) setter_int(p string, v int) {
param := fmt.Sprintf("%s.%s", ir.identifier(), p) param := fmt.Sprintf("%s.%s", ir.identifier(), p)
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 { func (ir *iRemote) getter_float(p string) float64 {
param := fmt.Sprintf("%s.%s", ir.identifier(), p) var param string
return getParameterFloat(param) 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 // setter_float sets the value v of an float parameter p
func (ir *iRemote) setter_float(p string, v float32) { func (ir *iRemote) setter_float(p string, v float64) {
param := fmt.Sprintf("%s.%s", ir.identifier(), p) var param string
setParameterFloat(param, float32(v)) 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 // getter_string returns the value of a string parameter p
func (ir *iRemote) getter_string(p string) string { func (ir *iRemote) getter_string(p string) string {
param := fmt.Sprintf("%s.%s", ir.identifier(), p) param := fmt.Sprintf("%s.%s", ir.identifier(), p)
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 // setter_string sets the value v of a string parameter p
func (ir *iRemote) setter_string(p, v string) { func (ir *iRemote) setter_string(p, v string) {
param := fmt.Sprintf("%s.%s", ir.identifier(), p) param := fmt.Sprintf("%s.%s", ir.identifier(), p)
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 // A kind represents a Voicemeeter kinds layout
type kind struct { type kind struct {
name string Name string
physIn, virtIn, physOut, virtOut, vbanIn, vbanOut int PhysIn, VirtIn, PhysOut, VirtOut, VbanIn, VbanOut int
} }
// numStrip returns the total number of strips for a kind // numStrip returns the total number of strips for a kind
func (k *kind) numStrip() int { func (k *kind) NumStrip() int {
n := k.physIn + k.virtIn n := k.PhysIn + k.VirtIn
return n return n
} }
// numBus returns the total number of buses for a kind // numBus returns the total number of buses for a kind
func (k *kind) numBus() int { func (k *kind) NumBus() int {
n := k.physOut + k.virtOut n := k.PhysOut + k.VirtOut
return n return n
} }
// String implements the fmt.stringer interface // String implements the fmt.stringer interface
func (k *kind) String() string { 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 // newBasicKind returns a basic kind struct address

View File

@ -1,8 +1,6 @@
package voicemeeter package voicemeeter
import "math" // levels represents the levels field for a channel
// levels
type levels struct { type levels struct {
iRemote iRemote
k *kind k *kind
@ -11,12 +9,15 @@ type levels struct {
id string id string
} }
func (l *levels) convertLevel(i float32) float32 { // returns true if any levels value for a strip/bus have been updated
if i > 0 { func (l *levels) IsDirty() bool {
val := 20 * math.Log10(float64(i)) var vals []bool
return float32(roundFloat(float64(val), 1)) 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 var _levelCache *levelCache
@ -24,20 +25,20 @@ var _levelCache *levelCache
// levelCache defines level slices used by the pooler to track updates // levelCache defines level slices used by the pooler to track updates
type levelCache struct { type levelCache struct {
stripMode int stripMode int
stripLevels []float32 stripLevels []float64
busLevels []float32 busLevels []float64
stripLevelsBuff []float32 stripLevelsBuff []float64
busLevelsBuff []float32 busLevelsBuff []float64
stripComp []bool stripComp []bool
busComp []bool busComp []bool
} }
// newLevelCache returns a levelCache struct address // newLevelCache returns a levelCache struct address
func newLevelCache(k *kind) *levelCache { func newLevelCache(k *kind) *levelCache {
stripLevels := make([]float32, (2*k.physIn)+(8*k.virtIn)) stripLevels := make([]float64, (2*k.PhysIn)+(8*k.VirtIn))
busLevels := make([]float32, 8*k.numBus()) busLevels := make([]float64, 8*k.NumBus())
stripComp := make([]bool, (2*k.physIn)+(8*k.virtIn)) stripComp := make([]bool, (2*k.PhysIn)+(8*k.VirtIn))
busComp := make([]bool, 8*k.numBus()) busComp := make([]bool, 8*k.NumBus())
if _levelCache == nil { if _levelCache == nil {
_levelCache = &levelCache{stripMode: 0, stripLevels: stripLevels, busLevels: busLevels, stripComp: stripComp, busComp: busComp} _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 package voicemeeter
type t_outputs interface { // iOutputs defines the interface outputs type must satisfy
GetA1() bool type iOutputs interface {
A1() bool
SetA1(val bool) SetA1(val bool)
GetA2() bool A2() bool
SetA2(val bool) SetA2(val bool)
GetA3() bool A3() bool
SetA3(val bool) SetA3(val bool)
GetA4() bool A4() bool
SetA4(val bool) SetA4(val bool)
GetA5() bool A5() bool
SetA5(val bool) SetA5(val bool)
GetB1() bool B1() bool
SetB1(val bool) SetB1(val bool)
GetB2() bool B2() bool
SetB2(val bool) SetB2(val bool)
GetB3() bool B3() bool
SetB3(val bool) SetB3(val bool)
} }
// outputs represents the outputs field (A1 - A5, B1 - B3)
// expected to be embedded
type outputs struct { type outputs struct {
iRemote iRemote
} }
// newOutputs returns an outputs type
func newOutputs(id string, i int) outputs { func newOutputs(id string, i int) outputs {
o := outputs{iRemote{id, i}} o := outputs{iRemote{id, i}}
return o return o
} }
// GetA1 returns the value of the A1 parameter // A1 returns the value of the A1 parameter
func (o *outputs) GetA1() bool { func (o *outputs) A1() bool {
return o.getter_bool("A1") return o.getter_bool("A1")
} }
@ -38,8 +42,8 @@ func (o *outputs) SetA1(val bool) {
o.setter_bool("A1", val) o.setter_bool("A1", val)
} }
// GetA2 returns the value of the A2 parameter // A2 returns the value of the A2 parameter
func (o *outputs) GetA2() bool { func (o *outputs) A2() bool {
return o.getter_bool("A2") return o.getter_bool("A2")
} }
@ -48,8 +52,8 @@ func (o *outputs) SetA2(val bool) {
o.setter_bool("A2", val) o.setter_bool("A2", val)
} }
// GetA3 returns the value of the A3 parameter // A3 returns the value of the A3 parameter
func (o *outputs) GetA3() bool { func (o *outputs) A3() bool {
return o.getter_bool("A3") return o.getter_bool("A3")
} }
@ -58,8 +62,8 @@ func (o *outputs) SetA3(val bool) {
o.setter_bool("A3", val) o.setter_bool("A3", val)
} }
// GetA4 returns the value of the A4 parameter // A4 returns the value of the A4 parameter
func (o *outputs) GetA4() bool { func (o *outputs) A4() bool {
return o.getter_bool("A4") return o.getter_bool("A4")
} }
@ -68,8 +72,8 @@ func (o *outputs) SetA4(val bool) {
o.setter_bool("A4", val) o.setter_bool("A4", val)
} }
// GetA5 returns the value of the A5 parameter // A5 returns the value of the A5 parameter
func (o *outputs) GetA5() bool { func (o *outputs) A5() bool {
return o.getter_bool("A5") return o.getter_bool("A5")
} }
@ -78,8 +82,8 @@ func (o *outputs) SetA5(val bool) {
o.setter_bool("A5", val) o.setter_bool("A5", val)
} }
// GetB1 returns the value of the B1 parameter // B1 returns the value of the B1 parameter
func (o *outputs) GetB1() bool { func (o *outputs) B1() bool {
return o.getter_bool("B1") return o.getter_bool("B1")
} }
@ -88,8 +92,8 @@ func (o *outputs) SetB1(val bool) {
o.setter_bool("B1", val) o.setter_bool("B1", val)
} }
// GetB2 returns the value of the B2 parameter // B2 returns the value of the B2 parameter
func (o *outputs) GetB2() bool { func (o *outputs) B2() bool {
return o.getter_bool("B2") return o.getter_bool("B2")
} }
@ -98,8 +102,8 @@ func (o *outputs) SetB2(val bool) {
o.setter_bool("B2", val) o.setter_bool("B2", val)
} }
// GetB3 returns the value of the B3 parameter // B3 returns the value of the B3 parameter
func (o *outputs) GetB3() bool { func (o *outputs) B3() bool {
return o.getter_bool("B3") return o.getter_bool("B3")
} }

View File

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

View File

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

View File

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

301
remote.go
View File

@ -3,73 +3,166 @@ package voicemeeter
import ( import (
"fmt" "fmt"
"os" "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 { type Remote struct {
kind *kind Kind *kind
Strip []t_strip Strip []iStrip
Bus []t_bus Bus []iBus
Button []button Button []button
Command *command Command *command
Vban *vban Vban *vban
Device *device Device *device
Recorder *recorder Recorder *recorder
Midi *midi_t
pooler *pooler pooler *pooler
timeout int
bits int
} }
// String implements the fmt.stringer interface // String implements the fmt.stringer interface
func (r *Remote) String() string { func (r *Remote) String() string {
return fmt.Sprintf("Voicemeeter %s", r.kind) return fmt.Sprintf("Voicemeeter %s", r.Kind)
} }
// Login logs into the API // Login logs into the API
// then it intializes the pooler // then it intializes the pooler
func (r *Remote) Login() { func (r *Remote) Login() error {
login(r.kind.name) err := login(r.Kind.Name, r.timeout, r.bits)
r.pooler = newPooler(r.kind) if err != nil {
return err
}
r.InitPooler()
return nil
} }
// Logout logs out of the API // Logout logs out of the API
// it also terminates the pooler // it also terminates the pooler
func (r *Remote) Logout() { func (r *Remote) Logout() error {
r.pooler.run = false 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 { 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 { 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 // Pdirty returns true iff a parameter value has changed
func (r *Remote) Pdirty() bool { func (r *Remote) Pdirty() (bool, error) {
return pdirty() pdirty, err := pdirty()
return pdirty, err
} }
// Mdirty returns true iff a macrobutton value has changed // Mdirty returns true iff a macrobutton value has changed
func (r *Remote) Mdirty() bool { func (r *Remote) Mdirty() (bool, error) {
return mdirty() mdirty, err := mdirty()
return mdirty, err
} }
func (r *Remote) SendText(script string) { // Sync is a helper method that waits for dirty parameters to clear
setParametersMulti(script) 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 // Register forwards the register method to Pooler
func (r *Remote) Register(o observer) { func (r *Remote) Register(channel chan string) {
r.pooler.Register(o) r.pooler.Register(channel)
} }
// Register forwards the deregister method to Pooler // EventAdd adds events to the Pooler
func (r *Remote) Deregister(o observer) { func (r *Remote) EventAdd(events ...string) {
r.pooler.Deregister(o) 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 { type remoteBuilder interface {
setKind() remoteBuilder setKind() remoteBuilder
makeStrip() remoteBuilder makeStrip() remoteBuilder
@ -79,11 +172,13 @@ type remoteBuilder interface {
makeVban() remoteBuilder makeVban() remoteBuilder
makeDevice() remoteBuilder makeDevice() remoteBuilder
makeRecorder() remoteBuilder makeRecorder() remoteBuilder
makeMidi() remoteBuilder
setDefaults() remoteBuilder
Build() remoteBuilder Build() remoteBuilder
Get() *Remote Get() *Remote
} }
// directory is responsible for directing the genericBuilder // director is responsible for directing the genericBuilder
type director struct { type director struct {
builder remoteBuilder builder remoteBuilder
} }
@ -103,138 +198,220 @@ func (d *director) Get() *Remote {
return d.builder.Get() return d.builder.Get()
} }
// genericBuilder represents a generic builder type
type genericBuilder struct { type genericBuilder struct {
k *kind k *kind
r Remote r Remote
} }
// setKind sets the kind for a builder of a kind
func (b *genericBuilder) setKind() remoteBuilder { func (b *genericBuilder) setKind() remoteBuilder {
b.r.kind = b.k b.r.Kind = b.k
return b return b
} }
// makeStrip makes a strip slice and assigns it to remote.Strip // 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 { func (b *genericBuilder) makeStrip() remoteBuilder {
fmt.Println("building strip") log.Debug("building strip")
_strip := make([]t_strip, b.k.numStrip()) strip := make([]iStrip, b.k.NumStrip())
for i := 0; i < b.k.numStrip(); i++ { for i := 0; i < b.k.NumStrip(); i++ {
if i < b.k.physIn { if i < b.k.PhysIn {
_strip[i] = newPhysicalStrip(i, b.k) strip[i] = newPhysicalStrip(i, b.k)
} else { } else {
_strip[i] = newVirtualStrip(i, b.k) strip[i] = newVirtualStrip(i, b.k)
} }
} }
b.r.Strip = _strip b.r.Strip = strip
return b return b
} }
// makeBus makes a bus slice and assigns it to remote.Bus // makeBus makes a bus slice and assigns it to remote.Bus
// []t_bus comprises of both physical and virtual bus types // []t_bus comprises of both physical and virtual bus types
func (b *genericBuilder) makeBus() remoteBuilder { func (b *genericBuilder) makeBus() remoteBuilder {
fmt.Println("building bus") log.Debug("building bus")
_bus := make([]t_bus, b.k.numBus()) bus := make([]iBus, b.k.NumBus())
for i := 0; i < b.k.numBus(); i++ { for i := 0; i < b.k.NumBus(); i++ {
if i < b.k.physOut { if i < b.k.PhysOut {
_bus[i] = newPhysicalBus(i, b.k) bus[i] = newPhysicalBus(i, b.k)
} else { } else {
_bus[i] = newVirtualBus(i, b.k) bus[i] = newVirtualBus(i, b.k)
} }
} }
b.r.Bus = _bus b.r.Bus = bus
return b return b
} }
// makeButton makes a button slice and assigns it to remote.Button // makeButton makes a button slice and assigns it to remote.Button
func (b *genericBuilder) makeButton() remoteBuilder { func (b *genericBuilder) makeButton() remoteBuilder {
fmt.Println("building button") log.Debug("building button")
_button := make([]button, 80) button := make([]button, 80)
for i := 0; i < 80; i++ { for i := 0; i < 80; i++ {
_button[i] = newButton(i) button[i] = newButton(i)
} }
b.r.Button = _button b.r.Button = button
return b return b
} }
// makeCommand makes a command type and assigns it to remote.Command // makeCommand makes a command type and assigns it to remote.Command
func (b *genericBuilder) makeCommand() remoteBuilder { func (b *genericBuilder) makeCommand() remoteBuilder {
fmt.Println("building command") log.Debug("building command")
b.r.Command = newCommand() b.r.Command = newCommand()
return b return b
} }
// makeVban makes a vban type and assigns it to remote.Vban // makeVban makes a vban type and assigns it to remote.Vban
func (b *genericBuilder) makeVban() remoteBuilder { func (b *genericBuilder) makeVban() remoteBuilder {
fmt.Println("building vban") log.Debug("building vban")
b.r.Vban = newVban(b.k) b.r.Vban = newVban(b.k)
return b return b
} }
// makeDevice makes a device type and assigns it to remote.Device // makeDevice makes a device type and assigns it to remote.Device
func (b *genericBuilder) makeDevice() remoteBuilder { func (b *genericBuilder) makeDevice() remoteBuilder {
fmt.Println("building device") log.Debug("building device")
b.r.Device = newDevice() b.r.Device = newDevice()
return b return b
} }
// makeRecorder makes a recorder type and assigns it to remote.Recorder // makeRecorder makes a recorder type and assigns it to remote.Recorder
func (b *genericBuilder) makeRecorder() remoteBuilder { func (b *genericBuilder) makeRecorder() remoteBuilder {
fmt.Println("building recorder") log.Debug("building recorder")
b.r.Recorder = newRecorder() b.r.Recorder = newRecorder()
return b return b
} }
// makeMidi makes a midi type and assigns it to remote.Midi
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 // Get returns a fully constructed remote type for a kind
func (b *genericBuilder) Get() *Remote { func (b *genericBuilder) Get() *Remote {
return &b.r return &b.r
} }
// basicBuilder represents a builder specific to basic type
type basicBuilder struct { type basicBuilder struct {
genericBuilder genericBuilder
} }
// Build defines the steps required to build a basic type // Build defines the steps required to build a basic type
func (basb *genericBuilder) Build() remoteBuilder { 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 { type bananaBuilder struct {
genericBuilder genericBuilder
} }
// Build defines the steps required to build a banana type // Build defines the steps required to build a banana type
func (banb *bananaBuilder) Build() remoteBuilder { 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 { type potatoBuilder struct {
genericBuilder genericBuilder
} }
// Build defines the steps required to build a potato type // Build defines the steps required to build a potato type
func (potb *potatoBuilder) Build() remoteBuilder { 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 // this is the interface entry point
func GetRemote(kindId string) *Remote { func NewRemote(kindId string, delay int, opts ...Option) (*Remote, error) {
_kind, ok := kindMap[kindId] kind, ok := kindMap[kindId]
if !ok { if !ok {
err := fmt.Errorf("unknown Voicemeeter kind '%s'", kindId) err := fmt.Errorf("unknown Voicemeeter kind '%s'", kindId)
fmt.Println(err) return nil, err
os.Exit(1)
} }
if delay < 0 {
err := fmt.Errorf("invalid delay value. should be >= 0")
return nil, err
}
vmsync = delay > 0
vmdelay = delay
director := director{} director := director{}
switch _kind.name { switch kind.Name {
case "basic": case "basic":
director.SetBuilder(&basicBuilder{genericBuilder{_kind, Remote{}}}) director.SetBuilder(&basicBuilder{genericBuilder{kind, Remote{}}})
case "banana": case "banana":
director.SetBuilder(&bananaBuilder{genericBuilder{_kind, Remote{}}}) director.SetBuilder(&bananaBuilder{genericBuilder{kind, Remote{}}})
case "potato": case "potato":
director.SetBuilder(&potatoBuilder{genericBuilder{_kind, Remote{}}}) director.SetBuilder(&potatoBuilder{genericBuilder{kind, Remote{}}})
} }
director.Construct() director.Construct()
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) { func TestGetBasicRemote(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
__rem := GetRemote("basic") __rem, _ := NewRemote("basic", 0)
t.Run("Should return a remote basic type", func(t *testing.T) { t.Run("Should return a remote basic type", func(t *testing.T) {
assert.NotNil(t, __rem) assert.NotNil(t, __rem)
}) })
@ -34,7 +34,7 @@ func TestGetBasicRemote(t *testing.T) {
func TestGetBananaRemote(t *testing.T) { func TestGetBananaRemote(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
__rem := GetRemote("banana") __rem, _ := NewRemote("banana", 0)
t.Run("Should return a remote banana type", func(t *testing.T) { t.Run("Should return a remote banana type", func(t *testing.T) {
assert.NotNil(t, __rem) assert.NotNil(t, __rem)
}) })
@ -60,7 +60,7 @@ func TestGetBananaRemote(t *testing.T) {
func TestGetPotatoRemote(t *testing.T) { func TestGetPotatoRemote(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
__rem := GetRemote("potato") __rem, _ := NewRemote("potato", 0)
t.Run("Should return a remote basic type", func(t *testing.T) { t.Run("Should return a remote basic type", func(t *testing.T) {
assert.NotNil(t, __rem) assert.NotNil(t, __rem)
}) })
@ -83,3 +83,25 @@ func TestGetPotatoRemote(t *testing.T) {
assert.NotNil(t, __rem.Recorder) 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 ( import (
"fmt" "fmt"
"time"
log "github.com/sirupsen/logrus"
) )
type t_strip interface { // iStrip defines the interface strip types must satisfy
type iStrip interface {
String() string String() string
GetMute() bool Mute() bool
SetMute(val bool) SetMute(val bool)
GetMono() bool Mono() bool
SetMono(val bool) SetMono(val bool)
GetSolo() bool Solo() bool
SetSolo(val bool) SetSolo(val bool)
GetLimit() int Limit() int
SetLimit(val int) SetLimit(val int)
GetLabel() string Label() string
SetLabel(val string) SetLabel(val string)
GetGain() float64 Gain() float64
SetGain(val float32) SetGain(val float64)
GetMc() bool Mc() bool
SetMc(val bool) SetMc(val bool)
GetComp() float64 Audibility() float64
SetComp(val float32) SetAudibility(val float64)
GetGate() float64 PanX() float64
SetGate(val float32) SetPanX(val float64)
GetAudibility() float64 PanY() float64
SetAudibility(val float32) 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 GainLayer() []gainLayer
Levels() *levels Levels() *levels
t_outputs iOutputs
} }
// strip represents a strip channel // strip represents a strip channel
type strip struct { type strip struct {
iRemote iRemote
outputs outputs
eQ *eQ
comp *comp
gate *gate
denoiser *denoiser
gainLayer []gainLayer gainLayer []gainLayer
levels levels *levels
} }
// GetMute returns the value of the Mute parameter // Mute returns the value of the Mute parameter
func (s *strip) GetMute() bool { func (s *strip) Mute() bool {
return s.getter_bool("Mute") return s.getter_bool("Mute")
} }
@ -49,8 +73,8 @@ func (s *strip) SetMute(val bool) {
s.setter_bool("Mute", val) s.setter_bool("Mute", val)
} }
// GetMono returns the value of the Mono parameter // Mono returns the value of the Mono parameter
func (s *strip) GetMono() bool { func (s *strip) Mono() bool {
return s.getter_bool("Mono") return s.getter_bool("Mono")
} }
@ -59,8 +83,8 @@ func (s *strip) SetMono(val bool) {
s.setter_bool("Mono", val) s.setter_bool("Mono", val)
} }
// GetSolo returns the value of the Solo parameter // Solo returns the value of the Solo parameter
func (s *strip) GetSolo() bool { func (s *strip) Solo() bool {
return s.getter_bool("Solo") return s.getter_bool("Solo")
} }
@ -69,8 +93,8 @@ func (s *strip) SetSolo(val bool) {
s.setter_bool("Solo", val) s.setter_bool("Solo", val)
} }
// GetLimit returns the value of the Limit parameter // Limit returns the value of the Limit parameter
func (s *strip) GetLimit() int { func (s *strip) Limit() int {
return s.getter_int("Limit") return s.getter_int("Limit")
} }
@ -79,8 +103,8 @@ func (s *strip) SetLimit(val int) {
s.setter_int("Limit", val) s.setter_int("Limit", val)
} }
// GetLabel returns the value of the Label parameter // Label returns the value of the Label parameter
func (s *strip) GetLabel() string { func (s *strip) Label() string {
return s.getter_string("Label") return s.getter_string("Label")
} }
@ -89,209 +113,514 @@ func (s *strip) SetLabel(val string) {
s.setter_string("Label", val) s.setter_string("Label", val)
} }
// GetGain returns the value of the Gain parameter // Gain returns the value of the Gain parameter
func (s *strip) GetGain() float64 { func (s *strip) Gain() float64 {
return s.getter_float("Gain") return s.getter_float("Gain")
} }
// SetGain sets the value of the Gain parameter // 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) s.setter_float("Gain", val)
} }
// PanX returns the value of the Pan_X parameter
func (s *strip) PanX() float64 {
return s.getter_float("Pan_x")
}
// SetPanX sets the value of the Pan_X parameter
func (s *strip) SetPanX(val float64) {
s.setter_float("Pan_x", val)
}
// PanY returns the value of the Pan_Y parameter
func (s *strip) PanY() float64 {
return s.getter_float("Pan_y")
}
// SetPanY sets the value of the Pan_Y parameter
func (s *strip) SetPanY(val float64) {
s.setter_float("Pan_y", val)
}
// Eq returns the eQ field
func (s *strip) Eq() *eQ {
return s.eQ
}
// GainLayer returns the gainlayer field // GainLayer returns the gainlayer field
func (s *strip) GainLayer() []gainLayer { func (s *strip) GainLayer() []gainLayer {
return s.gainLayer return s.gainLayer
} }
// Levels returns the gainlayer field // Levels returns the levels field
func (s *strip) Levels() *levels { 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 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) o := newOutputs(fmt.Sprintf("strip[%d]", i), i)
e := newEq(fmt.Sprintf("strip[%d].EQ", i), i)
c := newComp(i)
g := newGate(i)
d := newDenoiser(i)
gl := make([]gainLayer, 8) gl := make([]gainLayer, 8)
for j := 0; j < 8; j++ { for j := 0; j < 8; j++ {
gl[j] = newGainLayer(i, j) gl[j] = newGainLayer(i, j)
} }
l := newStripLevels(i, k) l := newStripLevels(i, k)
ps := physicalStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, gl, l}} ps := PhysicalStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, e, c, g, d, gl, l}}
return t_strip(&ps) return &ps
} }
// implement fmt.stringer interface in fmt // String implements fmt.stringer interface
func (p *physicalStrip) String() string { func (p *PhysicalStrip) String() string {
return fmt.Sprintf("PhysicalStrip%d", p.index) return fmt.Sprintf("PhysicalStrip%d", p.index)
} }
// GetComp returns the value of the Comp parameter // Audibility returns the value of the Audibility parameter
func (p *physicalStrip) GetComp() float64 { func (p *PhysicalStrip) Audibility() float64 {
return p.getter_float("Comp")
}
// SetComp sets the value of the Comp parameter
func (p *physicalStrip) SetComp(val float32) {
p.setter_float("Comp", val)
}
// GetGate returns the value of the Gate parameter
func (p *physicalStrip) GetGate() float64 {
return p.getter_float("Gate")
}
// SetGate sets the value of the Gate parameter
func (p *physicalStrip) SetGate(val float32) {
p.setter_float("Gate", val)
}
// GetAudibility returns the value of the Audibility parameter
func (p *physicalStrip) GetAudibility() float64 {
return p.getter_float("Audibility") return p.getter_float("Audibility")
} }
// SetAudibility sets the value of the Audibility parameter // SetAudibility sets the value of the Audibility parameter
func (p *physicalStrip) SetAudibility(val float32) { func (p *PhysicalStrip) SetAudibility(val float64) {
p.setter_float("Audibility", val) p.setter_float("Audibility", val)
} }
// GetMc panics reason invalid parameter // Mc logs a warning reason invalid parameter
func (p *physicalStrip) GetMc() bool { // it always returns zero value
panic("invalid parameter MC for physicalStrip") func (p *PhysicalStrip) Mc() bool {
log.Warn("invalid parameter MC for physicalStrip")
return false
} }
// SetMc panics reason invalid parameter // SetMc logs a warning reason invalid parameter
func (p *physicalStrip) SetMc(val bool) { func (p *PhysicalStrip) SetMc(val bool) {
panic("invalid parameter MC for physicalStrip") 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 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) o := newOutputs(fmt.Sprintf("strip[%d]", i), i)
e := newEq(fmt.Sprintf("strip[%d].EQ", i), i)
c := newComp(i)
g := newGate(i)
d := newDenoiser(i)
gl := make([]gainLayer, 8) gl := make([]gainLayer, 8)
for j := 0; j < 8; j++ { for j := 0; j < 8; j++ {
gl[j] = newGainLayer(i, j) gl[j] = newGainLayer(i, j)
} }
l := newStripLevels(i, k) l := newStripLevels(i, k)
vs := virtualStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, gl, l}} vs := VirtualStrip{strip{iRemote{fmt.Sprintf("strip[%d]", i), i}, o, e, c, g, d, gl, l}}
return t_strip(&vs) return &vs
} }
// implement fmt.stringer interface in fmt // String implements fmt.stringer interface
func (v *virtualStrip) String() string { func (v *VirtualStrip) String() string {
return fmt.Sprintf("VirtualStrip%d", v.index) return fmt.Sprintf("VirtualStrip%d", v.index)
} }
// GetMc returns the value of the MC parameter // Comp returns the comp field
func (v *virtualStrip) GetMc() bool { 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") return v.getter_bool("MC")
} }
// SetMc sets the value of the MC parameter // SetMc sets the value of the MC parameter
func (v *virtualStrip) SetMc(val bool) { func (v *VirtualStrip) SetMc(val bool) {
v.setter_bool("MC", val) v.setter_bool("MC", val)
} }
// GetComp panics reason invalid parameter // ColorX logs a warning reason invalid parameter
func (v *virtualStrip) GetComp() float64 { // it always returns zero value
panic("invalid parameter Comp for virtualStrip") func (v *VirtualStrip) ColorX() float64 {
log.Warn("invalid parameter ColorX for virtualStrip")
return 0
} }
// SetComp panics reason invalid parameter // SetColorX logs a warning reason invalid parameter
func (v *virtualStrip) SetComp(val float32) { func (v *VirtualStrip) SetColorX(val float64) {
panic("invalid parameter Comp for virtualStrip") log.Warn("invalid parameter ColorX for virtualStrip")
} }
// GetGate panics reason invalid parameter // ColorY logs a warning reason invalid parameter
func (v *virtualStrip) GetGate() float64 { // it always returns zero value
panic("invalid parameter Gate for virtualStrip") func (v *VirtualStrip) ColorY() float64 {
log.Warn("invalid parameter ColorY for virtualStrip")
return 0
} }
// SetGate panics reason invalid parameter // SetColorY logs a warning reason invalid parameter
func (v *virtualStrip) SetGate(val float32) { func (v *VirtualStrip) SetColorY(val float64) {
panic("invalid parameter Gate for virtualStrip") log.Warn("invalid parameter ColorY for virtualStrip")
} }
// GetAudibility panics reason invalid parameter // FxX logs a warning reason invalid parameter
func (v *virtualStrip) GetAudibility() float64 { // it always returns zero value
panic("invalid parameter Audibility for virtualStrip") func (v *VirtualStrip) FxX() float64 {
log.Warn("invalid parameter FxX for virtualStrip")
return 0
} }
// SetAudibility panics reason invalid parameter // SetFxX logs a warning reason invalid parameter
func (v *virtualStrip) SetAudibility(val float32) { func (v *VirtualStrip) SetFxX(val float64) {
panic("invalid parameter Audibility for virtualStrip") 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 { type gainLayer struct {
iRemote iRemote
index int index int
} }
// newGainLayer returns a gainlayer struct
func newGainLayer(i, j int) gainLayer { func newGainLayer(i, j int) gainLayer {
return gainLayer{iRemote{fmt.Sprintf("strip[%d]", i), i}, j} return gainLayer{iRemote{fmt.Sprintf("strip[%d]", i), i}, j}
} }
// Get gets the gain value for a single gainlayer
func (gl *gainLayer) Get() float64 { func (gl *gainLayer) Get() float64 {
return gl.getter_float(fmt.Sprintf("gainlayer[%d]", gl.index)) 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) 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 init int
var os int var os int
if i < k.physIn { if i < k.PhysIn {
init = i * 2 init = i * 2
os = 2 os = 2
} else { } else {
init = (k.physIn * 2) + ((i - k.physIn) * 8) init = (k.PhysIn * 2) + ((i - k.PhysIn) * 8)
os = 8 os = 8
} }
return levels{iRemote{fmt.Sprintf("strip[%d]", i), i}, k, init, os, "strip"} return &levels{iRemote{fmt.Sprintf("strip[%d]", i), i}, k, init, os, "strip"}
} }
func (l *levels) PreFader() []float32 { // PreFader returns the level values for this strip, PREFADER mode
func (l *levels) PreFader() []float64 {
_levelCache.stripMode = 0 _levelCache.stripMode = 0
var levels []float32 levels := make([]float64, l.offset)
for i := l.init; i < l.init+l.offset; i++ { for i := range levels {
levels = append(levels, l.convertLevel(_levelCache.stripLevels[i])) levels[i] = convertLevel(_levelCache.stripLevels[l.init+i])
} }
return levels 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 _levelCache.stripMode = 1
var levels []float32 levels := make([]float64, l.offset)
for i := l.init; i < l.init+l.offset; i++ { for i := range levels {
levels = append(levels, l.convertLevel(_levelCache.stripLevels[i])) levels[i] = convertLevel(_levelCache.stripLevels[l.init+i])
} }
return levels 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 _levelCache.stripMode = 2
var levels []float32 levels := make([]float64, l.offset)
for i := l.init; i < l.init+l.offset; i++ { for i := range levels {
levels = append(levels, l.convertLevel(_levelCache.stripLevels[i])) levels[i] = convertLevel(_levelCache.stripLevels[l.init+i])
} }
return levels 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 package voicemeeter_test
import ( import (
"os" "bytes"
"testing" "testing"
"time"
"github.com/onyx-and-iris/voicemeeter-api-go" log "github.com/sirupsen/logrus"
"github.com/onyx-and-iris/voicemeeter/v2"
) )
var ( var (
vmRem = voicemeeter.GetRemote("potato") logstring bytes.Buffer
vm, err = voicemeeter.NewRemote("potato", 30)
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
vmRem.Login() if err != nil {
code := m.Run() log.Fatal(err)
vmRem.Logout()
os.Exit(code)
}
func sync() {
time.Sleep(30 * time.Millisecond)
for vmRem.Pdirty() || vmRem.Mdirty() {
} }
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) { func TestStrip0Mute(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vmRem.Strip[0].SetMute(true) vm.Strip[0].SetMute(true)
sync() t.Run("Should return true when Strip[0].SetMute(true)", func(t *testing.T) {
t.Run("Should return true when SetMute(true)", func(t *testing.T) { assert.True(t, vm.Strip[0].Mute())
assert.True(t, vmRem.Strip[0].GetMute())
}) })
vmRem.Strip[0].SetMute(false) vm.Strip[0].SetMute(false)
sync() t.Run("Should return false when Strip[0].SetMute(false)", func(t *testing.T) {
t.Run("Should return false when SetMute(false)", func(t *testing.T) { assert.False(t, vm.Strip[0].Mute())
assert.False(t, vmRem.Strip[0].GetMute())
}) })
} }
func TestStrip3A1(t *testing.T) { func TestStrip3A1(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vmRem.Strip[3].SetA1(true) vm.Strip[3].SetA1(true)
sync() t.Run("Should return true when Strip[3].SetA1(true)", func(t *testing.T) {
t.Run("Should return true when SetA1(true)", func(t *testing.T) { assert.True(t, vm.Strip[3].A1())
assert.True(t, vmRem.Strip[3].GetA1())
}) })
vmRem.Strip[3].SetA1(false) vm.Strip[3].SetA1(false)
sync() t.Run("Should return false when Strip[3].SetA1(false)", func(t *testing.T) {
t.Run("Should return false when SetA1(false)", func(t *testing.T) { assert.False(t, vm.Strip[3].A1())
assert.False(t, vmRem.Strip[3].GetA1())
}) })
} }
func TestStrip2Limit(t *testing.T) { func TestStrip2Limit(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vmRem.Strip[2].SetLimit(-8) vm.Strip[2].SetLimit(-8)
sync() t.Run("Should return -8 when Strip[2].SetLimit(-8)", func(t *testing.T) {
t.Run("Should return -8 when SetLimit(-8)", func(t *testing.T) { assert.Equal(t, vm.Strip[2].Limit(), -8)
assert.Equal(t, vmRem.Strip[2].GetLimit(), -8)
}) })
vmRem.Strip[2].SetLimit(-32) vm.Strip[2].SetLimit(-32)
sync() t.Run("Should return -32 when Strip[2].SetLimit(-32)", func(t *testing.T) {
t.Run("Should return -32 when SetLimit(-8)", func(t *testing.T) { assert.Equal(t, vm.Strip[2].Limit(), -32)
assert.Equal(t, vmRem.Strip[2].GetLimit(), -32)
}) })
} }
func TestStrip4Label(t *testing.T) { func TestStrip4Label(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vmRem.Strip[4].SetLabel("test0") vm.Strip[4].SetLabel("test0")
sync() t.Run("Should return test0 when Strip[4].SetLabel('test0')", func(t *testing.T) {
t.Run("Should return test0 when SetLimit('test0')", func(t *testing.T) { assert.Equal(t, "test0", vm.Strip[4].Label())
assert.Equal(t, "test0", vmRem.Strip[4].GetLabel())
}) })
vmRem.Strip[4].SetLabel("test1") vm.Strip[4].SetLabel("test1")
sync() t.Run("Should return test1 when Strip[4].SetLabel('test1')", func(t *testing.T) {
t.Run("Should return test1 when SetLimit('test1')", func(t *testing.T) { assert.Equal(t, "test1", vm.Strip[4].Label())
assert.Equal(t, "test1", vmRem.Strip[4].GetLabel())
}) })
} }
func TestStrip5Gain(t *testing.T) { func TestStrip4Gain(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vmRem.Strip[4].SetGain(-20.8) vm.Strip[4].SetGain(-20.8)
sync() t.Run("Should return -20.8 when Strip[4].SetGain(-20.8)", func(t *testing.T) {
t.Run("Should return -20.8 when SetGain(-20.8)", func(t *testing.T) { assert.Equal(t, vm.Strip[4].Gain(), -20.8)
assert.Equal(t, vmRem.Strip[4].GetGain(), -20.8)
}) })
vmRem.Strip[4].SetGain(-3.6) vm.Strip[4].SetGain(-3.6)
sync() t.Run("Should return -3.6 when Strip[4].SetGain(-3.6)", func(t *testing.T) {
t.Run("Should return -3.6 when SetGain(-3.6)", func(t *testing.T) { assert.Equal(t, vm.Strip[4].Gain(), -3.6)
assert.Equal(t, vmRem.Strip[4].GetGain(), -3.6)
}) })
} }
func TestStrip3Comp(t *testing.T) { func TestStrip4CompKnob(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vmRem.Strip[4].SetComp(8.1) vm.Strip[4].Comp().SetKnob(8.1)
sync() t.Run("Should return 8.1 when Strip[4].Comp().SetKnob(8.1)", func(t *testing.T) {
t.Run("Should return 8.1 when SetGain(8.1)", func(t *testing.T) { assert.Equal(t, vm.Strip[4].Comp().Knob(), 8.1)
assert.Equal(t, vmRem.Strip[4].GetComp(), 8.1)
}) })
vmRem.Strip[4].SetComp(1.6) vm.Strip[4].Comp().SetKnob(1.6)
sync() t.Run("Should return 1.6 when Strip[4].Comp().SetKnob(1.6)", func(t *testing.T) {
t.Run("Should return 1.6 when SetGain(1.6)", func(t *testing.T) { assert.Equal(t, vm.Strip[4].Comp().Knob(), 1.6)
assert.Equal(t, vmRem.Strip[4].GetComp(), 1.6) })
}
func TestStrip0CompGainIn(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[0].Comp().SetGainIn(3.4)
t.Run("Should return 3.4 when Strip[0].Comp().SetGainIn(3.4)", func(t *testing.T) {
assert.Equal(t, vm.Strip[0].Comp().GainIn(), 3.4)
})
vm.Strip[0].Comp().SetGainIn(-19.3)
t.Run("Should return -19.3 when Strip[0].Comp().SetGainIn(-19.3)", func(t *testing.T) {
assert.Equal(t, vm.Strip[0].Comp().GainIn(), -19.3)
})
}
func TestStrip3GateKnob(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[3].Gate().SetKnob(8.1)
t.Run("Should return 8.1 when Strip[3].Gate().SetKnob(8.1)", func(t *testing.T) {
assert.Equal(t, vm.Strip[3].Gate().Knob(), 8.1)
})
vm.Strip[3].Gate().SetKnob(1.6)
t.Run("Should return 1.6 when Strip[3].Gate().SetKnob(1.6)", func(t *testing.T) {
assert.Equal(t, vm.Strip[3].Gate().Knob(), 1.6)
})
}
func TestStrip0CompAttack(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[0].Comp().SetAttack(3.4)
t.Run("Should return 3.4 when Strip[0].Comp().SetAttack(3.4)", func(t *testing.T) {
assert.Equal(t, vm.Strip[0].Comp().Attack(), 3.4)
})
vm.Strip[0].Comp().SetAttack(190.3)
t.Run("Should return 190.3 when Strip[0].Comp().SetAttack(190.3)", func(t *testing.T) {
assert.Equal(t, vm.Strip[0].Comp().Attack(), 190.3)
})
}
func TestStrip4Denoiser(t *testing.T) {
//t.Skip("skipping test")
vm.Strip[4].Denoiser().SetKnob(2.1)
t.Run("Should return 2.1 when Strip[4].Denoiser().SetKnob(2.1)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].Denoiser().Knob(), 2.1)
})
vm.Strip[4].Denoiser().SetKnob(5.6)
t.Run("Should return 5.6 when Strip[4].Denoiser().SetKnob(5.6)", func(t *testing.T) {
assert.Equal(t, vm.Strip[4].Denoiser().Knob(), 5.6)
}) })
} }
func TestStrip5Mc(t *testing.T) { func TestStrip5Mc(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vmRem.Strip[5].SetMc(true) vm.Strip[5].SetMc(true)
sync() t.Run("Should return true when Strip[5].SetMc(true)", func(t *testing.T) {
t.Run("Should return true when SetMc(true)", func(t *testing.T) { assert.True(t, vm.Strip[5].Mc())
assert.True(t, vmRem.Strip[5].GetMc())
}) })
vmRem.Strip[5].SetMc(false) vm.Strip[5].SetMc(false)
sync() t.Run("Should return false when Strip[5].SetMc(false)", func(t *testing.T) {
t.Run("Should return false when SetMc(false)", func(t *testing.T) { assert.False(t, vm.Strip[5].Mc())
assert.False(t, vmRem.Strip[5].GetMc())
}) })
} }
func TestStrip2GainLayer3(t *testing.T) { func TestStrip2GainLayer3(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vmRem.Strip[2].GainLayer()[3].Set(-18.3) vm.Strip[2].GainLayer()[3].Set(-18.3)
sync() t.Run("Should return -18.3 when Strip[2].GainLayer()[3].Set(-18.3)", func(t *testing.T) {
t.Run("Should return -18.3 when SetMc(true)", func(t *testing.T) { assert.Equal(t, vm.Strip[2].GainLayer()[3].Get(), -18.3)
assert.Equal(t, vmRem.Strip[2].GainLayer()[3].Get(), -18.3)
}) })
vmRem.Strip[2].GainLayer()[3].Set(-25.6) vm.Strip[2].GainLayer()[3].Set(-25.6)
sync() t.Run("Should return -25.6 when Strip[2].GainLayer()[3].Set(-25.6)", func(t *testing.T) {
t.Run("Should return -25.6 when SetMc(true)", func(t *testing.T) { assert.Equal(t, vm.Strip[2].GainLayer()[3].Get(), -25.6)
assert.Equal(t, vmRem.Strip[2].GainLayer()[3].Get(), -25.6)
}) })
} }
func TestBus3Eq(t *testing.T) { func TestBus3EqOn(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vmRem.Bus[3].SetEq(true) vm.Bus[3].Eq().SetOn(true)
sync() t.Run("Should return true when Bus[3].Eq().SetOn(true)", func(t *testing.T) {
t.Run("Should return true when SetEq(true)", func(t *testing.T) { assert.True(t, vm.Bus[3].Eq().On())
assert.True(t, vmRem.Bus[3].GetEq())
}) })
vmRem.Bus[3].SetEq(false) vm.Bus[3].Eq().SetOn(false)
sync() t.Run("Should return false when Bus[3].SetEq(false)", func(t *testing.T) {
t.Run("Should return false when SetEq(false)", func(t *testing.T) { assert.False(t, vm.Bus[3].Eq().On())
assert.False(t, vmRem.Bus[3].GetEq())
}) })
} }
func TestBus4Label(t *testing.T) { func TestBus4Label(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vmRem.Bus[4].SetLabel("test0") vm.Bus[4].SetLabel("test0")
sync() t.Run("Should return test0 when Bus[4].SetLabel('test0')", func(t *testing.T) {
t.Run("Should return test0 when SetEq('test0')", func(t *testing.T) { assert.Equal(t, "test0", vm.Bus[4].Label())
assert.Equal(t, "test0", vmRem.Bus[4].GetLabel())
}) })
vmRem.Bus[4].SetLabel("test1") vm.Bus[4].SetLabel("test1")
sync() t.Run("Should return test1 when Bus[4].SetLabel('test1')", func(t *testing.T) {
t.Run("Should return test1 when SetEq('test1')", func(t *testing.T) { assert.Equal(t, "test1", vm.Bus[4].Label())
assert.Equal(t, "test1", vmRem.Bus[4].GetLabel())
}) })
} }
func TestBus3ModeAmix(t *testing.T) { func TestBus3ModeAmix(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vmRem.Bus[3].Mode().SetAmix(true) vm.Bus[3].Mode().SetAmix(true)
sync() t.Run("Should return true when Bus[3].Mode().SetAmix(true)", func(t *testing.T) {
t.Run("Should return true when Mode().SetAmix(true)", func(t *testing.T) { assert.True(t, vm.Bus[3].Mode().Amix())
assert.True(t, vmRem.Bus[3].Mode().GetAmix())
}) })
} }
func TestVbanInStream0On(t *testing.T) { func TestVbanInStream0On(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vmRem.Vban.InStream[0].SetOn(true) vm.Vban.InStream[0].SetOn(true)
sync() t.Run("Should return true when Vban.InStream[0].SetOn(true)", func(t *testing.T) {
t.Run("Should return true when SetOn(true)", func(t *testing.T) { assert.True(t, vm.Vban.InStream[0].On())
assert.True(t, vmRem.Vban.InStream[0].GetOn())
}) })
vmRem.Vban.InStream[0].SetOn(false) vm.Vban.InStream[0].SetOn(false)
sync() t.Run("Should return false when Vban.InStream[0].SetOn(false)", func(t *testing.T) {
t.Run("Should return false when SetOn(false)", func(t *testing.T) { assert.False(t, vm.Vban.InStream[0].On())
assert.False(t, vmRem.Vban.InStream[0].GetOn())
}) })
} }
func TestVbanOutStream6On(t *testing.T) { func TestVbanOutStream6On(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vmRem.Vban.OutStream[6].SetOn(true) vm.Vban.OutStream[6].SetOn(true)
sync() t.Run("Should return true when Vban.OutStream[6].SetOn(true)", func(t *testing.T) {
t.Run("Should return true when SetOn(true)", func(t *testing.T) { assert.True(t, vm.Vban.OutStream[6].On())
assert.True(t, vmRem.Vban.OutStream[6].GetOn())
}) })
vmRem.Vban.OutStream[6].SetOn(false) vm.Vban.OutStream[6].SetOn(false)
sync() t.Run("Should return false when Vban.OutStream[6].SetOn(false)", func(t *testing.T) {
t.Run("Should return false when SetOn(false)", func(t *testing.T) { assert.False(t, vm.Vban.OutStream[6].On())
assert.False(t, vmRem.Vban.OutStream[6].GetOn())
}) })
} }
func TestVbanOutStream3Name(t *testing.T) { func TestVbanOutStream3Name(t *testing.T) {
t.Skip("skipping test") //t.Skip("skipping test")
vmRem.Vban.OutStream[3].SetName("test0") vm.Vban.OutStream[3].SetName("test0")
sync() t.Run("Should return test0 when Vban.OutStream[3].SetName('test0')", func(t *testing.T) {
t.Run("Should return test0 when SetName('test0')", func(t *testing.T) { assert.Equal(t, "test0", vm.Vban.OutStream[3].Name())
assert.Equal(t, "test0", vmRem.Vban.OutStream[3].GetName())
}) })
vmRem.Vban.OutStream[3].SetName("test1") vm.Vban.OutStream[3].SetName("test1")
sync() t.Run("Should return test1 when Vban.OutStream[3].SetName('test1')", func(t *testing.T) {
t.Run("Should return test1 when SetName('test1')", func(t *testing.T) { assert.Equal(t, "test1", vm.Vban.OutStream[3].Name())
assert.Equal(t, "test1", vmRem.Vban.OutStream[3].GetName())
}) })
} }
func TestVbanInStream4Bit(t *testing.T) { func TestVbanInStream4Bit(t *testing.T) {
t.Skip("skipping test") //t.Skip("skipping test")
t.Run("Should panic when instream SetBit(16)", func(t *testing.T) { vm.Vban.InStream[4].SetBit(16)
defer func() { t.Run("Should log 'bit is readonly for vban instreams' when instream Vban.InStream[4].SetBit(16)", func(t *testing.T) {
if r := recover(); r == nil { assert.Contains(t, logstring.String(), "bit is readonly for vban instreams")
t.Error("expected panic")
}
}()
vmRem.Vban.InStream[4].SetBit(16)
}) })
} }
func TestVbanOutStream4Bit(t *testing.T) { func TestVbanOutStream4Bit(t *testing.T) {
//t.Skip("skipping test") //t.Skip("skipping test")
vmRem.Vban.OutStream[4].SetBit(16) vm.Vban.OutStream[4].SetBit(16)
sync() t.Run("Should return 16 when Vban.OutStream[4].SetBit(16)", func(t *testing.T) {
t.Run("Should return 16 when SetBit(16)", func(t *testing.T) { assert.Equal(t, vm.Vban.OutStream[4].Bit(), 16)
assert.Equal(t, vmRem.Vban.OutStream[4].GetBit(), 16)
}) })
vmRem.Vban.OutStream[4].SetBit(24) vm.Vban.OutStream[4].SetBit(24)
sync() t.Run("Should return 24 when Vban.OutStream[4].SetBit(24)", func(t *testing.T) {
t.Run("Should return 24 when SetBit(24)", func(t *testing.T) { assert.Equal(t, vm.Vban.OutStream[4].Bit(), 24)
assert.Equal(t, vmRem.Vban.OutStream[4].GetBit(), 24)
}) })
} }

View File

@ -1,6 +1,6 @@
Function RunTests { Function RunTests {
$run_int_tests = "go clean -testcache; go test -v ."
$run_ext_tests = "go clean -testcache; go test -v .\tests\" $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_ext_tests
Invoke-Expression $run_int_tests Invoke-Expression $run_int_tests

13
util.go
View File

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