I'm building a small audio application for personal use and need to pipe to some external tools.
I want to structure my program around io.ReadClosers that I transform and pipe in to PulseAudios paplay command.
A simplified example:
package main
import (
"os/exec"
)
func main () {
speech, err := Say("Hello world!")
if err != nil {
log.Fatal(err)
}
err = Play(speech)
if err != nil {
log.Fatal(err)
}
}
// Say creates a stream containing text as a text-to-speech wav audio stream.
func Say(text string) (io.ReadCloser, error) {
speak := exec.Command("espeak", "--stdout", text)
out, err := speak.StdoutPipe()
if err != nil {
return nil, err
}
return out, speak.Start()
}
// Play a wav audio stream.
func Play(s io.ReadCloser) error {
play := exec.Command("paplay")
play.Stdin = s
play.Stderr = os.Stderr
return play.Run()
}
This is a simplified example, but I'd like to create wav streams using different commands, concatenate them and pipe them to paplay.
The go docs on exec.Cmd specify that I need to call Wait on each command to ensure the resources allocated for running the command are cleared:
Wait releases any resources associated with the Cmd.
But at the same time it is unclear if I need to call this when using StdoutPipe.
Will go clean up the command after the stdout pipe is exhausted and the command has exited or do I need to rethink my abstractions here?