Compare commits

...

14 Commits

Author SHA1 Message Date
1ad91b455a
Merge pull request #28 from pblivingston/special-commands
Special commands
2025-12-04 10:38:13 +00:00
pblivingston
1310ca25ef storepreset, recallpreset
pester tests pass for all kinds
manual tests pass for all kinds
- show/hide
- lock/unlock
- showvbanchat/hidevbanchat
2025-12-04 01:33:25 -05:00
pblivingston
cfa7de9b11 reset, save
- removed 'lock' test, corrected README example
- can now test 'save', 'reset', 'load'

prelim test for potato passes
2025-12-04 01:33:25 -05:00
pblivingston
b5546aa56c existing to methods
prelim manual testing passes for potato
2025-12-04 01:33:25 -05:00
77a8792377
Merge pull request #27 from pblivingston/recorder-commands
Recorder commands
2025-12-04 05:45:40 +00:00
pblivingston
df86ad2175 prefix, filetype
changed to write-only properties
pester tests pass for all kinds
2025-12-04 00:12:37 -05:00
pblivingston
1d41be7396 channel, types
- correct channel values
- add 'gain' to README
- cast getters to [int]
- add some int tests for safety
- skip recording test if basic

pester tests pass for all kinds
manual tests for safety pass
- channel
2025-12-03 04:18:25 -05:00
pblivingston
ab4baa5c44 remove loop, cleanup
- removed deprecated recorder.loop
- placed methods before hidden properties for readability
- added a couple mode tests for good measure
2025-12-03 03:31:30 -05:00
pblivingston
e42862c32d add string test
- can now test recorder.load($filename)
- prelim test passes for potato
2025-12-03 02:58:56 -05:00
pblivingston
0564dce7b6 recorder.state
- prelim tests pass for potato
2025-12-03 02:24:08 -05:00
pblivingston
8c3217b9a8 eject
prelim test passes for potato
'Command.Eject'
2025-12-02 18:12:15 -05:00
pblivingston
d7cb1d610d prerectime, prefix
prelim testing passes for potato
- prefix is currently write-only, so added as a method like FileType
2025-12-02 18:06:02 -05:00
pblivingston
58652b5a3f update docs
arming
2025-12-02 12:42:00 -05:00
pblivingston
9209bbbc65 armstrip, armbus, armedbus
- armstrip, armbus -> boolarraymembers
- armedbus

prelim tests pass for potato
2025-12-02 12:23:06 -05:00
6 changed files with 555 additions and 160 deletions

View File

@ -15,6 +15,9 @@ AddActionMembers now adds ScriptMethods instead of ScriptProperties:
- See Command section of README for details on using special commands - See Command section of README for details on using special commands
- See Recorder section of README for details on using playback/record actions - See Recorder section of README for details on using playback/record actions
Deprecated Recorder.Loop removed: use Recorder.Mode.Loop
Recorder.FileType changed from method to write-only property
### Added ### Added
- IRemote base class - IRemote base class
@ -31,6 +34,15 @@ AddActionMembers now adds ScriptMethods instead of ScriptProperties:
- ip, write-only - ip, write-only
- Bus.Sel, Bus.Monitor, Bus.Vaio - Bus.Sel, Bus.Monitor, Bus.Vaio
- Bus.Mode.Set($mode) - Bus.Mode.Set($mode)
- Recorder.Armedbus
- Recorder.PreRecTime
- Recorder.Prefix
- Recorder.Eject() references 'Command.Eject'
- Recorder.State
- Command.Reset()
- Command.Save($filepath)
- Command.StorePreset()
- Command.RecallPreset()
### Changed ### Changed
@ -43,6 +55,8 @@ AddActionMembers now adds ScriptMethods instead of ScriptProperties:
- Bus.Levels.Convert return type [float] -> [single] for naming consistency, no functional change - Bus.Levels.Convert return type [float] -> [single] for naming consistency, no functional change
- Meta: AddBoolMembers, AddIntMembers $arg types for consistency - Meta: AddBoolMembers, AddIntMembers $arg types for consistency
- Device: explicit $arg types for consistency - Device: explicit $arg types for consistency
- Recorder.Armstrip|Armbus -> BoolArrayMember: now have .Get()
- Cast Recorder getters to types for consistency
### Fixed ### Fixed
@ -53,6 +67,7 @@ AddActionMembers now adds ScriptMethods instead of ScriptProperties:
- vban.stream.port: [string]$arg -> [int]$arg - vban.stream.port: [string]$arg -> [int]$arg
- vban route range (API documentation is incorrect) - vban route range (API documentation is incorrect)
- vban.stream.sr: $this._port -> $this._sr - vban.stream.sr: $this._port -> $this._sr
- Recorder.channel values: 1..8 -> (2, 4, 6, 8)
## [3.3.0] - 2024-06-29 ## [3.3.0] - 2024-06-29

