Commands/Sources/Set-OBSAudioOutputSource.ps1

function Set-OBSAudioOutputSource {
    <#
    
    .SYNOPSIS
        Adds or sets an audio output source
    .DESCRIPTION
        Adds or sets an audio output source in OBS. This captures the audio that is being sent to an output device.
    .EXAMPLE
        Add-OBSAudioOutputSource
    .EXAMPLE
        Set-OBSAudioOutputSource -AudioDevice Speakers
    .NOTES
        This command currently only supports capturing default audio on Windows.
        To add support for other operating systems, file an issue or open a pull request.
    
    #>

            
    #>
    [Alias('Add-OBSAudioOutputSource')]
    param(
    # The name of the audio device.
    # This name or device ID of the audio device that should be captured.
    [Parameter(ValueFromPipelineByPropertyName)]
    [Alias('ItemValue','ItemName','DeviceID')]
    [string]
    $AudioDevice,
    # The name of the scene.
    # If no scene name is provided, the current program scene will be used.
    [Parameter(ValueFromPipelineByPropertyName)]
    [Alias('SceneName')]
    [string]
    $Scene,
    # The name of the input.
    # If no name is provided, "AudioOutput$($AudioDevice)" will be the input source name.
    [Parameter(ValueFromPipelineByPropertyName)]
    [Alias('InputName')]
    [string]
    $Name,
    # If set, will check if the source exists in the scene before creating it and removing any existing sources found.
    # If not set, you will get an error if a source with the same name exists.
    [Parameter(ValueFromPipelineByPropertyName)]
    [switch]
    $Force
    )
    dynamicParam {
    $baseCommand = 
        if (-not $script:AddOBSInput) {
            $script:AddOBSInput = 
                $executionContext.SessionState.InvokeCommand.GetCommand('Add-OBSInput','Function')
            $script:AddOBSInput
        } else {
            $script:AddOBSInput
        }
    $IncludeParameter = @()
    $ExcludeParameter = 'inputKind','sceneName','inputName'
    $DynamicParameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new()            
    :nextInputParameter foreach ($paramName in ([Management.Automation.CommandMetaData]$baseCommand).Parameters.Keys) {
        if ($ExcludeParameter) {
            foreach ($exclude in $ExcludeParameter) {
                if ($paramName -like $exclude) { continue nextInputParameter}
            }
        }
        if ($IncludeParameter) {
            $shouldInclude = 
                foreach ($include in $IncludeParameter) {
                    if ($paramName -like $include) { $true;break}
                }
            if (-not $shouldInclude) { continue nextInputParameter }
        }
        
        $DynamicParameters.Add($paramName, [Management.Automation.RuntimeDefinedParameter]::new(
            $baseCommand.Parameters[$paramName].Name,
            $baseCommand.Parameters[$paramName].ParameterType,
            $baseCommand.Parameters[$paramName].Attributes
        ))
    }
    $DynamicParameters
    }
        process {
        $myParameters = [Ordered]@{} + $PSBoundParameters
        
        if (-not $myParameters["Scene"]) {
            $myParameters["Scene"] = Get-OBSCurrentProgramScene
        }
        
        if (-not $myParameters["AudioDevice"]) {
            $myParameters["AudioDevice"] = "default"
        }
        # Window capture is a bit of a tricky one.
        # In order to get the WindowTitle to match that OBS needs, we need to look thru the input properties list.
        # and for that, an input needs to exist.
        if (-not $myParameters["Name"]) {
            
            if ($myParameters["AudioDevice"]) {
                $Name = $myParameters["Name"] = "AudioOutput-" + $myParameters["AudioDevice"]
            }
            else {
                $Name = $myParameters["Name"] = "AudioOutput"
            }
        }
        
                                                
        $myParameterData = [Ordered]@{}
        foreach ($parameter in $MyInvocation.MyCommand.Parameters.Values) {
            $bindToPropertyName = $null            
            
            foreach ($attribute in $parameter.Attributes) {
                if ($attribute -is [ComponentModel.DefaultBindingPropertyAttribute]) {
                    $bindToPropertyName = $attribute.Name
                    break
                }
            }
            if (-not $bindToPropertyName) { continue }
            if ($myParameters.Contains($parameter.Name)) {
                $myParameterData[$bindToPropertyName] = $myParameters[$parameter.Name]
                if ($myParameters[$parameter.Name] -is [switch]) {
                    $myParameterData[$bindToPropertyName] = $parameter.Name -as [bool]
                }
            }
        }        
        $addSplat = @{
            sceneName = $myParameters["Scene"]
            inputName = $myParameters["Name"]
            inputKind = "wasapi_output_capture"
            inputSettings = $myParameterData
            NoResponse = $myParameters["NoResponse"]
        }        
        # If -SceneItemEnabled was passed,
        if ($myParameters.Contains('SceneItemEnabled')) {
            # propagate it to Add-OBSInput.
            $addSplat.SceneItemEnabled = $myParameters['SceneItemEnabled'] -as [bool]
        }
        
        # Add the input.
        $outputAddedResult = Add-OBSInput @addSplat *>&1        
        $possibleDevices = Get-OBSInputPropertiesListPropertyItems -InputName $addSplat.inputName -PropertyName device_id
        foreach ($deviceInfo in $possibleDevices) {
            if (
                ($deviceInfo.itemName -eq $AudioDevice) -or
                ($deviceInfo.ItemValue -eq $AudioDevice) -or
                ($deviceInfo.ItemName -replace '\[[^\[\]]+\]\:\s' -eq $AudioDevice) -or
                ($deviceInfo.ItemValue -like "*$AudioDevice*") -or
                ($deviceInfo.ItemName -like "*$AudioDevice*")
            ) {
                $myParameterData["device_id"] = $deviceInfo.itemValue
                break
            }
        }
        # If -PassThru was passed
        if ($MyParameters["PassThru"]) {
            # pass it down to each command
            # Otherwise, remove SceneItemEnabled, InputKind, and SceneName
            $addSplat.PassThru = $true
            $addSplat.Remove('SceneItemEnabled')
            $addSplat.Remove('inputKind')
            $addSplat.Remove('sceneName')
            # and passthru Set-OBSInputSettings.
            Set-OBSInputSettings @addSplat
            
            return
        }
        if ($Force)  { # If we do, remove the input
            Remove-OBSInput -InputName $addSplat.inputName
            # and re-add our result.
            $outputAddedResult = Add-OBSInput @addSplat *>&1
            # If the output was still an error
            if ($outputAddedResult -is [Management.Automation.ErrorRecord]) {
                # use $psCmdlet.WriteError so that it shows the error correctly.
                $psCmdlet.WriteError($outputAddedResult)
            }
            # Otherwise, if we had a result
            elseif ($outputAddedResult) {
                # get the input from the scene.
                Get-OBSSceneItem -sceneName $myParameters["Scene"] |
                    Where-Object SourceName -eq $myParameters["Scene"]
            }
        } else {
            # Otherwise, get the input from the scene,
            $sceneItem = Get-OBSSceneItem -sceneName $myParameters["Scene"] |
                Where-Object SourceName -eq $myParameters["Name"]
            # update the input settings
            $sceneItem.Input.Settings = $addSplat.inputSettings
            $sceneItem # and return the scene item.
        }
    
    }
}