140 Commits

Author SHA1 Message Date
df2d1bb156 Merge pull request #25 from pblivingston/option-monitoringbus
Option.MonitoringBus
2025-12-01 22:57:22 +00:00
pblivingston
6e74db2751 Update README.md 2025-12-01 17:52:29 -05:00
pblivingston
5f064de010 Update higher.Tests.ps1
pester tests pass for all types
manual tests for safety pass
- buffer.asio
2025-12-01 17:52:29 -05:00
pblivingston
dd404ae337 monitoringBus, types
- add monitoringBus for convenience for Bus[i].Monitor since only one will be true
- cast getters to [int] for type consistency
2025-12-01 17:52:29 -05:00
68f582512a Merge pull request #24 from pblivingston/bus-params
Bus params
2025-12-01 22:47:23 +00:00
pblivingston
8f5536f139 update docs 2025-12-01 17:08:47 -05:00
pblivingston
7eb82c41a2 hide Convert, add mode.set
tests pass for all kinds
2025-12-01 17:05:58 -05:00
pblivingston
ec05790312 Update higher.Tests.ps1
pester tests pass for all kinds
manual tests for safety pass:
- bus.levels.all()
- bus.device.asio
2025-12-01 13:22:07 -05:00
pblivingston
2def27573d update docs 2025-12-01 12:06:51 -05:00
pblivingston
36d4e5f6c4 Update bus.ps1
- sel
- monitor
- vaio
- mono to int
- levels.convert [float] -> [single]
- device.asio $arg -> [string]$arg
2025-12-01 11:17:58 -05:00
e944dc46e6 Merge pull request #19 from pblivingston/vban-fixes
Vban fixes
2025-11-29 01:27:06 +00:00
pblivingston
d87cdbd444 'stream' -> 'audio'
VbanStream -> VbanAudio for clarity
2025-11-28 19:44:49 -05:00
pblivingston
06942a234d Update CHANGELOG.md 2025-11-28 19:25:58 -05:00
pblivingston
17e601a8d6 midi/text streams
- added midi and text streams with:
  - on
  - name
  - ip
had to manually test as these are currently write-only, but I suspect this is a bug/will change in the future
2025-11-28 19:22:56 -05:00
pblivingston
72185d14b3 update docs 2025-11-28 03:47:11 -05:00
pblivingston
81764f0e43 Update higher.Tests.ps1
pester tests pass for all kinds
- increased sleeps after restarts to 2s
- added tags to test types
2025-11-28 03:21:47 -05:00
pblivingston
90e9dcd06c Update vban.ps1
- vban.port
- on -> AddBoolMembers
- name, ip -> AddStringMembers
- cast getters to types
- correct read-only/write-only
- correct route range
2025-11-28 03:18:44 -05:00
b92a2422a7 Merge pull request #17 from pblivingston/fix-test-run
Update run.ps1
2025-11-28 02:32:40 +00:00
pblivingston
2eecdd7def Update run.ps1 2025-11-27 21:23:16 -05:00
5c623711f7 Merge pull request #16 from pblivingston/fx-params
Fx params
2025-11-28 02:17:05 +00:00
pblivingston
8d97df2d92 update docs 2025-11-27 20:56:06 -05:00
pblivingston
438fa525da Update higher.Tests.ps1
pester tests pass for all kinds
2025-11-27 20:55:12 -05:00
pblivingston
aa2c2a24af Update Voicemeeter.psm1 2025-11-27 20:55:12 -05:00
pblivingston
d3e9ad2bf4 Create fx.ps1 2025-11-27 20:55:12 -05:00
abdf2dbf5d Merge pull request #15 from pblivingston/eq-params
Eq params
2025-11-28 01:51:12 +00:00
pblivingston
02bc174746 forgot to save kinds 2025-11-27 20:33:03 -05:00
pblivingston
8038fc24ce identifier, kindOfEq
- move identifier back to BusEq and StripEq for clarity and looser coupling
- adjust kindmap so we can get channel count with kindOfEq
2025-11-27 20:21:43 -05:00
pblivingston
d13b08eff4 update docs 2025-11-27 12:09:06 -05:00
pblivingston
dedb4201be update tests
pester tests pass for all kinds
2025-11-27 11:54:54 -05:00
pblivingston
60d97a89b4 stripeq, buseq
- replace iremote with eq for stripeq and buseq
- move identifier to eq
- avoid passing entire parent objects
2025-11-27 09:49:46 -05:00
pblivingston
6154af7ad7 Create eq.ps1
- eq
- eq channel
- eq cell
2025-11-27 08:40:55 -05:00
07028478cc Merge pull request #13 from pblivingston/decouple-device
Decouple device
2025-11-27 12:31:51 +00:00
pblivingston
3f7bef56c1 strip, bus device
move stripdevice and busdevice back to strip & bus
2025-11-27 06:09:55 -05:00
pblivingston
820b897e84 update docs
Manual tests pass
- device.asio
2025-11-26 18:04:37 -05:00
pblivingston
1e4a2da821 update tests
pester tests pass for all kinds
- cast device.sr to int
2025-11-26 17:54:06 -05:00
pblivingston
ee85d5ffd8 decouple device
- basic A2 device supported
- asio only added to bus[0].device
2025-11-26 17:23:01 -05:00
b20f62f17c Merge pull request #12 from pblivingston/option-params
Option class
2025-11-26 18:52:24 +00:00
pblivingston
1587b2ea6a basic a2 delay 2025-11-26 13:33:42 -05:00
pblivingston
88468d4e52 accepted buffers
removed 896 from wdm and ks
2025-11-26 13:33:14 -05:00
pblivingston
a69902ec49 formatting 2025-11-26 13:32:06 -05:00
pblivingston
10c85cead5 remove mode
- mode.exclusif
- mode.swift
are not available without registry edits
2025-11-26 12:16:43 -05:00
pblivingston
d81cd32392 update docs
manual tests pass
- option.asiosr
- option.buffer.asio
2025-11-26 11:37:51 -05:00
pblivingston
e887e15168 Update higher.Tests.ps1
pester tests pass
2025-11-26 11:17:42 -05:00
pblivingston
c5a8813e9a option.ps1 2025-11-26 10:40:20 -05:00
16dd73231e Merge pull request #9 from pblivingston/patch-arraymembers
Patch and ArrayMember classes
2025-11-26 15:34:42 +00:00
pblivingston
15a977834d add a2 for basic
patch.outa2[i]
2025-11-26 10:01:32 -05:00
pblivingston
f3ed1de557 Update README.md
- correct postfxinsert
- correct examples
- better readability for patch arraymembers
2025-11-26 09:30:44 -05:00
pblivingston
54319924d0 update docs
manual tests all pass:
- asio[i].set($val)
- asio[i].get()
- outa2[i]-outa5[i].set($val)
- outa2[i]-outa5[i].get()

these require an asio device
2025-11-25 21:57:15 -05:00
4d54e0a15f Merge pull request #7 from pblivingston/iremote
Add IRemote abstract base class.
2025-11-26 02:33:58 +00:00
pblivingston
dce6f37bf1 Merge branch 'iremote' into patch-arraymembers 2025-11-25 21:32:31 -05:00
pblivingston
5fc5680c75 fix ToString 2025-11-25 21:29:45 -05:00
pblivingston
80869d4306 tests
pester tests pass
2025-11-25 21:25:13 -05:00
pblivingston
e0b01288ff Update kinds.ps1 2025-11-25 20:43:31 -05:00
pblivingston
3a5c7286f6 Patch
Patch class with:
- Patch.asio[i]
- Patch.OutA2[i]-OutA5[i]
- Patch.composite[i]
- Patch.insert[i]
- Patch.postFaderComposite
- Patch.postFxInsert
2025-11-25 20:38:57 -05:00
pblivingston
c086f58ade ArrayMember classes 2025-11-25 18:54:36 -05:00
pblivingston
e5137b842b Update CHANGELOG.md 2025-11-25 16:44:16 -05:00
pblivingston
78f7fc80d4 vban
implement iremote
2025-11-25 16:39:44 -05:00
pblivingston
62d9e89b5f strip
implement iremote
2025-11-25 16:33:04 -05:00
pblivingston
a6f7d8efe0 recorder
implement iremote
2025-11-25 16:05:13 -05:00
pblivingston
b372cf8087 Update command.ps1
forgot to pass to base
2025-11-25 15:59:39 -05:00
pblivingston
eeb30925fa command
implement iremote
2025-11-25 15:40:15 -05:00
pblivingston
09d8bd48eb bus
implement iremote
2025-11-25 15:35:00 -05:00
pblivingston
b0a6bf7b63 nullable index
make index nullable so ToString can append the index for indexed objects
2025-11-25 15:32:27 -05:00
pblivingston
9a2529c617 module path in tests
change module path so we can run from /tests/
2025-11-25 15:25:40 -05:00
pblivingston
2404bfb50f create iremote.ps1 2025-11-25 14:39:10 -05:00
bd0779add2 add Taskfile
upd tasks in launch.json

add with Task to Run tests in README
2025-06-06 13:50:16 +01:00
a0a2c72634 run through formatter
rename pre-commit to run

remove num and log parameters
2025-06-06 13:49:35 +01:00
0f68a2373d run through formatter 2025-06-06 13:48:24 +01:00
2d6437d37b run through formatter 2025-06-06 13:48:11 +01:00
f199fa587f add Voicemeeter + OBS button example 2025-06-05 20:19:16 +01:00
b6c9c65390 update requirements with note about different scriptdeck versions 2025-06-05 20:10:19 +01:00
436b47a5db upd example 2025-06-05 16:43:30 +01:00
0cdd71600f reword 2025-06-05 01:58:02 +01:00
41529c0d58 add module installation note 2025-06-05 01:55:53 +01:00
b1a6ac68c1 add stream deck example README 2025-06-05 01:51:15 +01:00
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
d48104c4a9 edit changelog 2022-12-18 17:03:47 +00:00
dfebb20c6e add remote sublcasses to changelog 2022-12-18 16:56:47 +00:00
150301c271 add Login() to Remote class.
method chain it in factory functions.
2022-12-18 16:48:28 +00:00
828a9a5731 minor changes. 2022-12-18 04:30:51 +00:00
3b1e469d2d Login refactor.
Return Exit value -2 for multiple login attempts.
2022-12-18 04:29:41 +00:00
3b1bb06c7d Classes for each remote kind added.
They subclass Remote.

Factory functions now return the Remote class of a Kind.

Return Exit value -1 for DLL setup error.
2022-12-18 04:27:55 +00:00
7e1c5616db fix output string 2022-12-17 20:16:02 +00:00
ff4391c6fb add entry/exit functions to readme.
rethrow LoginError exceptions, let the consumer handle it.
2022-12-17 20:10:53 +00:00
3b4185c251 update bus mode tests 2022-12-17 19:33:32 +00:00
deb3da15ea remove constructor annotations 2022-12-17 17:47:42 +00:00
408218ea32 typo fixes 2022-12-17 17:47:27 +00:00
5d1fc9736e md fixes 2022-12-17 17:19:01 +00:00
fed7489ac2 readme, changelog updated 2022-12-17 15:41:18 +00:00
cd7508b823 minor rework 2022-12-17 15:41:02 +00:00
6468270fd0 wdm, ks, mme and asio added to strip.device 2022-12-17 15:40:29 +00:00
42b17a7239 mode class added to Bus.
removed busmodemember meta functions.
2022-12-17 15:40:07 +00:00
714d761af2 update changelog 2022-12-17 02:13:17 +00:00
bb94e9d4e5 update tests to reflect v3 changes 2022-12-17 02:12:32 +00:00
72467a611b enable verbose output in examples
fix host key in obs example readme.
2022-12-17 02:11:00 +00:00
e01d4f134a comp, gate, denoiser, eq, device sections added
appgain|appmute added.

todo. add examples
2022-12-16 19:41:33 +00:00
9074e0d416 add note to close OBS to end script 2022-12-16 18:57:26 +00:00
bc3216bee7 Merge branch 'dev' into add-comp-gate-eq-device 2022-12-16 18:51:00 +00:00
2a476b8ffe start constructing version 3 section in changelog
add to todo list.
2022-12-16 18:41:22 +00:00
fc290ba90c entry/exit functions added 2022-12-16 18:19:35 +00:00
a40e36998c meta functions refactored to use class identifiers 2022-12-16 17:40:47 +00:00
c37b4e0b1b IVban class added
getters/setters moved into IVban

refactored to use identifier()
2022-12-16 17:30:06 +00:00
14c8a54f01 refactored to use identifier() 2022-12-16 17:29:04 +00:00
a642dfd154 getters now return boolean values 2022-12-16 17:27:58 +00:00
ec296059d4 refactored to use identifier() 2022-12-16 17:27:05 +00:00
c999e73e14 IStrip class added.
getters/setters moved into IStrip

Comp, Gate, Denoiser, Eq and Device classes added to PhysicalStrip

AppGain, AppMute methods added to VirtualStrip
2022-12-16 17:24:31 +00:00
d86ad2fe87 IBus class added.
getters/setters moved into IBus

Eq class added to Bus

Device class added to PhysicalBus
2022-12-16 17:18:51 +00:00
62ad51c6b8 examples now using relative import path
obs example now using obs-powershell module. README updated.
2022-12-16 17:16:02 +00:00
35 changed files with 3070 additions and 986 deletions

5
.gitignore vendored
View File