View File

@ -418,33 +418,44 @@ $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 commands are available:
- hide
- showvbanchat: bool, (write only)
- lock: bool, (write only)
The following methods are available: The following methods are available:
- Show() - Show()
- Hide()
- Lock()
- Unlock()
- ShowVBANChat()
- HideVBANChat()
- Restart() - Restart()
- Shutdown() - Shutdown()
- Reset(): Reset all config
- Save($filepath): string
- Load($filepath): string - Load($filepath): string
- StorePreset($index, $name): (int, string)
- RecallPreset($index or $name): (int or string)
- RunMacrobuttons(): Launches the macrobuttons app - RunMacrobuttons(): Launches the macrobuttons app
- CloseMacrobuttons(): Closes the macrobuttons app - CloseMacrobuttons(): Closes the macrobuttons app
example: example:
```powershell ```powershell
$vmr.command.show() $vmr.command.Show()
$vmr.command.Lock()
$vmr.command.lock = $true
$vmr.command.Load("path/to/filename.xml") $vmr.command.Load("path/to/filename.xml")
$vmr.command.RunMacrobuttons() $vmr.command.RunMacrobuttons()
$vmr.command.StorePreset(63, 'example')
$vmr.command.StorePreset('example')
$vmr.command.StorePreset(63) # same as StorePreset(63, '')
$vmr.command.StorePreset() # same as StorePreset(''), overwrites last recalled
$vmr.command.RecallPreset('example')
$vmr.command.RecallPreset(63)
$vmr.command.RecallPreset() # same as RecallPreset(''), recalls last recalled
``` ```
StorePreset('') and RecallPreset('') interact with the 'selected' preset. This is highlighted green in the GUI. Recalling a preset selects it. Storing a preset via GUI also selects it. Storing a preset with StorePreset does not select it.
### Fx ### Fx
The following Fx commands are available: The following Fx commands are available:
@ -527,9 +538,15 @@ The following commands are available:
- A1 - A5: bool - A1 - A5: bool
- B1 - B3: bool - B1 - B3: bool
- gain: float, from -60.0 to 12.0
- armedbus: int, from 0 to bus index
- state: string, ('play', 'stop', 'record', 'pause')
- prerectime: int, from 0 to 20 seconds
- prefix: string, write-only
- filetype: string, write-only, ('wav', 'aiff', 'bwf', 'mp3')
- samplerate: int, (22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000) - samplerate: int, (22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000)
- bitresolution: int, (8, 16, 24, 32) - bitresolution: int, (8, 16, 24, 32)
- channel: int, from 1 to 8 - channel: int, (2, 4, 6, 8)
- kbps: int, (32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320) - kbps: int, (32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320)
The following methods are available: The following methods are available:
@ -541,9 +558,9 @@ The following methods are available:
- Rew() - Rew()
- Record() - Record()
- Pause() - Pause()
- Eject()
- Load($filepath): string - Load($filepath): string
- GoTo($timestring): string, must match the format 'hh:mm:ss' - GoTo($timestring): string, must match the format 'hh:mm:ss'
- FileType($format): string, ('wav', 'aiff', 'bwf', 'mp3')
example: example:
@ -571,9 +588,10 @@ $vmr.recorder.mode.loop = $true
#### ArmStrip[i]|ArmBus[i] #### ArmStrip[i]|ArmBus[i]
The following method is available: The following methods are available:
- Set($val): bool - Set($val): bool
- Get()
example: example:

View File

