feat: full WSL2 audio support and Windows audio stability fix
This commit is contained in:
112
pkg/audio/capture_linux.go
Normal file
112
pkg/audio/capture_linux.go
Normal file
@@ -0,0 +1,112 @@
|
||||
//go:build linux
|
||||
|
||||
package audio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/gordonklaus/portaudio"
|
||||
)
|
||||
|
||||
// Capturer handles audio capture using PortAudio
|
||||
type Capturer struct {
|
||||
stream *portaudio.Stream
|
||||
running bool
|
||||
mu sync.Mutex
|
||||
onAudio func(samples []int16)
|
||||
currentLevel int
|
||||
levelMu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewCapturer() (*Capturer, error) {
|
||||
if err := initPortAudio(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Capturer{}, nil
|
||||
}
|
||||
|
||||
func (c *Capturer) SetCallback(fn func(samples []int16)) {
|
||||
c.mu.Lock()
|
||||
c.onAudio = fn
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
func (c *Capturer) Start() error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
c.stream, err = portaudio.OpenDefaultStream(1, 0, 48000, frameSamples, c.processCapture)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open portaudio capture stream: %w", err)
|
||||
}
|
||||
|
||||
if err := c.stream.Start(); err != nil {
|
||||
c.stream.Close()
|
||||
return fmt.Errorf("failed to start portaudio capture stream: %w", err)
|
||||
}
|
||||
|
||||
c.running = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Capturer) Stop() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if !c.running {
|
||||
return
|
||||
}
|
||||
c.running = false
|
||||
if c.stream != nil {
|
||||
c.stream.Abort()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Capturer) Close() {
|
||||
c.Stop()
|
||||
c.mu.Lock()
|
||||
if c.stream != nil {
|
||||
c.stream.Close()
|
||||
}
|
||||
c.mu.Unlock()
|
||||
terminatePortAudio()
|
||||
}
|
||||
|
||||
func (c *Capturer) processCapture(in []int16) {
|
||||
c.mu.Lock()
|
||||
callback := c.onAudio
|
||||
running := c.running
|
||||
c.mu.Unlock()
|
||||
|
||||
if !running || callback == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate level
|
||||
level := CalculateRMSLevel(in)
|
||||
c.levelMu.Lock()
|
||||
c.currentLevel = level
|
||||
c.levelMu.Unlock()
|
||||
|
||||
// Clone buffer and send to callback
|
||||
samples := make([]int16, len(in))
|
||||
copy(samples, in)
|
||||
callback(samples)
|
||||
}
|
||||
|
||||
func (c *Capturer) GetLevel() int {
|
||||
c.levelMu.RLock()
|
||||
defer c.levelMu.RUnlock()
|
||||
return c.currentLevel
|
||||
}
|
||||
|
||||
func (c *Capturer) IsRunning() bool {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
return c.running
|
||||
}
|
||||
Reference in New Issue
Block a user