28 Commits

Author SHA1 Message Date
fbfab5b4aa 3.3.0 section added to CHANGELOG
added dates for past versions
2024-06-29 10:03:32 +01:00
4d371a7582 Remove the 1 second wait from RunVoicemeeter
Write exception message to Debug
2024-06-29 07:13:11 +01:00
15b3b375bd md fix 2024-06-29 06:55:48 +01:00
c8abc6964a update RunVoicemeeter to launch x64 bit GUIs for all kinds
Keep testing login for up to 2 seconds.
If timeout exceeded throw VMRemoteError
2024-06-29 06:53:20 +01:00
907ee3e63b upd tested against 2024-06-28 11:12:01 +01:00
f3ed9c28c7 upd doc link 2024-01-03 09:38:22 +00:00
d305a4048d "\" -Join path parts 2023-08-17 15:02:03 +01:00
108731b4cf add RunMacrobuttons(), CloseMacrobuttons() 2023-08-17 03:19:05 +01:00
e7c648f1d0 fix function names 2023-08-17 03:05:32 +01:00
b21a71471b 3.2.0 section added to CHANGELOG 2023-08-17 02:57:24 +01:00
43367525c5 Errors section added to README 2023-08-17 02:56:38 +01:00
d0fbd6deef CAPIError properties renamed.
code and function better describe their meaning.
2023-08-17 02:54:30 +01:00
1df92afcfe check size of script 2023-08-17 02:53:01 +01:00
2ad8118f2c adjust the timings slightly 2023-08-17 00:14:12 +01:00
bc6162cf16 add cmdletbinding to examples for debug, verbose flags
add verbose,debug flags to launch scripts
2023-08-16 16:38:00 +01:00
9b3d9f2250 remove Write-Warning for CAPIErrors.
Allow them to bubble up.
(Might be worth adding a helper function to print stacktrace?)
2023-08-16 16:36:43 +01:00
844eaeabaa ErrorMessage removed from error classes 2023-08-16 15:13:30 +01:00
a78cdf9a99 RunVoicemeeter function added
All CAPIErrors are now logged and rethrown
2023-08-16 15:12:25 +01:00
a40adf27be readme, changelog updated 2023-08-16 03:05:06 +01:00
1397c14522 debug statements added to getters and setters
made some rearrangements to the dot sourcing

ButtonTypes enum added to macrobuttons.ps1

login string now includes version number

Test-RegistryValue added to inst.ps1
2023-08-16 02:52:12 +01:00
ff1bd5e6cc section 3.1.0 added to CHANGELOG
close #4
2023-08-15 15:44:14 +01:00
f480b637eb pester test script added to launch.json
CHANGELOG, README updated
2023-08-14 23:01:38 +01:00
09078d382b make examples and tests runnable if dot sourced 2023-08-14 22:44:09 +01:00
598e0dd647 add launch scripts 2023-08-14 21:45:18 +01:00
72b5ac02d3 adds missing Recorder parameters 2023-08-14 21:43:51 +01:00
a031d25a41 fix range checks 2023-08-14 21:30:32 +01:00
4e9ff66640 rework getters, setters in higher classes 2023-08-12 03:09:23 +01:00
bee52b6541 rename some of the internal classes
RecorderMode class added to Recorder

RunMacrobuttons() and CloseMacrobuttons() added to Special (Command)
2023-08-12 01:40:29 +01:00
19 changed files with 688 additions and 337 deletions

71
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,71 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "PowerShell: Launch CLI Example",
"type": "PowerShell",
"request": "launch",
"cwd": "${workspaceRoot}/examples/cli",
"script": "${workspaceFolder}/examples/cli/CLI.ps1",
"args": [
"-s",
"\"strip[0].mute\",",
"\"!strip[0].mute\",",
"\"strip[0].mute\",",
"\"bus[2].eq.on=1\",",
"\"command.lock=1\"",
"-Verbose",
"-Debug"
],
"createTemporaryIntegratedConsole": true
},
{
"name": "PowerShell: Launch NextBus Example",
"type": "PowerShell",
"request": "launch",
"cwd": "${workspaceRoot}/examples/nextbus",
"script": "${workspaceFolder}/examples/nextbus/GoTo-NextBus.ps1",
"args": [
"-Verbose",
"-Debug"
],
"createTemporaryIntegratedConsole": true
},
{
"name": "PowerShell: Launch OBS Example",
"type": "PowerShell",
"request": "launch",
"cwd": "${workspaceRoot}/examples/obs",
"script": "${workspaceFolder}/examples/obs/Vm-Obs-Sync.ps1",
"args": [
"-Verbose",
"-Debug"
],
"createTemporaryIntegratedConsole": true
},
{
"name": "PowerShell: Run Pester Tests",
"type": "PowerShell",
"request": "launch",
"cwd": "${workspaceRoot}",
"script": "${workspaceFolder}/tests/pre-commit.ps1",
"args": [],
"createTemporaryIntegratedConsole": true
},
{
"name": "PowerShell: Launch Quick Test",
"type": "PowerShell",
"request": "launch",
"cwd": "${workspaceRoot}",
"script": "${workspaceFolder}/quick.ps1",
"args": [
"-Verbose",
"-Debug"
],
"createTemporaryIntegratedConsole": true
}
]
}

View File

@@ -9,9 +9,43 @@ Before any major/minor/patch is released all test units will be run to verify th
## [Unreleased] These changes have not been added to PSGallery yet ## [Unreleased] These changes have not been added to PSGallery yet
- [x] Level methods for Strip,Bus classes implemented. - [ ]
## [3.0.0] ## [3.3.0] - 2024-06-29
### Added
- Add a timeout (2s) to the login function. If timeout exceeded a VMRemoteError is thrown.
### Changed
- Launch x64 bit GUIs for all kinds if on 64 bit system.
## [3.2.0] - 2023-08-17
### Added
- Debug statements added to Getters, Setters in higher classes.
- RunVoicemeeter function added to base.ps1. Accepts kind name as parameter.
- Errors section to README.
### Fixed
- All CAPIErrors are now exposed to the consumer.
- The function name and error code can be retrieved using [CAPIError].function and [CAPIError].code
- Set_By_Script now throws [VMRemoteError] if script length exceeds 48kB.
- parameter range checks in Vban class.
## [3.1.0] - 2023-08-15
### Added
- Level methods for Strip class implemented. See Strip.levels section in README.
- Level methods for Bus class implemented. See Bus.levels section in README.
- More Recorder commands implemented. See Recorder section in README.
- RunMacrobuttons, CloseMacrobuttons added to Special class
## [3.0.0] - 2023-08-09
v3 introduces some breaking changes. They are as follows: v3 introduces some breaking changes. They are as follows:

143
README.md
View File