@ -1,6 +1,6 @@
class Special : IRemote { class Special : IRemote {
Special ([Object]$remote) : base ($remote) { Special ([Object]$remote) : base ($remote) {
AddActionMembers -PARAMS @('restart', 'shutdown', 'show') AddActionMembers -PARAMS @('restart', 'shutdown', 'show', 'lock', 'reset')
} }
[string] identifier () { [string] identifier () {
@ -17,36 +17,57 @@ class Special : IRemote {
Stop-Process -Name 'VoicemeeterMacroButtons' Stop-Process -Name 'VoicemeeterMacroButtons'
} }
hidden $_hide = $($this | Add-Member ScriptProperty 'hide' ` [void] Hide () {
{ $this.Setter('show', $false)
$this._hide = $this.Setter('show', $false) }
} `
{}
)
hidden $_showvbanchat = $($this | Add-Member ScriptProperty 'showvbanchat' ` [void] Unlock () {
{ $this.Setter('lock', $false)
$this.Getter('DialogShow.VBANCHAT') }
} `
{
param([bool]$arg)
$this._showvbanchat = $this.Setter('DialogShow.VBANCHAT', $arg)
}
)
hidden $_lock = $($this | Add-Member ScriptProperty 'lock' ` [void] ShowVBANChat () {
{ $this.Setter('DialogShow.VBANCHAT', $true)
$this._lock = $this.Getter('lock') }
} `
{ [void] HideVBANChat () {
param([bool]$arg) $this.Setter('DialogShow.VBANCHAT', $false)
$this._lock = $this.Setter('lock', $arg) }
}
)
[void] Load ([string]$filename) { [void] Load ([string]$filename) {
$this.Setter('load', $filename) $this.Setter('load', $filename)
} }
[void] Save ([string]$filename) {
$this.Setter('save', $filename)
}
[void] StorePreset () {
$this.Setter('updatepreset', '')
}
[void] StorePreset ([string]$name) {
$this.Setter('updatepreset', $name)
}
[void] StorePreset ([int]$index) {
$this.Setter('preset[{0}].store' -f $index, '')
}
[void] StorePreset ([int]$index, [string]$name) {
$this.Setter('preset[{0}].store' -f $index, $name)
}
[void] RecallPreset () {
$this.Setter('recallpreset', '')
}
[void] RecallPreset ([string]$name) {
$this.Setter('recallpreset', $name)
}
[void] RecallPreset ([int]$index) {
$this.Setter('preset[{0}].recall' -f $index, 1)
}
} }
function Make_Command([Object]$remote) { function Make_Command([Object]$remote) {

View File

@ -2,20 +2,30 @@ class Recorder : IRemote {
[Object]$mode [Object]$mode
[System.Collections.ArrayList]$armstrip [System.Collections.ArrayList]$armstrip
[System.Collections.ArrayList]$armbus [System.Collections.ArrayList]$armbus
[System.Collections.ArrayList]$states
Recorder ([Object]$remote) : base ($remote) { Recorder ([Object]$remote) : base ($remote) {
$this.mode = [RecorderMode]::new($remote) $this.mode = [RecorderMode]::new($remote)
$this.armstrip = @() $this.armstrip = @()
0..($remote.kind.p_in + $remote.kind.v_in - 1) | ForEach-Object { $stripCount = $($remote.kind.p_in + $remote.kind.v_in)
$this.armstrip.Add([RecorderArmStrip]::new($_, $remote)) for ($i = 0; $i -lt $stripCount; $i++) {
$this.armstrip.Add([BoolArrayMember]::new($i, 'armstrip', $this))
} }
$this.armbus = @() $this.armbus = @()
0..($remote.kind.p_out + $remote.kind.v_out - 1) | ForEach-Object { $busCount = $($remote.kind.p_out + $remote.kind.v_out)
$this.armbus.Add([RecorderArmBus]::new($_, $remote)) for ($i = 0; $i -lt $busCount; $i++) {
$this.armbus.Add([BoolArrayMember]::new($i, 'armbus', $this))
} }
AddActionMembers -PARAMS @('play', 'stop', 'pause', 'replay', 'record', 'ff', 'rew') $this.states = @('play', 'stop', 'record', 'pause')
AddActionMembers -PARAMS $this.states
AddActionMembers -PARAMS @('replay', 'ff', 'rew')
AddFloatMembers -PARAMS @('gain') AddFloatMembers -PARAMS @('gain')
AddIntMembers -PARAMS @('prerectime')
AddChannelMembers AddChannelMembers
} }
@ -23,78 +33,9 @@ class Recorder : IRemote {
return 'Recorder' return 'Recorder'
} }
hidden $_loop = $($this | Add-Member ScriptProperty 'loop' ` [void] Eject () {
{ $this.remote.Setter('Command.Eject', 1)
[bool]$this.mode.loop }
} `
{
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) { [void] Load ([string]$filename) {
$this.Setter('load', $filename) $this.Setter('load', $filename)
@ -112,17 +53,142 @@ class Recorder : IRemote {
} }
} }
[void] FileType($format) { hidden $_samplerate = $($this | Add-Member ScriptProperty 'samplerate' `
[int]$val = 0 {
switch ($format) { [int]$this.Getter('samplerate')
'wav' { $val = 1 } } `
'aiff' { $val = 2 } {
'bwf' { $val = 3 } param([int]$arg)
'mp3' { $val = 100 } $opts = @(22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000)
default { "Filetype() got: $format, expected one of 'wav', 'aiff', 'bwf', 'mp3'" } if ($opts.Contains($arg)) {
$this._samplerate = $this.Setter('samplerate', $arg)
}
else {
"samplerate got: $arg, expected one of $opts" | Write-Warning
}
} }
$this.Setter('filetype', $val) )
}
hidden $_bitresolution = $($this | Add-Member ScriptProperty 'bitresolution' `
{
[int]$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' `
{
[int]$this.Getter('channel')
} `
{
param([int]$arg)
$opts = @(2, 4, 6, 8)
if ($opts.Contains($arg)) {
$this._channel = $this.Setter('channel', $arg)
}
else {
"channel got: $arg, expected one of $opts" | Write-Warning
}
}
)
hidden $_kbps = $($this | Add-Member ScriptProperty 'kbps' `
{
[int]$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
}
}
)
hidden $_prefix = $($this | Add-Member ScriptProperty 'prefix' `
{
return Write-Warning ("ERROR: $($this.identifier()).prefix is write only")
} `
{
param([string]$arg)
$this._prefix = $this.Setter('prefix', $arg)
}
)
hidden $_filetype = $($this | Add-Member ScriptProperty 'filetype' `
{
return Write-Warning ("ERROR: $($this.identifier()).filetype is write only")
} `
{
param([string]$arg)
[int]$val = 0
switch ($arg) {
'wav' { $val = 1 }
'aiff' { $val = 2 }
'bwf' { $val = 3 }
'mp3' { $val = 100 }
default { "Filetype() got: $arg, expected one of 'wav', 'aiff', 'bwf', 'mp3'" }
}
$this._filetype = $this.Setter('filetype', $val)
}
)
hidden $_armedbus = $($this | Add-Member ScriptProperty 'armedbus' `
{
foreach ($bus in 0..$($this.remote.kind.p_out + $this.remote.kind.v_out - 1)) {
if ($this.remote.Getter("Recorder.ArmBus[$bus]")) {
break
}
}
return $bus
} `
{
param([int]$arg)
$busMax = $this.remote.kind.p_out + $this.remote.kind.v_out - 1
if ($arg -ge 0 -and $arg -le $busMax) {
$this._armedbus = $this.remote.Setter("Recorder.ArmBus[$arg]", 1)
}
else {
Write-Warning ("Expected a bus index between 0 and $busMax")
}
}
)
hidden $_state = $($this | Add-Member ScriptProperty 'state' `
{
if ($this.Getter('pause')) { return 'pause' }
foreach ($state in $this.states) {
if ($this.Getter($state)) {
break
}
}
return $state
} `
{
param([string]$arg)
if (-not $this.states.Contains($arg)) {
Write-Warning ("Recorder.State got: $arg, expected one of $($this.states)")
return
}
if ($arg -eq 'pause' -and -not $this.Getter('record')) {
Write-Warning ("Recorder.State can only be set to 'pause' when recording")
return
}
$this._state = $this.Setter($arg, 1)
}
)
} }
class RecorderMode : IRemote { class RecorderMode : IRemote {
@ -135,33 +201,6 @@ class RecorderMode : IRemote {
} }
} }
class RecorderArm : IRemote {
RecorderArm ([int]$index, [Object]$remote) : base ($index, $remote) {
}
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) {
return [Recorder]::new($remote) return [Recorder]::new($remote)
} }

