I can output audio from Ableton Live and into my Go Pitch Tracking application

more technical title - Reading audio input from BlackHole port device

In this post, I use BlackHole to add a new Audio Device to my laptop. This allows me to send audio to it from third party applications like Ableton Live, and then read from it in my Go code.

First, I donated $10 to BlackHole and installed the 2 channel device. There are also 16 channel and 64 channel devices that I aspire to having use for one day. But for now, the 2 channel device is my speed.

Once installed, BlackHole 2ch shows up as an available Audio Input and Audio Output device. I set my output to the new driver and looped a midi sine wave instrument at middle C.

Within the code, using the portaudio package in Go, I can connect to the BlackHole 2ch device by finding the device by name and then specifying it in my portaudio stream creation. If I don't specify a device in my code, it will choose the DefaultInputDevice, which is default microphone.

What this unlocks

This unlocks the ability to work on post-audio-received workflows without needing to actively be making noise in the microphone. If there are any specific scenarios to test, I can set them up in Ableton, or another application.

Curious Things

My only curiosity is that I think my pitch output is eversoslightly sharp. Google says middle C is at 256hz, but my application is reading it at 258.146341hz. Very curious indeed.

Next steps -

Currently I can't hear any of the audio going to BlackHole, and I'd like to change that. Next step is to figure out how to route the audio back to the default device.

Code sample below -

1func findDeviceByName(name string) (*portaudio.DeviceInfo, error) { 2 devices, err := portaudio.Devices() 3 if err != nil { 4 return nil, err 5 } 6 for _, device := range devices { 7 if device.Name == name { 8 return device, nil 9 } 10 } 11 return nil, fmt.Errorf("device %s not found", name) 12}
go
1func initAudio(buffer []float32, deviceName Device) (*portaudio.Stream, error) { 2 err := portaudio.Initialize() 3 if err != nil { 4 return nil, err 5 } 6 var device *portaudio.DeviceInfo 7 if deviceName != "" { 8 device, err = findDeviceByName(string(deviceName)) 9 } else { 10 device, err = portaudio.DefaultInputDevice() 11 } 12 if err != nil { 13 return nil, err 14 } 15 streamParameters := portaudio.StreamParameters{ 16 Input: portaudio.StreamDeviceParameters{ 17 Device: device, 18 Channels: 1, 19 Latency: device.DefaultLowInputLatency, 20 }, 21 SampleRate: 44100, 22 FramesPerBuffer: len(buffer), 23 } 24 25 stream, err := portaudio.OpenStream(streamParameters, buffer) 26 if err != nil { 27 return nil, err 28 } 29 return stream, nil 30}
go