malgo
malgo copied to clipboard
Unable to capture from specific device
Hi,
I am unable to capture from a specific device, but I am able to play back. The code structure remains the same and is derived from the examples.
package main
import (
"fmt"
"log"
"os"
"github.com/gen2brain/malgo"
)
func main() {
context, err := malgo.InitContext(nil, malgo.ContextConfig{}, nil)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer func() {
_ = context.Uninit()
context.Free()
}()
// Capture devices.
infos, err := context.Devices(malgo.Capture)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("Capture Devices")
for i, info := range infos {
e := "ok"
full, err := context.DeviceInfo(malgo.Capture, info.ID, malgo.Shared)
if err != nil {
e = err.Error()
}
fmt.Printf(" ID: [%d]: %v, %s, [%s], formats: %+v\n",
i, info.ID, info.Name(), e, full.Formats)
}
id := scanIdOption(infos)
fmt.Printf("Reading audio from: %s\n", infos[id].Name())
deviceConfig := malgo.DefaultDeviceConfig(malgo.Capture)
deviceConfig.Capture.Format = malgo.FormatS16
deviceConfig.Capture.Channels = 1
deviceConfig.Playback.Format = malgo.FormatS16
deviceConfig.Playback.Channels = 1
deviceConfig.SampleRate = 16000
deviceConfig.Alsa.NoMMap = 1
deviceConfig.PeriodSizeInMilliseconds = 40
deviceConfig.Capture.DeviceID = infos[id].ID.Pointer()
var playbackSampleCount uint32
var capturedSampleCount uint32
pCapturedSamples := make([]byte, 0)
sizeInBytes := uint32(malgo.SampleSizeInBytes(deviceConfig.Capture.Format))
onRecvFrames := func(pSample2, pSample []byte, framecount uint32) {
sampleCount := framecount * deviceConfig.Capture.Channels * sizeInBytes
log.Println(framecount)
newCapturedSampleCount := capturedSampleCount + sampleCount
pCapturedSamples = append(pCapturedSamples, pSample...)
capturedSampleCount = newCapturedSampleCount
}
fmt.Println("Recording...")
captureCallbacks := malgo.DeviceCallbacks{
Data: onRecvFrames,
}
device, err := malgo.InitDevice(context.Context, deviceConfig, captureCallbacks)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
err = device.Start()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("Press Enter to stop recording...")
fmt.Scanln()
device.Uninit()
infos, err = context.Devices(malgo.Playback)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("Playback Devices")
for i, info := range infos {
e := "ok"
full, err := context.DeviceInfo(malgo.Playback, info.ID, malgo.Shared)
if err != nil {
e = err.Error()
}
fmt.Printf(" %d: %v, %s, [%s], formats: %+v\n",
i, info.ID, info.Name(), e, full.Formats)
}
id = scanIdOption(infos)
fmt.Printf("Playing audio to: %s\n", infos[id].Name())
deviceConfig = malgo.DefaultDeviceConfig(malgo.Playback)
deviceConfig.Capture.Format = malgo.FormatS16
deviceConfig.Capture.Channels = 1
deviceConfig.Playback.Format = malgo.FormatS16
deviceConfig.Playback.Channels = 1
deviceConfig.SampleRate = 16000
deviceConfig.Alsa.NoMMap = 1
deviceConfig.PeriodSizeInMilliseconds = 40
deviceConfig.Playback.DeviceID = infos[id].ID.Pointer()
onSendFrames := func(pSample, nil []byte, framecount uint32) {
samplesToRead := framecount * deviceConfig.Playback.Channels * sizeInBytes
if samplesToRead > capturedSampleCount-playbackSampleCount {
samplesToRead = capturedSampleCount - playbackSampleCount
}
copy(pSample, pCapturedSamples[playbackSampleCount:playbackSampleCount+samplesToRead])
playbackSampleCount += samplesToRead
if playbackSampleCount == uint32(len(pCapturedSamples)) {
playbackSampleCount = 0
}
}
fmt.Println("Playing...")
playbackCallbacks := malgo.DeviceCallbacks{
Data: onSendFrames,
}
device, err = malgo.InitDevice(context.Context, deviceConfig, playbackCallbacks)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
err = device.Start()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("Press Enter to quit...")
fmt.Scanln()
device.Uninit()
}
func scanIdOption(infos []malgo.DeviceInfo) int {
fmt.Println("\r\nChoose an ID from the list above: ")
var id int
_, err := fmt.Scanf("%d", &id)
if err != nil {
log.Fatal(err)
} else {
fmt.Printf("\r\nChosen ID: [%d]\n", id)
}
if id >= len(infos) {
fmt.Println("\r\nInvalid ID")
panic("Invalid ID")
}
return id
}
If I comment deviceConfig.Capture.DeviceID = infos[id].ID.Pointer(), then I get audio captured from the default stream.
When this line is enabled, the callback is triggered and I received 640 bytes of zeroes. No data is received. Am I missing something here?
Looks like there are some devices listed here that are not capture devices.
Capture Devices
ID: [0]: 616c73615f6f75747075742e7063692d303030305f30305f31662e332e616e616c6f672d73746572656f2e6d6f6e69746f72, Monitor of Built-in Audio Analog Stereo, [ok], formats: [{Format:4 Channels:2 SampleRate:48000 Flags:0}]
ID: [1]: 616c73615f696e7075742e7063692d303030305f30305f31662e332e616e616c6f672d73746572656f, Built-in Audio Analog Stereo, [ok], formats: [{Format:4 Channels:2 SampleRate:48000 Flags:0}]
ID: [2]: 616c73615f6f75747075742e7063692d303030305f30315f30302e312e68646d692d73746572656f2d6578747261312e6d6f6e69746f72, Monitor of GP107GL High Definition Audio Controller Digital Stereo (HDMI 2), [ok], formats: [{Format:4 Channels:2 SampleRate:48000 Flags:0}]
ID: [3]: 626c75657a5f696e7075742e36305f46445f41365f30425f34375f33442e30, miPods, [ok], formats: [{Format:2 Channels:1 SampleRate:16000 Flags:0}]
ID: [4]: 626c75657a5f6f75747075742e36305f46445f41365f30425f34375f33442e312e6d6f6e69746f72, Monitor of miPods, [ok], formats: [{Format:2 Channels:1 SampleRate:16000 Flags:0}]
Choose an ID from the list above:
ID's 0, 2 and 4 are not Capture devices, yet they show up here.
infos, err := context.Devices(malgo.Capture)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("Capture Devices")
for i, info := range infos {
e := "ok"
full, err := context.DeviceInfo(malgo.Capture, info.ID, malgo.Shared)
if err != nil {
e = err.Error()
}
fmt.Printf(" ID: [%d]: %v, %s, [%s], formats: %+v\n",
i, info.ID, info.Name(), e, full.Formats)
}
But it is not the case for Playback devices.
Playback Devices
0: 616c73615f6f75747075742e7063692d303030305f30305f31662e332e616e616c6f672d73746572656f, Built-in Audio Analog Stereo, [ok], formats: [{Format:4 Channels:2 SampleRate:48000 Flags:0}]
1: 616c73615f6f75747075742e7063692d303030305f30315f30302e312e68646d692d73746572656f2d657874726131, GP107GL High Definition Audio Controller Digital Stereo (HDMI 2), [ok], formats: [{Format:4 Channels:2 SampleRate:48000 Flags:0}]
2: 626c75657a5f6f75747075742e36305f46445f41365f30425f34375f33442e31, miPods, [ok], formats: [{Format:2 Channels:1 SampleRate:16000 Flags:0}]
Choose an ID from the list above:
In playback devices, Capture devices are not to be seen.
I don't see an issue, or perhaps I didn't understand you. What do you get from aplay -l and arecord -l, you should get the same list. Depending on the sound card you get a list of all possible devices, i.e. I get 5 playback devices but only one I can use, similar to capture devices, they are not playback but there are monitors and whatnot devices, pick the correct one based on channels, format, skip monitor devices, etc.
The point of enumeration is that you get a list of possible devices, and you pick and choose one, I don't think there is any guarantee here, anyway, these are bindings, and even if there is an issue it will not be solved here.
Hi @gen2brain,
I think the code is too much in the previous comment.
The Issue I faced initially:
- Playback devices were being shown in Capture list
- For e.g. Built In audio was shown as Monitor which was a Playback device.
- I was choosing this device as a Capture device, which was an incorrect choice.
- Choosing an option without "Monitor" in the name solved the issue.
- aplay output.wav returned immediately. The audio file was empty.
If it helps anyone, I will attach captureToFile.txt the modified capture.go file to store in in a wav file.