View File

@ -157,14 +157,30 @@ Describe -Tag 'higher', -TestName 'All Higher Tests' {
$vmr.recorder.B1 | Should -Be $expected $vmr.recorder.B1 | Should -Be $expected
} }
It 'Should set and get Recorder.loop' { It 'Should set and get Recorder.armstrip[i]' -ForEach @(
$vmr.recorder.loop = $value @{ Index = $phys_in }, @{ Index = $virt_in }
) {
$vmr.recorder.armstrip[$index].set($value)
$vmr.recorder.armstrip[$index].get() | Should -Be $value
} }
}
Context 'Command' { It 'Should set and get Recorder.armbus[i]' -ForEach @(
It 'Should set command.lock' { @{ Index = $phys_out }, @{ Index = $virt_out }
$vmr.command.lock = $value ) {
$vmr.recorder.armbus[$index].set($value)
$vmr.recorder.armbus[$index].get() | Should -Be $value
}
Context 'Mode' {
It 'Should set and get Recorder.mode.multitrack' {
$vmr.recorder.mode.multitrack = $value
$vmr.recorder.mode.multitrack | Should -Be $expected
}
It 'Should set and get Recorder.mode.loop' {
$vmr.recorder.mode.loop = $value
$vmr.recorder.mode.loop | Should -Be $expected
}
} }
} }
@ -603,6 +619,43 @@ Describe -Tag 'higher', -TestName 'All Higher Tests' {
} }
} }
} }
Context 'Recorder' -Skip:$ifBasic {
It 'Should set and get Recorder.armedbus' -ForEach @(
@{ Value = $phys_out }, @{ Value = $virt_out }
) {
$vmr.recorder.armedbus = $value
$vmr.recorder.armedbus | Should -Be $value
}
It 'Should set and get Recorder.prerectime' -ForEach @(
@{ Value = 5 }, @{ Value = 20 }
) {
$vmr.recorder.prerectime = $value
$vmr.recorder.prerectime | Should -Be $value
}
It 'Should set and get Recorder.samplerate' -ForEach @(
@{ Value = 44100 }, @{ Value = 48000 }
) {
$vmr.recorder.samplerate = $value
$vmr.recorder.samplerate | Should -Be $value
}
It 'Should set and get Recorder.bitresolution' -ForEach @(
@{ Value = 24 }, @{ Value = 16 }
) {
$vmr.recorder.bitresolution = $value
$vmr.recorder.bitresolution | Should -Be $value
}
It 'Should set and get Recorder.kbps' -ForEach @(
@{ Value = 96 }, @{ Value = 192 }
) {
$vmr.recorder.kbps = $value
$vmr.recorder.kbps | Should -Be $value
}
}
} }
Describe 'String Tests' -Tag 'string' { Describe 'String Tests' -Tag 'string' {
@ -845,5 +898,211 @@ Describe -Tag 'higher', -TestName 'All Higher Tests' {
} }
} }
} }
Context 'Recorder' -Skip:$ifBasic {
It 'Should record a test file, eject, and load it back' -Skip:$ifCustomDir {
try {
$prefix = 'stringtest'
$filetype = 'wav'
$vmr.recorder.prefix = $prefix
$vmr.recorder.filetype = $filetype
$vmr.recorder.state = 'record'
Start-Sleep -Milliseconds 100
$stamp = '{0:yyyy-MM-dd} at {0:HH}h{0:mm}m{0:ss}s' -f (Get-Date)
$vmr.recorder.state | Should -Be 'record'
Start-Sleep -Milliseconds 2000
$tmp = [System.IO.Path]::Combine($recDir, ("{0} {1}.{2}" -f $prefix, $stamp, $filetype))
$vmr.recorder.state = 'stop'
$vmr.recorder.eject()
Start-Sleep -Milliseconds 500
$vmr.recorder.state = 'play'
$vmr.recorder.state | Should -Be 'stop' # because no file is loaded
$vmr.recorder.load($tmp)
Start-Sleep -Milliseconds 500
if (-not $vmr.recorder.mode.playonload) {
$vmr.recorder.state = 'play'
}
$vmr.recorder.state | Should -Be 'play'
}
finally {
$vmr.recorder.state = 'stop'
$vmr.recorder.eject()
Start-Sleep -Milliseconds 500
if (Test-Path $tmp) {
Remove-Item -Path $tmp -Force
}
else {
throw "Recording file $tmp was not found."
}
}
}
}
Context 'Command' {
It 'Should save, reset, then load config' -ForEach @(
@{ Gain = -27.1; Mode = 'composite'; Bit = 24; Port = 1044 }
) {
$tmp = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "vmrconfig-$(New-Guid).xml")
try {
# set some values
$vmr.strip[$virt_in].gain = $gain
$vmr.bus[$phys_out].mode.set($mode)
$vmr.vban.outstream[$vban_outA].bit = $bit
$vmr.vban.port = $port
# save config
$vmr.command.save($tmp)
Start-Sleep -Milliseconds 100
Test-Path $tmp | Should -BeTrue
# reset config
$vmr.command.reset()
Start-Sleep -Milliseconds 500
# verify default values
$vmr.strip[$virt_in].gain | Should -Be 0.0
$vmr.bus[$phys_out].mode.get() | Should -Be 'normal'
$vmr.vban.outstream[$vban_outA].bit | Should -Be 16
$vmr.vban.port | Should -Be 6980
# load config
$vmr.command.load($tmp)
Start-Sleep -Milliseconds 500
# verify values
$vmr.strip[$virt_in].gain | Should -Be $gain
$vmr.bus[$phys_out].mode.get() | Should -Be $mode
$vmr.vban.outstream[$vban_outA].bit | Should -Be $bit
$vmr.vban.port | Should -Be $port
}
finally {
if (Test-Path $tmp) {
Remove-Item $tmp -Force
}
}
}
}
}
Describe 'Action Tests' -Tag 'action' {
Context 'Recorder' -Skip:$ifBasic {
Context 'Recording/Playback' -Skip:$ifCustomDir {
BeforeAll {
$prefix = 'actiontest'
$filetype = 'wav'
$vmr.recorder.prefix = $prefix
$vmr.recorder.filetype = $filetype
}
BeforeEach {
$vmr.recorder.record()
Start-Sleep -Milliseconds 100
$stamp = '{0:yyyy-MM-dd} at {0:HH}h{0:mm}m{0:ss}s' -f (Get-Date)
Start-Sleep -Milliseconds 2000
$tmp = [System.IO.Path]::Combine($recDir, ("{0} {1}.{2}" -f $prefix, $stamp, $filetype))
$vmr.recorder.pause()
Start-Sleep -Milliseconds 500
}
AfterEach {
$vmr.recorder.stop()
$vmr.recorder.eject()
Start-Sleep -Milliseconds 500
if (Test-Path $tmp) {
Remove-Item -Path $tmp -Force
}
else {
throw "Recording file $tmp was not found."
}
}
It 'Should call Recorder.record()' {
$vmr.recorder.record()
$vmr.recorder.state | Should -Be 'record'
}
It 'Should call Recorder.pause()' {
$vmr.recorder.record()
Start-Sleep -Milliseconds 500
$vmr.recorder.pause()
$vmr.recorder.state | Should -Be 'pause'
}
It 'Should call Recorder.play()' {
$vmr.recorder.stop()
Start-Sleep -Milliseconds 500
$vmr.recorder.play()
$vmr.recorder.state | Should -Be 'play'
}
}
}
Context 'Command' {
Context 'Preset Scene' -ForEach @(
@{ Index = 0; Name = 'Test Scene 1' }
@{ Index = 63; Name = 'Test Scene 64' }
) {
It "Should store preset at index '$index' with name '$name'" -ForEach @(
@{ Value = -15.5 }, @{ Value = -52.9 }
) {
$vmr.bus[$phys_out].gain = $value
$vmr.command.storepreset($index, $name)
$vmr.bus[$phys_out].gain = 0.0
$vmr.command.recallpreset($name)
Start-Sleep -Milliseconds 500
$vmr.bus[$phys_out].gain | Should -Be $value
}
It "Should update preset at index '$index'" -ForEach @(
@{ Value = $false }, @{ Value = $true }
) {
$vmr.strip[$virt_in].B1 = $value
$vmr.command.storepreset($index)
$vmr.strip[$virt_in].B1 = -not $value
$vmr.command.recallpreset($index)
Start-Sleep -Milliseconds 500
$vmr.strip[$virt_in].B1 | Should -Be $value
}
It "Should update preset with name '$name'" -ForEach @(
@{ Value = 2 }, @{ Value = 1 }
) {
$vmr.bus[$virt_out].mono = $value
$vmr.command.storepreset($name)
$vmr.bus[$virt_out].mono = 0
$vmr.command.recallpreset($name)
Start-Sleep -Milliseconds 500
$vmr.bus[$virt_out].mono | Should -Be $value
}
It "Should update last recalled preset ($index`: $(($index + 1).ToString('00')) - $name)" -ForEach @(
@{ Value = 0.8 }, @{ Value = 0.3 }
) {
$vmr.strip[$phys_in].color_y = $value
$vmr.command.storepreset()
$vmr.strip[$phys_in].color_y = 0.0
$vmr.command.recallpreset()
Start-Sleep -Milliseconds 500
$vmr.strip[$phys_in].color_y | Should -Be $value
}
}
}
} }
} }

