diff --git a/CHANGELOG.md b/CHANGELOG.md index 75ba6ce..e8e35df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ AddActionMembers now adds ScriptMethods instead of ScriptProperties: - Recorder.PreRecTime - Recorder.Prefix($prefix) - Recorder.Eject() references 'Command.Eject' +- Recorder.State ### Changed diff --git a/README.md b/README.md index e89ed11..0c9bf38 100644 --- a/README.md +++ b/README.md @@ -528,6 +528,7 @@ The following commands are available: - A1 - A5: bool - B1 - B3: bool - armedbus: int, from 0 to bus index +- state: string, ('play', 'stop', 'record', 'pause') - prerectime: int, from 0 to 20 seconds - samplerate: int, (22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000) - bitresolution: int, (8, 16, 24, 32) diff --git a/lib/recorder.ps1 b/lib/recorder.ps1 index 133bcd3..a5aa67a 100644 --- a/lib/recorder.ps1 +++ b/lib/recorder.ps1 @@ -2,6 +2,7 @@ class Recorder : IRemote { [Object]$mode [System.Collections.ArrayList]$armstrip [System.Collections.ArrayList]$armbus + [System.Collections.ArrayList]$states Recorder ([Object]$remote) : base ($remote) { $this.mode = [RecorderMode]::new($remote) @@ -18,8 +19,10 @@ class Recorder : IRemote { $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') AddIntMembers -PARAMS @('prerectime') @@ -121,7 +124,32 @@ class Recorder : IRemote { 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) + } + ) [void] Load ([string]$filename) { $this.Setter('load', $filename) diff --git a/tests/higher.Tests.ps1 b/tests/higher.Tests.ps1 index fd9f380..be54cb6 100644 --- a/tests/higher.Tests.ps1 +++ b/tests/higher.Tests.ps1 @@ -876,4 +876,60 @@ Describe -Tag 'higher', -TestName 'All Higher Tests' { } } } + + Describe 'Action Tests' -Tag 'action' { + Context 'Recorder' -Skip:$ifBasic { + Context 'Recording/Playback' -Skip:$ifCustomDir { + BeforeAll { + $prefix = 'temp' + $filetype = 'wav' + $vmr.recorder.prefix($prefix) + $vmr.recorder.filetype($filetype) + } + + BeforeEach { + $vmr.recorder.state = 'record' + $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.state = 'pause' + Start-Sleep -Milliseconds 500 + } + + AfterEach { + $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." + } + } + + 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' + } + } + } + } } diff --git a/tests/run.ps1 b/tests/run.ps1 index 02daa8b..5257371 100644 --- a/tests/run.ps1 +++ b/tests/run.ps1 @@ -1,8 +1,41 @@ [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 +function Test-RecDir ([object]$vmr, [string]$recDir) { + $prefix = 'temp' + $filetype = 'wav' + $vmr.recorder.prefix($prefix) + $vmr.recorder.filetype($filetype) + + + try { + $vmr.recorder.record() + $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() { try { $vmr = Connect-Voicemeeter -Kind $kind @@ -30,6 +63,10 @@ function main() { $ifNotBasic = $vmr.kind.name -ne 'basic' $ifNotPotato = $vmr.kind.name -ne 'potato' + # recording directory: default ~/My Documents/Voicemeeter, override if custom + $recDir = [System.IO.Path]::GetFullPath($recDir) + $ifCustomDir = Test-RecDir -vmr $vmr -recDir $recDir # avoid creating files we can't delete + Invoke-Pester -Tag $tag -PassThru | Out-Null } finally { Disconnect-Voicemeeter }