fix(icom): the audio stream no longer depends on the speakers

The sink — decode + recorder feed — was built only on the success path of
starting the monitor, so speakers-off (the remembered preference) or an
output device that failed to open silently disabled the WHOLE stream: the
session connected audio=false, and a ticked RX-audio checkbox did nothing.
The sink now exists whenever the stream is on; only the monitor obeys the
speaker choice.
This commit is contained in:
2026-08-30 01:53:06 +02:00
parent 58e36667a7
commit ec967e29cf
2 changed files with 24 additions and 31 deletions
+20 -29
View File
@@ -15637,6 +15637,25 @@ func (a *App) reloadCAT() {
// verification (see icomaudio.go) — hence experimental + opt-in.
acfg, _ := a.GetAudioSettings()
a.audioMgr.StopMonitor() // clear any prior monitor/sink so a re-save restarts cleanly
// The SINK — decode + recorder feed — exists whenever the stream is
// on. It used to be built only when the speakers were also wanted,
// so "speakers off" (or a sink that failed to open) silently turned
// the whole stream off: audio=false on the wire, no recordings, no
// voice keyer, and a ticked RX-audio checkbox that did nothing.
codec := audio.NewPCM16Codec()
audioSink = func(payload []byte) {
pcm, err := codec.Decode(payload)
if err != nil {
return
}
a.audioMgr.PushMonitorAudio(pcm)
// And to the QSO recorder, which has no device to capture from
// on this backend. It drops the samples unless a recording is
// actually running, so this costs a function call when idle.
if a.qsoRec != nil {
a.qsoRec.PushRX(pcm)
}
}
// Listening is a CHOICE, remembered: an operator sitting next to the
// radio hears it in the room and wants the stream only for the
// recorder and the voice keyer. Stop listening turns this off, the
@@ -15645,36 +15664,8 @@ func (a *App) reloadCAT() {
if a.settingOr(keyAudioMonitorOn, "1") != "1" {
applog.Printf("icom-net audio: stream on, speakers off (Listening was stopped by the operator)")
} else if err := a.audioMgr.StartMonitorSink(acfg.ListeningDevice); err != nil {
applog.Printf("icom-net audio: cannot start output sink: %v", err)
applog.Printf("icom-net audio: stream on, but the output sink failed: %v", err)
} else {
codec := audio.NewPCM16Codec()
audioSink = func(payload []byte) {
pcm, err := codec.Decode(payload)
if err != nil {
return
}
// The IC-7760 streams TWO-channel LPCM whatever the conninfo
// asks for (rxcodec 0x02 was requested, 1280-byte payloads
// kept arriving — 320 stereo frames per 20 ms tick, right
// channel all zeros). Played as mono that is half-speed
// metallic garble. A stereo-sized packet is folded to its
// left channel; a 640-byte mono packet (a rig that honours
// the request) passes through untouched.
if len(pcm) == 1280 {
mono := make([]byte, len(pcm)/2)
for i := 0; i+3 < len(pcm); i += 4 {
mono[i/2], mono[i/2+1] = pcm[i], pcm[i+1]
}
pcm = mono
}
a.audioMgr.PushMonitorAudio(pcm)
// And to the QSO recorder, which has no device to capture from
// on this backend. It drops the samples unless a recording is
// actually running, so this costs a function call when idle.
if a.qsoRec != nil {
a.qsoRec.PushRX(pcm)
}
}
applog.Printf("icom-net audio: RX audio streaming ENABLED (experimental) → %q", acfg.ListeningDevice)
}
}