Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to capture from specific device #56

Open
navinreddy23 opened this issue Aug 1, 2024 · 3 comments
Open

Unable to capture from specific device #56

navinreddy23 opened this issue Aug 1, 2024 · 3 comments

Comments

@navinreddy23
Copy link

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?

@navinreddy23
Copy link
Author

navinreddy23 commented Aug 1, 2024

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.

@gen2brain
Copy link
Owner

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.

@navinreddy23
Copy link
Author

navinreddy23 commented Aug 1, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants