Scripting Audacity With Powershell and Named Pipes

This is my first post. I just started learning Powershell and using named pipes with Audacity about two weeks ago.

I was able to get Powershell on Windows 11 to work with Audacity 3.4.2 using named pipes. The script is not very useful but it shows how to set up the named pipes.

I tried to document prerequisites and the code to explain what each section of the script does. Just save the code below as PowershellAndAudacityNamedPipes.ps1 and follow the instructions. Hopefully all will go well.

Charlie

#Filename PowershellAndAudacityNamedPipes.ps1
#Charlie Kennedy

#Tested on Windows 11 (I made sure I had updated it recently), Audacity 3.4.2,
#Powershell Version 5.1.22621.2706 (just use a recent installation)

#Prior to running, you will need to add or remove windows components.

#Add optional features
#To download and install optional features through the Settings app, use these steps:

#Enter Optional Features in the search text box and press enter
#Scroll to the bottom of the page and select More Windows features
#Expand the .Net Framework features (there may be more than one).
#Expand the WCF Services and click on Named Pipe Activation.

#You will also need to enable mod-script-pipe in Audacity as follows
#Launch Audacity
#Open the Preferences menu
#Click on the Modules section on the sidebar
#Switch mod-script-pipe from New to Enabled
#Then you must restart Audacity. 

#To execute this script, start Audacity before starting PowerShell
#Then run powershell and enter & $path\TestPowershellAudacityNamedPipes.ps1 substituting your $path to this script

###############################################################################

#Filename PowershellAndAudacityNamedPipes.ps1
#Charlie Kennedy

#Tested on Windows 11 (I made sure I had updated it recently), Audacity 3.4.2,
#Powershell Version 5.1.22621.2706 (just use a recent installation)

#Prior to running, you will need to add or remove windows components.

#Add optional features
#To download and install optional features through the Settings app, use these steps:

#Enter Optional Features in the search text box and press enter
#Scroll to the bottom of the page and select More Windows features
#Expand the .Net Framework features (there may be more than one).
#Expand the WCF Services and click on Named Pipe Activation.

#You will also need to enable mod-script-pipe in Audacity as follows
#Launch Audacity
#Open the Preferences menu
#Click on the Modules section on the sidebar
#Switch mod-script-pipe from New to Enabled
#Then you must restart Audacity. 

#To execute this script, start Audacity before starting PowerShell
#Then run powershell and enter & $path\TestPowershellAudacityNamedPipes.ps1 substituting your $path to this script

###############################################################################

