33 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
5bda43131b upd changelog 2023-08-09 14:44:47 +01:00
1c9c400f12 VBVMR_GetLevel binding added
Get_Level implemented in base.ps1

strip.{PreFader,PostFader,PostMute} methods added

bus.{All} added
2023-08-09 14:16:27 +01:00
aee3430962 fix strip[i].device.name bug
patch bump
2022-12-19 23:19:20 +00:00
47fb880b91 remove tasklist from unreleased 2022-12-19 00:45:13 +00:00
acc078632d add strip.eq/bus.eq example to readme 2022-12-18 18:52:22 +00:00
19 changed files with 992 additions and 508 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,12 +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
- [x] Implement command.load
- [x] Implement comp/gate parameters introduced in v3.0.2.8 of the api.
- [x] Add unit tests for new classes.
- [x] Update README with changes to Strip/Bus classes.
- [ ]
## [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:

180
README.md
View File

@@ -8,9 +8,9 @@ For past/future changes to this project 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
@@ -79,7 +79,7 @@ Voicemeeter factory function can be:
- Get-RemoteBanana
- Get-RemotePotato
Added in v3 you may also use the following entry/exit points:
Added in `v3` you may also use the following entry/exit points:
- Connect-Voicemeeter
- Disconnect-Voicemeeter
@@ -90,6 +90,8 @@ for example:
```powershell
$vmr = Connect-Voicemeeter -Kind "potato"
...
Disconnect-Voicemeeter
```
#### `Through the Shell`
@@ -113,13 +115,13 @@ $vmr.Logout()
The following strip commands are available:
- mute: boolean
- mono: boolean
- mc: boolean
- mute: bool
- mono: bool
- mc: bool
- k: int, from 0 to 4
- solo: boolean
- A1-A5: boolean
- B1-B3: boolean
- solo: bool
- A1-A5: bool
- B1-B3: bool
- limit: int, from -40 to 12
- gain: float, from -60.0 to 12.0
- label: string
@@ -133,10 +135,10 @@ The following strip commands are available:
- color_y: float, from 0.0 to 1.0
- fx_x: float, from -0.5 to 0.5
- fx_y: float, from 0.0 to 1.0
- postreverb: boolean
- postdelay: boolean
- postfx1: boolean
- postfx2: boolean
- postreverb: bool
- postdelay: bool
- postfx1: bool
- postfx2: bool
- gainlayer0-gainlayer7: float
for example:
@@ -163,7 +165,7 @@ The following strip.comp commands are available:
- release: float, from 0.0 to 5000.0
- knee: float, 0.0 to 1.0
- gainout: float, from -24.0 to 24.0
- makeup: boolean
- makeup: bool
for example:
@@ -204,7 +206,7 @@ $vmr.strip[3].denoiser.knob = 5
#### AppGain | AppMute
- `AppGain(amount, gain)` : string, float
- `AppMute(amount, mutestate)` : string, boolean
- `AppMute(amount, mutestate)` : string, bool
for example:
@@ -213,6 +215,20 @@ $vmr.strip[5].AppGain("Spotify", 0.5)
$vmr.strip[5].AppMute("Spotify", $true)
```
#### levels
The following strip.level commands are available:
- PreFader()
- PostFader()
- PostMute()
for example:
```powershell
$vmr.strip[2].levels.PreFader() -Join ', ' | Write-Host
```
### Bus
The following bus commands are available:
@@ -237,18 +253,18 @@ $vmr.bus[3].returnreverb = 5.7
The following bus.mode members are available:
- normal: boolean
- amix: boolean
- bmix: boolean
- repeat: boolean
- composite: boolean
- tvmix: boolean
- upmix21: boolean
- upmix41: boolean
- upmix61: boolean
- centeronly: boolean
- lfeonly: boolean
- rearonly: boolean
- normal: bool
- amix: bool
- bmix: bool
- repeat: bool
- composite: bool
- tvmix: bool
- upmix21: bool
- upmix41: bool
- upmix61: bool
- centeronly: bool
- lfeonly: bool
- rearonly: bool
The following bus.mode commands are available:
@@ -262,6 +278,18 @@ $vmr.bus[0].mode.centeronly = $true
$vmr.bus[0].mode.Get()
```
#### levels
The following strip.level commands are available:
- All()
for example:
```powershell
$vmr.bus[2].levels.All() -Join ', ' | Write-Host
```
### Strip|Bus
#### device
@@ -289,8 +317,15 @@ wdm, ks, mme, asio are defined as write only.
The following strip.eq | bus.eq commands are available:
- on: boolean
- ab: boolean
- on: bool
- ab: bool
for example:
```powershell
$vmr.strip[0].eq.on = $true
$vmr.bus[0].eq.ab = $false
```
#### FadeTo | FadeBy
@@ -324,19 +359,19 @@ $vmr.button[5].trigger = $true
### 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:
- on: boolean
- on: bool
- name: string
- ip: string
- port: int from 1024 - 65535
- sr: int (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000)
- port: int, from 1024 - 65535
- sr: in, (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000)
- channel: int from 1 to 8
- bit: int 16 or 24
- quality: int from 0 to 4
- route: int from 0 to 8
- bit: int, 16 or 24
- quality: int, from 0 to 4
- route: int, from 0 to 8
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.
@@ -355,18 +390,20 @@ $vmr.vban.outstream[3].bit = 16
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
- hide
- restart
- 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
- lock
- Load($filepath): string
- RunMacrobuttons(): Launches the macrobuttons app
- CloseMacrobuttons(): Closes the macrobuttons app
example:
@@ -376,11 +413,13 @@ $vmr.command.show
$vmr.command.lock = $true
$vmr.command.Load("path/to/filename.xml")
$vmr.command.RunMacrobuttons()
```
### Recorder
The following methods are available:
The following commands are available:
- play
- stop
@@ -388,19 +427,53 @@ The following methods are available:
- record
- ff
- 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
- A1 - A5
- B1 - B3
- Load($filepath): string
- GoTo($timestring): string, must match the format 'hh:mm:ss'
- FileType($format): string, ('wav', 'aiff', 'bwf', 'mp3')
example:
```powershell
$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
@@ -473,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.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 using .\tests\pre-commit.ps1 which accepts the following parameters:
@@ -488,4 +570,4 @@ Run tests from repository root in a subshell and write logs, like so:
### 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(
[switch]$interactive,
[switch]$output,
[String]$kind = "banana",
[String[]]$script = @()
)
Import-Module ..\..\lib\Voicemeeter.psm1
$VerbosePreference = "Continue"
function get-value {
param([object]$vmr, [string]$line)
try {
@@ -24,16 +22,16 @@ function msgHandler {
param([object]$vmr, [string]$line)
$line + " passed to handler" | Write-Debug
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)
$vmr.Setter($line.substring(1), 1 - $retval)
}
elseif ($line.Contains("=")) {
if ($output) { "Setting $line" | Write-Host }
"Setting $line" | Write-Debug
$vmr.SendText($line)
}
else {
if ($output) { "Getting $line" | Write-Host }
"Getting $line" | Write-Debug
$retval = get-value -vmr $vmr -line $line
$line + " = " + $retval | Write-Host
}
@@ -63,4 +61,4 @@ function main {
finally { Disconnect-Voicemeeter }
}
if ($MyInvocation.InvocationName -ne '.') { main }
main

View File

@@ -7,27 +7,30 @@
Credits go to @bobsupercow
#>
Import-Module ..\..\lib\Voicemeeter.psm1
[cmdletbinding()]
param()
$VerbosePreference = "Continue"
Import-Module ..\..\lib\Voicemeeter.psm1
try {
$vmr = Connect-Voicemeeter -Kind "potato"
$buses = @($vmr.bus[1], $vmr.bus[2], $vmr.bus[4], $vmr.bus[6])
"Buses in selection: $($buses)"
$unmutedIndex = $null
# 1)
"Cycling through bus selection to check for first unmuted Bus..." | Write-Host
foreach ($bus in $buses) {
# 2)
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)
$bus.mute = $true
# 3)
if ($buses[++$unmutedIndex]) {
"unmuting bus $($buses[$unmutedIndex].index)" | Write-Host
"Unmuting Bus $($buses[$unmutedIndex].index)" | Write-Host
$buses[$unmutedIndex].mute = $false
break
}
@@ -37,7 +40,7 @@ try {
# 4)
if ($null -eq $unmutedIndex) {
$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 obs-powershell
$VerbosePreference = "Continue"
function CurrentProgramSceneChanged {
param([System.Object]$data)
Write-Host "Switched to scene", $data.sceneName
@@ -10,19 +11,15 @@ function CurrentProgramSceneChanged {
switch ($data.sceneName) {
"START" {
$vmr.strip[0].mute = !$vmr.strip[0].mute
"Toggling Strip 0 mute"
}
"BRB" {
$vmr.strip[0].gain = -8.3
"Setting Strip 0 gain to -8.3"
}
"END" {
$vmr.strip[0].mono = $true
"Setting Strip 0 mono to `$true"
}
"LIVE" {
$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 }
}
@@ -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\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 {
[String]$vmpath
[Hashtable]$kind
[Object]$profiles
Remote ([String]$kindId) {
if (!(Setup_DLL)) {
Exit -1
}
$this.vmpath = Setup_DLL
$this.kind = GetKind($kindId)
$this.profiles = Get_Profiles($this.kind.name)
}
@@ -31,7 +39,7 @@ class Remote {
}
[String] GetVersion() {
return Version
return VmVersion
}
[void] Set_Profile([String]$config) {
@@ -75,7 +83,7 @@ class RemoteBasic : Remote {
$this.bus = Make_Buses($this)
$this.button = Make_Buttons
$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.button = Make_Buttons
$this.vban = Make_Vban($this)
$this.command = Make_Command
$this.command = Make_Command($this)
$this.recorder = Make_Recorder($this)
}
}
@@ -110,7 +118,7 @@ class RemotePotato : Remote {
$this.bus = Make_Buses($this)
$this.button = Make_Buttons
$this.vban = Make_Vban($this)
$this.command = Make_Command
$this.command = Make_Command($this)
$this.recorder = Make_Recorder($this)
}
}
@@ -129,7 +137,6 @@ Function Get-RemotePotato {
Function Connect-Voicemeeter {
param([String]$Kind)
try {
switch ($Kind) {
"basic" {
return Get-RemoteBasic
@@ -140,13 +147,10 @@ Function Connect-Voicemeeter {
"potato" {
return Get-RemotePotato
}
default { throw [LoginError]::new("Unknown Voicemeeter kind `"$Kind`"") }
default {
throw [LoginError]::new("Unknown Voicemeeter kind `"$Kind`"")
}
}
catch [LoginError], [CAPIError] {
Write-Warning $_.Exception.ErrorMessage()
throw
}
}
Function Disconnect-Voicemeeter {

View File

@@ -1,74 +1,95 @@
. $PSScriptRoot\errors.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 {
param(
[string]$kindId
)
try {
$retval = [int][Voicemeeter.Remote]::VBVMR_Login()
if (-not $retval) {
"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 })
if ($retval -notin @(0, 1, -2)) {
throw [CAPIError]::new($retval, "VBVMR_Login")
}
$retval = [int][Voicemeeter.Remote]::VBVMR_RunVoicemeeter([int64]$vmExe)
if (-not $retval) {
"STARTING VOICEMEETER" | Write-Verbose
Start-Sleep -s 1
switch ($retval) {
1 {
"Voicemeeter Engine running but GUI not launched. Launching GUI now." | Write-Verbose
RunVoicemeeter -kindId $kindId
}
else {
throw [CAPIError]::new($retval, $MyInvocation.MyCommand)
-2 {
throw [LoginError]::new("Login may only be called once per session.")
}
}
elseif ($retval -eq -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
}
else { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) }
catch [CAPIError] {
$exception = $_
$exception | Write-Debug
}
catch [LoginError] {
Write-Warning "$($_.Exception.ErrorMessage()). Fatal error, exiting..."
exit -2
} 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 }
"VERSION:[" + $(VmType).ToUpper() + "]" | Write-Verbose
}
function Logout {
Start-Sleep -m 20
Start-Sleep -m 100
$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 {
[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 {
[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 {
New-Variable -Name ptr -Value 0
$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) {
1 { return "basic" }
2 { return "banana" }
@@ -76,10 +97,12 @@ function VmType {
}
}
function Version {
function VmVersion {
New-Variable -Name ptr -Value 0
$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
$v2 = ($ptr -band 0x00FF0000) -shr 16
$v3 = ($ptr -band 0x0000FF00) -shr 8
@@ -92,28 +115,22 @@ function Param_Get {
param(
[string]$PARAM, [bool]$IS_STRING = $false
)
Start-Sleep -m 50
Start-Sleep -m 30
while (P_Dirty) { Start-Sleep -m 1 }
if ($IS_STRING) {
$BYTES = [System.Byte[]]::new(512)
try {
$retval = [int][Voicemeeter.Remote]::VBVMR_GetParameterStringA($PARAM, $BYTES)
if ($retval) { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) }
}
catch [CAPIError] {
Write-Warning $_.Exception.ErrorMessage()
if ($retval -notin @(0)) {
throw [CAPIError]::new($retval, "VBVMR_GetParameterStringA")
}
[System.Text.Encoding]::ASCII.GetString($BYTES).Trim([char]0)
}
else {
New-Variable -Name ptr -Value 0.0
try {
$retval = [int][Voicemeeter.Remote]::VBVMR_GetParameterFloat($PARAM, [ref]$ptr)
if ($retval) { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) }
}
catch [CAPIError] {
Write-Warning $_.Exception.ErrorMessage()
if ($retval -notin @(0)) {
throw [CAPIError]::new($retval, "VBVMR_GetParameterFloat")
}
[single]$ptr
}
@@ -123,17 +140,17 @@ function Param_Set {
param(
[string]$PARAM, [Object]$VALUE
)
try {
if ($VALUE -is [string]) {
$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 -notin @(0)) {
throw [CAPIError]::new($retval, "VBVMR_SetParameterFloat")
}
if ($retval) { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) }
}
catch [CAPIError] {
Write-Warning $_.Exception.ErrorMessage()
}
}
@@ -141,12 +158,9 @@ function MB_Set {
param(
[int64]$ID, [single]$SET, [int64]$MODE
)
try {
$retval = [int][Voicemeeter.Remote]::VBVMR_MacroButton_SetStatus($ID, $SET, $MODE)
if ($retval) { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) }
}
catch [CAPIError] {
Write-Warning $_.Exception.ErrorMessage()
if ($retval -notin @(0)) {
throw [CAPIError]::new($retval, "VBVMR_MacroButton_SetStatus")
}
}
@@ -158,12 +172,9 @@ function MB_Get {
while (M_Dirty) { Start-Sleep -m 1 }
New-Variable -Name ptr -Value 0.0
try {
$retval = [int][Voicemeeter.Remote]::VBVMR_MacroButton_GetStatus($ID, [ref]$ptr, $MODE)
if ($retval) { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) }
}
catch [CAPIError] {
Write-Warning $_.Exception.ErrorMessage()
if ($retval -notin @(0)) {
throw [CAPIError]::new($retval, "VBVMR_MacroButton_GetStatus")
}
[int]$ptr
}
@@ -195,11 +206,23 @@ function Set_By_Script {
param(
[string]$script
)
try {
$retval = [int][Voicemeeter.Remote]::VBVMR_SetParameters($script)
if ($retval) { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) }
if ($script.Length -gt 48000) {
throw [VMRemoteError]::new("Script size cannot be larger than 48kB")
}
catch [CAPIError] {
Write-Warning $_.Exception.ErrorMessage()
$retval = [int][Voicemeeter.Remote]::VBVMR_SetParameters($script)
if ($retval -notin @(0)) {
throw [CAPIError]::new($retval, "VBVMR_SetParameters")
}
}
function Get_Level {
param(
[int64]$MODE, [int64]$INDEX
)
New-Variable -Name ptr -Value 0.0
$retval = [int][Voicemeeter.Remote]::VBVMR_GetLevel($MODE, $INDEX, [ref]$ptr)
if ($retval -notin @(0)) {
throw [CAPIError]::new($retval, "VBVMR_GetLevel")
}
[float]$ptr
}

View File

@@ -1,18 +1,11 @@
function Setup_DLL {
try {
$vb_path = Get_VBPath
. $PSScriptRoot\inst.ps1
if ([string]::IsNullOrWhiteSpace($vb_path)) {
throw [VMRemoteError]::new("couldn't get Voicemeeter path")
}
$dll = Join-Path -Path $vb_path -ChildPath ("VoicemeeterRemote" + `
function Setup_DLL {
$VMPATH = Get_VMPath
$dll = Join-Path -Path $VMPATH -ChildPath ("VoicemeeterRemote" + `
(& { if ([Environment]::Is64BitOperatingSystem) { "64" } else { "" } }) + `
".dll")
}
catch [VMRemoteError] {
Write-Warning $_.Exception.ErrorMessage()
return $false
}
$Signature = @"
[DllImport(@"$dll")]
@@ -47,8 +40,11 @@ function Setup_DLL {
[DllImport(@"$dll")]
public static extern int VBVMR_SetParameters(String param);
[DllImport(@"$dll")]
public static extern int VBVMR_GetLevel(Int64 mode, Int64 index, ref float ptr);
"@
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 {
[int]$index
[Object]$remote
@@ -13,34 +11,46 @@ class IBus {
return "Bus[" + $this.index + "]"
}
[string] ToString() {
return $this.GetType().Name + $this.index
}
[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) {
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) {
Param_Set -PARAM "$($this.identifier()).$param" -Value $set
[void] Setter ($param, $val) {
$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"
}
}
class Bus : IBus {
[Object]$mode
[Object]$eq
[Object]$levels
Bus ([int]$index, [Object]$remote) : base ($index, $remote) {
AddBoolMembers -PARAMS @('mono', 'mute')
AddStringMembers -PARAMS @('label')
AddFloatMembers -PARAMS @('gain', 'returnreverb', 'returndelay', 'returnfx1', 'returnfx2')
$this.mode = [Mode]::new($index, $remote)
$this.eq = [Eq]::new($index, $remote)
$this.mode = [BusMode]::new($index, $remote)
$this.eq = [BusEq]::new($index, $remote)
$this.levels = [BusLevels]::new($index, $remote)
}
[string] ToString() {
return $this.GetType().Name + $this.index
}
[void] FadeTo ([single]$target, [int]$time) {
@@ -52,10 +62,41 @@ class Bus : IBus {
}
}
class Mode : IBus {
class BusLevels : IBus {
[int]$init
[int]$offset
BusLevels ([int]$index, [Object]$remote) : base ($index, $remote) {
$this.init = $index * 8
$this.offset = 8
}
[float] Convert([float]$val) {
if ($val -gt 0) {
return [math]::Round(20 * [math]::Log10($val), 1)
}
else {
return - 200.0
}
}
[System.Collections.ArrayList] Getter([int]$mode) {
[System.Collections.ArrayList]$vals = @()
$this.init..$($this.init + $this.offset - 1) | ForEach-Object {
$vals.Add($this.Convert($(Get_Level -MODE $mode -INDEX $_)))
}
return $vals
}
[System.Collections.ArrayList] All() {
return $this.Getter(3)
}
}
class BusMode : IBus {
[System.Collections.ArrayList]$modes
Mode ([int]$index, [Object]$remote) : base ($index, $remote) {
BusMode ([int]$index, [Object]$remote) : base ($index, $remote) {
$this.modes = @(
'normal', 'amix', 'bmix', 'repeat', 'composite', 'tvmix', 'upmix21', 'upmix41', 'upmix61',
'centeronly', 'lfeonly', 'rearonly'
@@ -78,8 +119,8 @@ class Mode : IBus {
}
}
class Eq : IBus {
Eq ([int]$index, [Object]$remote) : base ($index, $remote) {
class BusEq : IBus {
BusEq ([int]$index, [Object]$remote) : base ($index, $remote) {
AddBoolMembers -PARAMS @('on', 'ab')
}
@@ -92,12 +133,12 @@ class PhysicalBus : Bus {
[Object]$device
PhysicalBus ([int]$index, [Object]$remote) : base ($index, $remote) {
$this.device = [Device]::new($index, $remote)
$this.device = [BusDevice]::new($index, $remote)
}
}
class Device : IBus {
Device ([int]$index, [Object]$remote) : base ($index, $remote) {
class BusDevice : IBus {
BusDevice ([int]$index, [Object]$remote) : base ($index, $remote) {
}
[string] identifier () {

View File

@@ -1,8 +1,10 @@
. $PSScriptRoot\meta.ps1
class Special {
Special () {
[Object]$remote
Special ([Object]$remote) {
AddActionMembers -PARAMS @('restart', 'shutdown', 'show')
$this.remote = $remote
}
[string] identifier () {
@@ -14,18 +16,28 @@ class Special {
}
[single] Getter ($param) {
return Param_Get -PARAM "$($this.identifier()).$param" -IS_STRING $false
return $this.remote.Getter("$($this.identifier()).$param")
}
[void] Setter ($param, $val) {
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 {
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' `
{
$this._hide = $this.Setter('show', $false)
@@ -58,6 +70,6 @@ class Special {
}
}
function Make_Command {
return [Special]::new()
function Make_Command([Object]$remote) {
return [Special]::new($remote)
}

View File

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

View File

@@ -1,8 +1,19 @@
function Get_VBPath {
$reg_path = "Registry::HKEY_LOCAL_MACHINE\Software" + `
(& { if ([Environment]::Is64BitOperatingSystem) { "\WOW6432Node" } else { "" } }) + `
"\Microsoft\Windows\CurrentVersion\Uninstall"
$vm_key = "\VB:Voicemeeter {17359A74-1236-5467}\"
function Get_VMPath {
$REG_KEY = @(
"Registry::HKEY_LOCAL_MACHINE",
"Software",
(& { 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 {
[int32]$index
@@ -10,40 +16,42 @@ class MacroButton {
}
[int] Getter ($mode) {
"Button[$($this.index)].$([ButtonTypes].GetEnumName($mode))" | Write-Debug
return MB_Get -Id $this.index -Mode $mode
}
[void] Setter ($set, $mode) {
MB_Set -Id $this.index -SET $set -Mode $mode
[void] Setter ($val, $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' `
{
[bool]$this.Getter(1)
[bool]$this.Getter([ButtonTypes]::State)
} `
{
param($arg)
$this._state = $this.Setter($arg, 1)
$this._state = $this.Setter($arg, [ButtonTypes]::State)
}
)
hidden $_stateonly = $($this | Add-Member ScriptProperty 'stateonly' `
{
[bool]$this.Getter(2)
[bool]$this.Getter([ButtonTypes]::StateOnly)
} `
{
param($arg)
$this._stateonly = $this.Setter($arg, 2)
$this._stateonly = $this.Setter($arg, [ButtonTypes]::StateOnly)
}
)
hidden $_trigger = $($this | Add-Member ScriptProperty 'trigger' `
{
[bool]$this.Getter(3)
[bool]$this.Getter([ButtonTypes]::Trigger)
} `
{
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(
[Object]$DATA, [string]$CONF
)
try {
if ($null -eq $DATA -or -not $DATA.$CONF) {
throw [VMRemoteErrors]::new("No profile named $CONF was loaded")
throw [VMRemoteErrors]::new("No profile named '$CONF' has been loaded into memory.")
}
Param_Set_Multi -HASH $DATA.$CONF
Start-Sleep -m 1
}
catch [VMRemoteErrors] {
Write-Warning $_.Exception.ErrorMessage()
}
}

View File

@@ -1,12 +1,52 @@
. $PSScriptRoot\meta.ps1
class Recorder {
class IRecorder {
[Object]$remote
Recorder ([Object]$remote) {
IRecorder ([Object]$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')
AddFloatMembers -PARAMS @('gain')
AddChannelMembers
}
@@ -18,32 +58,146 @@ class Recorder {
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' `
{
return Write-Warning ("ERROR: $($this.identifier()).mode.loop is write only")
[bool]$this.mode.loop
} `
{
param([bool]$arg)
$this._loop = $this.Setter('mode.loop', $arg)
param($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) {
$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) {

View File

@@ -1,5 +1,3 @@
. $PSScriptRoot\meta.ps1
class IStrip {
[int]$index
[Object]$remote
@@ -14,19 +12,31 @@ class IStrip {
}
[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) {
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) {
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 Strip : IStrip {
[Object]$levels
Strip ([int]$index, [Object]$remote) : base ($index, $remote) {
AddBoolMembers -PARAMS @('mono', 'solo', 'mute')
AddIntMembers -PARAMS @('limit')
@@ -35,6 +45,8 @@ class Strip : IStrip {
AddChannelMembers
AddGainlayerMembers
$this.levels = [StripLevels]::new($index, $remote)
}
[string] ToString() {
@@ -50,6 +62,52 @@ class Strip : IStrip {
}
}
class StripLevels : IStrip {
[int]$init
[int]$offset
StripLevels ([int]$index, [Object]$remote) : base ($index, $remote) {
$p_in = $remote.kind.p_in
if ($index -lt $p_in) {
$this.init = $index * 2
$this.offset = 2
}
else {
$this.init = ($p_in * 2) + (($index - $p_in) * 8)
$this.offset = 8
}
}
[float] Convert([float]$val) {
if ($val -gt 0) {
return [math]::Round(20 * [math]::Log10($val), 1)
}
else {
return -200.0
}
}
[System.Collections.ArrayList] Getter([int]$mode) {
[System.Collections.ArrayList]$vals = @()
$this.init..$($this.init + $this.offset - 1) | ForEach-Object {
$vals.Add($this.Convert($(Get_Level -MODE $mode -INDEX $_)))
}
return $vals
}
[System.Collections.ArrayList] PreFader() {
return $this.Getter(0)
}
[System.Collections.ArrayList] PostFader() {
return $this.Getter(1)
}
[System.Collections.ArrayList] PostMute() {
return $this.Getter(2)
}
}
class PhysicalStrip : Strip {
[Object]$comp
[Object]$gate
@@ -62,16 +120,16 @@ class PhysicalStrip : Strip {
AddFloatMembers -PARAMS @('reverb', 'delay', 'fx1', 'fx2')
AddBoolMembers -PARAMS @('postreverb', 'postdelay', 'postfx1', 'postfx2')
$this.comp = [Comp]::new($index, $remote)
$this.gate = [Gate]::new($index, $remote)
$this.denoiser = [Denoiser]::new($index, $remote)
$this.eq = [Eq]::new($index, $remote)
$this.device = [Device]::new($index, $remote)
$this.comp = [StripComp]::new($index, $remote)
$this.gate = [StripGate]::new($index, $remote)
$this.denoiser = [StripDenoiser]::new($index, $remote)
$this.eq = [StripEq]::new($index, $remote)
$this.device = [StripDevice]::new($index, $remote)
}
}
class Comp : IStrip {
Comp ([int]$index, [Object]$remote) : base ($index, $remote) {
class StripComp : IStrip {
StripComp ([int]$index, [Object]$remote) : base ($index, $remote) {
AddFloatMembers -PARAMS @('gainin', 'ratio', 'threshold', 'attack', 'release', 'knee', 'gainout')
AddBoolMembers -PARAMS @('makeup')
}
@@ -91,8 +149,8 @@ class Comp : IStrip {
)
}
class Gate : IStrip {
Gate ([int]$index, [Object]$remote) : base ($index, $remote) {
class StripGate : IStrip {
StripGate ([int]$index, [Object]$remote) : base ($index, $remote) {
AddFloatMembers -PARAMS @('threshold', 'damping', 'bpsidechain', 'attack', 'hold', 'release')
}
@@ -111,8 +169,8 @@ class Gate : IStrip {
)
}
class Denoiser : IStrip {
Denoiser ([int]$index, [Object]$remote) : base ($index, $remote) {
class StripDenoiser : IStrip {
StripDenoiser ([int]$index, [Object]$remote) : base ($index, $remote) {
}
[string] identifier () {
@@ -130,8 +188,8 @@ class Denoiser : IStrip {
)
}
class Eq : IStrip {
Eq ([int]$index, [Object]$remote) : base ($index, $remote) {
class StripEq : IStrip {
StripEq ([int]$index, [Object]$remote) : base ($index, $remote) {
AddBoolMembers -PARAMS @('on', 'ab')
}
@@ -140,15 +198,15 @@ class Eq : IStrip {
}
}
class Device : IStrip {
Device ([int]$index, [Object]$remote) : base ($index, $remote) {
class StripDevice : IStrip {
StripDevice ([int]$index, [Object]$remote) : base ($index, $remote) {
}
[string] identifier () {
return "Strip[" + $this.index + "].Device"
}
hidden $_device = $($this | Add-Member ScriptProperty 'device' `
hidden $_name = $($this | Add-Member ScriptProperty 'name' `
{
$this.Getter_String('name')
} `

View File

@@ -1,9 +1,11 @@
class IVban {
[int32]$index
[Object]$remote
[string]$direction
IVban ([int]$index, [string]$direction) {
IVban ([int]$index, [Object]$remote, [string]$direction) {
$this.index = $index
$this.remote = $remote
$this.direction = $direction
}
@@ -11,25 +13,34 @@ class IVban {
return "vban." + $this.direction + "stream[" + $this.index + "]"
}
[string] ToString() {
return $this.GetType().Name + $this.index
}
[single] Getter ($param) {
return Param_Get -PARAM "$($this.identifier()).$param" -IS_STRING $false
return $this.remote.Getter($this.Cmd($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) {
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 {
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' `
@@ -68,7 +79,7 @@ class Vban : IVban {
} `
{
param([string]$arg)
if ($arg -in 1024..65535) {
if ($arg -ge 1024 -and $arg -le 65535) {
$this._port = $this.Setter('port', $arg)
}
else {
@@ -104,7 +115,7 @@ class Vban : IVban {
param([int]$arg)
if ($this.direction -eq "in") { Write-Warning ('Error, read only value') }
else {
if ($arg -in 1..8) {
if ($arg -ge 1 -and $arg -le 8) {
$this._channel = $this.Setter('channel', $arg)
}
else {
@@ -142,7 +153,7 @@ class Vban : IVban {
param([int]$arg)
if ($this.direction -eq "in") { Write-Warning ('Error, read only value') }
else {
if ($arg -in 0..4) {
if ($arg -ge 0 -and $arg -le 4) {
$this._quality = $this.Setter('quality', $arg)
}
else {
@@ -160,7 +171,7 @@ class Vban : IVban {
param([int]$arg)
if ($this.direction -eq "in") { Write-Warning ('Error, read only value') }
else {
if ($arg -in 0..8) {
if ($arg -ge 0 -and $arg -le 8) {
$this._route = $this.Setter('route', $arg)
}
else {
@@ -173,13 +184,13 @@ class Vban : IVban {
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 {
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 = @()
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 {
[void]$outstream.Add([VbanOutstream]::new($_, "out"))
[void]$outstream.Add([VbanOutstream]::new($_, $remote, "out"))
}
$CustomObject = [pscustomobject]@{

View File

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