@@ -1,7 +1,6 @@
# quick test
quick.ps1
lib/*.psd1 lib/*.psd1
**/*.log **/*.log
config.psd1 config.psd1
test-*.ps1

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

@@ -0,0 +1,59 @@
{
// 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/run.ps1",
"args": [],
"createTemporaryIntegratedConsole": true
},
]
}

View File

@@ -9,184 +9,290 @@ 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] Implement command.load ### Added
- [ ] Implement comp/gate parameters introduced in v3.0.2.8 of the api.
- IRemote base class
- ArrayMember classes for array-like properties
- Patch class
- Option class
- Device classes
- EQ class
- FX class
- Vban.port sets Vban.Instream[0].port
- Vban Midi and Command streams
- on, write-only
- name, write-only
- ip, write-only
- Bus.Sel, Bus.Monitor, Bus.Vaio
- Bus.Mode.Set($mode)
### Changed
- some vban.instream | vban.outstream commands now added with meta functions
- on
- name
- ip
- cast vban getters to types for consistency
- Bus.Mono -> [int] for stereo reverse
- Bus.Levels.Convert return type [float] -> [single] for naming consistency, no functional change
### Fixed
- some vban commands incorrectly read-only/write-only
- enable
- instream|outstream.quality
- instream|outstream.route
- vban.stream.port: [string]$arg -> [int]$arg
- vban route range (API documentation is incorrect)
- vban.stream.sr: $this._port -> $this._sr
## [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:
- Strip[i].comp now references [Comp] class. (see README for details on settings strip.comp parameters)
- Strip[i].gate now references [Gate] class. (see README for details on settings strip.gate parameters)
- Strip[i].eq now references [Eq] class. (see README for details on settings strip.eq parameters)
- Strip[i].device now references [Device] class. (see README for details on settings strip.device parameters)
- Bus[i].eq now references [Eq] class. (see README for details on settings bus.eq parameters)
- Bus[i].mode now implemented as its own class [Mode]. (see README for details on settings bus.mode parameters)
There are other changes but they should not be breaking.
### Changed
- meta functions refactored, they now use identifier() functions.
- OBS example reworked, now using obs-powershell module.
- Rethrow LoginError for unknown kind exceptions, let the consumer handle it from there.
### Added
- Entry/exit points Connect-Voicemeeter, Disconnect-Voicemeeter added to module.
- Comp, Gate, Denoiser and Eq classes added to PhysicalStrip
- Device class added to PhysicalStrip/PhysicalBus
- AppGain(), AppMute() methods added to VirtualStrip
- eq added to Bus
- interface classes IBus, IStrip and IVban added. getters/setters moved into interface classes.
- RemoteBasic, RemoteBanana and RemotePotato subclasses added.
### Fixed
- Button getters return boolean values.
### Removed
- Bus[i].mode\_{param} members removed. Replaced with Bus[i].mode.{param}
## [2.5.0] - 2022-10-27 ## [2.5.0] - 2022-10-27
### Added ### Added
- xy parameters added to strip/bus - xy parameters added to strip/bus
- fx parameters added to strip/bus - fx parameters added to strip/bus
- GetType, GetVersion added to Remote class. - GetType, GetVersion added to Remote class.
- SendText implemented (set parameters by script), added to Remote class. - SendText implemented (set parameters by script), added to Remote class.
- CLI example added - CLI example added
- README and CHANGELOG updated to reflect latest changes. - README and CHANGELOG updated to reflect latest changes.
### Changed ### Changed
- pester tests now support all kinds. - pester tests now support all kinds.
- GoToNextBus example refactored - GoToNextBus example refactored
- Previous console output now written to Debug stream. - Previous console output now written to Debug stream.
### Removed ### Removed
- setmulti, setandget and special examples. - setmulti, setandget and special examples.
## [2.4.0] - 2022-06-25 ## [2.4.0] - 2022-06-25
### Added ### Added
- fadeto, fadeby methods for strips/buses - fadeto, fadeby methods for strips/buses
- README and CHANGELOG updated to reflect latest changes. - README and CHANGELOG updated to reflect latest changes.
- Version 2.4 added to PSGAllery - Version 2.4 added to PSGAllery
### Changed ### Changed
- Move kinds, profiles into their own modules. - Move kinds, profiles into their own modules.
- remove global variable layout. added GetKind() to kinds. - remove global variable layout. added GetKind() to kinds.
- link to official documentation in readme now points to SDK repo. - link to official documentation in readme now points to SDK repo.
### Fixed ### Fixed
- number of macrobuttons - number of macrobuttons
## [2.3.0] - 2022-03-08 ## [2.3.0] - 2022-03-08
### Added ### Added
- mc, k properties added to virtual strips. - mc, k properties added to virtual strips.
- gainlayer properties added to all strips - gainlayer properties added to all strips
- busmode and eq_ab properties added to all buses. - busmode and eq_ab properties added to all buses.
- Added ability to load custom profiles in psd1 format. - Added ability to load custom profiles in psd1 format.
- Added hide command to Command class - Added hide command to Command class
- Added recorder module - Added recorder module
- Added recorder tests to higher.tests - Added recorder tests to higher.tests
- README and CHANGELOG updated to reflect latest changes. - README and CHANGELOG updated to reflect latest changes.
- Version 2.3 added to PSGAllery - Version 2.3 added to PSGAllery
### Changed ### Changed
- Pester tests refactored - Pester tests refactored
### Fixed ### Fixed
- eq, eq_ab getters now return boolean values - eq, eq_ab getters now return boolean values
- fixed bug with command action props - fixed bug with command action props
## [2.2.0] - 2022-01-19 ## [2.2.0] - 2022-01-19
### Added ### Added
- Add VMRemoteErrors class and subclass other error classes. - Add VMRemoteErrors class and subclass other error classes.
- Expose lower level setters and getters as well as polling parameters through Remote class. - Expose lower level setters and getters as well as polling parameters through Remote class.
- README and CHANGELOG updated to reflect latest changes. - README and CHANGELOG updated to reflect latest changes.
- Version 2.2 added to PSGAllery - Version 2.2 added to PSGAllery
### Changed ### Changed
- Rework set many parameters so class properties are set through the wrapper instead by VBVMR_SetParameters - Rework set many parameters so class properties are set through the wrapper instead by VBVMR_SetParameters
- Rework meta module. Separate functions for each member type. - Rework meta module. Separate functions for each member type.
- Update pester tests to reflect latest changes - Update pester tests to reflect latest changes
- Add throw LoginError if multiple login attempts are made. In testing the session was still crashing, however. - Add throw LoginError if multiple login attempts are made. In testing the session was still crashing, however.
## [2.1.0] - 2022-01-11 ## [2.1.0] - 2022-01-11
### Added ### Added
- Special command lock - Special command lock
- Special command showvbanchat - Special command showvbanchat
- vban.enable command added (toggle vban) - vban.enable command added (toggle vban)
- README and CHANGELOG updated to reflect latest changes. - README and CHANGELOG updated to reflect latest changes.
- Version 2.1 added to PSGAllery - Version 2.1 added to PSGAllery
### Changed ### Changed
- Subclass strip and bus classes into physical/virtual buses. - Subclass strip and bus classes into physical/virtual buses.
### Fixed ### Fixed
- Special command showvbanchat now accepts boolean - Special command showvbanchat now accepts boolean
## [2.0.0] - 2022-01-06 ## [2.0.0] - 2022-01-06
### Added ### Added
- README and CHANGELOG updated to reflect latest changes. - README and CHANGELOG updated to reflect latest changes.
- Version 2.0 added to PSGAllery - Version 2.0 added to PSGAllery
### Changed ### Changed
- Moved meta functions into own module - Moved meta functions into own module
- Vban class now custom object comprising of two arrays of subclasses for each stream type - Vban class now custom object comprising of two arrays of subclasses for each stream type
- Major version bumped due to changes to vban class - Major version bumped due to changes to vban class
- Pester tests updated to reflect changes. - Pester tests updated to reflect changes.
### Fixed ### Fixed
- Special commands now throw write only error on read attempt. - Special commands now throw write only error on read attempt.
## [1.8.0] - 2021-08-23 ## [1.8.0] - 2021-08-23
### Added ### Added
- Added special commands - Added special commands
### Changed ### Changed
- Add special section to README - Add special section to README
### Fixed ### Fixed
- Removed unneeded console output - Removed unneeded console output
## [1.6.0] - 2021-06-06 ## [1.6.0] - 2021-06-06
### Added ### Added
- Add vban commands - Add vban commands
- Added meta functions for bus/strip attrs - Added meta functions for bus/strip attrs
### Changed ### Changed
- Update tests to reflect changes - Update tests to reflect changes
- Add vban section to README - Add vban section to README
### Fixed ### Fixed
- Run 64bit exe for potato version if on 64bit OS - Run 64bit exe for potato version if on 64bit OS
## [1.5.0] - 2021-05-11 ## [1.5.0] - 2021-05-11
### Added ### Added
- Fetch dll path through registry (support for 32 and 64 bit) - Fetch dll path through registry (support for 32 and 64 bit)
- Add strip/bus commands section to README - Add strip/bus commands section to README
- Add label name command to Strips - Add label name command to Strips
## [1.4.0] - 2021-05-03 ## [1.4.0] - 2021-05-03
### Added ### Added
- Add gain, comp, limit to Strips - Add gain, comp, limit to Strips
- Update tests to reflect changes - Update tests to reflect changes
- Add logging + summary for tests - Add logging + summary for tests
- Add info to README about powershellget, nuget and psgallery - Add info to README about powershellget, nuget and psgallery
- Support other types of params in multi_set - Support other types of params in multi_set
### Changed ### Changed
- Multi_Set now accepts nested hash - Multi_Set now accepts nested hash
## [1.3.0] - 2021-04-30 ## [1.3.0] - 2021-04-30
### Added ### Added
- Updated README to include Installation instructions. - Updated README to include Installation instructions.
- Added FROM_SOURCE.md to explain alternative loading of scripts if directly - Added FROM_SOURCE.md to explain alternative loading of scripts if directly
downloaded. downloaded.
- Set_Multi command for setting many parameters at once. - Set_Multi command for setting many parameters at once.
## [1.0.0] - 2021-04-29 ## [1.0.0] - 2021-04-29
- Added module to PSGAllery - Added module to PSGAllery

535
README.md
View File

@@ -8,14 +8,14 @@ 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
- [Voicemeeter](https://voicemeeter.com/) - [Voicemeeter](https://voicemeeter.com/)
- Powershell 5.1+ or Powershell 7.2+ - Powershell 5.1+ or Powershell 7.2+
## Installation ## Installation
@@ -35,9 +35,9 @@ When prompted you will need to accept PSGallery as a trusted repository.
More Info: More Info:
- [PowerShellGet](https://docs.microsoft.com/en-us/powershell/scripting/gallery/installing-psget?view=powershell-7.1) - [PowerShellGet](https://docs.microsoft.com/en-us/powershell/scripting/gallery/installing-psget?view=powershell-7.1)
- [NuGet](https://www.powershellgallery.com/packages/NuGet/1.3.3) - [NuGet](https://www.powershellgallery.com/packages/NuGet/1.3.3)
- [PSGallery](https://docs.microsoft.com/en-gb/powershell/scripting/gallery/overview?view=powershell-7.1) - [PSGallery](https://docs.microsoft.com/en-gb/powershell/scripting/gallery/overview?view=powershell-7.1)
#### Direct download: #### Direct download:
@@ -75,9 +75,24 @@ finally { $vmr.Logout() }
Voicemeeter factory function can be: Voicemeeter factory function can be:
- Get-RemoteBasic - Get-RemoteBasic
- Get-RemoteBanana - Get-RemoteBanana
- Get-RemotePotato - Get-RemotePotato
Added in `v3` you may also use the following entry/exit points:
- Connect-Voicemeeter
- Disconnect-Voicemeeter
`Connect-Voicemeeter` takes a single parameter `Kind`.
for example:
```powershell
$vmr = Connect-Voicemeeter -Kind "potato"
...
Disconnect-Voicemeeter
```
#### `Through the Shell` #### `Through the Shell`
@@ -100,39 +115,35 @@ $vmr.Logout()
The following strip commands are available: The following strip commands are available:
- mute: bool - mute: bool
- mono: bool - mono: bool
- mc: bool - mc: bool
- k: int, from 0 to 4 - k: int, from 0 to 4
- solo: bool - solo: bool
- A1-A5: bool - A1-A5: bool
- B1-B3: bool - 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
- comp: float, from 0.0 to 10.0 - label: string
- gate: float, from 0.0 to 10.0 - reverb: float, from 0.0 to 10.0
- label: string - delay: float, from 0.0 to 10.0
- device: string - fx1: float, from 0.0 to 10.0
- sr: int - fx2: float, from 0.0 to 10.0
- reverb: float, from 0.0 to 10.0 - pan_x: float, from -0.5 to 0.5
- delay: float, from 0.0 to 10.0 - pan_y: float, from 0.0 to 1.0
- fx1: float, from 0.0 to 10.0 - color_x: float, from -0.5 to 0.5
- fx2: float, from 0.0 to 10.0 - color_y: float, from 0.0 to 1.0
- pan_x: float, from -0.5 to 0.5 - fx_x: float, from -0.5 to 0.5
- pan_y: float, from 0.0 to 1.0 - fx_y: float, from 0.0 to 1.0
- color_x: float, from -0.5 to 0.5 - postreverb: bool
- color_y: float, from 0.0 to 1.0 - postdelay: bool
- fx_x: float, from -0.5 to 0.5 - postfx1: bool
- fx_y: float, from 0.0 to 1.0 - postfx2: bool
- postreverb: boolean - gainlayer0-gainlayer7: float
- postdelay: boolean
- postfx1: boolean
- postfx2: boolean
- gainlayer0-gainlayer7: float
for example: for example:
``` ```powershell
$vmr.strip[5].gainlayer1 = -8.3 $vmr.strip[5].gainlayer1 = -8.3
``` ```
@@ -142,43 +153,216 @@ gainlayers defined for Potato version only.
mc, k for virtual strips only. mc, k for virtual strips only.
#### comp
The following strip.comp commands are available:
- knob: float, from 0.0 to 10.0
- gainin: float, from -24.0 to 24.0
- ratio: float, from 1.0 to 8.0
- threshold: float, from -40.0 to -3.0
- attack: float, from 0.0 to 200.0
- release: float, from 0.0 to 5000.0
- knee: float, 0.0 to 1.0
- gainout: float, from -24.0 to 24.0
- makeup: bool
for example:
```powershell
$vmr.strip[3].comp.attack = 8.5
```
#### gate
The following strip.gate commands are available:
- knob: float, from 0.0 to 10.0
- threshold: float, from -60.0 to -10.0
- damping: float, from -60.0 to -10.0
- bpsidechain: int, from 100 to 4000
- attack: float, from 0.0 to 1000.0
- hold: float, from 0.0 to 5000.0
- release: float, from 0.0 to 5000.0
for example:
```powershell
$vmr.strip[3].gate.threshold = -40.5
```
#### denoiser
The following strip.denoiser commands are available:
- knob: float, from 0.0 to 10.0
for example:
```powershell
$vmr.strip[3].denoiser.knob = 5
```
#### AppGain | AppMute
- `AppGain(amount, gain)` : string, float
- `AppMute(amount, mutestate)` : string, bool
for example:
```powershell
$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 ### Bus
The following bus commands are available: The following bus commands are available:
- mute: bool - mute: bool
- mono: bool - sel: bool
- eq: bool - monitor: bool
- eq_ab: bool - vaio: bool
- limit: int, from -40 to 12 - mono: int, 0 off, 1 mono, 2 stereo reverse
- gain: float, from -60.0 to 12.0 - gain: float, from -60.0 to 12.0
- label: string - label: string
- device: string - returnreverb: float, from 0.0 to 10.0
- sr: int - returndelay: float, from 0.0 to 10.0
- returnreverb: float, from 0.0 to 10.0 - returnfx1: float, from 0.0 to 10.0
- returndelay: float, from 0.0 to 10.0 - returnfx2: float, from 0.0 to 10.0
- returnfx1: float, from 0.0 to 10.0
- returnfx2: float, from 0.0 to 10.0
- mode\_: bool, any of the following:
@('normal', 'amix', 'bmix', 'repeat', 'composite', 'tvmix', 'upmix21',
'upmix41', 'upmix61', 'centeronly', 'lfeonly', 'rearonly')
for example: for example:
```powershell
$vmr.bus[3].returnreverb = 5.7
``` ```
$vmr.bus[3].mode_repeat = $true
#### modes
The following bus.mode members are available:
- 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:
- Set($mode): string, sets the current bus mode
- Get(): returns the current bus mode.
for example:
```powershell
$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 ### Strip|Bus
- `FadeTo(amount, time)` : float, int #### device
- `FadeBy(amount, time)` : float, int
The following strip.device | bus.device commands are available:
- name: string
- sr: int
- wdm: string
- ks: string
- mme: string
- asio: string
for example:
```powershell
$vmr.strip[0].device.wdm = "Mic|Line|Instrument 1 (Audient EVO4)"
$vmr.bus[0].device.name
```
name, sr are defined as read only.
wdm, ks, mme, asio are defined as write only.
asio only defined for Bus[0].Device
#### eq
The following strip.eq | bus.eq commands are available:
- on: bool
- ab: bool
The following strip.eq | bus.eq methods are available:
- Load($filepath) : string
- Save($filepath) : string
for example:
```powershell
$vmr.strip[0].eq.on = $true
$vmr.bus[0].eq.ab = $false
```
##### channel.cell
The following eq.channel.cell commands are available:
- on: bool
- type: int, from 0 to 6
- f: float, from 20.0 to 20000.0
- gain: float, from -12.0 to 12.0
- q: float, from 0.3 to 100.0
for example:
```powershell
$vmr.strip[2].eq.channel[1].cell[4].type = 1
$vmr.bus[5].eq.channel[6].cell[3].on = $false
```
#### FadeTo | FadeBy
- `FadeTo(amount, time)` : float, int
- `FadeBy(amount, time)` : float, int
Modify gain to or by the selected amount in db over a time interval in ms. Modify gain to or by the selected amount in db over a time interval in ms.
for example: for example:
``` ```powershell
$vmr.strip[3].FadeTo(-18.7, 1000) $vmr.strip[3].FadeTo(-18.7, 1000)
$vmr.bus[0].FadeBy(-10, 500) $vmr.bus[0].FadeBy(-10, 500)
``` ```
@@ -187,11 +371,11 @@ $vmr.bus[0].FadeBy(-10, 500)
Three modes defined: state, stateonly and trigger. Three modes defined: state, stateonly and trigger.
- State runs associated scripts - State runs associated scripts
- Stateonly does not run associated scripts - Stateonly does not run associated scripts
- Index range (0, 69) - Index range (0, 69)
``` ```powershell
$vmr.button[3].state = $true $vmr.button[3].state = $true
$vmr.button[4].stateonly = $false $vmr.button[4].stateonly = $false
@@ -201,19 +385,21 @@ $vmr.button[5].trigger = $true
### VBAN ### VBAN
- vmr.vban.enable: Toggle VBAN on or off. Accepts a boolean value. The following vban commands are available:
- enable: bool
- port: int, from 1024 - 65535
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 - sr: in, (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000)
- sr: int (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.
@@ -222,9 +408,9 @@ example:
```powershell ```powershell
$vmr.vban.enable = $true $vmr.vban.enable = $true
$vmr.vban.port = 6990
$vmr.vban.instream[0].on = $true $vmr.vban.instream[0].on = $true
$vmr.vban.instream[2].port = 6990
$vmr.vban.outstream[3].bit = 16 $vmr.vban.outstream[3].bit = 16
``` ```
@@ -232,18 +418,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 commands are available:
- show
- hide
- restart
- shutdown
- showvbanchat: bool, (write only)
- lock: bool, (write only)
The following methods are available: The following methods are available:
- show - Load($filepath): string
- hide - RunMacrobuttons(): Launches the macrobuttons app
- restart - CloseMacrobuttons(): Closes the macrobuttons app
- shutdown
- Load(filepath)
The following properties are write only and accept boolean values:
- showvbanchat
- lock
example: example:
@@ -253,31 +441,143 @@ $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()
```
### Fx
The following Fx commands are available:
- Reverb.on: bool
- Reverb.ab: bool
- Delay.on: bool
- Delay.ab: bool
for example:
```powershell
$vmr.fx.reverb.ab = $false
```
### Patch
The following Patch commands are available:
- postFaderComposite: bool
- postFxInsert: bool
The following Patch members have .Set($val) and .Get() available:
- asio[i]: int, from 0 to ASIO input channels
- OutA2[i]-OutA5[i]: int, from 0 to ASIO output channels
- composite[i]: int, from 0 to strip channels
- insert[i]: bool
for example:
```powershell
$vmr.patch.asio[3].set(2) # patches ASIO input channel 2 (2) to strip 2, channel 2 (3)
$vmr.patch.OutA3[0].set(24) # patches bus A3, channel 1 (0) to ASIO output channel 24
$vmr.patch.composite[5].set(0) # sets composite channel 6 (5) to default bus channel
$vmr.patch.insert[4].get()
```
### Option
The following Option commands are available:
- sr: int, (32000, 44100, 48000, 88200, 96000, 176400, 192000)
- asiosr: bool
- monitorOnSel: bool
- sliderMode: bool
- monitoringBus: int, from 0 to bus index
The following Option.delay[i] methods are available:
- Set($val): float, from 0.00 to 500.00
- Get()
for example:
```powershell
$vmr.Option.delay[2].set(30.26) # sets the delay for the third (2) bus
$vmr.Option.sliderMode = $false # sets slider mode to absolute
```
#### buffers
The following Option.buffer commands are available:
- mme: int, (441, 480, 512, 576, 640, 704, 768, 896, 1024, 1536, 2048)
- wdm: int, (128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 441, 448, 480, 512, 576, 640, 704, 768, 1024, 1536, 2048)
- ks: int, (128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 441, 448, 480, 512, 576, 640, 704, 768, 1024, 1536, 2048)
- asio: int, (0, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 441, 448, 480, 512, 576, 640, 704, 768, 1024)
for example:
```powershell
$vmr.Option.buffer.wdm = 512
$vmr.Option.buffer.asio = 0 # to use default buffer size
``` ```
### Recorder ### Recorder
The following commands are available:
- play
- stop
- pause
- 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 methods are available: The following methods are available:
- play - Load($filepath): string
- stop - GoTo($timestring): string, must match the format 'hh:mm:ss'
- pause - FileType($format): string, ('wav', 'aiff', 'bwf', 'mp3')
- record
- ff
- rew
The following properties accept boolean values.
- loop
- A1 - A5
- B1 - B3
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
@@ -328,9 +628,9 @@ will load a config file at profiles/banana/config.psd1 for Voicemeeter Banana.
Access to lower level Getters and Setters are provided with these functions: Access to lower level Getters and Setters are provided with these functions:
- `$vmr.Getter(param)`: For getting the value of a parameter expected to return a value other than string. - `$vmr.Getter(param)`: For getting the value of a parameter expected to return a value other than string.
- `$vmr.Getter_String(param)`: For getting the value of any parameter expected to return a string. - `$vmr.Getter_String(param)`: For getting the value of any parameter expected to return a string.
- `$vmr.Setter(param, value)`: For setting the value of any parameter. - `$vmr.Setter(param, value)`: For setting the value of any parameter.
```powershell ```powershell
$vmr.Getter('Strip[2].Mute') $vmr.Getter('Strip[2].Mute')
@@ -339,7 +639,7 @@ $vmr.Setter('Strip[4].Label', 'stripname')
$vmr.Setter('Strip[0].Gain', -3.6) $vmr.Setter('Strip[0].Gain', -3.6)
``` ```
- `$vmr.SendText`: Set parameters by script - `$vmr.SendText`: Set parameters by script
```powershell ```powershell
$vmr.SendText("strip[0].mute=1;strip[2].gain=3.8;bus[1].eq.On=1") $vmr.SendText("strip[0].mute=1;strip[2].gain=3.8;bus[1].eq.On=1")
@@ -347,22 +647,31 @@ $vmr.Setter('Strip[0].Gain', -3.6)
Access to lower level polling functions are provided with these functions: 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: Parameters:
- `kind`: Run tests of this kind - `kind`: Run tests of this kind
- `tag`: Run tests tagged with this marker (currently `higher` or `lower`) - `tag`: Run tests tagged with this marker (currently `higher` or `lower`)
- `num`: Run this number of tests
- `log`: Write summary log file
Run tests from repository root in a subshell and write logs, like so: *with Task*
`powershell .\tests\pre-commit.ps1 -k "potato" -t "higher" -log` ```console
task test -- -t "higher" -k "banana"
```
### 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)

11
Taskfile.yaml Normal file
View File

@@ -0,0 +1,11 @@
version: '3'
tasks:
test:
desc: 'Run tests'
preconditions:
- sh: 'pwsh -c "if ([System.Version](Get-InstalledModule Pester).Version.ToString() -gt [System.Version]"5.7.0") { exit 0 } else { exit 1 }"'
msg: 'Pester version must be greater than 5.7.0'
cmds:
- echo "Running tests..."
- pwsh -c "tests\run.ps1 {{.CLI_ARGS}}"

View File

@@ -1,11 +1,11 @@
[cmdletbinding()]
param( param(
[switch]$interactive, [switch]$interactive,
[switch]$output, [String]$kind = 'banana',
[String]$kind="banana",
[String[]]$script = @() [String[]]$script = @()
) )
Import-Module Voicemeeter Import-Module ..\..\lib\Voicemeeter.psm1
function get-value { function get-value {
param([object]$vmr, [string]$line) param([object]$vmr, [string]$line)
@@ -20,26 +20,26 @@ function get-value {
function msgHandler { 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
} }
} }
function read-hostuntilempty { function read-hostuntilempty {
param([object]$vmr) param([object]$vmr)
while (($line = Read-Host) -cne[string]::Empty) { msgHandler -vmr $vmr -line $line } while (($line = Read-Host) -cne [string]::Empty) { msgHandler -vmr $vmr -line $line }
} }
@@ -47,14 +47,10 @@ function main {
[object]$vmr [object]$vmr
try { try {
switch ($kind) { $vmr = Connect-Voicemeeter -Kind $kind
"basic" { $vmr = Get-RemoteBasic }
"banana" { $vmr = Get-RemoteBanana }
"potato" { $vmr = Get-RemotePotato }
}
if ($interactive) { if ($interactive) {
"Press <Enter> to exit" | Write-Host 'Press <Enter> to exit' | Write-Host
read-hostuntilempty -vmr $vmr read-hostuntilempty -vmr $vmr
return return
} }
@@ -62,7 +58,7 @@ function main {
msgHandler -vmr $vmr -line $_ msgHandler -vmr $vmr -line $_
} }
} }
finally { $vmr.Logout() } finally { Disconnect-Voicemeeter }
} }
if ($MyInvocation.InvocationName -ne '.') { main } main

View File

@@ -7,26 +7,30 @@
Credits go to @bobsupercow Credits go to @bobsupercow
#> #>
Import-Module Voicemeeter [cmdletbinding()]
param()
Import-Module ..\..\lib\Voicemeeter.psm1
try { try {
$vmr = Get-RemotePotato $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)
$buses | ForEach-Object { 'Cycling through bus selection to check for first unmuted Bus...' | Write-Host
$bus = $_ 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
} }
@@ -34,7 +38,10 @@ try {
} }
} }
# 4) # 4)
if ($null -eq $unmutedIndex) { $buses[0].mute = $false } if ($null -eq $unmutedIndex) {
"unmuting bus " + $buses[0].index | Write-Host $buses[0].mute = $false
"Unmuting Bus $($buses[0].index)" | Write-Host
}
} }
finally { $vmr.Logout() } finally { Disconnect-Voicemeeter }

View File

@@ -5,16 +5,17 @@ Demonstrates how to sync Voicemeeter states with OBS scene switches.
## Requirements ## Requirements
- [OBS Studio 28+](https://obsproject.com/) - [OBS Studio 28+](https://obsproject.com/)
- [OBSWebSocket for Powershell](https://github.com/onyx-and-iris/OBSWebSocket-Powershell) - [OBS-Powershell](https://github.com/StartAutomating/obs-powershell)
## Use ## Use
This example assumes the following: This example assumes the following:
- OBS connection info saved in `config.psd1`, placed next to `Vm-Obs-Sync.ps1`: - OBS connection info saved in `config.psd1`, placed next to `Vm-Obs-Sync.ps1`:
```psd1 ```psd1
@{ @{
hostname = "localhost" host = "localhost"
port = 4455 port = 4455
password = "mystrongpassword" password = "mystrongpassword"
} }
@@ -23,3 +24,5 @@ This example assumes the following:
- OBS scenes named `START`, `BRB`, `END` and `LIVE` - OBS scenes named `START`, `BRB`, `END` and `LIVE`
Simply run the script and change current OBS scene. Simply run the script and change current OBS scene.
Closing OBS will end the script.

View File

@@ -1,50 +1,68 @@
Import-Module Voicemeeter [cmdletbinding()]
Import-Module OBSWebSocket param()
$VerbosePreference = "Continue" Import-Module ..\..\lib\Voicemeeter.psm1
Import-Module obs-powershell
$info = @{ function CurrentProgramSceneChanged {
START = "Toggling Strip 0 mute" param([System.Object]$data)
BRB = "Setting Strip 0 gain to -8.3" Write-Host 'Switched to scene', $data.sceneName
END = "Setting Strip 0 mono to `$false"
LIVE = "Setting Strip 0 color_x to 0.3" switch ($data.sceneName) {
'START' {
$vmr.strip[0].mute = !$vmr.strip[0].mute
}
'BRB' {
$vmr.strip[0].gain = -8.3
}
'END' {
$vmr.strip[0].mono = $true
}
'LIVE' {
$vmr.strip[0].color_x = 0.3
}
default { 'Expected START, BRB, END or LIVE scene' | Write-Warning; return }
}
} }
function CurrentProgramSceneChanged($data) { function ExitStarted {
"Switched to scene " + $data.sceneName | Write-Host param([System.Object]$data)
'OBS shutdown has begun!' | Write-Host
switch ($data.SceneName) { break
"START" { $vmr.strip[0].mute = !$vmr.strip[0].mute } }
"BRB" { $vmr.strip[0].gain = -8.3 }
"END" { $vmr.strip[0].mono = $true } function eventHandler($data) {
"LIVE" { $vmr.strip[0].color_x = 0.3 } if (Get-Command $data.eventType -ErrorAction SilentlyContinue) {
default { "Expected START, BRB, END or LIVE scene" | Write-Warning; return } & $data.eventType -data $data.eventData
} }
$info[$data.SceneName] | Write-Host
} }
function ConnFromFile { function ConnFromFile {
$configpath = Join-Path $PSScriptRoot "config.psd1" $configpath = Join-Path $PSScriptRoot 'config.psd1'
return Import-PowerShellDataFile -Path $configpath return Import-PowerShellDataFile -Path $configpath
} }
function main { function main {
try { $vmr = Connect-Voicemeeter -Kind 'basic'
$vmr = Get-RemoteBasic
$conn = ConnFromFile
$r_client = Get-OBSRequest -hostname $conn.hostname -port $conn.port -pass $conn.password
$resp = $r_client.getVersion()
"obs version:" + $resp.obsVersion | Write-Host
"websocket version:" + $resp.obsWebSocketVersion | Write-Host
$e_client = Get-OBSEvent -hostname $conn.hostname -port $conn.port -pass $conn.password $conn = ConnFromFile
$callbacks = @("CurrentProgramSceneChanged", ${function:CurrentProgramSceneChanged}) $job = Watch-OBS -WebSocketURI "ws://$($conn.host):$($conn.port)" -WebSocketToken $conn.password
$e_client.Register($callbacks)
} finally { try {
$r_client.TearDown() while ($true) {
$e_client.TearDown() Receive-Job -Job $job | ForEach-Object {
$vmr.Logout() $data = $_.MessageData
if ($data.op -eq 5) {
eventHandler($data.d)
}
}
}
} }
finally {
Disconnect-OBS
Disconnect-Voicemeeter
}
} }
if ($MyInvocation.InvocationName -ne '.') { main } main

View File

@@ -0,0 +1,125 @@
# About
Thanks to the guys at [Start Automating](https://startautomating.com/) it's possible to use this module straight from your Stream Deck.
## Requirements
### ScriptDeck
*Windows Powershell*
- [Windows ScriptDeck](https://marketplace.elgato.com/product/windows-scriptdeck-857f01dd-8fd4-44d5-8ec7-67ac850b21d3)
*Powershell core*
- [ScriptDeck](https://marketplace.elgato.com/product/scriptdeck-927e59aa-b42d-4da7-84cc-8c78f4dd7e18)
Note, even though one of them is named Windows they both work on Windows for different powershell versions, see [this issue](https://github.com/StartAutomating/ScriptDeck/issues/120)
### Voicemeeter API Powershell
- Install it as a module, see [Installation](https://github.com/onyx-and-iris/voicemeeter-api-powershell?tab=readme-ov-file#installation)
## How
Once ScriptDeck is installed create a button using *Powershell Script*, then:
### On one button
Due to the design of Voicemeeter's API you may only login/logout once per session so in order to program multiple buttons you must do the following for just ONE button (it can be any button).
#### Button 1
*When Loaded*
```powershell
$global:vmr = Connect-Voicemeeter -Kind "banana"
```
*When Unloaded*
```powershell
Disconnect-Voicemeeter
```
*When Pressed*
```powershell
if ($vmr.strip[0].mute) {
$vmr.bus[0].mute=1
$vmr.bus[1].mute=1
} else {
$vmr.bus[0].mute=0
$vmr.bus[1].mute=0
}
```
### Other buttons
Then your other buttons can have any scripts using the `$vmr` object:
#### Button 2
*When Pressed*
```powershell
$vmr.strip[1].mute=1
$vmr.strip[2].mute=1
if (-not $vmr.strip[0].mute) {
$vmr.strip[0].mute=1
}
```
#### Button 3
*When Pressed*
```powershell
$vmr.strip[0].mute=$(-not $vmr.strip[0].mute)
$vmr.strip[1].mute=$(-not $vmr.strip[1].mute)
$vmr.strip[2].mute=$(-not $vmr.strip[2].mute)
```
---
Then let's say you have zillions of buttons you want to program, for each Stream Deck window configure ONE button as described above and the other buttons of the same window as described above.
If this explanation is unclear or you'd like me to add some screenshots just ask.
## Leveraging Powershell
Since we're now working with Powershell we can do some useful things, for example, lets create a button that interacts with Voicemeeter and OBS:
First make sure you've installed [obs-powershell](https://github.com/StartAutomating/obs-powershell).
Now let's create a button that only toggles some strip mutes if the current OBS scene is "LIVE".
#### Button
*When Loaded*
```powershell
$global:vmr = Connect-Voicemeeter -Kind "banana"
Connect-OBS -WebSocketToken <websocket token>
```
*When Unloaded*
```powershell
Disconnect-Voicemeeter
Disconnect-OBS
```
*When Pressed*
```powershell
$currentScene = $(Get-OBSCurrentProgramScene | Select-Object -ExpandProperty currentProgramSceneName)
if ($currentScene -eq "LIVE") {
$vmr.strip[0].mute=$(-not $vmr.strip[0].mute)
$vmr.strip[1].mute=$(-not $vmr.strip[1].mute)
$vmr.strip[2].mute=$(-not $vmr.strip[2].mute)
}
```

View File

@@ -1,38 +1,40 @@
. $PSScriptRoot\kinds.ps1 . $PSScriptRoot\errors.ps1
. $PSScriptRoot\meta.ps1
. $PSScriptRoot\base.ps1 . $PSScriptRoot\base.ps1
. $PSScriptRoot\kinds.ps1
. $PSScriptRoot\iremote.ps1
. $PSScriptRoot\arraymember.ps1
. $PSScriptRoot\device.ps1
. $PSScriptRoot\eq.ps1
. $PSScriptRoot\strip.ps1
. $PSScriptRoot\bus.ps1
. $PSScriptRoot\macrobuttons.ps1
. $PSScriptRoot\vban.ps1
. $PSScriptRoot\command.ps1
. $PSScriptRoot\recorder.ps1
. $PSScriptRoot\patch.ps1
. $PSScriptRoot\option.ps1
. $PSScriptRoot\fx.ps1
. $PSScriptRoot\profiles.ps1
class Remote { class Remote {
[String]$vmpath
[Hashtable]$kind [Hashtable]$kind
[System.Collections.ArrayList]$strip
[System.Collections.ArrayList]$bus
[System.Collections.ArrayList]$button
[PSCustomObject]$vban
[Object]$command
[Object]$recorder
[Object]$profiles [Object]$profiles
# Constructor Remote ([String]$kindId) {
Remote ([String]$kind_id) { $this.vmpath = Setup_DLL
$this.kind = GetKind($kind_id) $this.kind = GetKind($kindId)
$this.Setup()
}
[void] Setup() {
if (!(Setup_DLL)) {
Exit
}
Login -KIND $this.kind.name
$this.profiles = Get_Profiles($this.kind.name) $this.profiles = Get_Profiles($this.kind.name)
$this.strip = Make_Strips($this)
$this.bus = Make_Buses($this)
$this.button = Make_Buttons
$this.vban = Make_Vban($this)
$this.command = Make_Command
$this.recorder = Make_Recorder($this)
} }
[string] ToString() { [string] ToString() {
return "Voicemeeter " + $this.kind.name.substring(0, 1).toupper() + $this.kind.name.substring(1) return 'Voicemeeter ' + $this.kind.name.substring(0, 1).toupper() + $this.kind.name.substring(1)
}
[Remote] Login() {
Login -kindId $this.kind.name
return $this
} }
[void] Logout() { [void] Logout() {
@@ -44,7 +46,7 @@ class Remote {
} }
[String] GetVersion() { [String] GetVersion() {
return Version return VmVersion
} }
[void] Set_Profile([String]$config) { [void] Set_Profile([String]$config) {
@@ -76,16 +78,104 @@ class Remote {
[void] MDirty() { M_Dirty } [void] MDirty() { M_Dirty }
} }
class RemoteBasic : Remote {
[System.Collections.ArrayList]$strip
[System.Collections.ArrayList]$bus
[System.Collections.ArrayList]$button
[PSCustomObject]$vban
[Object]$command
[Object]$patch
[Object]$option
RemoteBasic () : base ('basic') {
$this.strip = Make_Strips($this)
$this.bus = Make_Buses($this)
$this.button = Make_Buttons
$this.vban = Make_Vban($this)
$this.command = Make_Command($this)
$this.patch = Make_Patch($this)
$this.option = Make_Option($this)
}
}
class RemoteBanana : Remote {
[System.Collections.ArrayList]$strip
[System.Collections.ArrayList]$bus
[System.Collections.ArrayList]$button
[PSCustomObject]$vban
[Object]$command
[Object]$patch
[Object]$option
[Object]$recorder
RemoteBanana () : base ('banana') {
$this.strip = Make_Strips($this)
$this.bus = Make_Buses($this)
$this.button = Make_Buttons
$this.vban = Make_Vban($this)
$this.command = Make_Command($this)
$this.patch = Make_Patch($this)
$this.option = Make_Option($this)
$this.recorder = Make_Recorder($this)
}
}
class RemotePotato : Remote {
[System.Collections.ArrayList]$strip
[System.Collections.ArrayList]$bus
[System.Collections.ArrayList]$button
[PSCustomObject]$vban
[Object]$command
[Object]$fx
[Object]$patch
[Object]$option
[Object]$recorder
RemotePotato () : base ('potato') {
$this.strip = Make_Strips($this)
$this.bus = Make_Buses($this)
$this.button = Make_Buttons
$this.vban = Make_Vban($this)
$this.command = Make_Command($this)
$this.fx = Make_Fx($this)
$this.patch = Make_Patch($this)
$this.option = Make_Option($this)
$this.recorder = Make_Recorder($this)
}
}
Function Get-RemoteBasic { Function Get-RemoteBasic {
return [Remote]::new('basic') [RemoteBasic]::new().Login()
} }
Function Get-RemoteBanana { Function Get-RemoteBanana {
return [Remote]::new('banana') [RemoteBanana]::new().Login()
} }
Function Get-RemotePotato { Function Get-RemotePotato {
return [Remote]::new('potato') [RemotePotato]::new().Login()
} }
Export-ModuleMember -Function Get-RemoteBasic, Get-RemoteBanana, Get-RemotePotato Function Connect-Voicemeeter {
param([String]$Kind)
switch ($Kind) {
'basic' {
return Get-RemoteBasic
}
'banana' {
return Get-RemoteBanana
}
'potato' {
return Get-RemotePotato
}
default {
throw [LoginError]::new("Unknown Voicemeeter kind `"$Kind`"")
}
}
}
Function Disconnect-Voicemeeter {
Logout
}
Export-ModuleMember -Function Get-RemoteBasic, Get-RemoteBanana, Get-RemotePotato, Connect-Voicemeeter, Disconnect-Voicemeeter

64
lib/arraymember.ps1 Normal file
View File

@@ -0,0 +1,64 @@
class ArrayMember : IRemote {
[string]$prefix
[Object]$parent
ArrayMember (
[int]$index, [string]$prefix, [Object]$parent
) : base ($index, $parent.remote) {
$this.prefix = $prefix
$this.parent = $parent
}
[string] identifier () {
$parentId = $this.parent.identifier()
return "{0}.{1}[{2}]" -f $parentId, $this.prefix, $this.index
}
[void] Set ($val) {
$this.Setter('', $val)
}
}
class BoolArrayMember : ArrayMember {
BoolArrayMember (
[int]$index, [string]$prefix, [Object]$parent
) : base ($index, $prefix, $parent) {}
[bool] Get () {
return [bool]$this.Getter('')
}
}
class IntArrayMember : ArrayMember {
IntArrayMember (
[int]$index, [string]$prefix, [Object]$parent
) : base ($index, $prefix, $parent) {}
[int] Get () {
return [int]$this.Getter('')
}
}
class FloatArrayMember : ArrayMember {
[int]$decimals
FloatArrayMember (
[int]$index, [string]$prefix, [Object]$parent, [int]$decimals = 1
) : base ($index, $prefix, $parent) {
$this.decimals = $decimals
}
[double] Get () {
return [math]::Round($this.Getter(''), $this.decimals)
}
}
class StringArrayMember : ArrayMember {
StringArrayMember (
[int]$index, [string]$prefix, [Object]$parent
) : base ($index, $prefix, $parent) {}
[string] Get () {
return [string]$this.Getter_String('')
}
}

View File

@@ -1,86 +1,108 @@
. $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]$KIND = $null [string]$kindId
) )
try { $retval = [int][Voicemeeter.Remote]::VBVMR_Login()
$retval = [int][Voicemeeter.Remote]::VBVMR_Login() if ($retval -notin @(0, 1, -2)) {
if (-not $retval) { "LOGGED IN" | Write-Verbose } throw [CAPIError]::new($retval, 'VBVMR_Login')
elseif ($retval -eq 1) {
"VM NOT RUNNING" | Write-Verbose
New-Variable -Name vm_exe -Value 0
switch ($KIND) {
'basic' { $vm_exe = 1; break }
'banana' { $vm_exe = 2; break }
'potato' {
if ([Environment]::Is64BitOperatingSystem) {
$vm_exe = 6
}
else { $vm_exe = 3 }
break
}
default { throw [LoginError]::new('Unknown Voicemeeter type') }
}
$retval = [int][Voicemeeter.Remote]::VBVMR_RunVoicemeeter([int64]$vm_exe)
if (-not $retval) { "STARTING VOICEMEETER" | Write-Verbose }
else { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) }
Start-Sleep -s 1
}
elseif ($retval -eq -2) {
throw [LoginError]::new('Login may only be called once per session')
}
else { throw [CAPIError]::new($retval, $MyInvocation.MyCommand) }
} }
catch [LoginError], [CAPIError] {
Write-Warning $_.Exception.ErrorMessage() switch ($retval) {
exit 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' }
3 { return "potato" } 3 { return 'potato' }
} }
} }
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
@@ -93,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
} }
@@ -124,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')
}
} }
} }
@@ -142,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()
} }
} }
@@ -159,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
} }
@@ -174,8 +184,8 @@ function Param_Set_Multi {
[hashtable]$HASH [hashtable]$HASH
) )
foreach ($key in $HASH.keys) { foreach ($key in $HASH.keys) {
$classobj, $m2, $m3 = $key.Split("_") $classobj, $m2, $m3 = $key.Split('_')
if ($m2 -match "^\d+$") { $index = [int]$m2 } else { $index = [int]$m3 } if ($m2 -match '^\d+$') { $index = [int]$m2 } else { $index = [int]$m3 }
foreach ($h in $HASH[$key].GetEnumerator()) { foreach ($h in $HASH[$key].GetEnumerator()) {
$property = $h.Name $property = $h.Name
@@ -196,11 +206,23 @@ 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')
} }
} }
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 { . $PSScriptRoot\inst.ps1
try {
$vb_path = Get_VBPath
if ([string]::IsNullOrWhiteSpace($vb_path)) { function Setup_DLL {
throw [VMRemoteErrors]::new("ERROR: 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 [VMRemoteErrors] {
Write-Warning $_.Exception.ErrorMessage()
return $false
}
$Signature = @" $Signature = @"
[DllImport(@"$dll")] [DllImport(@"$dll")]
@@ -47,8 +40,11 @@ function Setup_DLL {
[DllImport(@"$dll")] [DllImport(@"$dll")]
public static extern int VBVMR_SetParameters(String param); 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 Add-Type -MemberDefinition $Signature -Name Remote -Namespace Voicemeeter -PassThru | Out-Null
return $true return $VMPATH
} }

View File

@@ -1,95 +1,146 @@
. $PSScriptRoot\meta.ps1 class Bus : IRemote {
[Object]$mode
[Object]$eq
[Object]$levels
class Bus { Bus ([int]$index, [Object]$remote) : base ($index, $remote) {
[int]$index AddBoolMembers -PARAMS @('mute', 'sel', 'monitor')
[Object]$remote AddIntMembers -PARAMS @('mono')
# Constructor
Bus ([int]$index, [Object]$remote) {
$this.index = $index
$this.remote = $remote
AddBoolMembers -PARAMS @('mono', 'mute')
AddStringMembers -PARAMS @('label') AddStringMembers -PARAMS @('label')
AddFloatMembers -PARAMS @('gain', 'returnreverb', 'returndelay', 'returnfx1', 'returnfx2') AddFloatMembers -PARAMS @('gain', 'returnreverb', 'returndelay', 'returnfx1', 'returnfx2')
AddBusModeMembers -PARAMS @('normal', 'amix', 'bmix', 'repeat', 'composite', 'tvmix', 'upmix21') $this.mode = [BusMode]::new($index, $remote)
AddBusModeMembers -PARAMS @('upmix41', 'upmix61', 'centeronly', 'lfeonly', 'rearonly') $this.eq = [BusEq]::new($index, $remote)
$this.levels = [BusLevels]::new($index, $remote)
} }
[string] ToString() { [string] identifier () {
return $this.GetType().Name + $this.index return 'Bus[' + $this.index + ']'
} }
[single] Getter ($cmd) {
return Param_Get -PARAM $cmd -IS_STRING $false
}
[string] Getter_String ($cmd) {
return Param_Get -PARAM $cmd -IS_STRING $true
}
[void] Setter ($cmd, $set) {
Param_Set -PARAM $cmd -Value $set
}
[string] cmd ($arg) {
return "Bus[" + $this.index + "].$arg"
}
hidden $_eq = $($this | Add-Member ScriptProperty 'eq' `
{
[bool]$this.Getter($this.cmd('EQ.on'))
} `
{
param($arg)
$this._eq = $this.Setter($this.cmd('EQ.on'), $arg)
}
)
hidden $_eq_ab = $($this | Add-Member ScriptProperty 'eq_ab' `
{
[bool]$this.Getter($this.cmd('eq.ab'))
} `
{
param($arg)
$this._eq = $this.Setter($this.cmd('eq.ab'), $arg)
}
)
[void] FadeTo ([single]$target, [int]$time) { [void] FadeTo ([single]$target, [int]$time) {
$this.Setter($this.cmd('FadeTo'), "($target, $time)") $this.Setter('FadeTo', "($target, $time)")
} }
[void] FadeBy ([single]$target, [int]$time) { [void] FadeBy ([single]$target, [int]$time) {
$this.Setter($this.cmd('FadeBy'), "($target, $time)") $this.Setter('FadeBy', "($target, $time)")
}
}
class BusLevels : IRemote {
[int]$init
[int]$offset
BusLevels ([int]$index, [Object]$remote) : base ($index, $remote) {
$this.init = $index * 8
$this.offset = 8
}
hidden [single] Convert([single]$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 : IRemote {
[System.Collections.ArrayList]$modes
BusMode ([int]$index, [Object]$remote) : base ($index, $remote) {
$this.modes = @(
'normal', 'amix', 'bmix', 'repeat', 'composite', 'tvmix', 'upmix21', 'upmix41', 'upmix61',
'centeronly', 'lfeonly', 'rearonly'
)
AddBoolMembers -PARAMS $this.modes
}
[string] identifier () {
return 'Bus[' + $this.index + '].mode'
}
[string] Get () {
foreach ($mode in $this.modes) {
if ($this.$mode) {
break
}
}
return $mode
}
[void] Set ([string]$mode) {
if ($this.modes.Contains($mode)) {
$this.Setter($mode, $true)
}
else {
throw [System.ArgumentException]::new("Invalid mode: $mode")
}
}
}
class BusEq : Eq {
BusEq ([int]$index, [Object]$remote) : base ($index, $remote, 'Bus') {
}
[string] identifier () {
return 'Bus[' + $this.index + '].EQ'
} }
} }
class PhysicalBus : Bus { class PhysicalBus : Bus {
PhysicalBus ([int]$index, [Object]$remote) : base ($index, $remote) { [Object]$device
}
hidden $_device = $($this | Add-Member ScriptProperty 'device' `
{
$this.Getter_String($this.cmd('device.name'))
} `
{
return Write-Warning ("ERROR: " + $this.cmd('device.name') + " is read only")
}
)
hidden $_sr = $($this | Add-Member ScriptProperty 'sr' ` PhysicalBus ([int]$index, [Object]$remote) : base ($index, $remote) {
{ $this.device = [BusDevice]::new($index, $remote)
$this.Getter($this.cmd('device.sr'))
} ` AddBoolMembers -PARAMS @('vaio')
{ }
return Write-Warning ("ERROR: " + $this.cmd('device.sr') + " is read only")
}
)
} }
class VirtualBus : Bus { class VirtualBus : Bus {
[Object]$device
VirtualBus ([int]$index, [Object]$remote) : base ($index, $remote) { VirtualBus ([int]$index, [Object]$remote) : base ($index, $remote) {
if ($this.remote.kind.name -eq 'basic') {
$this.device = [BusDevice]::new($index, $remote)
}
}
}
class BusDevice : Device {
BusDevice ([int]$index, [Object]$remote) : base ($index, $remote) {
if ($this.index -eq 0) {
$this.AddASIO()
}
}
[string] identifier () {
return 'Bus[' + $this.index + '].Device'
}
hidden [void] AddASIO () {
Add-Member -InputObject $this -MemberType ScriptProperty -Name 'asio' `
-Value {
return Write-Warning ("ERROR: $($this.identifier()).asio is write only")
} -SecondValue {
param([string]$arg)
return $this.Setter('asio', $arg)
} -Force
} }
} }

View File

@@ -1,64 +1,54 @@
. $PSScriptRoot\meta.ps1 class Special : IRemote {
Special ([Object]$remote) : base ($remote) {
class Special {
# Constructor
Special () {
AddActionMembers -PARAMS @('restart', 'shutdown', 'show') AddActionMembers -PARAMS @('restart', 'shutdown', 'show')
} }
[string] ToString() { [string] identifier () {
return $this.GetType().Name return 'Command'
} }
[single] Getter ($cmd) { [void] RunMacrobuttons() {
return Param_Get -PARAM $cmd -IS_STRING $false 'Launching the MacroButtons app' | Write-Verbose
Start-Process -FilePath $(Join-Path -Path $this.remote.vmpath -ChildPath 'VoicemeeterMacroButtons.exe')
} }
[void] Setter ($param, $val) { [void] CloseMacrobuttons() {
if ($val -is [Boolean]) { 'Closing the MacroButtons app' | Write-Verbose
Param_Set -PARAM $param -Value $(if ($val) { 1 } else { 0 }) Stop-Process -Name 'VoicemeeterMacroButtons'
}
else {
Param_Set -PARAM $param -Value $val
}
}
[string] cmd ($arg) {
return "Command.$arg"
} }
hidden $_hide = $($this | Add-Member ScriptProperty 'hide' ` hidden $_hide = $($this | Add-Member ScriptProperty 'hide' `
{ {
$this._hide = $this.Setter($this.cmd('show'), $false) $this._hide = $this.Setter('show', $false)
} ` } `
{} {}
) )
hidden $_showvbanchat = $($this | Add-Member ScriptProperty 'showvbanchat' ` hidden $_showvbanchat = $($this | Add-Member ScriptProperty 'showvbanchat' `
{ {
$this.Getter($this.cmd('DialogShow.VBANCHAT')) $this.Getter('DialogShow.VBANCHAT')
} ` } `
{ {
param([bool]$arg) param([bool]$arg)
$this._showvbanchat = $this.Setter($this.cmd('DialogShow.VBANCHAT'), $arg) $this._showvbanchat = $this.Setter('DialogShow.VBANCHAT', $arg)
} }
) )
hidden $_lock = $($this | Add-Member ScriptProperty 'lock' ` hidden $_lock = $($this | Add-Member ScriptProperty 'lock' `
{ {
$this._lock = $this.Getter($this.cmd('lock')) $this._lock = $this.Getter('lock')
} ` } `
{ {
param([bool]$arg) param([bool]$arg)
$this._lock = $this.Setter($this.cmd('lock'), $arg) $this._lock = $this.Setter('lock', $arg)
} }
) )
[void] Load ([string]$filename) { [void] Load ([string]$filename) {
$this.Setter($this.cmd('load'), $filename) $this.Setter('load', $filename)
} }
} }
function Make_Command { function Make_Command([Object]$remote) {
return [Special]::new() return [Special]::new($remote)
} }

52
lib/device.ps1 Normal file
View File

@@ -0,0 +1,52 @@
class Device : IRemote {
Device ([int]$index, [Object]$remote) : base ($index, $remote) {
}
hidden $_name = $($this | Add-Member ScriptProperty 'name' `
{
$this.Getter_String('name')
} `
{
return Write-Warning ("ERROR: $($this.identifier()).name is read only")
}
)
hidden $_sr = $($this | Add-Member ScriptProperty 'sr' `
{
[int]$this.Getter('sr')
} `
{
return Write-Warning ("ERROR: $($this.identifier()).sr is read only")
}
)
hidden $_wdm = $($this | Add-Member ScriptProperty 'wdm' `
{
return Write-Warning ("ERROR: $($this.identifier()).wdm is write only")
} `
{
param($arg)
return $this.Setter('wdm', $arg)
}
)
hidden $_ks = $($this | Add-Member ScriptProperty 'ks' `
{
return Write-Warning ("ERROR: $($this.identifier()).ks is write only")
} `
{
param($arg)
return $this.Setter('ks', $arg)
}
)
hidden $_mme = $($this | Add-Member ScriptProperty 'mme' `
{
return Write-Warning ("ERROR: $($this.identifier()).mme is write only")
} `
{
param($arg)
return $this.Setter('mme', $arg)
}
)
}

60
lib/eq.ps1 Normal file
View File

@@ -0,0 +1,60 @@
class Eq : IRemote {
[System.Collections.ArrayList]$channel
[string]$kindOfEq
Eq ([int]$index, [Object]$remote, [string]$kindOfEq) : base ($index, $remote) {
$this.kindOfEq = $kindOfEq
AddBoolMembers -PARAMS @('on', 'ab')
$this.channel = @()
for ($ch = 0; $ch -lt $remote.kind.eq_ch[$this.kindOfEq]; $ch++) {
$this.channel.Add([EqChannel]::new($ch, $remote, $this.identifier()))
}
}
[void] Load ([string]$filename) {
$param = 'Command.Load{0}Eq[{1}]' -f $this.kindOfEq, $this.index
$this.remote.Setter($param, $filename)
}
[void] Save ([string]$filename) {
$param = 'Command.Save{0}Eq[{1}]' -f $this.kindOfEq, $this.index
$this.remote.Setter($param, $filename)
}
}
class EqChannel : IRemote {
[System.Collections.ArrayList]$cell
[string]$eqId
EqChannel ([int]$index, [Object]$remote, [string]$eqId) : base ($index, $remote) {
$this.eqId = $eqId
$this.cell = @()
$cellCount = $this.remote.kind.cells
for ($c = 0; $c -lt $cellCount; $c++) {
$this.cell.Add([EqCell]::new($c, $remote, $this.identifier()))
}
}
[string] identifier () {
return '{0}.Channel[{1}]' -f $this.eqId, $this.index
}
}
class EqCell : IRemote {
[string]$channelId
EqCell ([int]$index, [Object]$remote, [string]$channelId) : base ($index, $remote) {
$this.channelId = $channelId
AddBoolMembers -PARAMS @('on')
AddIntMembers -PARAMS @('type')
AddFloatMembers -PARAMS @('f', 'gain', 'q')
}
[string] identifier () {
return '{0}.Cell[{1}]' -f $this.channelId, $this.index
}
}

View File

@@ -1,30 +1,19 @@
class VMRemoteErrors : Exception { class VMRemoteError : Exception {
[string]$msg VMRemoteError ([string]$msg) : base ($msg) {
VMRemoteErrors ([string]$msg) {
$this.msg = $msg
}
[string] ErrorMessage () {
return $this.msg
} }
} }
class LoginError : VMRemoteErrors { class LoginError : VMRemoteError {
LoginError ([string]$msg) : base ([string]$msg) { LoginError ([string]$msg) : base ($msg) {
} }
} }
class CAPIError : VMRemoteErrors { 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 "ERROR: CAPI return value: {0} in {1}" -f $this.retval, $this.caller
}
}

37
lib/fx.ps1 Normal file
View File

@@ -0,0 +1,37 @@
class Fx : IRemote {
[Object]$reverb
[Object]$delay
Fx ([Object]$remote) : base ($remote) {
$this.reverb = [FxReverb]::new($remote)
$this.delay = [FxDelay]::new($remote)
}
[string] identifier () {
return 'Fx'
}
}
class FxReverb : IRemote {
FxReverb ([Object]$remote) : base ($remote) {
AddBoolMembers -PARAMS @('on', 'ab')
}
[string] identifier () {
return 'Fx.Reverb'
}
}
class FxDelay : IRemote {
FxDelay ([Object]$remote) : base ($remote) {
AddBoolMembers -PARAMS @('on', 'ab')
}
[string] identifier () {
return 'Fx.Delay'
}
}
function Make_Fx ([Object]$remote) {
return [Fx]::new($remote)
}

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.')
}
} }

52
lib/iremote.ps1 Normal file
View File

@@ -0,0 +1,52 @@
class IRemote {
[Nullable[int]]$index
[Object]$remote
IRemote ([Object]$remote) {
$this.remote = $remote
}
IRemote ([int]$index, [Object]$remote) {
$this.index = $index
$this.remote = $remote
}
[single] Getter ($param) {
$this.ToString() + " Getter: $($this.Cmd($param))" | Write-Debug
return $this.remote.Getter($this.Cmd($param))
}
[string] Getter_String ($param) {
$this.ToString() + " Getter_String: $($this.Cmd($param))" | Write-Debug
return $this.remote.Getter_String($this.Cmd($param))
}
[void] Setter ($param, $val) {
$this.ToString() + " Setter: $($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"
}
# Must be overridden by derived classes
[string] identifier () {
throw [System.NotImplementedException]::new("$($this.GetType().Name) must override identifier()")
}
[string] ToString() {
if ($null -ne $this.index) {
return $this.GetType().Name + $this.index
}
return $this.GetType().Name
}
}

View File

@@ -1,33 +1,48 @@
$KindMap = @{ $KindMap = @{
"basic" = @{ 'basic' = @{
"name" = "basic" 'name' = 'basic'
"p_in" = 2 'p_in' = 2
"v_in" = 1 'v_in' = 1
"p_out" = 1 'p_out' = 1
"v_out" = 1 'v_out' = 1
"vban_in" = 4 'asio_in' = 4
"vban_out" = 4 'asio_out' = 8
'composite' = 0
'insert' = 0
'vban' = @{ 'in' = 4; 'out' = 4; 'midi' = 1; 'text' = 1 }
'eq_ch' = @{ 'strip' = 0; 'bus' = 0 }
'cells' = 0
}; };
"banana" = @{ 'banana' = @{
"name" = "banana" 'name' = 'banana'
"p_in" = 3 'p_in' = 3
"v_in" = 2 'v_in' = 2
"p_out" = 3 'p_out' = 3
"v_out" = 2 'v_out' = 2
"vban_in" = 8 'asio_in' = 6
"vban_out" = 8 'asio_out' = 8
'composite' = 8
'insert' = 22
'vban' = @{ 'in' = 8; 'out' = 8; 'midi' = 1; 'text' = 1 }
'eq_ch' = @{ 'strip' = 0; 'bus' = 8 }
'cells' = 6
}; };
"potato" = @{ 'potato' = @{
"name" = "potato" 'name' = 'potato'
"p_in" = 5 'p_in' = 5
"v_in" = 3 'v_in' = 3
"p_out" = 5 'p_out' = 5
"v_out" = 3 'v_out' = 3
"vban_in" = 8 'asio_in' = 10
"vban_out" = 8 'asio_out' = 8
'composite' = 8
'insert' = 34
'vban' = @{ 'in' = 8; 'out' = 8; 'midi' = 1; 'text' = 1 }
'eq_ch' = @{ 'strip' = 2; 'bus' = 8 }
'cells' = 6
}; };
} }
function GetKind ([string]$kind_id) { function GetKind ([string]$kindId) {
$KindMap[$kind_id] $KindMap[$kindId]
} }

View File

@@ -1,7 +1,12 @@
enum ButtonTypes {
State = 1
StateOnly = 2
Trigger = 3
}
class MacroButton { class MacroButton {
[int32]$index [int32]$index
# Constructor
MacroButton ([int]$index) { MacroButton ([int]$index) {
$this.index = $index $this.index = $index
} }
@@ -11,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' `
{ {
$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' `
{ {
$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' `
{ {
$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

@@ -5,9 +5,9 @@ function AddBoolMembers () {
[hashtable]$Signatures = @{} [hashtable]$Signatures = @{}
foreach ($param in $PARAMS) { foreach ($param in $PARAMS) {
# Define getter # Define getter
$Signatures["Getter"] = "[bool]`$this.Getter(`$this.cmd('{0}'))" -f $param $Signatures['Getter'] = "[bool]`$this.Getter('{0}')" -f $param
# Define setter # Define setter
$Signatures["Setter"] = "param ( [Single]`$arg )`n`$this.Setter(`$this.cmd('{0}'), `$arg)" ` $Signatures['Setter'] = "param ( [Single]`$arg )`n`$this.Setter('{0}', `$arg)" `
-f $param -f $param
Addmember Addmember
@@ -21,9 +21,9 @@ function AddFloatMembers () {
[hashtable]$Signatures = @{} [hashtable]$Signatures = @{}
foreach ($param in $PARAMS) { foreach ($param in $PARAMS) {
# Define getter # Define getter
$Signatures["Getter"] = "[math]::Round(`$this.Getter(`$this.cmd('{0}')), 1)" -f $param $Signatures['Getter'] = "[math]::Round(`$this.Getter('{0}'), 1)" -f $param
# Define setter # Define setter
$Signatures["Setter"] = "param ( [Single]`$arg )`n`$this.Setter(`$this.cmd('{0}'), `$arg)" ` $Signatures['Setter'] = "param ( [Single]`$arg )`n`$this.Setter('{0}', `$arg)" `
-f $param -f $param
Addmember Addmember
@@ -37,9 +37,9 @@ function AddIntMembers () {
[hashtable]$Signatures = @{} [hashtable]$Signatures = @{}
foreach ($param in $PARAMS) { foreach ($param in $PARAMS) {
# Define getter # Define getter
$Signatures["Getter"] = "[Int]`$this.Getter(`$this.cmd('{0}'))" -f $param $Signatures['Getter'] = "[Int]`$this.Getter('{0}')" -f $param
# Define setter # Define setter
$Signatures["Setter"] = "param ( [Single]`$arg )`n`$this.Setter(`$this.cmd('{0}'), `$arg)" ` $Signatures['Setter'] = "param ( [Single]`$arg )`n`$this.Setter('{0}', `$arg)" `
-f $param -f $param
Addmember Addmember
@@ -53,9 +53,9 @@ function AddStringMembers () {
[hashtable]$Signatures = @{} [hashtable]$Signatures = @{}
foreach ($param in $PARAMS) { foreach ($param in $PARAMS) {
# Define getter # Define getter
$Signatures["Getter"] = "[String]`$this.Getter_String(`$this.cmd('{0}'))" -f $param $Signatures['Getter'] = "[String]`$this.Getter_String('{0}')" -f $param
# Define setter # Define setter
$Signatures["Setter"] = "param ( [String]`$arg )`n`$this.Setter(`$this.cmd('{0}'), `$arg)" ` $Signatures['Setter'] = "param ( [String]`$arg )`n`$this.Setter('{0}', `$arg)" `
-f $param -f $param
Addmember Addmember
@@ -69,9 +69,9 @@ function AddActionMembers () {
[hashtable]$Signatures = @{} [hashtable]$Signatures = @{}
foreach ($param in $PARAMS) { foreach ($param in $PARAMS) {
# Define getter # Define getter
$Signatures["Getter"] = "`$this.Setter(`$this.cmd('{0}'), `$true)" -f $param $Signatures['Getter'] = "`$this.Setter('{0}', `$true)" -f $param
# Define setter # Define setter
$Signatures["Setter"] = "" $Signatures['Setter'] = ''
Addmember Addmember
} }
@@ -83,7 +83,7 @@ function AddChannelMembers () {
[System.Collections.ArrayList]$channels = @() [System.Collections.ArrayList]$channels = @()
1..$($num_A + $num_B) | ForEach-Object { 1..$($num_A + $num_B) | ForEach-Object {
if ($_ -le $num_A) { $channels.Add("A{0}" -f $_) } else { $channels.Add("B{0}" -f $($_ - $num_A)) } if ($_ -le $num_A) { $channels.Add('A{0}' -f $_) } else { $channels.Add('B{0}' -f $($_ - $num_A)) }
} }
AddBoolMembers -PARAMS $channels AddBoolMembers -PARAMS $channels
@@ -93,40 +93,23 @@ function AddGainlayerMembers () {
[hashtable]$Signatures = @{} [hashtable]$Signatures = @{}
0..7 | ForEach-Object { 0..7 | ForEach-Object {
# Define getter # Define getter
$Signatures["Getter"] = "`$this.Getter(`$this.cmd('gainlayer[{0}]'))" -f $_ $Signatures['Getter'] = "`$this.Getter('gainlayer[{0}]')" -f $_
# Define setter # Define setter
$Signatures["Setter"] = "param ( [Single]`$arg )`n`$this.Setter(`$this.cmd('gainlayer[{0}]'), `$arg)" ` $Signatures['Setter'] = "param ( [Single]`$arg )`n`$this.Setter('gainlayer[{0}]', `$arg)" `
-f $_ -f $_
$param = "gainlayer{0}" -f $_ $param = 'gainlayer{0}' -f $_
$null = $param $null = $param
Addmember Addmember
} }
} }
function AddBusModeMembers () {
param(
[String[]]$PARAMS
)
[hashtable]$Signatures = @{}
foreach ($param in $PARAMS) {
# Define getter
$Signatures["Getter"] = "[bool]`$this.Getter(`$this.cmd('mode.{0}'))" -f $param
# Define setter
$Signatures["Setter"] = "param ( [Single]`$arg )`n`$this.Setter(`$this.cmd('mode.{0}'), `$arg)" `
-f $param
$param = "mode_{0}" -f $param
Addmember
}
}
function Addmember { function Addmember {
$AddMemberParams = @{ $AddMemberParams = @{
Name = $param Name = $param
MemberType = 'ScriptProperty' MemberType = 'ScriptProperty'
Value = [scriptblock]::Create($Signatures["Getter"]) Value = [scriptblock]::Create($Signatures['Getter'])
SecondValue = [scriptblock]::Create($Signatures["Setter"]) SecondValue = [scriptblock]::Create($Signatures['Setter'])
} }
$this | Add-Member @AddMemberParams $this | Add-Member @AddMemberParams
} }

138
lib/option.ps1 Normal file
View File

@@ -0,0 +1,138 @@
class Option : IRemote {
[System.Collections.ArrayList]$delay
[Object]$buffer
Option ([Object]$remote) : base ($remote) {
AddBoolMembers -PARAMS @('asiosr', 'monitorOnSel', 'sliderMode')
$this.buffer = [OptionBuffer]::new($remote)
$num_A = $this.remote.kind.p_out
if ($this.remote.kind.name -eq 'basic') {
$num_A += $this.remote.kind.v_out
}
$this.delay = @()
for ($i = 0; $i -lt $num_A; $i++) {
$this.delay.Add([FloatArrayMember]::new($i, 'delay', $this, 2))
}
}
[string] identifier () {
return 'Option'
}
hidden $_sr = $($this | Add-Member ScriptProperty 'sr' `
{
[int]$this.Getter('sr')
} `
{
param([int]$arg)
$opts = @(32000, 44100, 48000, 88200, 96000, 176400, 192000)
if ($opts.Contains($arg)) {
$this._sr = $this.Setter('sr', $arg)
}
else {
Write-Warning ('Expected one of', $opts)
}
}
)
hidden $_monitoringBus = $($this | Add-Member ScriptProperty 'monitoringBus' `
{
foreach ($bus in 0..$($this.remote.kind.p_out + $this.remote.kind.v_out - 1)) {
if ($this.remote.Getter("Bus[$bus].Monitor")) {
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._monitoringBus = $this.remote.Setter("Bus[$arg].Monitor", $arg)
}
else {
Write-Warning ("Expected a bus index between 0 and $busMax")
}
}
)
}
class OptionBuffer : IRemote {
OptionBuffer ([Object]$remote) : base ($remote) {}
[string] identifier () {
return 'Option.Buffer'
}
hidden $_mme = $($this | Add-Member ScriptProperty 'mme' `
{
[int]$this.Getter('mme')
} `
{
param([int]$arg)
$opts = @(441, 480, 512, 576, 640, 704, 768, 896, 1024, 1536, 2048)
if ($opts.Contains($arg)) {
$this._mme = $this.Setter('mme', $arg)
}
else {
Write-Warning ('Expected one of', $opts)
}
}
)
hidden $_wdm = $($this | Add-Member ScriptProperty 'wdm' `
{
[int]$this.Getter('wdm')
} `
{
param([int]$arg)
$opts = @(128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 441, 448, 480, 512, 576, 640, 704, 768, 1024, 1536, 2048)
if ($opts.Contains($arg)) {
$this._wdm = $this.Setter('wdm', $arg)
}
else {
Write-Warning ('Expected one of', $opts)
}
}
)
hidden $_ks = $($this | Add-Member ScriptProperty 'ks' `
{
[int]$this.Getter('ks')
} `
{
param([int]$arg)
$opts = @(128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 441, 448, 480, 512, 576, 640, 704, 768, 1024, 1536, 2048)
if ($opts.Contains($arg)) {
$this._ks = $this.Setter('ks', $arg)
}
else {
Write-Warning ('Expected one of', $opts)
}
}
)
hidden $_asio = $($this | Add-Member ScriptProperty 'asio' `
{
[int]$this.Getter('asio')
} `
{
param([int]$arg)
$opts = @(0, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 441, 448, 480, 512, 576, 640, 704, 768, 1024)
if ($opts.Contains($arg)) {
$this._asio = $this.Setter('asio', $arg)
}
else {
Write-Warning ('Expected one of', $opts)
}
}
)
}
function Make_Option ([Object]$remote) {
return [Option]::new($remote)
}

52
lib/patch.ps1 Normal file
View File

@@ -0,0 +1,52 @@
class Patch : IRemote {
[System.Collections.ArrayList]$asio
[System.Collections.ArrayList]$composite
[System.Collections.ArrayList]$insert
Patch ([Object]$remote) : base ($remote) {
AddBoolMembers -PARAMS @('postFaderComposite', 'postFxInsert')
$this.AddASIOOutMembers()
$this.asio = @()
for ($i = 0; $i -lt $remote.kind.asio_in; $i++) {
$this.asio.Add([IntArrayMember]::new($i, 'asio', $this))
}
$this.composite = @()
for ($i = 0; $i -lt $remote.kind.composite; $i++) {
$this.composite.Add([IntArrayMember]::new($i, 'composite', $this))
}
$this.insert = @()
for ($i = 0; $i -lt $remote.kind.insert; $i++) {
$this.insert.Add([BoolArrayMember]::new($i, 'insert', $this))
}
}
[string] identifier () {
return 'Patch'
}
hidden [void] AddASIOOutMembers () {
$num_A = $this.remote.kind.p_out
if ($this.remote.kind.name -eq 'basic') {
$num_A += $this.remote.kind.v_out
}
$asio_out = $this.remote.kind.asio_out
if ($asio_out -le 0) { return }
for ($a = 2; $a -le $num_A; $a++) {
[System.Collections.ArrayList]$members = @()
for ($i = 0; $i -lt $asio_out; $i++) {
$members.Add([IntArrayMember]::new($i, "OutA$a", $this))
}
Add-Member -InputObject $this -MemberType NoteProperty -Name "OutA$a" -Value $members -Force
}
}
}
function Make_Patch ([Object]$remote) {
return [Patch]::new($remote)
}

View File

@@ -1,5 +1,5 @@
function Get_Profiles ([string]$kind_id) { function Get_Profiles ([string]$kind_id) {
$basepath = Join-Path -Path $(Split-Path -Path $PSScriptRoot) -ChildPath "profiles" $basepath = Join-Path -Path $(Split-Path -Path $PSScriptRoot) -ChildPath 'profiles'
if (Test-Path $basepath) { if (Test-Path $basepath) {
$fullpath = Join-Path -Path $basepath -ChildPath $kind_id $fullpath = Join-Path -Path $basepath -ChildPath $kind_id
} }
@@ -11,7 +11,7 @@ function Get_Profiles ([string]$kind_id) {
$filenames | ForEach-Object { $filenames | ForEach-Object {
(Join-Path -Path $fullpath -ChildPath $_) | ForEach-Object { (Join-Path -Path $fullpath -ChildPath $_) | ForEach-Object {
$filename = [System.IO.Path]::GetFileNameWithoutExtension($_) $filename = [System.IO.Path]::GetFileNameWithoutExtension($_)
Write-Host ("Importing profile " + $kind_id + "/" + $filename) Write-Host ('Importing profile ' + $kind_id + '/' + $filename)
$data[$filename] = Import-PowerShellDataFile -Path $_ $data[$filename] = Import-PowerShellDataFile -Path $_
} }
} }
@@ -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,48 +1,164 @@
. $PSScriptRoot\meta.ps1 class Recorder : IRemote {
[Object]$mode
[System.Collections.ArrayList]$armstrip
[System.Collections.ArrayList]$armbus
class Recorder { Recorder ([Object]$remote) : base ($remote) {
[Object]$remote $this.mode = [RecorderMode]::new($remote)
# Constructor $this.armstrip = @()
Recorder ([Object]$remote) { 0..($remote.kind.p_in + $remote.kind.v_in - 1) | ForEach-Object {
$this.remote = $remote $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
} }
[string] ToString() { [string] identifier () {
return $this.GetType().Name return 'Recorder'
}
[single] Getter ($cmd) {
return Param_Get -PARAM $cmd -IS_STRING $false
}
[void] Setter ($param, $val) {
if ($val -is [Boolean]) {
Param_Set -PARAM $param -Value $(if ($val) { 1 } else { 0 })
}
else {
Param_Set -PARAM $param -Value $val
}
}
[string] cmd ($arg) {
return "Recorder.$arg"
} }
hidden $_loop = $($this | Add-Member ScriptProperty 'loop' ` hidden $_loop = $($this | Add-Member ScriptProperty 'loop' `
{ {
return Write-Warning ("ERROR: " + $this.cmd('mode.loop') + " is write only") [bool]$this.mode.loop
} ` } `
{ {
param([bool]$arg) param($arg)
$this._loop = $this.Setter($this.cmd('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($this.cmd('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 : IRemote {
RecorderMode ([Object]$remote) : base ($remote) {
AddBoolMembers -PARAMS @('recbus', 'playonload', 'loop', 'multitrack')
}
[string] identifier () {
return 'Recorder.Mode'
}
}
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)]"
} }
} }

View File

@@ -1,13 +1,7 @@
. $PSScriptRoot\meta.ps1 class Strip : IRemote {
[Object]$levels
class Strip {
[int]$index
[Object]$remote
Strip ([int]$index, [Object]$remote) {
$this.index = $index
$this.remote = $remote
Strip ([int]$index, [Object]$remote) : base ($index, $remote) {
AddBoolMembers -PARAMS @('mono', 'solo', 'mute') AddBoolMembers -PARAMS @('mono', 'solo', 'mute')
AddIntMembers -PARAMS @('limit') AddIntMembers -PARAMS @('limit')
AddFloatMembers -PARAMS @('gain', 'pan_x', 'pan_y') AddFloatMembers -PARAMS @('gain', 'pan_x', 'pan_y')
@@ -15,68 +9,180 @@ class Strip {
AddChannelMembers AddChannelMembers
AddGainlayerMembers AddGainlayerMembers
$this.levels = [StripLevels]::new($index, $remote)
} }
[string] ToString() { [string] identifier () {
return $this.GetType().Name + $this.index return 'Strip[' + $this.index + ']'
}
[single] Getter ($cmd) {
return Param_Get -PARAM $cmd -IS_STRING $false
}
[string] Getter_String ($cmd) {
return Param_Get -PARAM $cmd -IS_STRING $true
}
[void] Setter ($cmd, $val) {
Param_Set -PARAM $cmd -Value $val
}
[string] cmd ($arg) {
return "Strip[" + $this.index + "].$arg"
} }
[void] FadeTo ([single]$target, [int]$time) { [void] FadeTo ([single]$target, [int]$time) {
$this.Setter($this.cmd('FadeTo'), "($target, $time)") $this.Setter('FadeTo', "($target, $time)")
} }
[void] FadeBy ([single]$target, [int]$time) { [void] FadeBy ([single]$target, [int]$time) {
$this.Setter($this.cmd('FadeBy'), "($target, $time)") $this.Setter('FadeBy', "($target, $time)")
}
}
class StripLevels : IRemote {
[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 { class PhysicalStrip : Strip {
[Object]$comp
[Object]$gate
[Object]$denoiser
[Object]$eq
[Object]$device
PhysicalStrip ([int]$index, [Object]$remote) : base ($index, $remote) { PhysicalStrip ([int]$index, [Object]$remote) : base ($index, $remote) {
AddFloatMembers -PARAMS @('comp', 'gate', 'color_x', 'color_y', 'fx_x', 'fx_y') AddFloatMembers -PARAMS @('color_x', 'color_y', 'fx_x', 'fx_y')
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 = [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 StripComp : IRemote {
StripComp ([int]$index, [Object]$remote) : base ($index, $remote) {
AddFloatMembers -PARAMS @('gainin', 'ratio', 'threshold', 'attack', 'release', 'knee', 'gainout')
AddBoolMembers -PARAMS @('makeup')
} }
hidden $_device = $($this | Add-Member ScriptProperty 'device' ` [string] identifier () {
{ return 'Strip[' + $this.index + '].Comp'
$this.Getter_String($this.cmd('device.name')) }
} `
{
return Write-Warning ("ERROR: " + $this.cmd('device.name') + " is read only")
}
)
hidden $_sr = $($this | Add-Member ScriptProperty 'sr' ` hidden $_knob = $($this | Add-Member ScriptProperty 'knob' `
{ {
$this.Getter($this.cmd('device.sr')) $this.Getter_String('')
} ` } `
{ {
return Write-Warning ("ERROR: " + $this.cmd('device.sr') + " is read only") param($arg)
return $this.Setter('', $arg)
} }
) )
} }
class StripGate : IRemote {
StripGate ([int]$index, [Object]$remote) : base ($index, $remote) {
AddFloatMembers -PARAMS @('threshold', 'damping', 'bpsidechain', 'attack', 'hold', 'release')
}
[string] identifier () {
return 'Strip[' + $this.index + '].Gate'
}
hidden $_knob = $($this | Add-Member ScriptProperty 'knob' `
{
$this.Getter_String('')
} `
{
param($arg)
return $this.Setter('', $arg)
}
)
}
class StripDenoiser : IRemote {
StripDenoiser ([int]$index, [Object]$remote) : base ($index, $remote) {
}
[string] identifier () {
return 'Strip[' + $this.index + '].Denoiser'
}
hidden $_knob = $($this | Add-Member ScriptProperty 'knob' `
{
$this.Getter_String('')
} `
{
param($arg)
return $this.Setter('', $arg)
}
)
}
class StripEq : Eq {
StripEq ([int]$index, [Object]$remote) : base ($index, $remote, 'Strip') {
}
[string] identifier () {
return 'Strip[' + $this.index + '].EQ'
}
}
class StripDevice : Device {
StripDevice ([int]$index, [Object]$remote) : base ($index, $remote) {
}
[string] identifier () {
return 'Strip[' + $this.index + '].Device'
}
}
class VirtualStrip : Strip { class VirtualStrip : Strip {
VirtualStrip ([int]$index, [Object]$remote) : base ($index, $remote) { VirtualStrip ([int]$index, [Object]$remote) : base ($index, $remote) {
AddBoolMembers -PARAMS @('mc') AddBoolMembers -PARAMS @('mc')
AddIntMembers -PARAMS @('k') AddIntMembers -PARAMS @('k')
} }
[void] AppGain ([string]$appname, [single]$gain) {
$this.Setter('AppGain', "(`"$appname`", $gain)")
}
[void] AppMute ([string]$appname, [bool]$mutestate) {
$this.Setter('AppMute', "(`"$appname`", $(if ($mutestate) { 1 } else { 0 })")
}
} }

View File

@@ -1,70 +1,29 @@
class Vban { class Vban : IRemote {
[int32]$index
[string]$direction [string]$direction
# Constructor Vban ([int]$index, [Object]$remote, [string]$direction) : base ($index, $remote) {
Vban ([int]$index) { $this.direction = $direction
$this.index = $index
} }
[string] ToString() { [string] identifier () {
return $this.GetType().Name + $this.index return 'vban.' + $this.direction + 'stream[' + $this.index + ']'
} }
}
[single] Getter ($cmd) { class VbanAudio : Vban {
return Param_Get -PARAM $cmd -IS_STRING $false VbanAudio ([int]$index, [Object]$remote, [string]$direction) : base ($index, $remote, $direction) {
AddBoolMembers -PARAMS @('on')
AddStringMembers -PARAMS @('name', 'ip')
} }
[string] Getter_String ($cmd) {
return Param_Get -PARAM $cmd -IS_STRING $true
}
[void] Setter ($cmd, $set) {
Param_Set -PARAM $cmd -Value $set
}
[string] cmd ($arg) {
return "vban." + $this.direction + "stream[" + $this.index + "].$arg"
}
hidden $_on = $($this | Add-Member ScriptProperty 'on' `
{
$this.Getter($this.cmd('on'))
} `
{
param([bool]$arg)
$this._on = $this.Setter($this.cmd('on'), $arg)
}
)
hidden $_name = $($this | Add-Member ScriptProperty 'name' `
{
$this.Getter_String($this.cmd('name'))
} `
{
param([string]$arg)
$this._name = $this.Setter($this.cmd('name'), $arg)
}
)
hidden $_ip = $($this | Add-Member ScriptProperty 'ip' `
{
$this.Getter_String($this.cmd('ip'))
} `
{
param([string]$arg)
$this._ip = $this.Setter($this.cmd('ip'), $arg)
}
)
hidden $_port = $($this | Add-Member ScriptProperty 'port' ` hidden $_port = $($this | Add-Member ScriptProperty 'port' `
{ {
$this.Getter($this.cmd('port')) [int]$this.Getter('port')
} ` } `
{ {
param([string]$arg) param([int]$arg)
if ($arg -in 1024..65535) { if ($arg -ge 1024 -and $arg -le 65535) {
$this._port = $this.Setter($this.cmd('port'), $arg) $this._port = $this.Setter('port', $arg)
} }
else { else {
Write-Warning ('Expected value from 1024 to 65535') Write-Warning ('Expected value from 1024 to 65535')
@@ -74,15 +33,15 @@ class Vban {
hidden $_sr = $($this | Add-Member ScriptProperty 'sr' ` hidden $_sr = $($this | Add-Member ScriptProperty 'sr' `
{ {
$this.Getter($this.cmd('sr')) [int]$this.Getter('sr')
} ` } `
{ {
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 {
$opts = @(11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000) $opts = @(11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000)
if ($opts.Contains($arg)) { if ($opts.Contains($arg)) {
$this._port = $this.Setter($this.cmd('sr'), $arg) $this._sr = $this.Setter('sr', $arg)
} }
else { else {
Write-Warning ('Expected one of', $opts) Write-Warning ('Expected one of', $opts)
@@ -93,14 +52,14 @@ class Vban {
hidden $_channel = $($this | Add-Member ScriptProperty 'channel' ` hidden $_channel = $($this | Add-Member ScriptProperty 'channel' `
{ {
$this.Getter($this.cmd('channel')) [int]$this.Getter('channel')
} ` } `
{ {
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($this.cmd('channel'), $arg) $this._channel = $this.Setter('channel', $arg)
} }
else { else {
Write-Warning ('Expected value from 1 to 8') Write-Warning ('Expected value from 1 to 8')
@@ -111,16 +70,16 @@ class Vban {
hidden $_bit = $($this | Add-Member ScriptProperty 'bit' ` hidden $_bit = $($this | Add-Member ScriptProperty 'bit' `
{ {
$val = if ($this.Getter($this.cmd('bit')) -eq 1) { 16 } else { 24 } $val = if ($this.Getter('bit') -eq 1) { 16 } else { 24 }
return $val return $val
} ` } `
{ {
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 (@(16, 24).Contains($arg)) { if (@(16, 24).Contains($arg)) {
$val = if ($arg -eq 16) { 1 } else { 2 } $val = if ($arg -eq 16) { 1 } else { 2 }
$this._bit = $this.Setter($this.cmd('bit'), $val) $this._bit = $this.Setter('bit', $val)
} }
else { else {
Write-Warning ('Expected value 16 or 24') Write-Warning ('Expected value 16 or 24')
@@ -131,67 +90,156 @@ class Vban {
hidden $_quality = $($this | Add-Member ScriptProperty 'quality' ` hidden $_quality = $($this | Add-Member ScriptProperty 'quality' `
{ {
$this.Getter($this.cmd('quality')) [int]$this.Getter('quality')
} ` } `
{ {
param([int]$arg) param([int]$arg)
if ($this.direction -eq "in") { Write-Warning ('Error, read only value') } if ($arg -ge 0 -and $arg -le 4) {
$this._quality = $this.Setter('quality', $arg)
}
else { else {
if ($arg -in 0..4) { Write-Warning ('Expected value from 0 to 4')
$this._quality = $this.Setter($this.cmd('quality'), $arg)
}
else {
Write-Warning ('Expected value from 0 to 4')
}
} }
} }
) )
hidden $_route = $($this | Add-Member ScriptProperty 'route' ` hidden $_route = $($this | Add-Member ScriptProperty 'route' `
{ {
$this.Getter($this.cmd('route')) [int]$this.Getter('route')
} ` } `
{ {
param([int]$arg) param([int]$arg)
if ($this.direction -eq "in") { Write-Warning ('Error, read only value') } $rt = $this.remote.kind['p_' + $this.direction] + $this.remote.kind['v_' + $this.direction] - 1
if ($arg -ge 0 -and $arg -le $rt) {
$this._route = $this.Setter('route', $arg)
}
else { else {
if ($arg -in 0..8) { Write-Warning ("Expected value from 0 to $rt")
$this._route = $this.Setter($this.cmd('route'), $arg)
}
else {
Write-Warning ('Expected value from 0 to 8')
}
} }
} }
) )
} }
class VbanMidi : Vban {
VbanMidi ([int]$index, [Object]$remote, [string]$direction) : base ($index, $remote, $direction) {
}
class VbanInstream : Vban { hidden $_on = $($this | Add-Member ScriptProperty 'on' `
# Constructor {
VbanInstream ([int]$index) : base ($index) { return Write-Warning ("ERROR: $($this.identifier()).on is write only")
$this.direction = "in" } `
{
param([bool]$arg)
$this._on = $this.Setter('on', $arg)
}
)
hidden $_name = $($this | Add-Member ScriptProperty 'name' `
{
return Write-Warning ("ERROR: $($this.identifier()).name is write only")
} `
{
param([string]$arg)
$this._name = $this.Setter('name', $arg)
}
)
hidden $_ip = $($this | Add-Member ScriptProperty 'ip' `
{
return Write-Warning ("ERROR: $($this.identifier()).ip is write only")
} `
{
param([string]$arg)
$this._ip = $this.Setter('ip', $arg)
}
)
}
class VbanText : Vban {
VbanText ([int]$index, [Object]$remote, [string]$direction) : base ($index, $remote, $direction) {
}
hidden $_on = $($this | Add-Member ScriptProperty 'on' `
{
return Write-Warning ("ERROR: $($this.identifier()).on is write only")
} `
{
param([bool]$arg)
$this._on = $this.Setter('on', $arg)
}
)
hidden $_name = $($this | Add-Member ScriptProperty 'name' `
{
return Write-Warning ("ERROR: $($this.identifier()).name is write only")
} `
{
param([string]$arg)
$this._name = $this.Setter('name', $arg)
}
)
hidden $_ip = $($this | Add-Member ScriptProperty 'ip' `
{
return Write-Warning ("ERROR: $($this.identifier()).ip is write only")
} `
{
param([string]$arg)
$this._ip = $this.Setter('ip', $arg)
}
)
}
class VbanInAudio : VbanAudio {
VbanInAudio ([int]$index, [Object]$remote) : base ($index, $remote, 'in') {
} }
} }
class VbanInMidi : VbanMidi {
class VbanOutstream : Vban { VbanInMidi ([int]$index, [Object]$remote) : base ($index, $remote, 'in') {
# Constructor
VbanOutstream ([int]$index) : base ($index) {
$this.direction = "out"
} }
} }
class VbanInText : VbanText {
VbanInText ([int]$index, [Object]$remote) : base ($index, $remote, 'in') {
}
}
class VbanOutAudio : VbanAudio {
VbanOutAudio ([int]$index, [Object]$remote) : base ($index, $remote, 'out') {
}
}
class VbanOutMidi : VbanMidi {
VbanOutMidi ([int]$index, [Object]$remote) : base ($index, $remote, 'out') {
}
}
function Make_Vban ([Object]$remote) { function Make_Vban ([Object]$remote) {
[System.Collections.ArrayList]$instream = @() [System.Collections.ArrayList]$instream = @()
[System.Collections.ArrayList]$outstream = @() [System.Collections.ArrayList]$outstream = @()
0..$($remote.kind.vban_in - 1) | ForEach-Object { $totalInstreams = $remote.kind.vban.in + $remote.kind.vban.midi + $remote.kind.vban.text
[void]$instream.Add([VbanInstream]::new($_)) $totalOutstreams = $remote.kind.vban.out + $remote.kind.vban.midi
for ($i = 0; $i -lt $totalInstreams; $i++) {
if ($i -lt $remote.kind.vban.in) {
[void]$instream.Add([VbanInAudio]::new($i, $remote))
}
elseif ($i -lt ($remote.kind.vban.in + $remote.kind.vban.midi)) {
[void]$instream.Add([VbanInMidi]::new($i, $remote))
}
else {
[void]$instream.Add([VbanInText]::new($i, $remote))
}
} }
0..$($remote.kind.vban_out - 1) | ForEach-Object { for ($i = 0; $i -lt $totalOutstreams; $i++) {
[void]$outstream.Add([VbanOutstream]::new($_)) if ($i -lt $remote.kind.vban.out) {
[void]$outstream.Add([VbanOutAudio]::new($i, $remote))
}
else {
[void]$outstream.Add([VbanOutMidi]::new($i, $remote))
}
} }
$CustomObject = [pscustomobject]@{ $CustomObject = [pscustomobject]@{
@@ -201,11 +249,25 @@ function Make_Vban ([Object]$remote) {
$CustomObject | Add-Member ScriptProperty 'enable' ` $CustomObject | Add-Member ScriptProperty 'enable' `
{ {
return Write-Warning ("ERROR: vban.enable is write only") return [bool]( Param_Get -PARAM 'vban.enable' )
} ` } `
{ {
param([bool]$arg) param([bool]$arg)
Param_Set -PARAM 'vban.Enable' -Value $(if ($arg) { 1 } else { 0 }) Param_Set -PARAM 'vban.enable' -Value $(if ($arg) { 1 } else { 0 })
}
$CustomObject | Add-Member ScriptProperty 'port' `
{
return [int]( Param_Get -PARAM 'vban.instream[0].port' )
} `
{
param([int]$arg)
if ($arg -ge 1024 -and $arg -le 65535) {
Param_Set -PARAM 'vban.instream[0].port' -Value $arg
}
else {
Write-Warning ('Expected value from 1024 to 65535')
}
} }
$CustomObject $CustomObject

View File

@@ -1,11 +1,11 @@
Describe -Tag 'higher', -TestName 'All Higher Tests' { Describe -Tag 'higher', -TestName 'All Higher Tests' {
Describe 'Bool tests' -ForEach @( Describe 'Bool tests' -Tag 'bool' -ForEach @(
@{ Value = $true; Expected = $true } @{ Value = $true; Expected = $true }
@{ Value = $false; Expected = $false } @{ Value = $false; Expected = $false }
){ ) {
Context 'Strip, one physical one virtual' -ForEach @( Context 'Strip, one physical one virtual' -ForEach @(
@{ Index = $phys_in }, @{ Index = $virt_in } @{ Index = $phys_in }, @{ Index = $virt_in }
){ ) {
It "Should set and get Strip[$index].Mute" { It "Should set and get Strip[$index].Mute" {
$vmr.strip[$index].mute = $value $vmr.strip[$index].mute = $value
$vmr.strip[$index].mute | Should -Be $expected $vmr.strip[$index].mute | Should -Be $expected
@@ -27,69 +27,137 @@ Describe -Tag 'higher', -TestName 'All Higher Tests' {
} }
} }
Context 'Strip, physical only' -ForEach @(
@{ Index = $phys_in }
) {
Context 'Eq' -Skip:$ifNotPotato -ForEach @(
@{ Eq = $vmr.strip[$index].eq }
) {
It "Should set Strip[$index].EQ.On to $value" {
$eq.on = $value
$eq.on | Should -Be $value
}
It "Should set Strip[$index].EQ.AB to $value" {
$eq.ab = $value
$eq.ab | Should -Be $value
}
It "Should set Strip[$index].EQ.Channel[$strip_ch].Cell[$cells].On to $value" {
$eq.channel[$strip_ch].cell[$cells].on = $value
$eq.channel[$strip_ch].cell[$cells].on | Should -Be $value
}
}
}
Context 'Bus, one physical one virtual' -ForEach @( Context 'Bus, one physical one virtual' -ForEach @(
@{ Index = $phys_out }, @{ Index = $virt_out } @{ Index = $phys_out }, @{ Index = $virt_out }
){ ) {
It "Should set and get Bus[$index].Eq" -Skip:$ifBasic { It "Should set and get Bus[$index].Monitor" -Skip:$ifNotPotato {
$vmr.bus[$index].eq = $value $vmr.bus[$index].monitor = $value
$vmr.bus[$index].eq | Should -Be $expected $vmr.bus[$index].monitor | Should -Be $expected
} }
It "Should set and get Bus[$index].Mono" { It "Should set and get Bus[$index].Mute" {
$vmr.bus[$index].mono = $value $vmr.bus[$index].mute = $value
$vmr.bus[$index].mono | Should -Be $expected $vmr.bus[$index].mute | Should -Be $expected
} }
It "Should set and get Bus[$index].mode_amix" -Skip:$ifBasic { It "Should set and get Bus[$index].Sel" -Skip:$ifNotPotato {
$vmr.bus[$index].mode_amix = $value $vmr.bus[$index].sel = $value
$vmr.bus[$index].mode_amix | Should -Be $expected $vmr.bus[$index].sel | Should -Be $expected
} }
It "Should set and get Bus[$index].mode_centeronly" -Skip:$ifBasic { It "Should set and get Bus[$index].mode.amix" {
$vmr.bus[$index].mode_centeronly = $value $vmr.bus[$index].mode.amix = $value
$vmr.bus[$index].mode_centeronly | Should -Be $expected $vmr.bus[$index].mode.amix | Should -Be $expected
}
It "Should set and get Bus[$index].mode.centeronly" -Skip:$ifBasic {
$vmr.bus[$index].mode.centeronly = $value
$vmr.bus[$index].mode.centeronly | Should -Be $expected
}
Context 'Eq' -Skip:$ifBasic -ForEach @(
@{ Eq = $vmr.bus[$index].eq }
) {
It "Should set Bus[$index].EQ.On to $value" {
$eq.on = $value
$eq.on | Should -Be $value
}
It "Should set Bus[$index].EQ.AB to $value" {
$eq.ab = $value
$eq.ab | Should -Be $value
}
It "Should set Bus[$index].EQ.Channel[$bus_ch].Cell[$cells].On to $value" {
$eq.channel[$bus_ch].cell[$cells].on = $value
$eq.channel[$bus_ch].cell[$cells].on | Should -Be $value
}
}
}
Context 'Bus, physical only' -ForEach @(
@{ Index = $phys_out }
) {
It "Should set and get Bus[$index].vaio" {
$vmr.bus[$index].vaio = $value
$vmr.bus[$index].vaio | Should -Be $expected
} }
} }
Context 'Macrobutton' -ForEach @( Context 'Macrobutton' -ForEach @(
@{ Index = 0 }, @{ Index = 69 } @{ Index = 0 }, @{ Index = 69 }
){ ) {
It "Should set and get macrobutton[$index] State" { It "Should set and get macrobutton[$index] State" {
$vmr.button[$index].state = $value $vmr.button[$index].state = $value
$vmr.button[$index].state | Should -Be $expected $vmr.button[$index].state | Should -Be $expected
} }
} }
Context 'Vban instream' -ForEach @( Context 'Vban' {
@{ Index = $vban_in } It 'Should set and get Vban.enable' {
){ $vmr.vban.enable = $value
It "Should set vban.instream[$index].on" { $vmr.command.restart
$vmr.vban.instream[$index].on = $value Start-Sleep -Milliseconds 2000
$vmr.vban.instream[$index].on | Should -Be $expected $vmr.vban.enable | Should -Be $expected
}
Context 'Instream' -ForEach @(
@{ Index = $vban_inA }
# @{ Index = $vban_inM }
# @{ Index = $vban_inT }
) {
It "Should set vban.instream[$index].on" {
$vmr.vban.instream[$index].on = $value
$vmr.vban.instream[$index].on | Should -Be $expected
}
} }
}
Context 'Vban outstream' -ForEach @( Context 'Outstream' -ForEach @(
@{ Index = $vban_out } @{ Index = $vban_outA }
){ # @{ Index = $vban_outM }
It "Should set vban.outstream[$index].on" { ) {
$vmr.vban.outstream[$index].on = $value It "Should set vban.outstream[$index].on" {
$vmr.vban.outstream[$index].on | Should -Be $expected $vmr.vban.outstream[$index].on = $value
$vmr.vban.outstream[$index].on | Should -Be $expected
}
} }
} }
Context 'Recorder' -Skip:$ifBasic { Context 'Recorder' -Skip:$ifBasic {
It "Should set and get Recorder.A3" { It 'Should set and get Recorder.A3' {
$vmr.recorder.A3 = $value $vmr.recorder.A3 = $value
$vmr.recorder.A3 | Should -Be $expected $vmr.recorder.A3 | Should -Be $expected
} }
It "Should set and get Recorder.B1" { It 'Should set and get Recorder.B1' {
$vmr.recorder.B1 = $value $vmr.recorder.B1 = $value
$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.loop' {
$vmr.recorder.loop = $value $vmr.recorder.loop = $value
} }
} }
@@ -99,142 +167,682 @@ Describe -Tag 'higher', -TestName 'All Higher Tests' {
$vmr.command.lock = $value $vmr.command.lock = $value
} }
} }
Context 'Fx' -Skip:$ifNotPotato {
Context 'Delay' {
It 'Should set and get Fx.delay.on' {
$vmr.fx.delay.on = $value
$vmr.fx.delay.on | Should -Be $expected
}
It 'Should set and get Fx.delay.ab' {
$vmr.fx.delay.ab = $value
$vmr.fx.delay.ab | Should -Be $expected
}
}
Context 'Reverb' {
It 'Should set and get Fx.reverb.on' {
$vmr.fx.reverb.on = $value
$vmr.fx.reverb.on | Should -Be $expected
}
It 'Should set and get Fx.reverb.ab' {
$vmr.fx.reverb.ab = $value
$vmr.fx.reverb.ab | Should -Be $expected
}
}
}
Context 'Patch' {
It 'Should set and get Patch.insert[$insert]' -Skip:$ifBasic {
$vmr.patch.insert[$insert].set($value)
$vmr.patch.insert[$insert].get() | Should -Be $value
}
It 'Should set and get Patch.postfadercomposite' -Skip:$ifBasic {
$vmr.patch.postfadercomposite = $value
$vmr.patch.postfadercomposite | Should -Be $value
}
It 'Should set and get Patch.postfxinsert' -Skip:$ifBasic {
$vmr.patch.postfxinsert = $value
$vmr.patch.postfxinsert | Should -Be $value
}
}
Context 'Option' {
It 'Should set and get Option.monitoronsel' -Skip:$ifNotPotato {
$vmr.option.monitoronsel = $value
$vmr.command.restart
Start-Sleep -Milliseconds 2000
$vmr.option.monitoronsel | Should -Be $value
}
It 'Should set and get Option.slidermode' -Skip:$ifNotPotato {
$vmr.option.slidermode = $value
$vmr.command.restart
Start-Sleep -Milliseconds 2000
$vmr.option.slidermode | Should -Be $value
}
}
} }
Describe 'Float Tests' { Describe 'Float Tests' -Tag 'float' {
Describe 'Strip tests' { Context 'Strip, one physical one virtual' -ForEach @(
Context 'one physical, one virtual' -ForEach @( @{ Index = $phys_in }, @{ Index = $virt_in }
@{ Index = $phys_in }, @{ Index = $virt_in } ) {
){ It "Should set Strip[$index].Gain to $value" -ForEach @(
Context 'gain' -ForEach @( @{ Value = 3.6; Expected = 3.6 }, @{ Value = -8.2; Expected = -8.2 }
@{ Value = 3.6; Expected = 3.6 }, @{ Value = -8.2; Expected = -8.2 } ) {
){ $vmr.strip[$index].gain = $value
It "Should set Strip[$index].Gain to $value" { $vmr.strip[$index].gain | Should -Be $expected
$vmr.strip[$index].gain = $value }
$vmr.strip[$index].gain | Should -Be $expected }
}
Context 'Strip, physical only' -Skip:$ifBasic -ForEach @(
@{ Index = $phys_in }
) {
Context 'Knobs' -Skip:$ifBasic -ForEach @(
@{ Value = 8.3; Expected = 8.3 }, @{ Value = 5.1; Expected = 5.1 }
) {
It "Should set Strip[$index].Comp to $value" {
$vmr.strip[$index].comp.knob = $value
$vmr.strip[$index].comp.knob | Should -Be $expected
}
It "Should set Strip[$index].Gate to $value" {
$vmr.strip[$index].gate.knob = $value
$vmr.strip[$index].gate.knob | Should -Be $expected
}
It "Should set Strip[$index].Denoiser to $value" -Skip:$ifNotPotato {
$vmr.strip[$index].denoiser.knob = $value
$vmr.strip[$index].denoiser.knob | Should -Be $expected
} }
} }
Context 'physical only' -Skip:$ifBasic -ForEach @( Context 'Comp' -Skip:$ifNotPotato {
@{ Index = $phys_in } It "Should set Strip[$index].Comp.Attack" -ForEach @(
){
Context 'comp, gate' -ForEach @(
@{ Value = 8.3; Expected = 8.3 }, @{ Value = 5.1; Expected = 5.1 } @{ Value = 8.3; Expected = 8.3 }, @{ Value = 5.1; Expected = 5.1 }
){ ) {
It "Should set Strip[$index].Comp to $value" { $vmr.strip[$index].comp.attack = $value
$vmr.strip[$index].comp = $value $vmr.strip[$index].comp.attack | Should -Be $expected
$vmr.strip[$index].comp | Should -Be $expected
}
It "Should set Strip[$index].Gate to $value" {
$vmr.strip[$index].gate = $value
$vmr.strip[$index].gate | Should -Be $expected
}
} }
}
It "Should set Strip[$index].Comp.Knee" -ForEach @(
@{ Value = 0.3; Expected = 0.3 }, @{ Value = 0.8; Expected = 0.8 }
) {
$vmr.strip[$index].comp.knee = $value
$vmr.strip[$index].comp.knee | Should -Be $expected
}
}
Context 'Gate' -Skip:$ifNotPotato {
It "Should set Strip[$index].Gate.BPSidechain" -ForEach @(
@{ Value = 103.1; Expected = 103.1 }, @{ Value = 3800; Expected = 3800 }
) {
$vmr.strip[$index].gate.bpsidechain = $value
$vmr.strip[$index].gate.bpsidechain | Should -Be $expected
}
It "Should set Strip[$index].Gate.Hold" -ForEach @(
@{ Value = 0.3; Expected = 0.3 }, @{ Value = 5000; Expected = 5000 }
) {
$vmr.strip[$index].gate.hold = $value
$vmr.strip[$index].gate.hold | Should -Be $expected
}
}
Context 'EQ' -Skip:$ifNotPotato -ForEach @(
@{ Eq = $vmr.strip[$index].eq }
) {
It "Should set Strip[$index].EQ.Channel[$strip_ch].Cell[$cells].F" -ForEach @(
@{ Value = 1234.6; Expected = 1234.6 }, @{ Value = 7500; Expected = 7500 }
) {
$eq.channel[$strip_ch].cell[$cells].f = $value
$eq.channel[$strip_ch].cell[$cells].f | Should -Be $expected
}
It "Should set Strip[$index].EQ.Channel[$strip_ch].Cell[$cells].Gain" -ForEach @(
@{ Value = 4.2; Expected = 4.2 }, @{ Value = -7.3; Expected = -7.3 }
) {
$eq.channel[$strip_ch].cell[$cells].gain = $value
$eq.channel[$strip_ch].cell[$cells].gain | Should -Be $expected
}
It "Should set Strip[$index].EQ.Channel[$strip_ch].Cell[$cells].Q" -ForEach @(
@{ Value = 1.2; Expected = 1.2 }, @{ Value = 5.6; Expected = 5.6 }
) {
$eq.channel[$strip_ch].cell[$cells].q = $value
$eq.channel[$strip_ch].cell[$cells].q | Should -Be $expected
}
}
} }
Describe 'Bus tests' { Context 'Bus, one physical one virtual' -ForEach @(
Context 'one physical, one virtual' -ForEach @( @{ Index = $phys_out }, @{ Index = $virt_out }
@{ Index = $phys_out }, @{ Index = $virt_out } ) {
){ It "Should set Bus[$index].Gain" -ForEach @(
Context 'gain' -ForEach @( @{ Value = 5.2; Expected = 5.2 }, @{ Value = -38.2; Expected = -38.2 }
@{ Value = 5.2; Expected = 5.2 }, @{ Value = -38.2; Expected = -38.2 } ) {
){ $vmr.bus[$index].gain = $value
It "Should set Bus[$index].Gain to $value" { $vmr.bus[$index].gain | Should -Be $expected
$vmr.bus[$index].gain = $value }
$vmr.bus[$index].gain | Should -Be $expected
} Context 'EQ' -Skip:$ifBasic -ForEach @(
@{ Eq = $vmr.bus[$index].eq }
) {
It "Should set Bus[$index].EQ.Channel[$bus_ch].Cell[$cells].F" -ForEach @(
@{ Value = 1234.6; Expected = 1234.6 }, @{ Value = 7500; Expected = 7500 }
) {
$eq.channel[$bus_ch].cell[$cells].f = $value
$eq.channel[$bus_ch].cell[$cells].f | Should -Be $expected
} }
It "Should set Bus[$index].EQ.Channel[$bus_ch].Cell[$cells].Gain" -ForEach @(
@{ Value = 4.2; Expected = 4.2 }, @{ Value = -7.3; Expected = -7.3 }
) {
$eq.channel[$bus_ch].cell[$cells].gain = $value
$eq.channel[$bus_ch].cell[$cells].gain | Should -Be $expected
}
It "Should set Bus[$index].EQ.Channel[$bus_ch].Cell[$cells].Q" -ForEach @(
@{ Value = 1.2; Expected = 1.2 }, @{ Value = 5.6; Expected = 5.6 }
) {
$eq.channel[$bus_ch].cell[$cells].q = $value
$eq.channel[$bus_ch].cell[$cells].q | Should -Be $expected
}
}
}
Context 'Option' {
It "Should set and get Option.delay[$phys_out]" -ForEach @(
@{ Value = 486.57 }, @{ Value = 26.41 }
) {
$vmr.option.delay[$phys_out].set($value)
$vmr.command.restart
Start-Sleep -Milliseconds 2000
$vmr.option.delay[$phys_out].get() | Should -Be $value
} }
} }
} }
Describe 'Int Tests' -ForEach @( Describe 'Int Tests' -Tag 'int' {
@{ Index = $phys_in }, @{ Index = $virt_in } Context 'Strip, one physical, one virtual' -ForEach @(
){ @{ Index = $phys_in }, @{ Index = $virt_in }
Context 'Strip, one physical, one virtual' -Skip:$ifBasic -ForEach @( ) {
@{ Value = 3; Expected = 3 } It "Should set and get Strip[$index].Limit" -Skip:$ifBasic -ForEach @(
@{ Value = -6; Expected = -6 } @{ Value = 3; Expected = 3 }
){ @{ Value = -6; Expected = -6 }
It "Should set Strip[$index].Limit to 3" { ) {
$vmr.strip[$index].limit = $value $vmr.strip[$index].limit = $value
$vmr.strip[$index].limit | Should -Be $expected $vmr.strip[$index].limit | Should -Be $expected
} }
} }
Context 'Vban outstream' { Context 'Strip, physical only' -ForEach @(
Context 'sr' -ForEach @( @{ Index = $phys_in }
@{ Value = 44100; Expected = 44100 } ) {
@{ Value = 48000; Expected = 48000 } Context 'Device' {
){ It "Should get Strip[$index].Device.sr" {
It "Should set vban.outstream[$index].sr to $value" { $vmr.strip[$index].device.sr | Should -BeOfType [int]
$vmr.vban.outstream[$index].sr = $value
$vmr.vban.outstream[$index].sr | Should -Be $expected
} }
} }
Context 'channel' -ForEach @( Context 'Eq' -Skip:$ifNotPotato -ForEach @(
@{ Eq = $vmr.strip[$index].eq }
) {
It "Should set Strip[$index].EQ.Channel[$strip_ch].Cell[$cells].Type" -ForEach @(
@{ Value = 0; Expected = 0 }, @{ Value = 6; Expected = 6 }
) {
$eq.channel[$strip_ch].cell[$cells].type = $value
$eq.channel[$strip_ch].cell[$cells].type | Should -Be $expected
}
}
}
Context 'Bus, one physical one virtual' -ForEach @(
@{ Index = $phys_out }, @{ Index = $virt_out }
) {
It "Should set and get Bus[$index].Mono" -ForEach @(
@{ Value = 0; Expected = 0 }
@{ Value = 1; Expected = 1 } @{ Value = 1; Expected = 1 }
@{ Value = 2; Expected = 2 } @{ Value = 2; Expected = 2 }
){ ) {
It 'Should set vban.outstream[0].channel to 1' { $vmr.bus[$index].mono = $value
$vmr.bus[$index].mono | Should -Be $expected
}
Context 'Eq' -Skip:$ifBasic -ForEach @(
@{ Eq = $vmr.bus[$index].eq }
) {
It "Should set Bus[$index].EQ.Channel[$bus_ch].Cell[$cells].Type" -ForEach @(
@{ Value = 0; Expected = 0 }, @{ Value = 6; Expected = 6 }
) {
$eq.channel[$bus_ch].cell[$cells].type = $value
$eq.channel[$bus_ch].cell[$cells].type | Should -Be $expected
}
}
}
Context 'Bus, physical only' -ForEach @(
@{ Index = $phys_out }
) {
Context 'Device' {
It "Should get Bus[$index].Device.sr" {
$vmr.bus[$index].device.sr | Should -BeOfType [int]
}
}
}
Context 'Bus, virtual only' -ForEach @(
@{ Index = $virt_out }
) {
Context 'Device' -Skip:$ifNotBasic {
It "Should get Bus[$index].Device.sr" {
$vmr.bus[$index].device.sr | Should -BeOfType [int]
}
}
}
Context 'Vban' {
It 'Should set vban.port' -ForEach @(
@{ Value = 1024; Expected = 1024 }
@{ Value = 65535; Expected = 65535 }
) {
$vmr.vban.port = $value
$vmr.command.restart
Start-Sleep -Milliseconds 2000
$vmr.vban.port | Should -Be $expected
}
Context 'Instream' -ForEach @(
@{ Index = $vban_inA }
) {
It "Should set vban.instream[$index].port" -ForEach @(
@{ Value = 1024; Expected = 1024 }
@{ Value = 65535; Expected = 65535 }
) {
$vmr.vban.instream[$index].port = $value
$vmr.command.restart
Start-Sleep -Milliseconds 2000
$vmr.vban.instream[$index].port | Should -Be $expected
}
It "Should set vban.instream[$index].sr" {
$vmr.vban.instream[$index].sr | Should -BeOfType [int]
}
It "Should set vban.instream[$index].channel" {
$vmr.vban.instream[$index].channel | Should -BeOfType [int]
}
It "Should set vban.instream[$index].bit" {
$vmr.vban.instream[$index].bit | Should -BeOfType [int]
}
It "Should set vban.instream[$index].quality" -ForEach @(
@{ Value = 0; Expected = 0 }
@{ Value = 4; Expected = 4 }
) {
$vmr.vban.instream[$index].quality = $value
Start-Sleep -Milliseconds 500
$vmr.vban.instream[$index].quality | Should -Be $expected
}
It "Should set vban.instream[$index].route" -ForEach @(
@{ Value = $phys_in; Expected = $phys_in }
@{ Value = $virt_in; Expected = $virt_in }
) {
$vmr.vban.instream[$index].route = $value
$vmr.vban.instream[$index].route | Should -Be $expected
}
}
Context 'Outstream' -ForEach @(
@{ Index = $vban_outA }
) {
It "Should set vban.outstream[$index].port" -ForEach @(
@{ Value = 1024; Expected = 1024 }
@{ Value = 65535; Expected = 65535 }
) {
$vmr.vban.outstream[$index].port = $value
$vmr.command.restart
Start-Sleep -Milliseconds 2000
$vmr.vban.outstream[$index].port | Should -Be $expected
}
It "Should set vban.outstream[$index].sr" -ForEach @(
@{ Value = 44100; Expected = 44100 }
@{ Value = 48000; Expected = 48000 }
) {
$vmr.vban.outstream[$index].sr = $value
$vmr.vban.outstream[$index].sr | Should -Be $expected
}
It "Should set vban.outstream[$index].channel" -ForEach @(
@{ Value = 1; Expected = 1 }
@{ Value = 2; Expected = 2 }
) {
$vmr.vban.outstream[$index].channel = $value $vmr.vban.outstream[$index].channel = $value
$vmr.vban.outstream[$index].channel | Should -Be $expected $vmr.vban.outstream[$index].channel | Should -Be $expected
} }
}
It "Should set vban.outstream[$index].bit" -ForEach @(
@{ Value = 16; Expected = 16 }
@{ Value = 24; Expected = 24 }
) {
$vmr.vban.outstream[$index].bit = $value
$vmr.vban.outstream[$index].bit | Should -Be $expected
}
It "Should set vban.outstream[$index].quality" -ForEach @(
@{ Value = 0; Expected = 0 }
@{ Value = 4; Expected = 4 }
) {
$vmr.vban.outstream[$index].quality = $value
Start-Sleep -Milliseconds 500
$vmr.vban.outstream[$index].quality | Should -Be $expected
}
It "Should set vban.outstream[$index].route" -ForEach @(
@{ Value = $phys_out; Expected = $phys_out }
@{ Value = $virt_out; Expected = $virt_out }
) {
$vmr.vban.outstream[$index].route = $value
$vmr.vban.outstream[$index].route | Should -Be $expected
}
}
}
Context 'Patch' {
It 'Should set and get Patch.composite[$composite]' -Skip:$ifBasic -ForEach @(
@{ Value = 22 }, @{ Value = 6 }
) {
$vmr.patch.composite[$composite].set($value)
$vmr.patch.composite[$composite].get() | Should -Be $value
}
}
Context 'Option' {
It 'Should set and get Option.sr' -ForEach @(
@{ Value = 44100 }, @{ Value = 48000 }
) {
$vmr.option.sr = $value
Start-Sleep -Milliseconds 500
$vmr.option.sr | Should -Be $value
}
It 'Should set and get Option.MonitoringBus' -Skip:$ifNotPotato -ForEach @(
@{ Value = $phys_out }, @{ Value = $virt_out }
) {
$vmr.option.monitoringbus = $value
$vmr.option.monitoringbus | Should -Be $value
}
Context 'Option.buffer' -ForEach @(
@{ Value = 1024 }, @{ Value = 512 }
) {
It 'Should set and get mme buffer' {
$vmr.option.buffer.mme = $value
Start-Sleep -Milliseconds 500
$vmr.option.buffer.mme | Should -Be $value
}
It 'Should set and get wdm buffer' {
$vmr.option.buffer.wdm = $value
Start-Sleep -Milliseconds 500
$vmr.option.buffer.wdm | Should -Be $value
}
It 'Should set and get ks buffer' {
$vmr.option.buffer.ks = $value
Start-Sleep -Milliseconds 500
$vmr.option.buffer.ks | Should -Be $value
}
}
} }
} }
Describe 'String Tests' { Describe 'String Tests' -Tag 'string' {
Context 'Strip, one physical, one virtual' -ForEach @( Context 'Strip, one physical, one virtual' -ForEach @(
@{ Index = $phys_in }, @{ Index = $virt_in } @{ Index = $phys_in }, @{ Index = $virt_in }
){ ) {
It "Should set Strip[$index].Label" -ForEach @( It "Should set Strip[$index].Label" -ForEach @(
@{ Value = "test0"; Expected = "test0" } @{ Value = 'test0'; Expected = 'test0' }
@{ Value = "test1"; Expected = "test1" } @{ Value = 'test1'; Expected = 'test1' }
){ ) {
$vmr.strip[$index].label = $value $vmr.strip[$index].label = $value
$vmr.strip[$index].label | Should -Be $expected $vmr.strip[$index].label | Should -Be $expected
} }
} }
Context 'Bus, one physical, one virtual' -ForEach @( Context 'Strip, physical only' -ForEach @(
@{ Index = $phys_out }, @{ Index = $virt_out } @{ Index = $phys_in }
){ ) {
It "Should set Bus[$index].Label" -ForEach @( Context 'Device' -ForEach @(
@{ Value = "test0"; Expected = "test0" } @{ Value = 'testInput' }, @{ Value = '' }
@{ Value = "test1"; Expected = "test1" } ) {
){ It "Should set Strip[$index].Device.wdm" {
$vmr.bus[$index].label = $value $vmr.strip[$index].device.wdm = $value
$vmr.bus[$index].label | Should -Be $expected Start-Sleep -Milliseconds 800
$vmr.strip[$index].device.name | Should -Be $value
}
It "Should set Strip[$index].Device.ks" {
$vmr.strip[$index].device.ks = $value
Start-Sleep -Milliseconds 800
$vmr.strip[$index].device.name | Should -Be $value
}
It "Should set Strip[$index].Device.mme" {
$vmr.strip[$index].device.mme = $value
Start-Sleep -Milliseconds 800
$vmr.strip[$index].device.name | Should -Be $value
}
}
Context 'EQ' -Skip:$ifNotPotato -ForEach @(
@{ Eq = $vmr.strip[$index].eq }
) {
It "Should save then load Strip[$index].EQ" -ForEach @(
@{ Fq = 1234.5; Gain = 4.2; Ql = 56.2; Type = 3 }
) {
$tmp = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "vmreq-$(New-Guid).xml")
try {
# set some values
$eq.channel[$strip_ch].cell[$cells].f = $fq
$eq.channel[$strip_ch].cell[$cells].gain = $gain
$eq.channel[$strip_ch].cell[$cells].q = $ql
$eq.channel[$strip_ch].cell[$cells].type = $type
# save eq
$eq.Save($tmp)
Start-Sleep -Milliseconds 100
Test-Path $tmp | Should -BeTrue
# change values
$eq.channel[$strip_ch].cell[$cells].f = 1000
$eq.channel[$strip_ch].cell[$cells].gain = 0
$eq.channel[$strip_ch].cell[$cells].q = 1
$eq.channel[$strip_ch].cell[$cells].type = 0
# load eq
$eq.Load($tmp)
Start-Sleep -Milliseconds 100
# verify values
$eq.channel[$strip_ch].cell[$cells].f | Should -Be $fq
$eq.channel[$strip_ch].cell[$cells].gain | Should -Be $gain
$eq.channel[$strip_ch].cell[$cells].q | Should -Be $ql
$eq.channel[$strip_ch].cell[$cells].type | Should -Be $type
}
finally {
if (Test-Path $tmp) {
Remove-Item $tmp -Force
}
}
}
} }
} }
Describe 'Vban' -ForEach @( Context 'Bus, one physical, one virtual' -ForEach @(
@{ Index = $vban_in } @{ Index = $phys_out }, @{ Index = $virt_out }
){ ) {
Context 'instream' { It "Should set Bus[$index].Label" -ForEach @(
Context 'ip' -ForEach @( @{ Value = 'test0'; Expected = 'test0' }
@{ Value = "0.0.0.0"; Expected = "0.0.0.0" } @{ Value = 'test1'; Expected = 'test1' }
){ ) {
It "Should set vban.instream[$index].name to $value" { $vmr.bus[$index].label = $value
$vmr.vban.instream[$index].ip = $value $vmr.bus[$index].label | Should -Be $expected
$vmr.vban.instream[$index].ip | Should -Be $expected
}
}
} }
Context 'outstream' { It "Should set Bus[$index].Mode" -Skip:$ifBasic -ForEach @(
Context 'ip' -ForEach @( @{ Value = 'bmix'; Expected = 'bmix' }
@{ Value = "0.0.0.0"; Expected = "0.0.0.0" } @{ Value = 'upmix41'; Expected = 'upmix41' }
){ @{ Value = 'rearonly'; Expected = 'rearonly' }
It "Should set vban.outstream[$index].name to $value" { ) {
$vmr.vban.outstream[$index].ip = $value $vmr.bus[$index].mode.Set($value)
$vmr.vban.outstream[$index].ip | Should -Be $expected $vmr.bus[$index].mode.Get() | Should -Be $expected
}
Context 'EQ' -Skip:$ifBasic -ForEach @(
@{ Eq = $vmr.bus[$index].eq }
) {
It "Should save then load Bus[$index].EQ" -ForEach @(
@{ Fq = 1234.5; Gain = 4.2; Ql = 56.2; Type = 3 }
) {
$tmp = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "vmreq-$(New-Guid).xml")
try {
# set some values
$eq.channel[$bus_ch].cell[$cells].f = $fq
$eq.channel[$bus_ch].cell[$cells].gain = $gain
$eq.channel[$bus_ch].cell[$cells].q = $ql
$eq.channel[$bus_ch].cell[$cells].type = $type
# save eq
$eq.Save($tmp)
Start-Sleep -Milliseconds 100
Test-Path $tmp | Should -BeTrue
# change values
$eq.channel[$bus_ch].cell[$cells].f = 1000
$eq.channel[$bus_ch].cell[$cells].gain = 0
$eq.channel[$bus_ch].cell[$cells].q = 1
$eq.channel[$bus_ch].cell[$cells].type = 0
# load eq
$eq.Load($tmp)
Start-Sleep -Milliseconds 100
# verify values
$eq.channel[$bus_ch].cell[$cells].f | Should -Be $fq
$eq.channel[$bus_ch].cell[$cells].gain | Should -Be $gain
$eq.channel[$bus_ch].cell[$cells].q | Should -Be $ql
$eq.channel[$bus_ch].cell[$cells].type | Should -Be $type
} }
} finally {
if (Test-Path $tmp) {
Remove-Item $tmp -Force
}
}
}
}
}
Context 'Bus, physical only' -ForEach @(
@{ Index = $phys_out }
) {
Context 'Device' -ForEach @(
@{ Value = 'testOutput' }, @{ Value = '' }
) {
It "Should set Bus[$index].Device.wdm" {
$vmr.bus[$index].device.wdm = $value
Start-Sleep -Milliseconds 800
$vmr.bus[$index].device.name | Should -Be $value
}
It "Should set Bus[$index].Device.ks" {
$vmr.bus[$index].device.ks = $value
Start-Sleep -Milliseconds 800
$vmr.bus[$index].device.name | Should -Be $value
}
It "Should set Bus[$index].Device.mme" {
$vmr.bus[$index].device.mme = $value
Start-Sleep -Milliseconds 800
$vmr.bus[$index].device.name | Should -Be $value
}
}
}
Context 'Bus, virtual only' -ForEach @(
@{ Index = $virt_out }
) {
Context 'Device' -Skip:$ifNotBasic -ForEach @(
@{ Value = 'testOutput' }, @{ Value = '' }
) {
It "Should set Bus[$index].Device.wdm" {
$vmr.bus[$index].device.wdm = $value
Start-Sleep -Milliseconds 800
$vmr.bus[$index].device.name | Should -Be $value
}
It "Should set Bus[$index].Device.ks" {
$vmr.bus[$index].device.ks = $value
Start-Sleep -Milliseconds 800
$vmr.bus[$index].device.name | Should -Be $value
}
It "Should set Bus[$index].Device.mme" {
$vmr.bus[$index].device.mme = $value
Start-Sleep -Milliseconds 800
$vmr.bus[$index].device.name | Should -Be $value
}
}
}
Describe 'Vban' {
Context 'Instream' -ForEach @(
@{ Index = $vban_inA }
# @{ Index = $vban_inM }
# @{ Index = $vban_inT }
) {
It "Should set vban.instream[$index].name" -ForEach @(
@{ Value = 'TestIn0'; Expected = 'TestIn0' }
@{ Value = 'TestIn1'; Expected = 'TestIn1' }
) {
$vmr.vban.instream[$index].name = $value
$vmr.vban.instream[$index].name | Should -Be $expected
}
It "Should set vban.instream[$index].ip" -ForEach @(
@{ Value = '0.0.0.0'; Expected = '0.0.0.0' }
) {
$vmr.vban.instream[$index].ip = $value
$vmr.vban.instream[$index].ip | Should -Be $expected
}
}
Context 'Outstream' -ForEach @(
@{ Index = $vban_outA }
# @{ Index = $vban_outM }
) {
It "Should set vban.outstream[$index].name" -ForEach @(
@{ Value = 'TestOut0'; Expected = 'TestOut0' }
@{ Value = 'TestOut1'; Expected = 'TestOut1' }
) {
$vmr.vban.outstream[$index].name = $value
$vmr.vban.outstream[$index].name | Should -Be $expected
}
It "Should set vban.outstream[$index].ip" -ForEach @(
@{ Value = '0.0.0.0'; Expected = '0.0.0.0' }
) {
$vmr.vban.outstream[$index].ip = $value
$vmr.vban.outstream[$index].ip | Should -Be $expected
}
} }
} }
} }

View File

@@ -6,13 +6,13 @@ Describe -Tag 'lower', -TestName 'All Lower Tests' {
Describe 'Macrobutton Tests' -ForEach @( Describe 'Macrobutton Tests' -ForEach @(
@{ Value = 1; Expected = 1 } @{ Value = 1; Expected = 1 }
@{ Value = 0; Expected = 0 } @{ Value = 0; Expected = 0 }
){ ) {
Context 'buttons 0, 69' -ForEach @( Context 'buttons 0, 69' -ForEach @(
@{ Index = 0 }, @{ Index = 69 } @{ Index = 0 }, @{ Index = 69 }
){ ) {
Context 'state, stateonly and trigger' -ForEach @( Context 'state, stateonly and trigger' -ForEach @(
@{ Mode = 1 }, @{ Mode = 2 }, @{ Mode = 3 } @{ Mode = 1 }, @{ Mode = 2 }, @{ Mode = 3 }
){ ) {
It "Should set and get macrobutton[$index] State" { It "Should set and get macrobutton[$index] State" {
MB_Set -ID $index -SET $value -MODE $mode MB_Set -ID $index -SET $value -MODE $mode
MB_Get -ID $index -MODE $mode | Should -Be $expected MB_Get -ID $index -MODE $mode | Should -Be $expected
@@ -24,13 +24,13 @@ Describe -Tag 'lower', -TestName 'All Lower Tests' {
Describe 'Set and Get Param Float Tests' -ForEach @( Describe 'Set and Get Param Float Tests' -ForEach @(
@{ Value = 1; Expected = 1 } @{ Value = 1; Expected = 1 }
@{ Value = 0; Expected = 0 } @{ Value = 0; Expected = 0 }
){ ) {
Context 'Strip, one physical one virtual' -ForEach @( Context 'Strip, one physical one virtual' -ForEach @(
@{ Index = $phys_in }, @{ Index = $virt_in } @{ Index = $phys_in }, @{ Index = $virt_in }
){ ) {
Context 'mute, mono, A1, B2' -ForEach @( Context 'mute, mono, A1, B2' -ForEach @(
@{ param = "mute" }, @{ param = "A1" } @{ param = 'mute' }, @{ param = 'A1' }
){ ) {
It "Should set Strip[0].$param to 1" { It "Should set Strip[0].$param to 1" {
Param_Set -PARAM "Strip[$index].$param" -VALUE $value Param_Set -PARAM "Strip[$index].$param" -VALUE $value
Param_Get -PARAM "Strip[$index].$param" | Should -Be $expected Param_Get -PARAM "Strip[$index].$param" | Should -Be $expected
@@ -42,10 +42,10 @@ Describe -Tag 'lower', -TestName 'All Lower Tests' {
Describe 'Set and Get Param String Tests' -ForEach @( Describe 'Set and Get Param String Tests' -ForEach @(
@{ Value = 'test0'; Expected = 'test0' } @{ Value = 'test0'; Expected = 'test0' }
@{ Value = 'test1'; Expected = 'test1' } @{ Value = 'test1'; Expected = 'test1' }
){ ) {
Context 'Strip, one physical one virtual' -ForEach @( Context 'Strip, one physical one virtual' -ForEach @(
@{ Index = $phys_in }, @{ Index = $virt_in } @{ Index = $phys_in }, @{ Index = $virt_in }
){ ) {
It "Should set Strip[$index].Label to $value" { It "Should set Strip[$index].Label to $value" {
Param_Set -PARAM "Strip[$index].Label" -VALUE $value Param_Set -PARAM "Strip[$index].Label" -VALUE $value
Param_Get -PARAM "Strip[$index].Label" -IS_STRING $true | Should -Be $expected Param_Get -PARAM "Strip[$index].Label" -IS_STRING $true | Should -Be $expected

View File

@@ -1,84 +0,0 @@
Param([String]$tag, [Int]$num=1, [switch]$log, [string]$kind="potato")
Import-Module .\lib\Voicemeeter.psm1
Function ParseLog {
Param([String]$logfile)
$summary_file = Join-Path $PSScriptRoot "_summary.log"
if (Test-Path $summary_file) { Clear-Content $summary_file }
$PASSED_PATTERN = "^PassedCount\s+:\s(\d+)"
$FAILED_PATTERN = "^FailedCount\s+:\s(\d+)"
$DATA = @{
"passed" = 0
"failed" = 0
}
ForEach ($line in `
$(Get-content -Path "${logfile}")) {
if ($line -match $PASSED_PATTERN) {
$DATA["passed"] += $Matches[1]
}
elseif ($line -match $FAILED_PATTERN) {
$DATA["failed"] += $Matches[1]
}
}
"=========================`n" + `
"$num tests run:`n" + `
"=========================" | Tee-Object -FilePath $summary_file -Append
$DATA | ForEach-Object { $_ } | Tee-Object -FilePath $summary_file -Append
}
function main() {
try
{
switch ($kind) {
"basic" { $vmr = Get-RemoteBasic }
"banana" { $vmr = Get-RemoteBanana }
"potato" { $vmr = Get-RemotePotato }
}
Write-Host "Running tests for $vmr"
# test boundaries by kind
$phys_in = $vmr.kind.p_in - 1
$virt_in = $vmr.kind.p_in + $vmr.kind.v_in - 1
$phys_out = $vmr.kind.p_out - 1
$virt_out = $vmr.kind.p_out + $vmr.kind.v_out - 1
$vban_in = $vmr.kind.vban_in - 1
$vban_out = $vmr.kind.vban_out - 1
# skip conditions by kind
$ifBasic = $vmr.kind.name -eq "basic"
$ifBanana = $vmr.kind.name -eq "banana"
$ifPotato = $vmr.kind.name -eq "potato"
$ifNotBasic = $vmr.kind.name -ne "basic"
$ifNotBanana = $vmr.kind.name -ne "banana"
$ifNotPotato = $vmr.kind.name -ne "potato"
$logfile = Join-Path $PSScriptRoot "_results.log"
if (Test-Path $logfile) { Clear-Content $logfile }
1..$num | ForEach-Object {
if ($log) {
"Running test $_ of $num" | Tee-Object -FilePath $logfile -Append
Invoke-Pester -Tag $tag -PassThru | Tee-Object -FilePath $logfile -Append
}
else {
"Running test $_ of $num"
Invoke-Pester -Tag $tag -PassThru
}
}
if($log) { Parselog -logfile $logfile }
}
finally
{
$vmr.Logout()
}
}
if ($MyInvocation.InvocationName -ne '.') { main }

39
tests/run.ps1 Normal file
View File

@@ -0,0 +1,39 @@
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "", Target = "variablename")]
Param([String]$tag, [string]$kind = 'potato')
Import-Module (Join-Path (Split-Path $PSScriptRoot -Parent) 'lib\Voicemeeter.psm1') -Force
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
$phys_in = $vmr.kind.p_in - 1
$virt_in = $vmr.kind.p_in + $vmr.kind.v_in - 1
$phys_out = $vmr.kind.p_out - 1
$virt_out = $vmr.kind.p_out + $vmr.kind.v_out - 1
$vban_inA = $vmr.kind.vban.in - 1
$vban_inM = $vmr.kind.vban.in + $vmr.kind.vban.midi - 1
$vban_inT = $vmr.kind.vban.in + $vmr.kind.vban.midi + $vmr.kind.vban.text - 1
$vban_outA = $vmr.kind.vban.out - 1
$vban_outM = $vmr.kind.vban.out + $vmr.kind.vban.midi - 1
$insert = $vmr.kind.insert - 1
$composite = $vmr.kind.composite - 1
$strip_ch = $vmr.kind.eq_ch['strip'] - 1
$bus_ch = $vmr.kind.eq_ch['bus'] - 1
$cells = $vmr.kind.cells - 1
# skip conditions by kind
$ifBasic = $vmr.kind.name -eq 'basic'
$ifNotBasic = $vmr.kind.name -ne 'basic'
$ifNotPotato = $vmr.kind.name -ne 'potato'
Invoke-Pester -Tag $tag -PassThru | Out-Null
}
finally { Disconnect-Voicemeeter }
}
main