From 1fba7d2d574e9c99c6edaa4c93bc3e9b83cea239 Mon Sep 17 00:00:00 2001 From: rouggy Date: Sat, 29 Aug 2026 16:47:42 +0200 Subject: [PATCH] fix(icom): create the audio manager before the CAT link comes up reloadCAT builds the network audio sink gated on audioMgr, and startup created the manager AFTER the first reloadCAT: every fresh launch connected with audio silently off however the option was set, and the operator had to untick/save/retick it to hear anything. The manager is idle-until-used, so creating it earlier costs nothing. --- app.go | 81 +++++++++++++++++++++++++++----------------------- changelog.json | 6 ++-- 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/app.go b/app.go index b84880d..0182e6e 100644 --- a/app.go +++ b/app.go @@ -1252,6 +1252,49 @@ func (a *App) startup(ctx context.Context) { go a.applyRelayAuto(s.FreqHz, s.Band) } }) + // Digital Voice Keyer + QSO recorder (WASAPI). Idle until used. Created + // BEFORE the CAT link comes up, and it matters: a network Icom with RX + // audio enabled builds its audio sink inside reloadCAT, gated on audioMgr — + // when the manager was created later, every fresh launch connected with + // audio silently off, and the operator had to untick/save/retick the option + // to hear anything. + a.audioMgr = audio.NewManager(func() { + st := a.dvkStatus() + // When a voice message finishes (or is stopped), drop the PTT we keyed + // for it — but tag the release with the current key generation so it + // can't cut a transmission a newer message already started. + if !st.Playing { + a.pttMu.Lock() + keyed := a.dvkPttKeyed + gen := a.pttGen + if keyed { + a.dvkPttKeyed = false + } + a.pttMu.Unlock() + if keyed { + go a.dvkUnkeyPTT(gen) + } + // And give the microphone back, if we took it (see raiseFlexDVKDax). + // Off the callback's goroutine: this talks to the radio, and the + // audio manager is reporting a state change, not waiting on us. + go a.lowerFlexDVKDax() + } + if a.ctx != nil { + wruntime.EventsEmit(a.ctx, "audio:status", st) + } + }) + a.qsoRec = audio.NewRecorder() + if a.audioMgr != nil { + // A running monitor picks the new level up immediately: the operator is + // listening while they move the slider, and asking them to stop and + // restart it to hear the change is how a working control gets reported + // as broken. + if cfg, err := a.GetAudioSettings(); err == nil { + a.audioMgr.SetMonitorGain(cfg.FromGain) + } + } + a.startQSORecorderIfEnabled() + a.reloadCAT() // The QSO logbook lives where the ACTIVE PROFILE points it: the local SQLite @@ -1479,44 +1522,6 @@ func (a *App) startup(ctx context.Context) { }) a.solar.Start() - // Digital Voice Keyer + QSO recorder (WASAPI). Idle until used. - a.audioMgr = audio.NewManager(func() { - st := a.dvkStatus() - // When a voice message finishes (or is stopped), drop the PTT we keyed - // for it — but tag the release with the current key generation so it - // can't cut a transmission a newer message already started. - if !st.Playing { - a.pttMu.Lock() - keyed := a.dvkPttKeyed - gen := a.pttGen - if keyed { - a.dvkPttKeyed = false - } - a.pttMu.Unlock() - if keyed { - go a.dvkUnkeyPTT(gen) - } - // And give the microphone back, if we took it (see raiseFlexDVKDax). - // Off the callback's goroutine: this talks to the radio, and the - // audio manager is reporting a state change, not waiting on us. - go a.lowerFlexDVKDax() - } - if a.ctx != nil { - wruntime.EventsEmit(a.ctx, "audio:status", st) - } - }) - a.qsoRec = audio.NewRecorder() - if a.audioMgr != nil { - // A running monitor picks the new level up immediately: the operator is - // listening while they move the slider, and asking them to stop and - // restart it to hear the change is how a working control gets reported - // as broken. - if cfg, err := a.GetAudioSettings(); err == nil { - a.audioMgr.SetMonitorGain(cfg.FromGain) - } - } - a.startQSORecorderIfEnabled() - // NET Control store (global JSON, shared across logbooks). if ns, err := netctl.Open(filepath.Join(a.dataDir, "nets.json")); err != nil { applog.Printf("netctl: open failed: %v", err) diff --git a/changelog.json b/changelog.json index e780324..5cdffdd 100644 --- a/changelog.json +++ b/changelog.json @@ -17,7 +17,8 @@ "QSO recorder: starting a fresh manual take resets the counter for good — it used to flash 0 and jump back to the previous take’s elapsed time.", "MY_RIG follows the radio actually connected: the entry form now asks the active radio first, before the per-band default — an IC-7760 on the air no longer logs as the Flex the band plan names.", "QSO recorder: a manual take on a network Icom records the network RX stream, not the “From radio” sound card (which captured another radio’s DAX on a mixed station).", - "Icom network audio: duplicate and late-retransmitted packets no longer reach the recorder — recordings came out longer than the QSO, slowed and stuttering, while the speakers played fine." + "Icom network audio: duplicate and late-retransmitted packets no longer reach the recorder — recordings came out longer than the QSO, slowed and stuttering, while the speakers played fine.", + "Icom network audio: it now starts with the app — launching OpsLog connected with audio silently off, and the option had to be unticked and re-saved to hear anything." ], "fr": [ "Console Elecraft : le S-mètre est calibré sur un vrai K3 — S9 et les +dB correspondent désormais à l’affichage de la radio (il lisait environ deux points S trop bas).", @@ -34,7 +35,8 @@ "Enregistreur de QSO : démarrer une nouvelle prise manuelle remet le compteur à zéro pour de bon — il affichait 0 puis resautait au temps de la prise précédente.", "MY_RIG suit la radio réellement connectée : le formulaire interroge d’abord la radio active, avant le défaut par bande — un IC-7760 à l’antenne ne se logue plus comme le Flex prévu par le plan de bande.", "Enregistreur de QSO : une prise manuelle sur un Icom réseau enregistre le flux RX réseau, pas la carte son « From Radio » (qui capturait le DAX d’une autre radio sur une station mixte).", - "Audio réseau Icom : les paquets dupliqués ou retransmis en retard n’atteignent plus l’enregistreur — les enregistrements sortaient plus longs que le QSO, ralentis et hachés, alors que les haut-parleurs jouaient bien." + "Audio réseau Icom : les paquets dupliqués ou retransmis en retard n’atteignent plus l’enregistreur — les enregistrements sortaient plus longs que le QSO, ralentis et hachés, alors que les haut-parleurs jouaient bien.", + "Audio réseau Icom : il démarre maintenant avec l’application — au lancement, la connexion se faisait audio coupé, et il fallait décocher/recocher l’option pour entendre quelque chose." ] }, {