mirror of
https://github.com/onyx-and-iris/voicemeeter.git
synced 2026-04-18 05:23:31 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6512b35155 | |||
| 5aabd0a343 | |||
| 0558e8f81d | |||
| 07018d1703 | |||
| 73627ddbf1 | |||
| dd55dd8fdf | |||
|
|
00d0be8055 | ||
| 418d04a08e | |||
| 8038670203 | |||
| 3319a8c4f4 | |||
| b777025f88 | |||
| 93d5e2db67 | |||
| a23a9f8598 | |||
| 195ee326a0 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -19,3 +19,7 @@ config.toml
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# work files
|
||||
go.work
|
||||
go.work.sum
|
||||
13
CHANGELOG.md
13
CHANGELOG.md
@@ -11,6 +11,19 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
|
||||
|
||||
- [x]
|
||||
|
||||
## [2.1.0] - 2024-07-01
|
||||
|
||||
### Added
|
||||
|
||||
- Added a configurable 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.
|
||||
|
||||
28
README.md
28
README.md
@@ -1,16 +1,14 @@
|
||||
[](https://pkg.go.dev/github.com/onyx-and-iris/voicemeeter)
|
||||
[](https://pkg.go.dev/github.com/onyx-and-iris/voicemeeter/v2)
|
||||
|
||||
# A Go Wrapper for Voicemeeter API
|
||||
|
||||
This package offers a Go interface for the Voicemeeter Remote C API.
|
||||
# A Go Wrapper for the Voicemeeter API
|
||||
|
||||
For an outline of past/future changes refer to: [CHANGELOG](CHANGELOG.md)
|
||||
|
||||
## Tested against
|
||||
|
||||
- Basic 1.0.8.8
|
||||
- Banana 2.0.6.8
|
||||
- Potato 3.0.2.8
|
||||
- Basic 1.1.1.1
|
||||
- Banana 2.1.1.1
|
||||
- Potato 3.1.1.1
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -23,7 +21,7 @@ Initialize your own module then `go get`
|
||||
|
||||
```
|
||||
go mod init github.com/x/y
|
||||
go get github.com/onyx-and-iris/voicemeeter
|
||||
go get github.com/onyx-and-iris/voicemeeter/v2
|
||||
```
|
||||
|
||||
## `Use`
|
||||
@@ -67,7 +65,7 @@ func vmConnect() (*voicemeeter.Remote, error) {
|
||||
}
|
||||
```
|
||||
|
||||
## `voicemeeter.NewRemote(<kindId>, <delay>)`
|
||||
## `voicemeeter.NewRemote(<kindId>, <delay>, opts ...Option)`
|
||||
|
||||
### `kindId`
|
||||
|
||||
@@ -83,6 +81,18 @@ Pass a delay in milliseconds to force the getters to wait for dirty parameters t
|
||||
|
||||
Useful if not listening for event updates.
|
||||
|
||||
### `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))`
|
||||
|
||||
### `voicemeeter.WithBits(timeout int)`
|
||||
|
||||
Set a login timeout, defaults to 2 seconds. For example to set it to 1s:
|
||||
|
||||
`voicemeeter.NewRemote("banana", 20, voicemeeter.WithBits(1))`
|
||||
|
||||
## `Remote Type`
|
||||
|
||||
#### `vm.Strip`
|
||||
|
||||
34
base.go
34
base.go
@@ -2,8 +2,10 @@ package voicemeeter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
@@ -45,16 +47,29 @@ var (
|
||||
// login logs into the API,
|
||||
// attempts to launch Voicemeeter if it's not running,
|
||||
// initializes dirty parameters.
|
||||
func login(kindId string) error {
|
||||
func login(kindId string, timeout, bits int) error {
|
||||
res, _, _ := vmLogin.Call()
|
||||
if res == 1 {
|
||||
runVoicemeeter(kindId)
|
||||
time.Sleep(time.Second)
|
||||
runVoicemeeter(kindId, bits)
|
||||
} else if res != 0 {
|
||||
err := fmt.Errorf("VBVMR_Login returned %d", res)
|
||||
return err
|
||||
}
|
||||
log.Info("Logged into Voicemeeter ", kindId)
|
||||
|
||||
var ver_s string
|
||||
start := time.Now()
|
||||
var err error
|
||||
for time.Since(start).Seconds() < float64(timeout) {
|
||||
time.Sleep(time.Duration(100) * time.Millisecond)
|
||||
if ver_s, err = getVersion(); err == nil {
|
||||
log.Infof("Logged into Voicemeeter %s v%s", kindMap[kindId], ver_s)
|
||||
log.Debugf("Log in time: %.2f", time.Since(start).Seconds())
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return errors.New("timeout logging into the API")
|
||||
}
|
||||
clear()
|
||||
return nil
|
||||
}
|
||||
@@ -68,18 +83,22 @@ func logout(kindId string) error {
|
||||
err := fmt.Errorf("VBVMR_Logout returned %d", int32(res))
|
||||
return err
|
||||
}
|
||||
log.Info("Logged out of Voicemeeter ", kindId)
|
||||
log.Infof("Logged out of Voicemeeter %s", kindMap[kindId])
|
||||
return nil
|
||||
}
|
||||
|
||||
// runVoicemeeter attempts to launch a Voicemeeter GUI of a kind.
|
||||
func runVoicemeeter(kindId string) error {
|
||||
func runVoicemeeter(kindId string, bits int) error {
|
||||
vals := map[string]uint64{
|
||||
"basic": 1,
|
||||
"banana": 2,
|
||||
"potato": 3,
|
||||
}
|
||||
res, _, _ := vmRunvm.Call(uintptr(vals[kindId]))
|
||||
val := vals[kindId]
|
||||
if strings.Contains(runtime.GOARCH, "64") && bits == 64 {
|
||||
val += 3
|
||||
}
|
||||
res, _, _ := vmRunvm.Call(uintptr(val))
|
||||
if int32(res) != 0 {
|
||||
err := fmt.Errorf("VBVMR_RunVoicemeeter returned %d", int32(res))
|
||||
return err
|
||||
@@ -93,6 +112,7 @@ func getVersion() (string, error) {
|
||||
res, _, _ := vmGetvmVersion.Call(uintptr(unsafe.Pointer(&ver)))
|
||||
if int32(res) != 0 {
|
||||
err := fmt.Errorf("VBVMR_GetVoicemeeterVersion returned %d", int32(res))
|
||||
log.Error(err.Error())
|
||||
return "", err
|
||||
}
|
||||
v1 := (ver & 0xFF000000) >> 24
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
module main
|
||||
module hotkeys
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203
|
||||
github.com/onyx-and-iris/voicemeeter v1.10.1
|
||||
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
)
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/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 v1.10.1 h1:kpPN/cTYe1JG2aLUD+/m/bMUbp7wXrqrk1fL+sV8yko=
|
||||
github.com/onyx-and-iris/voicemeeter v1.10.1/go.mod h1:LayUoN/MWSqKXSOGQ7AcLvwoefsL+zQ9CjncLs3WqsU=
|
||||
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0 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=
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module main
|
||||
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 v1.10.1
|
||||
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
)
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/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 v1.10.1 h1:kpPN/cTYe1JG2aLUD+/m/bMUbp7wXrqrk1fL+sV8yko=
|
||||
github.com/onyx-and-iris/voicemeeter v1.10.1/go.mod h1:LayUoN/MWSqKXSOGQ7AcLvwoefsL+zQ9CjncLs3WqsU=
|
||||
github.com/onyx-and-iris/voicemeeter/v2 v2.0.0 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=
|
||||
|
||||
@@ -20,8 +20,7 @@ func newObserver(vm *voicemeeter.Remote) *observer {
|
||||
return &observer{vm, make(chan string)}
|
||||
}
|
||||
|
||||
// OnUpdate satisfies the observer interface defined in publisher.go
|
||||
// for each event type an action is triggered when the event occurs.
|
||||
// Listen registers the observer channel and listens for updates.
|
||||
func (o observer) Listen() {
|
||||
o.vm.Register(o.events)
|
||||
|
||||
@@ -47,13 +46,14 @@ 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, registers observer for updates
|
||||
// runs updates for 30 seconds and then deregisters observer.
|
||||
// main connects to Voiceemeter and runs a single observer for 30 seconds.
|
||||
func main() {
|
||||
vm, err := vmConnect()
|
||||
if err != nil {
|
||||
@@ -67,6 +67,7 @@ func main() {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
10
recorder.go
10
recorder.go
@@ -51,3 +51,13 @@ func (r *recorder) Rew() {
|
||||
func (r *recorder) Loop(val bool) {
|
||||
r.setter_bool("Mode.Loop", val)
|
||||
}
|
||||
|
||||
// Gain returns the value of the Gain parameter
|
||||
func (r *recorder) Gain() float64 {
|
||||
return r.getter_float("Gain")
|
||||
}
|
||||
|
||||
// SetGain sets the value of the Gain parameter
|
||||
func (r *recorder) SetGain(val float64) {
|
||||
r.setter_float("Gain", val)
|
||||
}
|
||||
|
||||
67
remote.go
67
remote.go
@@ -21,6 +21,8 @@ type Remote struct {
|
||||
Midi *midi_t
|
||||
|
||||
pooler *pooler
|
||||
timeout int
|
||||
bits int
|
||||
}
|
||||
|
||||
// String implements the fmt.stringer interface
|
||||
@@ -31,7 +33,7 @@ func (r *Remote) String() string {
|
||||
// Login logs into the API
|
||||
// then it intializes the pooler
|
||||
func (r *Remote) Login() error {
|
||||
err := login(r.Kind.Name)
|
||||
err := login(r.Kind.Name, r.timeout, r.bits)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -57,12 +59,10 @@ func (r *Remote) InitPooler() {
|
||||
|
||||
// Run launches the Voicemeeter GUI for a kind.
|
||||
func (r *Remote) Run(kindId string) error {
|
||||
err := runVoicemeeter(kindId)
|
||||
err := runVoicemeeter(kindId, r.bits)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
clear()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -173,6 +173,7 @@ type remoteBuilder interface {
|
||||
makeDevice() remoteBuilder
|
||||
makeRecorder() remoteBuilder
|
||||
makeMidi() remoteBuilder
|
||||
setDefaults() remoteBuilder
|
||||
Build() remoteBuilder
|
||||
Get() *Remote
|
||||
}
|
||||
@@ -212,7 +213,7 @@ func (b *genericBuilder) setKind() remoteBuilder {
|
||||
// makeStrip makes a strip slice and assigns it to remote.Strip
|
||||
// []iStrip comprises of both physical and virtual strip types
|
||||
func (b *genericBuilder) makeStrip() remoteBuilder {
|
||||
log.Info("building strip")
|
||||
log.Debug("building strip")
|
||||
strip := make([]iStrip, b.k.NumStrip())
|
||||
for i := 0; i < b.k.NumStrip(); i++ {
|
||||
if i < b.k.PhysIn {
|
||||
@@ -228,7 +229,7 @@ func (b *genericBuilder) makeStrip() remoteBuilder {
|
||||
// makeBus makes a bus slice and assigns it to remote.Bus
|
||||
// []t_bus comprises of both physical and virtual bus types
|
||||
func (b *genericBuilder) makeBus() remoteBuilder {
|
||||
log.Info("building bus")
|
||||
log.Debug("building bus")
|
||||
bus := make([]iBus, b.k.NumBus())
|
||||
for i := 0; i < b.k.NumBus(); i++ {
|
||||
if i < b.k.PhysOut {
|
||||
@@ -243,7 +244,7 @@ func (b *genericBuilder) makeBus() remoteBuilder {
|
||||
|
||||
// makeButton makes a button slice and assigns it to remote.Button
|
||||
func (b *genericBuilder) makeButton() remoteBuilder {
|
||||
log.Info("building button")
|
||||
log.Debug("building button")
|
||||
button := make([]button, 80)
|
||||
for i := 0; i < 80; i++ {
|
||||
button[i] = newButton(i)
|
||||
@@ -254,39 +255,46 @@ func (b *genericBuilder) makeButton() remoteBuilder {
|
||||
|
||||
// makeCommand makes a command type and assigns it to remote.Command
|
||||
func (b *genericBuilder) makeCommand() remoteBuilder {
|
||||
log.Info("building command")
|
||||
log.Debug("building command")
|
||||
b.r.Command = newCommand()
|
||||
return b
|
||||
}
|
||||
|
||||
// makeVban makes a vban type and assigns it to remote.Vban
|
||||
func (b *genericBuilder) makeVban() remoteBuilder {
|
||||
log.Info("building vban")
|
||||
log.Debug("building vban")
|
||||
b.r.Vban = newVban(b.k)
|
||||
return b
|
||||
}
|
||||
|
||||
// makeDevice makes a device type and assigns it to remote.Device
|
||||
func (b *genericBuilder) makeDevice() remoteBuilder {
|
||||
log.Info("building device")
|
||||
log.Debug("building device")
|
||||
b.r.Device = newDevice()
|
||||
return b
|
||||
}
|
||||
|
||||
// makeRecorder makes a recorder type and assigns it to remote.Recorder
|
||||
func (b *genericBuilder) makeRecorder() remoteBuilder {
|
||||
log.Info("building recorder")
|
||||
log.Debug("building recorder")
|
||||
b.r.Recorder = newRecorder()
|
||||
return b
|
||||
}
|
||||
|
||||
// makeMidi makes a midi type and assigns it to remote.Midi
|
||||
func (b *genericBuilder) makeMidi() remoteBuilder {
|
||||
log.Info("building midi")
|
||||
log.Debug("building midi")
|
||||
b.r.Midi = newMidi()
|
||||
return b
|
||||
}
|
||||
|
||||
// setDefaults sets defaults for optional members
|
||||
func (b *genericBuilder) setDefaults() remoteBuilder {
|
||||
b.r.bits = 64
|
||||
b.r.timeout = 2
|
||||
return b
|
||||
}
|
||||
|
||||
// Get returns a fully constructed remote type for a kind
|
||||
func (b *genericBuilder) Get() *Remote {
|
||||
return &b.r
|
||||
@@ -306,7 +314,8 @@ func (basb *genericBuilder) Build() remoteBuilder {
|
||||
makeCommand().
|
||||
makeVban().
|
||||
makeDevice().
|
||||
makeMidi()
|
||||
makeMidi().
|
||||
setDefaults()
|
||||
}
|
||||
|
||||
// bananaBuilder represents a builder specific to banana type
|
||||
@@ -324,7 +333,8 @@ func (banb *bananaBuilder) Build() remoteBuilder {
|
||||
makeVban().
|
||||
makeDevice().
|
||||
makeRecorder().
|
||||
makeMidi()
|
||||
makeMidi().
|
||||
setDefaults()
|
||||
}
|
||||
|
||||
// potatoBuilder represents a builder specific to potato type
|
||||
@@ -342,7 +352,8 @@ func (potb *potatoBuilder) Build() remoteBuilder {
|
||||
makeVban().
|
||||
makeDevice().
|
||||
makeRecorder().
|
||||
makeMidi()
|
||||
makeMidi().
|
||||
setDefaults()
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -350,6 +361,22 @@ var (
|
||||
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)
|
||||
@@ -357,7 +384,7 @@ func init() {
|
||||
|
||||
// NewRemote returns a Remote type for a kind
|
||||
// this is the interface entry point
|
||||
func NewRemote(kindId string, delay int) (*Remote, error) {
|
||||
func NewRemote(kindId string, delay int, opts ...Option) (*Remote, error) {
|
||||
kind, ok := kindMap[kindId]
|
||||
if !ok {
|
||||
err := fmt.Errorf("unknown Voicemeeter kind '%s'", kindId)
|
||||
@@ -380,5 +407,11 @@ func NewRemote(kindId string, delay int) (*Remote, error) {
|
||||
director.SetBuilder(&potatoBuilder{genericBuilder{kind, Remote{}}})
|
||||
}
|
||||
director.Construct()
|
||||
return director.Get(), nil
|
||||
r := director.Get()
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(r)
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user