Function PowershellAndAudacityNamedPipes() {
   Param(				# I just included these for my use later
      [Parameter(Mandatory=$true)]
      [String]$originalFileName,            # Message string
      [Parameter(Mandatory=$true)]
      [Double]$percentofOriginalSpeed         # Message string
   )

   #Write to “\.\pipe\ToSrvPipe”
   #Read from “\.\pipe\FromSrvPipe”

   $ComputerName = '.'
   $PipeNameToSrv = 'ToSrvPipe'
   $PipeNameFromSrv = 'FromSrvPipe'
   $PipeOpt  = [System.IO.Pipes.PipeOptions]::None
   $PipeImpersonationLevel = [System.Security.Principal.TokenImpersonationLevel]::Impersonation
   $npipeClientToSrv = $null # Named pipe stream
   $npipeClientFromSrv = $null # Named pipe stream

   $pipeWriter = $pipeReader = $null   # Stream Writer and Stream Reader

try {
   #**Create the named pipes** I tried in InOut PipeDirection but I wasn't able to get it to work.
######################################   
   $npipeClientToSrv = new-object System.IO.Pipes.NamedPipeClientStream($ComputerName, $PipeNameToSrv, [System.IO.Pipes.PipeDirection]::Out, [System.IO.Pipes.PipeOptions]::None, [System.Security.Principal.TokenImpersonationLevel]::Impersonation)

   $npipeClientFromSrv = new-object System.IO.Pipes.NamedPipeClientStream($ComputerName, $PipeNameFromSrv, [System.IO.Pipes.PipeDirection]::In, [System.IO.Pipes.PipeOptions]::None, [System.Security.Principal.TokenImpersonationLevel]::Impersonation)

   **#Connect the pipes giving them each 2,000 milliseconds to connect**
######################################
   $npipeClientToSrv.Connect(2000)
   $npipeClientFromSrv.Connect(2000)

   **#Test to see if the named pipes exist**
######################################
   if (!$npipeClientToSrv.IsConnected) { 
      Write-Output "npipeClientToSrv did not connect" 
   }Else{
      Write-Output "npipeClientToSrv connected"
   }

   if (!$npipeClientFromSrv.IsConnected) { 
      Write-Output "npipeClientFromSrv did not connect" 
   }Else{
      Write-Output "npipeClientFromSrv connected"
   }

   **#Create the pipeReader**
######################################
   $pipeReader = new-object System.IO.StreamReader($npipeClientFromSrv)

   **#Create the pipeWriter and set it to AutoFlush the buffers** 
######################################  
   $pipeWriter = new-Object System.IO.StreamWriter($npipeClientToSrv)
   $pipeWriter.AutoFlush = $true

   **#Test to see if the pipeReader and pipeWriter exist**
######################################
   if($pipeReader){
   Write-Output "pipeReader exists"
   }Else{
      Write-Output "pipeReader doesn't exist"
   }

      if($pipeWriter){
   Write-Output "pipeWriter exists"
   }Else{
      Write-Output "pipeWriter doesn't exist"
   }

###############################################################################
#ToDo: Add more messages and the code to handle the different responses to each command
#you can send Audacity, and maybe a while loop for other commands.
#Another thing would be to use functions to modularize the code.

###############################################################################
# IMPORTANT! Please note that sending commands down the pipe sends the commands
# to a buffer. The response to the commands make it seem like the commands have
# finished but you may have to build in pauses in your script to give the tasks
# time to complete. As an example, Sliding stretches take over a minute to complete
# so you might have to wait sleep commands for 70 to 80 seconds to let them complete.
###############################################################################
**#The response from Audacity to the command "Help: Command=Help" was**

**#A blank line**
**#{"id":"Help", "name":"Help", "params":**
**#    [**
**#      { "key":"Command", "type":"string", "default":"Help" },**
**#      { "key":"Format", "type":"enum", "default":"JSON", "enum":**
**#          [ "JSON", "LISP", "Brief" ] } ],**
**#  "url":"Extra_Menu:_Scriptables_II#help",**
**#  "tip":"Gives help on a command." }**
**#BatchCommand finished: OK**
**#A blank line**

**#The response from Audacity to the non-existent command "Belch: Command=Belch" was**

#Your batch command of Belch was not recognized.
#BatchCommand finished: Failed!
#BatchCommand finished: Failed!

#While the code below works, it isn't completely correct in handling the response 

**# Test pipeWriter**
######################################
   $Msg = "Help: Command=Help"
   #$Msg = "Belch: Command=Belch"
   $pipeWriter.WriteLine($Msg)
   $pipeWriter.Flush()

**# Test pipeReader**
######################################
#The code in this section isn't exactly right. While you are looking for "BatchCommand finished: OK",
#the last line in the response is a blank line. Not all commands provide the same response so it will
#take a while to gather the command to be sent and the resonse expected.

   Sleep 2
   while (($data = $pipeReader.ReadLine()) -ne $null) {
      Write-Output $data
      if($data -eq "BatchCommand finished: OK"){
         #this next line was necessary for Import2
         if(($data = $pipeReader.ReadLine()) -eq ""){
            Write-Output $data
            break
            }
         break
      }else{
      if($data -eq "BatchCommand finished: Failed!"){
         Write-Output $data
         break
         }
      }      
   }

###############################################################################

   } catch {

**# ToDo handle errors from the try statement**

   } finally {
#Close objects
   if ($pipeWriter -ne $null) {
      #$pipeWriter.Dispose() # Release resources
      $pipeWriter = $null   # Force the PowerShell garbage collector to delete the .net object
   }
   if ($pipeReader -ne $null) {
      $pipeReader.Dispose() # Release resources
      $pipeReader = $null   # Force the PowerShell garbage collector to delete the .net object
   }
   if ($npipeClientToSrv -ne $null) {
      $npipeClientToSrv.Dispose() # Release resources
   }
   if ($npipeClientFromSrv -ne $null) {
      $npipeClientFromSrv.Dispose() # Release resources
   }
 }
}

**PowershellAndAudacityNamedPipes "Param1" "80.0"**

I’ve added code tags to your post so that the formatting is preserved.

For future reference, when posting code, use three backticks before and after your code block like this:

```
# Formatted code here…
```