@@ -8,9 +8,9 @@ For past/future changes to this project refer to: [CHANGELOG](CHANGELOG.md)
## Tested against ## Tested against
- Basic 1.0.8.8 - Basic 1.1.1.1
- Banana 2.0.6.8 - Banana 2.1.1.1
- Potato 3.0.2.8 - Potato 3.1.1.1
## Requirements ## Requirements
@@ -115,13 +115,13 @@ $vmr.Logout()
The following strip commands are available: The following strip commands are available:
- mute: boolean - mute: bool
- mono: boolean - mono: bool
- mc: boolean - mc: bool
- k: int, from 0 to 4 - k: int, from 0 to 4
- solo: boolean - solo: bool
- A1-A5: boolean - A1-A5: bool
- B1-B3: boolean - B1-B3: bool
- limit: int, from -40 to 12 - limit: int, from -40 to 12
- gain: float, from -60.0 to 12.0 - gain: float, from -60.0 to 12.0
- label: string - label: string
@@ -135,10 +135,10 @@ The following strip commands are available:
- color_y: float, from 0.0 to 1.0 - color_y: float, from 0.0 to 1.0
- fx_x: float, from -0.5 to 0.5 - fx_x: float, from -0.5 to 0.5
- fx_y: float, from 0.0 to 1.0 - fx_y: float, from 0.0 to 1.0
- postreverb: boolean - postreverb: bool
- postdelay: boolean - postdelay: bool
- postfx1: boolean - postfx1: bool
- postfx2: boolean - postfx2: bool
- gainlayer0-gainlayer7: float - gainlayer0-gainlayer7: float
for example: for example:
@@ -165,7 +165,7 @@ The following strip.comp commands are available:
- release: float, from 0.0 to 5000.0 - release: float, from 0.0 to 5000.0
- knee: float, 0.0 to 1.0 - knee: float, 0.0 to 1.0
- gainout: float, from -24.0 to 24.0 - gainout: float, from -24.0 to 24.0
- makeup: boolean - makeup: bool
for example: for example:
@@ -206,7 +206,7 @@ $vmr.strip[3].denoiser.knob = 5
#### AppGain | AppMute #### AppGain | AppMute
- `AppGain(amount, gain)` : string, float - `AppGain(amount, gain)` : string, float
- `AppMute(amount, mutestate)` : string, boolean - `AppMute(amount, mutestate)` : string, bool
for example: for example:
@@ -253,18 +253,18 @@ $vmr.bus[3].returnreverb = 5.7
The following bus.mode members are available: The following bus.mode members are available:
- normal: boolean - normal: bool
- amix: boolean - amix: bool
- bmix: boolean - bmix: bool
- repeat: boolean - repeat: bool
- composite: boolean - composite: bool
- tvmix: boolean - tvmix: bool
- upmix21: boolean - upmix21: bool
- upmix41: boolean - upmix41: bool
- upmix61: boolean - upmix61: bool
- centeronly: boolean - centeronly: bool
- lfeonly: boolean - lfeonly: bool
- rearonly: boolean - rearonly: bool
The following bus.mode commands are available: The following bus.mode commands are available:
@@ -317,8 +317,8 @@ wdm, ks, mme, asio are defined as write only.
The following strip.eq | bus.eq commands are available: The following strip.eq | bus.eq commands are available:
- on: boolean - on: bool
- ab: boolean - ab: bool
for example: for example:
@@ -359,19 +359,19 @@ $vmr.button[5].trigger = $true
### VBAN ### VBAN
- vmr.vban.enable: Toggle VBAN on or off. Accepts a boolean value. - vmr.vban.enable: Toggle VBAN on or off. Accepts a bool value.
For each vban in/out stream the following parameters are defined: For each vban in/out stream the following parameters are defined:
- on: boolean - on: bool
- name: string - name: string
- ip: string - ip: string
- port: int from 1024 - 65535 - port: int, from 1024 - 65535
- sr: int (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000) - sr: in, (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000)
- channel: int from 1 to 8 - channel: int from 1 to 8
- bit: int 16 or 24 - bit: int, 16 or 24
- quality: int from 0 to 4 - quality: int, from 0 to 4
- route: int from 0 to 8 - route: int, from 0 to 8
SR, channel and bit are defined as readonly for instreams. Attempting to write SR, channel and bit are defined as readonly for instreams. Attempting to write
to those parameters will throw an error. They are read and write for outstreams. to those parameters will throw an error. They are read and write for outstreams.
@@ -390,18 +390,20 @@ $vmr.vban.outstream[3].bit = 16
Certain 'special' commands are defined by the API as performing actions rather than setting values. Certain 'special' commands are defined by the API as performing actions rather than setting values.
The following methods are available: The following commands are available:
- show - show
- hide - hide
- restart - restart
- shutdown - shutdown
- Load(filepath) - showvbanchat: bool, (write only)
- lock: bool, (write only)
The following properties are write only and accept boolean values: The following methods are available:
- showvbanchat - Load($filepath): string
- lock - RunMacrobuttons(): Launches the macrobuttons app
- CloseMacrobuttons(): Closes the macrobuttons app
example: example:
@@ -411,11 +413,13 @@ $vmr.command.show
$vmr.command.lock = $true $vmr.command.lock = $true
$vmr.command.Load("path/to/filename.xml") $vmr.command.Load("path/to/filename.xml")
$vmr.command.RunMacrobuttons()
``` ```
### Recorder ### Recorder
The following methods are available: The following commands are available:
- play - play
- stop - stop
@@ -423,19 +427,53 @@ The following methods are available:
- record - record
- ff - ff
- rew - rew
- A1 - A5: bool
- B1 - B3: bool
- samplerate: int, (22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000)
- bitresolution: int, (8, 16, 24, 32)
- channel: int, from 1 to 8
- kbps: int, (32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320)
The following properties accept boolean values. The following methods are available:
- loop - Load($filepath): string
- A1 - A5 - GoTo($timestring): string, must match the format 'hh:mm:ss'
- B1 - B3 - FileType($format): string, ('wav', 'aiff', 'bwf', 'mp3')
example: example:
```powershell ```powershell
$vmr.recorder.play $vmr.recorder.play
$vmr.recorder.A1 = $true
$vmr.recorder.loop = $true $vmr.recorder.GoTo("00:01:15") # go to 1min 15sec into track
```
#### Mode
The following commands are available:
- recbus
- playonload
- loop
- multitrack
example:
```powershell
$vmr.recorder.mode.loop = $true
```
#### ArmStrip[i]|ArmBus[i]
The following method is available:
- Set($val): bool
example:
```powershell
$vmr.recorder.armstrip[0].Set($true)
``` ```
### Multiple parameters ### Multiple parameters
@@ -508,6 +546,15 @@ Access to lower level polling functions are provided with these functions:
- `$vmr.PDirty`: Returns true if a parameter has been updated. - `$vmr.PDirty`: Returns true if a parameter has been updated.
- `$vmr.MDirty`: Returns true if a macrobutton has been updated. - `$vmr.MDirty`: Returns true if a macrobutton has been updated.
### Errors
- `VMRemoteError`: Base custom error class.
- `LoginError`: Raised when a login error occurs.
- `CAPIError`: Raised when a C-API function returns an error code.
- The following class properties are available:
- `function`: The name of the C-API function that returned the error code.
- `code`: The error code.
### Run tests ### Run tests
Run tests using .\tests\pre-commit.ps1 which accepts the following parameters: Run tests using .\tests\pre-commit.ps1 which accepts the following parameters:
@@ -523,4 +570,4 @@ Run tests from repository root in a subshell and write logs, like so:
### Official Documentation ### Official Documentation
- [Voicemeeter Remote C API](https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/update-docs/VoicemeeterRemoteAPI.pdf) - [Voicemeeter Remote C API](https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/main/VoicemeeterRemoteAPI.pdf)

View File

