func newEcho(delay time.Duration) *echo { h, err := portaudio.DefaultHostApi() chk(err) p := portaudio.LowLatencyParameters(h.DefaultInputDevice, h.DefaultOutputDevice) p.Input.Channels = 1 p.Output.Channels = 1 e := &echo{buffer: make([]float32, int(p.SampleRate*delay.Seconds()))} e.Stream, err = portaudio.OpenStream(p, e.processAudio) chk(err) return e }
// Get a channel to read convenient data snippets from a device. // returns // <-chan TMSnippet for caller to get audio samples // chan<- bool for caller to send on when wanting to gracefully shutdown the audio stream and associated chans (true and false are treated the same here) // <-chan error for the data-sending goroutine to report any errors // error nil unless the stream can't be opened // Note that this function doesn't (yet) automatically close if errors happen. It is currently the caller's responsibility to check if the error can be ignored and if not, to send something to the chan<- bool. func MakeInputChannel(device Device) (<-chan *TMSnippet, chan<- bool, <-chan error, error) { streamParams := pa.LowLatencyParameters(device.paDeviceInfo, nil) streamParams.FramesPerBuffer = 64 // Number taken from portaudio-go/portaudio/examples/record.go line 70 fmt.Printf("Stream parameters: %v\n", streamParams) buffer := make([]float32, streamParams.FramesPerBuffer) stream, err := pa.OpenStream(streamParams, buffer) if err != nil { return nil, nil, nil, err } tmschan := make(chan *TMSnippet, 10) closechan := make(chan bool) errchan := make(chan error) go func() { if err := stream.Start(); err != nil { errchan <- err } defer func() { err := stream.Close() if err != nil { errchan <- err } close(closechan) close(errchan) }() for { err := stream.Read() if err != nil { errchan <- err break } tms := new(TMSnippet) tms.SampleHz = streamParams.SampleRate tms.Samples = make([]float32, len(buffer)) copy(tms.Samples, buffer) tmschan <- tms } /* for { if _, end := <-closechan; end { break } var tms TMSnippet tms.SampleHz = streamParams.SampleRate if framesAvailable, err := stream.AvailableToRead(); err == nil { if framesAvailable <= 0 { errchan <- errors.New("No frames available.") continue } // TODO: per TODO below, see if len(buffer) is more appropriate than framesAvailable tms.Samples = make([]float32, framesAvailable) // TODO: see if this gets stream.AvailableToRead()'s "maximum number of frames that can be read from the stream without blocking or busy waiting" described in portaudio.h or if it gets len(buffer) frames per method comment err = stream.Read() if err != nil { errchan <- err } else { n := copy(tms.Samples, buffer) if n != framesAvailable { errchan <- errors.New("Didn't get expected number of frames.") } tmschan <- &tms } } else { errchan <- err } } */ }() return tmschan, closechan, errchan, nil }