From f4956a63bba004e909c7b9999b76369f9e5685a0 Mon Sep 17 00:00:00 2001 From: rouggy Date: Thu, 30 Jul 2026 23:27:30 +0200 Subject: [PATCH] fix: say why playing a recording on the air did nothing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "I click play and nothing happens" is what an operator sees when a function returns an error into a promise the interface swallows. Every refusal in the playback path is now logged as well as returned, and each names its own cause — audio not initialised, the take still running, the take empty, the device rejecting the file. One of those was not even checked: playing back needs an OUTPUT device INTO the radio, which is a different setting from the recorder's input and is very likely unset on a station that has only ever recorded. It now refuses with that sentence instead of failing somewhere inside the player. --- app.go | 19 ++++++++++++++++++- changelog.json | 6 ++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app.go b/app.go index 3ae8307..4dda0ae 100644 --- a/app.go +++ b/app.go @@ -7710,16 +7710,32 @@ func (a *App) QSOAudioResume() bool { // loop. Stopping is the operator's statement that the take is finished, and it // is one click they have already made. func (a *App) QSOAudioPlayOnAir() error { + // Every refusal below is LOGGED as well as returned. "I click play and + // nothing happens" is what an operator sees when a function returns an error + // into a promise the interface swallowed — and the difference between "no + // output device configured" and "the take was empty" cannot be guessed from + // the outside. if a.qsoRec == nil || a.audioMgr == nil { + applog.Printf("qso-rec: play refused — audio subsystem not initialised") return fmt.Errorf("audio not initialized") } if !a.qsoRec.Paused() { + applog.Printf("qso-rec: play refused — the recording is still running (stop it first)") return fmt.Errorf("stop the recording before playing it on the air") } pcm, err := a.qsoRec.PeekQSO() if err != nil { + applog.Printf("qso-rec: play refused — %v", err) return err } + cfgEarly, _ := a.GetAudioSettings() + if strings.TrimSpace(cfgEarly.ToRadio) == "" { + // The recorder needs an INPUT from the radio; playing back needs an + // OUTPUT into it, which is a different device and may well be unset on a + // station that only ever recorded. + applog.Printf("qso-rec: play refused — no output device to the radio configured (Settings → Audio)") + return fmt.Errorf("no audio output to the radio is configured — set it in Settings → Audio") + } // A plain WAV in the data dir, overwritten each time: the player takes a // path, and MP3 encoding would add seconds to something the operator is // waiting on with the transmitter keyed. @@ -7728,7 +7744,7 @@ func (a *App) QSOAudioPlayOnAir() error { return fmt.Errorf("prepare playback: %w", err) } - cfg, _ := a.GetAudioSettings() + cfg := cfgEarly if err := a.pttKey(cfg); err != nil { applog.Printf("qso-rec: PTT on failed before playback: %v", err) // Keep going — the audio still reaches the rig and the operator may use VOX. @@ -7738,6 +7754,7 @@ func (a *App) QSOAudioPlayOnAir() error { a.pttMu.Unlock() } if err := a.audioMgr.Play(cfg.ToRadio, path, cfg.TXGain); err != nil { + applog.Printf("qso-rec: playback on %q failed: %v", cfg.ToRadio, err) a.pttMu.Lock() keyed := a.dvkPttKeyed gen := a.pttGen diff --git a/changelog.json b/changelog.json index 35ee29b..7c70b8d 100644 --- a/changelog.json +++ b/changelog.json @@ -4,11 +4,13 @@ "date": "", "en": [ "FlexRadio: in split, OpsLog stays on the receive slice instead of following the transmitter.", - "QSO recording works on CW again. Your own sidetone is not in the file, the other station is." + "QSO recording works on CW again. Your own sidetone is not in the file, the other station is.", + "Playing a recording on the air now says why when it cannot — most often no audio output to the radio is configured." ], "fr": [ "FlexRadio : en split, OpsLog reste sur la slice de réception au lieu de suivre l'émission.", - "L'enregistrement des QSO fonctionne à nouveau en CW. Votre propre signal d'écoute n'est pas dans le fichier, celui du correspondant l'est." + "L'enregistrement des QSO fonctionne à nouveau en CW. Votre propre signal d'écoute n'est pas dans le fichier, celui du correspondant l'est.", + "L'émission d'un enregistrement indique désormais pourquoi elle échoue — le plus souvent aucune sortie audio vers la radio n'est configurée." ] }, {