From fc75bc20200477c9a368e5532ea7d6a7183034aa Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Tue, 20 Jan 2026 19:53:15 +0000 Subject: [PATCH] update obs vm sync example --- examples/obs/Sync-OBS-Voicemeeter.ps1 | 293 ++++++++++++++++++++++++++ examples/obs/Vm-Obs-Sync.ps1 | 68 ------ 2 files changed, 293 insertions(+), 68 deletions(-) create mode 100644 examples/obs/Sync-OBS-Voicemeeter.ps1 delete mode 100644 examples/obs/Vm-Obs-Sync.ps1 diff --git a/examples/obs/Sync-OBS-Voicemeeter.ps1 b/examples/obs/Sync-OBS-Voicemeeter.ps1 new file mode 100644 index 0000000..f12c4b7 --- /dev/null +++ b/examples/obs/Sync-OBS-Voicemeeter.ps1 @@ -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 \ No newline at end of file diff --git a/examples/obs/Vm-Obs-Sync.ps1 b/examples/obs/Vm-Obs-Sync.ps1 deleted file mode 100644 index 75f3911..0000000 --- a/examples/obs/Vm-Obs-Sync.ps1 +++ /dev/null @@ -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