fix(audio): the radio is offered only where it can actually be

Adding it to the device lists put it in all four dropdowns, and an
operator picked it as their RECORDING MIC — which would have recorded the
station they were listening to instead of their own voice, on every voice
message and every QSO recording. It is not a microphone, and it is not a
pair of speakers either: the audio going the other way is transmit audio.
So it is listed for 'From radio' and 'To radio', and taken out of the mic
and the listening dropdowns.

And the RX monitor is disabled when the radio IS the source, with a line
saying why: the TCI stream is pushed into the QSO recorder, and nothing
plays it to the speakers. The button was there, enabled, doing nothing
audible — which is worse than a button that says it cannot.
This commit is contained in:
2026-08-26 19:04:02 +02:00
parent 98c49f6bbc
commit afde919e5b
2 changed files with 19 additions and 6 deletions
+17 -4
View File
@@ -6773,6 +6773,13 @@ function SettingsModalImpl({ onClose, onSaved, initialSection, onMainPaneChanged
xiegu: t('cat.optXiegu'),
} as Record<string, string>)[catCfg.backend] ?? '';
// The radio-over-network entry, which ListAudioInputDevices and
// ListAudioOutputDevices add when the CAT link can carry audio. It is a
// real choice for two of the four fields and nonsense for the other two.
const NET_DEVICE = 'net:radio';
const soundCardsOnly = (devs: AudioDev[]) => devs.filter((d) => d.id !== NET_DEVICE);
const fromRadioIsNetwork = audioCfg.from_radio === NET_DEVICE;
const deviceSelect = (
field: keyof AudioSettings,
devices: AudioDev[],
@@ -6807,10 +6814,16 @@ function SettingsModalImpl({ onClose, onSaved, initialSection, onMainPaneChanged
{deviceSelect('from_radio', audioInputs, t('aud.phFromRadio'))}
<Label className="text-sm">{t('aud.toRadio')}</Label>
{deviceSelect('to_radio', audioOutputs, t('aud.phToRadio'))}
{/* The radio is offered for the two fields it can BE where the
received audio comes from, and where the voice keyer sends
its messages and taken out of the other two. It is not a
microphone: choosing it here would record the station you are
listening to instead of your own voice. And it is not a pair of
speakers: the audio going that way is transmit audio. */}
<Label className="text-sm">{t('aud.recMic')}</Label>
{deviceSelect('recording_device', audioInputs, t('aud.phRecMic'))}
{deviceSelect('recording_device', soundCardsOnly(audioInputs), t('aud.phRecMic'))}
<Label className="text-sm">{t('aud.listening')}</Label>
{deviceSelect('listening_device', audioOutputs, t('aud.phListening'))}
{deviceSelect('listening_device', soundCardsOnly(audioOutputs), t('aud.phListening'))}
</div>
<p className="text-[11px] text-muted-foreground">
<strong>{t('aud.fromRadioShort')}</strong> {t('aud.explainFrom')}{' '}
@@ -6828,8 +6841,8 @@ function SettingsModalImpl({ onClose, onSaved, initialSection, onMainPaneChanged
size="sm"
className="h-8"
onClick={toggleMonitor}
disabled={!monitorOn && !audioCfg.from_radio}
title={t('aud.monitorTitle')}
disabled={!monitorOn && (!audioCfg.from_radio || fromRadioIsNetwork)}
title={fromRadioIsNetwork ? t('aud.monitorNoTci') : t('aud.monitorTitle')}
>
{monitorOn ? t('aud.stopListening') : t('aud.listenRadio')}
</Button>
+2 -2
View File
@@ -494,7 +494,7 @@ const en: Dict = {
'aud.noneDefault': '— none / system default —', 'aud.defaultTag': '(default)',
'aud.fromRadioShort': 'From Radio', 'aud.toRadioShort': 'To Radio', 'aud.explainFrom': '= what you receive (used by the QSO recorder).', 'aud.explainTo': '= where voice-keyer messages are transmitted.',
'aud.monitorTitle': "Hear the rig's RX audio (From Radio) through your Listening device", 'aud.listenRadio': '▶ Listen to radio', 'aud.stopListening': '■ Stop listening',
'aud.monitorOn': 'RX monitor running — From Radio → Listening device.', 'aud.monitorHint': 'Live-monitor the rig here (USB codec now; network audio later).',
'aud.monitorOn': 'RX monitor running — From Radio → Listening device.', 'aud.monitorNoTci': 'Listening here is not available with the radio as the source: the TCI stream feeds the QSO recorder, not the monitor. Listen on the radio itself.', 'aud.monitorHint': 'Live-monitor the rig here (USB codec now; network audio later).',
'aud.txTitle': 'Key PTT and pipe your live mic into the rig (To Radio device)', 'aud.talkRadio': '🎙 Talk to radio (TX)', 'aud.stopTalk': '■ Stop talking (TX)',
'aud.txOn': 'TRANSMITTING — mic → To Radio, PTT keyed. Click to stop.', 'aud.txHint': 'Live mic → rig with PTT (USB now; network TX later).',
'aud.recorder': 'QSO recorder', 'aud.recordEvery': 'Record every QSO to an audio file (From Radio + your mic)', 'aud.recFolder': 'Recordings folder', 'aud.browse': 'Browse…',
@@ -962,7 +962,7 @@ const fr: Dict = {
'aud.noneDefault': '— aucun / défaut système —', 'aud.defaultTag': '(défaut)',
'aud.fromRadioShort': 'Depuis la radio', 'aud.toRadioShort': 'Vers la radio', 'aud.explainFrom': "= ce que vous recevez (utilisé par l'enregistreur de QSO).", 'aud.explainTo': '= où sont émis les messages du manipulateur vocal.',
'aud.monitorTitle': "Écouter l'audio RX du poste (Depuis la radio) sur votre périphérique d'écoute", 'aud.listenRadio': '▶ Écouter la radio', 'aud.stopListening': "■ Arrêter l'écoute",
'aud.monitorOn': "Écoute RX active — Depuis la radio → périphérique d'écoute.", 'aud.monitorHint': "Écoute directe du poste (codec USB pour l'instant ; audio réseau plus tard).",
'aud.monitorOn': "Écoute RX active — Depuis la radio → périphérique d'écoute.", 'aud.monitorNoTci': "L'écoute ici n'est pas disponible avec la radio comme source : le flux TCI alimente l'enregistreur de QSO, pas l'écoute. Écoute sur la radio elle-même.", 'aud.monitorHint': "Écoute directe du poste (codec USB pour l'instant ; audio réseau plus tard).",
'aud.txTitle': 'Activer le PTT et envoyer votre micro vers le poste (périphérique « Vers la radio »)', 'aud.talkRadio': '🎙 Parler à la radio (TX)', 'aud.stopTalk': '■ Arrêter de parler (TX)',
'aud.txOn': 'ÉMISSION — micro → Vers la radio, PTT activé. Cliquez pour arrêter.', 'aud.txHint': "Micro direct → poste avec PTT (USB pour l'instant ; TX réseau plus tard).",
'aud.recorder': 'Enregistreur de QSO', 'aud.recordEvery': 'Enregistrer chaque QSO dans un fichier audio (Depuis la radio + votre micro)', 'aud.recFolder': 'Dossier des enregistrements', 'aud.browse': 'Parcourir…',