Q: how to encode input audio bytes to PCM properly
I am a bit of a novice to audio encoding. I am attempting to capture desktop audio using WASAPI loopback Code here: https://gist.github.com/husainhz7/c8d4fac481a3b860243566835afaeca0
I want to feed this audio to libopus to encode an opus stream. libopus requires PCM encoded floats/ints. What is the best way to do that here. I see that miniaudio provides 'ma_encoder_write_pcm_frames' which I don't think is supported by malgo.
Hi @husainhz7,
I was able to throw together the following to capture raw PCM and save it to a file. I used Audacity to import the raw stream to confirm it did correctly get captured.
package main
import (
"fmt"
"log"
"path/filepath"
"os"
"time"
"github.com/gen2brain/malgo"
)
// Create function to sample music and return basename.
func sample() string {
ctx, err := malgo.InitContext(nil, malgo.ContextConfig{}, func(message string) {
fmt.Printf("LOG <%v>\n", message)
})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer func() {
_ = ctx.Uninit()
ctx.Free()
}()
deviceConfig := malgo.DefaultDeviceConfig(malgo.Duplex)
deviceConfig.Capture.Format = malgo.FormatS32
deviceConfig.Capture.Channels = 1
deviceConfig.SampleRate = 8000
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
newCapturedSampleCount := capturedSampleCount + sampleCount
pCapturedSamples = append(pCapturedSamples, pSample...)
capturedSampleCount = newCapturedSampleCount
}
captureCallbacks := malgo.DeviceCallbacks{
Data: onRecvFrames,
}
device, err := malgo.InitDevice(ctx.Context, deviceConfig, captureCallbacks)
if err != nil {
log.Printf("Failed to init device: %v", err)
os.Exit(1)
}
err = device.Start()
if err != nil {
log.Printf("Failed to start device: %v", err)
os.Exit(1)
}
// Count to 10 seconds
for i := 11; i > 1; i-- {
fmt.Printf("Sampling audio for %d seconds \r", i)
time.Sleep(time.Second)
}
basename := filepath.Join("tmp", time.Now().Format("20060102150405"))
rawFile := fmt.Sprintf("%s.pcm", basename)
// Save pCapturedSamples to file
f, err := os.Create(rawFile)
if err != nil {
log.Printf("Error creating file: %v", err)
}
defer f.Close()
_, err = f.Write(pCapturedSamples)
if err != nil {
log.Printf("Error saving PCM data: %v", err)
}
device.Uninit()
return basename + ".pcm"
}
The pCapturedSamples holds your raw frames. You should be able to pass this data directly into your ma_encorder_write_pcm_frames() method as-is.