@@ -1,14 +1,12 @@
[cmdletbinding()]
param( param(
[switch]$interactive, [switch]$interactive,
[switch]$output,
[String]$kind = "banana", [String]$kind = "banana",
[String[]]$script = @() [String[]]$script = @()
) )
Import-Module ..\..\lib\Voicemeeter.psm1 Import-Module ..\..\lib\Voicemeeter.psm1
$VerbosePreference = "Continue"
function get-value { function get-value {
param([object]$vmr, [string]$line) param([object]$vmr, [string]$line)
try { try {
@@ -24,16 +22,16 @@ function msgHandler {
param([object]$vmr, [string]$line) param([object]$vmr, [string]$line)
$line + " passed to handler" | Write-Debug $line + " passed to handler" | Write-Debug
if ($line[0] -eq "!") { if ($line[0] -eq "!") {
if ($output) { "Toggling " + $line.substring(1) | Write-Host } "Toggling " + $line.substring(1) | Write-Debug
$retval = get-value -vmr $vmr -line $line.substring(1) $retval = get-value -vmr $vmr -line $line.substring(1)
$vmr.Setter($line.substring(1), 1 - $retval) $vmr.Setter($line.substring(1), 1 - $retval)
} }
elseif ($line.Contains("=")) { elseif ($line.Contains("=")) {
if ($output) { "Setting $line" | Write-Host } "Setting $line" | Write-Debug
$vmr.SendText($line) $vmr.SendText($line)
} }
else { else {
if ($output) { "Getting $line" | Write-Host } "Getting $line" | Write-Debug
$retval = get-value -vmr $vmr -line $line $retval = get-value -vmr $vmr -line $line
$line + " = " + $retval | Write-Host $line + " = " + $retval | Write-Host
} }
@@ -63,4 +61,4 @@ function main {
finally { Disconnect-Voicemeeter } finally { Disconnect-Voicemeeter }
} }
if ($MyInvocation.InvocationName -ne '.') { main } main

View File

@@ -7,27 +7,30 @@
Credits go to @bobsupercow Credits go to @bobsupercow
#> #>
Import-Module ..\..\lib\Voicemeeter.psm1 [cmdletbinding()]
param()
$VerbosePreference = "Continue" Import-Module ..\..\lib\Voicemeeter.psm1
try { try {
$vmr = Connect-Voicemeeter -Kind "potato" $vmr = Connect-Voicemeeter -Kind "potato"
$buses = @($vmr.bus[1], $vmr.bus[2], $vmr.bus[4], $vmr.bus[6]) $buses = @($vmr.bus[1], $vmr.bus[2], $vmr.bus[4], $vmr.bus[6])
"Buses in selection: $($buses)"
$unmutedIndex = $null $unmutedIndex = $null
# 1) # 1)
"Cycling through bus selection to check for first unmuted Bus..." | Write-Host
foreach ($bus in $buses) { foreach ($bus in $buses) {
# 2) # 2)
if (-not $bus.mute) { if (-not $bus.mute) {
"bus $($bus.index) is unmuted... muting it" | Write-Host "Bus $($bus.index) is unmuted... muting it" | Write-Host
$unmutedIndex = $buses.IndexOf($bus) $unmutedIndex = $buses.IndexOf($bus)
$bus.mute = $true $bus.mute = $true
# 3) # 3)
if ($buses[++$unmutedIndex]) { if ($buses[++$unmutedIndex]) {
"unmuting bus $($buses[$unmutedIndex].index)" | Write-Host "Unmuting Bus $($buses[$unmutedIndex].index)" | Write-Host
$buses[$unmutedIndex].mute = $false $buses[$unmutedIndex].mute = $false
break break
} }
@@ -37,7 +40,7 @@ try {
# 4) # 4)
if ($null -eq $unmutedIndex) { if ($null -eq $unmutedIndex) {
$buses[0].mute = $false $buses[0].mute = $false
"unmuting bus $($buses[0].index)" | Write-Host "Unmuting Bus $($buses[0].index)" | Write-Host
} }
} }

View File

@@ -1,8 +1,9 @@
[cmdletbinding()]
param()
Import-Module ..\..\lib\Voicemeeter.psm1 Import-Module ..\..\lib\Voicemeeter.psm1
Import-Module obs-powershell Import-Module obs-powershell
$VerbosePreference = "Continue"
function CurrentProgramSceneChanged { function CurrentProgramSceneChanged {
param([System.Object]$data) param([System.Object]$data)
Write-Host "Switched to scene", $data.sceneName Write-Host "Switched to scene", $data.sceneName
@@ -10,19 +11,15 @@ function CurrentProgramSceneChanged {
switch ($data.sceneName) { switch ($data.sceneName) {
"START" { "START" {
$vmr.strip[0].mute = !$vmr.strip[0].mute $vmr.strip[0].mute = !$vmr.strip[0].mute
"Toggling Strip 0 mute"
} }
"BRB" { "BRB" {
$vmr.strip[0].gain = -8.3 $vmr.strip[0].gain = -8.3
"Setting Strip 0 gain to -8.3"
} }
"END" { "END" {
$vmr.strip[0].mono = $true $vmr.strip[0].mono = $true
"Setting Strip 0 mono to `$true"
} }
"LIVE" { "LIVE" {
$vmr.strip[0].color_x = 0.3 $vmr.strip[0].color_x = 0.3
"Setting Strip 0 color_x to 0.3"
} }
default { "Expected START, BRB, END or LIVE scene" | Write-Warning; return } default { "Expected START, BRB, END or LIVE scene" | Write-Warning; return }
} }
@@ -68,4 +65,4 @@ function main {
} }
} }
if ($MyInvocation.InvocationName -ne '.') { main } main

View File

@@ -1,14 +1,22 @@
. $PSScriptRoot\kinds.ps1 . $PSScriptRoot\errors.ps1
. $PSScriptRoot\meta.ps1
. $PSScriptRoot\base.ps1 . $PSScriptRoot\base.ps1
. $PSScriptRoot\kinds.ps1
. $PSScriptRoot\strip.ps1
. $PSScriptRoot\bus.ps1
. $PSScriptRoot\macrobuttons.ps1
. $PSScriptRoot\vban.ps1
. $PSScriptRoot\command.ps1
. $PSScriptRoot\recorder.ps1
. $PSScriptRoot\profiles.ps1
class Remote { class Remote {
[String]$vmpath
[Hashtable]$kind [Hashtable]$kind
[Object]$profiles [Object]$profiles
Remote ([String]$kindId) { Remote ([String]$kindId) {
if (!(Setup_DLL)) { $this.vmpath = Setup_DLL
Exit -1
}
$this.kind = GetKind($kindId) $this.kind = GetKind($kindId)
$this.profiles = Get_Profiles($this.kind.name) $this.profiles = Get_Profiles($this.kind.name)
} }
@@ -31,7 +39,7 @@ class Remote {
} }
[String] GetVersion() { [String] GetVersion() {
return Version return VmVersion
} }
[void] Set_Profile([String]$config) { [void] Set_Profile([String]$config) {
@@ -75,7 +83,7 @@ class RemoteBasic : Remote {
$this.bus = Make_Buses($this) $this.bus = Make_Buses($this)
$this.button = Make_Buttons $this.button = Make_Buttons
$this.vban = Make_Vban($this) $this.vban = Make_Vban($this)
$this.command = Make_Command $this.command = Make_Command($this)
} }
} }
@@ -92,7 +100,7 @@ class RemoteBanana : Remote {
$this.bus = Make_Buses($this) $this.bus = Make_Buses($this)
$this.button = Make_Buttons $this.button = Make_Buttons
$this.vban = Make_Vban($this) $this.vban = Make_Vban($this)
$this.command = Make_Command $this.command = Make_Command($this)
$this.recorder = Make_Recorder($this) $this.recorder = Make_Recorder($this)
} }
} }
@@ -110,7 +118,7 @@ class RemotePotato : Remote {
$this.bus = Make_Buses($this) $this.bus = Make_Buses($this)
$this.button = Make_Buttons $this.button = Make_Buttons
$this.vban = Make_Vban($this) $this.vban = Make_Vban($this)
$this.command = Make_Command $this.command = Make_Command($this)
$this.recorder = Make_Recorder($this) $this.recorder = Make_Recorder($this)
} }
} }
@@ -129,24 +137,20 @@ Function Get-RemotePotato {
Function Connect-Voicemeeter { Function Connect-Voicemeeter {
param([String]$Kind) param([String]$Kind)
try { switch ($Kind) {
switch ($Kind) { "basic" {
"basic" { return Get-RemoteBasic
return Get-RemoteBasic }
} "banana" {
"banana" { return Get-RemoteBanana
return Get-RemoteBanana }
} "potato" {
"potato" { return Get-RemotePotato
return Get-RemotePotato }
} default {
default { throw [LoginError]::new("Unknown Voicemeeter kind `"$Kind`"") } throw [LoginError]::new("Unknown Voicemeeter kind `"$Kind`"")
} }
} }
catch [LoginError], [CAPIError] {
Write-Warning $_.Exception.ErrorMessage()
throw
}
} }
Function Disconnect-Voicemeeter { Function Disconnect-Voicemeeter {

View File

@@ -1,74 +1,95 @@
. $PSScriptRoot\errors.ps1 . $PSScriptRoot\errors.ps1
. $PSScriptRoot\binding.ps1 . $PSScriptRoot\binding.ps1
. $PSScriptRoot\profiles.ps1
. $PSScriptRoot\inst.ps1
. $PSScriptRoot\strip.ps1
. $PSScriptRoot\bus.ps1
. $PSScriptRoot\macrobuttons.ps1
. $PSScriptRoot\vban.ps1
. $PSScriptRoot\command.ps1
. $PSScriptRoot\recorder.ps1
function Login { function Login {
param( param(
[string]$kindId [string]$kindId
) )
try { $retval = [int][Voicemeeter.Remote]::VBVMR_Login()
$retval = [int][Voicemeeter.Remote]::VBVMR_Login() if ($retval -notin @(0, 1, -2)) {
if (-not $retval) { throw [CAPIError]::new($retval, "VBVMR_Login")
"LOGGED IN" | Write-Verbose
}
elseif ($retval -eq 1) {
"VM NOT RUNNING" | Write-Verbose
New-Variable -Name vmExe -Value 0
if ( $kindId -eq "basic" ) { $vmExe = 1 }
elseif ( $kindId -eq "banana" ) { $vmExe = 2 }
elseif ( $kindId -eq "potato" ) {
$vmExe = $(if ([Environment]::Is64BitOperatingSystem) { 6 } else { 3 })
}
$retval = [int][Voicemeeter.Remote]::VBVMR_RunVoicemeeter([int64]$vmExe)
if (-not $retval) {
"STARTING VOICEMEETER" | Write-Verbose
Start-Sleep -s 1
}
else {
throw [CAPIError]::new($retval, $MyInvocation.MyCommand)
}
}
elseif ($retval -eq -2) {
throw [LoginError]::new('login may only be called once per session')
}
else { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) }
} }
catch [LoginError] {
Write-Warning "$($_.Exception.ErrorMessage()). Fatal error, exiting..." switch ($retval) {
exit -2 1 {
"Voicemeeter Engine running but GUI not launched. Launching GUI now." | Write-Verbose
RunVoicemeeter -kindId $kindId
}
-2 {
throw [LoginError]::new("Login may only be called once per session.")
}
}
$timeout = New-TimeSpan -Seconds 2
$sw = [diagnostics.stopwatch]::StartNew()
$exception = $null
do {
Start-Sleep -m 100
try {
"Successfully logged into Voicemeeter [" + $(VmType).ToUpper() + "] Version " + $(VmVersion) | Write-Verbose
$exception = $null
break
}
catch [CAPIError] {
$exception = $_
$exception | Write-Debug
}
} while ($sw.elapsed -lt $timeout)
if ($null -ne $exception) {
throw [VMRemoteError]::new("Timeout logging into the API.")
} }
while (P_Dirty -or M_Dirty) { Start-Sleep -m 1 } while (P_Dirty -or M_Dirty) { Start-Sleep -m 1 }
"VERSION:[" + $(VmType).ToUpper() + "]" | Write-Verbose
} }
function Logout { function Logout {
Start-Sleep -m 20 Start-Sleep -m 100
$retval = [int][Voicemeeter.Remote]::VBVMR_Logout() $retval = [int][Voicemeeter.Remote]::VBVMR_Logout()
if (-not $retval) { "LOGGED OUT" | Write-Verbose } if ($retval -notin @(0)) {
throw [CAPIError]::new($retval, "VBVMR_Logout")
}
if ($retval -eq 0) { "Sucessfully logged out" | Write-Verbose }
}
function RunVoicemeeter {
param(
[string]$kindId
)
$kinds = @{
"basic" = $(if ([Environment]::Is64BitOperatingSystem) { 4 } else { 1 })
"banana" = $(if ([Environment]::Is64BitOperatingSystem) { 5 } else { 2 })
"potato" = $(if ([Environment]::Is64BitOperatingSystem) { 6 } else { 3 })
}
$retval = [int][Voicemeeter.Remote]::VBVMR_RunVoicemeeter([int64]$kinds[$kindId])
if ($retval -notin @(0)) {
throw [CAPIError]::new($retval, "VBVMR_RunVoicemeeter")
}
} }
function P_Dirty { function P_Dirty {
[bool][Voicemeeter.Remote]::VBVMR_IsParametersDirty() $retval = [Voicemeeter.Remote]::VBVMR_IsParametersDirty()
if ($retval -notin @(0, 1)) {
throw [CAPIError]::new($retval, "VBVMR_IsParametersDirty")
}
[bool]$retval
} }
function M_Dirty { function M_Dirty {
[bool][Voicemeeter.Remote]::VBVMR_MacroButton_IsDirty() $retval = [Voicemeeter.Remote]::VBVMR_MacroButton_IsDirty()
if ($retval -notin @(0, 1)) {
throw [CAPIError]::new($retval, "VBVMR_MacroButton_IsDirty")
}
[bool]$retval
} }
function VmType { function VmType {
New-Variable -Name ptr -Value 0 New-Variable -Name ptr -Value 0
$retval = [int][Voicemeeter.Remote]::VBVMR_GetVoicemeeterType([ref]$ptr) $retval = [int][Voicemeeter.Remote]::VBVMR_GetVoicemeeterType([ref]$ptr)
if ($retval) { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) } if ($retval -notin @(0)) {
throw [CAPIError]::new($retval, "VBVMR_GetVoicemeeterType")
}
switch ($ptr) { switch ($ptr) {
1 { return "basic" } 1 { return "basic" }
2 { return "banana" } 2 { return "banana" }
@@ -76,10 +97,12 @@ function VmType {
} }
} }
function Version { function VmVersion {
New-Variable -Name ptr -Value 0 New-Variable -Name ptr -Value 0
$retval = [int][Voicemeeter.Remote]::VBVMR_GetVoicemeeterVersion([ref]$ptr) $retval = [int][Voicemeeter.Remote]::VBVMR_GetVoicemeeterVersion([ref]$ptr)
if ($retval) { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) } if ($retval -notin @(0)) {
throw [CAPIError]::new($retval, "VBVMR_GetVoicemeeterVersion")
}
$v1 = ($ptr -band 0xFF000000) -shr 24 $v1 = ($ptr -band 0xFF000000) -shr 24
$v2 = ($ptr -band 0x00FF0000) -shr 16 $v2 = ($ptr -band 0x00FF0000) -shr 16
$v3 = ($ptr -band 0x0000FF00) -shr 8 $v3 = ($ptr -band 0x0000FF00) -shr 8
@@ -92,28 +115,22 @@ function Param_Get {
param( param(
[string]$PARAM, [bool]$IS_STRING = $false [string]$PARAM, [bool]$IS_STRING = $false
) )
Start-Sleep -m 50 Start-Sleep -m 30
while (P_Dirty) { Start-Sleep -m 1 } while (P_Dirty) { Start-Sleep -m 1 }
if ($IS_STRING) { if ($IS_STRING) {
$BYTES = [System.Byte[]]::new(512) $BYTES = [System.Byte[]]::new(512)
try { $retval = [int][Voicemeeter.Remote]::VBVMR_GetParameterStringA($PARAM, $BYTES)
$retval = [int][Voicemeeter.Remote]::VBVMR_GetParameterStringA($PARAM, $BYTES) if ($retval -notin @(0)) {
if ($retval) { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) } throw [CAPIError]::new($retval, "VBVMR_GetParameterStringA")
}
catch [CAPIError] {
Write-Warning $_.Exception.ErrorMessage()
} }
[System.Text.Encoding]::ASCII.GetString($BYTES).Trim([char]0) [System.Text.Encoding]::ASCII.GetString($BYTES).Trim([char]0)
} }
else { else {
New-Variable -Name ptr -Value 0.0 New-Variable -Name ptr -Value 0.0
try { $retval = [int][Voicemeeter.Remote]::VBVMR_GetParameterFloat($PARAM, [ref]$ptr)
$retval = [int][Voicemeeter.Remote]::VBVMR_GetParameterFloat($PARAM, [ref]$ptr) if ($retval -notin @(0)) {
if ($retval) { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) } throw [CAPIError]::new($retval, "VBVMR_GetParameterFloat")
}
catch [CAPIError] {
Write-Warning $_.Exception.ErrorMessage()
} }
[single]$ptr [single]$ptr
} }
@@ -123,17 +140,17 @@ function Param_Set {
param( param(
[string]$PARAM, [Object]$VALUE [string]$PARAM, [Object]$VALUE
) )
try { if ($VALUE -is [string]) {
if ($VALUE -is [string]) { $retval = [int][Voicemeeter.Remote]::VBVMR_SetParameterStringA($PARAM, $VALUE)
$retval = [int][Voicemeeter.Remote]::VBVMR_SetParameterStringA($PARAM, $VALUE) if ($retval -notin @(0)) {
throw [CAPIError]::new($retval, "VBVMR_SetParameterStringA")
} }
else {
$retval = [int][Voicemeeter.Remote]::VBVMR_SetParameterFloat($PARAM, $VALUE)
}
if ($retval) { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) }
} }
catch [CAPIError] { else {
Write-Warning $_.Exception.ErrorMessage() $retval = [int][Voicemeeter.Remote]::VBVMR_SetParameterFloat($PARAM, $VALUE)
if ($retval -notin @(0)) {
throw [CAPIError]::new($retval, "VBVMR_SetParameterFloat")
}
} }
} }
@@ -141,12 +158,9 @@ function MB_Set {
param( param(
[int64]$ID, [single]$SET, [int64]$MODE [int64]$ID, [single]$SET, [int64]$MODE
) )
try { $retval = [int][Voicemeeter.Remote]::VBVMR_MacroButton_SetStatus($ID, $SET, $MODE)
$retval = [int][Voicemeeter.Remote]::VBVMR_MacroButton_SetStatus($ID, $SET, $MODE) if ($retval -notin @(0)) {
if ($retval) { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) } throw [CAPIError]::new($retval, "VBVMR_MacroButton_SetStatus")
}
catch [CAPIError] {
Write-Warning $_.Exception.ErrorMessage()
} }
} }
@@ -158,12 +172,9 @@ function MB_Get {
while (M_Dirty) { Start-Sleep -m 1 } while (M_Dirty) { Start-Sleep -m 1 }
New-Variable -Name ptr -Value 0.0 New-Variable -Name ptr -Value 0.0
try { $retval = [int][Voicemeeter.Remote]::VBVMR_MacroButton_GetStatus($ID, [ref]$ptr, $MODE)
$retval = [int][Voicemeeter.Remote]::VBVMR_MacroButton_GetStatus($ID, [ref]$ptr, $MODE) if ($retval -notin @(0)) {
if ($retval) { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) } throw [CAPIError]::new($retval, "VBVMR_MacroButton_GetStatus")
}
catch [CAPIError] {
Write-Warning $_.Exception.ErrorMessage()
} }
[int]$ptr [int]$ptr
} }
@@ -195,12 +206,12 @@ function Set_By_Script {
param( param(
[string]$script [string]$script
) )
try { if ($script.Length -gt 48000) {
$retval = [int][Voicemeeter.Remote]::VBVMR_SetParameters($script) throw [VMRemoteError]::new("Script size cannot be larger than 48kB")
if ($retval) { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) }
} }
catch [CAPIError] { $retval = [int][Voicemeeter.Remote]::VBVMR_SetParameters($script)
Write-Warning $_.Exception.ErrorMessage() if ($retval -notin @(0)) {
throw [CAPIError]::new($retval, "VBVMR_SetParameters")
} }
} }
@@ -209,12 +220,9 @@ function Get_Level {
[int64]$MODE, [int64]$INDEX [int64]$MODE, [int64]$INDEX
) )
New-Variable -Name ptr -Value 0.0 New-Variable -Name ptr -Value 0.0
try { $retval = [int][Voicemeeter.Remote]::VBVMR_GetLevel($MODE, $INDEX, [ref]$ptr)
$retval = [int][Voicemeeter.Remote]::VBVMR_GetLevel($MODE, $INDEX, [ref]$ptr) if ($retval -notin @(0)) {
if ($retval) { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) } throw [CAPIError]::new($retval, "VBVMR_GetLevel")
}
catch [CAPIError] {
Write-Warning $_.Exception.ErrorMessage()
} }
[float]$ptr [float]$ptr
} }

View File

@@ -1,18 +1,11 @@
function Setup_DLL { . $PSScriptRoot\inst.ps1
try {
$vb_path = Get_VBPath
if ([string]::IsNullOrWhiteSpace($vb_path)) { function Setup_DLL {
throw [VMRemoteError]::new("couldn't get Voicemeeter path") $VMPATH = Get_VMPath
}
$dll = Join-Path -Path $vb_path -ChildPath ("VoicemeeterRemote" + ` $dll = Join-Path -Path $VMPATH -ChildPath ("VoicemeeterRemote" + `
(& { if ([Environment]::Is64BitOperatingSystem) { "64" } else { "" } }) + ` (& { if ([Environment]::Is64BitOperatingSystem) { "64" } else { "" } }) + `
".dll") ".dll")
}
catch [VMRemoteError] {
Write-Warning $_.Exception.ErrorMessage()
return $false
}
$Signature = @" $Signature = @"
[DllImport(@"$dll")] [DllImport(@"$dll")]
@@ -53,5 +46,5 @@ function Setup_DLL {
"@ "@
Add-Type -MemberDefinition $Signature -Name Remote -Namespace Voicemeeter -PassThru | Out-Null Add-Type -MemberDefinition $Signature -Name Remote -Namespace Voicemeeter -PassThru | Out-Null
return $true return $VMPATH
} }

