Switch the lab reflector to jayReflector and harden local station use.
Build DJ1JAY’s fork with an optional Satellite/Trunk stub, allow -c/--config, open capture and playback as separate devices, and add a lab start helper. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+73
-41
@@ -12,47 +12,51 @@ import (
|
||||
|
||||
const stationRate = 16000
|
||||
|
||||
// Start opens the default input and output and converts them to and from
|
||||
// 16 kHz mono. It returns when the devices are running. They close when ctx ends.
|
||||
// Start opens the default capture device and the default playback device and
|
||||
// converts them to and from 16 kHz mono. It returns when both devices are
|
||||
// running. They close when ctx ends.
|
||||
func Start(ctx context.Context, capture func([]int16), playback func() []int16) error {
|
||||
mctx, err := malgo.InitContext(nil, malgo.ContextConfig{}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg := malgo.DefaultDeviceConfig(malgo.Duplex)
|
||||
cfg.SampleRate = stationRate
|
||||
cfg.Capture.Format = malgo.FormatS16
|
||||
cfg.Capture.Channels = 1
|
||||
cfg.Playback.Format = malgo.FormatS16
|
||||
cfg.Playback.Channels = 1
|
||||
|
||||
var playRes, capRes converter
|
||||
capCfg := malgo.DefaultDeviceConfig(malgo.Capture)
|
||||
capCfg.SampleRate = stationRate
|
||||
capCfg.Capture.Format = malgo.FormatS16
|
||||
capCfg.Capture.Channels = 1
|
||||
|
||||
playCfg := malgo.DefaultDeviceConfig(malgo.Playback)
|
||||
playCfg.SampleRate = stationRate
|
||||
playCfg.Playback.Format = malgo.FormatS16
|
||||
playCfg.Playback.Channels = 1
|
||||
|
||||
var capRes, playRes converter
|
||||
var playBuf []int16
|
||||
callbacks := malgo.DeviceCallbacks{
|
||||
Data: func(output, input []byte, framecount uint32) {
|
||||
if len(input) > 0 {
|
||||
channels := 1
|
||||
if framecount > 0 {
|
||||
channels = len(input) / int(framecount) / 2
|
||||
}
|
||||
if channels < 1 {
|
||||
channels = 1
|
||||
}
|
||||
pcm := capRes.Convert(downmix(bytesToS16(input), channels))
|
||||
if len(pcm) > 0 {
|
||||
capture(pcm)
|
||||
}
|
||||
|
||||
capDev, err := malgo.InitDevice(mctx.Context, capCfg, malgo.DeviceCallbacks{
|
||||
Data: func(_, input []byte, framecount uint32) {
|
||||
if len(input) == 0 {
|
||||
return
|
||||
}
|
||||
channels := channelCount(len(input), framecount)
|
||||
pcm := capRes.Convert(downmix(bytesToS16(input), channels))
|
||||
if len(pcm) > 0 {
|
||||
capture(pcm)
|
||||
}
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
mctx.Free()
|
||||
return err
|
||||
}
|
||||
|
||||
playDev, err := malgo.InitDevice(mctx.Context, playCfg, malgo.DeviceCallbacks{
|
||||
Data: func(output, _ []byte, framecount uint32) {
|
||||
if len(output) == 0 {
|
||||
return
|
||||
}
|
||||
channels := 1
|
||||
if framecount > 0 {
|
||||
channels = len(output) / int(framecount) / 2
|
||||
}
|
||||
if channels < 1 {
|
||||
channels = 1
|
||||
}
|
||||
channels := channelCount(len(output), framecount)
|
||||
playBuf = append(playBuf, playRes.Convert(playback())...)
|
||||
need := int(framecount)
|
||||
var frame []int16
|
||||
@@ -66,32 +70,60 @@ func Start(ctx context.Context, capture func([]int16), playback func() []int16)
|
||||
}
|
||||
writeS16(output, frame, channels)
|
||||
},
|
||||
}
|
||||
dev, err := malgo.InitDevice(mctx.Context, cfg, callbacks)
|
||||
})
|
||||
if err != nil {
|
||||
capDev.Uninit()
|
||||
mctx.Free()
|
||||
return err
|
||||
}
|
||||
rate := int(dev.SampleRate())
|
||||
if rate == 0 {
|
||||
rate = stationRate
|
||||
|
||||
capRate := int(capDev.SampleRate())
|
||||
if capRate == 0 {
|
||||
capRate = stationRate
|
||||
}
|
||||
capRes = converter{from: rate, to: stationRate}
|
||||
playRes = converter{from: stationRate, to: rate}
|
||||
if err := dev.Start(); err != nil {
|
||||
dev.Uninit()
|
||||
playRate := int(playDev.SampleRate())
|
||||
if playRate == 0 {
|
||||
playRate = stationRate
|
||||
}
|
||||
capRes = converter{from: capRate, to: stationRate}
|
||||
playRes = converter{from: stationRate, to: playRate}
|
||||
|
||||
if err := capDev.Start(); err != nil {
|
||||
playDev.Uninit()
|
||||
capDev.Uninit()
|
||||
mctx.Free()
|
||||
return err
|
||||
}
|
||||
if err := playDev.Start(); err != nil {
|
||||
capDev.Stop()
|
||||
playDev.Uninit()
|
||||
capDev.Uninit()
|
||||
mctx.Free()
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
dev.Stop()
|
||||
dev.Uninit()
|
||||
_ = playDev.Stop()
|
||||
_ = capDev.Stop()
|
||||
playDev.Uninit()
|
||||
capDev.Uninit()
|
||||
mctx.Free()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func channelCount(bytes int, framecount uint32) int {
|
||||
channels := 1
|
||||
if framecount > 0 {
|
||||
channels = bytes / int(framecount) / 2
|
||||
}
|
||||
if channels < 1 {
|
||||
return 1
|
||||
}
|
||||
return channels
|
||||
}
|
||||
|
||||
func bytesToS16(b []byte) []int16 {
|
||||
n := len(b) / 2
|
||||
out := make([]int16, n)
|
||||
|
||||
Reference in New Issue
Block a user