feat(tci): the voice keyer can send its messages through the radio
The radio now appears as a device in both audio lists — 'Radio (TCI network audio)' — so the receive audio and the voice keyer can both take the CAT link instead of a sound card. No virtual cable, no second card, no Windows mixer between the recording and the air. The transmit side reuses the exchange the tone probe established, with a WAV in place of the sine: the radio asks, we answer with the next slice, and it sets the pace. The message is converted once, up front, rather than per frame — a voice message is a few hundred kilobytes, and resampling inside a callback that has 21 ms to answer would put arithmetic on the path where a late frame is a gap on the air. The PTT is untouched by all this: the keyer keys before and unkeys after exactly as it does with a sound card, so a transmission is bracketed by the same code whichever way the audio travels. And the playback runs OFF the CAT goroutine, since everything else about the rig goes through that one place and a ten-second message would otherwise freeze the frequency display and the antenna following for ten seconds. Two refusals rather than silent failure. A radio that has gone away stops being offered as a device at all, and a radio whose transmit audio source is still the microphone is caught within a fifth of a second — a voice keyer that transmits silence is worse than one that says why it will not.
This commit is contained in:
+9
-1
@@ -71,7 +71,15 @@ func tciToRecorderPCM(rate int, samples []float32) []byte {
|
||||
// for it: opening a 384 kB/s stream on a station that records nothing is work
|
||||
// the radio does for nobody.
|
||||
func (a *App) startTCIRecording() {
|
||||
if a.cat == nil || a.settingOr(keyTCIRecAudio, "") != "1" {
|
||||
if a.cat == nil {
|
||||
return
|
||||
}
|
||||
// Two ways to ask for the same thing: the option in the TCI section, or
|
||||
// simply choosing the radio as the "From radio" device. The second is where
|
||||
// an operator looks first — it is the question they are already answering —
|
||||
// so it has to work as well as the tick box.
|
||||
cfg, _ := a.GetAudioSettings()
|
||||
if a.settingOr(keyTCIRecAudio, "") != "1" && cfg.FromRadio != audio.NetworkDeviceID {
|
||||
return
|
||||
}
|
||||
err := a.cat.TCIAudioDo(func(t cat.TCIAudioController) error {
|
||||
|
||||
Reference in New Issue
Block a user