View File

@@ -1,5 +1,3 @@
. $PSScriptRoot\meta.ps1
class IBus { class IBus {
[int]$index [int]$index
[Object]$remote [Object]$remote
@@ -13,20 +11,26 @@ class IBus {
return "Bus[" + $this.index + "]" return "Bus[" + $this.index + "]"
} }
[string] ToString() {
return $this.GetType().Name + $this.index
}
[single] Getter ($param) { [single] Getter ($param) {
return Param_Get -PARAM "$($this.identifier()).$param" -IS_STRING $false $this.ToString() + " Getter: $($this.Cmd($param))" | Write-Debug
return $this.remote.Getter($this.Cmd($param))
} }
[string] Getter_String ($param) { [string] Getter_String ($param) {
return Param_Get -PARAM "$($this.identifier()).$param" -IS_STRING $true $this.ToString() + " Getter_String: $($this.Cmd($param))" | Write-Debug
return $this.remote.Getter_String($this.Cmd($param))
} }
[void] Setter ($param, $set) { [void] Setter ($param, $val) {
Param_Set -PARAM "$($this.identifier()).$param" -Value $set $this.ToString() + " Setter: $($this.Cmd($param))=$val" | Write-Debug
$this.remote.Setter($this.Cmd($param), $val)
}
[string] Cmd ($param) {
if ([string]::IsNullOrEmpty($param)) {
return $this.identifier()
}
return "$($this.identifier()).$param"
} }
} }
@@ -40,9 +44,13 @@ class Bus : IBus {
AddStringMembers -PARAMS @('label') AddStringMembers -PARAMS @('label')
AddFloatMembers -PARAMS @('gain', 'returnreverb', 'returndelay', 'returnfx1', 'returnfx2') AddFloatMembers -PARAMS @('gain', 'returnreverb', 'returndelay', 'returnfx1', 'returnfx2')
$this.mode = [Mode]::new($index, $remote) $this.mode = [BusMode]::new($index, $remote)
$this.eq = [Eq]::new($index, $remote) $this.eq = [BusEq]::new($index, $remote)
$this.levels = [Levels]::new($index, $remote) $this.levels = [BusLevels]::new($index, $remote)
}
[string] ToString() {
return $this.GetType().Name + $this.index
} }
[void] FadeTo ([single]$target, [int]$time) { [void] FadeTo ([single]$target, [int]$time) {
@@ -54,11 +62,11 @@ class Bus : IBus {
} }
} }
class Levels : IBus { class BusLevels : IBus {
[int]$init [int]$init
[int]$offset [int]$offset
Levels ([int]$index, [Object]$remote) : base ($index, $remote) { BusLevels ([int]$index, [Object]$remote) : base ($index, $remote) {
$this.init = $index * 8 $this.init = $index * 8
$this.offset = 8 $this.offset = 8
} }
@@ -68,7 +76,7 @@ class Levels : IBus {
return [math]::Round(20 * [math]::Log10($val), 1) return [math]::Round(20 * [math]::Log10($val), 1)
} }
else { else {
return -200.0 return - 200.0
} }
} }
@@ -85,10 +93,10 @@ class Levels : IBus {
} }
} }
class Mode : IBus { class BusMode : IBus {
[System.Collections.ArrayList]$modes [System.Collections.ArrayList]$modes
Mode ([int]$index, [Object]$remote) : base ($index, $remote) { BusMode ([int]$index, [Object]$remote) : base ($index, $remote) {
$this.modes = @( $this.modes = @(
'normal', 'amix', 'bmix', 'repeat', 'composite', 'tvmix', 'upmix21', 'upmix41', 'upmix61', 'normal', 'amix', 'bmix', 'repeat', 'composite', 'tvmix', 'upmix21', 'upmix41', 'upmix61',
'centeronly', 'lfeonly', 'rearonly' 'centeronly', 'lfeonly', 'rearonly'
@@ -111,8 +119,8 @@ class Mode : IBus {
} }
} }
class Eq : IBus { class BusEq : IBus {
Eq ([int]$index, [Object]$remote) : base ($index, $remote) { BusEq ([int]$index, [Object]$remote) : base ($index, $remote) {
AddBoolMembers -PARAMS @('on', 'ab') AddBoolMembers -PARAMS @('on', 'ab')
} }
@@ -125,12 +133,12 @@ class PhysicalBus : Bus {
[Object]$device [Object]$device
PhysicalBus ([int]$index, [Object]$remote) : base ($index, $remote) { PhysicalBus ([int]$index, [Object]$remote) : base ($index, $remote) {
$this.device = [Device]::new($index, $remote) $this.device = [BusDevice]::new($index, $remote)
} }
} }
class Device : IBus { class BusDevice : IBus {
Device ([int]$index, [Object]$remote) : base ($index, $remote) { BusDevice ([int]$index, [Object]$remote) : base ($index, $remote) {
} }
[string] identifier () { [string] identifier () {

View File

@@ -1,8 +1,10 @@
. $PSScriptRoot\meta.ps1
class Special { class Special {
Special () { [Object]$remote
Special ([Object]$remote) {
AddActionMembers -PARAMS @('restart', 'shutdown', 'show') AddActionMembers -PARAMS @('restart', 'shutdown', 'show')
$this.remote = $remote
} }
[string] identifier () { [string] identifier () {
@@ -14,18 +16,28 @@ class Special {
} }
[single] Getter ($param) { [single] Getter ($param) {
return Param_Get -PARAM "$($this.identifier()).$param" -IS_STRING $false return $this.remote.Getter("$($this.identifier()).$param")
} }
[void] Setter ($param, $val) { [void] Setter ($param, $val) {
if ($val -is [Boolean]) { if ($val -is [Boolean]) {
Param_Set -PARAM "$($this.identifier()).$param" -Value $(if ($val) { 1 } else { 0 }) $this.remote.Setter("$($this.identifier()).$param", $(if ($val) { 1 } else { 0 }))
} }
else { else {
Param_Set -PARAM "$($this.identifier()).$param" -Value $val $this.remote.Setter("$($this.identifier()).$param", $val)
} }
} }
[void] RunMacrobuttons() {
"Launching the MacroButtons app" | Write-Verbose
Start-Process -FilePath $(Join-Path -Path $this.remote.vmpath -ChildPath "VoicemeeterMacroButtons.exe")
}
[void] CloseMacrobuttons() {
"Closing the MacroButtons app" | Write-Verbose
Stop-Process -Name "VoicemeeterMacroButtons"
}
hidden $_hide = $($this | Add-Member ScriptProperty 'hide' ` hidden $_hide = $($this | Add-Member ScriptProperty 'hide' `
{ {
$this._hide = $this.Setter('show', $false) $this._hide = $this.Setter('show', $false)
@@ -58,6 +70,6 @@ class Special {
} }
} }
function Make_Command { function Make_Command([Object]$remote) {
return [Special]::new() return [Special]::new($remote)
} }

View File

@@ -1,30 +1,19 @@
class VMRemoteError : Exception { class VMRemoteError : Exception {
[string]$msg VMRemoteError ([string]$msg) : base ($msg) {
VMRemoteError ([string]$msg) {
$this.msg = $msg
}
[string] ErrorMessage () {
return $this.msg
} }
} }
class LoginError : VMRemoteError { class LoginError : VMRemoteError {
LoginError ([string]$msg) : base ([string]$msg) { LoginError ([string]$msg) : base ($msg) {
} }
} }
class CAPIError : VMRemoteError { class CAPIError : VMRemoteError {
[int]$retval [int]$code
[string]$caller [string]$function
CAPIError ([int]$retval, [string]$caller) { CAPIError ([int]$code, [string]$function) : base ("$function returned $code") {
$this.retval = $retval $this.code = $code
$this.caller = $caller $this.function = $function
} }
}
[string] ErrorMessage () {
return "CAPI return value: {0} in {1}" -f $this.retval, $this.caller
}
}

View File

@@ -1,8 +1,19 @@
function Get_VBPath { function Get_VMPath {
$reg_path = "Registry::HKEY_LOCAL_MACHINE\Software" + ` $REG_KEY = @(
(& { if ([Environment]::Is64BitOperatingSystem) { "\WOW6432Node" } else { "" } }) + ` "Registry::HKEY_LOCAL_MACHINE",
"\Microsoft\Windows\CurrentVersion\Uninstall" "Software",
$vm_key = "\VB:Voicemeeter {17359A74-1236-5467}\" (& { if ([Environment]::Is64BitOperatingSystem) { "WOW6432Node" } else { "" } }),
"Microsoft",
"Windows",
"CurrentVersion",
"Uninstall"
).Where({ $_ -ne "" }) -Join "\"
$VM_KEY = "VB:Voicemeeter {17359A74-1236-5467}"
return $(Get-ItemPropertyValue -Path ($reg_path + $vm_key) -Name UninstallString | Split-Path -Parent) try {
return $(Get-ItemPropertyValue -Path (@($REG_KEY, $VM_KEY) -Join "\") -Name UninstallString | Split-Path -Parent)
}
catch {
throw [VMRemoteError]::new("Unable to fetch Voicemeeter path from the Registry.")
}
} }

View File

@@ -1,3 +1,9 @@
enum ButtonTypes {
State = 1
StateOnly = 2
Trigger = 3
}
class MacroButton { class MacroButton {
[int32]$index [int32]$index
@@ -10,40 +16,42 @@ class MacroButton {
} }
[int] Getter ($mode) { [int] Getter ($mode) {
"Button[$($this.index)].$([ButtonTypes].GetEnumName($mode))" | Write-Debug
return MB_Get -Id $this.index -Mode $mode return MB_Get -Id $this.index -Mode $mode
} }
[void] Setter ($set, $mode) { [void] Setter ($val, $mode) {
MB_Set -Id $this.index -SET $set -Mode $mode "Button[$($this.index)].$([ButtonTypes].GetEnumName($mode))=$val" | Write-Debug
MB_Set -Id $this.index -SET $val -Mode $mode
} }
hidden $_state = $($this | Add-Member ScriptProperty 'state' ` hidden $_state = $($this | Add-Member ScriptProperty 'state' `
{ {
[bool]$this.Getter(1) [bool]$this.Getter([ButtonTypes]::State)
} ` } `
{ {
param($arg) param($arg)
$this._state = $this.Setter($arg, 1) $this._state = $this.Setter($arg, [ButtonTypes]::State)
} }
) )
hidden $_stateonly = $($this | Add-Member ScriptProperty 'stateonly' ` hidden $_stateonly = $($this | Add-Member ScriptProperty 'stateonly' `
{ {
[bool]$this.Getter(2) [bool]$this.Getter([ButtonTypes]::StateOnly)
} ` } `
{ {
param($arg) param($arg)
$this._stateonly = $this.Setter($arg, 2) $this._stateonly = $this.Setter($arg, [ButtonTypes]::StateOnly)
} }
) )
hidden $_trigger = $($this | Add-Member ScriptProperty 'trigger' ` hidden $_trigger = $($this | Add-Member ScriptProperty 'trigger' `
{ {
[bool]$this.Getter(3) [bool]$this.Getter([ButtonTypes]::Trigger)
} ` } `
{ {
param($arg) param($arg)
$this._trigger = $this.Setter($arg, 3) $this._trigger = $this.Setter($arg, [ButtonTypes]::Trigger)
} }
) )
} }

View File

@@ -24,14 +24,9 @@ function Set_Profile {
param( param(
[Object]$DATA, [string]$CONF [Object]$DATA, [string]$CONF
) )
try { if ($null -eq $DATA -or -not $DATA.$CONF) {
if ($null -eq $DATA -or -not $DATA.$CONF) { throw [VMRemoteErrors]::new("No profile named '$CONF' has been loaded into memory.")
throw [VMRemoteErrors]::new("No profile named $CONF was loaded")
}
Param_Set_Multi -HASH $DATA.$CONF
Start-Sleep -m 1
}
catch [VMRemoteErrors] {
Write-Warning $_.Exception.ErrorMessage()
} }
Param_Set_Multi -HASH $DATA.$CONF
Start-Sleep -m 1
} }

View File

@@ -1,12 +1,52 @@
. $PSScriptRoot\meta.ps1 class IRecorder {
class Recorder {
[Object]$remote [Object]$remote
Recorder ([Object]$remote) { IRecorder ([Object]$remote) {
$this.remote = $remote $this.remote = $remote
}
[single] Getter ($param) {
$this.Cmd($param) | Write-Debug
return $this.remote.Getter($this.Cmd($param))
}
[void] Setter ($param, $val) {
"$($this.Cmd($param))=$val" | Write-Debug
if ($val -is [Boolean]) {
$this.remote.Setter($this.Cmd($param), $(if ($val) { 1 } else { 0 }))
}
else {
$this.remote.Setter($this.Cmd($param), $val)
}
}
[string] Cmd ($param) {
if ([string]::IsNullOrEmpty($param)) {
return $this.identifier()
}
return "$($this.identifier()).$param"
}
}
class Recorder : IRecorder {
[Object]$remote
[Object]$mode
[System.Collections.ArrayList]$armstrip
[System.Collections.ArrayList]$armbus
Recorder ([Object]$remote) : base ($remote) {
$this.mode = [RecorderMode]::new($remote)
$this.armstrip = @()
0..($remote.kind.p_in + $remote.kind.v_in - 1) | ForEach-Object {
$this.armstrip.Add([RecorderArmStrip]::new($_, $remote))
}
$this.armbus = @()
0..($remote.kind.p_out + $remote.kind.v_out - 1) | ForEach-Object {
$this.armbus.Add([RecorderArmBus]::new($_, $remote))
}
AddActionMembers -PARAMS @('play', 'stop', 'pause', 'replay', 'record', 'ff', 'rew') AddActionMembers -PARAMS @('play', 'stop', 'pause', 'replay', 'record', 'ff', 'rew')
AddFloatMembers -PARAMS @('gain')
AddChannelMembers AddChannelMembers
} }
@@ -18,32 +58,146 @@ class Recorder {
return $this.GetType().Name return $this.GetType().Name
} }
[single] Getter ($param) {
return Param_Get -PARAM "$($this.identifier()).$param" -IS_STRING $false
}
[void] Setter ($param, $val) {
if ($val -is [Boolean]) {
Param_Set -PARAM "$($this.identifier()).$param" -Value $(if ($val) { 1 } else { 0 })
}
else {
Param_Set -PARAM "$($this.identifier()).$param" -Value $val
}
}
hidden $_loop = $($this | Add-Member ScriptProperty 'loop' ` hidden $_loop = $($this | Add-Member ScriptProperty 'loop' `
{ {
return Write-Warning ("ERROR: $($this.identifier()).mode.loop is write only") [bool]$this.mode.loop
} ` } `
{ {
param([bool]$arg) param($arg)
$this._loop = $this.Setter('mode.loop', $arg) $this.mode.loop = $arg
}
)
hidden $_samplerate = $($this | Add-Member ScriptProperty 'samplerate' `
{
$this.Getter('samplerate')
} `
{
param([int]$arg)
$opts = @(22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000)
if ($opts.Contains($arg)) {
$this._samplerate = $this.Setter('samplerate', $arg)
}
else {
"samplerate got: $arg, expected one of $opts" | Write-Warning
}
}
)
hidden $_bitresolution = $($this | Add-Member ScriptProperty 'bitresolution' `
{
$this.Getter('bitresolution')
} `
{
param([int]$arg)
$opts = @(8, 16, 24, 32)
if ($opts.Contains($arg)) {
$this._bitresolution = $this.Setter('bitresolution', $arg)
}
else {
"bitresolution got: $arg, expected one of $opts" | Write-Warning
}
}
)
hidden $_channel = $($this | Add-Member ScriptProperty 'channel' `
{
$this.Getter('channel')
} `
{
param([int]$arg)
if ($arg -ge 1 -and $arg -le 8) {
$this._channel = $this.Setter('channel', $arg)
}
else {
"channel got: $arg, expected value from 1 to 8" | Write-Warning
}
}
)
hidden $_kbps = $($this | Add-Member ScriptProperty 'kbps' `
{
$this.Getter('kbps')
} `
{
param([int]$arg)
$opts = @(32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320)
if ($opts.Contains($arg)) {
$this._kbps = $this.Setter('kbps', $arg)
}
else {
"kbps got: $arg, expected one of $opts" | Write-Warning
}
} }
) )
[void] Load ([string]$filename) { [void] Load ([string]$filename) {
$this.Setter('load', $filename) $this.Setter('load', $filename)
} }
[void] GoTo ([string]$timestring) {
try {
if ([datetime]::ParseExact($timestring, "HH:mm:ss", $null)) {
$timespan = [timespan]::Parse($timestring)
$this.Setter("GoTo", $timespan.TotalSeconds)
}
}
catch [FormatException] {
"Time string $timestring does not match the required format 'hh:mm:ss'" | Write-Warning
}
}
[void] FileType($format) {
[int]$val = 0
switch ($format) {
"wav" { $val = 1 }
"aiff" { $val = 2 }
"bwf" { $val = 3 }
"mp3" { $val = 100 }
default { "Filetype() got: $format, expected one of 'wav', 'aiff', 'bwf', 'mp3'" }
}
$this.Setter("filetype", $val)
}
}
class RecorderMode : IRecorder {
RecorderMode ([Object]$remote) : base ($remote) {
AddBoolMembers -PARAMS @('recbus', 'playonload', 'loop', 'multitrack')
}
[string] identifier () {
return "Recorder.Mode"
}
}
class RecorderArm : IRecorder {
[int]$index
RecorderArm ([int]$index, [Object]$remote) : base ($remote) {
$this.index = $index
}
Set ([bool]$val) {
$this.Setter("", $(if ($val) { 1 } else { 0 }))
}
}
class RecorderArmStrip : RecorderArm {
RecorderArmStrip ([int]$index, [Object]$remote) : base ($index, $remote) {
}
[string] identifier () {
return "Recorder.ArmStrip[$($this.index)]"
}
}
class RecorderArmBus : RecorderArm {
RecorderArmBus ([int]$index, [Object]$remote) : base ($index, $remote) {
}
[string] identifier () {
return "Recorder.ArmBus[$($this.index)]"
}
} }
function Make_Recorder ([Object]$remote) { function Make_Recorder ([Object]$remote) {

View File

@@ -1,5 +1,3 @@
. $PSScriptRoot\meta.ps1
class IStrip { class IStrip {
[int]$index [int]$index
[Object]$remote [Object]$remote
@@ -14,15 +12,25 @@ class IStrip {
} }
[single] Getter ($param) { [single] Getter ($param) {
return Param_Get -PARAM "$($this.identifier()).$param" -IS_STRING $false $this.Cmd($param) | Write-Debug
return $this.remote.Getter($this.Cmd($param))
} }
[string] Getter_String ($param) { [string] Getter_String ($param) {
return Param_Get -PARAM "$($this.identifier()).$param" -IS_STRING $true $this.Cmd($param) | Write-Debug
return $this.remote.Getter_String($this.Cmd($param))
} }
[void] Setter ($param, $val) { [void] Setter ($param, $val) {
Param_Set -PARAM "$($this.identifier()).$param" -Value $val "$($this.Cmd($param))=$val" | Write-Debug
$this.remote.Setter($this.Cmd($param), $val)
}
[string] Cmd ($param) {
if ([string]::IsNullOrEmpty($param)) {
return $this.identifier()
}
return "$($this.identifier()).$param"
} }
} }
@@ -38,7 +46,7 @@ class Strip : IStrip {
AddChannelMembers AddChannelMembers
AddGainlayerMembers AddGainlayerMembers
$this.levels = [Levels]::new($index, $remote) $this.levels = [StripLevels]::new($index, $remote)
} }
[string] ToString() { [string] ToString() {
@@ -54,11 +62,11 @@ class Strip : IStrip {
} }
} }
class Levels : IStrip { class StripLevels : IStrip {
[int]$init [int]$init
[int]$offset [int]$offset
Levels ([int]$index, [Object]$remote) : base ($index, $remote) { StripLevels ([int]$index, [Object]$remote) : base ($index, $remote) {
$p_in = $remote.kind.p_in $p_in = $remote.kind.p_in
if ($index -lt $p_in) { if ($index -lt $p_in) {
$this.init = $index * 2 $this.init = $index * 2
@@ -112,16 +120,16 @@ class PhysicalStrip : Strip {
AddFloatMembers -PARAMS @('reverb', 'delay', 'fx1', 'fx2') AddFloatMembers -PARAMS @('reverb', 'delay', 'fx1', 'fx2')
AddBoolMembers -PARAMS @('postreverb', 'postdelay', 'postfx1', 'postfx2') AddBoolMembers -PARAMS @('postreverb', 'postdelay', 'postfx1', 'postfx2')
$this.comp = [Comp]::new($index, $remote) $this.comp = [StripComp]::new($index, $remote)
$this.gate = [Gate]::new($index, $remote) $this.gate = [StripGate]::new($index, $remote)
$this.denoiser = [Denoiser]::new($index, $remote) $this.denoiser = [StripDenoiser]::new($index, $remote)
$this.eq = [Eq]::new($index, $remote) $this.eq = [StripEq]::new($index, $remote)
$this.device = [Device]::new($index, $remote) $this.device = [StripDevice]::new($index, $remote)
} }
} }
class Comp : IStrip { class StripComp : IStrip {
Comp ([int]$index, [Object]$remote) : base ($index, $remote) { StripComp ([int]$index, [Object]$remote) : base ($index, $remote) {
AddFloatMembers -PARAMS @('gainin', 'ratio', 'threshold', 'attack', 'release', 'knee', 'gainout') AddFloatMembers -PARAMS @('gainin', 'ratio', 'threshold', 'attack', 'release', 'knee', 'gainout')
AddBoolMembers -PARAMS @('makeup') AddBoolMembers -PARAMS @('makeup')
} }
@@ -141,8 +149,8 @@ class Comp : IStrip {
) )
} }
class Gate : IStrip { class StripGate : IStrip {
Gate ([int]$index, [Object]$remote) : base ($index, $remote) { StripGate ([int]$index, [Object]$remote) : base ($index, $remote) {
AddFloatMembers -PARAMS @('threshold', 'damping', 'bpsidechain', 'attack', 'hold', 'release') AddFloatMembers -PARAMS @('threshold', 'damping', 'bpsidechain', 'attack', 'hold', 'release')
} }
@@ -161,8 +169,8 @@ class Gate : IStrip {
) )
} }
class Denoiser : IStrip { class StripDenoiser : IStrip {
Denoiser ([int]$index, [Object]$remote) : base ($index, $remote) { StripDenoiser ([int]$index, [Object]$remote) : base ($index, $remote) {
} }
[string] identifier () { [string] identifier () {
@@ -180,8 +188,8 @@ class Denoiser : IStrip {
) )
} }
class Eq : IStrip { class StripEq : IStrip {
Eq ([int]$index, [Object]$remote) : base ($index, $remote) { StripEq ([int]$index, [Object]$remote) : base ($index, $remote) {
AddBoolMembers -PARAMS @('on', 'ab') AddBoolMembers -PARAMS @('on', 'ab')
} }
@@ -190,8 +198,8 @@ class Eq : IStrip {
} }
} }
class Device : IStrip { class StripDevice : IStrip {
Device ([int]$index, [Object]$remote) : base ($index, $remote) { StripDevice ([int]$index, [Object]$remote) : base ($index, $remote) {
} }
[string] identifier () { [string] identifier () {

View File

@@ -1,9 +1,11 @@
class IVban { class IVban {
[int32]$index [int32]$index
[Object]$remote
[string]$direction [string]$direction
IVban ([int]$index, [string]$direction) { IVban ([int]$index, [Object]$remote, [string]$direction) {
$this.index = $index $this.index = $index
$this.remote = $remote
$this.direction = $direction $this.direction = $direction
} }
@@ -11,25 +13,34 @@ class IVban {
return "vban." + $this.direction + "stream[" + $this.index + "]" return "vban." + $this.direction + "stream[" + $this.index + "]"
} }
[string] ToString() {
return $this.GetType().Name + $this.index
}
[single] Getter ($param) { [single] Getter ($param) {
return Param_Get -PARAM "$($this.identifier()).$param" -IS_STRING $false return $this.remote.Getter($this.Cmd($param))
} }
[string] Getter_String ($param) { [string] Getter_String ($param) {
return Param_Get -PARAM "$($this.identifier()).$param" -IS_STRING $true $this.Cmd($param) | Write-Debug
return $this.remote.Getter_String($this.Cmd($param))
} }
[void] Setter ($param, $val) { [void] Setter ($param, $val) {
Param_Set -PARAM "$($this.identifier()).$param" -Value $val "$($this.Cmd($param))=$val" | Write-Debug
$this.remote.Setter($this.Cmd($param), $val)
}
[string] Cmd ($param) {
if ([string]::IsNullOrEmpty($param)) {
return $this.identifier()
}
return "$($this.identifier()).$param"
} }
} }
class Vban : IVban { class Vban : IVban {
Vban ([int]$index, [string]$direction) : base ($index, $direction) { Vban ([int]$index, [Object]$remote, [string]$direction) : base ($index, $remote, $direction) {
}
[string] ToString() {
return $this.GetType().Name + $this.index
} }
hidden $_on = $($this | Add-Member ScriptProperty 'on' ` hidden $_on = $($this | Add-Member ScriptProperty 'on' `
@@ -68,7 +79,7 @@ class Vban : IVban {
} ` } `
{ {
param([string]$arg) param([string]$arg)
if ($arg -in 1024..65535) { if ($arg -ge 1024 -and $arg -le 65535) {
$this._port = $this.Setter('port', $arg) $this._port = $this.Setter('port', $arg)
} }
else { else {
@@ -104,7 +115,7 @@ class Vban : IVban {
param([int]$arg) param([int]$arg)
if ($this.direction -eq "in") { Write-Warning ('Error, read only value') } if ($this.direction -eq "in") { Write-Warning ('Error, read only value') }
else { else {
if ($arg -in 1..8) { if ($arg -ge 1 -and $arg -le 8) {
$this._channel = $this.Setter('channel', $arg) $this._channel = $this.Setter('channel', $arg)
} }
else { else {
@@ -142,7 +153,7 @@ class Vban : IVban {
param([int]$arg) param([int]$arg)
if ($this.direction -eq "in") { Write-Warning ('Error, read only value') } if ($this.direction -eq "in") { Write-Warning ('Error, read only value') }
else { else {
if ($arg -in 0..4) { if ($arg -ge 0 -and $arg -le 4) {
$this._quality = $this.Setter('quality', $arg) $this._quality = $this.Setter('quality', $arg)
} }
else { else {
@@ -160,7 +171,7 @@ class Vban : IVban {
param([int]$arg) param([int]$arg)
if ($this.direction -eq "in") { Write-Warning ('Error, read only value') } if ($this.direction -eq "in") { Write-Warning ('Error, read only value') }
else { else {
if ($arg -in 0..8) { if ($arg -ge 0 -and $arg -le 8) {
$this._route = $this.Setter('route', $arg) $this._route = $this.Setter('route', $arg)
} }
else { else {
@@ -173,13 +184,13 @@ class Vban : IVban {
class VbanInstream : Vban { class VbanInstream : Vban {
VbanInstream ([int]$index, [string]$direction) : base ($index, $direction) { VbanInstream ([int]$index, [Object]$remote, [string]$direction) : base ($index, $remote, $direction) {
} }
} }
class VbanOutstream : Vban { class VbanOutstream : Vban {
VbanOutstream ([int]$index, [string]$direction) : base ($index, $direction) { VbanOutstream ([int]$index, [Object]$remote, [string]$direction) : base ($index, $remote, $direction) {
} }
} }
@@ -189,10 +200,10 @@ function Make_Vban ([Object]$remote) {
[System.Collections.ArrayList]$outstream = @() [System.Collections.ArrayList]$outstream = @()
0..$($remote.kind.vban_in - 1) | ForEach-Object { 0..$($remote.kind.vban_in - 1) | ForEach-Object {
[void]$instream.Add([VbanInstream]::new($_, "in")) [void]$instream.Add([VbanInstream]::new($_, $remote, "in"))
} }
0..$($remote.kind.vban_out - 1) | ForEach-Object { 0..$($remote.kind.vban_out - 1) | ForEach-Object {
[void]$outstream.Add([VbanOutstream]::new($_, "out")) [void]$outstream.Add([VbanOutstream]::new($_, $remote, "out"))
} }
$CustomObject = [pscustomobject]@{ $CustomObject = [pscustomobject]@{

View File

@@ -34,6 +34,7 @@ Function ParseLog {
function main() { function main() {
try { try {
$vmr = Connect-Voicemeeter -Kind $kind $vmr = Connect-Voicemeeter -Kind $kind
$vmr.command.RunMacrobuttons() # ensure macrobuttons is running before we begin
Write-Host "Running tests for $vmr" Write-Host "Running tests for $vmr"
# test boundaries by kind # test boundaries by kind
@@ -72,5 +73,4 @@ function main() {
} }
main
if ($MyInvocation.InvocationName -ne '.') { main }