update obs vm sync example

This commit is contained in:
onyx-and-iris 2026-01-20 19:53:15 +00:00
parent d51ffacfaf
commit fc75bc2020
2 changed files with 293 additions and 68 deletions

View File

@ -0,0 +1,293 @@
<#
.SYNOPSIS
Synchronizes OBS Studio scene changes with Voicemeeter audio settings.
.DESCRIPTION
This script monitors OBS Studio for scene changes via WebSocket connection and
automatically adjusts Voicemeeter audio settings based on the active scene.
.PARAMETER ConfigPath
Path to the configuration file. Defaults to 'config.psd1' in the script directory.
.PARAMETER VoicemeeterKind
Type of Voicemeeter to connect to. Defaults to 'basic'.
.EXAMPLE
.\Vm-Obs-Sync.ps1
.EXAMPLE
.\Vm-Obs-Sync.ps1 -ConfigPath "C:\myconfig.psd1" -VoicemeeterKind "banana"
#>
[CmdletBinding()]
param(
[string]$ConfigPath = (Join-Path $PSScriptRoot 'config.psd1'),
[ValidateSet('basic', 'banana', 'potato')]
[string]$VoicemeeterKind = 'basic'
)
#Requires -Modules obs-powershell
# Import required modules
try {
Import-Module ..\..\lib\Voicemeeter.psm1
Import-Module obs-powershell
}
catch {
Write-Error "Failed to import required modules: $($_.Exception.Message)"
exit 1
}
# Script-level variables
$script:vmr = $null
$script:obsJob = $null
$script:shouldExit = $false
#region Helper Functions
function Write-Log {
<#
.SYNOPSIS
Writes timestamped log messages to the console.
#>
param(
[Parameter(Mandatory, ValueFromPipeline)]
[string]$Message,
[ValidateSet('Info', 'Warning', 'Error')]
[string]$Level = 'Info'
)
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$logMessage = "[$timestamp] [$Level] $Message"
switch ($Level) {
'Info' { Write-Information $logMessage -InformationAction Continue }
'Warning' { Write-Warning $logMessage }
'Error' { Write-Error $logMessage }
}
}
function Get-ConnectionConfig {
<#
.SYNOPSIS
Loads OBS connection configuration from file.
#>
param([string]$Path = $ConfigPath)
try {
if (-not (Test-Path $Path)) {
throw "Configuration file not found: $Path"
}
$config = Import-PowerShellDataFile -Path $Path -ErrorAction Stop
# Validate required properties
$requiredProperties = @('host', 'port', 'password')
foreach ($prop in $requiredProperties) {
if (-not $config.ContainsKey($prop)) {
throw "Missing required configuration property: $prop"
}
}
Write-Log "Configuration loaded successfully from: $Path"
return $config
}
catch {
Write-Log "Failed to load configuration: $($_.Exception.Message)" -Level Error
throw
}
}
function Initialize-Connections {
<#
.SYNOPSIS
Initializes connections to Voicemeeter and OBS.
#>
try {
$script:vmr = Connect-Voicemeeter -Kind $VoicemeeterKind -ErrorAction Stop
Write-Log 'Voicemeeter connection established'
$obsConfig = Get-ConnectionConfig
$webSocketUri = "ws://$($obsConfig.host):$($obsConfig.port)"
$script:obsJob = Watch-OBS -WebSocketURI $webSocketUri -WebSocketToken $obsConfig.password -ErrorAction Stop
Write-Log "OBS connection at $webSocketUri established"
}
catch {
Write-Log "Failed to initialize connections: $($_.Exception.Message)" -Level Error
throw
}
}
function Disconnect-All {
<#
.SYNOPSIS
Safely disconnects from all services.
#>
Write-Log 'Cleaning up connections...'
try {
if ($script:obsJob) {
Remove-Job -Job $script:obsJob -Force -ErrorAction SilentlyContinue
Disconnect-OBS -ErrorAction SilentlyContinue
}
}
catch {
Write-Log "Error disconnecting from OBS: $($_.Exception.Message)" -Level Warning
}
try {
if ($script:vmr) {
Disconnect-Voicemeeter -ErrorAction SilentlyContinue
}
}
catch {
Write-Log "Error disconnecting from Voicemeeter: $($_.Exception.Message)" -Level Warning
}
Write-Log 'Cleanup completed'
}
#endregion
#region Event Handlers
function Invoke-CurrentProgramSceneChanged {
<#
.SYNOPSIS
Handles OBS scene change events.
#>
param(
[Parameter(Mandatory)]
[System.Object]$EventData
)
if (-not $EventData.sceneName) {
Write-Log 'Scene change event received but no scene name provided' -Level Warning
return
}
Write-Log "Scene changed to: $($EventData.sceneName)"
try {
switch ($EventData.sceneName) {
'START' {
Write-Log 'Toggling mute for strip 0'
$script:vmr.strip[0].mute = !$script:vmr.strip[0].mute
}
'BRB' {
Write-Log 'Setting gain to -8.3dB for strip 0'
$script:vmr.strip[0].gain = -8.3
}
'END' {
Write-Log 'Enabling mono for strip 0'
$script:vmr.strip[0].mono = $true
}
'LIVE' {
Write-Log 'Setting color_x to 0.3 for strip 0'
$script:vmr.strip[0].color_x = 0.3
}
default {
Write-Log "Unknown scene '$($EventData.sceneName)'. Expected: START, BRB, END, or LIVE" -Level Warning
}
}
}
catch {
Write-Log "Error processing scene change: $($_.Exception.Message)" -Level Error
}
}
function Invoke-ExitStarted {
<#
.SYNOPSIS
Handles OBS exit events.
#>
param([System.Object]$EventData)
Write-Log 'OBS shutdown detected - initiating graceful exit'
$script:shouldExit = $true
}
function Invoke-EventDispatcher {
<#
.SYNOPSIS
Dispatches OBS events to appropriate handlers.
#>
param(
[Parameter(Mandatory)]
[System.Object]$EventData
)
if (-not $EventData.eventType) {
Write-Log 'Event received without eventType property' -Level Warning
return
}
$handlerName = "Invoke-$($EventData.eventType)"
if (Get-Command $handlerName -ErrorAction SilentlyContinue) {
try {
& $handlerName -EventData $EventData.eventData
}
catch {
Write-Log "Error in event handler '$handlerName': $($_.Exception.Message)" -Level Error
}
}
else {
Write-Log "No handler found for event type: $($EventData.eventType)" -Level Warning
}
}
#endregion
#region Main Execution
function Start-VoicemeeterObsSync {
<#
.SYNOPSIS
Main execution function for the sync process.
#>
Write-Log 'Starting Voicemeeter-OBS synchronization service'
try {
Initialize-Connections
Write-Log 'Monitoring OBS events... Press Ctrl+C to stop'
while (-not $script:shouldExit) {
try {
$obsEvents = Receive-Job -Job $script:obsJob -ErrorAction SilentlyContinue
foreach ($obsEvent in $obsEvents) {
if ($obsEvent.MessageData.op -eq 5) {
Invoke-EventDispatcher -EventData $obsEvent.MessageData.d
}
}
Start-Sleep -Milliseconds 100
}
catch {
Write-Log "Error processing OBS events: $($_.Exception.Message)" -Level Error
}
}
}
catch {
Write-Log "Fatal error: $($_.Exception.Message)" -Level Error
exit 1
}
finally {
Disconnect-All
}
Write-Log 'Voicemeeter-OBS synchronization service stopped'
}
# Handle Ctrl+C gracefully
$null = Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action {
$script:shouldExit = $true
}
Start-VoicemeeterObsSync
#endregion

View File

@ -1,68 +0,0 @@
[cmdletbinding()]
param()
Import-Module ..\..\lib\Voicemeeter.psm1
Import-Module obs-powershell
function CurrentProgramSceneChanged {
param([System.Object]$data)
Write-Host 'Switched to scene', $data.sceneName
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 ExitStarted {
param([System.Object]$data)
'OBS shutdown has begun!' | Write-Host
break
}
function eventHandler($data) {
if (Get-Command $data.eventType -ErrorAction SilentlyContinue) {
& $data.eventType -data $data.eventData
}
}
function ConnFromFile {
$configpath = Join-Path $PSScriptRoot 'config.psd1'
return Import-PowerShellDataFile -Path $configpath
}
function main {
$vmr = Connect-Voicemeeter -Kind 'basic'
$conn = ConnFromFile
$job = Watch-OBS -WebSocketURI "ws://$($conn.host):$($conn.port)" -WebSocketToken $conn.password
try {
while ($true) {
Receive-Job -Job $job | ForEach-Object {
$data = $_.MessageData
if ($data.op -eq 5) {
eventHandler($data.d)
}
}
}
}
finally {
Disconnect-OBS
Disconnect-Voicemeeter
}
}
main