View File

@ -1,8 +1,42 @@
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "", Target = "variablename")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "", Target = "variablename")]
Param([String]$tag, [string]$kind = 'potato') Param([String]$tag, [string]$kind = 'potato', [string]$recDir = (Join-Path ([Environment]::GetFolderPath('MyDocuments')) 'Voicemeeter'))
Import-Module (Join-Path (Split-Path $PSScriptRoot -Parent) 'lib\Voicemeeter.psm1') -Force Import-Module (Join-Path (Split-Path $PSScriptRoot -Parent) 'lib\Voicemeeter.psm1') -Force
function Test-RecDir ([object]$vmr, [string]$recDir) {
$prefix = 'temp'
$filetype = 'wav'
$vmr.recorder.prefix = $prefix
$vmr.recorder.filetype = $filetype
try {
$vmr.recorder.record()
Start-Sleep -Milliseconds 100
$stamp = '{0:yyyy-MM-dd} at {0:HH}h{0:mm}m{0:ss}s' -f (Get-Date)
Start-Sleep -Milliseconds 2000
$tmp = Join-Path $recDir ("{0} {1}.{2}" -f $prefix, $stamp, $filetype)
$vmr.recorder.stop()
$vmr.recorder.eject()
Start-Sleep -Milliseconds 500
}
catch {
Write-Warning "Failed to record pre-check clip: $_"
}
if (Test-Path $tmp) {
Remove-Item -Path $tmp -Force
return $false
}
else {
Write-Warning "Recorder output not found at given path: $tmp"
Write-Warning "Skipping Recording/Playback tests. Provide custom path with -recDir"
return $true
}
}
function main() { function main() {
try { try {
$vmr = Connect-Voicemeeter -Kind $kind $vmr = Connect-Voicemeeter -Kind $kind
@ -30,6 +64,15 @@ function main() {
$ifNotBasic = $vmr.kind.name -ne 'basic' $ifNotBasic = $vmr.kind.name -ne 'basic'
$ifNotPotato = $vmr.kind.name -ne 'potato' $ifNotPotato = $vmr.kind.name -ne 'potato'
# recording directory: default ~/My Documents/Voicemeeter, override if custom
$recDir = [System.IO.Path]::GetFullPath($recDir)
if ($ifBasic) {
$ifCustomDir = $ifBasic # basic can't record, so skip the test
}
else {
$ifCustomDir = Test-RecDir -vmr $vmr -recDir $recDir # avoid creating files we can't delete
}
Invoke-Pester -Tag $tag -PassThru | Out-Null Invoke-Pester -Tag $tag -PassThru | Out-Null
} }
finally { Disconnect-Voicemeeter } finally { Disconnect-Voicemeeter }