Watch
1
0
Fork
You've already forked mautrix-whatsapp
0
mirror of https://github.com/mautrix/whatsapp.git synced 2026-08-24 21:14:55 -04:00
mautrix-whatsapp/pkg/connector/voip/audio.go

151 lines
3.1 KiB
Go
Raw Permalink Normal View History

package voip
import (
"errors"
"fmt"
"io"
"math"
"sync"
lkmedia "github.com/livekit/media-sdk"
"github.com/purpshell/meowcaller"
)
var ErrAudioSourceClosed = errors.New("voip: audio source closed")
func Float32FrameToPCM16(frame []float32) lkmedia.PCM16Sample {
sample := make(lkmedia.PCM16Sample, len(frame))
for i, value := range frame {
switch {
case value > 1:
value = 1
case value < -1:
value = -1
}
if value == 1 {
sample[i] = math.MaxInt16
} else {
sample[i] = int16(value * 32768)
}
}
return sample
}
func PCM16ToFloat32Frame(sample lkmedia.PCM16Sample) []float32 {
frame := make([]float32, len(sample))
for i, value := range sample {
frame[i] = float32(value) / 32768
}
return frame
}
type LiveKitPCMWriter struct {
mu sync.RWMutex
track interface {
WriteSample(lkmedia.PCM16Sample) error
}
closed bool
}
func NewLiveKitPCMWriter(track interface {
WriteSample(lkmedia.PCM16Sample) error
}) *LiveKitPCMWriter {
return &LiveKitPCMWriter{track: track}
}
func (w *LiveKitPCMWriter) WriteFrame(frame []float32) error {
w.mu.RLock()
defer w.mu.RUnlock()
if w.closed {
return ErrAudioSourceClosed
}
if w.track == nil {
return nil
}
return w.track.WriteSample(Float32FrameToPCM16(frame))
}
func (w *LiveKitPCMWriter) Close() error {
w.mu.Lock()
w.closed = true
w.track = nil
w.mu.Unlock()
return nil
}
type MeowcallerAudioSource struct {
mu sync.Mutex
cond *sync.Cond
queue []float32
closed bool
maxSize int
}
func NewMeowcallerAudioSource(maxFrames int) *MeowcallerAudioSource {
if maxFrames <= 0 {
maxFrames = 8
}
src := &MeowcallerAudioSource{
maxSize: maxFrames * meowcaller.FrameSamples,
}
src.cond = sync.NewCond(&src.mu)
return src
}
func (src *MeowcallerAudioSource) WriteSample(sample lkmedia.PCM16Sample) error {
src.mu.Lock()
defer src.mu.Unlock()
if src.closed {
return ErrAudioSourceClosed
}
frame := PCM16ToFloat32Frame(sample)
src.queue = append(src.queue, frame...)
if len(src.queue) > src.maxSize {
copy(src.queue, src.queue[len(src.queue)-src.maxSize:])
src.queue = src.queue[:src.maxSize]
}
src.cond.Signal()
return nil
}
func (src *MeowcallerAudioSource) SampleRate() int {
return meowcaller.SampleRate
}
func (src *MeowcallerAudioSource) String() string {
return fmt.Sprintf("MeowcallerAudioSource(%d)", meowcaller.SampleRate)
}
func (src *MeowcallerAudioSource) ReadFrame() ([]float32, error) {
src.mu.Lock()
defer src.mu.Unlock()
for len(src.queue) < meowcaller.FrameSamples && !src.closed {
src.cond.Wait()
}
if len(src.queue) == 0 && src.closed {
return nil, io.EOF
}
frame := make([]float32, meowcaller.FrameSamples)
n := copy(frame, src.queue)
if n == len(src.queue) {
src.queue = src.queue[:0]
} else {
copy(src.queue, src.queue[n:])
src.queue = src.queue[:len(src.queue)-n]
}
return frame, nil
}
func (src *MeowcallerAudioSource) Close() error {
src.mu.Lock()
src.closed = true
src.queue = nil
src.cond.Broadcast()
src.mu.Unlock()
return nil
}
var (
_ meowcaller.AudioSink = (*LiveKitPCMWriter)(nil)
_ meowcaller.AudioSource = (*MeowcallerAudioSource)(nil)
)